安卓动态调试smali
安装smalidea # 下载链接: https://bitbucket.org/JesusFreke/smali/downloads/smalidea-0.05.zip
- 下载
- Android Studio安装smalidea
进入Settings->Plugins点击Install plugin from disk选中下载好的压缩包 - 安装完成 解包并配置AS
- 反编译成smali代码 apktool d 应用调试.apk 2. AS打开
- 设置smali根目录
- 配置AS调试环境
配置调试环境
打开安卓模拟器
安装待调试程序到模拟器
adb install aaa.apk开启调试状态
adb shell am start -D -n hfdcxy.com.myapplication/hfdcxy.com.myapplication.MainActivity转发端口
# 查看待调试程序pid
adb shell ps | grep hfdcxy
# 转发端口
# [pid]为pid值
adb forward tcp:8700 jdwp:[pid]
开始调试 # 1. 下断点
打开AS中代码,点击调试行的左侧,点击处生成一个小红点,即为断点。
\2. 开始调试 点击右上⻆按钮,开始调试。模拟器显示如图。
然后输入内容,即可看到AS中变化。
安卓动态调试Frida的使用:
1、安装
1 | sudo pip install frida |
2、下载frida-server
文件
在frida官方Github下载server文件,如果是真机,选择 frida-server-版本-android-arm.xz
;如果是模拟器,选择 frida-server-版本-android-x86.xz
。如果操作系统是64位,则选择后缀为_64
的文件。下载完成后需要解压。(下载很慢或无法下载可尝试用手机连4G下载, 亲测可行)
3、将解压后的frida-server
文件push到真机或模拟器中
1 | # 不一定要放在data/local/tmp目录下,但该目录所需权限较低 |
4、依次运行以下命令,修改server文件权限
1 | adb shell |
这里就安装完成了,接着我们开始实现hook操作:
1、端口转发
1 | adb forward tcp:27042 tcp:27042 |
2、然后进入 shell 并切换到data/local/tmp目录下来启动frida-server:
1 | adb shell |
3、运行脚本即可
1 | import frida,sys |
解析下这里的原理:
1 | Java.perform(function(){ |
安卓jeb动态调试apk
1、jeb下载安装(在吾爱破解上有,3.0.0以上的jeb才可以)
2、直接以动态调试形式打开apk
1 | adb shell am start -D -n hfdcxy.com.myapplication/hfdcxy.com.myapplication.MainActivity |
3、然后打开jeb,在smali代码中下断点,接着attach上进程即会在代码处停下来,这里需要注意的是,有时候寄存器的变量类型是错的,所以可以自己调整寄存器的变量类型和值
smali语言语法初探:
关键字
.field private isFlag:z — 定义变量
.method — 方法
.parameter — 方法参数
.prologue — 方法开始
.line 12 — 此方法位于第12行
invoke-super — 调用父函数
const/high16 v0, 0x7fo3 — 把0x7fo3赋值给v0
invoke-direct — 调用函数
return-void — 函数返回void
.end method — 函数结束
new-instance — 创建实例
iput-object — 对象赋值
iget-object — 调用对象
invoke-static — 调用静态函数
数据类型
java里面包含两种数据类型,基本数据类型和引用类型(包括对象),同时映射到smali也是有这两大类型。
基本数据类型
- B — byte
- C — char
- D — double (64 bits)
- F — float
- I — int
- J — long (64 bits)
- S — short
- V — void 只能用于返回值类型
- Z — boolean
对象类型
- Lxxx/yyy/zzz; — object
L
表示这是一个对象类型
xxx/yyy
是该对象所在的包
zzz
是对象名称
;
标识对象名称的结束
数组类型
- [XXX — array
1
2
3
4 [I`表示一个int型的一维数组,相当于`int[]`
增加一个维度增加一个`[`,如`[[I`表示`int[][]`
数组每一个维度最多255个;
对象数组表示也是类似,如String数组的表示是`[Ljava/lang/String
寄存器与变量
java中变量都是存放在内存中的,android为了提高性能,变量都是存放在寄存器中的,寄存器为32位,可以支持任何类型,其中long和double是64为的,需要使用两个寄存器保存。
寄存器采用v和p来命名
v表示本地寄存器,p表示参数寄存器,关系如下
如果一个方法有两个本地变量,有三个参数
v0
第一个本地寄存器
v1
第二个本地寄存器
v2=p0
(this)
v3=p1
第一个参数
v4=p2
第二个参数
v5=p3
第三个参数
当然,如果是静态方法的话就只有5个寄存器了,不需要存this了。
.registers
使用这个指令指定方法中寄存器的总数
.locals
使用这个指定表明方法中非参寄存器的总数,放在方法的第一行。
方法和字段
方法签名
methodName(III)Lpackage/name/ObjectName;
如果做过ndk开发的对于这样的签名应该很熟悉的,就是这样来标识一个方法的。上面methodName标识方法名,III表示三个整形参数,Lpackage/name/ObjectName;表示返回值的类型。
方法的表示
Lpackage/name/ObjectName;——>methodName(III)Z
即 package.name.ObjectName中的 function boolean methondName(int a, int b, int c) 类似这样子
字段的表示
Lpackage/name/ObjectName;——>FieldName:Ljava/lang/String;
即表示: 包名,字段名和各字段类型
方法的定义
比如下面的一个方法
指令执行
smali字节码是类似于汇编的,如果你有汇编基础,理解起来是非常容易的。
比如:
move v0, v3 #把v3寄存器的值移动到寄存器v0上.
const v0, 0x1 #把值0x1赋值到寄存器v0上。
invoke-static {v4, v5}, Lme/isming/myapplication/MainActivity;->sum(II)I #执行方法sum(),v4,v5的值分别作为sum的参数。
条件跳转分支
“if-eq vA, vB, :cond_x” — 如果vA等于vB则跳转到:cond_x
“if-ne vA, vB, :cond_x” — 如果vA不等于vB则跳转到:cond_x
“if-lt vA, vB, :cond_x” — 如果vA小于vB则跳转到:cond_x
“if-ge vA, vB, :cond_x” — 如果vA大于等于vB则跳转到:cond_x
“if-gt vA, vB, :cond_x” — 如果vA大于vB则跳转到:cond_x
“if-le vA, vB, :cond_x” — 如果vA小于等于vB则跳转到:cond_x
“if-eqz vA, :cond_x” — 如果vA等于0则跳转到:cond_x
“if-nez vA, :cond_x” — 如果vA不等于0则跳转到:cond_x
“if-ltz vA, :cond_x” — 如果vA小于0则跳转到:cond_x
“if-gez vA, :cond_x” — 如果vA大于等于0则跳转到:cond_x
“if-gtz vA, :cond_x” — 如果vA大于0则跳转到:cond_x
“if-lez vA, :cond_x” — 如果vA小于等于0则跳转到:cond_x
参考连接:
链接:https://www.jianshu.com/p/ba9b374346dd
终端操作的常见命令行:
1 | cd Library/Android/sdk/tools #进入模拟器的终端目录 |
如果是native方法的话,只能看.so库文件,用ida动态调试:
1、将apk解压,然后将x86下的libc库拖到ida中进行动态分析
2、将a1+668这种类型通过y,一键转成JNIEnv *ptr类型,即可很清晰看出来东西
3、shift+F12和Hex View中去找可用的字符串,可以定位到关键逻辑点5216CCB620
4、开始配置环境
1 | adb push xxx/xxx/android__x86_server /data/local/tmp/ 将ida里面的android_x86_server复制到模拟器中 |
这样就配置好了ida的调试程序环境
5、玄学问题解决
由于在mac中ddms无法直接打开,但是动态调试我们又需要用到,所以要找到原因,最后发现原因是jdk的版本过高,所以这里先把java版本从11降低到1.8.92:
但是发现打开动弹不得,谷歌了一下问题,找到了解决方法:
先下载一下swt:
解压出来,重命名下载的压缩包里面的swt.jar
为org.eclipse.swt.cocoa.macosx.x86_64_3.100.1.v4236b.jar
,然后copy 到lib/monitor-x86_64/plugins/
目录下,如下:用户名结合自己的实际情况来定
1 | cp /Users/v1ct0r/Downloads/swt-4.7.1a-cocoa-macosx-x86_64/org.eclipse.swt.cocoa.macosx.x86_64_3.100.1.v4236b.jar ./ |
覆盖原来的文件,到此结束,重启ddms即可,命令行输入monitor:
现在开始调试工作:
1、开启调试服务
1 | /data/local/tmp # ./android_x86_server |
2、设置端口转发
这时候android_server
已经在监听Android设备的23946端口,我们还需要将这个端口转发到我们的电脑上:
1 | adb forward tcp:23946 tcp:23946 |
3、以调试模式启动Activity(在涉及到JNI_Load时要用到)
现在,我们可以开始着手调试程序了,我们先以调试模式启动程序的主Activity,命令格式为:
1 | adb shell am start -D -n hfdcxy.com.myapplication/hfdcxy.com.myapplication.MainActivity 包名/类名 |
启动程序后(也可以不同调试状态)就可以开始调试了,我们找到IDA的Debugger
菜单,选择Attach process
,在弹出的窗口中选择我们的被调试进程hfdcxy.com.myapplication
,当IDA进入调试状态后,就已经成功附加到目标进程。
IDA会首先断在程序刚开始执行的地方,这时候可以选择继续运行让程序继续向下执行。然后就可以在手机上操作了。一般来说下断点直接在调试前的汇编中下断点即可,在执行到断点时候也可以以伪代码的形式调试。
在源程序断点下来,然后再安卓重新启动一次apk,就可以在ida中选择debugger/attach process/程序进程,即可实现动态调试。
这样就可以直接定位到关键逻辑处!
4、开启ddms
其实直接attach进程就可以了,不用其他的那么麻烦的操作
4、直接运行程序调试(只在java层调用时)
开启服务,端口转发后,选择ida的remote linux dbg选项
在ida中下断点:
模拟器启动程序:
attach进程:
接着f9,然后在模拟器输入字符串,就可以在ida里断下来了:
现在就和平常的动态调试没啥两样了~
总结:
jeb动态调试查看寄存器和内存信息+frida动态调试hook函数修改信息+ida动态调试.so文件