首页
社区
课程
招聘
[原创] Frida hook Java/Native与init_array 自吐最终方案
发表于: 2021-5-10 14:39 20530

[原创] Frida hook Java/Native与init_array 自吐最终方案

2021-5-10 14:39
20530

题目来自看雪2020春季班10月份第一题和第三题。这两个题目本身难度不高,但是确实考察对源码的理解,同时也不失为一个frida的案例。

HttpURLConnection的开发过程中,设置参数时会用到以下方法。分析并hook上这些方法,写出一两个参数的自吐。

具体在测试时可以自己写一个简单的demo并在APP中显示调用如下函数

测试app功能正常后,用Objection直接把以下两个类全都hook上:java.net.URLConnection以及java.net.HttpURLConnection

结果如图发现反复调用这几个函数,其他函数都没有调用:

怀疑底层是否真实使用这些类实现的,找到对应源码,发现其实HttpURLConnection类也是一个抽象类,就是说在Java中实际上是其Impl类去实现:

使用WallBreaker去搜索HttpURLConnection会发现确实有一个类com.android.okhttp.internal.huc.HttpURLConnectionImpl

去源码里搜了搜发现,确实这个类实现了这个函数

源码看HttpURLConnectionImpl

晕死,结果这个类也是okHttp做的底层???先hook一下这个类

会发现这个实现类确实实现了之前没有hook到的三个函数

那么最终脚本就直接写就是了

最终效果如下

在课时⑨中给出的init array的自吐 hook_linker,32位版本解决了,请分析64位版本,尝试给出解决方案并解决。

以8.1为例,查看源码后会发现

dlopen调用过程中最终是

目录/bionic/linker/linker_soinfo.cpp


这个函数是模版函数,把linker拷到IDA上一看,会发现这个函数是唯一的,且参数是4个,同时会发现64位的linker中是没有call_function这个函数的,这个函数被优化成了代码片段,插进call_constructors函数和call_array函数中了。
最终模仿这个函数直接写一个js版本就行了关键的代码如下

但是这样只是hooksoinit_array节中函数,还存在.init_proc的构造函数并未hook,和32位一样本来是继续去hook call_function函数,在脱出/system/lib64/libart.so后,发现call_function这个symbol无法找到,观察下图发现这个函数被inline了。

但是仔细观察.init_proc.init_array函数调用前后,都会有一个log的判断,直接去hook这个_dl_async_safe_format_log函数吧

但是首先得_dl_g_ld_debug_verbosity这个值大于等于2这个函数才会执行,那么先使用frida去这个变量的地址,然后修改这个变量的值使其达到_dl_async_safe_format_log函数会执行的条件即可。

最终frida关键代码如下:

最终效果如下,

64位:

32位效果:

同样的完整代码已经上传github,看这里

一切答案都在源码中。

