目录

异常堆栈解析原理

构建产物介绍

[h2]ArkTS调试产物sourceMap

release模式编译产物,产物位置:{ProjectPath}/{ModuleName}/build/{product}/cache/default/default@CompileArkTS/esmodule/release/sourceMaps.map

[h2]C++调试产物debug so

带调试信息的so数据,产物位置:{ProjectPath}/{ModuleName}/build/{product}/intermediates/libs

配置方式请参考release编译带调试信息的so。

[h2]代码混淆产物nameCache

反混淆映射表,release模式编译产物,产物位置:{ProjectPath}/{ModuleName}/build/{product}/cache/default/default@CompileArkTS/esmodule/release/obfuscation

C++堆栈解析原理

[h2]编译选项差异

[h2]release编译带调试信息的so

通常release的so中的符号表、调试信息会被移除。

若需要保留so文件中的符号表、调试信息,需要在build-profile.json5的buildOption/externalNativeOptions中配置参数:"arguments": "-DCMAKE_BUILD_TYPE=RelWithDebInfo"。

{
  "apiType": "stageMode",
  "buildOption": {
    "externalNativeOptions": {
      "path": "./src/main/cpp/CMakeLists.txt",
      "arguments": "-DCMAKE_BUILD_TYPE=RelWithDebInfo",
      "cppFlags": "",
    }
  },
}

编译后会生成2份so产物:

[h2]C++堆栈解析流程

llvm-addr2line(在DevEco Studio安装目录的sdk/default/openharmony/native/llvm/bin目录下)是将函数地址解析成文件名或行号的工具。

给出一个可执行文件中的地址或一个可重定位对象中的偏移部分的地址,使用调试信息来找出与之相关的文件名和行号。

常用参数:

参数

用途

-a

以十六进制形式显示地址

-C

将符号名解码为用户级别的名字

-e

设置需要转换地址的可执行文件名

-f

显示文件名、行号和函数名信息

-F

显示函数名及文件行号

-j

读取指定部分的偏移量,而不是绝对地址

-p

每个地址信息单独占一行

参考示例:

查看文件名、行号和函数名相关信息:

llvm-addr2line -f -e File.so

查找指定的地址所对应的代码位置:

llvm-addr2line 0x00000000004005e7 -e test -f -C -s

例如:

llvm-addr2line -e libapplication.so 00003714 -f -C

ASan堆栈解析:

ArkTS堆栈解析原理

[h2]sourceMap格式

图1 源码
图2 编译后产物

实际代码行映射关系:

70->29

71->30

72->31

73->32

sourceMap结构:

单个module构建产物sourceMaps.map为merge文件,实际包含该模块的所有文件的映射关系;每个json中key以编译构建产物的唯一路径作为主键,运行程序的abc中保留了对应的key信息,当运行时异常代码归属到该文件时输出信息为该key,sources为实际源码文件信息,用于异常堆栈还原源码;mappings为编码后的行列号映射表,每个文件有独立的映射关系。

[h2]sourceMap解析流程

下图是sourceMap中每个json的key的结构化处理过程,以“entry|har1|1.0.0|src/main/ets/pages/w.ts”为例,各字段含义如下。

以“|”为分隔符,entry是本模块oh-package.json5中的name,har1|1.0.0是依赖的har1包的oh-package.json5中的name和version(如果没有依赖包,则是本模块oh-package.json5中的name和version),src/main/ets/pages/w.ts是引用的源码文件路径。

图3 sourceMap中的key结构化处理

反混淆解析原理

代码混淆配置请参考代码混淆。

[h2]代码混淆产物介绍

混淆映射表:$ProjectPath\$ModuleName\build\$product\cache\default\default@CompileArkTS\esmodule\release\obfuscation\nameCache.json

