A64dbg尝鲜——实战某加固so CrackMe

  • A+
所属分类:逆向工程
A64dbg尝鲜——实战某加固so CrackMe

本文为看雪论坛优秀文章

看雪论坛作者ID:0x指纹 




前言




1. Q&A


Q1:为什么写这篇文章?
 
A:因为一方面,目前还没有使用A64dbg工具进行实战并附有详细分析和使用的文章,此文算是新品开箱的尝鲜文章,分享给各位看雪用户。另一方面,作为安卓逆向爱好者,拿到新的逆向工具还是比较激动的,在进行了不少尝试后,还是希望分享给大家一些有用的东西的。
 
Q2:文章所涉及的功能,是A64dbg不同于其他逆向工具的全部了吗?
 
A:当然不是,涉及的只是我个人最感兴趣的功能——即A64dbg的UnicornVM调试模式下的ADCpp脚本系统。
 
Q3:看完文章后去下载A64dbg可以复现这篇文章吗?
 
A:可能不行,我是拿到了A64dbg的测试授权,在授权情况下拥有完整功能后才进行的测试,未授权的话可能会有些限制,但我并没有具体地测试此文使用到的A64dbg功能被限制到了什么程度,如果感兴趣的话可自行尝试。
 
Q4:文中写的内容好像用unidbg也可以做到,有什么区别吗?
 
A:unidbg很强大,但A64dbg的UnicornVM模式并不同于unidbg,怎么简洁明了地区别理解它们呢,那就是unidbg需要模拟linux系统和java环境,而A64dbg的UnicornVM模式并没有模拟这些,是在设备机中启动了lidadbg-server然后客户端调试器进行对接,也就是说是运行在真实的环境中的。

2. 关于A64dbg的ADCpp脚本系统


最近上手了A64dbg的unicornVM模式的下调试,分析样本为so文件动态加解密的CrackMe:
https://bbs.pediy.com/thread-266546.htm#msg_header_h3_1
一文中进行了360加固的crackme。
 
先要说明的是,在了解A64dbg之后,首先吸引到我的是A64dbg的ADCpp脚本系统,先看一下介绍。

A64dbg尝鲜——实战某加固so CrackMe

A64Dbg-ADCpp脚本系统简介:

IDA有IDC这样的类C脚本系统,Frida嫁接了一层JavaScript脚本系统。虽然用起来还行,但始终不能让人满意。因为与Native打交道,就应该像C/C++那样直白,毕竟操作void *才是Native的精华。受限于C/C++是编译型的静态语言,想要实现像Python/JavaScript那样的便捷性着实不易。


LLDB的表达式倒是可以使用C/C++语句,但是高度依赖类型系统,否则难以写出有效的C/C++表达式。即便如此,还是太弱了,不能写出复杂的完整C/C++函数。

但是,这一切即将成为过去时,我们将在A64Dbg v1.6专业版中引入UnicornVM的时候同时引入ADCpp这个C/C++脚本系统,它是把C/C++定义为了编译型动态语言,使用解释执行的方式运行代码。C/C++最终编译为机器相关的arch.adc字节码,由UnicornVM直接加载执行。不需要手动指定头文件、不需要手动指定链接库文件、不需要手动加载动态库,你只需要code,剩下的就是交给ADCpp了。

你还可以在A64Dbg端定义数据接收的Python3函数,然后在C/C++端发送给它,类似于Frida JavaScript与Python的交互,提供ADCpp接口可以主动调用内存模块中的函数,或者是只要有一个指针即可。

以上引用自链接:
https://mp.weixin.qq.com/s/ShV5-V4XDiyb4mfvVkiAHA

就是说通过ADCpp脚本系统你可以实现使用C/C++代码来完成对内存中模块函数的注入hook,或者是进行主动调用,甚至只要有一个指针地址就可以实现,并且不管是系统空间还是用户空间。
 
基于对此功能的好奇,我进行了简单的测试,尝试使用A64dbg主动调用socrackme的解密函数,随后完成dump解密的so文件,以测试A64dbg在安卓逆向中内存操作的实战效果,这里将测试结果分享给大家。



