如何借助Proguard混淆映射,在运行时获取Android方法混淆名?
Android方法混淆映射与反射调用方案
一、能不能直接在运行时拿到混淆后的方法名?
直接做不到。Proguard/R8混淆后,原始方法名myMethod会被替换成无意义的短名称(比如a),但你代码里硬写的"myMethod"字符串不会自动同步成混淆后的值,而且Android系统没有内置API能在运行时从原始方法名反向查到混淆后的名称——除非你提前把映射关系打包进应用。
二、用Gradle任务合并映射文件的可行方案
完全可以实现,核心思路是把Proguard生成的映射文件里的关键信息打包进APK,运行时读取映射关系再反射调用,步骤如下:
- 获取Proguard映射文件:每次混淆构建后,会在
app/build/outputs/mapping/{buildType}/mapping.txt生成映射文件,里面记录了所有原始类、方法、字段和混淆后名称的对应关系。 - 编写Gradle任务:
- 读取
mapping.txt,解析出目标方法myMethod的混淆后名称。 - 把映射关系生成JSON或其他格式的文件,放到
assets目录打包进APK。
- 读取
- 运行时读取映射并反射调用:启动时读取assets里的映射文件,根据原始方法名拿到混淆后的名称,再执行反射。
示例Gradle任务(Groovy)
task generateMethodMapping { doLast { def mappingFile = file("${project.buildDir}/outputs/mapping/release/mapping.txt") if (!mappingFile.exists()) return def methodMap = [:] mappingFile.eachLine { line -> // 匹配目标方法的映射行,格式类似:com.example.YourClass.myMethod()V -> a.a():V def matcher = line =~ /(\w+\.\w+\.myMethod\(\)V) -> (\w+\.\w+\(\)V)/ if (matcher.matches()) { def originalMethod = matcher[0][1].split("\\.")[-1].replace("()V", "") def obfuscatedMethod = matcher[0][2].split("\\.")[-1].replace("()V", "") methodMap[originalMethod] = obfuscatedMethod } } // 生成JSON到assets目录 def assetsDir = file("${project.projectDir}/src/main/assets") assetsDir.mkdirs() def mappingJson = new File(assetsDir, "method_mapping.json") mappingJson.text = new groovy.json.JsonBuilder(methodMap).toPrettyString() } } // 绑定到混淆任务之后执行 afterEvaluate { tasks.findByName("minifyReleaseWithR8")?.finalizedBy(generateMethodMapping) }
运行时读取与反射示例(Java)
private String getObfuscatedMethodName(String originalName) { try { InputStream is = getAssets().open("method_mapping.json"); byte[] buffer = new byte[is.available()]; is.read(buffer); is.close(); JSONObject jsonObject = new JSONObject(new String(buffer, StandardCharsets.UTF_8)); return jsonObject.getString(originalName); } catch (IOException | JSONException e) { e.printStackTrace(); return originalName; // 兜底用原始名称,避免崩溃 } } // 反射调用逻辑 try { String obfuscatedName = getObfuscatedMethodName("myMethod"); Method method = getClass().getDeclaredMethod(obfuscatedName); method.invoke(this); } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { e.printStackTrace(); }
三、其他实用替代方案
- 注解标记法:自定义一个注解(比如
@ObfuscatedMapping),给myMethod加上这个注解,然后配置Proguard保留该注解。再用Gradle任务扫描所有带注解的方法,自动生成映射文件,比硬编码方法名更灵活,适合多方法场景。 - 固定映射复用:如果你的项目迭代不频繁,可以用R8的
-applymapping参数复用之前的映射文件,这样myMethod的混淆名称会固定下来,你可以直接把这个固定名称硬写到代码里,但灵活性较差,不适合频繁改代码的项目。
内容的提问来源于stack exchange,提问作者Mohammad
相关产品推荐
相关产品推荐

