【PoRE】Lab9: Debugging

回到目录

内容总结

  • 继之前的动态插桩(Dynamic Instrumentation)之后,这节课又介绍了一个很BUG级别的武器——调试(Debugging)。其实在之前CS:APP的Lab中就已经有涉及过调试(BombLab等)了,当时使用的是GDB,那么这次使用的是IDA。调试,可以认为是一种动态的逆向手段,通过设置断点(Breakpoint)等方法动态地查看程序或代码的运行情况。

  关于动态插桩,Lab中并没有涉及,课件也主要围绕动态插桩的原理,以及两个Android插桩工具Xposed、Frida展开。关于这两个工具,会有PJ涉及它们的实践,网上比较好的教程也比较多,出于版权的考虑这里就不转载了。

  • 课件中还介绍了调试的原理,以及如何这些原理实现反调试(Anti-debugging)。譬如,Linux系统下提供的ptrace()系统调用,以及GDB工具的使用。在Android Studio中可以装Smalidea这一插件进行调试。
  • 实现反调试,就需要针对这些工具如何实现调试,进行精准“爆破”。从apk本身的角度来说,设置Manifest中android:debuggable,或者使用android.os.Debug.isDebuggerConnected()这一API。从ptrace()来说,可以让自己进程调用ptrace(),目标为自己(ptrace myself)。此外,使用IDA调试时,由于需要android_server,可以通过查看端口23946及对应的uid来初步判断是否可能被调试。

Lab简介与参考

  • 这个Lab一共分成两个部分(在同一个apk中实现了两个按钮),需要获取两个flag,应对应Smalidea和IDA的使用。不过在这里,我的两个Flag都是通过IDA调试Native Code获取的。据说使用Smalidea的话会更简单,这里就不提供新的解法了。(懒得再做一遍吧??!)
  • 先来看看Task 1。

  还是一样,每次都使用jadx先看看反编译得到的代码。可以在MainActivity中找到两个onClick()函数,对应两个按钮组件的实现。注意到,onCreate()函数很长,能捕捉到一些关键词,比如Xposed、Signature……这些应该是助教设置的“保护措施”。Xposed模块的检测可能更烦一些,如果不影响做PJ的话平静地把Xposed模块删了就可以。如果不舍得或不能删的话,就把这两段代码全给绕过了,就没有问题了。
  然后阅读Flag 1对应的代码:
图1: Flag1对应按钮的onClick()函数
  大概地看看逻辑,charArray是我们的输入,charArray2是调用generate()函数后的结果,而这个函数是native函数!麻烦了!在此之后是针对两个数组的异或操作。不过这里有一个问题,就是细看这个代码逻辑会非常奇怪:z2在没有初始化的情况下就放入了if条件,以及z2 = true;退出循环后对应的if-else竟然是提示"Wrong"的那一个。在这里我们默认逻辑就是我们想的最简单的那样,事实上也是如此——读者可以大概地看看Smali代码,就能发现jadx也没有那么靠谱。(好像之前说到Decompiler的时候我留了个伏笔来着0.0)
  后面的异或操作,简简单单的一小段代码就可以帮助我们了。难度在于generate()函数。

  如果你使用Smalidea进行调试的话,这一步其实很简单了:只要查看调用generate()调用完后对应的结果就可以了。而前往查看Smali代码,就是要看看为什么会生成这个。

  使用apktool解压之后,用IDA打开。找到generate()函数。
图2: generate()函数的关键部分
  我们并不关心前面如何生成。不过这里有一个很奇怪的if条件判断。如果我们先忽视它,在return (int)(* a1)->newStringUTF(a1, v10);这句语句前打个断点的话,会惊奇地发现每次调试,v10的结果都指向hahaha。(感觉又是来自助教的嘲讽!!)
  实际上这里就是一个反调试手段了。那么如何攻破它呢?一个比较可行的方法,我们在之后的Task 2中会看到。这里能发现一个更有趣的地方:如果反调试手段没有起到效果,它会让v10指向v12。无论返回的是什么,我们只需要搞懂v12是什么就可以了!
  我们之前说,调试的方便之处在于可以看到程序运行的即时状态。进入调试模式之后,查看x86代码,找到这个if内的语句:
图3: 找到v12
  图中打了两个断点,一个断点对应的是IDA代码中的*(_BYTE *)(v12 + 21) = 0;,对应字符串结尾。事实上只要前面这个断点就可以了。点击运行。

  关于较新版本IDA(我使用的是7.5)的使用,助教给的一些参考似乎已经“过时”了。这里简单地说一下:连接android_server以及端口转发都是必要的,在IDA中选择Debugger - Attach to process即可,选择合适的进程即可。之后,在虚拟机内点击按钮,会发现没有任何反应,这就说明调试工具起了作用,随后点击绿色开始(或者Debugger - Start process)即可。

  对应的x86代码是在修改EDX寄存器,那么在断点执行到这里的时候查看EDX寄存器:
