You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android R8混淆后,如何基于mapping.txt自动修改C++代码?

解决方案:基于R8 mapping.txt自动适配C++代码的编译流程

针对你遇到的两个核心问题,以下是具体的实现方案,完全基于Gradle和自定义脚本完成,无需永久修改源码:


一、仅编译时临时修改C++代码,不改动原项目文件

核心思路是复制原始C++代码到临时目录,在临时目录中完成符号替换,再让CMake指向该临时目录编译,原始源码目录保持完全不变。

具体步骤:

  1. 在App模块的build.gradle中定义Gradle任务,负责创建临时目录、复制源码、执行符号替换脚本:

    android {
        // ...其他配置
        buildTypes {
            release {
                minifyEnabled true
                proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
                // 让CMake指向临时生成的源码目录
                externalNativeBuild {
                    cmake {
                        path file('build/tmp/native-obfuscated/CMakeLists.txt')
                    }
                }
            }
        }
    
        // 生成混淆后临时原生代码的任务
        task generateObfuscatedNativeSources(type: Copy) {
            def originalSrcDir = file('src/main/cpp')
            def tempSrcDir = file('build/tmp/native-obfuscated')
    
            // 清空并复制原始C++代码到临时目录
            delete tempSrcDir
            from originalSrcDir
            into tempSrcDir
    
            // 复制完成后执行符号替换脚本
            doLast {
                def mappingFile = file("build/outputs/mapping/release/mapping.txt")
                def replaceScript = file("scripts/replace_native_symbols.py")
                exec {
                    commandLine "python", replaceScript.absolutePath, tempSrcDir.absolutePath, mappingFile.absolutePath
                }
            }
        }
    }
    
  2. 编写符号替换脚本(以Python为例),解析mapping.txt并替换临时目录中C++代码的类名、字段名:

    import os
    import re
    
    def parse_mapping(mapping_path):
        class_map = {}
        field_map = {}
        current_class = None
        with open(mapping_path, 'r') as f:
            for line in f:
                line = line.strip()
                # 匹配类映射:com/example/MyClass -> a:
                class_match = re.match(r'^([\w\/]+) -> (\w+):$', line)
                if class_match:
                    original_class = class_match.group(1).replace('/', '.')
                    obfuscated_class = class_match.group(2)
                    class_map[original_class] = obfuscated_class
                    current_class = original_class
                    continue
                # 匹配字段映射:    myField: Ljava/lang/String; -> a
                field_match = re.match(r'^\s+([\w]+): .+ -> (\w+)$', line)
                if field_match and current_class:
                    original_field = field_match.group(1)
                    obfuscated_field = field_match.group(2)
                    field_map[f"{current_class}.{original_field}"] = obfuscated_field
        return class_map, field_map
    
    def replace_symbols(file_path, class_map, field_map):
        with open(file_path, 'r') as f:
            content = f.read()
        # 替换JNI中的类名引用(如FindClass("com.example.MyClass"))
        for original, obfuscated in class_map.items():
            content = content.replace(f'"{original}"', f'"{obfuscated}"')
        # 替换JNI中的字段名引用(如GetFieldID(clazz, "myField", "..."))
        for original_full, obfuscated in field_map.items():
            _, original_field = original_full.split('.', 1)
            content = re.sub(r'GetFieldID\([^,]+, "' + re.escape(original_field) + r'",', 
                            f'GetFieldID(\\1, "{obfuscated}",', content)
        with open(file_path, 'w') as f:
            f.write(content)
    
    def main(src_dir, mapping_path):
        class_map, field_map = parse_mapping(mapping_path)
        for root, _, files in os.walk(src_dir):
            for file in files:
                if file.endswith(('.cpp', '.h')):
                    replace_symbols(os.path.join(root, file), class_map, field_map)
    
    if __name__ == '__main__':
        import sys
        if len(sys.argv) != 3:
            print("Usage: python replace_native_symbols.py <临时源码目录> <mapping.txt路径>")
            sys.exit(1)
        main(sys.argv[1], sys.argv[2])
    

二、调整编译顺序,确保mapping.txt生成后再编译C++代码

默认情况下,原生代码编译任务早于R8混淆任务,需要通过Gradle任务依赖强制调整顺序:

在App模块的build.gradle中添加任务依赖配置:

android {
    // ...其他配置
    afterEvaluate {
        // 获取关键任务实例
        def minifyTask = tasks.findByName('minifyReleaseWithR8')
        def generateNativeTask = tasks.findByName('generateObfuscatedNativeSources')
        def nativeBuildTask = tasks.findByName('externalNativeBuildRelease')

        if (minifyTask && generateNativeTask && nativeBuildTask) {
            // 强制顺序:R8混淆生成mapping → 生成临时C++代码 → 编译原生代码
            generateNativeTask.dependsOn minifyTask
            nativeBuildTask.dependsOn generateNativeTask
            // 打破原生编译默认先于Kotlin编译的顺序
            minifyTask.mustRunAfter tasks.findByName('compileReleaseKotlin')
        }
    }
}

关键说明:

  • minifyReleaseWithR8任务完成后,mapping.txt才会生成在build/outputs/mapping/release/目录下
  • 通过dependsOn和mustRunAfter强制任务执行顺序,确保脚本能拿到最新的混淆映射关系
  • Debug模式若不需要混淆,可单独配置CMake指向原始源码目录,避免额外开销

内容的提问来源于stack exchange,提问作者Paul-Etienne

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 08:22:37