一、逆向分析前置准备(工具与环境)
在开始 Android 端逆向前,需搭建适配的工具链,覆盖 “抓包 - 反编译 - 动态调试” 全流程,尤其针对机票 APP 常见的加密加固场景(如 360 加固、爱加密),需额外准备脱壳工具。
1. 核心工具清单
| 工具类别 | 工具名称 | 用途说明 | 机票 APP 适配点 |
|---|---|---|---|
| 抓包工具 | Fiddler/Charles | 捕获 APP 网络请求,获取接口 URL、参数、响应数据 | 分析机票查询、预订接口的请求格式(如是否含 sign 签名、timestamp 时间戳) |
| 脱壳工具 | Frida-dexdump/BlackDex | 对加固 APP 脱壳,提取真实 DEX 文件(核心代码载体) | 多数航司 APP 会加固,需脱壳后才能反编译查看接口加密逻辑 |
| 反编译工具 | Apktool/Jadx-Gui | 反编译 APK 为 Smali/Java 代码,查看源码逻辑 | 定位接口调用处(如机票价格请求的方法名、参数组装逻辑) |
| 动态调试工具 | Frida/IDA Pro | 动态 Hook APP 方法,打印参数与返回值,破解加密算法 | Hook 机票接口的加密函数(如 sign 生成方法),获取明文参数 |
| 模拟器 / 真机 | 夜神模拟器 / ROOT 安卓机 | 运行待逆向 APP,提供逆向环境(需 ROOT 或开启 USB 调试) | 模拟器可规避部分 APP 的真机检测,适合初期测试 |
| 代码编辑器 | VS Code(Smali 插件) | 编辑 Smali 代码(如需修改 APP 逻辑绕过检测) | 若 APP 检测 Root 环境,可修改 Smali 代码禁用检测逻辑 |
2. 环境配置关键步骤
-
ROOT 环境搭建:
真机选择已 ROOT 的 Android 设备(如小米 / 一加,解锁 Bootloader 后刷入 Magisk);模拟器需开启 “ROOT 权限”(夜神模拟器:设置→开启 ROOT 权限→重启),确保 Frida 等工具能正常注入。
-
证书配置(抓包必备):
因机票 APP 多采用 HTTPS 协议,需将 Fiddler/Charles 根证书安装到 Android 设备(系统证书目录:
/system/etc/security/cacerts/),避免抓包时出现 “证书无效” 导致请求失败。注意:Android 7.0 + 默认不信任用户证书,需将证书转为系统证书格式(.0 格式)并通过 ROOT 权限复制到系统目录。
二、逆向核心流程:从抓包到接口提取(机票 APP 案例)
以 “某航司 Android APP” 为例,完整拆解从网络请求分析到关键接口提取的全流程,重点解决 “接口加密”“参数未知” 两大核心问题。
1. 第一步:初步抓包分析(定位目标接口)
目标:通过抓包确定机票相关接口的基本信息(URL、请求方式、参数特征),为后续逆向提供方向。
操作步骤:
-
配置 Fiddler 代理:
手机 / 模拟器与电脑处于同一局域网,设置代理 IP 为电脑 IP,端口为 Fiddler 默认端口(8888),开启 Fiddler 的 “Capture Traffic”。
-
触发目标行为:
打开航司 APP,操作 “查询北京 - 上海 2025 年 1 月 10 日机票”,Fiddler 捕获到请求(如
https://api.airline.com/flights/query)。 -
分析请求特征:
-
查看 “Inspectors→Headers”:确认请求方法为 POST,Content-Type 为
application/json,请求头含Device-ID(设备唯一标识)、Token(用户登录态)。 -
查看 “Inspectors→Request Body”:发现参数加密(如
{"data":"U2FsdGVkX1+...","sign":"a3f2d1e4..."}),无法直接获取明文参数(如出发地、日期)。
结论:该机票查询接口参数已加密,需通过反编译与动态调试破解加密逻辑。
2. 第二步:APP 脱壳(提取可反编译的 DEX 文件)
若航司 APP 已加固(如启动页显示 “360 加固保护”),直接用 Apktool 反编译会得到加密的 DEX 文件(如classes.dex.encrypted),需先脱壳。
实操(以 Frida-dexdump 为例):
-
安装 Frida 环境:
电脑端:
pip install frida-tools;手机端:根据 CPU 架构(arm64/arm)推送对应版本的frida-server到/data/local/tmp/,并赋予执行权限(chmod 755 frida-server),启动服务(./frida-server)。 -
执行脱壳命令:
\# 查看APP包名(如com.airline.app)
adb shell dumpsys window | grep mCurrentFocus
\# 脱壳并输出DEX文件到当前目录
frida-dexdump -U -f com.airline.app -o ./dex\_output
-
验证脱壳结果:
进入
dex_output目录,若存在classes.dex“classes2.dex” 等文件,且文件大小正常(通常几 MB),说明脱壳成功。
3. 第三步:反编译(定位加密与接口调用逻辑)
用 Jadx-Gui 打开脱壳后的 DEX 文件,可视化查看 Java 代码,定位机票接口的加密方法与调用处。
关键操作:
-
搜索关键词定位:
在 Jadx-Gui 中搜索抓包得到的接口 URL 片段(如
/flights/query),找到对应的请求方法:
// 示例:找到机票查询接口的调用方法
public class FlightApi {
public static void queryFlight(String departCity, String destCity, String date, Callback callback) {
// 1. 组装明文参数
HashMap\<String, String> params = new HashMap<>();
params.put("departCity", departCity);
params.put("destCity", destCity);
params.put("date", date);
params.put("timestamp", String.valueOf(System.currentTimeMillis()));
// 2. 加密参数(关键:调用EncryptUtil加密)
String encryptedData = EncryptUtil.encrypt(params.toString());
String sign = EncryptUtil.generateSign(params);
// 3. 发起POST请求
OkHttpClient client = new OkHttpClient();
RequestBody body = RequestBody.create(MediaType.parse("application/json"),
"{\\"data\\":\\"" + encryptedData + "\\",\\"sign\\":\\"" + sign + "\\"}");
Request request = new Request.Builder()
.url("https://api.airline.com/flights/query")
.post(body)
.build();
client.newCall(request).enqueue(callback);
}
}
-
分析加密逻辑:
定位
EncryptUtil类,查看encrypt与generateSign方法:
-
encrypt方法:使用 AES 算法,密钥从Build.MANUFACTURER(设备厂商)与固定字符串拼接生成(如key = "airline_" + Build.MANUFACTURER)。 -
generateSign方法:将参数按 key 排序后拼接,再用 MD5 哈希生成 sign(如sign = MD5(paramsSortedStr + "secretKey123"))。
关键发现:已明确接口参数的加密算法(AES)、密钥生成规则、sign 签名逻辑,下一步通过动态调试验证并提取明文参数。
4. 第四步:动态调试(Hook 加密方法,提取明文与接口)
用 Frida 编写 Hook 脚本,动态拦截EncryptUtil.encrypt与FlightApi.queryFlight方法,打印明文参数、加密结果、接口 URL,最终获取可直接调用的接口信息。
Frida Hook 脚本示例(hook_flight_api.js):
// 1. Hook EncryptUtil.encrypt方法,打印明文与加密结果
Java.use("com.airline.util.EncryptUtil").encrypt.implementation = function(plainText) {
console.log("\[+] EncryptUtil.encrypt 明文参数: " + plainText);
// 调用原方法获取加密结果
var encrypted = this.encrypt(plainText);
console.log("\[+] EncryptUtil.encrypt 加密结果: " + encrypted);
return encrypted;
};
// 2. Hook FlightApi.queryFlight方法,打印完整参数与接口URL
Java.use("com.airline.api.FlightApi").queryFlight.implementation = function(departCity, destCity, date, callback) {
console.log("\[+] 机票查询接口参数: ");
console.log(" 出发地: " + departCity);
console.log(" 目的地: " + destCity);
console.log(" 日期: " + date);
console.log(" 接口URL: https://api.airline.com/flights/query");
// 调用原方法,不影响APP正常运行
this.queryFlight(departCity, destCity, date, callback);
};
执行 Hook 并查看结果:
- 运行 Hook 脚本:
frida -U -f com.airline.app -l hook\_flight\_api.js --no-pause
-
触发查询行为:
在 APP 中再次查询 “北京 - 上海 2025 年 1 月 10 日机票”,电脑端控制台输出:
\[+] 机票查询接口参数:
出发地: PEK(北京首都机场三字码)
目的地: SHA(上海虹桥机场三字码)
日期: 2025-01-10
接口URL: https://api.airline.com/flights/query
\[+] EncryptUtil.encrypt 明文参数: {"departCity":"PEK","destCity":"SHA","date":"2025-01-10","timestamp":1736486400000}
\[+] EncryptUtil.encrypt 加密结果: U2FsdGVkX1+...(与抓包结果一致)
成果:已提取到机票查询接口的明文参数规则(出发地用三字码、含 timestamp)、完整 URL、加密算法细节,可基于此编写 Python 脚本模拟请求。
三、关键数据提取与接口调用(落地实现)
基于逆向得到的接口信息,编写 Python 脚本模拟 APP 请求,提取机票价格、舱位等关键数据,解决 “手动抓包无法自动化” 的问题。
1. 还原加密逻辑(Python 实现)
根据逆向得到的 AES 加密与 sign 生成规则,编写加密工具函数:
import json
import time
import hashlib
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
from base64 import b64encode
\# 1. AES加密(对应APP中的EncryptUtil.encrypt)
def aes\_encrypt(plain\_text, device\_manufacturer="Xiaomi"):
\# 密钥生成规则:airline\_ + 设备厂商(APP中获取Build.MANUFACTURER)
key = ("airline\_" + device\_manufacturer).encode("utf-8")
\# AES模式:CBC,IV为固定16位(逆向从代码中找到:"airline\_iv\_123456")
iv = "airline\_iv\_123456".encode("utf-8")
\# 明文编码并补位
plain\_bytes = plain\_text.encode("utf-8")
padded\_bytes = pad(plain\_bytes, AES.block\_size)
\# 加密
cipher = AES.new(key, AES.MODE\_CBC, iv)
encrypted\_bytes = cipher.encrypt(padded\_bytes)
\# Base64编码返回(与APP加密结果格式一致)
return b64encode(encrypted\_bytes).decode("utf-8")
\# 2. 生成sign(对应APP中的EncryptUtil.generateSign)
def generate\_sign(params, secret\_key="secretKey123"):
\# 参数按key升序排序
sorted\_params = sorted(params.items(), key=lambda x: x\[0])
\# 拼接为字符串(如departCity=PEK\&date=2025-01-10...)
params\_str = "&".join(\[f"{k}={v}" for k, v in sorted\_params])
\# 拼接secretKey后MD5哈希
sign\_str = params\_str + secret\_key
return hashlib.md5(sign\_str.encode("utf-8")).hexdigest()
\# 3. 组装请求参数
def build\_request\_params(depart\_city, dest\_city, date):
\# 明文参数(与APP中一致)
params = {
"departCity": depart\_city,
"destCity": dest\_city,
"date": date,
"timestamp": str(int(time.time() \* 1000)) # 毫秒级时间戳
}
\# 加密data与生成sign
encrypted\_data = aes\_encrypt(json.dumps(params))
sign = generate\_sign(params)
\# 返回最终请求体
return {
"data": encrypted\_data,
"sign": sign
}
2. 调用接口提取机票数据
使用requests库发送 POST 请求,解析响应数据(需先逆向响应解密逻辑,此处假设响应为明文 JSON):
import requests
def get\_flight\_data(depart\_city, dest\_city, date):
\# 1. 组装请求参数
request\_body = build\_request\_params(depart\_city, dest\_city, date)
\# 2. 设置请求头(与APP抓包结果一致,含Device-ID、Token)
headers = {
"User-Agent": "AirlineApp/7.2.0 (Android; 13; Xiaomi M2102K1C)",
"Device-ID": "861234567890123", # 从APP中Hook获取真实Device-ID
"Token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", # 登录后获取的Token
"Content-Type": "application/json"
}
\# 3. 发送请求
url = "https://api.airline.com/flights/query"
response = requests.post(url, json=request\_body, headers=headers, verify=False)
\# 4. 解析响应(示例:响应为机票列表JSON)
if response.status\_code == 200:
flight\_list = response.json()\["data"]\["flights"]
\# 提取关键数据(航班号、价格、舱位)
result = \[]
for flight in flight\_list:
result.append({
"flight\_no": flight\["flightNo"],
"depart\_time": flight\["departTime"],
"arrive\_time": flight\["arriveTime"],
"cabin": flight\["cabin"],
"price": flight\["price"],
"remaining\_seats": flight\["remainingSeats"] # 剩余座位数
})
return result
else:
raise Exception(f"接口请求失败:{response.status\_code},响应:{response.text}")
\# 调用示例:查询北京(PEK)-上海(SHA)2025-01-10机票
if \_\_name\_\_ == "\_\_main\_\_":
flights = get\_flight\_data("PEK", "SHA", "2025-01-10")
print("提取的机票数据:")
for idx, flight in enumerate(flights, 1):
print(f"\n航班{idx}:")
print(f" 航班号:{flight\['flight\_no']}")
print(f" 时间:{flight\['depart\_time']} - {flight\['arrive\_time']}")
print(f" 舱位/价格:{flight\['cabin']} / ¥{flight\['price']}")
print(f" 剩余座位:{flight\['remaining\_seats']}")
四、常见问题与解决方案(QA)
Q1:逆向时 APP 检测到 Root / 模拟器,直接闪退怎么办?
A:两种应对方案:
- 修改 Smali 代码绕过检测:用 Apktool 反编译 APP,搜索 “Root”“emulator” 相关关键词,找到检测方法(如
isRooted()),将返回值改为false(Smali 中const/4 v0, 0x0),重新打包签名。
- 示例:若找到 Smali 代码
method public static isRooted()Z,原代码为:
.method public static isRooted()Z
.locals 1
\# 原逻辑:检测到 Root 则返回 0x1(true)
const/4 v0, 0x1
return v0
.end method
- 修改后:
.method public static isRooted()Z
.locals 1
\# 强制返回 0x0(false),绕过 Root 检测
const/4 v0, 0x0
return v0
.end method
- 重新打包:
apktool b 反编译目录 -o modified.apk,再用jarsigner签名(需提前生成签名文件:keytool -genkey -v -keystore my.keystore -alias myalias -keyalg RSA -validity 3650)。
- 使用 Frida Hook 禁用检测:编写 Hook 脚本拦截检测方法,强制返回 “非 Root / 非模拟器”:
// Hook Root 检测方法
Java.use("com.airline.util.DeviceCheck").isRooted.implementation = function() {
console.log("\[+] 绕过 Root 检测");
return false; // 强制返回非 Root
};
// Hook 模拟器检测方法(如 isEmulator())
Java.use("com.airline.util.DeviceCheck").isEmulator.implementation = function() {
console.log("\[+] 绕过模拟器检测");
return false; // 强制返回非模拟器
};
// 若检测通过 Build.PROP 识别模拟器,Hook Build 类修改属性
Java.use("android.os.Build").MANUFACTURER.implementation = function() {
return "Xiaomi"; // 伪装为小米真机
};
Java.use("android.os.Build").MODEL.implementation = function() {
return "M2102K1C"; // 伪装为小米11机型
};
- 执行脚本:
frida -U -f com.airline.app -l hook_bypass_detection.js --no-pause,APP 即可正常启动。
Q2:接口参数中出现动态变化的 “nonce”“sign”,逆向时无法固定生成规则怎么办?
A:这类动态参数通常由 APP 本地算法生成(如结合时间戳、设备信息、随机字符串),可通过以下两种方式解决:
- Hook 动态参数生成方法:直接拦截生成参数的函数,获取最终生成的参数值,无需还原算法。
- 示例:若
sign由SignUtil.generateSign(params, nonce, timestamp)生成,Hook 该方法:
// 找到 SignUtil 类的 generateSign 方法(需根据实际包名调整)
const SignUtil = Java.use("com.airline.util.SignUtil");
// 重载方法(需匹配参数个数与类型,此处假设为 3 个参数:params(Map)、nonce(String)、timestamp(String))
SignUtil.generateSign.overload("java.util.Map", "java.lang.String", "java.lang.String").implementation = function(params, nonce, timestamp) {
// 打印输入参数
console.log("\[+] generateSign 参数:");
console.log(" params: " + JSON.stringify(params));
console.log(" nonce: " + nonce);
console.log(" timestamp: " + timestamp);
// 调用原方法获取 sign
const sign = this.generateSign(params, nonce, timestamp);
console.log("\[+] 生成的 sign: " + sign);
return sign;
};
- 执行脚本后,触发接口请求,控制台会输出
nonce“timestamp”“sign” 的真实值,直接用于 Python 模拟请求即可。
- 直接调用 APP 本地算法生成参数:若参数生成逻辑复杂(如涉及 Native 层 C++ 代码),可通过
frida-inject或Xposed模块,在 APP 运行时调用其算法生成参数,再传递给爬虫脚本。
- 示例:用 Frida 调用
generateSign方法生成参数:
// 在脚本中主动调用 APP 方法生成 sign
function generateSignForCrawl(params) {
const SignUtil = Java.use("com.airline.util.SignUtil");
const nonce = Java.use("java.util.UUID").randomUUID().toString().replace(/-/g, ""); // 生成随机 nonce(模仿 APP 逻辑)
const timestamp = String(Date.now());
const sign = SignUtil.generateSign(params, nonce, timestamp);
return { nonce, timestamp, sign };
}
// 暴露方法给外部(如通过 frida-rpc 供 Python 调用)
rpc.exports = {
getsign: generateSignForCrawl
};
- Python 端调用:
import frida
import json
\# 连接设备
device = frida.get\_usb\_device()
\# 附加到 APP 进程
session = device.attach("com.airline.app")
\# 加载脚本
with open("hook\_call\_sign\_method.js", "r", encoding="utf-8") as f:
script = session.create\_script(f.read())
script.load()
\# 调用脚本中的 getsign 方法生成参数
params = {"departCity": "PEK", "destCity": "SHA", "date": "2025-01-10"}
sign\_data = script.exports.getsign(params)
print("动态参数:", sign\_data)
\# 输出:{"nonce": "a1b2c3d4e5f6...", "timestamp": "1736486400000", "sign": "f8e7d6c5b4a3..."}
Q3:接口响应是加密数据(如乱码字符串),无法直接解析怎么办?
A:需先逆向响应解密逻辑,步骤如下:
- 定位解密方法:在 Jadx-Gui 中搜索 “decrypt”“decode” 等关键词,或跟踪接口回调逻辑(如
FlightApi.queryFlight的Callback实现类),找到响应处理代码。
- 示例:回调中解密响应的代码:
public class FlightCallback implements Callback {
@Override
public void onSuccess(String response) {
// 响应解密(调用 EncryptUtil.decrypt)
String decryptedResponse = EncryptUtil.decrypt(response);
// 解析明文 JSON
JSONObject json = new JSONObject(decryptedResponse);
List\<Flight> flightList = parseFlightList(json);
}
}
- 分析解密算法:查看
EncryptUtil.decrypt方法,确认算法类型(如 AES、RSA)、密钥、IV 等信息。
- 示例:AES 解密逻辑:
public static String decrypt(String encryptedData) {
// 密钥与加密时一致(airline\_ + 设备厂商)
String key = "airline\_" + Build.MANUFACTURER;
String iv = "airline\_iv\_123456"; // 与加密 IV 相同
// Base64 解码 → AES 解密 → 去除补位
byte\[] encryptedBytes = Base64.decode(encryptedData, Base64.DEFAULT);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT\_MODE, new SecretKeySpec(key.getBytes(), "AES"), new IvParameterSpec(iv.getBytes()));
byte\[] decryptedBytes = cipher.doFinal(encryptedBytes);
return new String(decryptedBytes, StandardCharsets.UTF\_8);
}
- Python 实现解密:根据逆向结果编写解密函数,解析响应数据:
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad
from base64 import b64decode
def aes\_decrypt(encrypted\_data, device\_manufacturer="Xiaomi"):
\# 与 APP 解密逻辑一致
key = ("airline\_" + device\_manufacturer).encode("utf-8")
iv = "airline\_iv\_123456".encode("utf-8")
\# Base64 解码
encrypted\_bytes = b64decode(encrypted\_data)
\# AES 解密
cipher = AES.new(key, AES.MODE\_CBC, iv)
decrypted\_bytes = cipher.decrypt(encrypted\_bytes)
\# 去除补位(PKCS5Padding)
plain\_bytes = unpad(decrypted\_bytes, AES.block\_size)
return plain\_bytes.decode("utf-8")
\# 调用示例:解密接口响应
encrypted\_response = response.json()\["data"] # 加密的响应数据
plain\_response = aes\_decrypt(encrypted\_response)
flight\_json = json.loads(plain\_response)
print("解密后的机票数据:", flight\_json)
Q4:APP 采用 Native 层(C/C++)加密,Jadx-Gui 无法查看代码怎么办?
A:Native 层加密需通过逆向 SO 文件(libxxx.so)解决,核心步骤如下:
-
提取 SO 文件:从 APK 解压目录的
lib/arm64-v8a/(或lib/armeabi-v7a/)文件夹中找到加密相关的 SO 文件(如libencrypt.so)。 -
分析 SO 文件:使用 IDA Pro 或 Ghidra 反编译 SO 文件,定位加密函数(如
Java_com_airline_util_EncryptUtil_nativeEncrypt,对应 Java 层的native encrypt方法)。
- 关键技巧:在 IDA 中搜索 “AES”“MD5” 等关键词,或跟踪函数调用栈,找到加密算法的核心逻辑(如 AES 密钥扩展、轮函数)。
- Hook Native 函数:若无法还原算法,直接用 Frida Hook Native 方法获取输入输出:
// Hook Native 加密函数(需根据实际函数签名调整)
// 函数签名格式:Java\_包名\_类名\_方法名(包名中的 . 替换为 \_,内部类用 \$ 连接)
Interceptor.attach(Module.findExportByName("libencrypt.so", "Java\_com\_airline\_util\_EncryptUtil\_nativeEncrypt"), {
onEnter: function(args) {
// args\[0] = JNIEnv\*, args\[1] = jobject(this), args\[2] = jstring(明文参数)
const plainText = Java.vm.getEnv().getStringUtfChars(args\[2], null).readCString();
console.log("\[+] Native 加密明文:", plainText);
// 保存明文参数,供 onLeave 时使用
this.plainText = plainText;
},
onLeave: function(retval) {
// retval = jstring(加密结果)
const encryptedText = Java.vm.getEnv().getStringUtfChars(retval, null).readCString();
console.log("\[+] Native 加密结果:", encryptedText);
}
});
- 执行脚本后,即可获取 Native 层加密的明文与结果,直接用于模拟请求。
五、逆向合规性与风险提示
- 法律合规:
-
逆向前需确认 APP 的《用户协议》《隐私政策》,禁止逆向未经授权的商业 APP(如航司 APP 若明确禁止 “抓取、逆向”,需提前获得授权);
-
禁止利用逆向获取的数据从事违法活动(如倒卖机票价格、侵犯用户隐私),仅可用于合法的技术研究或企业内部数据分析。
- 技术风险:
-
部分 APP 采用 “加固 + 反调试 + 行为检测” 三重防护(如梆梆加固、爱加密高级版),逆向难度极高,可能导致工具崩溃、设备被封禁;
-
动态调试时需避免频繁操作,防止 APP 触发风控系统(如临时封禁设备 ID、账号),建议使用测试账号而非真实用户账号。
- 维护成本:
-
APP 版本更新可能导致加密算法、接口参数变化(如 sign 生成规则修改),需定期重新逆向,确保爬虫脚本兼容性;
-
建议监控接口返回码(如 401 未授权、403 禁止访问),及时发现逆向逻辑失效问题。
六、工具链推荐与进阶学习
1. 进阶工具推荐
| 工具名称 | 用途说明 | 适用场景 |
|---|---|---|
| Objection | Frida 可视化交互工具,支持一键绕过 Root 检测、查看内存中的类与方法 | 快速调试,无需编写复杂 Hook 脚本 |
| Xposed + JustTrustMe | 禁用 APP 的 SSL 证书校验,解决抓包时 “证书无效” 问题 | 无需安装系统证书,适合 Android 7.0+ 设备 |
| GDA (Ghidra) | 免费开源的逆向工具,支持反编译 SO 文件、分析 Native 代码 | 替代 IDA Pro(收费),适合个人学习 |
| Packet Capture | Android 端抓包工具,无需电脑,直接在手机上捕获 APP 网络请求 | 临时分析,无需配置代理 |
2. 学习资源
-
书籍:《Android 逆向工程权威指南》《Frida 逆向与 Hook 实战》
-
社区:看雪学院(https://bbs.pediy.com/)、吾爱破解(https://www.52pojie.cn/)、Frida 官方文档(https://frida.re/docs/home/)
-
实战项目:尝试逆向开源的 Demo APP(如 https://github.com/lasting-yang/frida-demo),熟悉工具流程后再挑战复杂 APP。
1359

被折叠的 条评论
为什么被折叠?



