Найти вручную зарегистрированный (запутанный) адрес собственной функции

Я пытаюсь понять приложение для Android, которое содержит собственный метод с именем foo в классе com.app.Bar

Внутри класса Bar есть статическое предложение, которое загружает общий объект System.loadLibrary("libfoo.so") я предполагаю, что это сборка с -fvisibility = hidden, потому что единственный экспорт JNI_OnLoad нет JNIEXPORT void JNICALL Java_com_app_Bar_foo что значит public native int foo не следует соглашению об именах.

  1. Каков процесс, когда foo вызывается?
  2. Как я могу извлечь адрес foo? Я знаком с Фридой
  3. Есть ли способ вывести все JNINativeMethod[] methods?

Что я пробовал до сих пор?

  1. JNIAnalyzer выводит 13K строк, которые выглядят как JNI_OnLoad@@Base+0x712e

  2. Фрида скрипт, чтобы попытаться найти foo адрес (не работал)

function intercept(address) {
    try {
        Interceptor.attach(address, {
            onEnter: function(args) {
                console.log("onEnter", address);
            },
            onLeave: function(ignored) {}
        });
    } catch (e) {
        console.error(e);
    }
}
function Main() {
    var dlopen = new NativeFunction(Module.findExportByName(null, 'dlopen'), 'pointer', ['pointer', 'int']);
    var dlsym = new NativeFunction(Module.findExportByName(null, 'dlsym'), 'pointer', ['pointer', 'pointer']);
    Process.enumerateModulesSync().forEach(function(m) {
        if (m.name === "libfoo.so") {
            console.log("Module", JSON.stringify(m));
            var handle = dlopen(Memory.allocUtf8String(m.path), 1);
            var symb = Memory.allocUtf8String("foo");
            var exports = Module.enumerateExportsSync(m.name);
            console.log(JSON.stringify({
                handle: handle,
                symb: symb,
                dlsym: dlsym(handle, symb),
                exports: exports.map(function(ex){ return ex.address + ": " + ex.name })
            }, null, 2));
            // intercept all exports
            exports.forEach(function(ex){
                intercept(ex.address);
            });
            // explicit intercept foo by known offset
            intercept(m.base.add(0x22334)); // this outputs "Error: unable to intercept function at 0x86c96328; please file a bug"
        }
    });

    console.log("sleep..");
    Thread.sleep(1.5);
    console.log("invoke", Java.use('com.clazz.foo').signToken("A".repeat(32)));
}
Java.perform(Main);

2 ответа

Решение

Я решил это с помощью Frida

Зацепление art::JNI::RegisterNativeMethods(_JNIEnv*, _jclass*, JNINativeMethod const*, int, bool) а также art::JNI::FindClass после libart.so модуль загружен.

Код

var fIntercepted = false;

