CTF安卓逆向练习第一弹

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/wmh_100200/article/details/72847878

CTF安卓逆向练习第一弹–用户名与密码获取

写在前面的话:终于开始写博客了,记录自己逆向学习的一些过程,因为太小白了,所以权当抛砖引玉,供初学者参考。可能会有些错误,欢迎指出。

本篇从ctf逆向的一道题入手,主要练习so调试相关技术,以及断点下法思路,动态调试等。题目来源是ctf2016年逆向的第一题(第一题简单嘛),apk包:re1-e7e4ad1a.apk,网上好多资源就自己去找吧。

整体概述及大概思路

首先把apk在安卓模拟器中运行出来,看一下这个apk大概是做什么的,需要我们做什么。

模拟器中主界面

从这个app的主界面上可以看出这个app的功能就是输入编号和密码,然后点击sign按钮,实现注册成功。所以我们需要做的事情就是找出编号和密码(flag)。
那么遇到这样的问题,一般情况编号(也就是用户名)是写在java层的,然后加密算法也是写到java层的,而最关键的密码则很有可能是写在native层中的。果然,查看一下这个apk的结构,果然看到了so文件,说明这里用到了native的一些东西,这就需要我们不仅要会分析java层还需要会分析native层的一些东西,这里用到了jeb进行java层的分析,还有ida进行native层的分析及动态调试。

java层分析与编号/用户名查找

首先打开jeb,把apk拖进去,分析java代码及apk逻辑结构
整体逻辑

整体结构还是比较简单的,当然也是比较利于分析的,点开Seclreg,发现onCreat
这里写图片描述

说明这是程序的入口,q键查看java代码,发现两个按钮,刚好一个是sign的按钮,一个是exit按钮。
这里写图片描述
点开b,刚好能看到一行注册成功的判断语句
这里写图片描述
该判断语句如下

if((Seclreg.get_username(this.a).equals(this.a.getResources().getString(2131034119))) && (Seclreg.f(this.a).getpl(Seclreg.e(this.a), Seclreg.d(this.a))))

可以看出在&&的前半句是用来判断编号的,后面是用来判断密码的。
先看前半段,有个getString的id号是2131034119,转化为16进制是7f050007,在public.xml文件中,找到id号为7f050007的字段,刚好是username,然后看到它的类型是strings,那么到strings.xml文件中继续查找,对应的字段是secl-007,至此,我们已经把编号找到了。当然有些比较方便的技巧是直接ctrl+F,直接找username的字段,一般都是有的。
这里写图片描述
这里写图片描述
因为本文的apk代码比较少,所以可以每个类都分析一下,比如a类中是对密钥加密的算法,其中代码清楚的显示了使用了des进行加密,同时密钥在strings.xml中也能找到(这个密钥是对密码进行加密时用的)。
这里写图片描述
接下来再分析一下之前if判断语句的后半部分,这里用到了getpl,然后发现这个getpl刚好有native,这就说明此时需要对so文件进行分析。接下来使用ida对so文件进行分析。
这里写图片描述
小tip:因为混淆的原因,我们看到的类都是a、b等不便于分析与记忆的类,当我们进行分析的时候,分析出来某个类的时候,可以按n键,对这个类进行重新命名,命名我们自己熟悉与常用的英文名称,这样面对大型程序的时候就可以很好的阅读

IDA进行静态分析

上面提到so文件,那么就要对so文件进行分析,使用ida进行分析,可以先进行静态分析,把逻辑结构弄清楚之后就可以找到下断点的地方,再进行动态分析。那么,先把ida打开把,把so文件拖进去。可以在exports窗口中直接找到我们需要的函数getpl(一般都是java_com开头),也可以在左侧Function window窗口,用ctrl+F建,进行快速查找。
这里写图片描述
然后定位到代码处,空格键,查看代码逻辑,或F5键,查看伪C代码。查看代码,从下网上看,发现getpl这个函数是关键函数。这里有个小tip,好多时候我们看到有v4+676这样的代码,这个时候选中,点击y键,替换名字为JNIEnv*,此时可以发现,这个变成了我们可以理解,方便阅读的形式。同理,这里的n键与jeb中的功能一样,可以将难以阅读的函数或变量换成我们自己便于理解的词语。
这里写图片描述这里写图片描述
然后确定getpl函数为关键函数之后,双击getpl,跳入getpl函数的代码中。很长的一段代码,因为我们只是做逆向,目的只是为了获取flag,因此没有必要从头阅读详细分析整个程序,因此从后往前阅读,找到关键点即可,没有必要全部读懂。(因为我自己也不会啊)然后果然发现了关键的判断地方,这里有两个关键的地方,一个是if(v11 ==39),也就是说这个密码长度要39位。但是我们实际输入不了那么多,所以这里要把这个判断语句改一下,改成相反的效果,这样程序走流程的时候刚好能走到这个分支,进而执行下面的语句。再然后就是一个j_j_strncmp的一个比较函数,这里一分析,发现s2是我们自己输入的密码,而这个p1刚好就是我们需要进行比较的,也就是要找的flag。
这里写图片描述
那么下面就需要做两个步骤了,一个是把这个判断长度等于39的语句改成相反效果,从而使程序走这个分支,还有一步就是获取下面比较语句中的p1的值,从而找到flag。下面我们把这个伪c代码切换到arm汇编语句中再仔细研究一下(可以通过空格键切换逻辑图定位到关键代码中)。
这里写图片描述
关键代码就在这几句中,其中BNE就是判断跳转代码,通过查找资料可知,BNE对应的相反逻辑是BEQ,BNE对应的hex机器码是D1,而BEQ对应的hex机器码是D0,因此只要找到BNE代码的位置,将dex代码改成D0即可,此时可以点击Hex View界面进行查看,点击F2按钮进行更改,再点击F2按钮保存,这里的F2更改保存只是临时的,并不能保存在磁盘中。
小tip:j_j_strlen是获取长度函数,j_j_strncmp是比较函数

