背景故事 :偶然的机会,我发现了一个能在vscode里边就能反编译和重打包apk的工具-apklab(9baK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6m8f1p5E0x3j5h3u0Q4x3V1k6m8f1p5E0x3j5h3u0Q4x3U0W2Q4x3V1y4Q4c8e0g2Q4z5o6g2Q4b7U0k6Q4c8e0k6Q4b7f1y4Q4b7e0q4Q4x3V1y4Q4c8e0N6Q4z5f1y4Q4z5p5u0Q4c8e0g2Q4z5o6S2Q4b7U0m8Q4c8e0g2Q4b7V1g2Q4b7f1g2Q4c8e0c8Q4b7V1k6Q4b7e0q4Q4c8e0g2Q4z5o6g2Q4b7f1y4Q4c8e0c8Q4b7V1y4Q4z5e0N6Q4c8e0g2Q4z5p5k6Q4b7U0N6Q4c8e0c8Q4b7U0S2Q4z5p5q4Q4c8e0k6Q4z5f1y4Q4z5o6W2Q4c8e0c8Q4b7V1q4Q4b7V1q4Q4c8e0g2Q4z5p5k6Q4z5e0q4Q4c8e0g2Q4b7U0S2Q4z5e0k6Q4c8e0g2Q4b7f1c8Q4z5e0m8Q4c8e0c8Q4b7V1c8Q4b7V1k6Q4c8e0N6Q4z5e0c8Q4b7e0S2Q4c8e0g2Q4b7e0c8Q4b7e0N6Q4c8e0k6Q4b7e0S2Q4b7e0q4Q4c8e0g2Q4z5f1g2Q4z5p5u0Q4c8e0k6Q4z5f1c8Q4b7e0g2Q4c8e0S2Q4b7V1k6Q4z5f1u0Q4c8e0S2Q4b7e0q4Q4z5p5y4Q4c8e0k6Q4b7V1y4Q4z5p5k6Q4c8e0k6Q4b7U0c8Q4z5f1g2Q4c8e0g2Q4z5o6S2Q4z5o6k6Q4c8e0k6Q4z5f1g2Q4z5e0m8Q4c8e0y4Q4z5o6m8Q4z5o6u0Q4c8e0k6Q4z5o6S2Q4z5e0q4Q4c8e0g2Q4b7U0m8Q4b7U0q4Q4c8e0g2Q4z5f1y4Q4b7e0S2Q4c8e0k6Q4z5o6y4Q4b7U0y4Q4c8f1k6Q4b7V1y4Q4z5p5y4Q4c8e0S2Q4z5o6y4Q4b7V1c8Q4c8e0c8Q4b7U0S2Q4z5p5c8Q4c8e0S2Q4z5o6y4Q4b7V1c8Q4c8e0c8Q4b7V1c8Q4b7V1k6Q4c8e0N6Q4z5e0c8Q4b7e0S2m8d9g2!0q4y4g2!0m8y4q4!0m8y4#2!0q4y4W2!0m8z5q4!0m8x3g2!0q4y4g2)9&6c8g2)9^5b7W2!0q4y4W2)9&6c8q4!0m8y4g2!0q4y4g2!0m8c8g2)9^5b7#2!0q4y4W2)9^5z5q4)9&6x3q4!0q4y4W2)9&6y4g2!0n7y4q4!0q4y4q4!0n7z5q4!0m8b7g2!0q4y4W2!0n7y4g2)9^5x3g2!0q4y4#2!0m8z5q4)9^5b7W2!0q4y4#2)9&6b7g2)9^5y4q4!0q4z5g2)9^5x3q4)9^5y4W2!0q4y4g2)9&6x3q4)9&6x3g2!0q4y4g2)9^5z5q4)9^5y4W2!0q4y4W2)9&6c8g2)9&6x3q4!0q4y4g2)9&6x3g2!0m8x3W2)9K6c8R3`.`.
我发现,在目前AI编程软件顶多只能做静态分析,可以与电脑交互,但是无法与手机进行交互,更不用说进行动态分析。所以,要想让大模型进行逆向,要解决两个问题
大模型应该能够控制手机
大模型应该能够hook指定app
这两个工具,想必大家都不陌生,一个是adb,一个是frida,那么如何让大模型使用这两个工具呢?答案就是MCP!
MCP(Model Context Protocol,模型上下文协议) ,2024年11月底,由 Anthropic 推出的一种开放标准,旨在统一大模型与外部数据源和工具之间的通信协议。MCP 的主要目的在于解决当前 AI 模型因数据孤岛限制而无法充分发挥潜力的难题,MCP 使得 AI 应用能够安全地访问和操作本地及远程数据,为 AI 应用提供了连接万物的接口。 当然,市面上已经存在各种各样的MCP工具,有没有能够直接用的工具呢?adb的mcp工具倒是有一些,但是各有各的不足,frida相关的mcp比较少,且基本不可用,比如说不支持自定义frida路径,不能自动端口转发,工具定义太复杂等,于是,基于以上背景,我们自定义了两个mcp,分别是adb-mcp 和frida-mcp, 用于手机控制以及动态调试。
至于如何开发以及调试属于自己的MCP工具,后续会专门出文章,我们先来看结果,这两个mcp分别有自己的函数,大模型可以根据当前的上下文,调用这些工具的组合
比如安装一个app,大模型会先检查现在的连接的设备list_devices,拿到设备信息get_device_info,然后安装install_app,安装完成之后再次检查是否成功list_packages 这一切都不要我们关心,我们只需要发送一个指令,安装xxx就可以
在准备工作完成之后,来看看今天的案例
实战案例
某小众女权站点(x瓣)
本来APP不难,主要看AI能做到什么程度
我们这次挑战尽可能用cursor做一切操作,首先使用cursor安装app
可以看到调用了一些工具,app以及成功安装了
然后使用reqable进行抓包,可以看到
import requests
headers = {
'User-Agent': 'Rexxar-Core/0.1.3 api-client/1 com.douban.frodo/7.89.0(307) Android/33 product/coral vendor/Google model/Pixel 4 XL brand/google rom/android network/wifi udid/748659a92430f1e85b21daae1949c5698fdab92f platform/mobile nd/1 com.douban.frodo/7.89.0(307) Rexxar/1.2.151 platform/mobile 1.2.151',
# 'Accept-Encoding': 'br,gzip',
}
params = {
'type': 'movie',
'start': '0',
'count': '10',
'udid': '748659a92430f1e85b21daae1949c5698fdab92f',
'rom': 'android',
'apikey': '0dad551ec0f84ed02907ff5c42e8ec70',
's': 'rexxar_new',
'channel': 'ali_market',
'timezone': 'Asia/Shanghai',
'device_id': '748659a92430f1e85b21daae1949c5698fdab92f',
'os_rom': 'android',
'sugar': '0',
'loc_id': '108288',
'_sig': 's3haQycPjsj6HkvW36nNHArIsa4=',
'_ts': '1756145025',
}
response = requests.get(
'a09K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6X3M7X3!0V1L8#2)9J5k6h3c8G2N6h3u0S2L8W2)9J5k6h3y4G2L8g2)9J5c8X3q4H3K9g2)9J5c8Y4j5J5i4K6u0r3M7%4g2T1K9X3g2U0N6q4)9#2k6X3y4G2L8r3I4W2j5%4c8A6L8$3&6Q4x3V1k6E0L8%4k6A6k6g2)9#2k6Y4u0W2j5h3I4Q4y4h3k6@1K9h3#2W2i4K6g2X3K9r3!0@1k6i4y4@1i4K6u0r3K9i4c8W2L8i4y4Q4x3U0k6Q4x3U0x3K6z5g2)9K6b7W2)9J5b7H3`.`.
params=params,
headers=headers,
)
print(response.text)
with open("response.txt", "w", encoding="utf-8") as f:
f.write(response.text) _sig是主要的加密参数,
我们现在需要使用apklab来反编译整个apk
直接询问大模型加密字段和加密方法
可以看到已经分析出来加密的格式了,问下大模型还需要什么,大模型告诉我们的下一步方案,给出
这里需要注意的是,豆瓣有frida检测,大模型不知道,我已经过了,后续分享代码
大模型生成hook代码
Java.perform(function() {
// 1) 核心签名处:拿到 _sig/_ts、确认明文构造
try {
var Sig = Java.use("d0.a");
Sig.C.overload('java.lang.String','java.lang.String','java.lang.String').implementation = function(url, method, token) {
var ret = this.C(url, method, token);
console.log("\n=== d0.a.C called ===");
console.log("URL: " + url);
console.log("Method: " + method);
console.log("Token: " + (token || "null"));
if (ret) {
console.log("_sig: " + ret.first);
console.log("_ts: " + ret.second);
}
return ret;
};
console.log("[+] Hooked d0.a.C");
} catch (e) {
console.log("[-] d0.a.C hook failed: " + e);
}
// 2) 拿密钥最佳点:HMAC-SHA1 的 SecretKeySpec 构造
try {
var SecretKeySpec = Java.use("javax.crypto.spec.SecretKeySpec");
SecretKeySpec.$init.overload('[B','java.lang.String').implementation = function(keyBytes, alg) {
if (alg === "HmacSHA1") {
var StringCls = Java.use("java.lang.String");
var keyStr = StringCls.$new(keyBytes); // 若是可打印字符串
var Base64 = Java.use("android.util.Base64");
var b64 = Base64.encodeToString(keyBytes, 2);
// 备用 hex
var hex = (function(b){
var out=[]; for (var i=0;i<b.length;i++){ var v=b[i]&0xff; out.push(("0"+v.toString(16)).slice(-2)); }
return out.join("");
})(keyBytes);
console.log("\n=== Found HMAC key (SecretKeySpec) ===");
console.log("alg: " + alg);
console.log("key(ascii?): " + keyStr);
console.log("key(base64): " + b64);
console.log("key(hex): " + hex);
}
return this.$init(keyBytes, alg);
};
console.log("[+] Hooked SecretKeySpec([B,\"HmacSHA1\"])");
} catch (e) {
console.log("[-] SecretKeySpec hook failed: " + e);
}
// 3) 备选:拦截 Mac.init 也能拿到 key
try {
var Mac = Java.use("javax.crypto.Mac");
Mac.init.overload('java.security.Key').implementation = function(key) {
try {
var alg = this.getAlgorithm();
if (alg === "HmacSHA1" || alg === "HmacSHA256" || alg === "HmacSHA512") {
var kclass = key.getClass().getName();
var encField = null;
try { encField = Java.cast(key, Java.use("javax.crypto.spec.SecretKeySpec")).getEncoded(); } catch (_){}
var b64 = encField ? Java.use("android.util.Base64").encodeToString(encField, 2) : "N/A";
console.log("\n=== Mac.init ===");
console.log("Mac alg: " + alg);
console.log("Key class: " + kclass);
console.log("Key(base64): " + b64);
}
} catch (_e) {}
this.init(key);
};
console.log("[+] Hooked Mac.init(Key)");
} catch (e) {
console.log("[-] Mac.init hook failed: " + e);
}
console.log("[*] Hooks ready. Trigger login to capture key/_sig/_ts.");
}); 完整过检测代码
function hook_sig() {
Java.perform(function() {
// 1) 核心签名处:拿到 _sig/_ts、确认明文构造
try {
var Sig = Java.use("d0.a");
Sig.C.overload('java.lang.String','java.lang.String','java.lang.String').implementation = function(url, method, token) {
var ret = this.C(url, method, token);
console.log("\n=== d0.a.C called ===");
console.log("URL: " + url);
console.log("Method: " + method);
console.log("Token: " + (token || "null"));
if (ret) {
console.log("_sig: " + ret.first);
console.log("_ts: " + ret.second);
}
return ret;
};
console.log("[+] Hooked d0.a.C");
} catch (e) {
console.log("[-] d0.a.C hook failed: " + e);
}
// 2) 拿密钥最佳点:HMAC-SHA1 的 SecretKeySpec 构造
try {
var SecretKeySpec = Java.use("javax.crypto.spec.SecretKeySpec");
SecretKeySpec.$init.overload('[B','java.lang.String').implementation = function(keyBytes, alg) {
if (alg === "HmacSHA1") {
var StringCls = Java.use("java.lang.String");
var keyStr = StringCls.$new(keyBytes); // 若是可打印字符串
var Base64 = Java.use("android.util.Base64");
var b64 = Base64.encodeToString(keyBytes, 2);
// 备用 hex
var hex = (function(b){
var out=[]; for (var i=0;i<b.length;i++){ var v=b[i]&0xff; out.push(("0"+v.toString(16)).slice(-2)); }
return out.join("");
})(keyBytes);
console.log("\n=== Found HMAC key (SecretKeySpec) ===");
console.log("alg: " + alg);
console.log("key(ascii?): " + keyStr);
console.log("key(base64): " + b64);
console.log("key(hex): " + hex);
}
return this.$init(keyBytes, alg);
};
console.log("[+] Hooked SecretKeySpec([B,\"HmacSHA1\"])");
} catch (e) {
console.log("[-] SecretKeySpec hook failed: " + e);
}
// 3) 备选:拦截 Mac.init 也能拿到 key
try {
var Mac = Java.use("javax.crypto.Mac");
Mac.init.overload('java.security.Key').implementation = function(key) {
try {
var alg = this.getAlgorithm();
if (alg === "HmacSHA1" || alg === "HmacSHA256" || alg === "HmacSHA512") {
var kclass = key.getClass().getName();
var encField = null;
try { encField = Java.cast(key, Java.use("javax.crypto.spec.SecretKeySpec")).getEncoded(); } catch (_){}
var b64 = encField ? Java.use("android.util.Base64").encodeToString(encField, 2) : "N/A";
console.log("\n=== Mac.init ===");
console.log("Mac alg: " + alg);
console.log("Key class: " + kclass);
console.log("Key(base64): " + b64);
}
} catch (_e) {}
this.init(key);
};
console.log("[+] Hooked Mac.init(Key)");
} catch (e) {
console.log("[-] Mac.init hook failed: " + e);
}
console.log("[*] Hooks ready. Trigger login to capture key/_sig/_ts.");
});
}
function hook_dlopen(){
//Android8.0之后加载so通过android_dlopen_ext函数
var android_dlopen_ext = Module.findExportByName(null,"android_dlopen_ext");
console.log("addr_android_dlopen_ext",android_dlopen_ext);
Interceptor.attach(android_dlopen_ext,{
onEnter:function(args){
var pathptr = args[0];
if(pathptr!=null && pathptr != undefined){
var path = ptr(pathptr).readCString();
if(path.indexOf("libmsaoaidsec.so")!=-1){
console.log("android_dlopen_ext:",path);
hook_call_constructors()
}
}
},
onLeave:function(retvel){
//console.log("leave!");
}
})
}
function hook_call_constructors() {
var linker64_base_addr = Module.getBaseAddress("linker64")
var call_constructors_func_off = 0x4e4dc //这个地址是从手机上把linker64 pull出来,然后到IDA中找到的,不同的机型,地址可能不一样
var call_constructors_func_addr = linker64_base_addr.add(call_constructors_func_off)
var listener = Interceptor.attach(call_constructors_func_addr, {
onEnter: function (args) {
console.log("hooked call_constructors")
var module = Process.findModuleByName("libmsaoaidsec.so")
if (module != null) {
Interceptor.replace(module.base.add(0x1c544), new NativeCallback(function () {
console.log("0x1c544:替换成功")
}, "void", []))
Interceptor.replace(module.base.add(0x1b924), new NativeCallback(function () {
console.log("0x1B924:替换成功")
}, "void", []))
Interceptor.replace(module.base.add(0x26e5c), new NativeCallback(function () {
console.log("0x26e5c:替换成功")
}, "void", []))
listener.detach()
}
},
})
}
function main(){
hook_dlopen()
hook_sig()
}
main() 大模型进行hook,一切都不要我们操作自动验证结果
重写python请求
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
豆瓣API请求发送演示
展示多种请求发送方式
"""
import json
from douban_signature_final import DoubanSignatureFinal
def demo_simple_request():
"""演示简单的API请求"""
print("=== 方法1: 使用完整库发送请求 ===\n")
# 创建签名器
signer = DoubanSignatureFinal()
# 电影热榜API
url = "0b9K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6X3M7X3!0V1L8#2)9J5k6h3c8G2N6h3u0S2L8W2)9J5k6h3y4G2L8g2)9J5c8X3q4H3K9g2)9J5c8Y4j5J5i4K6u0r3M7%4g2T1K9X3g2U0N6q4)9#2k6X3y4G2L8r3I4W2j5%4c8A6L8$3&6Q4x3V1k6E0L8%4k6A6k6g2)9#2k6Y4u0W2j5h3I4Q4y4h3k6@1K9h3#2W2i4K6g2X3K9r3!0@1k6i4y4@1i4K6u0r3K9i4c8W2L8i4y4Q4x3U0k6I4N6h3!0@1i4K6y4n7
params = {
'type': 'movie',
'start': '0',
'count': '5', # 只获取5部电影
'udid': '748659a92430f1e85b21daae1949c5698fdab92f',
'rom': 'android',
'apikey': '0dad551ec0f84ed02907ff5c42e8ec70',
's': 'rexxar_new',
'channel': 'ali_market',
'timezone': 'Asia/Shanghai',
'device_id': '748659a92430f1e85b21daae1949c5698fdab92f',
'os_rom': 'android',
'sugar': '0',
'loc_id': '108288',
}
try:
# 发送请求
response = signer.make_request(url, params, "GET")
print(f"状态码: {response.status_code}")
if response.status_code == 200:
data = response.json()
print("✅ 请求成功!")
print(f"总数: {data.get('count', 0)}")
# 显示电影信息
items = data.get('subject_collection_items', [])
for i, item in enumerate(items, 1):
movie = item.get('rating', {})
print(f"{i}. {item.get('title', 'N/A')} - 评分: {movie.get('value', 'N/A')}")
else:
print(f"❌ 请求失败: {response.text}")
except Exception as e:
print(f"❌ 请求异常: {e}")
def demo_manual_request():
"""演示手动构造请求"""
print(f"\n=== 方法2: 手动构造请求 ===\n")
import requests
from douban_signature_final import DoubanSignatureFinal
signer = DoubanSignatureFinal()
# 书籍推荐API
url = "d3bK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6X3M7X3!0V1L8#2)9J5k6h3c8G2N6h3u0S2L8W2)9J5k6h3y4G2L8g2)9J5c8X3q4H3K9g2)9J5c8Y4j5J5i4K6u0r3M7%4g2T1K9X3g2U0N6q4)9#2k6X3y4G2L8r3I4W2j5%4c8A6L8$3&6Q4x3V1k6T1L8$3!0C8i4K6g2X3j5X3g2K6N6s2y4W2L8r3I4W2M7W2)9J5c8X3W2@1k6h3#2K6i4K6t1$3M7i4g2G2N6q4)9K6b7R3`.`.
params = {
'type': 'book',
'start': '0',
'count': '3',
'apikey': '0dad551ec0f84ed02907ff5c42e8ec70',
}
# 手动生成签名
sig, ts = signer.generate_signature(url, "GET")
params['_sig'] = sig
params['_ts'] = ts
# 构造请求头
headers = {
'User-Agent': 'Rexxar-Core/0.1.3 api-client/1 com.douban.frodo/7.89.0(307) Android/33 product/coral vendor/Google model/Pixel 4 XL brand/google rom/android network/wifi udid/748659a92430f1e85b21daae1949c5698fdab92f platform/mobile nd/1 com.douban.frodo/7.89.0(307) Rexxar/1.2.151 platform/mobile 1.2.151',
'Accept': 'application/json',
}
print(f"请求URL: {url}")
print(f"生成的签名: {sig}")
print(f"时间戳: {ts}")
try:
# 发送GET请求
response = requests.get(url, params=params, headers=headers)
print(f"\n状态码: {response.status_code}")
if response.status_code == 200:
data = response.json()
print("✅ 请求成功!")
# 显示书籍信息
items = data.get('subject_collection_items', [])
for i, item in enumerate(items, 1):
rating = item.get('rating', {})
print(f"{i}. {item.get('title', 'N/A')} - 评分: {rating.get('value', 'N/A')}")
else:
print(f"❌ 请求失败: {response.text}")
except Exception as e:
print(f"❌ 请求异常: {e}")
def demo_post_request():
"""演示POST请求 (注意:这只是演示,可能需要登录)"""
print(f"\n=== 方法3: POST请求演示 ===\n")
import requests
from douban_signature_final import DoubanSignatureFinal
signer = DoubanSignatureFinal()
# 假设的用户API (可能需要登录)
url = "185K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6X3M7X3!0V1L8#2)9J5k6h3c8G2N6h3u0S2L8W2)9J5k6h3y4G2L8g2)9J5c8X3q4H3K9g2)9J5c8Y4j5J5i4K6u0r3N6i4y4W2M7W2)9J5c8Y4m8J5L8$3k6A6L8r3g2Q4x3U0k6I4N6h3!0@1i4K6y4n7
# POST请求体
post_data = json.dumps({
"action": "get_profile"
})
# 生成签名 (包含请求体)
sig, ts = signer.generate_signature(url, "POST", post_data)
# 查询参数
params = {
'apikey': '0dad551ec0f84ed02907ff5c42e8ec70',
'_sig': sig,
'_ts': ts,
}
headers = {
'User-Agent': 'Rexxar-Core/0.1.3 api-client/1 com.douban.frodo/7.89.0(307) Android/33 product/coral vendor/Google model/Pixel 4 XL brand/google rom/android network/wifi udid/748659a92430f1e85b21daae1949c5698fdab92f platform/mobile nd/1 com.douban.frodo/7.89.0(307) Rexxar/1.2.151 platform/mobile 1.2.151',
'Content-Type': 'application/json',
}
print(f"POST URL: {url}")
print(f"请求体: {post_data}")
print(f"生成的签名: {sig}")
try:
response = requests.post(url, params=params, data=post_data, headers=headers)
print(f"\n状态码: {response.status_code}")
print(f"响应: {response.text[:200]}...")
except Exception as e:
print(f"❌ 请求异常: {e}")
def demo_multiple_apis():
"""演示调用多个不同的API"""
print(f"\n=== 方法4: 批量API调用 ===\n")
signer = DoubanSignatureFinal()
apis = [
{
"name": "电影正在热映",
"url": "0abK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6X3M7X3!0V1L8#2)9J5k6h3c8G2N6h3u0S2L8W2)9J5k6h3y4G2L8g2)9J5c8X3q4H3K9g2)9J5c8Y4j5J5i4K6u0r3M7%4g2T1K9X3g2U0N6q4)9#2k6X3y4G2L8r3I4W2j5%4c8A6L8$3&6Q4x3V1k6E0L8%4k6A6k6g2)9#2k6Y4y4Z5L8%4N6A6L8X3N6Q4x3V1k6A6N6r3g2E0M7#2)9J5y4Y4q4#2L8%4c8Q4x3@1u0Q4x3V1x3`.
"params": {'type': 'movie', 'start': '0', 'count': '3', 'apikey': '0dad551ec0f84ed02907ff5c42e8ec70'}
},
{
"name": "电视剧热榜",
"url": "bdcK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6X3M7X3!0V1L8#2)9J5k6h3c8G2N6h3u0S2L8W2)9J5k6h3y4G2L8g2)9J5c8X3q4H3K9g2)9J5c8Y4j5J5i4K6u0r3M7%4g2T1K9X3g2U0N6q4)9#2k6X3y4G2L8r3I4W2j5%4c8A6L8$3&6Q4x3V1k6@1N6W2)9#2k6X3S2G2N6q4)9J5c8X3W2@1k6h3#2K6i4K6t1$3M7i4g2G2N6q4)9K6b7W2)9J5b7#2)9J5y4X3&6T1M7%4m8Q4x3@1t1`.
"params": {'type': 'tv', 'start': '0', 'count': '3', 'apikey': '0dad551ec0f84ed02907ff5c42e8ec70'}
},
{
"name": "音乐新碟榜",
"url": "5a4K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6X3M7X3!0V1L8#2)9J5k6h3c8G2N6h3u0S2L8W2)9J5k6h3y4G2L8g2)9J5c8X3q4H3K9g2)9J5c8Y4j5J5i4K6u0r3M7%4g2T1K9X3g2U0N6q4)9#2k6X3y4G2L8r3I4W2j5%4c8A6L8$3&6Q4x3V1k6E0N6i4y4A6j5#2)9#2k6X3I4S2N6r3g2K6N6q4)9J5c8X3W2@1k6h3#2K6i4K6t1$3M7i4g2G2N6q4)9K6b7W2)9J5b7H3`.`.
"params": {'type': 'music', 'start': '0', 'count': '3', 'apikey': '0dad551ec0f84ed02907ff5c42e8ec70'}
}
]
for api in apis:
print(f" 调用API: {api['name']}")
try:
response = signer.make_request(api['url'], api['params'], "GET")
if response.status_code == 200:
data = response.json()
count = data.get('count', 0)
print(f" ✅ 成功 - 获取到 {count} 条数据")
# 显示第一条数据的标题
items = data.get('subject_collection_items', [])
if items:
first_item = items[0]
print(f" 首条: {first_item.get('title', 'N/A')}")
else:
print(f" ❌ 失败 - 状态码: {response.status_code}")
except Exception as e:
print(f" ❌ 异常: {e}")
print()
def demo_save_response():
"""演示保存响应数据"""
print(f"=== 方法5: 保存响应数据 ===\n")
signer = DoubanSignatureFinal()
url = "c54K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6X3M7X3!0V1L8#2)9J5k6h3c8G2N6h3u0S2L8W2)9J5k6h3y4G2L8g2)9J5c8X3q4H3K9g2)9J5c8Y4j5J5i4K6u0r3M7%4g2T1K9X3g2U0N6q4)9#2k6X3y4G2L8r3I4W2j5%4c8A6L8$3&6Q4x3V1k6E0L8%4k6A6k6g2)9#2k6Y4u0W2j5h3I4Q4y4h3k6@1K9h3#2W2i4K6g2X3K9r3!0@1k6i4y4@1i4K6u0r3K9i4c8W2L8i4y4Q4x3U0k6I4N6h3!0@1i4K6y4n7
params = {
'type': 'movie',
'start': '0',
'count': '10',
'apikey': '0dad551ec0f84ed02907ff5c42e8ec70',
}
try:
response = signer.make_request(url, params, "GET")
if response.status_code == 200:
data = response.json()
# 保存原始响应
with open("douban_movies.json", "w", encoding="utf-8") as f:
json.dump(data, f, ensure_ascii=False, indent=2)
print("✅ 数据已保存到 douban_movies.json")
# 提取并保存简化版本
simplified = []
items = data.get('subject_collection_items', [])
for item in items:
simplified.append({
"title": item.get('title'),
"rating": item.get('rating', {}).get('value'),
"year": item.get('year'),
"directors": [d.get('name') for d in item.get('directors', [])],
"genres": item.get('genres', [])
})
with open("douban_movies_simple.json", "w", encoding="utf-8") as f:
json.dump(simplified, f, ensure_ascii=False, indent=2)
print("✅ 简化数据已保存到 douban_movies_simple.json")
print(f" 共获取 {len(simplified)} 部电影数据")
else:
print(f"❌ 请求失败: {response.status_code}")
except Exception as e:
print(f"❌ 异常: {e}")
if __name__ == "__main__":
print("???? 豆瓣API请求发送演示\n")
# 运行所有演示
demo_simple_request()
demo_manual_request()
demo_post_request()
demo_multiple_apis()
demo_save_response()
print(" 所有演示完成!")
print("\n 使用提示:")
print("1. 简单调用: 使用 DoubanSignatureFinal().make_request()")
print("2. 手动控制: 先生成签名,再发送requests请求")
print("3. 批量调用: 循环调用不同API")
print("4. 数据保存: 将响应保存为JSON文件")
mcp路径
eddK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6*7K9r3W2*7K9s2g2G2k6r3g2E0j5h3!0Q4x3V1k6S2k6r3u0Q4x3X3c8E0j5%4l9`.
84aK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6*7K9r3W2*7K9s2g2G2k6r3g2E0j5h3!0Q4x3V1k6X3M7X3W2V1j5g2)9J5k6r3#2U0M7l9`.`.
总结与展望:
adb-mcp与frida-mcp的出现,为ai逆向分析提供了一个新的思路,不仅可以静态分析,还可以动态调试,今后会分享mcp的写法以及如何使用MCP分析so函数
[培训]Windows内核深度攻防:从Hook技术到Rootkit实战!