function revealNativeMethods() {
    if (fIntercepted === true) {
        return;
    }
    var jclassAddress2NameMap = {};
    var androidRunTimeSharedLibrary = "libart.so"; // may change between devices
    Module.enumerateSymbolsSync(androidRunTimeSharedLibrary).forEach(function(symbol){
        switch (symbol.name) {
            case "_ZN3art3JNI21RegisterNativeMethodsEP7_JNIEnvP7_jclassPK15JNINativeMethodib":
                /*
                    $ c++filt "_ZN3art3JNI21RegisterNativeMethodsEP7_JNIEnvP7_jclassPK15JNINativeMethodib"
                    art::JNI::RegisterNativeMethods(_JNIEnv*, _jclass*, JNINativeMethod const*, int, bool)
                */
                var RegisterNativeMethodsPtr = symbol.address;
                console.log("RegisterNativeMethods is at " + RegisterNativeMethodsPtr);
                Interceptor.attach(RegisterNativeMethodsPtr, {
                    onEnter: function(args) {
                        var methodsPtr = ptr(args[2]);
                        var methodCount = parseInt(args[3]);
                        for (var i = 0; i < methodCount; i++) {
                            var pSize = Process.pointerSize;
                            /*
                                https://android.googlesource.com/platform/libnativehelper/+/master/include_jni/jni.h#129
                                typedef struct {
                                    const char* name;
                                    const char* signature;
                                    void* fnPtr;
                                } JNINativeMethod;
                            */
                            var structSize = pSize * 3; // JNINativeMethod contains 3 pointers
                            var namePtr = Memory.readPointer(methodsPtr.add(i * structSize));
                            var sigPtr = Memory.readPointer(methodsPtr.add(i * structSize + pSize));
                            var fnPtrPtr = Memory.readPointer(methodsPtr.add(i * structSize + (pSize * 2)));
                            // output schema: className#methodName(arguments)returnVal@address
                            console.log(
                                // package & class, replacing forward slash with dot for convenience
                                jclassAddress2NameMap[args[0]].replace(/\//g, '.') +
                                '#' + Memory.readCString(namePtr) + // method
                                Memory.readCString(sigPtr) + // signature (arguments & return type)
                                '@' + fnPtrPtr // C side address
                            );
                        }
                    },
                    onLeave: function (ignoredReturnValue) {}
                });
                break;
            case "_ZN3art3JNI9FindClassEP7_JNIEnvPKc": // art::JNI::FindClass
                Interceptor.attach(symbol.address, {
                    onEnter: function(args) {
                        if (args[1] != null) {
                            jclassAddress2NameMap[args[0]] = Memory.readCString(args[1]);
                        }
                    },
                    onLeave: function (ignoredReturnValue) {}
                });
                break;
        }
    });
    fIntercepted = true;
}

Java.perform(revealNativeMethods);

я предполагаю, что это сборка с -fvisibility=hidden, потому что единственным экспортом является JNI_OnLoad, нет JNIEXPORT void JNICALL Java_com_app_Bar_foo, что означает, что public native int foo не соответствует соглашению об именах.

Это не всегда так, потому что вы также можете использовать RegisterNatives внутри JNI_OnLoad объявить ваши методы JNI, не придерживаясь типичного соглашения об именах.

/*
 * Register several native methods for one class.
 */
static int registerNativeMethods(JNIEnv* env, const char* className,
                                 JNINativeMethod* gMethods, int numMethods)
{
    jclass clazz;
    clazz = env->FindClass(className);
    if (clazz == NULL) {
        ALOGE("Native registration unable to find class '%s'", className);
        return JNI_FALSE;
    }
    if (env->RegisterNatives(clazz, gMethods, numMethods) < 0) {
        ALOGE("RegisterNatives failed for '%s'", className);
        return JNI_FALSE;
    }
    return JNI_TRUE;
}

Что происходит, когда вызывается foo?

Может быть, вы можете взглянуть на эту статью https://arophix.com/2017/12/17/andoid-jni-summary/

Как я могу извлечь адрес Foo? (Я знакома с Фридой)

Я не уверен насчет Frida, для Cydia Substrate вы можете использовать dlsym найти адрес указанного символа .soнапример,

void* find_symbol(const char* libraryname, const char* symbolname)
{
    void *imagehandle = dlopen(libraryname, RTLD_GLOBAL | RTLD_NOW);
    if (imagehandle != NULL) {
        void * sym = dlsym(imagehandle, symbolname);
        if (sym != NULL) {
            LOGE("symbol (%s) is found at address %p (%p) in lib %s", symbolname, sym, &sym, libraryname);
            return sym;
        } else {
            LOGE("find_symbol() can't find symblo (%s).", symbolname);
            return NULL;
        }
    } else {
        LOGE("dlopen error: %s, when opening lib %s",dlerror(), libraryname);
        return NULL;
    }
}

Вот полное руководство по использованию Cydia Substrate https://arophix.com/2017/11/30/android-substrate-hooking/,

-------- Обновления --------

Для Фриды, может быть, это похоже. Вот ссылка, которая может помочь в вашем случае. https://www.notsosecure.com/instrumenting-native-android-functions-using-frida/

Другие вопросы по тегам