IDA动态调试

下面要进行动态调试,首先先把动态调试的服务开启,在虚拟机中启动IDA的server。

adb shell /data/local/tmp/android_server

小tip:有的时候开启IDAserver的时候如果出现报错,the process list of the remote machine is empty ,此时需要用root权限打开IDA的server

然后进行端口转发

adb forward tcp:23946 tcp:23946

开始动态调试,先在模拟器中运行app程序,因为程序要打开后才会将库等加载进去。然后打开IDA,将so文件拖进去,然后选中Debugger->Remote ARMLinux/Android debugger
这里写图片描述
点开Attach to process,按照id排列,选中要分析的app项目,然后进入动态调试,有的时候需要等一会儿,软件加载进去。
这里写图片描述
下面来确定下断点的位置,通过之前静态的分析,我们知道下断点实在so库中的getpl函数。如何找到这个函数的位置,我们要先找到so文件的库,然后再找到这个函数所在,因此点击ctrl+S,改界面显示所有链接库的位置,用search进行搜索,因为我们这个apk中so文件的名字就叫libplokm.so,因此用名称进行搜索即可,记录此时这个链接库的地址,在这里我的机器上是A518B000
这里写图片描述
确定了so的位置之后如何确定getpl函数的位置?此时我们再打开一个IDA,把so文件拖进去,查看getpl的地址,可以在左侧函数窗口中进行搜索(ctrl+F),一般都是java_com开头的,然后我们记录这个函数的静态地址7CEC。
这里写图片描述
然后我们就可以计算出这个函数在加载之后,动态的地址了,就是A518B000+7CEC=A5192CEC,此时点击G键,进行地址跳转操作,输入要跳转的地址,然后跳转到我们要下断点的地方,点击F2,下断点,此时该地址代码变红。
这里写图片描述
然后点击左上角的绿色三角按钮,开始动态调试
这里写图片描述
若想要程序走到断点处,需要将编号填写正确,因此在模拟器中填写之前分析好的正确的编号,然后随便填写密码,点击sign按钮,此时程序停止在断点处。
这里写图片描述
通过之前的分析,我们知道有一步是判断密码长度为39的,因此我们需要到这一步更改判断条件,点击空格键,查看代码整体结构,找到我们需要修改的地方。
这里写图片描述这里写图片描述
找到此时BNE判断语句的地址,A5192FEE,下断点,然后到下面HEX view中找到该地址所在地方并进行更改。选中要改的地址,然后View->Open subviews->Hex dump即可直接跳转到要更改的hex地方,然后F2键,实现更改和保存(再按一次实现保存),根据上面的分析,此时将D1改成D0
这里写图片描述
此时再看我们的arm汇编界面,发现指令已经更改,将此处下断点之后,点击绿色的三角按钮进行调试。
这里写图片描述
之后点击F8或者F7键进行单步调试,在hex view中右键,Synchronize with可以查看不同寄存器中的值,运行到我们之前分析的地方,走过这个分支之后,第一步把R0寄存器赋值就是我们要找的flag,进行查看时果然就是这样,可以在右面General reqisters寄存器地址中查看对应hex view中的值,也可以用刚刚说的右键的方式直接找的R0的存储值,hex view中清楚的显示了flag的值
这里写图片描述
至此,这道题已经解出,flag就是:

SSCTF{oty3eaP$g986iwhw32j%OJ)g0o7J.CG:}

真机调试

另,还有一种方法是用真机进行调试,只需要把AndroidManifest.xml文件中进行改写,添加android:debuggable=”true”的条件就行,这里网上都有一些教程的。

<application
                  android:icon="@drawable/ic_launcher"
                  android:label="@string/app_name" 
                  android:debuggable="true">

用真机调试的一个好处就是在ida静态分析之后,可以直接下断点,然后动态调试起来就不用再重新下断点找地址了。
不知道为啥我这里静态调试下好的断点,在动态调试中都没了,还要重新找地方,重新下断点。估计是配置或者是因为虚拟机调试的原因吧。

至此安卓逆向练习第一弹结束,写的有点繁琐,把一些小的步骤也都写上去了,因为刚开始学,所以有些步骤不是很熟练,就把每一步的操作都记录下来了,以后熟练了会好很多,当然最重要的还是要注重这个分析程序的思路和方法吧。另:感谢凯哥的技术支持和对小白的耐心讲解:-)

展开阅读全文

没有更多推荐了,返回首页