华为仓颉语言逆向初探
字数 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

逆向分析关键点:

  1. 程序入口点为mainCRTStartupWinMainCRTStartup
  2. 最终会调用_tmainCRTStartup初始化环境
  3. 主要执行流程:
    • 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 关键函数识别

  1. 通过CJ_MRT_CjRuntimeStart调用定位入口函数
  2. 使用bindiff工具对比有符号和无符号版本
  3. 识别关键函数特征:
    • 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. 计算从1到10的所有奇数的平方和
  2. 初始值2024加上这个平方和
  3. 将结果与初始值1973进行异或操作

4. 仓颉语言特性总结

4.1 多语言交互

  • 支持与C和Python的直接交互
  • 类似胶水语言特性,可利用现有生态资源

4.2 编译特性

  • 函数量庞大(简单Hello World可能包含1000+函数)
  • 支持一键混淆(函数名、变量名混淆)
  • 支持OLLVM控制流平坦化
  • 自动检测安全隐患(整数溢出等)

4.3 异常处理机制

  1. 标准try-catch-finally结构
  2. try-with-resources语法:
    • 自动管理非内存资源
    • 在try后定义资源变量
    • 表达式结束时自动释放资源
    • catch和finally块可选

5. 逆向技巧总结

  1. 字符串处理

    • 默认使用0x26异或加密
    • 可通过动态调试在解密后获取字符串
    • 或静态分析解密函数还原
  2. 符号恢复

    • 使用bindiff对比有符号版本
    • 通过关键函数特征识别(如CJ_MCC_*系列函数)
  3. 依赖库

    • 常用库:libcangjie-runtime和libsecurec
    • 需确保分析环境包含必要DLL
  4. 控制流分析

    • 注意OLLVM混淆可能性
    • 关注异常处理流程
  5. 安全检测

    • 编译器会自动插入安全检查代码
    • 如整数溢出检查等

附录:关键函数签名

  1. 运行时初始化:

    void CJ_MRT_CjRuntimeInit();
    
  2. 运行时启动:

    int CJ_MRT_CjRuntimeStart(void(*entry_point)());
    
  3. 栈检查:

    void CJ_MCC_StackGrowStub();
    
  4. 安全点检查:

    void CJ_MCC_Safepoint();
    
  5. 异常抛出:

    void CJ_MCC_ThrowException(void* exception);
    
华为仓颉编程语言逆向分析技术文档 1. 仓颉语言概述 华为仓颉编程语言是华为自主研发的通用编程语言,是鸿蒙原生应用的主要编程语言之一,具有以下特性: 智能化、全场景和高性能 与C和Python的良好兼容性(可外接调用) 支持字符串和常量混淆功能 内置安全检测机制(如整数溢出、缓冲区溢出等) 2. 基础逆向分析 2.1 Hello World程序分析 示例程序: 编译命令: 逆向分析关键点: 程序入口点为 mainCRTStartup 和 WinMainCRTStartup 最终会调用 _tmainCRTStartup 初始化环境 主要执行流程: CJ_MRT_CjRuntimeInit() - 运行时初始化 CJ_MRT_SetCommandLineArgs() - 设置命令行参数 CJ_MRT_CjRuntimeStart(cj_entry_) - 启动运行时 2.2 字符串处理分析 仓颉语言默认使用字符串混淆技术: 识别代码中的明文字符串并加密保存 程序初始化时解密字符串 默认使用单字节异或(0x26)加密 字符串解密函数示例: 解密脚本(Python): 3. 去符号化程序分析 3.1 关键函数识别 通过 CJ_MRT_CjRuntimeStart 调用定位入口函数 使用 bindiff 工具对比有符号和无符号版本 识别关键函数特征: CJ_MCC_StackGrowStub - 栈增长检查 CJ_MCC_Safepoint - 安全点检查 CJ_MCC_CheckThreadLocalDataOffset - 线程局部数据检查 3.2 算法还原示例 分析关键算法逻辑: 算法逻辑: 计算从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混淆可能性 关注异常处理流程 安全检测 : 编译器会自动插入安全检查代码 如整数溢出检查等 附录:关键函数签名 运行时初始化: 运行时启动: 栈检查: 安全点检查: 异常抛出: