唉唉~没办法,因为楼主是天秤座的,天生太懒了,所以这个帖子仅仅是简单说一下思路,截图什么的就肯定看不到了,而且我已经把题目的APK搞丢了。
补充一些题外话。如果参加过上次娜迦举办的沙龙,应该知道我现在也是从事apk保护工作的,所以限于工作原因,很多技巧都不应该公开的。但是这次比赛,还是公开了很多逆向技巧。
例如kill这东西,至少我没在别的公开场合见别人用过。只要合理利用kill,可以解决大部分反调试的附加问题(必要时需定制ROM)。
希望大家还是以学习为主,如果某天你利用这些技巧,破解了xx保护,xx加固,xx壳的话,只要偷着乐就好,还是不要拿出来炫耀了。毕竟很多人是靠这个吃饭的。
PS:还好我BOSS不上看雪,否则鸭梨山大啊。
第一题
就不说了,会点android逆向的都能很简单的搞定。
好吧,看到有人说jd-gui,有一部分看不到java代码。我只想说,根本就不需要看啊,看到那个Log.i就应该知道看log就行了。
第二题
把so拖到ida里面,找到native函数,按F5,就能看到一个很明显的比较循环,动态调试,在循环上下断点看内容就OK了。
看讨论,很多人说有反调试,过不了。对此貌似没有任何印象。
不过我的环境是自己改过的,有一些常规过反调试的措施。
根据后面几道题看到的内容,估计是读取了/proc/<pid>/status文件里面的状态和TracerId,这个自己想办法把读取的内容改了吧,一般是hook gets,之前有篇文章貌似说过。
一劳永逸的办法就是定制一个kernel,把调试信息全部去掉。
第三题
从这道题开始有点小麻烦。
就算有一些过反调试的措施,在我的环境下,ida一attach上去,程序就会退出。
看到有ptrace调用TRACEME,但返回结果都是-1,貌似是没影响。之前误以为是什么ptrace的高级用法,浪费了一些时间。事实上,在完成第三题的时候,我还是不能正常调试。
至于不调试怎么做呢。这里有个小技巧,就是利用kill指令,
kill -19 <pid>
可以让进程暂停,这个时候,使用IDA附加上去,程序就不会退出了。当时我没有运行,如果想运行的话,可以先
kill -18 <pid>
恢复之前暂停的状态,然后在ida里面运行。这个就是后面两道题的调试方法了。还有一些细节后两道题再说。
IDA附加上去之后,我是通过枚举gDvm->userDexFiles这个Hash表来查看加载的dex文件(实际上就两项),发现lib里面一个以y结尾的so被当作jar文件加载了。
(吐槽一下,发现很多人不知道userDexFiles这个东西,我觉得看过dalvik源码的话,对这个印象应该比较深刻才对)
解压缩so可以找到classes.dex文件,反编译发现所有函数内容都是什么RuntimeException。
这个问题请参照:
http://bbs.pediy.com/showthread.php?t=192836
实际就是运行时修改CodeOffset指向真正的代码内容。
这个问题我之前提出过一个简单的解决办法,就是dump大块内存,把真正代码对应的内容也dump出来(32位系统,最大也就4G,还要再去掉内核空间,所以完全能接受),然后baksmali再smali一下,原始dex就得到了。
具体操作,回到ida附加的进程上来,通过gDvm->userDexFiles可以找到DexOrJar结构,然后找so的JarFile结构体,再看DvmDex结构体的第二项,貌似是DexHeader*,指向的就是内存中dex文件首地址。得到地址之后,把dex文件和之前odex头一起dump出来,得到odex文件。然后上010Editor,查看得到的odex文件中class_def里面的类method的CodeOff项,值大概是0x2000+左右(或者0x12000+,根据环境不同也可能是其他值),总之是指向文件之外的内存。
之后对照/proc/<pid>/maps文件,查看从dex所在内存到CodeOff指向的内存,哪些可读,就直接dump,不能读或者没映射的就随便用字符填充。再次dump可以得到一个几M的odex。
然后修复此odex文件,就是用010Editor的DEX模板解析odex,遇到不能解析的值,进行修改。我是偷懒,直接把不能解析的置0了。
然后baksmali + smali可以得到正常的dex文件。
剩下的就是看dex文件代码了。这道题直接看java代码就能得到答案,有几行关键代码
v5 = new e().a(this.c);
这个是把输入进行转换,在class e里面能看到对应关系
v0_6 = v5.substring(0, 2).hashCode();
if(v0_6 == 3618) {
if(v1_6[0] + v1_6[1] != 168) {
这里是用前两个字符得到hashCode。因为对Java不熟,所以枚举了一下hashCode可能的值。
之后两个if就是找到hashCode是3618,且字符ascii值和为168的,找出来貌似是"s5"(记不清了)
byte[] v5_1 = (e.class.getAnnotation(f.class).a() + a.class.getAnnotation(f.class).a()).getBytes();
之后就是读取class e和class a中f的注释(说不清楚,不理解的自己百度getAnnotation,反正我是自己百度的。
之后把得到的字符拼起来,就是最后答案了,当然,要用class e里面的'.'和'_'表示,用空格分隔
第四题
dump dex的方法同上题。不过此题要动态调试。之前说道通过kill暂停进程,可以让ida附加上去,但是一运行,就会收到各种信号,pass给程序,程序就会退出。
当时我采用的方法是,直接手工忽略产生的信号,然后看哪个线程产生的信号,就把哪个线程挂起。挂了几个之后,就能正常调试了。
然后回到dex上来,根据流程,找到是把输入框的内容,传给了M$j函数,这是一个native函数。
之后就要动态调试M$j,但是M$j的起始部分是动态生成(注1),然后跳转到SO的某处执行。
因为懒得从头开始调,所以我当时的做法是修改了一下ROM,在dvmCallJNIMethod里面,每调用一个method,就判断method->name是不是"M$j",是的话,就把method->insns输出出来(注2)。这样就能很容获得M$j的地址。
获得M$j的地址之后,就是在地址上下断点,然后动态调试了。调试的时候发现代码进行了混淆,瞬间就蛋疼了。不过还好这是功能函数,这种混淆之前就考虑过。应对办法就是,在该函数所有的BL和BLX指令上面下断点,然后F9运行就行了。当断下来之后,仔细看看BL和BLX调用了哪个函数,参数是什么,返回值是什么。这样M$j的做了什么事,就能猜到了。
结果就是M$j调用了dex里面的bh.a函数,然后把返回值跟某个字符串进行了比较。
然后就是看bh.a的内容,具体忘了,就是几个Java的编码函数,和两个native函数。Java编码函数因为有源码,可以很容易写出解码函数(函数本身也很简单),关键是两个native函数。
用同样的办法调试两个native函数。
第一函数是String操作,关注String的内容,可以发现是把String的第四个(Index=3)字符进行+8操作。
第二个函数内容不是太明显,不过可以看到两个Base64编码的字符串,一个是我们输入的值进行编码得到的,那另外一个就很可疑了。而且还看到了调用strlen获取字符串长度,所以猜测是有长度的比较,说不定就有内容的比较(内容比较看不到)。所以就尝试性地把第二个字符串进行逆操作(解码),惊喜地得到了通关答案。那么剩下的就不用继续分析。
第五题
这道题没有做。其实在做第四题的时候,我就猜到第五题肯定就是把代码混淆做到算法里面去了。
之前考虑过代码混淆,算法+代码混淆是最佳组合,反正我没想到什么巧妙的办法去解。所以看到native函数内容就直接放弃了,太伤脑细胞。
在此不得不说一下第一名的暴力枚举想法还是不错的。我有考虑到是比赛,但是没想到可以暴力破解,因为考虑到第二题的答案,如果还是那种的话,基本跑不出来。
在我考虑的比赛中,觉得如果肯花费一点时间,把那个native函数单步走上两三遍,肯定能有所发现的。毕竟通过第四题来看,这次的代码混淆主要都是一些跳转指令,并没有对中间数据(我猜的)进行混淆,这样就有分析的目的了,不管怎么跳,怎么修改值,盯住数据不放就行了。
假如有数据的混淆,例如输入值是a,进来就是a^b^c^d^e^f^g = x,然后在用到a的时候,预设一个值h=b^c^d^e^f^g,这个时候直接用x^h就是a了。当然,这只是简单的说明,实际上可以更复杂。
所以啊,虽然知道这道比赛题可以做,还是要放弃,为了我的脑细胞着想。
注1:DEBUG模式启动进程,在RegisterNativeMethod(貌似是这样写,不是做开发的,记不清楚了)函数下断点,就能看到起始地址,但是该地址内容全为0,F9之后,再次断在RegisterNativeMethod时,可以看到该地址被写了4个字节
注2:很多人不认为对于native函数,method->insns是指向函数地址的。实际上,在Android4.4(之前的好像不是,我没看过源码),native在调用过一次之后,insns会被置为函数的实际地址,如果想了解,请参看dvmResolveNativeMethod的实现。
OK,解题思路就说完了,剩下的就是吐吐槽
关于这次比赛,总觉得线上长时间比赛按照时间排名很不科学。例如以前参加某安全公司的线上竞赛,目测第一就被内定了,然后告诉我说什么你提交的晚啦什么的。尼玛,一个月的比赛,哥20号左右才知道,然后提交了卡着各种不给判题,出不了成绩,然后我就只能呵呵了。
当然啦,这次的第一,还是让我比较信服的。确实自叹不如,尤其还这么努力。
还有就是我所期待的ptrace的题目没有出现。总之ptrace也是纸老虎啊。
如果看过我以前的看雪文章的话,就应该知道,我本来是做漏洞分析的啊,顺便兼职程序猿。然后自从几个月前决心转移动安全,就开始找相关工作。一开始是要做Android的漏洞挖掘,结果面的公司都说我资历不够,让我做别的先练手,只有某某某安全大师给了非常好的职位和待遇,结果因为个人原因,没有选择去。之后就是选择了现在这份工作,做Android Apk加固的相关研究。从此就走上一条不归路啊!!!
之前发帖吐槽逆向,貌似对想入这行的人影响不好。但这些纯属个人观点吧。因为我的强项还是分析,逆向这种东西,纯属一种工具而已,需要的时候用用就好,专门当作职业的话,我还是觉得太累了。
尤其是现在Android代码混淆开始出现,真心觉得这行混不下去了。所以啊,诸位有谁是做移动端漏洞研究的,可以联系一下我哦,我们要多亲近亲近才是。
总之呢,说转行是认真的,但不是立刻,我还是要先做些准备,欢迎各位来讨论移动端漏洞相关的问题。
这次发帖呢,学学玩命个,跟大家混个脸熟,说不定以后还会共事呢。
OK,就这些吧,有啥问题请回帖说明,稍后我会尽量一一答复的。
阿里云助力开发者!2核2G 3M带宽不限流量!6.18限时价,开
发者可享99元/年,续费同价!