Skip to content

Latest commit

 

History

History
248 lines (200 loc) · 9.83 KB

README_RU.md

File metadata and controls

248 lines (200 loc) · 9.83 KB

Pygmalion

English | Русский

pygmalion это автономная non-root android библиотека, необходимая для кастомизации UI android-приложений посредством создания хуков для AssetManager и Activity

Поддерживаются устройства с Android 5.0 - Android 15+ и x86, x86_64, arm32, и arm64 архитектурами.

Caution

Я и контрибьютеры не несём ответственности за любой причинённый вред, вызванный использованием этой библиотеки в ваших проектах. Также этот проект находится в альфа-стадии, поэтому обратная совместимость с будущими обновлениями не гарантируется.

Мотивация

Изначально это была странная идея создания средства для кастомизации приложений без использования root, xposed и т.п., и я не планировал использовать натив. Но, как оказалось, это был единственный нормальный способ осуществить мою задумку перехвата вызовов AssetManager. Помимо этого я не хочу использовать какой-нибудь известный ART hooking framework из-за его нестабильности по сравнению с моим методом.

Описание проекта

pygmalion нужен для тех случаев, когда требуется реализовать кастомизацию приложений (пр. изменение акцентных цветов) без исходного кода. Чтобы использовать эту библиотеку, нужно знать про комплексные и примитивные (хранящие простые значения) ресурсы (см. App resources overview). Также вы можете ознакомиться с исходным кодом AssetManager, Resources, TypedValue и TypedArray для определённых платформ.

Принцип работы

pygmalion работает на двух уровнях:

  • Java-уровень предоставляет API для создания хуков для AssetManager и Activity.
  • Нативный уровень используется для перехвата собственных нативных функций AssetManager, связанных с ресурсами, путем перерегистрации оных, для дальнейших манипуляций с извлеченными данными (идентификаторами атрибутов, значениями ресурсов и т. д.) до того, как они будут возвращены в точку вызова.

На нативном уровне pygmalion перехватывает функцию, которая используется для регистрации всех JNI функций AssetManager c сигнатурой

_ZN7android37register_android_content_AssetManagerEP7_JNIEnv

Затем целевые функции находятся из дампа по жестко-закодированным именам и сигнатурам и заменяются на функции-хуки, которые вызывают Java-функции с реализацией логики. Этот способ более стабильный, чем многие ART hooking фреймфорки, потому что там используется встроенный JNI API и проверенные хукинг-библиотеки.

Note

Для Android N pygmalion отключает @FastNative оптимизации для всех функций AssetManager, потому что это приводит к сбоям.

TODO

  • Добавить хуки для LayoutInflater

Ограничения

  • Вы можете хукать только AssetManager и Activity.
  • В хуках AssetManager вы не можете добавлять/удалять атрибуты

Подпроекты:

  • library - "ядро" pygmalion со всеми API нужными для разработки.
  • demo - android-приложение, разработанное как "галерея" для демонстрации.
  • stub - обеспечивает прямой доступ к приватным android api.

Юзкейсы

Я настоятельно рекомендую сто раз подумать, прежде чем использовать эту библиотеку в своих проектах. Она изначально предназначена не для нормальной android-разработки, а для случаев когда нет доступа к исходному коду. Например, для разработки модификаций android-приложений таких как Revanced's mods, VTLIte и т.д.

Интеграция

build.gradle:

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar'])
}

build.gradle.kts:

dependencies {
    implementation(
        fileTree("libs") {
            include("*.jar", "*.aar")
        }
    )
}

Разработка

Инициализация

pygmalion имеет API для ручного управления:

/*
* проверяем проинициализировался ли Pygmalion
* и включаем хукинг 
*/
Pygmalion.initialize()
if(Pygmalion.isInitialized()) {
    Pyhmalion.hook()
}
//отключаем хукинг
Pygmalion.unhook()

Hooks

Добавление/удаление хуков для AssetManager:

Kotlin:

@JvmField
val exampleHook =
    assetHook {
        onLoadResourceValue { asset ->
            //etc
        }
        onLoadThemeAttrValue { asset ->
            //etc
        }
        onApplyStyle { assets ->
            //etc
        }
        onResolveAttrs { assets ->
            //etc
        }
        onRetrieveAttrs { assets ->
            //etc
        }
    }

fun hook() {
    //Добавляем хуки для AssetManager
    Pygmalion.registerAssetHooks(exampleHook,/*...*/)
}

fun unhook() {
    //Удаляем хуки для AssetManager
    Pygmalion.unregisterAssetHooks(exampleHook)
}

Java:

public class HooksExample {
    private static IAssetHook hook = new IAssetHook() {
        @Override
        public void onLoadResourceValueHook(int resId, @NotNull TypedValue outValue, boolean resolveAttrs) {
            //etc
        }

        @Override
        public void onLoadThemeAttrValueHook(int resId, @NotNull TypedValue outValue, boolean resolveAttrs) {
            //etc
        }

        @Override
        public void onApplyStyleHook(int defStyleAttr, int defStyleRes, @NotNull int[] inAttrs, @NotNull List<TypedValue> outValues) {
            //etc
        }

        @Override
        public void onResolveAttrsHook(int defStyleAttr, int defStyleRes, @NotNull int[] inValues, @NotNull int[] inAttrs, @NotNull List<TypedValue> outValues) {
            //etc
        }

        @Override
        public void onRetrieveAttrsHook(@NotNull int[] inAttrs, @NotNull List<TypedValue> outValues) {
            //etc
        }
    };

    public static void hook() {
        //Добавляем хуки для AssetManager
        Pygmalion.registerAssetHooks(hook,hook);
    }
    
    public static void unhook() {
        //Удаляем хуки для AssetManager
        Pygmalion.unregisterAssetHooks(hook,hook);
    }
}

Add/remove Activity hooks:

Kotlin:

@JvmField
val exampleHook = activityLifecycleCallbacks {
    onActivityCreated { activity, bundle : Bundle? ->
        //etc
    }
    
    onActivityResumed { activity ->
        //etc
    }
    
    //etc
}

fun hook() {
    //Добавляем хуки для Activity
    Pygmalion.registerActivityLifecycleCallbacks(exampleHook,/*...*/)
}

fun unhook() {
    //Удаляем хуки для Activity
    Pygmalion.unregisterActivityLifecycleCallbacks(exampleHook,/*...*/)
}

Java:

public class HooksExample {
    private static IActivityLifecycleCallback hook = new IActivityLifecycleCallback() {
        @Override
        public void onActivityCreated(@NotNull Activity activity, @Nullable Bundle savedInstanceState) {
            //etc
        }

        @Override
        public void onActivityResumed(@NotNull Activity activity) {
            //etc
        }
        
        //etc
    };

    public static void hook() {
        //Добавляем хуки для Activity
        Pygmalion.registerActivityLifecycleCallbacks(hook,hook);
    }

    public static void unhook() {
        //Удаляем хуки для Activity
        Pygmalion.unregisterActivityLifecycleCallbacks(hook,hook);
    }
}