Windows10 X64
Python 3.7
OppoR9st anroid6.0.1
adb
frida
burpsuite+brida插件
参考链接:https://wnma3mz.github.io/hexo_blog/2018/01/25/%E7%94%A8ADB%E8%B0%83%E8%AF%95%E5%AE%89%E5%8D%93%E6%89%8B%E6%9C%BA/
下载adb:https://dl.google.com/android/repository/platform-tools-latest-windows.zip
How to Install ADB on Windows, macOS, and Linux:https://www.xda-developers.com/install-adb-windows-macos-linux/
然后解压 配置环境变量。
然后手机USB数据线连接到电脑。记得打开usb调试模式,当然了你想把root了也可以。
adb连接不上的原因:https://mianao.info/2015/12/07/adb%E6%97%A0%E6%B3%95%E8%BF%9E%E6%8E%A5android%E8%AE%BE%E5%A4%87%E7%9A%84%E5%87%A0%E7%A7%8D%E5%8E%9F%E5%9B%A0
附上adb调试命令大全:https://blog.csdn.net/qq_15364915/article/details/52369266
常用adb命令小记:
# 查看所有连接设备
> adb devices
# 进行截图保存在sd卡的根目录下,名字为screen.png
> adb shell screencap -p /sdcard/screen.png
# 将截图发送到本地(当前目录下),也可以发送其他文件
> adb pull /sdcard/screen.png
# 删除本地文件
> adb shell rm /sdcard/screen.png
# 发送电脑里的文件到设备
> adb shell push screen.png /sdcard/
# 进入手机的交互环境,操作类似linux终端,exit或者Ctrl+C退出
> adb shell
# 点击手机屏幕(1000,1000)的位置
> adb shell input tap 1000 1000
# 输入字符串"helloworld",此处不能直接输入中文,且字符串不能有空格
> adb shell input text helloworld
# 滑动屏幕,从(100, 100)到(1000,1000),经历10s(也可以当作长按屏幕来使用)
> adb shell input swipe 100 100 1000 1000 10
# 查看当前运行的App, 这里Windows没有grep所以会运行失败,可以进入先进入交互环境再输入下面去掉"adb shell"命令
> adb shell dumpsys window | grep mCurrentFocus
# 或者
> adb shell dumpsys activity activities | grep mFocusedActivity
# 按下电源键
> adb shell input keyevent 26
# 按下返回键
> adb shell input keyevent 4
# 按下HOME健
> adb shell input keyevent 3
# 点亮屏幕
> adb shell input keyevent 224
# 熄灭屏幕
> adb shell input keyevent 223
# 查看手机安装了哪些App,输出按行输出App的包名
> adb shell pm list packages
# 加"-s"表示只输出系统应用
# 加"-3"表示只输出第三方应用
# 加字符串表示过滤应用名称,当然也可以使用grep
# 安装apk
> adb install
# 卸载apk
> adb uninstall
# 从桌面启动app
> adb shell monkey -p -c android.intent.category.LAUNCHER 1
# 关闭app
> adb shell am force-stop
$ adb devices
* daemon not running; starting now at tcp:5037
* daemon started successfully
List of devices attached
33b4c4be device
说明连接上了.
查看手机cpu版本。 根据内核版本去安装相应的frida。
$ adb shell
shell@R9s:/ $ cat /proc/cpuinfo
Processor : AArch64 Processor rev 4 (aarch64)
processor : 0
Features : fp asimd evtstrm aes pmull sha1 sha2 crc32
CPU implementer : 0x41
CPU architecture: 8
CPU variant : 0x0
CPU part : 0xd03
CPU revision : 4
......
根据cpu版本去下载相应frida-server,手机是AArch64的。
参考链接:https://www.jianshu.com/p/c349471bdef7
记得python版本要3.7。
3.6的话会找不到dll,看奈沙的blog是之前是3.5出现了这个问题:https://blog.csdn.net/whklhhhh/article/details/79302848
他是3.5要更新到3.6,现在我3.6不行的话更新到3.7然后并没有解决,最后解决方案是再装个frida-tools:
python3.7 -m pip install frida -i http://mirrors.aliyun.com/pypi/simple/ --trusted-host mirrors.aliyun.com
python37 -m pip install frida-tools -i http://mirrors.aliyun.com/pypi/simple/ --trusted-host mirrors.aliyun.com
安装好之后执行
frida-ps
正常执行之后说明frida已经正常安装。
然后在手机上安装fridaserver
选择arm64的android-server下载:https://github.com/frida/frida/releases/download/12.7.4/frida-server-12.7.4-android-arm64.xz
$ adb push frida-server-12.7.4-android-arm64 /data/local/tmp/frida-server
frida-server-12.7.4-android-arm64: 1 file pushed. 2.5 MB/s (38414672 bytes in 14.544s)
$ adb shell
$ su
R9s:/ # whoami
root
R9s:/ # cd /data/local/tmp/
R9s:/data/local/tmp # chmod 755 frida-server
R9s:/data/local/tmp # ./frida-server
记得手机要root,不然没权限。
然后另开一个cmd。
frida-ps -U
如果hook不上就转发下端口
adb forward tcp:27042 tcp:27042
以一个ctf题为例来玩一下frida:
https://github.com/ctfs/write-ups-2015/raw/master/seccon-quals-ctf-2015/binary/reverse-engineering-android-apk-1/rps.apk
参考链接:https://blog.csdn.net/zouyuanxc/article/details/80492465
frida理解:https://blog.csdn.net/jiangwei0910410003/article/details/80372118
hook分两种:
第一、Java层代码Hook操作
1、hook方法包括构造方法和对象方法,构造方法固定写法是$init,普通方法直接是方法名,参数可以自己定义也可以使用系统隐含的变量arguments获取。
2、修改方法的参数和返回值,直接调用原始方法通过传入想要修改的参数来做到修改参数的目的,以及修改返回值即可。
3、构造对象和修改对象的属性值,直接用反射进行操作,构造对象用固定写法的$new即可。
4、直接用Java的Exception对象打印堆栈信息,然后通过adb logcat -s AndroidRuntime来查看异常信息跟踪代码。
总结:获取对象的类类型是Java.use方法,方法有重载的话用overload(…)解决。
第二、Native层代码Hook操作
1、hook导出的函数直接用so文件名和函数名即可。
2、hook未导出的函数需要计算出函数在内存中的绝对地址,通过查看maps文件获取so的基地址+函数的相对地址即可,最后不要忘了+1操作。
总结:Native中最常用的就是内存地址指针了,所以如果要正确的获取值一定要用Memory类作为辅助,特别是字符串信息。
先推荐一个神器:jadx 将apk用jadx打开 直接反汇编出源码.
package com.example.seccon2015.rock_paper_scissors;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
import java.util.Random;
public class MainActivity extends Activity implements OnClickListener {
Button P;
Button S;
int cnt = 0;
int flag;
private final Handler handler = new Handler();
int m;
int n;
Button r;
private final Runnable showMessageTask = new Runnable() {
public void run() {
TextView tv3 = (TextView) MainActivity.this.findViewById(R.id.textView3);
if (MainActivity.this.n - MainActivity.this.m == 1) {
MainActivity.this.cnt++;
tv3.setText("WIN! +" + String.valueOf(MainActivity.this.cnt));
} else if (MainActivity.this.m - MainActivity.this.n == 1) {
MainActivity.this.cnt = 0;
tv3.setText("LOSE +0");
} else if (MainActivity.this.m == MainActivity.this.n) {
tv3.setText("DRAW +" + String.valueOf(MainActivity.this.cnt));
} else if (MainActivity.this.m < MainActivity.this.n) {
MainActivity.this.cnt = 0;
tv3.setText("LOSE +0");
} else {
MainActivity.this.cnt++;
tv3.setText("WIN! +" + String.valueOf(MainActivity.this.cnt));
}
if (1000 == MainActivity.this.cnt) {
tv3.setText("SECCON{" + String.valueOf((MainActivity.this.cnt + MainActivity.this.calc()) * 107) + "}");
}
MainActivity.this.flag = 0;
}
};
public native int calc();
static {
System.loadLibrary("calc");
}
/* access modifiers changed from: protected */
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
this.P = (Button) findViewById(R.id.button);
this.S = (Button) findViewById(R.id.button3);
this.r = (Button) findViewById(R.id.buttonR);
this.P.setOnClickListener(this);
this.r.setOnClickListener(this);
this.S.setOnClickListener(this);
this.flag = 0;
}
public void onClick(View v) {
if (this.flag != 1) {
this.flag = 1;
((TextView) findViewById(R.id.textView3)).setText("");
TextView tv = (TextView) findViewById(R.id.textView);
TextView tv2 = (TextView) findViewById(R.id.textView2);
this.m = 0;
this.n = new Random().nextInt(3);
tv2.setText(new String[]{"CPU: Paper", "CPU: Rock", "CPU: Scissors"}[this.n]);
if (v == this.P) {
tv.setText("YOU: Paper");
this.m = 0;
}
if (v == this.r) {
tv.setText("YOU: Rock");
this.m = 1;
}
if (v == this.S) {
tv.setText("YOU: Scissors");
this.m = 2;
}
this.handler.postDelayed(this.showMessageTask, 1000);
}
}
}
代码逻辑也比较简单,按钮按一下选择石头剪刀布,然后电脑是随机石头剪刀布。赢到1000分即可。
发现是直接调用calc函数运算一下得到最后flag,hook一下方法去调用calc即可。我这里hook的是onclick函数,点击即可弹flag。
exp:
import frida, sys
def on_message(message, data):
if message['type'] == 'send':
print("[*] {0}".format(message['payload']))
else:
print(message)
jscode = """
Java.perform(function () {
var MainActivity = Java.use('com.example.seccon2015.rock_paper_scissors.MainActivity');
MainActivity.onClick.implementation = function (v) {
send("Hook Start...");
var returnValue = this.calc();
send("Return:"+returnValue);
var result = (1000+returnValue)*107;
send("Flag:"+"SECCON{"+result.toString()+"}");
}
});
"""
process = frida.get_usb_device().attach('com.example.seccon2015.rock_paper_scissors')
script = process.create_script(jscode)
script.on('message', on_message)
script.load()
sys.stdin.read()
第二种方法就是hook结果,将分数改为999,然后出拳必赢,hook到onclick,再点击触发,即可弹flag
import frida, sys
def on_message(message, data):
if message['type'] == 'send':
print("[*] {0}".format(message['payload']))
else:
print(message)
jscode = """
Java.perform(function () {
var MainActivity = Java.use('com.example.seccon2015.rock_paper_scissors.MainActivity');
MainActivity.onClick.implementation = function (v) {
send("Hook Start...");
this.onClick(v);
this.n.value = 0;
this.m.value = 2;
this.cnt.value = 999;
send("Success!")
}
});
"""
process = frida.get_usb_device().attach('com.example.seccon2015.rock_paper_scissors')
script = process.create_script(jscode)
script.on('message', on_message)
script.load()
sys.stdin.read()
第三种方法就是去搞so文件了,从native层hook来获取calc的返回值。
以上是java层的hook。接下来玩一下native层的hook。
附上一个批量提取apk中so文件的脚本:
# export_so.py
#!/usr/bin/env python
# coding=utf-8
import zipfile
import os
path = "./"
so_path="./so/"
apklist=os.listdir(path)
for APK in apklist:
if APK.endswith(".apk"):
portion = os.path.splitext(APK)
apkname = portion[0]
abs_so_path=os.path.join(so_path,apkname)
abs_zipAPK_path=os.path.join(path,APK)
z = zipfile.ZipFile(abs_zipAPK_path,'r')
solists=[]
for filename in z.namelist():
if filename.endswith(".so"):
sofileName = os.path.basename(filename)
soSource = os.path.basename(os.path.dirname(filename))
storePath=os.path.join(abs_so_path,soSource)
if not os.path.exists(storePath):
os.makedirs(storePath)
newsofile=os.path.join(storePath,sofileName)
f = open(newsofile,'w')
f.write(z.read(filename))
libcalc.so是我们的目标。
丢到ida里面去看看。
拿到函数名是 Java_com_example_seccon2015_rock_1paper_1scissors_MainActivity_calc
然后去写脚本hooknative层即可。
exp:
import frida, sys
# export native function
native_hook_code = """
Java.perform(function(){
send("Running Script");
var exports = Module.findExportByName("libcalc.so","Java_com_example_seccon2015_rock_1paper_1scissors_MainActivity_calc");
send("so native pointers:"+exports);
Interceptor.attach(exports,{
onEnter: function(args){
//send("so function args is: " +args[0]+","+args[1]+","+args[2]);
send("123");
},
onLeave: function(retval){
send("so result value is :"+retval);
}
});
});
"""
def on_message(message, data):
if message['type'] == 'send':
print("[*] {0}".format(message['payload']))
else:
print(message)
process = frida.get_device_manager().enumerate_devices()[-1].attach("com.example.seccon2015.rock_paper_scissors")
script = process.create_script(native_hook_code)
script.on('message', on_message)
script.load()
sys.stdin.read()
D:\Frida>python37 rps_native_exp.py
[*] Running Script
[*] so native pointers:0xe8473c75
[*] 123
[*] so result value is :0x7
同样拿到返回值是7。然后进行运算
if (1000 == MainActivity.this.cnt) {
tv3.setText("SECCON{" + String.valueOf((MainActivity.this.cnt + MainActivity.this.calc()) * 107) + "}");
}
也就是(1000+7)*107结果是107749,和之前的结果一样。