在前一篇文章中详细介绍了Android现阶段可以采用的几种反调试方案策略,我们在破解逆向应用的时候,一般现在第一步都回去解决反调试,不然后续步骤无法进行,当然如果你是静态分析的话获取就没必要了。但是有时候必须要借助动态调试方可破解,就需要进行操作了。现阶段反调试策略主要包括以下几种方式:
第一、自己附加进程,先占坑,ptrace(PTRACE_TRACEME, 0, 0, 0)!
第二、签名校验不可或缺的一个选择,本地校验和服务端校验双管齐下!
第三、借助系统api判断应用调试状态和调试属性,最基础的防护!
第四、轮训检查android_server调试端口信息和进程信息,防护IDA的一种有效方式!
第五、轮训检查自身status中的TracerPid字段值,防止被其他进程附加调试的一种有效方式!
所以本文就来一一讲解如何解决这几种方式的反调试方案。
这种方式需要采用静态方式分析代码,找到关键方法进行反调试代码功能注释,这种方式可以应对与上面所有的反调试方案,但是对于轮训检查这种方式就不太适合了,为什么?因为大家如果看过这篇文章:脱掉360加固应用保护壳;操作过的同学会发现,在去除反调试功能的时候那种痛苦了。所以这种注释代码,个人觉得只适用于以下几种反调试:
第一、自己附加进程
这个可以IDA打开关键so代码,找到这段代码处:ptrace(PTRACE_TRACEME, 0, 0, 0),直接nop掉即可。这个没什么难度,因为就一行代码,说白了就几条arm指令罢了。IDA静态分析so也是无压力的。
第二、签名校验
这个在之前的文章中介绍过了,不了解的同学可以查看这篇文章:Android中破解某应用的签名校验逻辑;最后总结了一个比较简单的过滤签名校验的方法:先在Jadx中打开应用之后,全局搜字符串内容:”signatures”,这个就可以定位到获取应用签名信息的地方了,然后可以依次跟踪校验的地方了。找到具体地方代码直接注释即可。
但是如果服务端交互信息中携带了签名校验,而签名校验又在so中,那么就需要另外操作了,这部分知识点将在后面单独一篇文章详细介绍如何破解。
第三、调用系统api判断当前应用是否处于调试状态
这种方式看到我们实现的逻辑还是比较简单的,直接调用系统的android.os.Debug.isDebuggerConnected()方法和判断当前应用属性:ApplicationInfo.FLAG_DEBUGGABLE,那么可以依然采用全局搜索反编译之后的应用内容,找到这部分内容,然后直接注释代码即可。
上面分析完了,直接使用静态方式+注释代码功能解决了之前提到的三种反调试方案。但是还有两种没有解决,下面就会详细介绍一种非常靠谱方便永久的方法。而这部分内容才是本文的重点。首先来看看如何解决之前提到的利用检查IDA调试端口23946这个反调试方案。这个其实思路很简单,因为你检查的端口号是默认的23946,所以我们如果能把这个端口号改成其他值,那么其实就解决了。
修改这个端口号,也比较简单:网上有一种方案就是android_server本身支持自定义端口号的,命令很简单:
./android_server -p12345;直接加上-p参数即可,注意端口号和参数之间没空格:
有的人说,这方法这么简单,那下面就不介绍了,当然不是,我写文章的目的不是为了简单,而是为了让大家了解更多的知识,宁愿多走弯路,走多条路出来。而且上面的这种方式每次都加-p比较麻烦,我想用另外一种方式去一次性解决问题,同时我更想在这个过程中熟悉一下IDA的使用,使用IDA打开android_server文件,其实他是elf格式的,打开无压力,打开之后使用shift+12查看字符串内容界面:
找到这三处关键字符串内容,我们可以通过以往运行过android_server之后的提示信息察觉:
找到这三处字符串内容,下面就简单了,一处一处进行修改,双击字符串条目内容:
选中按X键,进行切换:
选择第一个跳转到arm指令处:
这是graph视图,可以使用空格键进行切换:
看到arm指令了,LDR R5,=0x5D8A;其中0x5D8A就是十进制的23946,也就是默认端口号,所以这里我们只需要将这个arm指令,改成MOVS R5,#0xDD;可对R5进行重新赋值,这里赋值为DD,也就是221;这里有个小问题就是如何进行修改,IDA中可以切换到Hex View视图进行修改编辑二进制,但是这样修改不会生效到源文件中,所以我们这里还得借助一个二进制编辑工具010Editor,我们使用这个软件打开android_server之后,使用Ctrl+G可以直接跳转到指定地址,使用Ctrl+F可以跳转到搜索内容处,记住以下这两个快捷键。
这里看到了99 4D就是:LDR R5, =0x5D8A 对应的指令十六进制值,关于指令和十六进制值之间转化可以去网上搜一个小工具即可。我们想将其变成 MOVS R5,#0xDD 指令,对应的十六进制是:DD 25,其中DD就是立即数值,25表示MOVS R5指令。所以下面就可以直接进行修改即可:
修改完成之后,进行保存即可,这样我们就修改好了一处,还有两处操作一模一样:
继续修改init_sockets处,命令都是一样的,记住地址:B98A,去010Editor中进行修改即可:
然后继续修改IDA Android 32-bit...处
记住地址:B64C,去010Editor进行修改即可:
这样我们就全部改好了,保存android_server文件,再次使用IDA打开,找到一个地方查看修改是否成功:
的确修改成功了,下面我们把android_server拷贝到设备中运行,看看端口是否为221(0xDD):
看到了,这里成功的修改了,android_server监听端口了,主要当打开IDA进行连接的时候需要注意端口是221,而不是23946了,或者你可以用adb forward tcp:221...命令进行转发也可以!
这种方式是为了解决现在常用的反调试策略,就是轮训检查进程的TracerPid值,所以我们需要修改设备的boot.img文件,将这个值直接写死为0即可。关于如何修改操作,看雪上已经有大神讲解了非常详细的过程,我就是按照这个流程进行操作的:http://bbs.pediy.com/thread-207538.htm,因为每个设备的boot.img都不一样,所以在操作的过程中可能遇到很多问题,所以下面就把我操作的过程中遇到的问题讲解一下,顺便精炼的说一下步骤:
第一步,你得有一个可以折腾的root手机
因为现在是在玩boot.img了,后面得刷机,所以你得搞一个你觉得没多大意义的设备,即使成砖头了也无妨。当然一般不会成为砖头。
第二步:root环境下提取zImage内核文件
这里我用的是三星note2设备,自己刷了一个CM4.4系统,按照大神的贴中先去找到系统boot的文件位置,这个路径一定要注意:/dev/block/platform/[每个设备目录不一样]/by-name;其中platform目录中的子目录因为每个设备都不一样,所以需要注意,查看自己设备目录名称,然后进入到by-name之后,使用 ls -l 命令查看详情,找到一项BOOT,记住link的路径地址,这里是/dev/block/mmcblk0p8,然后使用命令,将boot导出为boot.img
dd if=/dev/block/mmcblk0p8 of=/data/local/boot.img
adb pull /data/local/boot.img boot.img
这里可能有人会遇到一个问题就是,看到多个BOOT,比如BOOT1,BOOT2,这里可以选择BOOT即可,也有的人会发现没这个选项,那么只能在刷个其他系统进行操作了。
第三步:借助bootimg.exe工具解压boot.img文件
这个工具我会在后面一起给出压缩包,命令用法很简单,
解包是:bootimg.exe --unpack-boot boot.img
压包是:bootimg.exe --repack-boot
这里有一个坑,我找到两个版本,第一个版本工具操作之后刷机总是黑屏启动失败,最后找到了第二个版本工具才成功的。其实这些工具原理很简单,就是解析boot.img文件格式罢了,因为boot和recovery映像并不是一个完整的文件系统,它们是一种android自定义的文件格式,该格式包括了2K的文件头,后面紧跟着是用gzip压缩过的内核,再后面是一个ramdisk内存盘,然后紧跟着第二阶段的载入器程序(这个载入器程序是可选的,在某些映像中或许没有这部分):
我们想要的是kernel内核信息。所以用这个工具进行操作之后,会发现有这么几个目录和文件:
解压之后有一个kernel文件,这个就是内核文件,而ramdisk.gz就是释放到设备目录内容,也就是initrd目录,进入查看内容:
看到了吧,这就是最终设备的目录结构,可以看到这里有init.rc启动文件,default.prop配置文件等。
接下来我们就要对kernel内核文件进行特别处理了:将kernel文件复制为文件名为zImage.gz的文件,并使用010Editor工具,Ctrl+F快捷键查找十六进制内容1F 8B 08 00,找到后把前面的数据全删掉,使zImage.gz文件变成标准的gzip压缩文件,这样子就可以使用gunzip解压了。命令:gunzip zImage.gz;这时候获取到了解压之后的zImage才是我们要处理的最终文件。
第四步:IDA打开zImage内核文件进行修改
有了上面一步得到的内核文件zImage,直接使用IDA打开,但是打开的时候需要注意设置选项:
然后设置开始地址为0xC0008000:
这里为什么要设置成这个起始地址,因为Linux启动内核的地址为0xC0008000;打开之后,我们可以直接shift+F12,查看字符串内容,因为我们想改TracerPid值,所以直接搜字符串"TracerPid"值:
双击进入,这时候我们可以记下这个地址,然后减去刚刚我们那个偏移地址0xC0008000:
也就是0xC0A3853C-0xC0008000=0xA3053C,这里没有像看雪大神操作那么复杂,先去定位函数位置,修改指令,因为每个设备不一样,指令代码就不一样,不具备通用性,所以这里有一个更好的方案,就是直接改TracerPid的格式字符串值,原始格式化字符串内容为:
\t%s\nTgid:\t%d\nPid:\t%d\nPPid:\t%d\nTracerPid:\t0\t\nUid:\t%d\t%d\t%d\t%d\nGid:\t%d\t%d\t%d\t%d\n
这里应该用到了C语言中的占位符%d,来进行值的填充,那么我们可以把TracerPid那一项的占位符%d,改成'0',但是'%d'是两个字符,所以我们可以改成'00',或者'0\t',或者'0\n';只要保证修改后的字符串内容对其就好。这样TracerPid这一项的值占位符就失效,值永远都是0了。而上面计算的地址就是我们要去010Editor中操作的地址,用010Editor打开zImage文件,Ctrl+G跳转到0xA3053C处:
这里我们将其改成'0\t'值,对应的十六进制就是:30 09;这样我们就修改成功了。
第五步:生成修改后的boot.img文件
这里操作其实就是一个相反的过程,首先使用gzip命令压缩上面修改好的内核文件zImage:gzip -n -f -9 zImage;然后
使用010Editor将压缩好的zImage.gz的二进制数据覆盖到原kernel文件的1F 8B 08 00处的位置(回写回去时不能改变原kernel文件的大小及修改原kernel文件后面的内容,否则会很麻烦),这时得到了新的kernel文件内容。这里需要特别强调一下,也就是我踩过的坑:比如kernel原来是10M大小,1F8B0800之前删除的是1M,我们修改之后的zImage.gz大小是8M,那么我们回写覆盖的时候一定是1M~9M的位置,而kernel的前面1M内容和后面1M内容不能有任何改动,搞错的话,刷机会出现启动失败的情况。下面用我操作的案例讲解一下:
这是我修改之后的压缩好的zImage.gz文件,最后一个数据是0x65E18D,然后全选内容复制好,记住之后,再去原来的kernel内容:
在kernel中的1F8B0800位置是0x47A0,那么我们就需要把刚刚赋值的内容从这里开始替换,到哪里结束呢?将这两个地址相加即可:0x65E18D+0x47A0=0x66292D;也就是到0x66292D结束:
这样原来的kernel内容大小肯定不会发生变化了,始终都是0x662967,所以在替换内容的时候内容一定不能发生变化。替换完成之后,将新的kernel文件替换原来的kernel文件,在使用之前提到的bootimg.exe工具生成新的boot.img文件即可。
第六步:刷机boot.img文件
这里有一个坑,在刷机的时候用到的是fastboot命令,但是遇到最多的问题就是这个错误:
这个是因为设备还没有启动fastboot,关于每个设备启动fastboot不一样操作,比如小米是电源键+音量减,三星是音量减+HOME键+电源键;具体设备可以自行网上搜索即可。到了fastboot界面再次运行fastboot就可以了:
fastboot flash boot boot-new.img
然后在运行fastboot reboot重启设备即可。有的同学在操作的时候,始终进入fastboot失败,导致fastboot命令运行错误,这个真解决不了那就换个手机试一下吧。
这时候我们启动设备,然后调试一个app,发现他的TracerPid值永远都是0了,因为我之前将TracerPid改成'00'字符串了,也是可以的:
因为感觉不正规,所以就有重新改成了'0\t'值了。都是可以的。
注意:一定要保存原始提取的内核文件boot.img,当你把设备弄成砖头启动失败的时候,可以在把这个原始的boot.img刷回去就可!
不知道大家以前在看:脱360加固应用的保护壳 文章的时候当时说到了一个工具mprop,他的作用就是能够改写系统的内存中的ro.debuggable这个属性值,这样我们就没必要每次反编译app,然后在AndroidManifest.xml中添加android:debuggable="true",让应用可调试了。
当时说到这个工具有一个弊端就是他只能修改内存中的值,当设备重启就会失效,那么现在我们可以让他永久有效,其实这个属性值,是在系统根目录下的default.prop文件中的,设备启动就会解析存入内存中。所以如果我们能够把这个文件中的值改成1,那么就永久有效了。在上面解包boot.img的时候,说到了有一个initrd目录,其实default.prop就是在这个目录下:
这里我们直接将其改成1,因为我们现在已经进行了修改boot.img操作,那就顺便把这个功能也给改了。多方便呀!
第一步:设备root之后,查看设备的内核文件路径: cd /dev/block/platform/[具体设备具体查看]/by-name,然后使用命令ls -l 查看boot属性的,记住路径
第二步:dd if=/dev/block/[你的内核路径] of=/data/local/boot.img
adb pull /data/local/boot.img boot.img
第三步:使用bootimg.exe工具进行boot.img解包;得到kernel文件,将kernel文件复制为文件名为zImage.gz的文件,并使用010Editor工具,Ctrl+F快捷键查找十六进制内容1F 8B 08 00,找到后把前面的数据全删掉,使zImage.gz文件变成标准的gzip压缩文件,这样子就可以使用gunzip解压了。命令:gunzip zImage.gz
第四步:使用IDA打开zImage内核文件,记得设置选项和起始地址:0xC0008000;打开之后,使用shift+F12查找到字符串“TracerPid”值,记住文件起始地址,然后减去0xC0008000;在使用010Editor打开内核文件,Ctrl+G跳转到这个地址,进行内容修改,将TracerPid那个占位符‘%d’改成‘0\t’保存即可
第五步:首先使用gzip命令压缩上面修改好的内核文件zImage:gzip -n -f -9 zImage;然后使用010Editor将压缩好的zImage.gz的二进制数据覆盖到原kernel文件的1F 8B 08 00处的位置(回写回去时不能改变原kernel文件的大小及修改原kernel文件后面的内容,否则会很麻烦)
第六步:启动设备为fastboot模式,然后使用fastboot命令进行刷机:fastboot flash boot boot-new.img,然后在重启即可
总结一张图:
踩过的坑
坑一:一定保留最原始提取的内核文件boot.img,为了防止你刷失败了,可以还原操作。
坑二:修改TracerPid值时,只需要将‘%d’占位符改成‘0\t’即可,无需改动arm命令操作。
坑三:在还原kernel文件的使用一定要记得不能改变原始kernel文件的大小。
坑四:fastboot命令运行失败,设备必须处于fastboot模式,如果还不行那就换个手机吧。
第一:关于反调试的第一种解决方案比较简单,就是静态分析代码,找到反调试的位置,然后注释代码即可。
第二:对于监听IDA端口反调试,通过修改android_server的启动端口,这里也学会了如何修改端口号操作。
第三:修改内核文件,让TracerPid始终为0,ro.debuggable属性值始终为1,这个操作过程还是有点繁琐的,遇到的问题肯定很多,而且每个人遇到的问题可能不一样,但是这是一个锻炼的过程,如果成功了意味着你学会了提取内核操作,了解内核文件结构,学会分析内核文件,修改内核文件。意义重大。比如你还可以修改设备的启动图,慢慢的你可以定制自己的rom了。
第四:在以上操作中,也熟悉了IDA工具使用,了解到了字符串内容永远都是寻找问题的最好突破口,IDA中查找字符串Shirt+F12即可,010Editor中Ctrl+G和Ctrl+F查找快捷键。
解包boot.img文件的工具下载地址:http://download.csdn.net/detail/jiangwei0910410003/9793611
本文介绍的内容主要是如何解决反调试问题,主要是三种方案,最后一种修改手机内核文件的操作比较繁琐,遇到的问题也会比较多。但是如果要是成功了,以后进行破解逆向就方便多了。所以就努力看文章,自己手动操作一次。看完文章之后,记得多多点赞和分享扩散,要是有打赏就最好啦啦!
更多内容:点击这里
关注微信公众号,最新技术干货实时推送