记录黑客技术中优秀的内容,传播黑客文化,分享黑客技术精华

Frida和Xposed打印init_array的字符串解密函数

2020-11-22 21:14

本文为看雪论坛文章

看雪论坛作者ID:无造



本文为看雪安卓高研2w班(8月班)优秀学员作品。


下面先让我们来看看学员的学习心得吧!


学员感想


这是2W班8月份的练习题第一题。

题目要求是:ollvm针对加密字符串的解密都是在位于init_array节当中的函数。请编写Xposed插件,完成对测试apk中位于init_array节中的函数的hook,并获取对应的解密后的字符串。
 
这题还是需要查看源码,主要是so文件加载,这方面的文章网上还是蛮多的。根据源码可以找到系统是什么时候调用,接下来就是hook验证流程了。

ps. 题目附件请点击“阅读原文”下载。




解题过程



查看代码


有壳,frida脚本整体dump即可:

public boolean check(String content) {    if (Shell(content)) {        return true;    }    return false;} public native boolean Shell(Object obj);

查看Shell函数,静态注册的:

bool __fastcall sub_87C4(const char *a1){  s = (char *)a1;  v2 = (unsigned __int8 *)off_1B004;     if ( strlen(s) < 6 )      v4 = (unsigned __int8)*s == *v2        && (unsigned __int8)s[1] == v2[1]        && (unsigned __int8)s[2] == v2[2]        && (unsigned __int8)s[3] == v2[3]        && (unsigned __int8)s[4] == v2[4];...

这里就是取off_1B004的5个长度进行比较:

.data:0001B004 off_1B004       DCD byte_1B008          ; DATA XREF: sub_876C+16o.data:0001B004                                         ; sub_876C+18r ....data:0001B008 ; _BYTE byte_1B008[8].data:0001B008 byte_1B008      DCB 0x9F, 0x94, 0x99, 0x9F, 0x97, 0xFC, 0, 0

先手动计算一下 ^= 0xFCu 得到 [99,104,101,99,107] = check
 
验证正确,开始写xposed。

初试hook

由于ollvm字符串混淆的函数名开头都是.datadiv_decode,所以可以先hook以这个函数开头的数据:

//hook .datadiv_decode9080531325931451386void *(*old_datadiv_decode)() = nullptr;void *new_datadiv_decode() {    __android_log_print(4, "hookso", "new_datadiv_decode onEnter");    void *result = old_datadiv_decode();    __android_log_print(4, "hookso", "new_datadiv_decode onLeave");    return result;} void starthookInitArray() {    void *libchandle = dlopen("libnative-lib.so", RTLD_NOW);    __android_log_print(4, "hookso", "libchandle->0x%x",libchandle);    void *datadiv_decode_addr = dlsym(libchandle, ".datadiv_decode9080531325931451386");    __android_log_print(4, "hookso", "datadiv_decode_addr->0x%x",datadiv_decode_addr);    if (registerInlineHook((uint32_t) datadiv_decode_addr, (uint32_t) new_datadiv_decode,                           (uint32_t **) &old_datadiv_decode) !=        ELE7EN_OK) {        return;    }    if (inlineHook((uint32_t) datadiv_decode_addr) == ELE7EN_OK) {        __android_log_print(4, "hookso", "hook native-lib.so->datadiv_decode9080531325931451386 success!");        //return -1;    }}

这里虽然hook了,但是并没有打印日志,应该是hook之前就运行了。看看loadLibrary。
 
java层中loadLibrary最终调用了nativeLoad。
 
进入art可以找到do_dlopen,网上也有比较多关于do_dlopen的文章。
 
我们要hook的init_array就在dlopen的时候通过do_dlopen->call_constructors->call_array->call_function就已经运行了。这个流程走完后,loadLibrary才算作结束,所以我们再hook init_array中的函数的时候已经不会再次运行了。
 
接下来尝试hook linker 中的call_xxx相关函数。

frida hook 验证


使用xposed dlopen linker并没有取到模块,由于并不熟悉xposed,所以这里首先使用frida验证猜想,环境是pixel 8.0。
 
使用frida hook linker模块的几个call_xx函数都正常,这里过滤下call_function的typename为function就是init_array中的函数,可以在它运行前hook它:

Interceptor.attach(addr_call_function_args, {    onEnter : function(args){        var typename = args[0].readCString();        var soname = args[2].readCString();        if(typename == "function" && soname.indexOf("libnative-lib.so") > -1){            //这里是init_array            var funcaddr = args[1];            console.log("addr_call_function_args onEnter->",funcaddr);            Interceptor.attach(funcaddr, {                onEnter : function(args){                    console.log("call ", funcaddr," onEnter->");                },                onLeave: function(retval){                    console.log("call ", funcaddr," onLeave->");                }            });        }    },    onLeave: function(retval){        // console.log("addr_call_function_args onLeave->");    }});

android_dlopen_ext: /data/app/com.kanxue.hookinit_array-ePb0iNQ5c_WaWNJ8D3IHsw==/lib/arm/libnative-lib.soaddr_call_function_args onEnter-> 0xcbf0b1b9call  0xcbf0b1b9  onEnter->     //.datadiv_decode9080531325931451386call  0xcbf0b1b9  onLeave->addr_call_function_args onEnter-> 0xcbf0a76dcall  0xcbf0a76d  onEnter->     //sub_876Ccall  0xcbf0a76d  onLeave->libnative-> [object Object]libnative-> 0xcbf02000datadiv_decode_addr-> 0xcbf0b1b9

可以看到0xcbf0b1b9就是.datadiv_decode9080531325931451386字符串解密函数,接下来打印对比:

Interceptor.attach(addr_call_function_args, {    onEnter : function(args){        var typename = args[0].readCString();        var soname = args[2].readCString();        if(typename == "function" && soname.indexOf("libnative-lib.so") > -1){            //这里是init_array            var funcaddr = args[1];            console.log("addr_call_function_args onEnter->",funcaddr);            console.log("before function->", g_start_byte.readByteArray(g_byte_len));            this.printafter = true;        }    },    onLeave: function(retval){        console.log("after function->", g_start_byte.readByteArray(g_byte_len));        var bs =  g_start_byte.readByteArray(g_byte_len);        var start = -1;        var preu8 = 0;        for(var i=0; i<g_byte_len; i++){            var valu8 =  g_start_byte.add(i).readU8();            if(valu8 == 0){                if(preu8 != 0){                    console.log("so addr[0x"+(g_start_byte.add(start+1) - g_libnative.base).toString(16)+"]->",g_start_byte.add(start+1).readCString());                }                start = i;            }            preu8 = valu8;        }    }}); Interceptor.attach(func_call_constructors, {    onEnter : function(args){        var soname = args[0].readCString();        if( soname.indexOf("libnative-lib.so") > -1){            //这时候就能通过findModuleByName查找到我们需要的so            var libnative = Process.findModuleByName("libnative-lib.so");            g_libnative = libnative;            g_start_byte = g_libnative.base.add(0x1B008);            g_byte_len =  0x1B0C0 - 0x1B008;            console.log("func_call_constructors onEnter->",soname,g_libnative.base );        }    },    onLeave: function(retval){        // console.log("func_call_constructors onLeave->");    }});

before function->            0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F  0123456789ABCDEF00000000  9f 94 99 9f 97 fc 00 00 14 17 16 11 10 13 12 1d  ................00000010  1c 1f 1e 19 18 1b 1a 05 04 07 06 01 00 03 02 0d  ................00000020  0c 0f 34 37 36 31 30 33 32 3d 3c 3f 3e 39 38 3b  ..4761032=<?>98;00000030  3a 25 24 27 26 21 20 23 22 2d 2c 2f 65 64 67 66  :%$'&! #"-,/edgf00000040  61 60 63 62 6d 6c 7e 7a 55 8c da a9 6b 46 4f 4f  a`cbml~zU...kFOO00000050  4c 03 45 51 4c 4e 03 60 08 08 23 f2 f1 e3 f9 f3  L.EQLN.`..#.....00000060  cf e3 e4 e2 f9 fe f7 90 75 78 78 7b 77 75 60 7b  ........uxx{wu`{00000070  66 28 40 2a 2e 2e 75 78 78 7b 77 75 60 71 3c 67  f(@*..uxx{wu`q<g00000080  7d 6e 71 4b 60 34 7a 3d 34 33 7a 33 34 71 6c 77  }nqK`4z=43z34qlw00000090  71 71 70 67 34 79 75 6c 7d 79 61 79 34 67 61 64  qqpg4yul}yay4gad000000a0  64 7b 66 60 71 70 34 67 7d 6e 71 14 21 99 e9 cb  d{f`qp4g}nq.!...000000b0  fd 99 e9 cb 20 51 ea cb                          .... Q..after function->            0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F  0123456789ABCDEF00000000  63 68 65 63 6b 00 00 00 41 42 43 44 45 46 47 48  check...ABCDEFGH00000010  49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58  IJKLMNOPQRSTUVWX00000020  59 5a 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e  YZabcdefghijklmn00000030  6f 70 71 72 73 74 75 76 77 78 79 7a 30 31 32 33  opqrstuvwxyz012300000040  34 35 36 37 38 39 2b 2f 00 25 73 00 48 65 6c 6c  456789+/.%s.Hell00000050  6f 20 66 72 6f 6d 20 43 2b 2b 00 62 61 73 69 63  o from C++.basic00000060  5f 73 74 72 69 6e 67 00 61 6c 6c 6f 63 61 74 6f  _string.allocato00000070  72 3c 54 3e 3a 3a 61 6c 6c 6f 63 61 74 65 28 73  r<T>::allocate(s00000080  69 7a 65 5f 74 20 6e 29 20 27 6e 27 20 65 78 63  ize_t n) 'n' exc00000090  65 65 64 73 20 6d 61 78 69 6d 75 6d 20 73 75 70  eeds maximum sup000000a0  70 6f 72 74 65 64 20 73 69 7a 65 00 21 d9 ea cb  ported size.!...000000b0  fd d9 ea cb 20 91 eb cb                          .... ...so addr[0x1b008]-> checkso addr[0x1b010]-> ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/so addr[0x1b051]-> %sso addr[0x1b054]-> Hello from C++so addr[0x1b063]-> basic_stringso addr[0x1b070]-> allocator<T>::allocate(size_t n) 'n' exceeds maximum supported size

这里输出结果和加密函数datadiv_decode9080531325931451386对应上了,流程是走通了,然后就是在xposed中实现。

Xposed 实现


Xposed环境是n5 6.0。
 
Xposed使用dlopen和dlsym无法获取非导出函数call_constructors。所以这里ida打开so文件,获取函数偏移自己计算,这里是thumb模式,所以需要+1,这里也可以找枚举符号表的代码,就偷个懒写死了:

void *addr_call_constructors = (void *)((unsigned int)liblinker+0x2654+1);

结果hook没反应,这时候frida和xposed一起使用,看看差别是什么:
可以看出地址不一样,这时候查看dlopen源码发现是重新加载了linker模块,所以导致不一样。而frida是直接查找现有模块的地址。xposed也可以根据maps文件来查找。不主动加载看看能不能hook到。
 
由于linker每次加载地址并没有变化,所以这里先写死看看能不能Hook住:

void *liblinker =  (void *)(0xb6f16000);__android_log_print(4, "hookso", "liblinker->0x%x",liblinker); void *addr_call_constructors = (void *)((unsigned int)liblinker+0x2654+1);__android_log_print(4, "hookso", "addr_call_constructors->0x%x",addr_call_constructors);

08-16 20:06:08.558 7176-7176/com.kanxue.hookinit_array I/hookso: addr_call_constructors->0xb6f1865508-16 20:06:08.574 7176-7176/com.kanxue.hookinit_array I/hookso: hook linker->call_constructors success!08-16 20:06:08.643 7176-7176/com.kanxue.hookinit_array I/hookso: new_call_constructors onEnter08-16 20:06:08.643 7176-7176/com.kanxue.hookinit_array I/hookso: new_call_constructors onEnter

思路是行得通,这边new_call_constructors 有hook到,但是可能参数问题,导致程序崩溃。
 
之后尝试hook call_fucntion正常运行,先不管call_constructors:

08-16 20:44:39.379 16857-16857/com.kanxue.hookinit_array I/hookso: hook linker->call_fucntion success!08-16 20:44:39.709 16857-16857/com.kanxue.hookinit_array I/hookso: new_call_fucntion onEnter DT_INIT libnative-lib.so08-16 20:44:39.709 16857-16857/com.kanxue.hookinit_array I/hookso: new_call_fucntion onEnter function libnative-lib.so addr:0xb3e551b908-16 20:44:39.709 16857-16857/com.kanxue.hookinit_array I/hookso: new_call_fucntion onEnter function libnative-lib.so addr:0xb3e5476d

接下来就是查看maps文件找出linker和libnative-lib.so的地址,然后打印出函数运行前后加密字符串的变化。
 
这里需要注意xposed加载hook的so文件时机不能在libnative-lib.so之后,我这边最开始就加载了:

if( loadPackageParam.packageName.equals("com.kanxue.hookinit_array")){    XposedBridge.log("XLZH" + loadPackageParam.packageName);    System.load("/data/data/com.kanxue.hookinit_array/lib/hookso.so");}

就是网上抄抄代码处理maps和打印,对c++开发方面了解很浅。

void *new_call_fucntion(void* name,void* a2,void* a3) {    char* soname = (char *)name;    char* type = (char *)a2;    bool printafter = false;    if(strstr(soname,"libnative-lib.so")) {        if(strstr(type,"DT_INIT")) {            addr_libnative = get_module_base(-1, "libnative-lib.so");            __android_log_print(4, "hookso", "new_call_fucntion onEnter DT_INIT %s soaddr:0x%x",                                soname, addr_libnative);        } else if(strstr(type,"function")) {            printafter = true;            __android_log_print(4, "hookso", "new_call_fucntion onEnter function %s addr:0x%x",                                soname, a3);            unsigned char storebox[200]={0};            if(memcpy(storebox, (void *)((unsigned int)addr_libnative+0x1B008), 200)!=NULL)            {                char printstr[1024] = {0};                for(int z=0;z<200;z++)                {                    sprintf(printstr+z*2, "%02x", storebox[z]);                }                __android_log_print(4, "hookso", "0x%x->%s",((unsigned int)addr_libnative+0x1B008),printstr);            }        }    }    void *result = old_call_fucntion(name, a2 ,a3);    if(printafter){        __android_log_print(4, "hookso", "new_call_fucntion onLeave function %s addr:0x%x",                            soname, a3);        unsigned char storebox[200]={0};        if(memcpy(storebox, (void *)((unsigned int)addr_libnative+0x1B008), 200)!=NULL)        {            char printstr[1024] = {0};            for(int z=0;z<200;z++)            {                sprintf(printstr+z*2, "%02x", storebox[z]);            }            __android_log_print(4, "hookso",  "0x%x->%s",((unsigned int)addr_libnative+0x1B008),printstr);        }    }    return result;} void starthookInitArray() {    void* liblinker = get_module_base(-1, "/system/bin/linker");    __android_log_print(4, "hookso", "liblinker->0x%x",liblinker);     //0x2434是6.0 call_function偏移    void *addr_call_function = (void *)((unsigned int)liblinker+0x2434+1);    __android_log_print(4, "hookso", "addr_call_function->0x%x",addr_call_function);     if (registerInlineHook((uint32_t) addr_call_function, (uint32_t) new_call_fucntion,                           (uint32_t **) &old_call_fucntion) !=        ELE7EN_OK) {        return;    }    if (inlineHook((uint32_t) addr_call_function) == ELE7EN_OK) {        __android_log_print(4, "hookso", "hook linker->call_constructors success!");        //return -1;    }}

打印的结果:
将结果放到010中查看:
和frida实现的效果一样。



- End -


看雪ID:无造

https://bbs.pediy.com/user-home-571058.htm

  *本文由看雪论坛 无造 原创,转载请注明来自看雪社区。


好消息!!现在看雪《安卓高级研修班》线下班 & 网课(12月班)开始同步招生啦!以前没报上高研班的小伙伴赶快抓紧机会报名,升职加薪唾手可得!!



推荐文章++++

* 不用CR0或MDL修改内核非分页写保护内存的一种思路(x64)

* 一个自定义classloader的函数抽取壳样本

* RC4、Base64魔改看雪CTF-变形金刚学习笔记

* 一个易上手的函数抽取样本还原

* 编写单机游戏连连看辅助的全过程






公众号ID:ikanxue
官方微博:看雪安全
商务合作:wsc@kanxue.com



求分享

求点赞

求在看


“阅读原文一起来充电吧!

知识来源: https://mp.weixin.qq.com/s?__biz=MjM5NTc2MDYxMw==&mid=2458374574&idx=2&sn=05de9f7e8defb1694fb15abfffbe2cfa

阅读:7755 | 评论:0 | 标签:解密

想收藏或者和大家分享这篇好文章→复制链接地址

“Frida和Xposed打印init_array的字符串解密函数”共有0条留言

发表评论

姓名:

邮箱:

网址:

验证码:

公告

❤人人都能成为掌握黑客技术的英雄❤

ADS

标签云

本页关键词