现在主流的加固平台有:梆梆加固,爱加密,360加固,腾讯加固,在之前的1篇文章中介绍了:如何脱掉“爱加密”的壳,现在这里要脱掉另外1个平台的壳:360加固,由于有了之前的脱壳经验,很多基础知识和准备工作这里就不详细介绍了,为了能够脱掉他家的壳,用1个案例来去360平台进行加固,然落后行脱壳。下面就来开始脱壳:
首先拿到加固以后的apk,这里为了方便查看内部信息,先不用dex2jar+jd-gui工具进行分析了,直接使用我们之前分析了源码的1个工具:Jadx,直接查看:
其实现在的加固的常规套路都差不多,这里看到和之前分析的爱加密加固的情势几近1样,这里的壳Application是StubApplication在attachBaseContext中做1些初始化操作,1般是将assets目录中的so文件拷贝到程序的沙盒目录下:/data/data/xxx/files/..;然后再用System.load进行加载,通过查看可以得知源程序apk已被加密了,就是寄存在这里的so中,之前的文章也是分析了,1般源程序加密以后就寄存在那几个目录下,1般是:dex文件尾部,libs目录,assets目录。
下面再来看1下他的AndroidManifest.xml文件:
找到了他的入口Activity了,但是这里没有android:debuggable="true",所以程序是不能被调试的,所以我们需要添加这个属性,然后在进行回编译进行调试,这时候候就需要使用到apktool工具了:
好了,这里看到,360加固为了避免apktool反编译功能,添加了1个qihoo属性,这个属性apktool不认识就报错了,但是我们之前的1篇文章已介绍了:Apktool工具毛病修复,我们有了apktool源码,可以直接进行修复的,然落后行反编译:
反编译成功了,查看他的AndroidManifest.xml文件内容:
的确,是有1个属性qihoo,这个就是Android系统在解析apk文件的时候,发现不存在的属性直接略过,但是apktool工具却不会,360加固就是利用这个漏洞来增加反编译难度的,但是我们之前的1篇文章中介绍了如何修复,这里修复很简单了。所以说只要有了apktool源码,甚么都好做了。
然后我们在添加android:debuggable属性:
然后回编译:
这时候候看到,在回编译的时候也是报错了,说找不到这个属性,为了方便这里直接把android:qihoo给干掉,由于其实他没有任何作用的,就是为了干扰反编译工作的,所以直接去掉便可,然后在回编译:
好了,回编译成功,然后在进行签名打包便可。这里就不在介绍了。
那末从上面我们可以看到,其实360加固为了避免反编译,就利用了Android系统本身在解析apk的时候,遇到不认识的属性直接略过,而apktool工具却不会的漏洞来给AndroidManifest.xml中添加1个混淆反编译的属性:qihoo,幸亏我们有源码,可以修复这个问题,在进行反编译便可,这里也希望apktool官网能够及时修复这个漏洞。为了回编译成功,我们可以直接把这个属性删除。不然回编译也是会报错的。这个属性只是360为了混淆反编译工作,所以删除对程序逻辑没有任何影响的。
这里就要开始介绍本文的第1个重点了:如何在不需要反编译的情况下,添加android:debuggable属性,就能够进行调试。
这个现在已有很多工具可以做了,先来讲说具体的原理吧:
其实Android中有1些经常使用的配置信息都是寄存在1个文件中,比如装备的系统,版本号,cpu型号等信息,而这个文件位置在:
/system/build.prop
我们查看文件的内容,可以看到很多装备的信息,而且这些ro开头的表示这些属性值是只读的,不能进行修改的。
同时Android中提供了两个命令来操作这些信息:getprop和setprop命令:
查看系统的sdk版本号
设置系统的sdk版本号为22,可是这里并没有修改成功,缘由就是由于ro开头的属性是不允许后期修改的,改也是可以修改的,需要重新编译系统镜像文件boot.img,但是这里其实不是本人介绍的重点了。
既然Android中的1些系统属性值寄存在1个文件中的,而且这些值是只读的,固然不但可以通过getprop命令读取,有1个api也是可以直接读取的,就是:System.getProperty("ro.build.version.sdk");其实这个方法是native层实现的,具体就不分析了。
那末这个文件是存储这些属性值的,那末是谁来进行解析加载到内存中,能够给每一个app都能访问到呢?
这个工作就是init.rc进程操作的,我们应当了解了系统启动的时候第1步就是解析init.rc文件,这个文件是在系统的根目录下,这里会做很多初始化操作,这里不详细分析了,后面再分析Android中系统启动流程的时候在详细分析。这里同时会做属性文件的解析工作,所以,Android 属性系统通过系统服务提供系统配置和状态的管理。为了让运行中的所有进程同享系统运行时所需要的各种设置值,系统会开辟1个属性存储区域,并提供访问该内存区域的 API。所有进程都可以访问属性值,但是只有 init 进程可以修改属性值,其他进程若想修改属性值,需要向 init 进程发出要求,终究由 init 进程负责修改属性值。
那末上面说到的是system/build.prop文件。里面主要是系统的配置信息,其实还有1个重要文件在根目录下面:default.prop:
这里有1个重要属性:ro.debuggable,对这里就是关系到系统中每一个利用是不是能够被调试的关键。其实在Android系统中1个利用能否被调试是这么判断的:
当Dalvik虚拟机从android利用框架中启动时,系统属性ro.debuggable为1,如果该值被置1,系统中所有的程序都是可以调试的。如果系统中的 ro.debuggable 为0,则会判断程序的AndroidManifest.xml中application标签中的 android:debuggable元素是不是为true,如果为true则开启调试支持。
好了到这里,我们可以总结1下了:
Android系统中有1个可以调试所有装备中的利用的开关,在根目录中的default.prop文件中的ro.debuggable属性值,如果把这个值设置成1的话,那末装备中所有利用都可以被调试,即便在AndroidManifest.xml中没有android:debuggable=true,还是可以调试的。而这些系统属性的文件system/build.prop和default.prop,都是init进程来进行解析的,系统启动的时候就会去解析init.rc文件,这个文件中有配置关于系统属性的解析工作信息。然后会把这些系统属性信息解析到内存中,提供给所有app进行访问,这块信息也是内存同享的。但是这些ro开头的属性信息只能init进程进行修改。下面来分析1下修改这个属性值的3种方式:
第1种:直接修改default.prop文件中的值,然后重启装备
那末现在如果依照上面的目的:就是不需要反编译apk,添加android:debuggable属性的话,直接修改default.prop文件,把ro.debuggable属性改成1便可,但是通过上面的分析,修改完成以后肯定需要重启装备的,由于需要让init进程重新解析属性文件,把属性信息加载内存中方可起作用的。但是并没有那末顺利,在实践的进程中,修改了这个属性,结果出现的结果就是装备死机了,其实想一想也是正常的,如果属性能够通过这些文件来修改的话,那就感觉系统会出现各种问题了,感觉系统是不会让修改这些文件的内容的。
第2种:改写系统文件,重新编译系统镜像文件,然后刷入到装备中
那末上面修改default.prop文件,结果致使死机,终究也是没有修改成功,我们还有甚么办法呢?其实上面已提到过1次了,就是这些属性文件实际上是在系统镜像文件boot.img在系统启动的时候,释放到具体目录中的,也就是说如果我们能够直接修改boot.img中的这个属性便可,那末这个操作是可以进行的,但是困难那是不1般的顺利,最少我没成功过,修改系统文件,然后重新编译镜像文件,最后在刷到装备中。这个进程我尝试过是失败了,不过理论上是可以的。而且这类方式如果成功了,那末这个装备就是永久可以进行各种利用的调试了。
第3种:注入init进程,修改内存中的属性值
那末上面直接重新编译boot.img,然后在刷到装备中的工作是失败的,那末还有其他方法吗?肯定是有的,我们其实在上面分析了,init进程会解析这个属性文件,然后把这些属性信息解析到内存中,给所有app进行访问使用,所以在init进程的内存块中是存在这些属性值的,那末这时候候就好办了,有1个技术可以做到了,就是进程注入技术,我们可使用ptrace注入到init进程,然后修改内存中的这些属性值,只要init进程不重启的话,那末这些属性值就会起效。好了,这个方法可以尝试,但是这个方法有1个弊端,就是如果init进程挂了重启的话,那末设置就没有任何效果了,必须重新操作了,所以有效期不是很长,但是1般情况下只要保证装备不重启的话,init进程会1直存在的,而且如果产生了init进程挂掉的情况,那末装备肯定会重启的。到时候在重新操作1下便可。
好了上面分析了3种方式去设置系统中的调试属性总开关,那末最后1种方式是最靠谱的。
而且思路也很简单,但是我们不会重新去写这个代码逻辑的,由于已有大神做了这件事,具体工具后面会给出下载地址:
这个工具用法很简单,首先把可履行文件mprop拷贝到装备中的目录下,然后运行命令:
./mprop ro.debuggable 1
这个工具可以修改内存中所有的属性值,包括机型信息。
这里修改完成以后,使用getprop命令在查看值,发现修改成功了,但是需要注意的是,我们修改的是内存的值,而不是文件中的值。所以default.prop文件中的内容是没有产生变化的。
这时候候,我们可使用Eclipse的DDMS来查看可以调试的利用列表:
固然也能够使用adb jdwp命令来查看可以调试的进程id:
但是惋惜的是,发现还是没有展现装备中所有的利用,其实这里是有1个细节问题了,由于我们虽然修改了内存值,但是有1个进程我们需要重启1下,哪一个进程呢?那就是adbd这个进程,这个进程是adb的守护进程,就是装备连接信息传输后台进程,所以想看到可以调试的进程信息的话,那末需要重启这个进程,这样连接信息才会更新。
重启这个进程很简单:直接使用stop;start命令便可
其实这是两个命令,用分号隔开,首先是干掉进程,然后在重启。
运行完命令以后,再去看DDMS窗口信息:
这时候候所有的利用进程都是可以调试的了,这时候候我们在使用dumpsys package命令查看1个利用的包信息:
这里可以看到,这个利用的flags标志中并没有debuggable属性值,但是这个利用是可以调试的。所以看到ro.debuggable这个是总开关,只要他为1,开启的话,即便没有android:debuggable也是可以的了。
好了到这里,我们来总结1下:
1、我们的目的是怎样在不需要反编译apk包,添加android:debuggable属性,就能够进行apk的调试?
2、我们通过分析系统属性文件和系统启动流程和解析系统属性文件的流程,知道了装备中关于调试有1个总开关属性值:ro.debuggable,默许是0,不开启的。那末这时候候我们就能够料想有这几种方式可以去修改。
3、分析了3种方式去修改这个属性值:
第1种方式:直接修改default.prop文件中的这个字段值,但是惋惜的是修改失败,在修改的进程中出现死机,重启装备以后,属性值还是0。
第2种方式:修改系统源码的编译脚本,直接修改属性值,然后重新编译镜像文件boot.img,然后刷入到装备中,但是在实践的进程中并没有成功,所以放弃了,而且这类方式有1个好处就是1旦修改了,只要不在重新刷系统,那末这个字段将永久有效。
第3种方式:注入到init进程,修改内存中的这些系统属性值,这类方式实现是最简单的,但是有1个问题,就是1旦装备重启,init进程重新解析default.prop文件的话,那末ro.debuggable值将又重新被清空,需要再次注入修改。
4、最后采取了第3种方式,不过网上已有人写了这样的工具,用法也很简单:./mprop ro.debuggable 1;但是修改完成以后,1定要记得重新启动adbd进程,这样才能够获得到可以调试利用信息。
5、使用工具修改完成以后,在Eclipse中的DDMS窗口发现,装备中的所有利用都处于可以调试状态了。也就是说我们的操作成功了。
那末上面的这个进程成功以后的意义还是很大的:标志着我们以后如果是单纯的想让1个apk能够被调试,去反编译在添加属性值的话,其实这类方式很高效的。可让任意1个apk出于被调试状态。
讲完了上面的1个重点以后,下面我们就开始来说解本文的另外1个重点,开始脱壳了。
第1步:开启android_server
第2步:端口转发
第3步:启动利用
adb shell am start -D -n com.CMapp/com.e4a.runtime.android.mainActivity
第4步:开启IDA,附加进程
第5步:设置Debugger Option选项
第6步:运行jdb调试等待
jdb -connect com.sun.jdi.SocketAttach:hostname=127.0.0.1,port=10265
注意:这里需要注意了,由于我们改了系统的ro.debuggable属性,装备中所有的利用都处于可调式状态,基本端口8700已被占用了,那末这时候候需要使用被调试程序的独有端口了,可以在DDMS窗口进行查看。
第7步:关键函数下断点
首先找到mmap函数的内存地址,这里可以直接使用G键,通过函数名来跳转:
注意:这里和之前的脱爱加密的壳方法可能不1样了,还记得之前脱爱加密的壳的时候,给fopen和fgets函数下断点,由于如果有反调试的话,肯定是读取/proc/pid/status文件中的TracerPid字段值的,然后修改TracerPid值为0便可,但是这个方法对360加固的不好使了,由于360加固的反调试是通过mmap函数来读取/proc/pid/status,所以这里需要给mmap函数下断点了,而且后面还会看到给dvmDexFileOpenPartial这个函数下断点也不好使了,缘由是360加固自己在底层实现了解析dex的函数来替换了这个dvmDexFileOpenPartial函数。但是不论是他自己实现dex解析加载,终究都是需要把dex文件加载到内存中,还是得用mmap函数来进行操作。所以在脱360加固的壳的时候mmap函数是重点。
好了给mmap函数下了断点,下面就F9运行程序吧:
进入到了mmap的断点处,这里由于mmap函数代码比较长,为了节省时间,我们可以在mmap函数的结束处下1个断点,然后直接F9运行到函数的结尾处,由于系统中有很多个so需要加载到内存中,所以mmap函数会履行屡次,但是其实我们最关心的是加载我们自己的so文件,即libjiagu.so文件,由于这个才是我们的native层代码,所以等出现以下界面:
这时候候,说明这个so文件被加载到内存中了,也就是程序的native层代码开始履行了,注意不能在F9了,而是使用F8单步调试:
F8单步运行到这里的时候,遇到1个问题,就是F8了很屡次,始终在这个地方履行,后来分析了arm指令以后,发现原来这里是1个循环,初始值是0,存储在R11中,然后逐渐加1,和R3中存储的阈值作比较,通过查看寄存器的值,发现R3寄存器中是A7,所以这里得去修改寄存器R11的值了,不然我们得单步A7次,这里直接把R11值修改成A6:
修改寄存器也是很容易的,直接右击寄存器:
点击Modify value:
点击OK,以后再来看看R11的寄存器的值:
修改成功了,这时候候在单步F8,两次以后就履行完了循环了,从这里也能够看到,这个地方也算是为了避免被调试,加大调试本钱的1种方式。继续往下走:
到这里,履行完BL以后就退出调试界面了,尝试屡次都1样,所以料想反调试肯定在这里,可以F7跟进去看看:
到BLX这里,每次之前完也是退出调试界面,所以这里还得F7单步进入看看:
这里看到了1行重要的arm指令:CMP比较指令,而且是和0比较,极可能这里就是比较TracerPid的值是不是为0,如果不为0就退出,可以查看R0寄存器的内容:
然后在查看被调试进程的TracerPid的值:
果然R0存储的是TracerPid的值,为了验证正确性,这里继续:
果然,运行到了自杀的地方,1直单步运行:
退出程序了。
那末上面就知道了反调试的地方,就好办了,直接修改寄存器R0的值为0便可:
然后继续单步F8运行,后面还有1个CMP和0进行比较的地方,我们1样进行置零操作,再次单步F8,当运行到此处的时候:
看到memcpy函数的时候,这时候候可以直接运行F9,又会履行到mmap那里,然后顺次F9,还是运行到了上面的那个循环,这样顺次类推,在这个进程中我运行了7次循环,改了R0值改了9次,所以这个地方会履行屡次是正常的,但是这里在我屡次调试以后总了1个好的方法,就是看到屡次履行的线路都差不多:
mmap函数=》循环=》(MOV R0,R8)BL=》(MOV LR,R4)BLX=》CMP R0,#0=》mmap....
这个进程中,其实为了简便我们可以
1》在mmap函数的开始处,结束处下1个断点,这两个断点是为了后面加载内存的dex文件做准备
2》在循环处下1个断点,这个断点是为了修改循环值,节省时间
3》在BL处下个断点,是为了进入BLX
4》在BLX处下个断点,是为了进入比较TracerPid处
5》在CMP下断点,是为了修改TracerPid的值
同时在这个进程中,需要使用F9,直接跳转到下1个断点,高效,只有在到达了CMP处的时候,要用F8单步调试,而且这个地方1定要谨慎,不能按错了,不然又得从头再来,我吃了很屡次亏,也重来了很屡次。只要当看到了memcpy函数的时候,再次F9到下1个断点处。更需要注意的是:每次到达mmap断点处的时候,1定要看当前栈信息的视图窗口,看看是不是出现了classes.dex的字样,由于终究都是使用mmap来把解密以后的dex加载到内存中的,所以这里1定要注意,是本次调试的核心。
固然这个只是个人的调试思路,每一个人都有自己的思路,只要能成功都可以。
就这样来回弄了几次以后,终究看到了曙光:
当再次来到了mmap函数处的时候,终究看到了classes.dex字样了,说明这里开始解密dex然落后行加载到内存了,这时候候不能在F9跳转了,而是F8单步运行,然后查看R0寄存器的值:
每次都是履行完__mmap2这个函数以后,R0就有值了,每次看到R0中有值的时候,可以到Hex View窗口中使用G键开始地址跳转,查看是不是为dex内容:
如果发现不是,就还是单步F8,知道mmap函数结束,然后再次F9,到达mmap函数开始处,时刻看紧Hex View,栈窗口,R0寄存器这3个地方的值:
在屡次尝试以后,终究成功了,这里看到了熟习的dex文件的头信息,关于dex文件的头部信息可以看这篇文章:Dex文件格式解析
所以这里在头部信息的第33个字节然后连续4个字节就是dex的长度了,那末现在有了dex在内存中的其实位置,长度大小,下面就能够使用Shirt+F2打开脚本履行窗口,dump出内存中的dex数据:
static main(void)
{
auto fp, begin, end, dexbyte;
fp = fopen("E:\\dump.dex", "wb");
begin = 0x755A9000;
//偏移0x20处,取4字节为dex文件大小
end = 0x755A9000 + 0x0004BC38;
for ( dexbyte = begin; dexbyte < end; dexbyte ++ )
fputc(Byte(dexbyte), fp);
}
保存到E:\dump.dex,然后在使用Jadx工具进行查看:
这里可以查看到源码了,而且类名,方法名,变量名都是用中文来命名的,感觉好不习惯,但是Java中是支持这么干的,由于Java采取的是Unicode编码的。
案例下载:http://download.csdn.net/detail/jiangwei0910410003/9561416
好了到这里,我们就成功了脱掉了360加固的壳了,下面来总结1下他的壳的特点和调试需要注意的点:
1、首先360加固仍然是外部套1个Application壳:StubApplication,源程序加密寄存在libjiagu.so,放在了assets目录下,在Application启动的时候,释放到利用的沙盒目录files下面,然后在使用System.load方法进行加载,这个和爱加密的方式是1样的
2、关于360加固的反调试,仍然使用的是读取/proc/[pid]/status中的TracerPid字段值,判断是不是为0,但是这里和爱加密不1样的是,在读取这个文件的时候不是用的fopen系统函数,而是mmap系统函数,所以在解决反调试的时候需要给这个函数下断点。
3、360加固底层不是采取dvmDexFileOpenPartial这个系统函数来解析dex然后加载到内存中的,而是自己实现了1个函数,所以给这个函数下断点,然后获得参数值来dump内存中的dex数据是行不通的,但是有1个思路就是不管他用哪一个函数去解析dex加载到内存,终究都得使用mmap这个系统函数来操作,所以还得给这个函数下断点,所以这里在调试的时候需要时刻注意的是当断点到达了mmap函数处的时候,需要视察Stack View栈窗口中是不是出现了classes.dex字样,如果出现了,说明开始解密dex文件,准备加载到内存中了,那末这时候候需要视察R0寄存器的值,然后在Hex View中跳转到指定内存地址,可以视察到是不是为dex内存数据
4、在视察是不是为内存数据的时候,需要注意dex文件是有自己的文件格式的,那末头信息就是个根据,所以我们可以查看开头为:dex.35 这样的内容来判断此处为dex数据,由于dex头部信息中也有dex的文件大小,那末这时候候就能够使用脚本dump处内存中的dex数据了。
5、在调试的进程中,会发现很多断点屡次履行,特别是有1个循环,需要我们修改寄存器的值来快速结束循环,而且在关键处下断点,也是加快调试效力的。
1、本文开始的时候介绍了通过注入系统init进程,修改内存中的系统属性值:ro.debuggable,让装备中所有的利用都可以被调试,这个功能将对后续逆向破解有重大意义,也会省去了反编译的工作。所以这个方式还是很具有里程碑意义的。
2、在脱爱加密的壳的时候,学习到了给fopen和fgets这两个系统函数下断点来解决反调试,在这里我们又多了1个下断点的好去处就是给mmap下断点,当发现给fopen函数下断点不好使的时候,在尝试给mmap下个断点吧。
3、在脱爱加密的壳的时候,给dvmDexFileOpenPartial函数下断点,来获得dex在内存的起始地址和大小,从而dump处内存中的dex数据,但是360加固并没有走这个函数,由于在给这个函数下断点的时候,他压根没走到,所以判定它内部使用了其他的函数去解析dex的,然后加载到内存中的,但是如果最后加载到内存中,那肯定要用到mmap函数,所以只要给mmap函数下断点便可。
本篇文章就介绍了如何脱掉360平台加固的apk利用的壳,在结合之前的1篇脱掉爱加密家的壳的知识,看到现在在脱壳的时候其实就两点,1点是找到关键处解决反调试,1般都是fopen,fgets,mmap,open等系统函数下断点,还有1点就是如何找到内存中dex的起始地址和dex的大小,这个1般现在就是dvmDexFileOpenPartial函数下断点,还有就是给mmap函数下断点。
更多内容:点击这里
关注微信公众号,最新Android技术实时推送
下一篇 扑克牌顺子