// URLConnection
httpUrlConnection.setDoOutput(true);
httpUrlConnection.setDoInput(true);
HttpUrlConnection.setUseCaches(false);
httpUrlConnection.setRequestProperty("Content-type", "application/x-java-serialized-object");
httpUrlConnection.connect();
// HttpURLConnection
httpUrlConnection.setRequestMethod("POST");
// URLConnection
httpUrlConnection.setDoOutput(true);
httpUrlConnection.setDoInput(true);
HttpUrlConnection.setUseCaches(false);
httpUrlConnection.setRequestProperty("Content-type", "application/x-java-serialized-object");
httpUrlConnection.connect();
// HttpURLConnection
httpUrlConnection.setRequestMethod("POST");
 
 
 
 
 
 
 
 
 
 
 
 
 
 
function hook_java(){
    Java.perform(function(){
        // httpUrlConnection.setDoOutput(true);
        // httpUrlConnection.setDoInput(true);
        // HttpUrlConnection.setUseCaches(false);
        var URLConnection = Java.use("java.net.URLConnection")
        URLConnection.setDoOutput.implementation = function(isOutput){
            console.log("URLConnection.setDoOutput : ",isOutput);
            return this.setDoOutput(isOutput);
        }
        URLConnection.setDoInput.implementation = function(isInput){
            console.log("URLConnection.setDoInput : ",isInput);
            return this.setDoInput(isInput);
        }
        URLConnection.setUseCaches.implementation = function(isUseCaches){
            console.log("URLConnection.setUseCaches : ",isUseCaches);
            return this.setUseCaches(isUseCaches);
        }
        // com.android.okhttp.internal.huc.HttpURLConnectionImpl
        var HttpURLConnectionImpl = Java.use("com.android.okhttp.internal.huc.HttpURLConnectionImpl");
        HttpURLConnectionImpl.setRequestProperty.implementation = function(name,value){
            console.log("HttpURLConnectionImpl.setRequestProperty => ",name,": ",value);
            return this.setRequestProperty(name,value);
        }
        HttpURLConnectionImpl.setRequestMethod.implementation = function(type){
            console.log("HttpURLConnectionImpl.setRequestMethod : ",type);
            return this.setRequestMethod(type);
        }
        HttpURLConnectionImpl.connect.implementation = function(){
            console.log("HttpURLConnectionImpl.connect");
            return this.connect();
        }
 
    });
}
 
function main(){
    hook_java();
}
setImmediate(main);
function hook_java(){
    Java.perform(function(){
        // httpUrlConnection.setDoOutput(true);
        // httpUrlConnection.setDoInput(true);
        // HttpUrlConnection.setUseCaches(false);
        var URLConnection = Java.use("java.net.URLConnection")
        URLConnection.setDoOutput.implementation = function(isOutput){
            console.log("URLConnection.setDoOutput : ",isOutput);
            return this.setDoOutput(isOutput);
        }
        URLConnection.setDoInput.implementation = function(isInput){
            console.log("URLConnection.setDoInput : ",isInput);
            return this.setDoInput(isInput);
        }
        URLConnection.setUseCaches.implementation = function(isUseCaches){
            console.log("URLConnection.setUseCaches : ",isUseCaches);
            return this.setUseCaches(isUseCaches);
        }
        // com.android.okhttp.internal.huc.HttpURLConnectionImpl
        var HttpURLConnectionImpl = Java.use("com.android.okhttp.internal.huc.HttpURLConnectionImpl");
        HttpURLConnectionImpl.setRequestProperty.implementation = function(name,value){
            console.log("HttpURLConnectionImpl.setRequestProperty => ",name,": ",value);
            return this.setRequestProperty(name,value);
        }
        HttpURLConnectionImpl.setRequestMethod.implementation = function(type){
            console.log("HttpURLConnectionImpl.setRequestMethod : ",type);
            return this.setRequestMethod(type);
        }
        HttpURLConnectionImpl.connect.implementation = function(){
            console.log("HttpURLConnectionImpl.connect");
            return this.connect();
        }
 
    });
}
 
function main(){
    hook_java();
}
setImmediate(main);
 
 
 
soinfo::call_constructors()
    call_function("DT_INIT", init_func_, get_realpath());
    call_array("DT_INIT_ARRAY", init_array_, init_array_count_, false, get_realpath());
            ------>循环调用了 call_function("function", functions[i], realpath);
soinfo::call_constructors()
    call_function("DT_INIT", init_func_, get_realpath());
    call_array("DT_INIT_ARRAY", init_array_, init_array_count_, false, get_realpath());
            ------>循环调用了 call_function("function", functions[i], realpath);
