最近在研究车联网领域的APP安全机制,选择了"车智赢"这款主流车商服务平台作为分析对象。版本v5.6.1的登录协议采用了典型的参数加密方案,包含三个关键参数:_sign(请求签名)、pwd(密码)和udid(设备标识)。本文将详细拆解这三个参数的生成算法,并给出可直接复用的Python实现。
在移动应用安全领域,登录协议的逆向分析是基础但重要的技能。通过这次实战,不仅能理解车智赢的安全机制设计思路,也能掌握通用的逆向分析方法论。特别说明:本文仅用于安全研究学习,所有技术细节都已脱敏处理。
工欲善其事,必先利其器。逆向分析需要准备以下工具链:
建议在虚拟机中搭建测试环境,避免影响主机系统。我使用的是Ubuntu 20.04 LTS,配合Windows物理机进行交叉验证。
定位登录相关代码的核心思路是特征搜索:
在Jadx中全局搜索后,发现登录逻辑集中在:
code复制com.che168.autotradercloud.login.model.LoginModel
这个类中的login()方法包含了完整的登录流程。
经验之谈:大型APP的代码往往非常庞大,建议先通过关键字符串缩小范围,再通过调用关系梳理流程。
通过抓包可以看到pwd参数呈现32位十六进制字符串的特征:
code复制pwd=5f4dcc3b5aa765d61d8327deb882cf99
这明显符合MD5哈希的特征。但需要确认是否包含额外的处理逻辑。
在LoginModel中追踪密码处理流程:
java复制String encryptedPwd = SecurityUtil.encodeMD5(rawPassword);
requestParams.put("pwd", encryptedPwd);
继续查看SecurityUtil类的实现:
java复制public static String encodeMD5(String str) {
try {
byte[] digest = MessageDigest.getInstance("MD5").digest(str.getBytes());
StringBuilder sb = new StringBuilder();
for (byte b : digest) {
sb.append(String.format("%02x", b));
}
return sb.toString();
} catch (Exception e) {
return "";
}
}
分析可知:
Python实现:
python复制import hashlib
def encrypt_password(password):
"""密码加密算法
Args:
password: 明文密码字符串
Returns:
32位小写MD5哈希值
"""
return hashlib.md5(password.encode()).hexdigest()
注意事项:虽然MD5已被证明不安全,但在很多传统系统中仍广泛使用。实际项目中建议至少使用SHA-256加盐。
签名是API安全的重要防线,主要防止:
车智赢采用典型的"密钥+参数排序+二次哈希"方案。
通过搜索_sign的赋值位置,最终定位到:
code复制com.che168.autotradercloud.model.LaunchModel
中的签名生成方法:
java复制String sign = SignManager.INSTANCE.signByType(1, paramsMap);
关键点:
完整签名流程:
Python实现:
python复制def generate_sign(params, sign_type=1):
"""生成请求签名
Args:
params: 参数字典
sign_type: 签名类型,1表示登录
Returns:
32位大写MD5签名
"""
secret_keys = {
1: "W@oC!AH_6Ew1f6%8",
# 其他类型的密钥可以在这里扩展
}
if sign_type not in secret_keys:
raise ValueError(f"Unsupported sign type: {sign_type}")
key = secret_keys[sign_type]
# 参数按key排序后拼接
sorted_params = "".join([f"{k}{params[k]}" for k in sorted(params.keys())])
sign_str = key + sorted_params + key
return hashlib.md5(sign_str.encode()).hexdigest().upper()
调试技巧:可以先硬编码一个已知正确的参数组合,对比生成的签名与服务端返回的签名,验证算法正确性。
设备标识是风控系统的重要依据:
车智赢的UDID采用"随机数据+时间戳+3DES加密"的方案。
关键代码位于:
code复制com.che168.autotradercloud.utils.AppUtils
中的getUDID方法:
java复制public static String getUDID(Context context) {
String raw = UUID.randomUUID() + "|" + System.nanoTime() + "|" + getDeviceId(context);
return SecurityUtil.encode3Des(context, raw);
}
3DES加密参数:
Python实现:
python复制from Crypto.Cipher import DES3
from Crypto.Util.Padding import pad
import base64
import uuid
def generate_udid():
"""生成设备UDID
Returns:
Base64编码的3DES加密字符串
"""
# 1. 构造原始字符串
raw_str = f"{uuid.uuid4()}|{int(time.time()*1e9)}|358908"
# 2. 3DES加密
key = b"appapiche168comappapiche168comap"[:24]
iv = b"appapich"
cipher = DES3.new(key, DES3.MODE_CBC, iv)
padded = pad(raw_str.encode(), DES3.block_size)
encrypted = cipher.encrypt(padded)
# 3. Base64编码
return base64.b64encode(encrypted).decode()
注意事项:实际设备ID可能来自IMEI等硬件信息,但在这个案例中服务端似乎没有严格校验,使用固定值也能通过。
将所有参数按接口要求组装:
python复制def build_login_params(username, password):
"""构建登录请求参数
Args:
username: 登录账号
password: 明文密码
Returns:
完整的参数字典
"""
params = {
"userName": username,
"pwd": encrypt_password(password),
"udid": generate_udid(),
"timestamp": str(int(time.time() * 1000)),
# 其他固定参数
"clientType": "android",
"version": "5.6.1"
}
# 生成签名
params["_sign"] = generate_sign(params)
return params
使用requests库发送登录请求:
python复制import requests
def login(username, password):
url = "https://dealercloudapi.che168.com/tradercloud/sealed/login/login.ashx"
params = build_login_params(username, password)
headers = {
"User-Agent": "Dalvik/2.1.0 (Linux; U; Android 10; Pixel 4 Build/QQ3A.200805.001)",
"Content-Type": "application/x-www-form-urlencoded"
}
response = requests.post(url, data=params, headers=headers)
return response.json()
可能原因:
调试方法:
python复制# 打印签名前的原始字符串
print("Sign string:", key + sorted_params + key)
解决方案:
通过这次逆向分析,我们完整还原了车智赢APP的登录协议实现。关键点在于理解每个安全组件的设计意图,并通过代码审计验证算法细节。在实际安全测试中,还需要注意合法合规,所有测试都应在授权范围内进行。