嘟嘟牛在线登陆加密分析-RPC调用

加密参数
JADX反编译后搜索代码
user/login

基本可以确定就是从这里发起网络请求
跟进

addRequestMap
方法分析

这一看逻辑就清晰了
先添加一个时间戳,在对sign进行加密后在调用encodeDesMap进行加密
最后put到请求中
先分析

encodeDesMap


有KEY和IV,不是AES就是DES加密
encrypt64




DES加密后在Base64加密
那么data就是明文了,hook这个方法取到参数

let RequestUtil = Java.use("com.dodonew.online.http.RequestUtil");
        RequestUtil["encodeDesMap"].overload('java.lang.String', 'java.lang.String', 'java.lang.String').implementation = function (data, desKey, desIV) {
            console.log("**********获取DES明文**********");
            console.log(`参数明文----> ${data}, desKey= ${desKey}, desIV= ${desIV}`);
            let result = this["encodeDesMap"](data, desKey, desIV);
            console.log(`参数加密结果----> ${result}`);
            return result;
        };


与抓包结果一样,这样明文就拿到了。
sign分析,回到刚刚那个传递sign参数的那个函数


字符串最后拼接了一个key后在进行md5加密,
hook一下md5这个函数


很明显,拼接除了sign参数其余全部在添加一个key=sdlkjsdljf0j2fsjk 的最终字符串在进行md5加密。
这样sign与参数加密都分析完了。

RPC

from fastapi import FastAPI, Query
from fastapi.responses import JSONResponse
import frida
import uvicorn

# 创建FastAPI实例
app = FastAPI()

# 定义固定的 key 和 iv
FIXED_KEY = "65102933"
FIXED_IV = "32028092"


# 定义一个GET请求的路由:/decrpyt
@app.get("/decrypt")
def decrypt(encryptedData: str = Query(..., description="The encrypted data to be decrypted")):
    # 处理掉加密数据中的空格和换行符,不然解密函数无法正常解密
    encryptedData = encryptedData.replace(' ', '').replace('\\n', '')

    # 定义处理frida消息的回调函数
    def on_message(message, data):
        if message['type'] == 'send':
            print('[* message]', message['payload'])
        elif message['type'] == 'error':
            print('[!] error:', message['stack'])

    jsCode = """
    rpc.exports = {
        getDecryptInfo: function(encryptedData, key, iv) {
            var result = '';
            Java.perform(function(){
                var RequestUtil = Java.use("com.dodonew.online.http.RequestUtil");
                var instance = RequestUtil.$new();
        
                try {
                    console.log("encryptedData: " + encryptedData);
                    var decodedResult = instance.decodeDesJson(encryptedData, key, iv);
                try {
                    result = JSON.parse(decodedResult);
                } catch (e) {
                    console.log("Result is not valid JSON:", decodedResult);
                    result = decodedResult;
                }
            } catch (e) {
                console.log("调用 decodeDesJson 失败: " + e);
            }
            });
            return result;
        }
    };
    """
    process = frida.get_usb_device().attach('嘟嘟牛在线')
    script = process.create_script(jsCode)
    script.on('message', on_message)
    script.load()

    # 调用 Frida 的 RPC 方法,将 API 请求者传递的 jsonData 和固定的 key, iv 发送到 Frida
    getDecryptData = script.exports.get_decrypt_info(encryptedData, FIXED_KEY, FIXED_IV)
    return JSONResponse({'result': getDecryptData})


# 主入口,运行FastAPI应用
if __name__ == "__main__":
    uvicorn.run(app, host="127.0.0.1", port=50000)