{
  "home/src/main/ets/homeability/HomeAbility.ets": {
    "IdentifierCache": {
      "#AbilityConstant": "AbilityConstant",
      "#hilog": "hilog",
      "#UIAbility": "UIAbility",
      "#Want": "Want",
      "#window": "window",
      "HomeAbility#onWindowStageCreate#__function": "i"
    },
    "MemberMethodCache": {
      "onCreate:10:16": "onCreate",
      "onDestroy:18:20": "onDestroy",
      "onWindowStageCreate:22:33": "onWindowStageCreate",
      "onWindowStageDestroy:35:38": "onWindowStageDestroy",
      "onForeground:40:43": "onForeground",
      "onBackground:45:48": "onBackground"
    },
    "obfName": "home/src/main/ets/homeability/HomeAbility.ets"
  },
  "compileSdkVersion": "5.0.0.25",
  "entryPackageInfo": "home|1.0.0",
  "PropertyCache": {
    "integratedHsp": "i",
    "asanClick": "j",
    "Index_Params": "m",
    "testNapi": "o",
    "Index": "t",
    "testObfuscation": "g2"
  }
}

[h2]代码混淆解析

异常堆栈如下:

Pid:58348
Uid:20020156
Reason:RangeError
Error name:RangeError
Error message:The number cannot be converted to a BigInt because it is not an integer
Stacktrace:
Cannot get SourceMap info, dump raw stack:
    at g2 (home|home|1.0.0|src/main/ets/pages/a.ts:6:6)
    at getVersion (home|home|1.0.0|src/main/ets/pages/a.ts:2:2)
    at anonymous (home|home|1.0.0|src/main/ets/pages/Index.ts:61:61)

  1. 经过sourceMap映射转码堆栈如下:
    at g2 (home/src/main/ets/pages/tool.ts:7:27)
    at getVersion (home/src/main/ets/pages/tool.ts:2:30)
    at anonymous (home/src/main/ets/pages/Index.ets:23:40)

    a.ts通过sourceMap还原为tool.ts。

    "home|home|1.0.0|src/main/ets/pages/a.ts": {
        "version": 3,
        "file": "tool.ts",
        "sources": [
          "home/src/main/ets/pages/tool.ts"
        ],
        "names": [],
        "mappings": "AAAA,MAAM,CAAC,OAAO,UAAU,UAAU,IAAI,MAAM;IAC1C,IAAI,KAAM,IAAiB,CAAA;IAC3B,UAAW;AACb,CAAC;AAED,eAA2B,MAAM;IAC/B,IAAI,GAAG,GAAG,MAAM,CAAC,MAAM,CAAC,CAAA;IACxB,OAAO,GAAG,CAAC;AACb,CAAC",
        "sourceRoot": "",
        "entry-package-info": "home|1.0.0"
      }
  2. 函数级文件名映射。

    查看混淆映射表:$ProjectPath\$ModuleName\build\$product\cache\default\default@CompileArkTS\esmodule\release\obfuscation\nameCache.json

    "home/src/main/ets/pages/tool.ts": {
        "IdentifierCache": {
          "getVersion#res": "h2",
          "#testObfuscation:6:9": "g2"
        },
        "MemberMethodCache": {},
        "obfName": "home/src/main/ets/pages/a.ts"
      }

    该字段的IdentifierCache与MemberMethodCache中保存了方法名混淆前后的映射关系,对应格式为:"源码方法名:该方法起始行号:该方法结束行号":"混淆后方法名"。

    源码方法名中的"源码方法名"代表上下级关系,故匹配后可以通过"#"保留最后名称。

    第一条堆栈混淆后的方法名为"g2",若存在多个"g2"则需要通过行号范围过滤,故利用上述字段对该方法名进行还原:

    1. 通过key(home/src/main/ets/pages/tool.ts)查找到映射表。
    2. 在上述字段中找出所有混淆后方法名为"g2"的条目,该条目为:
      "#testObfuscation:6:9": "g2"
    3. 找到行号范围包含步骤一中还原后行号的条目,步骤一中得到的行号为7包含在6-9之内,因此可以得到源码对应方法名为"#testObfuscation",经过字符串处理结果为"testObfuscation"。
      at testObfuscation (home/src/main/ets/pages/tool.ts:7:27)
      at getVersion (home/src/main/ets/pages/tool.ts:2:30)
      at anonymous (home/src/main/ets/pages/Index.ets:23:40)