[SWPU 2019]easyapp——手把手frida环境配置与初步使用以及so文件动态调试教程
字数 1728 2025-08-24 20:49:22
Frida环境配置与Android SO文件动态调试教程
前言
本教程将详细介绍如何配置Frida环境以及进行Android SO文件的动态调试,以解决[SWPU 2019]easyapp题目为例。教程包含Java层分析、native层分析、动态调试SO文件和使用Frida hook函数两种解法。
环境准备
硬件与软件要求
-
设备选择:
- 雷电模拟器(推荐使用安卓5及以下版本)
- 或安卓5及以下的真机(高版本可能导致程序崩溃)
-
必要工具:
- JEB 4.20.0(或其他反编译工具)
- IDA Pro 8.3
- ADB工具
- Anaconda(Python环境管理)
-
目标APK:
- 未加固的APK文件(com.example.ndktest2)
程序分析
Java层分析
使用JEB反编译APK后,查看MainActivity:
package com.example.ndktest2;
import android.os.Bundle;
import android.view.View.OnClickListener;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity {
private EditText password;
static {
System.loadLibrary("native-lib");
}
public native String Encrypt();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.setContentView(0x7F09001C); // layout:activity_main
EditText password = (EditText)this.findViewById(0x7F07005B); // id:password
((Button)this.findViewById(0x7F07004E)).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (password.getText().toString().equals(MainActivity.this.Encrypt())) {
Toast.makeText(MainActivity.this, "登录成功", 1).show();
return;
}
Toast.makeText(MainActivity.this, "登录失败", 0).show();
}
});
}
}
关键点:
- 加载了native库"native-lib"
- 调用了native方法Encrypt()
- 用户输入与Encrypt()返回值比较决定登录成功与否
Native层分析
- 使用IDA打开libnative-lib.so文件
- 搜索Encrypt函数,发现可能被混淆
- 真正的加密函数实际上是"test"函数
- 通过JNI_OnLoad函数分析注册过程:
int __cdecl JNI_OnLoad(_JavaVM *a1) {
void *v3; // [esp+24h] [ebp-18h] BYREF
if (_JavaVM::GetEnv(a1, &v3, 65542))
return -1;
if (sub_A570(v3))
return 65542;
return -1;
}
_BOOL4 __cdecl sub_A570(_JNIEnv *a1) {
return sub_A5F0(a1, "com/example/ndktest2/MainActivity", off_38008, 1) != 0;
}
_BOOL4 __cdecl sub_A5F0(_JNIEnv *a1, char *a2, int a3, int a4) {
int Class; // [esp+24h] [ebp-18h]
Class = _JNIEnv::FindClass(a1, a2);
return Class && _JNIEnv::RegisterNatives(a1, Class, a3, a4) >= 0;
}
关键点:
- 使用RegisterNatives注册了native方法
- JNINativeMethod结构体包含方法名、签名和实际函数指针
- 真正的加密函数是test而非Encrypt
解法一:动态调试SO文件
准备工作
- 启动模拟器并安装APK
- 检查设备连接:
adb devices - 查看设备架构:
adb shell getprop ro.product.cpu.abi(应为x86)
搭建调试环境
-
将IDA的android_x86_server推送到设备:
adb push android_x86_server /data/local/tmp/ adb shell chmod 755 /data/local/tmp/android_x86_server adb shell /data/local/tmp/android_x86_server -
端口转发:
adb forward tcp:23946 tcp:23946
启动调试
- 检查调试权限(AndroidManifest.xml中android:debuggable="true")
- 以调试模式启动应用:
adb shell am start -D -n com.example.ndktest2/.MainActivity - 使用JEB附加进程进行调试
IDA动态调试
- 在IDA中打开libnative-lib.so
- 在加密函数(test)处下断点
- 选择Remote Linux debugger
- 设置调试选项:
- Hostname: localhost
- Port: 23946
- 附加到目标进程
- 在模拟器中输入任意内容并点击登录
- IDA将在断点处暂停,可查看寄存器中的flag值
关键点:
- 断点应下在真正的加密函数test而非Encrypt
- 查看寄存器v1可获取flag:"YouaretheB3ST"
解法二:使用Frida hook函数
Python环境配置
- 安装Anaconda管理Python环境
- 创建Python 3.7环境:
conda create -n python37 python=3.7 conda activate python37
安装Frida
- 安装Frida核心:
pip install frida==12.8.0 - 安装Frida-tools:
pip install frida-tools==5.4.0 - 安装objection:
pip install objection==1.8.4
安装Frida-server
- 下载与Frida版本匹配的frida-server(12.8.0)
- 根据设备架构选择正确版本(x86)
- 推送并运行:
adb push frida-server /data/local/tmp/ adb shell chmod 755 /data/local/tmp/frida-server adb shell /data/local/tmp/frida-server
Hook函数
- 启动objection注入:
objection -g com.example.ndktest2 explore - Hook Encrypt方法:
android hooking watch class_method com.example.ndktest2.MainActivity.Encrypt --dump-args --dump-backtrace --dump-return - 在APP中输入任意内容并点击登录
- objection将显示hook到的返回值:"YouaretheB3ST"
关键点:
- Frida和frida-server版本必须一致
- 确保frida-server持续运行
- 可直接获取加密函数的返回值
总结
通过本教程,我们学习了:
- APK的Java层和native层分析方法
- JNI函数注册机制和混淆识别技巧
- 两种获取加密flag的方法:
- IDA动态调试SO文件
- Frida hook技术
最终flag为:flag{YouaretheB3ST}
常见问题解决
- 程序崩溃:确保使用安卓5及以下版本
- 断点不生效:确认下在真正的加密函数(test)而非Encrypt
- Frida连接失败:检查版本匹配和frida-server是否运行
- IDA无法附加:确认端口转发和调试权限设置正确
扩展学习
- JNI函数注册机制
- Frida高级hook技巧
- IDA动态调试其他架构SO文件
- 对抗反调试技术