function hook_init_array() {
    //console.log("hook_constructor",Process.pointerSize);
    if (Process.pointerSize == 4) {
        var linker = Process.findModuleByName("linker");
    }else if (Process.pointerSize == 8) {
        var linker = Process.findModuleByName("linker64");
 
    }
 
 
    var addr_call_array = null;
    if (linker) {
        var symbols = linker.enumerateSymbols();
        for (var i = 0; i < symbols.length; i++) {
            var name = symbols[i].name;
            if (name.indexOf("call_array") >= 0) {
                addr_call_array = symbols[i].address;
            }
        }
    }
    if (addr_call_array) {
        Interceptor.attach(addr_call_array, {
            onEnter: function (args) {
 
                this.type = ptr(args[0]).readCString();
                //console.log(this.type,args[1],args[2],args[3])
                if (this.type == "DT_INIT_ARRAY") {
                    this.count = args[2];
                    //this.addrArray = new Array(this.count);
                    this.path = ptr(args[3]).readCString();
                    var strs = new Array(); //定义一数组
                    strs = this.path.split("/"); //字符分割
                    this.filename = strs.pop();
                    if(this.count > 0){
                        console.log("path : ", this.path);
                        console.log("filename : ", this.filename);
                    }
                    for (var i = 0; i < this.count; i++) {
                        console.log("offset : init_array["+i+"] = ", ptr(args[1]).add(Process.pointerSize*i).readPointer().sub(Module.findBaseAddress(this.filename)));
                        //插入hook init_array代码
                    }
                }
            },
            onLeave: function (retval) {
 
            }
        });
    }
}
function hook_init_array() {
    //console.log("hook_constructor",Process.pointerSize);
    if (Process.pointerSize == 4) {
        var linker = Process.findModuleByName("linker");
    }else if (Process.pointerSize == 8) {
        var linker = Process.findModuleByName("linker64");
 
    }
 
 
    var addr_call_array = null;

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

最后于 2021-5-11 11:34 被Simp1er编辑 ,原因:
收藏
免费 14
支持
分享
最新回复 (9)
雪    币: 116
活跃值: (1012)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
没看懂 最后是在hook init_array里的函数吗
2021-5-11 21:34
0
雪    币: 2270
活跃值: (5537)
能力值: ( LV8,RANK:146 )
在线值:
发帖
回帖
粉丝
3
万里星河 没看懂 最后是在hook init_array里的函数吗
2021-5-12 08:37
0
雪    币: 2677
活跃值: (5390)
能力值: ( LV10,RANK:177 )
在线值:
发帖
回帖
粉丝
4
mark了,太强了
2021-5-12 13:21
0
雪    币: 1223
活跃值: (46)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
收藏,学习了
2021-5-16 15:46
0
雪    币: 498
活跃值: (4291)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
太强了
2023-7-6 12:22
0
雪    币: 19
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
7
mark
2023-12-30 14:16
0
雪    币: 657
活跃值: (5509)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8

大佬  有个问题请教下  在hook linker时候 拿信息的话hook init_ary感觉就行了  为什么还要处理下.init_proc  我在测试的时候只处理hook init_ary确实拿到的加载so不是很齐全  然后处理了init_proc就好了 搞不懂了    这个init_proc的构造函数是什么东西 我在原理里面没有找到 前来请教下 

最后于 2024-5-17 01:22 被梦_魇编辑 ,原因:
2024-5-16 17:06
0
雪    币: 47
活跃值: (115)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
qig
9
梦_魇 大佬&nbsp;&nbsp;有个问题请教下&nbsp;&nbsp;在hook&nbsp;linker时候&nbsp;拿信息的话hook&nbsp ...
soinfo::call_constructors()
    call_function("DT_INIT", init_func_, get_realpath());
    call_array("DT_INIT_ARRAY", init_array_, init_array_count_, false, get_realpath());
            ------>循环调用了 call_function("function", functions[i], realpath);    我理解 init_proc就是这个 call_function("DT_INIT", init_func_, get_realpath());  ,可以在这个函数里面做点事情.
2024-5-20 10:27
2
雪    币: 657
活跃值: (5509)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
感谢大佬   我看了下安卓的源码 搞定了  这个文章里面那个看libart.so那个地方写错了  我当时不懂为什么要去看libart.so
2024-5-20 19:13
0
游客
登录 | 注册 方可回帖
返回
//