乐于分享
好东西不私藏

【Android 逆向实战】x智赢 APP 登录加密逆向分析

【Android 逆向实战】x智赢 APP 登录加密逆向分析
点击上方「凌霜雁安全志」公众号卡片 → 点击「关注」,网安技术 / 工具 / 自研资源,这里都有!设为星标,新干货第一时间发送~🌟

本文为 Android 逆向学习笔记,仅用于技术研究与交流。


一、抓包分析

打开 APP 点击登录,抓取数据包

  发现三个需要逆向的参数

参数

用途 

逆向优先级

pwd

密码加密

⭐⭐⭐

_sign

 签名校验 

⭐⭐⭐

udid

设备标识

⭐⭐

二、密码加密逆向(MD5)

1、在 jadx 中全局搜索接口 URL 路径,找到调用位置

2、右键 → 查看用例,追踪到加密调用

3、确认密码通过 SecurityUtil.encodeMD5() 进行 MD5 加密

Frida Hook 验证

import fridaimport sysrdev = frida.get_remote_device()session = rdev.attach("xx赢+")scr = """Java.perform(function () {    var SecurityUtil = Java.use("com.example.utils.SecurityUtil");    SecurityUtil.encodeMD5.implementation = function(str){        console.log("传入的参数:", str);        var res = this.encodeMD5(str);        console.log("返回值:", res);        return str;    }});"""script = session.create_script(scr)def on_message(message, data):    print(message, data)script.on("message", on_message)script.load()sys.stdin.read()

运行流程:打开 APP → 运行 Python 脚本 → 点击登录 → 查看控制台输出

✅ 确认:pwd 参数就是 SecurityUtil.encodeMD5(明文密码) 的结果。


三、签名 sign 逆向

回顾请求数据包中有 _sign 参数:

1、在 jadx 中全局搜索"_sign"

2、发现 toSign() 方法

3、第一次 Hook 没有输出,说明未走此路径

4、换个思路:搜索 KEY_SIGN 常量

5、查看用例,发现 SignManager.INSTANCE.signByType() 方法

6、构造 Frida 脚本 Hook 此方法

import fridaimport sysrdev = frida.get_remote_device()session = rdev.attach("xx赢+")scr = """Java.perform(function () {    var SignManager = Java.use("com.example.base.SignManager");    SignManager.signByType.implementation = function(signType, paramMap){        console.log("传入参数 : ");        console.log(signType);        console.log(paramMap);        var res = this.signByType(signType, paramMap);        console.log("sign签名结果 : ", res);        return res;    }});"""script = session.create_script(scr)def on_message(message, data):    print(message, data)script.on("message", on_message)script.load()sys.stdin.read()
7.7、运行后点击登录,对比 Frida 输出与抓包结果

✅ 确认:sign 一致!生成方法就是 SignManager.signByType()

算法还原

