查找手动注册(混淆)的本机功能地址

时间:2018-08-12 17:44:00

标签: android java-native-interface reverse-engineering frida

我正在尝试了解一个Android应用,该应用在类foo中包含名为com.app.Bar的本地方法

在类Bar中,有一个静态子句加载共享对象System.loadLibrary("libfoo.so"),我认为它是用-fvisibility=hidden构建的,因为唯一的导出是JNI_OnLoad,没有{{ 1}},这意味着JNIEXPORT void JNICALL Java_com_app_Bar_foo不跟随naming convention

  1. public native int fooinvoked时的处理过程是什么?
  2. 如何提取foo的地址?我熟悉Frida
  3. 是否有一种输出所有foo的方法?

到目前为止,我尝试了什么?

  1. JNIAnalyzer输出1万3千行,看起来像JNINativeMethod[] methods

  2. Frida脚本尝试查找JNI_OnLoad@@Base+0x712e地址(没有用)

foo

2 个答案:

答案 0 :(得分:3)

  

我假设使用-fvisibility = hidden构建,因为唯一的导出是JNI_OnLoad,没有JNIEXPORT void JNICALL Java_com_app_Bar_foo,这意味着公共本地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很熟悉)

我不确定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/的完整教程,

--------更新--------

编辑#1

对于Frida,也许是相似的。这是一个参考链接,可能会对您的情况有所帮助。 https://www.notsosecure.com/instrumenting-native-android-functions-using-frida/

编辑#2

有关基于Frida的钩子的完整的Github教程项目,请参阅Using Apktool and Frida to do reverse engineering on Android Apk.

答案 1 :(得分:1)

我已经使用Frida

解决了它

在加载art::JNI::RegisterNativeMethods(_JNIEnv*, _jclass*, JNINativeMethod const*, int, bool)模块之后钩住art::JNI::FindClasslibart.so

Code

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);