图4: 查看EDX寄存器(1)

  记下这个地址(复制粘贴,或者靠鱼的记忆),然后任意选中左边的窗口,按下快捷键G,就可以选择跳转到任何地址。输入对应地址即可。

  找到EDX寄存器对应的字符串:
图5: 查看EDX寄存器(2)
  看起来助教不仅很会嘲讽,也很调皮(逃)。记下这21位长度的字符串,就是generate()的结果了。之后回到Java代码,也就是和charArray数组的21个数字进行位异或运算,得到的结果就是flag的内容。这里跑个代码就行:
图6: C++代码
  轻量级的Dev确实很香。跑完之后,Flag1就搞到手了。

  • 总结一下Flag 1的获取,其实最简单的方法就是使用Smalidea,直接获取generate()函数的返回值,不用关心它具体的生成方法。这里并没有使用。多说几句,结合我们之前学到的内容,实际上会有其他的方法解决这个问题,比如可以进行重打包攻击(绕过签名检查即可),多来一句打印generate()结果(即charArray2),也可以起到我们想要的效果。(这就是一种“静态”的攻击了)
  • 接下去我们来看看Flag 2的获取。

  还是一样,先找到代码:
图7: 第二个按钮的onClick()函数
  这个函数就写得很干净了……关键之处在于调用了一个native方法flag2,需要传入一个字符串,返回要是一个布尔值。接下去开工!
  但是可想而知,必然会有拦路虎。第一个拦路虎就是,找不到flag2函数!助教实际上也给了提示(看了一下学姐的博客,似乎上一届没有这个提示??),是关于JNI的动态注册机制导致的。读者可以前往这篇博客了解一下动态注册机制,并确保在阅读下文之前搞清楚它。
  那么事不宜迟,找到onLoad()函数,找到接口:
图8: 动态注册
  对比一下动态注册机制要求的三元组,也就是代码中的v9了。看看它的内容:
图9: 查看v9(1)

  可以通过双击,迅速地查看这些地址下的内容。

图10: 查看v9(2)
  看到三元组的内容依次是flag2,O0OO0OO00O000OO()和()Z,第一个和第三个分别是native方法名和返回值类型,中间这个就是真正的函数。不得不说,助教起名字的功力也真不浅。接下去看这个函数,祈祷不会再有什么妖魔鬼怪了。
图11: 真正的flag2函数的关键代码
  跳过一大片代码,找到关键的地方,就是这个返回值的理想来源,要比较两个字符串。猜测一下,应该是flag和输入的比较。那么最开始想得很简单:在这里打个断点,看看v13,看看v12,不就好了?然事不尽如人意:这个断点在调试时根本执行不到。罪魁祸首在于最外面的一个if判断:
图12: 罪魁祸首
  刚才我们绞尽脑汁,绕过了这个神出鬼没的条件,这回只能勇敢面对它了。要想执行到里面,看看两个变量,就必须要通过这个条件,这时候祭出IDA调试的一个强大功能——寄存器修改!
图13: 读x86代码(1)
  读一下x86代码,发现关键的判断是.text:BE656C50这一句,在这一句打个断点。
  这个指令的两个操作数,其一是寄存器CL,其二是EDX指向的数据。修改这个栈结构可能会招致奇怪的结果,但是CL我们是可以随便改的。打下断点之后查看两个寄存器:
图14: 读x86代码(2)
  看一下,这里的trick_time是0,而ECX的最后一位是1(在右边的视野可以看到),异或的结果就是1,会进入标签loc_BE656ED5,而这个跳转地址对应的地方就是直接返回0,显然不是我们想要的。于是修改ECX寄存器,简单地把最后一位改为0:
图15: 修改ECX寄存器
  然后逐步执行,发现进入了我们想要的语句!
图16: 达到了想要的结果

  可以看到图中的EIP,这就验证了我们顺利进入了下面我们想要它进入的地方了。

  那么之后的内容就和我们最开始设想的一样:在修改result对应的x86代码处打个断点,也就是查看strcmp函数的两个参数:
图17: 在strcmp函数调用前打个断点
  运行,直到遇到这个断点,查看寄存器状态:
图18: 查看寄存器状态
  也就是图中的ESI和EAX。分别跳转至对应的地址,其中一个就是我们在第二个按钮对应的文本框内的输入内容,另一个就是我们要获取的Flag 2。任务至此,顺利完成。

写在最后

  • 如各位所见,这个Lab虽然放在偏后的位置,助教也说“后面的Lab挺简单的”,但事实上似乎并不如此,其中IDA的使用,也需要初学者琢磨一会。虽然我个人认为,这些功能的使用只是这个强大工具的冰山一角,不过在这个Lab中也有许多操作了,在之后的PJ要攻破别人的native code时,这些技巧也是必要的。
  • Lab中的报告,除了这两个Flag及对应过程之外,还需要总结一下在这个代码中有哪些Anti-debugging的技巧。我们所绕过的那个if,也就是两个Flag获取中都出现的那三个trick_xxxx,就是一个Anti-debugging的强大技巧。除此之外还有一些。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值