public final String signByType(int signType, TreeMap<StringString> paramMap) {    StringBuilder sb = new StringBuilder();    String str = KEY_V1;    if (signType != 0) {        if (signType == 1) {            str = KEY_V2;       // ← 签名密钥        } else if (signType == 2) {            str = KEY_SHARE;        } else if (signType == 3) {            str = KEY_OTHER;        }    }    sb.append(str);    for (String str2 : paramMap.keySet()) {        sb.append(str2);        sb.append(paramMap.get(str2));    }    sb.append(str);    String strEncodeMD5 = SecurityUtil.encodeMD5(sb.toString());    return strEncodeMD5.toUpperCase();  // 转大写}

加密逻辑

签名原文 = KEY + (key1+value1) + (key2+value2) + ... + KEYsign   = MD5(签名原文).toUpperCase()

示例输入 TreeMap:

{  "_appid": "example.android",  "appbuildcode": "512",  "appchannelid": "test",  "appversion": "5.6.1",  "channelid": "test",  "pwd": "e10adc3949ba59abbe56e057f20f883e",  "signkey": "",  "type": "",  "udid": "b4WGOFLDuw2JZhJVayDt6bhHTCuC5HWTBX71PpssDLzYBiAIOrx2mnTOglOd n8eaG8vIDFfhpNo=",  "username": "13888888888"}

验证结果与 Frida 输出一致:


四、udid 逆向(3DES + JNI)

Step 1:定位 Java 层加密方法

全局搜索 "udid"

选择 AppUtils.getUDID() 方法:

Step 2:Frida Hook 验证

import fridaimport sysrdev = frida.get_remote_device()session = rdev.attach("xx赢+")scr = """Java.perform(function () {    var AppUtils = Java.use("com.example.util.AppUtils");    AppUtils.getUDID.implementation = function(context){        console.log("传入参数 : ");        console.log(context);        var res = this.getUDID(context);        console.log("udid结果 : ", res);        return res;    }});"""script = session.create_script(scr)def on_message(message, data):    print(message, data)script.on("message", on_message)script.load()sys.stdin.read()

结果对比:

来源
Frida 输出
b4WGOFLDuw2JZhJVayDt6bhHTCuC5HWTBX71PpssDLz9PKzbxJxICuxxYm9e Kr9e+DRuHBq6bM9LwqKSlbHi4Q==
抓包数据
b4WGOFLDuw2JZhJVayDt6bhHTCuC5HWTBX71PpssDLz9PKzbxJxICuxxYm9e%20Kr9e%2BDRuHBq6bM9LwqKSlbHi4Q%3D%3D

✅ URL 编码后一致,确认加密方式。

Step 3:追踪加密算法

public static String getUDID(Context context) {    return SecurityUtil.encode3Des(context,        getIMEI(context)        + "|"        + System.nanoTime()        + "|"        + SPUtils.getDeviceId()    );}

加密参数配置:

private static final String iv = "******";  // 加密向量// 加密算法: DESede/CBC/PKCS5Padding

Step 4:Hook 3DES 方法获取明文

import fridaimport sysrdev = frida.get_remote_device()session = rdev.attach("xx赢+")scr = """Java.perform(function () {    var SecurityUtil = Java.use("com.example.utils.SecurityUtil");    SecurityUtil.encode3Des.implementation = function(context, str){        console.log("传入参数 : ");        console.log(context);        console.log("3DES明文:", str);        var res = this.encode3Des(context, str);        console.log("3DES密文:", res);        return res;    }});"""script = session.create_script(scr)def on_message(message, data):    print(message, data)script.on("message", on_message)script.load()sys.stdin.read()

多次触发输出:

3DES明文: 5f16248c-6aa8-3215-b582-32c39930b603|25623394644804|5087153DES密文: b4WGOFLDuw2JZhJVayDt6bhHTCuC5HWTBX71PpssDLym0FRcrI3OuK3+GRfm 6Q2KLw4sb0Y5JgrKXJnaPfGERg==3DES明文: 5f16248c-6aa8-3215-b582-32c39930b603|25639844499464|5087153DES密文: b4WGOFLDuw2JZhJVayDt6bhHTCuC5HWTBX71PpssDLym0FRcrI3OuBJT3U+H 6fEP7scjf0jTdWKMcDtAo8tNMA==

明文结构分析

{IMEI}|{nanoTime}|{deviceId} UUID  |  变化的纳秒  | 固定值

其中 System.nanoTime() 返回设备运行时间(纳秒级),每次调用值都会变化。

Step 5:追踪密钥 → JNI 逆向

// 密钥获取链路String desKey = AHAPIHelper.getDesKey(context);    → getSignDesKey(context);        → CheckSignUtil.get3desKey(context);  // native 方法

get3desKey 带有 native 标识,属于 JNI 开发,密钥在 SO 层。

打开 APK 资源文件 lib/ 目录,找到 libnative-lib.so

使用 IDA 分析 SO 文件

包名 com.example.jni,类名 CheckSignUtil,在 IDA 的 Export 中搜索:

java_com_example_jni_CheckSignUtil

确认为静态注册

按 F5 反编译为 C 伪代码:

追踪到 DES3_KEY 变量:

🎯 找到 DES Key:appapiche168comappapiche168co

在线验证 3DES 加密

✅ 加密结果一致,密钥确认正确。


五、完整登录脚本

将以上逆向成果汇总为 Python 脚本:

import hashlibfrom Crypto.Cipher import DES3from Crypto.Util.Padding import padimport base64import requestsimport timeimport uuiddef nanoTime():    CUSTOM_INIT_NS = 38418185978939    current_real_ns = int(time.perf_counter() * 10 ** 9)    offset = current_real_ns - CUSTOM_INIT_NS    result = int(time.perf_counter() * 10**9 - offset)    return resultdef pwdEncrypt(pwd):    md5 = hashlib.md5()    md5.update(pwd.encode("utf-8"))    return md5.hexdigest().upper()def getSign(dict):    md5 = hashlib.md5()    key = "W@oC!AH_6Ew1f6%8"    str1 = key + "".join(["{}{}".format(key, dict[key]) for key in sorted(dict.keys())]) + key    md5.update(str1.encode("utf-8"))    return md5.hexdigest().upper()def getUdid():    str1 = f"{str(uuid.uuid4())}|{nanoTime()}|508715"    DES3_KEY = "appapiche168comappapiche"    DES3_IV = "appapich"    MODE = DES3.MODE_CBC    BLOCK_SIZE = DES3.block_size    key_bytes = DES3_KEY.encode("utf-8")    iv_bytes = DES3_IV.encode("utf-8")    plain_bytes = str1.encode("utf-8")    cipher = DES3.new(key_bytes, MODE, iv=iv_bytes)    padded_data = pad(plain_bytes, BLOCK_SIZE)    encrypted_bytes = cipher.encrypt(padded_data)    base64_str = base64.b64encode(encrypted_bytes).decode("utf-8")    wrapped_str = base64_str[:60] + " " + base64_str[60:]    return wrapped_strdef login(uname, pwd):    udid = getUdid()    enpwd = pwdEncrypt(pwd)    dict = {        "_appid""atc.android",        "appversion""3.17.0",        "channelid""csy",        "pwd": enpwd,        "udid": udid,        "username": uname    }    sign = getSign(dict)    burp0_url = "https://dealercloudapi.xxx.com:443/tradercloud/sealed/login/login.ashx"    burp0_headers = {        "cache-control""public, max-age=0",        "traceid""atc.android_2ec446fa-9068-4baf-8ddc-a5c411b96570",        "content-type""application/x-www-form-urlencoded",        "accept-encoding""gzip",        "user-agent""okhttp/3.14.9"    }    dict["_sign"] = sign    proxy = {"https""127.0.0.1:8080""http""127.0.0.1:8080"}    respon = requests.post(burp0_url, headers=burp0_headers, data=dict, proxies=proxy, verify=False)    return respon.json()if __name__ == '__main__':    res = login("13888888888""123456")    print(res)

总结

逆向目标
加密方式
难度
pwd
MD5
_sign
MD5 + 固定密钥
⭐⭐
udid
3DES + JNI
⭐⭐⭐

通过 Frida Hook 快速定位加密入口,结合 IDA 分析 SO 层密钥,最终实现完整的登录协议逆向。这是 Android 逆向中典型的"Java 层定位 + Native 层分析"流程。

【免责与授权声明】本文旨在进行安全技术研究、提升安全防御意识,所有内容仅限于授权环境下的测试与学习。请务必遵守《网络安全法》及相关法律法规。 文中涉及的漏洞信息及利用方法,严禁用于任何未授权的非法测试与攻击。任何个人或组织因不当使用本文内容而触犯法律,均需自行承担全部责任。安全之路,始于责任,忠于技术。愿与各位同仁共筑更安全的数字世界。