在前一篇文章中详细介绍了 Android现阶段可以采用的几种反调试方案策略,我们在破解逆向应用的时候,一般现在第一步都回去解决反调试,不然后续步骤无法进行,当然如果你是静态分析的话获取就没必要了。但是有时候必须要借助动态调试方可破解,就需要进行操作了。现阶段反调试策略主要包括以下几种方式:
第一、自己附加进程,先占坑,ptrace(PTRACE_TRACEME, 0, 0, 0)!
第二、签名校验不可或缺的一个选择,本地校验和服务端校验双管齐下!
第三、借助系统api判断应用调试状态和调试属性,最基础的防护!
第四、轮训检查android_server调试端口信息和进程信息,防护IDA的一种有效方式!
第五、轮训检查自身status中的TracerPid字段值,防止被其他进程附加调试的一种有效方式!
所以本文就来一一讲解如何解决这几种方式的反调试方案。
第一种:找到关键方法,注释代码
这种方式需要采用静态方式分析代码,找到关键方法进行反调试代码功能注释,这种方式可以应对与上面所有的反调试方案,但是对于轮训检查这种方式就不太适合了,为什么?操作过的同学会发现,在去除反调试功能的时候那种痛苦了。所以这种注释代码,个人觉得只适用于以下几种反调试:
第一、自己附加进程
这个可以IDA打开关键so代码,找到这段代码处:ptrace(PTRACE_TRACEME, 0, 0, 0),直接nop掉即可。这个没什么难度,因为就一行代码,说白了就几条arm指令罢了。IDA静态分析so也是无压力的。
第二、签名校验
最后总结了一个比较简单的过滤签名校验的方法:先在Jadx中打开应用之后,全局搜字符串内容:"signatures",这个就可以定位到获取应用签名信息的地方了,然后可以依次跟踪校验的地方了。找到具体地方代码直接注释即可。但是如果服务端交互信息中携带了签名校验,而签名校验又在so中,那么就需要另外操作了,这部分知识点将在后面单独一篇文章详细介绍如何破解。
第三、调用系统api判断当前应用是否处于调试状态
这种方式看到我们实现的逻辑还是比较简单的,直接调用系统的android.os.Debug.isDebuggerConnected()方法和判断当前应用属性:ApplicationInfo.FLAG_DEBUGGABLE,那么可以依然采用全局搜索反编译之后的应用内容,找到这部分内容,然后直接注释代码即可。
第二种:修改IDA通信端口
上面分析完了,直接使用静态方式+注释代码功能解决了之前提到的三种反调试方案。但是还有两种没有解决,下面就会详细介绍一种非常靠谱方便永久的方法。而这部分内容才是本文的重点。首先来看看如何解决之前提到的利用检查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中进行修改即可:
记住地址:B64C,去010Editor进行修改即可:
这样我们就全部改好了,保存android_server文件,再次使用IDA打开,找到一个地方查看修改是否成功:
的确修改成功了,下面我们把android_server拷贝到设备中运行,看看端口是否为221(0xDD):
看到了,这里成功的修改了,android_server监听端口了,主要当打开IDA进行连接的时候需要注意端口是221,而不是23946了,或者你可以用adb forward tcp:221...命令进行转发也可以!
第三种:修改boot.img文件,跳过反调试
这种方式是为了解决现在常用的反调试策略,就是轮训检查进程的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
六、总结
本文介绍的内容主要是如何解决反调试问题,主要是三种方案,最后一种修改手机内核文件的操作比较繁琐,遇到的问题也会比较多。但是如果要是成功了,以后进行破解逆向就方便多了。所以就努力看文章,自己手动操作一次。
更多内容:点击这里
关注微信公众号,最新技术干货实时推送