360旧版加固的vm与反调试‍




在开始之前,先来说下so文件动态加解密的CrackMe一文所做的分析,作者是看雪用户genliese。genliese在文章开头说的很清楚,因为360加固的反调试所以so文件采用全静态分析,这是全文的基调。
 
关于反调试,笔者简单分析了下此版本的360加固,发现是典型的旧360壳,判断标志是libjiagu.so中存在__fun_a_18函数,而反调试就在里面的case当中,在论坛上几年前就已经有相关的分析文章了,最早的应该是某数字公司VMP脱壳简记:
https://bbs.pediy.com/thread-223528.htm
 
__fun_a_18函数的流程图看上去像是被加了ollvm控制流平坦化,但其实是一个基于堆栈的vm,找了很多资料,国内应该没有详细分析此vm的文章,但是论坛里有一篇翻译老外分析360加固的文章(老外挑战360加固--实战分析(很详细) ):
https://bbs.pediy.com/thread-225561.htm
里面有详细说这个vm,并且在github有相关还原代码。

A64dbg尝鲜——实战某加固so CrackMe

众所周知,旧版360加固的学习价值很高,是入门者练手分析很好的素材,这里我整理了些相关文章,列出来方便大家学习:

(1) 360加固保分析
https://bbs.pediy.com/thread-260049.htm

(2) 某数字公司VMP脱壳简记
https://bbs.pediy.com/thread-223528.htm

(3) 根据”so劫持”过360加固详细分析
https://bbs.pediy.com/thread-223796.htm

(4) 老外挑战360加固--实战分析(很详细)
https://bbs.pediy.com/thread-225561.htm

(5) vm_emulator.py

(6) 360加固之onCreate函数还原并重打包
https://bbs.pediy.com/thread-223223.htm

(7) 某壳分析学习过程
https://bbs.pediy.com/thread-224708.htm

(8) 某vmp壳原理分析笔记
https://bbs.pediy.com/thread-225798.htm

现在回过头来说下反调试,旧版360加固主要有时间反调试、rtld_db_dlactivity反调试、traceid反调试和IDA端口反调试。
 
其中rtld_db_dlactivity反调试和traceid反调试是针对ptrace方式调试的反调试,在使用A64dbg的UnicornVM模式调试的测试过程中,笔者并没有发现触发反调试,说明A64dbg-UnicornVM模式的调试机制并不依赖于ptrace,也就是能直接无视掉此壳的反调试。
 
如果大家想尝试使用IDA进行调试分析,过反调试的话可以参考这篇文章360加固保分析:
https://bbs.pediy.com/thread-260049.htm


so文件分析




文章中genliese使用FART定制ROM进行了脱壳,笔者进了测试,这个版本的360壳并没有进行指令抽取及vmp,使用dex整体dump即可脱壳。
 
dex的代码逻辑很简单,输入传进native层的test函数进行验证。

A64dbg尝鲜——实战某加固so CrackMe

首先libnative-lib.so的.init_array中的函数进行了字符串解密,

A64dbg尝鲜——实战某加固so CrackMe

我们跳过,直接看下JNI_OnLoad函数,可以看到字符串还没有被解密出来,以及java层的test方法应该是关联到了native层的ooxx函数。

A64dbg尝鲜——实战某加固so CrackMe

