1. 项目背景与目标解析
"车智赢APP登录协议逆向分析"这个标题直接指向了移动应用安全领域的核心课题——协议逆向工程。作为一名长期从事移动安全研究的工程师,我经常需要分析各类APP的通信协议,而登录环节往往是整个安全体系中最关键的突破口。
这次分析的车智赢APP是一款主流的车载智能控制应用,用户通过它能够远程查看车辆状态、控制车门锁、启动引擎等功能。这类涉及硬件控制的APP,其安全设计通常比普通社交或工具类应用更为严格。登录协议作为第一道安全防线,往往会采用各种加密算法和混淆手段来防止恶意攻击。
我们的核心目标是完整还原其登录过程中的关键算法实现,包括但不限于:
- 登录请求的数据包结构
- 参数加密/签名算法
- 密钥生成与管理机制
- 防重放攻击措施
- 会话维持方案
2. 分析环境与工具准备
2.1 基础环境搭建
工欲善其事,必先利其器。在进行逆向分析前,我们需要搭建一个稳定的分析环境:
-
测试设备:建议使用root过的Android真机(如Pixel 3)或x86架构的模拟器(Android Studio自带的模拟器对ARM转译支持不佳)
-
抓包工具:
- Charles/Fiddler:用于HTTPS流量拦截
- Wireshark:辅助分析底层协议
- Burp Suite:高级请求重放与修改
-
逆向工具链:
- Jadx/Ghidra:APK反编译
- Frida:动态代码注入
- IDA Pro:原生库分析
- Objection:运行时对象探查
重要提示:测试设备必须与日常使用设备物理隔离,避免因分析操作导致主用账号被封禁。
2.2 目标APP处理
获取待分析的APP包体有多种途径:
bash复制# 从设备中提取已安装APK
adb shell pm path com.example.chezhiying
adb pull /data/app/~~1wQ3Zx==/com.example.chezhiying-123/base.apk
# 使用第三方市场下载历史版本
python3 apkeep.py -a com.example.chezhiying -d
建议优先分析v3.2.1版本(2023年Q2发布),这个版本在安全机制上有代表性且尚未引入最新的混淆方案。
3. 协议逆向方法论
3.1 静态分析切入点
首先通过jadx进行基础反编译:
java复制// 定位登录Activity的典型方法
grep -r "extends Activity" ./smali/
grep -r "login" ./res/values/strings.xml
关键发现:
- 登录入口类:
com.chezhiying.auth.LoginActivity - 核心网络请求:
com.chezhiying.network.ApiClient - 加密工具类:
com.chezhiying.security.CryptoUtils
3.2 动态调试技巧
静态分析往往不足以还原完整算法,需要结合动态调试:
python复制# Frida脚本示例:拦截加密函数
Interceptor.attach(Module.findExportByName("libnative.so", "encrypt"), {
onEnter: function(args) {
console.log("Input buffer:", hexdump(args[0]));
console.log("Key:", args[1].toInt32());
},
onLeave: function(retval) {
console.log("Output:", hexdump(retval));
}
});
常见问题排查:
- 如果遇到反调试检测,可以hook
ptrace调用 - 针对证书固定(SSL Pinning),使用Objection的
android sslpinning disable命令 - 对于native层混淆,需要关注
JNI_OnLoad的初始化逻辑
4. 核心算法还原
4.1 登录请求结构解析
通过抓包分析,典型登录请求如下:
json复制POST /v3/auth/login HTTP/1.1
{
"username": "encrypted_base64_data",
"password": "a7b2c4...",
"device_id": "ANDROID_123456",
"timestamp": 1689234567,
"signature": "sha256=9a3f8b..."
}
关键字段说明:
| 字段名 | 加密方式 | 生成规则 |
|---|---|---|
| username | AES-256-CBC | 手机号经过PKCS7填充后加密 |
| password | RSA-OAEP | 使用服务器公钥加密 |
| signature | HMAC-SHA256 | 对请求体+时间戳+设备特征拼接后计算 |
4.2 密钥管理机制
逆向发现密钥采用分层管理方案:
- 静态密钥:硬编码在
res/raw/keys.bin中,用于初始握手 - 动态密钥:通过
/v3/auth/init接口下发,有效期为24小时 - 会话密钥:登录成功后生成,用于后续API通信
密钥更新流程伪代码:
python复制def update_keys(init_resp):
encrypted_key = base64decode(init_resp['key'])
iv = init_resp['iv']
# 使用设备唯一标识作为解密因子
device_hash = sha256(get_android_id())
decrypted = aes_decrypt(encrypted_key, device_hash[:16], iv)
current_app_key = json.loads(decrypted)['app_key']
4.3 签名算法实现
签名算法是防止请求篡改的关键,逆向还原后的Python实现:
python复制import hmac
import hashlib
def generate_signature(params, timestamp, device_id):
secret = get_current_secret() # 从本地存储获取当前密钥
sorted_params = '&'.join([f'{k}={v}' for k,v in sorted(params.items())])
raw_string = f"{sorted_params}&t={timestamp}&d={device_id}"
return hmac.new(secret.encode(), raw_string.encode(), hashlib.sha256).hexdigest()
关键细节:
- 参数需要按字母序排序
- 时间戳精确到秒级
- 设备ID需要与注册时一致
5. 安全防护与对抗
5.1 反逆向措施分析
车智赢APP采用了多种防护手段:
- 代码混淆:使用ProGuard+自定义字典混淆
- 原生保护:关键算法放在native层,使用OLLVM混淆
- 环境检测:检查root、模拟器、调试状态
- 行为验证:异常请求触发短信验证
绕过方案示例:
javascript复制// 绕过root检测
Java.perform(function() {
var Secure = Java.use('android.os.Secure');
Secure.getString.overload('android.content.ContentResolver', 'java.lang.String')
.implementation = function(cr, key) {
if (key === 'android_id') return 'original_device_id';
return this.getString(cr, key);
};
});
5.2 协议安全评估
根据分析结果,该登录协议的安全等级评估:
| 安全维度 | 实现方案 | 强度评估 |
|---|---|---|
| 传输安全 | TLS 1.2 + 证书固定 | ★★★★★ |
| 请求防篡改 | HMAC-SHA256签名 | ★★★★☆ |
| 敏感数据保护 | 分层加密方案 | ★★★★ |
| 防重放攻击 | 时间戳窗口验证 | ★★★☆ |
| 密钥管理 | 动态轮换机制 | ★★★★ |
主要风险点:
- 初始密钥硬编码问题
- 时间戳验证允许±5分钟窗口
- 部分旧版本存在RSA弱填充问题
6. 完整算法还原实例
结合静态和动态分析,最终还原的登录流程Python实现:
python复制import requests
from Crypto.Cipher import AES, PKCS1_OAEP
from Crypto.PublicKey import RSA
import base64
import time
class CheZhiYingLogin:
def __init__(self):
self.base_url = "https://api.chezhiying.com/v3"
self.device_id = "ANDROID_" + "1234567890abcdef"
self.static_key = bytes.fromhex("a1b2c3d4...") # 从APK中提取
def encrypt_username(self, phone):
iv = os.urandom(16)
cipher = AES.new(self.static_key, AES.MODE_CBC, iv)
padded = phone + (16 - len(phone) % 16) * chr(16 - len(phone) % 16)
encrypted = cipher.encrypt(padded.encode())
return base64.b64encode(iv + encrypted).decode()
def init_session(self):
resp = requests.get(f"{self.base_url}/auth/init",
headers={"X-Device-ID": self.device_id})
return self._process_init_response(resp.json())
def login(self, username, password):
timestamp = int(time.time())
init_data = self.init_session()
rsa_key = RSA.import_key(init_data['server_pubkey'])
cipher = PKCS1_OAEP.new(rsa_key)
enc_password = base64.b64encode(cipher.encrypt(password.encode())).decode()
params = {
"username": self.encrypt_username(username),
"password": enc_password,
"device_id": self.device_id,
"timestamp": timestamp
}
params['signature'] = self._generate_signature(params, timestamp)
response = requests.post(f"{self.base_url}/auth/login", json=params)
return self._process_login_response(response.json())
关键调试技巧:
- 使用
stetho等工具实时查看APP内网络请求 - 对native层算法,通过
frida-trace追踪openssl调用 - 遇到加密乱码时,检查是否缺少PKCS7填充
7. 经验总结与延伸思考
在实际分析过程中,有几个值得记录的发现:
- 版本差异问题:v3.2.1与v3.3.0的密钥派生算法不同,新版本增加了HKDF步骤
- 设备指纹生成:除了常规的ANDROID_ID,还融合了Build.PROP中的多个字段
- 异常处理机制:连续5次签名错误会触发账号临时锁定
- 降级攻击防护:服务器会拒绝使用旧版本协议的请求
对于想进一步研究的朋友,建议关注:
- 如何自动化检测协议变更
- 使用unicorn引擎模拟执行加密函数
- 结合符号执行技术提高分析效率
这个案例展示了现代移动应用典型的安全设计思路,其中的分层加密、动态密钥等方案值得其他开发者参考借鉴。当然,从安全研究的角度看,任何防护方案都存在改进空间,关键在于建立持续的安全迭代机制。