前言
为什么要使用rpc进行转发???
现在开发app主流的方案是Java,一些中大厂app是Java+C++,C++最后生成的是so,是arm汇编。
一般分析arm汇编才是最难的,所以中大厂会更倾向把重要加密放在so中,来增强爬虫或者破解的难度!!!
但是如果使用rpc的话,你就不太需要分析繁琐的Java层和so层的加密了!
只需要找到特定的加解密函数和算法,然后frida主动调用java层或so层的方法,拿到加密前后的内容即可!
rpc转发案例
本次使用的app是嘟嘟牛,下载地址如下:
嘟嘟牛在线app下载-嘟嘟牛在线充值下载v4.50 安卓版-绿色资源网 (downcc.com)
抓包
通过抓包登录接口发现,走的是http://api.dodovip.com/api/user/login
提交的请求头是一个{Encrypt:xxxx}
,这???
这块作为平常的渗透或者进一步分析肯定是不行的,所以需要找一下参数 Encrypt
的生成逻辑。
分析
1、直接将app拖入jadx中,查看,发现在AndroidManifest.xml
文件中声明的Activity在源代码中找不到。如 com.dodonew.online.ui.StartActivity
2、这样看应该是有做加壳,查壳工具检测,发现使用了《360加固》
3、脱壳,直接将app拖进在线网站
网址:https://nop.gs
等上大概几分钟,就会脱好了,直接下载下来就行:
4、将得到的dex文件,拖入到jadx中。搜索关键字 Encrypt
找到对应的加密逻辑位置:
可以看到这块使用的是DES加解密,加密函数为RequestUtil.encodeDesMap
5、Objection对该方法直接hook,查看参数、返回值和调用栈
android hooking watch class_method com.dodonew.online.http.RequestUtil.encodeDesMap --dump-args --dump-return --dump-backtrace
|
6、Frida简单写一下hook代码,如下:
function printStack(name) { Java.perform(function () { var Exception = Java.use("java.lang.Exception"); var ins = Exception.$new("Exception"); var straces = ins.getStackTrace(); if (straces != undefined && straces != null) { var strace = straces.toString(); var replaceStr = strace.replace(/,/g, "\r\n"); console.log("=============================" + name + " Stack strat======================="); console.log(replaceStr); console.log("=============================" + name + " Stack end=======================\r\n"); Exception.$dispose(); } }); }
function main() { Java.perform(function() { function printMap2(map){ return Java.cast(map, Java.use("java.util.HashMap")); }
Java.use("com.dodonew.online.http.RequestUtil").encodeDesMap.overload('java.lang.String', 'java.lang.String', 'java.lang.String').implementation=function(data,desKey,desIV){ console.log("RequestUtil encodeDesMap is call") printStack("RequestUtil encodeDesMap") console.log("data:",data) console.log("desKey:",desKey) console.log("desIV:",desIV) var result = this.encodeDesMap(data, desKey, desIV) console.log("RequestUtil encodeDesMap result:", result) return result }
Java.use("com.dodonew.online.http.RequestUtil").paraMap.overload('java.util.Map', 'java.lang.String', 'java.lang.String').implementation = function(addMap,append,sign){ console.log("RequestUtil paraMap is call") console.log("addMap:",addMap) console.log("addMap:",printMap2(addMap)) console.log("append:",append) console.log("sign:",sign) var result = this.paraMap(addMap, append, sign) console.log("RequestUtil paraMap result:", result) return result }
Java.use("com.dodonew.online.http.RequestUtil").decodeDesJson.implementation=function(data,desKey,desIV){ console.log("RequestUtil decodeDesMap is call") printStack("RequestUtil decodeDesMap") console.log("data:",data) console.log("desKey:",desKey) console.log("desIV:",desIV) var result = this.decodeDesJson(data, desKey, desIV) console.log("RequestUtil decodeDesMap result:", result) return result } }); }
setImmediate(main)
|
整理
根据上面分析的结果,可以整理出主动调用应该是这样调用的,一个加密,一个解密。
function callparaMap(username, userPwd, timeStamp) { let result = ""; Java.perform(function () { let map = Java.use("java.util.HashMap").$new(); map.put("timeStamp", timeStamp) map.put("loginImei", "Androidnull") map.put("equtype", "ANDROID") map.put("userPwd", userPwd) map.put("username", username) let r1 = Java.use("com.dodonew.online.http.RequestUtil").paraMap(map, "sdlkjsdljf0j2fsjk", "sign") result = Java.use("com.dodonew.online.http.RequestUtil").encodeDesMap(r1, "65102933", "32028092") }) return result;
}
function calldecodedesjson(data) { let result = ""; Java.perform(function () { result = Java.use("com.dodonew.online.http.RequestUtil").decodeDesJson(data, "65102933", "32028092") }) return result; }
|
搭建服务
既然上述已经把逻辑捋清楚了,并且也已经写好的主动调用的js代码。
那么就来了,如何和python结合到一起,跑成一个web,这样爬虫只需要响应的参数拿到返回值即可。
代码
from fastapi import FastAPI import uvicorn import frida
jsCode = """ function callparamap(username, userPwd, timeStamp) { let result = ""; Java.perform(function () { let map = Java.use("java.util.HashMap").$new(); map.put("timeStamp", timeStamp) map.put("loginImei", "Androidnull") map.put("equtype", "ANDROID") map.put("userPwd", userPwd) map.put("username", username) // let r1 = Java.use("com.dodonew.online.http.RequestUtil").paraMap(map, "sdlkjsdljf0j2fsjk", "sign") // console.log("r1:", r1) // result = Java.use("com.dodonew.online.http.RequestUtil").encodeDesMap(r1, "65102933", "32028092") // console.log("r2:", r2) }) return result; } function calldecodedesjson(data) { let result = ""; Java.perform(function () { result = Java.use("com.dodonew.online.http.RequestUtil").decodeDesJson(data, "65102933", "32028092") // console.log("decode:", decode) }) return result; } rpc.exports = { encrypt: callparamap, decode: calldecodedesjson, }; """
process = frida.get_usb_device().attach('com.dodonew.online') script = process.create_script(jsCode) print('[*] Running 小肩膀') script.load()
app = FastAPI()
@app.get("/getencrypt") async def getencrypt(username, password, timestamp): result = script.exports.encrypt(username, password, timestamp) return {"data": result}
from pydantic import BaseModel
class Item(BaseModel): data: str
@app.post("/getdecode") async def getdecode(item: Item): result = script.exports.decode(item.data) return {"data": result}
if __name__ == '__main__': uvicorn.run(app, port=8080)
|
运行
构造请求调用
代码
import requests import time import json
dt = time.time() * 1000
url = f"http://127.0.0.1:8080/getencrypt?username=18903916120&password=1111×tamp={dt}" r1 = requests.get(url) print(r1.json())
url = "http://api.dodovip.com/api/user/login" headers = { "Content-Type": "application/json;charset=utf-8" } data = { "Encrypt": r1.json().get("data") } print(data) r = requests.post(url=url, headers=headers, data=json.dumps(data)) print(r.text)
data = { "data": r.text } url = "http://127.0.0.1:8080/getdecode" r = requests.post(url=url,headers=headers, data=json.dumps(data)) print(r.text)
|
运行