我们来看ooxx函数。
.text:00008DC4                 EXPORT ooxx.text:00008DC4 ooxx                                    ; CODE XREF: j_ooxx+8↑j.text:00008DC4                                         ; DATA XREF: LOAD:000007C0↑o ....text:00008DC4.text:00008DC4 var_2C          = -0x2C.text:00008DC4 var_28          = -0x28.text:00008DC4 var_24          = -0x24.text:00008DC4 var_18          = -0x18.text:00008DC4 var_14          = -0x14.text:00008DC4 var_10          = -0x10.text:00008DC4.text:00008DC4 ; __unwind {.text:00008DC4                 PUSH            {R4-R7,LR}.text:00008DC6                 ADD             R7, SP, #0xC.text:00008DC8                 SUB             SP, SP, #0x24.text:00008DCA                 MOV             R3, R2.text:00008DCC                 MOV             R12, R1.text:00008DCE                 MOV             LR, R0.text:00008DD0                 STR             R0, [SP,#0x30+var_10].text:00008DD2                 STR             R1, [SP,#0x30+var_14].text:00008DD4                 STR             R2, [SP,#0x30+var_18].text:00008DD6                 STR             R3, [SP,#0x30+var_24].text:00008DD8                 STR.W           R12, [SP,#0x30+var_28].text:00008DDC                 STR.W           LR, [SP,#0x30+var_2C].text:00008DE0                 BL              sub_8930.text:00008DE4                 MOV             R0, R0.text:00008DE6                 MOV             R0, R0.text:00008DE8                 MOV             R0, R0.text:00008DEA                 MOV             R0, R0.text:00008DEC                 MOV             R0, R0.text:00008DEE                 MOV             R0, R0.text:00008DF0                 MOV             R0, R0.text:00008DF2                 MOV             R0, R0.text:00008DF4                 MOV             R0, R0.text:00008DF6                 MOV             R0, R0.text:00008DF8                 MOV             R0, R0.text:00008DFA                 MOV             R0, R0.text:00008DFC                 MOV             R0, R0.text:00008DFE                 MOV             R0, R0.text:00008E00                 BX              R0.text:00008E00 ; End of function ooxx.text:00008E00.text:00008E00 ; ---------------------------------------------------------------------------.text:00008E02                 DCW 0x4502.text:00008E04                 DCD 0x41064304, 0x4D0A4F08, 0x490E4B0C, 0x55125710, 0x51165314.text:00008E04                 DCD 0x5D1A5F18, 0x591E5B1C, 0x65226720, 0x61266324, 0x6D2A6F28.text:00008E2C                 DCD 0x692E6B2C.text:00008E30                 DCD 0x75327730.text:00008E34                 DCD 0x71367334.text:00008E38                 DCD 0x7D3A7F38, 0x793E7B3C.text:00008E40                 DCD 0x5420740.text:00008E44                 DCD 0x1460344.text:00008E48                 DCD 0xD4A0F48

可以看到初始化后跳到了sub_8930,而sub_8930函数中,我们看到了十分明显的内存指令解密标志mprotect以及cacheflush函数。
 
至于如何具体进行的内存指令解密操作,so文件动态加解密的CrackMe一文中十分详细的讲解了,这里我就不多说了,此crackme是一个很好的指令解密学习素材,大家感兴趣的话可以动手分析下。可以猜到,sub_8930进行内存指令解密后,便是开始执行真正的代码了,就是对输入进行校验。

A64dbg尝鲜——实战某加固so CrackMe


到这一步可以知道,想要拿到flag就需要知道被加密的代码是什么。genliese采取的方式是使用fridad动态dump内存中的加密指令的解密密钥,然后再使用密钥静态解密文件中的指令加密部分,然后IDA即可反编译出解密后的ooxx函数,以及不得不称赞的是genliese的代码写的很漂亮,让人看的很舒服。

A64dbg-ADCpp测试


好了,下面就开始进入此文的关键的部分,对A64dbg的ADCpp脚本系统的内存操作功能进行测试,这里测试的主要是ADCpp脚本系统的主动调用和内存dump特性,我们的测试思路如下所述。
 
尽管我们可以在指令解密完后设下断点,然后输入flag点击触发断点随后dump内存。但是我们并不这样做,而是选择让程序保持运行,使用ADCpp脚本系统主动调用指令解密函数ooxx,然后让断点停在指令解密完后,随后进行dump so文件。
 
也就是说程序一方面正在运行等待我们的输入,光标还在闪动,而另一方面,我们已经悄无声息地开辟了一条新的战线,执行了内存里的函数,并停在了我们设下的断点,然后进行了dump操作。
 
至于为什么在解密完的时刻设下断点,让调试器在这里停在这里,因为ooxx函数解密出来的指令在对输入进行校验后,还会将ooxx再加密回去。

(1) 启动lidadbg-server


我们在设备机里面启动lidadbg-server后,然后打开A64dbg,Options->Preferences里客户端进行设置。
 
填好Remote Android栏下的ip和port,接着填好adb的路径,以及最重要的是Default Platform选择Remote UnicornVM Android,这样才能进行UnicornVM模式下的调试。

A64dbg尝鲜——实战某加固so CrackMe

然后运行Apk,点击File->attach,然后搜索包名attach即可。

A64dbg尝鲜——实战某加固so CrackMe

(2) 设置断点


第一次加载会比较慢,等待加载好后,点击Symbols窗口,左下角搜索模块名称libnative-lib.so,右下角搜索函数ooxx,双击。

A64dbg尝鲜——实战某加固so CrackMe

进入汇编窗口,可以看到跳到了sub_8930函数,双击。

A64dbg尝鲜——实战某加固so CrackMe

然后找到指令后解密完后,调用cacheflush的地方,设下断点。


A64dbg尝鲜——实战某加固so CrackMe


(3) ADCpp脚本系统主动调用解密函数ooxx


现在断点已经设置好了,按照我们的计划,要开始进行主动调用了。
 
ADCpp脚本运行有两种方式,一种是直接载入脚本,另一种则是通过最下方的命令行窗口,这里我们都尝试一下。
 
然后主动调用函数也有两种方式,一种适用于有符号的函数,我们通过extern "C" void ooxx();就可以引用A64dbg解析好的有符号的函数,可以直接调用;另一种则是知道函数在内存中的地址后,我们将绝对地址转化为函数指针然后进行调用,这时候要注意汇编是否在thumb模式下。

方式一


方式一,从解析出来的模块符号表中找到ooxx函数,然后进行调用。
 
具体操作是将如下代码保存文件,后缀名为.cc,然后File->ADCpp Script,找到保存的文件,双击。
extern "C" void ooxx();
void adc_main_thread() { ooxx();}
方式二


比如你知道了ooxx函数在内存的起始地址为0x859EDC4,thumb模式下进行|1,然后将这个绝对地址转换为函数指针,即可进行调用。
 
具体操作是在最下面的Command窗口,选择ADCpp模式,输入((void(*)())(0xD859EDC4 | 1))();,然后回车。

A64dbg尝鲜——实战某加固so CrackMe

(4) 效果


A64dbg尝鲜——实战某加固so CrackMe


最后它们都会在ooxx里设置的断点停下来。

(5) Python脚本Dump内存中已解密so文件。


在这时候内存中的so文件已经完成了解密,我们dump下来即可,我们该如何dump呢?还记文章开头那张图的右上角是什么吗,我们来看一下。
 
A64dbg尝鲜——实战某加固so CrackMe

没错,我们是可以使用python3来处理ADCpp脚本系统的返回结果的,实际上,ADCpp脚本系统也提供给python丰富的接口去做很多事情,诸如操作内存、断点等,如果感兴趣的话下载A64dbg后可以查看A64dbg/python3/adp.py文件。

A64dbg尝鲜——实战某加固so CrackMe

这里我们只需要dump内存,使用readMemory一个接口就足够了。
def readMemory(addr, size):    """    read memory at addr with size within the page, the result is a bytes object.    """    return api_proc_result('readMemory', (addr, size))

然后我们要确定so文件在内存中的分布。
 
首先我们执行ADCpp的commd,即printf("pid: %d",getpid()),获取到pid,这里使用到了ADCpp的内置接口getpid()。

A64dbg尝鲜——实战某加固so CrackMe

然后切换TarShell模式,这里就相当于android设备的shell了。

A64dbg尝鲜——实战某加固so CrackMe

然后执行cat /proc/10138/maps | grep libnative-lib.so,查看应用在内存中的分布。
 
A64dbg尝鲜——实战某加固so CrackMe

我们可以看到so文件在内存中分布在两块区域中,0xd861d000-0xd8636000和0xd8637000-0xd863a000。
 
接着我们写好python脚本,使用readMemory接口对so文件进行dump。
start1 = 0xd861d000end1 = 0xd8636000
start2 = 0xd8637000end2 = 0xd863a000

def dump(start,end): page_bytes = b'' for i in range((end-start) // 0x1000 ): page_bytes = page_bytes + readMemory(start+i*0x1000, 0x1000) return page_bytes
so_bytes = b''so_bytes = so_bytes + dump(start1,end1)so_bytes = so_bytes + dump(start2,end2)
with open("D:\so_dump.so",'wb') as f: f.write(so_bytes) f.close()

接着点击Files->Python script,然后找到保存的python脚本,点击即可。
 
此时装载到内存中的so文件就已经被dump下来了,我们使用二进制对比工具进行下对比。

A64dbg尝鲜——实战某加固so CrackMe

可以看到中间的是解密部分不同,以及内存中dump下来的so文件缺少section表,如果感兴趣的话可以手动修复。

(6) 效果验证


然后就到了验证效果的时刻了,我们把dump下来的so文件拖进IDA,IDA会提醒section表解析错误,我们略过继续打开,然后搜索ooxx函数,可以看到ooxx已经被解密了。
 
A64dbg尝鲜——实战某加固so CrackMe

好了,到现在我们的测试已经按预想中的完成了。


后记




怎么评价A64dbg?
 
整体评价目前我不太好说,原因是目前我还只是尝试了使用A64dbg的UnicornVM模式调试下的ADCpp脚本系统,进行了简单的安卓逆向测试。
 
但是如果单是讲ADCpp脚本系统的话,我只能说A64dbg给我带来了焕然一新的so层逆向体验,在测试另一个加固apk时候,只写了几行C代码进行主动调用字符串解密函数,就输出了所有加密字符串,这无疑给静态分析带来了极大的便利,是使用frida操作native层所不能及的。当然也可能是我的水平火候不够,不能站在一个更高的角度上去对比分析。
 
以及目前我只是测试了主动调用和内存操作的特性,使用C/C++进行so注入hook还在探索中,但是这几句话你能想到什么呢?

我想到了可以直接把安卓源码中的原生头文件包含进来,然后使用ADCpp脚本系统去注入hook,只需要少量并且简洁的C/C++代码就能实现一个脱壳机插件。
 
当然这只是个猜想,因为目前A64dbg还不太稳定成熟,不依赖ptrace的断点机制多少有些不稳定,以及A64dbg仍然缺少大量实战的洗礼,很多bug都在埋伏隐藏中,只有在实战中短兵相接才会暴露出来。
 
但是我还是很期待有天A64dbg能成熟稳定到支撑起一个脱壳机插件的运行,也希望到时候我有足够强的实力水平来驾驭这个工具,因为我也是在不断使用A64dbg进行尝试时想明白了一个道理,工具永远是次要的,最重要的一直都是使用工具的人。
 
论坛一种通过后端编译优化脱混淆壳的方法:
https://bbs.pediy.com/thread-260626.htm
帖子下有个评论让我印象一直很深刻,在这里分享给大家。


A64dbg尝鲜——实战某加固so CrackMe



A64dbg尝鲜——实战某加固so CrackMe

- End -



A64dbg尝鲜——实战某加固so CrackMe


看雪ID:0x指纹 

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

  *本文由看雪论坛 0x指纹 原创,转载请注明来自看雪社区。



A64dbg尝鲜——实战某加固so CrackMe
2021 KCTF 春季赛防守方 征题倒计时~



# 往期推荐





A64dbg尝鲜——实战某加固so CrackMe
公众号ID:ikanxue
官方微博:看雪安全
商务合作:[email protected]



A64dbg尝鲜——实战某加固so CrackMe

球分享

A64dbg尝鲜——实战某加固so CrackMe

球点赞

A64dbg尝鲜——实战某加固so CrackMe

球在看



A64dbg尝鲜——实战某加固so CrackMe

点击“阅读原文”,了解更多!

本文始发于微信公众号(看雪学院):A64dbg尝鲜——实战某加固so CrackMe

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: