安卓APP动态调试-IDA实用攻略

netwind 2015-05-06 10:27:00

0x00 前言


随着智能手机的普及,移动APP已经贯穿到人们生活的各个领域。越来越多的人甚至已经对这些APP应用产生了依赖,包括手机QQ、游戏、导航地图、微博、微信、手机支付等等,尤其2015年春节期间各大厂商推出的抢红包活动,一时让移动支付应用变得异常火热。

然后移动安全问题接憧而至,主要分为移动断网络安全和客户端应用安全。目前移动APP软件保护方面还处于初级阶段,许多厂商对APP安全认识不够深入,产品未经过加密处理,使得逆向分析者能够通过逆向分析、动态调试等技术来破解APP,这样APP原本需要账号密码的功能可以被破解者顺利绕过,使得厂商利益严重受损。

对未加壳的APP进行动态调试,通常可以非常顺利且快速地绕过一些登陆限制或功能限制。本文将以安卓APP为例,来详细介绍一下移动APP动态调试技术。

0x01 调试环境搭建


1.1 安装JDK

JAVA环境的搭建请自行查找资料,这里不做详述。

1.2 安装Android SDK

下载地址:http://developer.android.com/sdk/index.html

下载完安装包后解压到任意一目录,然后点击运行SDK Manager.exe,然后选择你需要的版本进行安装,如图:

1.3 安装Eclipse集成开发环境

下载地址:http://www.eclipse.org/downloads。选择Eclipse for Mobile Developers,解压到任意目录即可。

1.4 创建Android Virtual Device

动态调试可以用真实的手机来做调试环境,也可以用虚拟机来做调试环境,本文采用虚拟机环境。因此创建虚拟机步骤如下:

1打开Eclipse –>windows->Android Virtual Device

2点击Create,然后选择各个参数如图:

这里Target 就是前面步骤中安装的SDK 选择任意你觉得喜欢的版本就可以。点击OK 就创建完毕。

1.5 安装 APK改之理

这个是一个很好用的辅助调试的软件,请自行搜索下载。

1.6 安装 IDA6.6

IDA6.6开始支持安卓APP指令的调试,现该版本已经提供免费下载安装,请自行搜搜。

0x02 Dalvik指令动态调试


2.1 准备工作

安卓APP应用程序后缀为apk,实际上是一个压缩包,我们把它改后缀为rar打开如图:

其中classes.dex是应用的主要执行程序,包含着所有Dalvik指令。我们用APK改之理打开apk,软件会自动对其进行反编译。反编译后会有很多smail文件,这些文件保存的就是APP的Dalvik指令。

在APK改之理里双击打开AndroidManifest.xml,为了让APP可调试,需要在application 标签里添加一句android:debuggable="true" 如图:

然后点击保存按钮,然后编译生成新的apk文件。接着打开Eclipse –>windows->Android Virtual Device,选择刚才创建的虚拟机,然后点击start,虚拟机便开始运行。偶尔如果Eclipse启动失败,报错,可以同目录下修改配置文件:

把配置参数原本为512的改为256 原本为1024的改为512,然后再尝试启动。

在SDK安装目录有个命令行下的调试工具adb shell,本机所在目录为E:\adt-bundle-windows-x86-20140702\sdk\platform-tools,把adb.exe注册到系统环境变量中,打开dos命令行窗口执行adb shell 就可以进入APP命令行调试环境,或者切换到adb所在目录来执行adb shell。

这里先不进入adb shell,在DOS命令行下执行命令:adb install d:\1.apk 来安装我们刚才重新编译好的APK文件。安装完毕会有成功提示。

2.2 利用IDA动态调试

将APP包里的classes.dex解压到任意一目录,然后拖进IDA。等待IDA加载分析完毕,点击Debugger->Debugger Options如图

按图所示勾选在进程入口挂起,然后点击Set specific options 填入APP包名称和入口activity 如图:

其中包的名称和入口activity 都可以通过APK改之理里的AndroidManifest.xml 文件获取:

<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.simpleencryption">
    <application android:allowBackup="true" android:debuggable="true" android:icon="@drawable/creakme_bg2" android:label="@string/app_name" android:theme="@style/AppTheme">
        <activity android:label="@string/app_name" android:name=".MainActivity">

然后在IDA点击Debugger->Process Options

其他默认不变,端口这里改为8700。这里默认端口是23946,我在这里困扰了很久,就是因为这个端口没有改为8700所致。然后我们看看这个8700端口是怎么来的。在Android SDK里提供了一款工具DDMS,用来监视APP的运行状态和结果。在SDK的TOOLS目录有个DDMS.BAT的脚步,运行后就会启动DDMS。由于我的本机安装了SDK的ADT插件,DDMS集成到了Eclips中,打开Eclips->Open perspective->ddms就启动了DDMS。

如图所示:

在DDMS选中某个进程后面就会注释出它的调试端口,本机这里是8700。

到此所有的工作就准备就绪,然后就可以下断点来调试该APP了。我们在APK改之理中在com目录下查看smali文件 发现MainActivity.smali里有一个感兴趣的函数getPwdFromPic(),那么我们就对它下断以跟踪APP的运行。

在IDA里搜索字符串getPwdFromPic,发现onClick有调用该函数

我们在onClick 函数开始位置按F2下断如图:

然后点击上图中绿色三角形按钮启动调试如图:

调试过程中有一个问题出现了很多次,浪费了我大量的时间,就在写文章的时候,操作时还是遇到了这样的问题。就是点击启动后IDA提示can’t bind socket,琢磨了很久终于找到原因了,当打开过一次DDMS后 每次启动Eclips都会启动DDMS 而8700端口正是被这个DDMS给占用了,然后每次都会启动失败,解决办法就是 虚拟机运行起来后关闭掉Eclips,这时一切就正常了!

事例中是一个APP crackme 提示输入密码才能进入正确界面。这个时候我们输入123,点击登陆,IDA中断在了我们设置断点的地方,这时选中ida->debugger->use source level debugger,然后点击ida->debugger->debugger windows->locals打开本地变量窗口,如图:

然后按F7或F8单步跟踪程序流程,同时可以观察到变量值的变化,也可以在IDA右键选择图形视图,可以看到整个APP执行的流程图:

如上图所示 变量窗口中我们输入了123 被转化成的密码是么广亡,pw变量也显示出了正确的密码,其实这个时候已经很容易判断出正确密码了。

0x03 Andoid原生动态链接库动态调试

通常为了加密保护等措施,有时dex执行过程中会调用动态链接库文件,该文件以so为后缀,存在于APP文件包里。

这里我们以动态附加的方式来调试原生库。

3.1 准备工作

1、将IDA->dbgsrv目录下的android_server拷贝到虚拟机里,并赋予可执行权限

DOS命令分别为:

adb shell pull d:\ android_server /data/data/sv
adb shell chmod 755 /data/data/sv

2、启动调试服务器android_server

命令:adb shell /data/data/sv

服务器默认监听23946端口。

3、重新打开DOS窗口进行端口转发,命令:

adb forward tcp:23946 tcp:23946如图:

3.2 利用IDA进行动态调试

1、虚拟机里启动要调试的APP 2、启动IDA,打开debugger->attach->remote Armlinux/andoid debugger

端口改为23946 其他保持不变,点击OK

如上图,选中要调试的APP 的数据包名,然后点击OK。

正常情况下,IDA会把APP进程挂起。

3、由于当前程序不是在动态链接库领空,这时我们要重新打开一个IDA,用它打开需要调试的so文件,找到需要下断的位置的文件偏移,并做记录,然后关闭后面打开的这个IDA。

4、在原IDA界面按下ctrl+s键,找到并找到需要调试的so,同时记录该文件的加载基址。然后点击OK 或者cancel按钮关闭对话框。

5、按下快捷键G 输入基址+文件偏移所得地址,点击OK 就跳转到SO文件需要下断的地方,这时按下F2键设置断点。当APP执行到此处时便可以断下来。

3.3 在反调试函数运行前进行动态调试

程序加载so的时候,会执行JNI_OnLoad函数,做一系列的准备工作。通常反调试函数也会放到JNI_OnLoad函数里。进行4.2中第2步时也许会遇到如下情况:

这时APP检测到了调试器,会自动退出,那么这时调试策略需要有所改变。

接着4.1第3步后,在DOS命令行执行命令:

adb shell am start -D -n com.yaotong.crackme/com.yaotong.crackme.MainActivity    

来以调试模式启动APP 如图:

com.yaotong.crackme是APP包名称,com.yaotong.crackme.MainActivity是执行入口 这些可以用APK改之理查看。

这时由于APP还未运行,那么反调试函数也起不了作用,按照4.2中第2步把APP挂起。这时IDA会中断在某个位置

然后点击debugger->debugger opions设置如下:

点击OK 后按F9运行APP,然后再DOS命令下执行命令:

jdb -connect com.sun.jdi.SocketAttach:hostname=127.0.0.1,port=8700

这时APP会断下来,然后按照4.2中的3、4、5补找到JNI_OnLoad函数的地址并下断,然后按F9 会中断下来。然后便可以继续动态跟踪调试分析。

0x04 主要参考资料


1、《Andoroid 软件安全与逆向分析》

2、看雪论坛安卓安全版

3、吾爱破解论坛安卓版

感谢看雪论坛好友:我是小三、QEver、非虫等的热心指教!

评论

N

Nicky 2015-05-06 10:30:40

感谢分享,讲的很详细

mickey 2015-05-06 11:02:37

好文章

C

chavez_wang 2015-05-06 11:10:22

好文,mark

C

conqu3r 2015-05-06 11:19:40

[email protected]/* <![CDATA[ */!function(t,e,r,n,c,a,p){try{t=document.currentScript||function(){for(t=document.getElementsByTagName('script'),e=t.length;e--;)if(t[e].getAttribute('data-cfhash'))return t[e]}();if(t&&(c=t.previousSibling)){p=t.parentNode;if(a=c.getAttribute('data-cfemail')){for(e='',r='0x'+a.substr(0,2)|0,n=2;a.length-n;n+=2)e+='%'+('0'+('0x'+a.substr(n,2)^r).toString(16)).slice(-2);p.replaceChild(document.createTextNode(decodeURIComponent(e)),c)}p.removeChild(t)}}catch(u){}}()/* ]]> */_hqd

屎蛋 2015-05-06 11:20:22

[email protected]/* <![CDATA[ */!function(t,e,r,n,c,a,p){try{t=document.currentScript||function(){for(t=document.getElementsByTagName('script'),e=t.length;e--;)if(t[e].getAttribute('data-cfhash'))return t[e]}();if(t&&(c=t.previousSibling)){p=t.parentNode;if(a=c.getAttribute('data-cfemail')){for(e='',r='0x'+a.substr(0,2)|0,n=2;a.length-n;n+=2)e+='%'+('0'+('0x'+a.substr(n,2)^r).toString(16)).slice(-2);p.replaceChild(document.createTextNode(decodeURIComponent(e)),c)}p.removeChild(t)}}catch(u){}}()/* ]]> */_hqd

瘦蛟舞 2015-05-06 13:55:47

科普是一种公益行为.

代码疯子 2015-05-07 08:56:36

3.1 准备工作 adb pull -> adb push

N

netwind 2015-05-07 18:04:13

@代码疯子 对的 笔误 谢谢!

代码疯子 2015-05-08 15:14:11

@netwind 最近学习调试ijiami遇到了一个问题,想请教一下,不知道你遇到过没有。
IDA经常弹got SIGILL signal (Illegal instruction) (exc.code 4, tid 25041)这个错误,一直弹个不停,要等很久才不弹了,才能继续调试……谢谢

N

netwind 2015-05-10 18:18:58

@代码疯子 遇到过,调试阿里第3题的时候,不知道是否是因为反调试线程发出的信号 。KILL -P 线程ID试试
tid 25041

H

Hmily 2015-05-11 17:49:44

赞一个,提供一下楼主所用到的工具在爱盘都有下载:
http://down.52pojie.cn/Tools/

S

somebody365 2015-05-11 19:09:30

3.2部分的4.“在原IDA界面按下ctrl+s键,找到并找到需要调试的so”,找不到libcrackme.so是怎么回事呢?多谢

1

1c3z 2015-05-14 20:36:13

感谢分享:
收集了一份软件
http://pan.baidu.com/s/1qWyrH3u

代码疯子 2015-05-16 20:25:44

@netwind 谢谢,下次试试

代码疯子 2015-05-16 20:28:01

@somebody365 可能是你的so还没被加载

路人甲 2015-11-08 14:51:42

请教下:为何debug时,总弹出:JDWP error:Unknown error code:0x00002746?

路人甲 2015-12-02 17:23:15

wei(xin)调试需要注意点什么吗?博主可否指教一下呢,我调试的时候断点下好了,但是发现线程执行不过去,总是卡在一个地方,可是我这个地方并没有下断点,F8之后会执行到乱码DCB段,特别多的DBC段,p以后还会卡在别的地方,按理说这些都不用管的吧。

D

Diver 2015-12-03 10:49:13

楼主,很好的帖子,给32个赞,但其中有一些小问题,比如:传android_server时,用adb push c:\dex\android_server /data/data/sv

路人甲 2015-12-30 16:52:24

@Diver 是的,我也发现了,对于强迫症的我表示很不能接受,呵呵

路人甲 2015-12-31 16:54:22

调用jdb -connect com.sun.jdi.SocketAttach:hostname=127.0.0.1,port=8700这个指令时出现如下错误
λ jdb -connect "com.sun.jdi.SocketAttach:hostname=127.0.0.1,port=8700"
java.net.SocketException: Software caused connection abort: recv failed
at java.net.SocketInputStream.socketRead0(Native Method)
at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
at java.net.SocketInputStream.read(SocketInputStream.java:170)
at java.net.SocketInputStream.read(SocketInputStream.java:141)
at com.sun.tools.jdi.SocketTransportService.handshake(SocketTransportService.java:130)
at com.sun.tools.jdi.SocketTransportService.attach(SocketTransportService.java:232)
at com.sun.tools.jdi.GenericAttachingConnector.attach(GenericAttachingConnector.java:116)
at com.sun.tools.jdi.SocketAttachingConnector.attach(SocketAttachingConnector.java:90)
at com.sun.tools.example.debug.tty.VMConnection.attachTarget(VMConnection.java:519)
at com.sun.tools.example.debug.tty.VMConnection.open(VMConnection.java:328)
at com.sun.tools.example.debug.tty.Env.init(Env.java:63)
at com.sun.tools.example.debug.tty.TTY.main(TTY.java:1066)

S

soFree 2016-04-18 21:43:40

8700也困扰了我,看了楼主的文章才恍然大悟,多谢楼主的好文,感激不尽

S

soFree 2016-04-18 21:46:06

@Hmily 大大破解的010_Editor_v6.0.2一直在用,特别好用,感谢分享

路人甲 2016-04-30 15:43:40

IDA调试so好像看不到call stack信息,call stack窗口基本上都是一层
有没有什么方法看到比较详细的函数调用栈

N

netwind

挖掘漏洞为乐趣

twitter weibo github wechat

随机分类

MongoDB安全 文章:3 篇
事件分析 文章:223 篇
硬件与物联网 文章:40 篇
Windows安全 文章:88 篇
渗透测试 文章:154 篇

扫码关注公众号

WeChat Offical Account QRCode

最新评论

Article_kelp

因为这里的静态目录访功能应该理解为绑定在static路径下的内置路由,你需要用s

N

Nas

师傅您好!_static_url_path那 flag在当前目录下 通过原型链污

Z

zhangy

你好,为什么我也是用windows2016和win10,但是流量是smb3,加密

K

k0uaz

foniw师傅提到的setfge当在类的字段名成是age时不会自动调用。因为获取

Yukong

🐮皮

目录