frida 得到.init .init_array等函数地址

前言

移动端Android安全的发展,催生了各种Android加固的诞生,基于ELF文件的特性,很多的加固厂商在进行Android逆向的对抗的时,都会在Android的so文件中进行动态的对抗,对抗的点一般在so文件的.init段和JNI_OnLoad处。因此,我们在逆向分析各种厂商的加固so时,需要在so文件的.init段和JNI_OnLoad处下断点进行分析,过掉这些加固的so对抗。

今天就来使用如何在这些段上下断点,和hook init段的函数

实验

先来看看so库加载时的执行顺序


//动态注册时都会执行到这个方法中
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
    JNIEnv *env = NULL;//定义JNI Env
    jint result = -1;
    /*JavaVM::GetEnv 原型为 jint (*GetEnv)(JavaVM*, void**, jint);
     */
    if (vm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_6) != JNI_OK) {
        return result;
    }
    __android_log_print(ANDROID_LOG_ERROR, "SharkChilli", "JNI_OnLoad");

    result = JNI_VERSION_1_6;
    return result;
}

//编译生成后在.init段
extern "C" void _init(void) {
    __android_log_print(ANDROID_LOG_ERROR, "SharkChilli", "_init");

}

//编译生成后在.init_array段
__attribute__((constructor)) void myinitarray(void) {
    __android_log_print(ANDROID_LOG_ERROR, "SharkChilli",
                        "myinitarray __attribute__((constructor))");
}

__attribute__((constructor)) void myinitarray2(void) {
    __android_log_print(ANDROID_LOG_ERROR, "SharkChilli",
                        "myinitarray2 __attribute__((constructor))");
}

class Test {
public:
    Test() {
        __android_log_print(ANDROID_LOG_ERROR, "SharkChilli",
                            "Test Test()");
    }
};
Test  objTest;

运行后日志如下

2020-04-23 10:53:59.776 24392-24392/com.shark.initapp E/SharkChilli: _init
2020-04-23 10:53:59.776 24392-24392/com.shark.initapp E/SharkChilli: myinitarray __attribute__((constructor))
2020-04-23 10:53:59.776 24392-24392/com.shark.initapp E/SharkChilli: myinitarray2 __attribute__((constructor))
2020-04-23 10:53:59.776 24392-24392/com.shark.initapp E/SharkChilli: Test Test()
2020-04-23 10:53:59.777 24392-24392/com.shark.initapp E/SharkChilli: JNI_OnLoad

由此可以知道上面的执行顺序依次是init>init_array>c++全局对象初始化>JNI_OnLoad

frida Hook

读者需要阅读上一章android loadLibrary源码分析,从上一章可以知道在Android4中我们需要Hook CallFunction函数,在Android8中我们需要Hook call_function函数来得到.init .init_array等函数等相关信息

我们以Android8为例来演示

我们先将上面的代码以32位模式运行在手机上,这里为了方便在32位上。

adb install -t --abi armeabi-v7a .\app-debug.apk

由于该函数没有导出我们无法直接获得。
现在有一个问题我们如何得到call_function的地址? 这里有两种方式(其实殊途同归)。
先将linker导出

adb pull /system/bin/linker
  1. 使用IDA打开linker,在IDA中查找符号call_function
    image.png

    通过demangler可以知道我们需要Hook的函数为_dl__ZL13call_functionPKcPFviPPcS2_ES0,它的偏移地址为0x00013554,在frida中我们得到linker的基址加上这个偏移就是它在内存中的地址了。

2.使用frida 查询导出符号中call_function,代码如下

var linker = Process.getModuleByName("linker");


    var call_function_addr = null;
    var exports = linker.enumerateSymbols();

    for (var m = 0; m < exports.length; m++) {
        // console.log(exports[m].name);
        if (exports[m].name.indexOf("__dl__ZL13call_functionPKcPFviPPcS2_ES0_") >= 0) {
            call_function_addr = exports[m].address;
            //得到call_function偏移地址 因为我们运行在32位上所以需要-1
            console.log("call_function offset:", ptr(call_function_addr).sub(linker.base).sub(0x1));
        }
    }
Spawning `com.shark.initapp`...
call_function offset: 0x13554
Spawned `com.shark.initapp`. Resuming main thread!

可以看到这个和IDA中分析到的是一样的。

最后我们使用这个地址去Hook并打印相关参数即可

function hook_call_function() {
    var linker = Process.getModuleByName("linker");


    var call_function_addr = null;
    var exports = linker.enumerateSymbols();

    for (var m = 0; m < exports.length; m++) {
        // console.log(exports[m].name);
        if (exports[m].name.indexOf("__dl__ZL13call_functionPKcPFviPPcS2_ES0_") >= 0) {
            call_function_addr = exports[m].address;
            //得到call_function偏移地址 因为我们运行在32位上所以需要-1
            console.log("call_function offset:", ptr(call_function_addr).sub(linker.base).sub(0x1));
            // console.log("find call_function_addr:", exports[m].name, call_function_addr);
            hook_func(call_function_addr);
        }
    }

}

function hook_func(call_address) {
    //不能在这得到libnative-lib.so的基地址,因为这个时候还没加载进来

    Interceptor.attach(call_address, {
        onEnter: function (args) {
            /**
             * 参数一:函数名称
             * 参数二:执行函数地址
             * 参数三:函数所在so库
             */
            //过滤一下 我们只关注libnative-lib.so中的函数
            if (args[2].readCString().indexOf("libnative-lib.so") >= 0) {
                var mod_address = Module.findBaseAddress("libnative-lib.so");
                console.log("[function name:", args[0].readCString());
                //计算出函数在libnative-lib.so中的偏移地址 因为我们运行在32位上所以需要-1
                console.log("function offset:", ptr(args[1]).sub(mod_address).sub(0x1),"]");
            }


        }, onLeave: function (ret) {
            // console.log("ret:", ret);
        }
    });
}

setImmediate(hook_call_function);

打印结果如下

[function name: DT_INIT
function offset: 0x12cc0 ]

[function name: function
function offset: 0x12cdc ]

[function name: function
function offset: 0x12cf8 ]

[function name: function
function offset: 0x12d28 ]

我们使用IDA打开libnative-lib.so查询相关函数


image.png

其中还有一个就是c++全局对象初始化


image.png

由此我们也可以知道c++全局对象初始化的构造函数也是在.init_array段中

ok到这就拿到了我们想要的函数地址了,至于后面如何使用就看读者自己了

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容