Redirection des fonctionnalités dans les bibliothèques natives Android

Dans cet article, je vais parler un peu de la façon dont vous pouvez rediriger les appels de fonction vers des bibliothèques natives à l'aide du cadre AndHook . Vous pouvez intercepter les appels publics (fonctions exportées) et non publics, directement à leur adresse. Vous pouvez en savoir plus sur la redirection ici et sur la page du framework.


A titre d'exemple, nous considérerons le cas de l'implémentation de sa bibliothèque. Cependant, ce cadre vous permet également de travailler sans reconstruire l'application à l'aide de xposed.


Dans cet article, j'utiliserai Visual Studio et BatchApkTool pour Windows. Au lieu de Visual Studio, vous pouvez utiliser Android Studio, ou même le compiler via gcc ou clang (une option pour avancé). Au lieu de BatchApkTool, vous pouvez utiliser apktool. Je ne m'arrêterai pas en détail sur le travail avec BatchApkTool, car Il n'est pas difficile de travailler avec le programme.


Vous devez d'abord analyser l'APK à l'aide de BatchApkTool ou apktool.


Téléchargez maintenant la bibliothèque et le fichier d'en-tête depuis la page du projet, pour cela vous devez suivre ce lien et ce lien et télécharger la bibliothèque pour votre plateforme. (Je n'ai pas trouvé la différence entre la version de Compat et la version habituelle, j'ai personnellement utilisé la version normale. Peut-être qu'ils le diront dans les commentaires)


Configuration de Visual Studio pour fonctionner avec NDK

Visual Studio Installer :



.


Création d'un nouveau projet dans Visual Studio

, , , .


" (Android)", .



:

. : , : < >, API 14 ARM X86, ARM64 X64 21 .
.
-> , :




, , , , .


. .



. .



, .cpp


#include "AndHook.h"

Créons la fonction JNI_OnLoad, dans laquelle nous enregistrerons nos hooks.
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved)
{
    JNIEnv* env = NULL;
    jint result = -1;
    LOGD("JNI_OnLoad start!");

    if (vm->GetEnv((void**)&env, JNI_VERSION_1_4) != JNI_OK) {
        LOGE("JNI_OnLoad! GetEnv failed");
        return -1;
    }

    RegisterHooks();

    result = JNI_VERSION_1_4;
    LOGD("JNI_OnLoad! finished!");
    return result;
}

Les RegisterHooks fonctionnent eux-mêmes:


//       #define
#ifdef __arm__
#define Test_Offset 0x1234
#elif __aarch64__
#define Test_Offset 0x1235
#elif __i386__
#define Test_Offset 0x1236
#elif __x86_64__
#define Test_Offset 0x1237
#endif
void (*Old_Test)();
void Test()
{
    LOGD("Test!");
    // Do something
    Old_Test();
}
void (*Old_Test2)();
void Test2()
{
    LOGD("Test2!");
    Old_Test2(); //      Test2
}
void RegisterHooks() {
    const void* libil2cpp = AKGetImageByName("libil2cpp.so"); //     .
    if (libil2cpp == NULL) {
        LOGW("AKGetImageByName return null!");
        return;
    }

    //      
    //  -     IDA/Ghidra
    //    il2cpp-     https://github.com/Perfare/Il2CppDumper
    AKHookFunction(AKFindAnonymity(libil2cpp, Test_Offset), reinterpret_cast<void*>(&Test), reinterpret_cast<void**>(&Old_Test));
    //       Test_Offset   libil2cpp       Test,  .       Old_Test.

    //   
    AKHookFunction(AKFindSymbol(libil2cpp, "Test2"), reinterpret_cast<void*>(&Test2), reinterpret_cast<void**>(&Old_Test2));

    //         :
    void (*Test3)();
    Test3 = reinterpret_cast<void (*)()>(AKFindAnonymity(libil2cpp, 0x12345));
    Test3();

    //      
    const uint8_t data[] = { 0x00, 0xF0, 0x20, 0xE3 }; //  NOP
    AKPatchMemory(reinterpret_cast<const void*>(AKFindAnonymity(libil2cpp, 0x123456)), reinterpret_cast<const void*>(&data), 4);
    //       0x123456

    AKCloseImage(libil2cpp);
}

Aussi pour patcher sous plusieurs architectures, il est pratique d'utiliser #define


Exemple
#ifdef __arm__
#define Patch_Offset 0x1234
#define Patch_Data { 0x00, 0xF0, 0x20, 0xE3 }
#elif __aarch64__
#define Patch_Offset 0x1234
#define Patch_Data { 0x1F, 0x20, 0x03, 0xD5 }
#endif

const uint8_t data[] = Patch_Data;
const size_t len = sizeof(data) / sizeof(uint8_t);
AKPatchMemory(reinterpret_cast<const void*>(AKFindAnonymity(libil2cpp, Patch_Offset)), reinterpret_cast<const void*>(&data), len);

Une fois que nous avons fini d'écrire le code, il doit être compilé.
Pour ce faire, vous avez besoin de:


Choisissez la plateforme cible


Assembler le projet


Si plusieurs plates-formes répètent les étapes 1-2.


AndHook lib . ( ).


. MainActivity . onCreate MainActivity :


const-string v0, "SharedObject1"
invoke-static {v0}, Ljava/lang/System;->loadLibrary(Ljava/lang/String;)V

SharedObject1 lib .so
il2cpp- MainActivity onCreate smali/com/unity3d/player/UnityPlayerActivity.smali.


.


:
https://github.com/asLody/AndHook/
http://armconverter.com/
http://armconverter.com/hextoarm/


All Articles