华为仓颉语言逆向初探
字数 1287 2025-08-23 18:31:34
华为仓颉编程语言逆向分析技术文档
1. 仓颉语言概述
华为仓颉编程语言是华为自主研发的通用编程语言,是鸿蒙原生应用的主要编程语言之一,具有以下特性:
- 智能化、全场景和高性能
- 与C和Python的良好兼容性(可外接调用)
- 支持字符串和常量混淆功能
- 内置安全检测机制(如整数溢出、缓冲区溢出等)
2. 基础逆向分析
2.1 Hello World程序分析
示例程序:
// hello.cj
main() {
println("flag{ezcangjie}")
}
编译命令:
cjc hello.cj -o hello.exe
逆向分析关键点:
- 程序入口点为
mainCRTStartup和WinMainCRTStartup - 最终会调用
_tmainCRTStartup初始化环境 - 主要执行流程:
CJ_MRT_CjRuntimeInit()- 运行时初始化CJ_MRT_SetCommandLineArgs()- 设置命令行参数CJ_MRT_CjRuntimeStart(cj_entry_)- 启动运行时
2.2 字符串处理分析
仓颉语言默认使用字符串混淆技术:
- 识别代码中的明文字符串并加密保存
- 程序初始化时解密字符串
- 默认使用单字节异或(0x26)加密
字符串解密函数示例:
__int64 string_decode77193E0F156E() {
unsigned int v0; // ecx
__int64 result; // rax
unsigned int vars4; // [rsp+4h] [rbp+4h]
vars4 = 0;
do {
v0 = vars4;
*((_BYTE *)&_const_cjstring_data_0 + (int)vars4 + 16) ^= 0x26u;
result = ++vars4;
} while (v0 < 0x40);
return result;
}
解密脚本(Python):
data = [0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x06, 0x06, 0x06,
0x69, 0x53, 0x52, 0x06, 0x49, 0x40, 0x06, 0x4B, 0x43, 0x4B, 0x49, 0x54,
0x5F, 0x67, 0x48, 0x06, 0x43, 0x5E, 0x45, 0x43, 0x56, 0x52, 0x4F, 0x49,
0x48, 0x06, 0x4E, 0x47, 0x55, 0x06, 0x49, 0x45, 0x45, 0x53, 0x54, 0x54,
0x43, 0x42, 0x1C, 0x60, 0x67, 0x6D, 0x63, 0x06, 0x60, 0x6D, 0x67, 0x63,
0x45, 0x47, 0x48, 0x41, 0x43, 0x5C, 0x40, 0x4A, 0x47, 0x41, 0x4C, 0x4F,
0x43, 0x00, 0x00, 0x00]
result = ''.join(chr(byte ^ 0x26) for byte in data)
print(result)
3. 去符号化程序分析
3.1 关键函数识别
- 通过
CJ_MRT_CjRuntimeStart调用定位入口函数 - 使用
bindiff工具对比有符号和无符号版本 - 识别关键函数特征:
CJ_MCC_StackGrowStub- 栈增长检查CJ_MCC_Safepoint- 安全点检查CJ_MCC_CheckThreadLocalDataOffset- 线程局部数据检查
3.2 算法还原示例
分析关键算法逻辑:
v18 = 2024LL;
v19 = 1973LL;
v20 = 1LL;
v21 = 1;
while (v20 < 10 || v21 & 1) {
if ((v21 & 1) != 0) {
v21 = 0;
} else {
v20 += 2;
}
if (v20 <= 10) {
v32 = v20 * v20; // 平方计算
v18 += v32; // 累加平方和
}
}
v19 ^= v18; // 最终异或操作
算法逻辑:
- 计算从1到10的所有奇数的平方和
- 初始值2024加上这个平方和
- 将结果与初始值1973进行异或操作
4. 仓颉语言特性总结
4.1 多语言交互
- 支持与C和Python的直接交互
- 类似胶水语言特性,可利用现有生态资源
4.2 编译特性
- 函数量庞大(简单Hello World可能包含1000+函数)
- 支持一键混淆(函数名、变量名混淆)
- 支持OLLVM控制流平坦化
- 自动检测安全隐患(整数溢出等)
4.3 异常处理机制
- 标准try-catch-finally结构
- try-with-resources语法:
- 自动管理非内存资源
- 在try后定义资源变量
- 表达式结束时自动释放资源
- catch和finally块可选
5. 逆向技巧总结
-
字符串处理:
- 默认使用0x26异或加密
- 可通过动态调试在解密后获取字符串
- 或静态分析解密函数还原
-
符号恢复:
- 使用bindiff对比有符号版本
- 通过关键函数特征识别(如CJ_MCC_*系列函数)
-
依赖库:
- 常用库:libcangjie-runtime和libsecurec
- 需确保分析环境包含必要DLL
-
控制流分析:
- 注意OLLVM混淆可能性
- 关注异常处理流程
-
安全检测:
- 编译器会自动插入安全检查代码
- 如整数溢出检查等
附录:关键函数签名
-
运行时初始化:
void CJ_MRT_CjRuntimeInit(); -
运行时启动:
int CJ_MRT_CjRuntimeStart(void(*entry_point)()); -
栈检查:
void CJ_MCC_StackGrowStub(); -
安全点检查:
void CJ_MCC_Safepoint(); -
异常抛出:
void CJ_MCC_ThrowException(void* exception);