文章总结: 本文详细记录了对某企业通讯APP的完整逆向分析过程,涉及国密算法、融云SDK和Frida动态调试等技术。关键发现包括应用采用三层架构、HTTP通信使用SM2+SM4国密加密、本地存储采用AES加密,并存在硬编码密钥的安全隐患。文章提供了多层Hook策略、主动序列化等实战技巧,并给出避免硬编码密钥、加强代码混淆等安全建议。 综合评分: 85 文章分类: 移动安全,逆向分析,加密协议,安全工具,Android安全
安卓逆向 — 一款企业IM的加密协议逆向全过程
原创
yushao yushao
逆向有你
2026年5月21日 10:57 河南
在小说阅读器读本章
去阅读
#
本文记录了对某企业通讯APP的完整逆向分析过程,涉及国密算法、融云SDK、Frida动态调试等技术。仅供技术学习交流,请勿用于非法用途。
一、起因
最近拿到一款企业内部通讯APP,基于融云SDK二次开发,使用了国密加密体系。出于技术研究目的,我决定深入分析其通讯协议和加密实现。
二、整体架构分析
2.1 技术栈识别
通过APK反编译,发现该应用采用了三层架构:
┌─────────────────────────────────────┐
│ 业务层(自定义消息协议) │
├─────────────────────────────────────┤
│ 融云 IM SDK (第三方即时通讯) │
│ ├─ Java API 层 │
│ └─ Native 层 (libRongIMLib.so) │
├─────────────────────────────────────┤
│ 加密层(SM2 + SM4 国密算法) │
└─────────────────────────────────────┘
关键发现:
- • 主要代码使用 Java/Kotlin 编写
- • 核心通讯基于融云SDK的TCP长连接
- • HTTP接口采用国密SM2+SM4混合加密
- • 本地存储使用AES加密
2.2 网络通讯方式
该应用使用了两种通讯方式:
- 1. TCP长连接:用于即时消息收发(融云SDK实现)
- 2. HTTPS接口:用于业务API调用(国密加密)
三、加密体系解密
3.1 HTTP通信加密(国密体系)
硬编码的SM2公钥
在 ParamsBuilder.java 中发现硬编码的SM2公钥:
public static final String SM2_PUBLIC_KEY =
"fdsafsdfsdafsdfasdfasdfasdfasdfasdfasdfadsasdfdas."
加密流程
- 1. 生成32字节随机密钥
- 2. 使用SM2公钥加密随机密钥
- 3. 使用SM4/ECB模式加密消息体
- 4. HTTP Header 标记:
hdc-encryption-enabled: true
关键代码片段:
// SM4加密实现
Cipher cipher = Cipher.getInstance("SM4/ECB/PKCS5Padding",
BouncyCastleProvider.INSTANCE);
cipher.init(Cipher.ENCRYPT_MODE,
new SecretKeySpec(keyBytes, "SM4"));
byte[] encrypted = cipher.doFinal(plainText.getBytes());
3.2 本地存储加密(AES)
敏感数据(Token、密码等)使用AES加密存储:
- • 算法:
AES/ECB/PKCS5Padding - • 密钥处理:原始key补齐到32字节(不足部分填充”0″)
- • 编码方式:Base64 (NO_WRAP)
3.3 特殊发现:硬编码SM4密钥
在 CacheTask.java 中发现一个硬编码的SM4密钥:
private static final String SECRET_KEY =
"64536456345634565434565463";
用于特定字段的SM4解密。
四、消息协议分析
4.1 消息类型体系
所有自定义消息通过 @MessageTag 注解注册:
| 消息类型 | Tag标识 | 用途 | | — | — | — | | TextMessage | RC:TxtMsg | 文本消息 | | ApprovalMessage | RCE:ApprovalMsg | 审批消息 | | SecretChatPromptMessage | RCE:SCPM | 密聊提示 | | GroupCommandMessage | RCE:GrpCmd | 群指令 | | PinMessage | RCE:PinMsg | 公告消息 |
4.2 消息JSON结构
基础消息体包含以下字段:
{
"mentionedInfo":{
"type":1,
"userIdList":[],
"mentionedContent":"..."
},
"user":{
"id":"...",
"name":"...",
"portrait":"..."
},
"extra":"...",
"isBurnAfterRead":false,
"audit":{
"auditType":1,
"project":"..."
}
}
4.3 端到端加密会话
融云SDK支持端到端加密,通过 RCEncryptedSession 维护:
class RCEncryptedSession {
String encKey; // 加密密钥
String encXA; // 密钥交换参数
String remoteEncId; // 对端会话标识
int encStatus; // 状态码
}
状态码含义:
- • -1: NOT_FOUND
- • 1: REQUEST
- • 2: RESPONSE
- • 3: ENCRYPTED
- • 4: CANCELED
五、动态调试实战
5.1 初次尝试与问题
最初编写的Frida脚本只能Hook到部分信息:
// 只能看到这些
[RongIMClient.sendMessage]
objectName=null
convType=PRIVATE
targetId=10007585
问题分析:
- •
msg.getObjectName()返回null - • Native方法签名不匹配导致Hook失败
- • 消息内容在传递到Native层前就被序列化
5.2 解决方案
关键突破点在于:在Java层直接调用 encode() 方法主动序列化
// 修改后的Hook代码
RongIMClientImpl.sendMessage.implementation = function() {
var msg = arguments[0];
var mc = msg.getContent(); // 获取MessageContent对象
if (mc) {
var encoded = mc.encode(); // 主动序列化
console.log("明文内容:");
console.log(bytesToString(encoded)); // 输出明文JSON
}
return this.sendMessage.apply(this, arguments);
};
5.3 多层防御策略
最终采用了多点拦截策略:
- 1. API入口层:
RongIMClient.sendMessage() - 2. 序列化层:各消息类型的
encode()方法 - 3. 持久化层:
NativeObject.SaveMessage() - 4. Native入口层:
sendMessageWithOption()
这样即使某一层Hook失败,其他层也能捕获到明文。
5.4 最终效果
运行脚本后的输出示例:
══ SEND conv=PRIVATE target=10007585 [10:23:45] ═════
Class: io.rong.message.TextMessage
[156B] 明文 JSON:
{
"content": "测试消息",
"user": {
"id": "user123",
"name": "张三"
},
"extra": ""
}
[HEX] 7b 22 63 6f 6e 74 65 6e 74 22 3a 22 ...
六、关键技术要点总结
6.1 加密向量汇总
| 参数 | 值 | 用途 | | — | — | — | | SM2公钥 | 04c945cb7cc… | HTTP密钥交换 | | SM4密钥 | bb218d79ddd… | 特定字段解密 | | AES模式 | ECB/PKCS5 | 本地存储 | | API地址 | https://域名:173/rce-api | 业务接口 |
6.2 Frida Hook技巧
1. 处理方法重载
var overloads = Class.method.overloads;
for (var i = 0; i < overloads.length; i++) {
(function(idx) {
overloads[idx].implementation = function() {
// Hook逻辑
return overloads[idx].apply(this, arguments);
};
})(i);
}
2. 主动调用对象方法
var obj = arguments[0];
var result = obj.someMethod(); // 直接调用Java对象方法
3. 字节数组转字符串
function bytesToString(bytes) {
return Java.use("java.lang.String")
.$new(bytes, "UTF-8")
.toString();
}
6.3 常见坑点
❌ 错误做法:
// 直接Hook native方法容易失败
NativeObject.sendMessageWithOption.implementation = ...
✅ 正确做法:
// 在Java层拦截并主动序列化
var mc = msg.getContent();
var plaintext = mc.encode();
七、安全建议
作为开发者,从这次逆向分析中可以得到以下安全启示:
7.1 不要硬编码密钥
// ❌ 危险
private static final String KEY = "bb218d79ddd211cf...";
// ✅ 安全
// 使用密钥派生函数(KDF)从用户密码/设备特征动态生成
7.2 加强代码混淆
- • 使用ProGuard/R8进行深度混淆
- • 对关键类名、方法名进行字符串加密
- • 使用Native层实现核心加密逻辑
7.3 实施多层防护
- • Root/越狱检测
- • Frida检测(检查端口、进程、特征文件)
- • 完整性校验(签名验证、DEX校验)
- • 运行时环境检测
八、技术延伸
8.1 如何防御类似逆向
- 1. 使用VMP/Ollvm等加固方案
- 2. 关键逻辑下沉到Native层
- 3. 实施SSL Pinning防止中间人攻击
- 4. 动态密钥协商(避免硬编码)
- 5. 代码自校验(检测Hook痕迹)
8.2 进一步研究方向
- • 融云SDK的Native层协议分析
- • TLS层的流量解密
- • 端到端加密的密钥交换机制
- • 消息存储数据库的加密实现
九、总结
通过本次逆向分析,我们完整梳理了一款企业IM应用的:
✅ 整体架构设计 ✅ 国密加密实现 ✅ 消息协议结构 ✅ 动态调试方法
核心收获:
- 1. 硬编码密钥是重大安全隐患
- 2. Java层的加密在Frida面前形同虚设
- 3. 多层Hook策略能有效应对各种防护
- 4. 国密算法的正确实施同样重要
免责声明
本文所有内容仅供技术研究和学习交流使用,请勿将相关技术用于非法用途。对他人系统进行未授权的渗透测试属于违法行为,后果自负。
关键词: #逆向工程 #移动安全 #Frida #国密算法 #Android安全 #即时通讯 #加密协议
| | |
| — | — |
| |
|
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:逆向有你 yushao yushao《安卓逆向 — 一款企业IM的加密协议逆向全过程》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。








评论