Всем привет! Как многие знают, с Android 10 (Target sdk 29) google ввели новую политику безопасности. Новая политика SELinux звучит просто: «Нельзя исполнять файлы из той директории, в которую можно записывать». Всё это очень хорошо, но многие проекты сломались (В том числе и мой). Termux из google play УМЕЕТ запускать бинарные файлы на target sdk 29+. Я решил поделится, как выполнить бинарный файл из data/data/com.ваш.пакет/files на новых версиях sdk БЕЗ полного клонирования Termux и БЕЗ С/C++ части. Сам метод запуска будет именно на java. Репозиторий termux, откуда был взят способ: https://github.com/termux‑play‑store/termux‑apps.
В чём суть: любой бинарный файл, который вы запускаете, имеет свой контекст. Если вы запускаете через нативную директорию (data/app/и так далее), перед этим положив бинарные файлы в jniLins — контекст у такого бинарного файла будет правильным, и SELinux даст разрешение на запуск (Granted), но в случае с data/data другой случай, оттуда SELinux УЖЕ ОТКАЖЕТСЯ запускать бинарный файл (Denied). Разрешил SELinux запуск или отклонил — можно смотреть в logcat. Однако, в системе существует системная утилита, которая может запустить бинарник, а самое главное — SELinux РАЗРЕШИТ ей запуститься, так как она системная. Её имя linker или linker64 (Зависит от разрядности, 32 бита или 64).
Запустив линкер и передав ему наш бинарный файл из data/data — SELinux разрешит ему выполниться и сразу разрешит исполнение нашего бинарного файла. Тут сразу возникает вопрос: а если бинарный файл попробует подключить so библиотеку? Ей будет отказано? Здесь тоже есть решение: существует termux‑exec. Это бинарный файл, который перехватывает попытку подключения чего‑либо и выполняет трюк с линкером. (linker или linker64 определяет автоматически). Вы можете собрать его из исходников (https://github.com/termux‑play‑store/termux‑exec), но лично я полностью скопировал среду (Где этот уже собранный файл идёт в usr/lib) termux, так как мне нужно было запускать OpenJDK 17 под termux.
Пишем сам метод.
// Устаналиваем пути File appFilesDir = context.getFilesDir(); // Путь к data/data/пакет/files/, нужен контекст, передайте его File usrDir = new File(appFilesDir, "usr"); // Путь к data/data/пакет/files/usr/ File javaExecutable = new File(appFilesDir, "usr/bin/java"); // ЗАМЕНИТЕ НА ВАШ БИНАРНИК // Создаём окружение чтобы бинарник для термукса чувствовал себя как дома Map<String, String> envMap = new HashMap<>(); // Сам дом envMap.put("HOME", new File(appFilesDir, "home").getAbsolutePath()); // Папка пользователя envMap.put("PREFIX", usrDir.getAbsolutePath()); // Временнаяя папка envMap.put("TMPDIR", new File(appFilesDir, "tmp").getAbsolutePath()); // Путь к бинарникам envMap.put("PATH", new File(usrDir, "bin").getAbsolutePath() + ":" + System.getenv("PATH")); // Папка для динамичкских библиотек File libDir = new File(usrDir, "lib"); envMap.put("LD_LIBRARY_PATH", libDir.getAbsolutePath()); // Наш перехватчик (Его нужно собрать либо взять пряом из термукса) File termuxExecSo = new File(libDir, "libtermux-exec.so"); envMap.put("LD_PRELOAD", termuxExecSo.getAbsolutePath()); // Системные переменные которые ВОЗМОЖНО будут нужны бинарнику, лучше их задать String[] systemVarsToCopy = { "ANDROID_ART_ROOT", "ANDROID_ASSETS", "ANDROID_DATA", "ANDROID_I18N_ROOT", "ANDROID_ROOT", "ANDROID_RUNTIME_ROOT", "ANDROID_STORAGE", "ANDROID_TZDATA_ROOT", "BOOTCLASSPATH", "DEX2OATBOOTCLASSPATH", "EXTERNAL_STORAGE", "SYSTEMSERVERCLASSPATH" }; for (String var : systemVarsToCopy) { String value = System.getenv(var); if (value != null) { envMap.put(var, value); } } // Формируем команду List<String> commandList = new ArrayList<>(); // Определяем, нам нужен linker или linker64 (Это зависит от архитектуры процессора) String linkerPath = "/system/bin/linker" + (android.os.Process.is64Bit() ? "64" : ""); // Заполняем саму команду commandList.add(linkerPath); // Запускаем линкер, а не бинарник. Это самый важный этап commandList.add(javaExecutable.getAbsolutePath()); // Ваш бинарник, у меня это java из OpenJDK commandList.add("-version"); // Ваши аргументы, у меня всего 1 аргумент чтобы узнать версию
Всё! Мы построили команду для запуска на новых targert sdk. Теперь осталось запустить этот процесс
Пример запуска (В try‑catch), если у вас простой бинарник — вам достаточно этого. Для более сложных сделайте получение вывода. Информация об этом есть в интернете.
// Создаём ProcessBuilder с нашей командой для линкера ProcessBuilder pb = new ProcessBuilder(commandList); // Устаналиваем окружение pb.environment().putAll(envMap); // Запускаем и ждём завершения pb.start().waitFor();
Дальше идёт обычная работа с процессами, никакой «Магии» дальше нет. Вывод вы можете организовать самостоятельно, главное — проверить, что бинарный файл запущен.
Вот, что мы видим в logcat:
Когда я добавил вывод — я получил вывод версии OpenJDK. Пользуйтесь. Работает даже на target sdk 36. Сам вывод никак не относится к теме статьи, она рассказывает про запуск бинарных файлов из директорий, куда можно записывать на новых версиях sdk.
ссылка на оригинал статьи https://habr.com/ru/articles/943188/
Добавить комментарий