安卓逆向(Android Reverse)
frida脚本脱壳
wp:
原理解释:
adb连接手机模拟器
下载adb工具,下载后添加系统变量要重启,而且模拟器会自带adb,要提前去除环境变量!
//查看adb的来源
D:\CTF_Study\Reverse>where adb
E:\ReverseTools\platform-tools\adb.exe
adb基本命令:
adb help //帮助文档
adb connect IP:port //连接
adb shell //命令行模式-使用linux命令
adb kill-server
adb start-server //关闭和开启服务
下面开始链接模拟器:
不知道为啥:夜神模拟器安装后点击按钮会闪退,雷电模拟器不开root点击也会闪退
所以使用雷电模拟器:
这里总结了一些常用的PC版模拟器连接方法!其他的自行搜索!
**MuMu模拟器:adb connect 127.0.0.1:7555
夜神模拟器:adb connect 127.0.0.1:62001
雷电模拟器:adb connect 127.0.0.1:5555
逍遥安卓模拟器:adb connect 127.0.0.1:21503
天天模拟器:adb connect 127.0.0.1:6555
海马玩模拟器:adb connect 127.0.0.1:5300
连接成功!
D:\CTF_Study\Reverse\从0到1CTFer成长之路-Android\n1book数字壳的传说(Frida hook脱壳)
>adb connect 127.0.0.1:5555
* daemon not running; starting now at tcp:5037
* daemon started successfully
connected to 127.0.0.1:5555
Frida-基于python+java+js-hook框架
环境安装:
pip install frida
pip install frida-tools
D:\CTF_Study\Reverse\从0到1CTFer成长之路-Android\n1book数字壳的传说(Frida hook脱壳)
>frida --version
16.1.11
查看手机模拟器,发现有两台设备不知道啥原因,主要看127.0.0.1:5555:
D:\CTF_Study\Reverse\从0到1CTFer成长之路-Android\n1book数字壳的传说(Frida hook脱壳)>adb devices
List of devices attached
127.0.0.1:5555 device
emulator-5554 device
开始启动shell看看手机型号:
知识来源:adb查看手机设备型号、品牌、机型等信息_abd 获取手机型号-CSDN博客
- 如果只有一个
#直接
adb shell
- 如果有多台(其实只有一台不知道雷电模拟器发什么疯)
D:\CTF_Study\Reverse\从0到1CTFer成长之路-Android\n1book数字壳的传说(Frida hook脱壳)
>adb -s 127.0.0.1:5555 shell
#成功进入shell
#查看手机型号
star2qltechn:/ $ getprop ro.product.cpu.abi
x86_64
#查看大量信息
star2qltechn:/ $ getprop | grep product
[ro.build.product]: [star2qltechn]
[ro.product.board]: [GM1900]
[ro.product.brand]: [OnePlus]
[ro.product.cpu.abi]: [x86_64]
...
[ro.product.vendor.name]: [star2qltezh]
[ro.vendor.product.cpu.abilist]: [x86_64,x86,arm64-v8a,armeabi-v7a,armeabi]
[ro.vendor.product.cpu.abilist32]: [x86,armeabi-v7a,armeabi]
[ro.vendor.product.cpu.abilist64]: [x86_64,arm64-v8a]
需要向Android手机上传一个frida-server服务程序:Release Frida 16.2.1 · frida/frida (github.com)
下载个这个;frida-server-16.2.1-android-x86_64.xz
把这个文件传入模拟器:
命令:adb -s 127.0.0.1:5555 push Windows路径 Android路径
D:\CTF_Study\Reverse\从0到1CTFer成长之路-Android\n1book数字壳的传说(Frida hook脱壳)
>adb -s 127.0.0.1:5555 push E:\ReverseTools\platform-tools\tools\frida-server /data/local/tmp
E:\ReverseTools\platform-tools\tools\frida-server: 1 file pushed, 0 skipped. 46.2 MB/s (31731620 bytes in 0.655s)
D:\CTF_Study\Reverse\从0到1CTFer成长之路-Android\n1book数字壳的传说(Frida hook脱壳)
>adb -s 127.0.0.1:5555 push E:\ReverseTools\platform-tools\tools\frida-server-16.2.1-android-x86_64.xz /data/local/tmp
E:\ReverseTools\platform-tools\tools\frida-server-16.2.1-a...le pushed, 0 skipped. 54.8 MB/s (31731620 bytes in 0.552s)
查看文件是否成功传入:
D:\CTF_Study\Reverse\从0到1CTFer成长之路-Android\n1book数字壳的传说(Frida hook脱壳)>adb -s 127.0.0.1:5555 shell
star2qltechn:/ $ cd /data/local/tmp
star2qltechn:/data/local/tmp $ ls
frida-server frida-server-16.2.1-android-x86_64.xz
给frida-server赋予可执行权限:
star2qltechn:/data/local/tmp $ chmod 777 frida-server
上面的完成了就可以开始下一步了!
启动试试!
运行之后发现权限不足!
Unable to save SELinux policy to the kernel: Permission denied
查了一下需要开启root模式!
借鉴:Android逆向:Frida Hook基础_unable to save selinux policy to the kernel: permi-CSDN博客
# su进入root
star2qltechn:/data/local/tmp $ su
#成功进入
:/ # ls
acct init.usb.rc product
bin init.zygote32.rc sbin
....
#成功启动server
:/data/local/tmp # ./frida-server
然后就可以开启另一个cmd来查看模拟器的进程了!先启动要脱壳的目标!
C:\Users\Administrator>frida-ps -U
PID Name
---- -----------------------------------------------
2309 adbd
...
2768 memfd:frida-helper-32 (deleted)
2986 n1book1
...
FRIDA-DEXDump-脱壳工具
脱壳原理讲解:深入 FRIDA-DEXDump 中的矛与盾 (qq.com)
原理:
- 在进程的内存中搜索dex文件头
- 如果dex头被抹除,则需要开启深度搜索模式,搜索其他关键字段
- 如果dex的文件file_size字段被抹去,就需要搜索dex的尾部字段来判断是否是dex和dex的大小
思维导图:frida-dexdump脱壳工具简单使用的思维导图 - 『移动安全区』 - 吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn
下载:
PS D:\桌面> pip3 install frida-dexdump
# 使用frida-dexdump(frida-dexdump -U -f 包名 -o 保存地址)
# frida-dexdump -U -p port -o 保存地址 //通过端口
# frida-dexdump -U -n n1book1 -o 保存地址 //通过名字
C:\Users\Administrator>frida-dexdump -U -n n1book1 -o D:\CTF_Study\Reverse
-------------------------------------------------------------------------------------------------------------------------------
__ _ _ _ _
/ _|_ __(_) __| | __ _ __| | _____ ____| |_ _ _ __ ___ _ __
| |_| '__| |/ _` |/ _` |_____ / _` |/ _ \ \/ / _` | | | | '_ ` _ \| '_ \
| _| | | | (_| | (_| |_____| (_| | __/> < (_| | |_| | | | | | | |_) |
|_| |_| |_|\__,_|\__,_| \__,_|\___/_/\_\__,_|\__,_|_| |_| |_| .__/
|_|
https://github.com/hluwa/frida-dexdump
-------------------------------------------------------------------------------------------------------------------------------
Attaching...
INFO:Agent:DexDumpAgent<Connection(pid=Session(pid=2986), connected:True), attached=True>: Attach.
INFO:frida-dexdump:[+] Searching...
INFO:frida-dexdump:[*] Successful found 6 dex, used 3 time.
INFO:frida-dexdump:[+] Starting dump to 'D:\CTF_Study\Reverse'...
INFO:frida-dexdump:[+] DexMd5=df2b99537b2d11d3074d6fe752a763bb, SavePath=D:\CTF_Study\Reverse\classes.dex, DexSize=0x2154fc
INFO:frida-dexdump:[+] DexMd5=4d956f9be62251c9b41aec34bdc39ad4, SavePath=D:\CTF_Study\Reverse\classes02.dex, DexSize=0x77e4
INFO:frida-dexdump:[+] DexMd5=f1771b68f5f9b168b79ff59ae2daabe4, SavePath=D:\CTF_Study\Reverse\classes03.dex, DexSize=0x11c
INFO:frida-dexdump:[+] DexMd5=3b7c50e48cd68df85142c2402a09cfec, SavePath=D:\CTF_Study\Reverse\classes04.dex, DexSize=0x21ba38
INFO:frida-dexdump:[*] All done...
成功脱壳!
开始分析脱壳文件
未脱壳前的apk:
脱壳后的dex文件有很多!
由于这个脱壳技术是在内存中搜索dex文件头所以有很大概率会将非核心的dex文件也给dump下来,只需要简单看看就可以i知道核心dex是哪个!
由于dump出来的dex文件没有xml文件所以用jeb也打开apk来分析!
1.寻找程序入口点
原理:
android AndroidManifest.xml文件的作用魔城烟雨的博客-CSDN博客
找到这个文件开始分析!
根据程序的入口点分析出,该程序共注册了<activity>
1个静态页面,其中页面的实现方法可以从android:name="..."
中得知:
<activity android:name="com.sec.n1book1.MainActivity">
可以在代码结构区找到他们的页面实现位置!
找到目标就可以开始分析伪代码了!
@Override // androidx.appcompat.app.AppCompatActivity
protected native void onCreate(Bundle arg1) {
class com.sec.n1book1.MainActivity.1 implements View.OnClickListener {
@Override // android.view.View$OnClickListener
public void onClick(View v) {
if(Secret.check(MainActivity.this.ed.getText().toString()) != -1) {
Toast.makeText(StubApp.getOrigApplicationContext(MainActivity.this.getApplicationContext()), "right", 1).show();
return;
}
Toast.makeText(StubApp.getOrigApplicationContext(MainActivity.this.getApplicationContext()), "error", 1).show();
}
}
}
这里的逻辑很简单,将输入传入Secret.check函数进行判断如果返回值不为-1就说明flag正确!
双击跟进check函数就可以发现加密是AES:
public class Secret {
static int check(String s) {
return AESUtils.encryptString2Base64(s, s.substring(0, 6), "123456").replaceAll("\r|\n", "").equals("u0uYYmh4yRpPIT/zSP7EL/MOCliVoVLt3gHcrXDymLc=") ? 0 : -1;
}
}
别人的wp:
它调用了`encryptString2Base64(String content, String password, String iv)`这个方法,从而得知这是一个CBC加密,密钥为flag前6位,iv为123456。
然而我并不知道填充怎么处理,参考网上的一些脚本才知道是用空格处理。
上py脚本
from Crypto.Cipher import AES
import base64
password = b'n1book'.ljust(32, b" ")
b64 = b'u0uYYmh4yRpPIT/zSP7EL/MOCliVoVLt3gHcrXDymLc='
en_text = base64.b64decode(b64)
iv = b'123456'.ljust(16,b" ")
aes = AES.new(password,AES.MODE_CBC,iv)
den_text = aes.decrypt(en_text)
print(den_text)