脱壳练习(0)-动态调试1代脱壳

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/m0_37344790/article/details/79102031

一直在练习脱壳,奈何学校的事情比较多,也刚经历期末考试,虽然期末考试还没结束,但总算有了自己的时间,嗯,来总结一下练习脱壳以来的心得体会和过程经验。
那就先从最简单的1代加壳技术开始吧,1代加固就是最简单的通过替换classloader来进行dex的运行时加载,刚好有个很不错的例子,是17年9月底 信息安全铁人三项赛总决赛 个人赛的一道android题,当时题目要求就是脱壳,拿到完整dex,提交签名可得分。(当时年少无知,动调都不是很熟,遗憾未能拿分)
我们开始吧(末尾有附件)。

1. 查看java层,找到目标so

将目标apk拖进jadx中,查看java层逻辑。可发现只有两个Application类,加载so的逻辑在MyWrapperProxyApplication中。

这里写图片描述
这里写图片描述
java层就是这么简单,我们可在assets目录下找到这个so,
这里写图片描述

2. 静态分析目标so

在此以arm_v7a举例。
将so拖进ida后找到Jni_Onload函数。
这里写图片描述
这里写图片描述
记下JNI_Onload的偏移地址0x242C;此时查看String窗口,发现ClassLoader字样,大概可以确定是通过替换classloader的1代加固。
这里写图片描述
接下来分析JNI_Onload函数,如上图可能某些参数识别不准确,我们可以导入jni.h文件辅助分析,在此不是重点,简单提一下。
File -> Load file -> Parse c header file
在JNI_Onload函数中,重点关注sub_xxx函数,首先找出反调试,再找出替换classloader的位置。
在此直接给出,反调试在偏移为 0x000025E0 处,替换classloader加载dex在 0x0000267C处。
反调试函数:
这里写图片描述
loadDex处函数:
这里写图片描述
通过分析反调试函数,我们的目标最终确定在偏移为0xFFBC的函数中,通过atoi函数检测Tracerid的值是否为零,反调试原理具体可参考:Android逆向反调试练习
这里写图片描述
查看这几个函数,我们可以都是通过创建新线程进行检测:
检测Tracerid:
这里写图片描述
添加文件监控:
这里写图片描述
在其他地方还检测其他应用,防hook。在此对我们产生影响的只有检测Tracerid,记下偏移地址,手动修改atoi返回值,nop pthread_create 或者 暂停线程都可以,因为实在死循环中不停检测,所以我们可以采取后两种方式,在此我们当线程跑起来后直接suspend就可以过反调试。记下调用atoi的偏移地址。
接下来,我们查看loadDex地方,可以看到明显替换classloader的提示信息:
这里写图片描述
如果是dvm模式下的我们可以通过在dvmDexFileOpenPartial下断点,那如果是art模式下呢,在函数的前半段我们看到了关键点。
这里写图片描述
在这里可能稍微麻烦些,不过倒腾一下肯定是可以找到的。在此以dvm为例进行脱壳。
在loadDex函数结束后就到了 snprintf(&v15, 0xFFu, “end load dex, result is %d”, v8);提示我们加载结束的地方。我们也记下此处的偏移地址:0x000026A0;

3.动态调试so,脱壳

接下来开始动态调试;
1.启动android端的代理服务,修改默认端口号
这里写图片描述
2.进行端口转发,调试启动apk
这里写图片描述
3.本地设置地址和端口
这里写图片描述
设置无误点击确定后:
这里写图片描述
记下pid的值:19482,点击Ok,即进入界面
4.个人习惯 下断点前,进行附加jdb
这里写图片描述
然后就可以开始愉快的so动态调试了;
5.进行debugger设置
Debugger -> Debugger Option
这里写图片描述
6.一路F9 可到 linker
这里写图片描述
到达linker后,我们才可以看到我们的目标so
7.开始下断掉
libdvm 在 dvmDexFileOpenPartial 函数下断掉
打开Module窗口,找到libdvm,点击进行,查看目标函数,即可进入
这里写图片描述
这里写图片描述
在函数开始处下断点:
这里写图片描述

接下来在目标so下断点,这里我们需要在两处下断点:JNI_Onload,调用atoi处,前面静态分析时我们已经记下偏移地址;
下断点可采用 基址+偏移 的方式
在本次动调下基址为:0x7560C000
这里写图片描述
则JNI_Onload地址为:0x7560C000 + 0x242C = 0x7560E42C
atoi为0x7560C000 + 0x1001E = 0x7561C01E
通过G快捷键下好断点。
这里写图片描述
这里写图片描述
8.F9进入JNI_Onload,再几次F9后到我们atoi下断掉处,这里可以看到创建了新的线程,暂时不用管。
到达atoi函数后我们选择直接把当前线程suspended掉。
这里写图片描述
再F9直到了dvmDexFileOpenPartial
这里写图片描述
在HexView区,同步R0寄存器,可看到dex035字样,此时R0寄存器的值表示dex在内存开始的位置,R1表示dex的大小。
这里写图片描述
这里写图片描述
使用idc脚本进行dump

static main(void)
    {
        auto fp, begin, end, dexbyte;
        fp = fopen("d:\\dumpDex\\dump01.dex", "wb"); //打开或创建一个文件
        begin =  0x7663E008;              //dex基址
        end = begin + 0x0030CF78;            //dex基址 + dex文件大小
        for ( dexbyte = begin; dexbyte < end;dexbyte ++ )
        {
            fputc(Byte(dexbyte), fp);     //按字节将其dump到本地文件中
        }
    }

静等dump,结束后拖入jadx中,发现dex并不完整,所有函数实现均为空。
这里写图片描述
明显不会是动态自修改,肯定会在加载前的某一刻进行还原。这里我们可以直接在loadDex函数结束,snprintf函数处重新进行内存dump,因为一般来讲dex被mmap进内存后,在内存中的位置不会再变。
在snprintf下断点,静态分析时已经拿到偏移地址。
这里似乎是多dex的情况,又进行了一次dvmDexFileOpenPartial,我们直接F9到snprintf就好。
这里写图片描述
查看dex内存地址,dex字样仍在。
再次使用同样的脚本进行dump,大小不变,名称改为dump02.dex。
这里写图片描述
同样,拖进jadx查看;
这里写图片描述
啊哈,脱壳成功。

给上附件:链接:https://pan.baidu.com/s/1qZwFwxI 密码:kh9d

虽然只是一代壳,但是过程思路都是自己的(好像也没啥),刚脱完时还是有一些成就感。
一代壳,脱完了,二代三代有空也会总结一下,不过都是前人的思路,甚至具体细节都搞不太懂,还望指教。同时也希望自己能在逆向,移动安全上越走越远。

猜你喜欢

转载自blog.csdn.net/m0_37344790/article/details/79102031