某社交App协议分析之X-SIGN
字数 1328 2025-08-06 12:21:05
某社交App协议分析之X-SIGN生成全流程教学文档
一、环境准备与抓包配置
-
工具准备
- Charles:用于HTTPS抓包(需安装根证书)。
- Android设备:需Root或模拟器(如Genymotion)。
- 证书配置:
- 导出Charles根证书(
.cer格式)。 - 计算证书哈希:
openssl x509 -inform DER -subject_hash_old -in 证书名.cer。 - 重命名证书为
哈希值.0,复制到Android系统证书目录:/system/etc/security/cacerts/。 - 在手机设置中信任Charles证书(系统凭据)。
- 导出Charles根证书(
-
代理设置
- Charles开启
SSL Proxying,设置*:*允许所有域名。 - 手机配置代理:IP为PC本地IP,端口默认8888。
- Charles开启
二、静态分析APK
-
反编译工具
- 使用GDAE或JADX反编译APK,搜索关键字符串
X-SIGN。
- 使用GDAE或JADX反编译APK,搜索关键字符串
-
关键代码定位
- 找到
c()方法,其内部调用链:tj.put("X-SIGN", this.a(uoe, tj, this.d)); - 参数分析:
uoe:通过Coded.getInstance().aesEncode()生成的字节数组。tj:请求头Map(含User-Agent、X-KV等)。this.d:固定字符串(如pF5RkqjVJ4nPiR7i)。
- 找到
-
核心方法
a()分析- 调用
Coded.getInstance().sign(uobyteArray, bytes):uobyteArray:a()方法的arg0(含请求数据)。bytes:a()方法的arg2(盐值)。
- 调用
三、动态Hook关键函数
-
Frida脚本编写
- Hook目标:
aesEncode、a、sign方法。 - 难点处理:
- 字节数组输出:转换为Hex或Base64字符串。
- Map遍历:通过
keySet()迭代打印键值对。
function bytes2hex(arrBytes) { return Array.from(arrBytes, byte => byte.toString(16).padStart(2, '0')).join(''); } Java.perform(() => { // Hook aesEncode let Coded = Java.use('com.xxxxxx.util.jni.Coded'); Coded.aesEncode.overload('[B', 'int', '[B', 'int', '[B').implementation = function(...args) { console.log('aesEncode Input:', bytes2hex(args[0])); let result = this.aesEncode(...args); console.log('aesEncode Output:', result); return result; }; // Hook sign Coded.sign.overload('[B', '[B').implementation = function(arg0, arg1) { console.log('sign Input:', bytes2hex(arg0), bytes2hex(arg1)); let result = this.sign(arg0, arg1); console.log('sign Output:', result); return result; }; }); - Hook目标:
-
输出分析
aesEncode:生成加密后的字节数组(长度1543)。sign:输入为User-Agent拼接请求数据+盐值前8字节,输出Base64编码的X-SIGN。
四、SO层逆向分析
-
模块定位
- 静态代码块加载
libcoded.so(通过System.loadLibrary)。 - IDA分析:
- 函数
sdbyecbu37x:JNI接口,调用sign函数。 sign函数逻辑:ptr = malloc(data_len + 8); // 分配空间 memcpy(ptr, data, data_len); // 复制数据 *(uint32_t*)(ptr + data_len) = salt; // 拼接盐值前8字节 j_sha1(ptr, output, data_len + 8); // SHA1哈希
- 函数
- 静态代码块加载
-
加密流程
- 输入:
User-Agent + 请求数据 + 盐值前8字节。 - 算法:SHA1哈希 → Base64编码 → 生成X-SIGN。
- 输入:
五、算法复现(Python示例)
import hashlib
import base64
def generate_xsign(user_agent: bytes, request_data: bytes, salt: bytes) -> str:
# 拼接数据: User-Agent + 请求数据 + 盐值前8字节
combined = user_agent + request_data + salt[:8]
# SHA1哈希
sha1 = hashlib.sha1(combined).digest()
# Base64编码
return base64.b64encode(sha1).decode()
# 示例数据
user_agent = b"MomoChat/8.31.6 Android/7075 (IN2020; Android 7.1.2; Gapps 1; zh_CN; 14; OnePlus)"
request_data = b"...抓包中的请求体..."
salt = b"pF5RkqjVJ4nPiR7i"
xsign = generate_xsign(user_agent, request_data, salt)
print("X-SIGN:", xsign) # 输出: gB0dO33VfWkQyOWE7++tv/EBrtc=
六、总结与防护建议
-
关键点
- X-SIGN由
User-Agent + 请求数据 + 固定盐值经SHA1和Base64生成。 - 盐值硬编码在SO层,需逆向提取(如
pF5RkqjVJ4nPiR7i)。
- X-SIGN由
-
防护建议
- App端:动态盐值、代码混淆、Native层校验。
- 服务端:限制高频请求、多因素验证。
-
延伸风险
- 协议逆向可导致批量注册/登录攻击,建议加强风控策略。
附录
(完)