首页
课程
问答
CTF
社区
招聘
峰会
发现
排行榜
知识库
工具下载
看雪20年
看雪商城
证书查询
登录
注册
首页
社区
课程
招聘
发现
问答
CTF
排行榜
知识库
工具下载
峰会
看雪商城
证书查询
社区
Android安全
发新帖
4
3
[原创]利用frida和dexlib2进行字符串反混淆
发表于: 2025-12-3 17:28
59168
[原创]利用frida和dexlib2进行字符串反混淆
BediveRe_
2025-12-3 17:28
59168
## 1.字符串混淆 都是传入了一个函数来解密的格式  思路:用frida去hook最后的结果,然后用dexlib2来patch 目前写了一个小demo,尝试对一个地方进行patch ```java package org.example; import com.google.common.collect.Lists; import org.jf.dexlib2.DexFileFactory; import org.jf.dexlib2.Opcodes; import org.jf.dexlib2.iface.ClassDef; import org.jf.dexlib2.iface.DexFile; import org.jf.dexlib2.iface.Method; import org.jf.dexlib2.iface.MethodImplementation; import org.jf.dexlib2.iface.instruction.Instruction; import org.jf.dexlib2.iface.instruction.OneRegisterInstruction; import org.jf.dexlib2.iface.instruction.ReferenceInstruction; import org.jf.dexlib2.iface.reference.StringReference; import org.jf.dexlib2.builder.MutableMethodImplementation; import org.jf.dexlib2.builder.instruction.BuilderInstruction21c; import org.jf.dexlib2.immutable.reference.ImmutableStringReference; import org.jf.dexlib2.builder.instruction.BuilderInstruction10x; import org.jf.dexlib2.Opcode; import org.jf.dexlib2.immutable.ImmutableClassDef; import org.jf.dexlib2.immutable.ImmutableMethod; import org.jf.dexlib2.writer.io.FileDataStore; import org.jf.dexlib2.writer.pool.DexPool; import org.jf.dexlib2.builder.BuilderInstruction; import org.jf.dexlib2.immutable.ImmutableMethodImplementation; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.List; public class Main { public static void main(String[] args) throws IOException { File file = new File("C:\\Users\\cpq\\IdeaProjects\\untitled\\src\\main\\resources\\classes03.dex"); DexFile dexFile = DexFileFactory.loadDexFile(file, Opcodes.getDefault()); // 1. 创建 DexPool 用于重组和写入 Dex DexPool dexPool = new DexPool(Opcodes.getDefault()); for (ClassDef classDef : dexFile.getClasses()) { if (classDef.getType().equals("Lio/liankong/riskdetector/core/VO0O0OOOOOOO0;")) { System.out.println("Processing target class: " + classDef.getType()); // 2. 获取修改后的类定义 ClassDef modifiedClass = ReplaceStr(classDef); dexPool.internClass(modifiedClass); } else { // 3. 非目标类直接原样放入池中 dexPool.internClass(classDef); } } // 4. 保存文件 File outFile = new File("classes03_patched.dex"); System.out.println("Writing to " + outFile.getAbsolutePath()); dexPool.writeTo(new FileDataStore(outFile)); System.out.println("Done."); } private static ClassDef ReplaceStr(ClassDef classDef) { boolean isClassModified = false; List<Method> newMethods = new ArrayList<>(); for (Method method : classDef.getMethods()) { MethodImplementation methodImpl = method.getImplementation(); // 仅处理 <clinit> 且有实现的方法 if (methodImpl != null && method.getName().equals("<clinit>")) { MutableMethodImplementation mutableImpl = new MutableMethodImplementation(methodImpl); // === Fix 1: 必须复制到一个新的 ArrayList 中 === // mutableImpl.getInstructions() 返回的是只读视图,不能直接 set List<BuilderInstruction> instructions = new ArrayList<>(mutableImpl.getInstructions()); boolean isMethodModified = false; for (int i = 0; i < instructions.size(); i++) { Instruction instruction = instructions.get(i); if (instruction instanceof ReferenceInstruction) { ReferenceInstruction refInst = (ReferenceInstruction) instruction; if (refInst.getReference() instanceof org.jf.dexlib2.iface.reference.MethodReference) { org.jf.dexlib2.iface.reference.MethodReference methodRef = (org.jf.dexlib2.iface.reference.MethodReference) refInst .getReference(); if (methodRef.getDefiningClass().equals("Lio/liankong/riskdetector/OO0O0000O000;") && methodRef.getName().equals("IIIIIIIILIII")) { System.out.println("Found target call at index " + i); if (i >= 2) { Instruction prev1 = instructions.get(i - 1); Instruction prev2 = instructions.get(i - 2); if (prev1.getOpcode().name.startsWith("const-string") && prev2.getOpcode().name.startsWith("const-string")) { String str1 = ((StringReference) ((ReferenceInstruction) prev1).getReference()) .getString(); String str2 = ((StringReference) ((ReferenceInstruction) prev2).getReference()) .getString(); if (str2.contains("ggFH+206pVWHeByNO0rnJtdlR40APuch1xYmkHAu3TnsX1DGAGyuaYM=") && str1.contains("qj59oF0XnBQ=")) { System.out.println(" --> Patching..."); int targetReg = ((OneRegisterInstruction) prev2).getRegisterA(); BuilderInstruction newConstStr = new BuilderInstruction21c( Opcode.CONST_STRING, targetReg, new ImmutableStringReference("\u98ce\u9669App\u68c0\u6d4b")); BuilderInstruction nop = new BuilderInstruction10x(Opcode.NOP); instructions.set(i, newConstStr); instructions.set(i+1,newConstStr); // if (i + 1 < instructions.size()) { // instructions.set(i + 1, nop); // } isMethodModified = true; isClassModified = true; } } } } } } } if (isMethodModified) { // === Fix 2: 使用修改后的指令列表构建新的 Implementation === // 我们不能复用 mutableImpl,因为它里面的列表没变。我们手动创建一个 ImmutableImplementation。 MethodImplementation newImplementation = new ImmutableMethodImplementation( mutableImpl.getRegisterCount(), instructions, // 我们修改后的 List mutableImpl.getTryBlocks(), mutableImpl.getDebugItems()); newMethods.add(new ImmutableMethod( method.getDefiningClass(), method.getName(), method.getParameters(), method.getReturnType(), method.getAccessFlags(), method.getAnnotations(), method.getHiddenApiRestrictions(), newImplementation // 传入新的实现 )); } else { newMethods.add(method); // 未修改,保留原样 } } else { newMethods.add(method); // 其他方法,保留原样 } } if (isClassModified) { return new ImmutableClassDef( classDef.getType(), classDef.getAccessFlags(), classDef.getSuperclass(), classDef.getInterfaces(), classDef.getSourceFile(), classDef.getAnnotations(), classDef.getFields(), newMethods); } else { return classDef; } } } ``` 踩雷:  修改成  应该是长度对不齐,就会失败,对齐之后会发现确实去掉了这个地方  利用frida打一个表,然后尝试批量化 ```js Java.perform(function () { var OO0O0000O000 = Java.use("io.liankong.riskdetector.OO0O0000O000"); OO0O0000O000["IIIIIIIILIII"].implementation = function (str, str2) { let result = this["IIIIIIIILIII"](str, str2); str = str.replace(/\n/g, ''); str2 = str2.replace(/\n/g, ''); console.log(`${str2},${str}:${result}`); return result; }; }); ``` 凡是在frida里面被hook调用到的都会被patch回去  最后改成遍历所有类就ok了,重写了回溯,成功 最终版本 ```java package org.example; import org.jf.dexlib2.DexFileFactory; import org.jf.dexlib2.Opcodes; import org.jf.dexlib2.Opcode; import org.jf.dexlib2.iface.ClassDef; import org.jf.dexlib2.iface.DexFile; import org.jf.dexlib2.iface.Method; import org.jf.dexlib2.iface.MethodImplementation; import org.jf.dexlib2.iface.instruction.Instruction; import org.jf.dexlib2.iface.instruction.OneRegisterInstruction; import org.jf.dexlib2.iface.instruction.ReferenceInstruction; import org.jf.dexlib2.iface.reference.StringReference; import org.jf.dexlib2.builder.MutableMethodImplementation; import org.jf.dexlib2.builder.instruction.BuilderInstruction21c; import org.jf.dexlib2.builder.instruction.BuilderInstruction10x; import org.jf.dexlib2.immutable.reference.ImmutableStringReference; import org.jf.dexlib2.immutable.ImmutableClassDef; import org.jf.dexlib2.immutable.ImmutableMethod; import org.jf.dexlib2.immutable.ImmutableMethodImplementation; import org.jf.dexlib2.writer.io.FileDataStore; import org.jf.dexlib2.writer.pool.DexPool; import org.jf.dexlib2.builder.BuilderInstruction; import java.io.File; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Paths; import java.util.ArrayList; import java.util.List; public class Main { static class PatchConfig { String targetStr1; // 对应参数 v2 (例如 key) String targetStr2; // 对应参数 v0 (例如密文) String newStr; // 替换后的明文 public PatchConfig(String t1, String t2, String n) { this.targetStr1 = t1; this.targetStr2 = t2; this.newStr = n; } } public static void main(String[] args) throws IOException { // 输入和输出文件路径 File file = new File("C:\\Users\\cpq\\IdeaProjects\\untitled\\src\\main\\resources\\classes03.dex"); String configPath = "C:\\Users\\cpq\\IdeaProjects\\untitled\\src\\main\\resources\\tmp.txt"; // 配置文件路径 File outFile = new File("classes03_patched.dex"); List<PatchConfig> patchConfigs = new ArrayList<>(); List<String> lines = Files.readAllLines(Paths.get(configPath), StandardCharsets.UTF_8); for (String line : lines) { if (line.trim().isEmpty() || line.startsWith("#")) continue; String[] parts = line.split(":", 2); if (parts.length == 2) { String encryptedPart = parts[0]; String newStr = parts[1]; String[] encParams = encryptedPart.split(","); if (encParams.length >= 2) { String t1 = encParams[0].trim(); String t2 = encParams[1].trim(); // 将解析结果存入列表 去重 if (patchConfigs.stream().noneMatch(pc -> pc.targetStr1.equals(t1) && pc.targetStr2.equals(t2))) { patchConfigs.add(new PatchConfig(t1, t2, newStr)); } } else { System.err.println("Skipping invalid line (comma missing): " + line); } } else { System.err.println("Skipping invalid line (colon missing): " + line); } } System.out.println("Loaded " + patchConfigs.size() + " replacement rules."); // 2. 加载 Dex DexFile dexFile = DexFileFactory.loadDexFile(file, Opcodes.getDefault()); DexPool dexPool = new DexPool(Opcodes.getDefault()); // 3. 遍历并修改类 for (ClassDef classDef : dexFile.getClasses()) { if (classDef.getType().contains("io/liankong/riskdetector")) { System.out.println("Processing target class: " + classDef.getType()); ClassDef currentClass = classDef; for (PatchConfig config : patchConfigs) { currentClass = ReplaceStr( currentClass, "Lio/liankong/riskdetector/OO0O0000O000;", // 解密函数所在类 "IIIIIIIILIII", // 解密函数名 config.targetStr1, // 对应 mapping 中的 TargetStr1 config.targetStr2, // 对应 mapping 中的 TargetStr2 config.newStr // 对应 mapping 中的 NewStr ); } dexPool.internClass(currentClass); } else { dexPool.internClass(classDef); } } // 4. 保存文件 System.out.println("Writing to " + outFile.getAbsolutePath()); dexPool.writeTo(new FileDataStore(outFile)); System.out.println("Done."); } private static ClassDef ReplaceStr(ClassDef classDef, String ClassName, String MethodName, String TargetStr1, String TargetStr2, String NewStr) { boolean isClassModified = false; List<Method> newMethods = new ArrayList<>(); for (Method method : classDef.getMethods()) { MethodImplementation methodImpl = method.getImplementation(); if (methodImpl != null) { MutableMethodImplementation mutableImpl = new MutableMethodImplementation(methodImpl); List<BuilderInstruction> instructions = new ArrayList<>(mutableImpl.getInstructions()); boolean isMethodModified = false; for (int i = 0; i < instructions.size(); i++) { Instruction instruction = instructions.get(i); if (instruction instanceof ReferenceInstruction) { ReferenceInstruction refInst = (ReferenceInstruction) instruction; if (refInst.getReference() instanceof org.jf.dexlib2.iface.reference.MethodReference) { org.jf.dexlib2.iface.reference.MethodReference methodRef = (org.jf.dexlib2.iface.reference.MethodReference) refInst .getReference(); if (methodRef.getDefiningClass().equals(ClassName) && methodRef.getName().equals(MethodName)) { String foundContent = null; // 离 invoke 最近的 (参数2) String foundKey = null; // 离 invoke 较远的 (参数1) int foundCount = 0; // 向上回溯查找前两个 const-string for (int k = i - 1; k >= 0; k--) { Instruction prev = instructions.get(k); if (prev.getOpcode().name.startsWith("const-string")) { String val = ((StringReference) ((ReferenceInstruction) prev).getReference()).getString(); if (foundCount == 0) { foundContent = val; foundCount++; } else if (foundCount == 1) { foundKey = val; foundCount++; break; } } // 防止回溯过远 (可选) if (i - k > 30) break; } // 检查是否匹配 if (foundCount == 2 && foundKey.contains(TargetStr2) && foundContent.contains(TargetStr1)) { System.out.println("Patching found in: " + method.getName()); int targetReg = 0; boolean hasMoveResult = false; if (i + 1 < instructions.size()) { Instruction nextInst = instructions.get(i + 1); if (nextInst.getOpcode().name.startsWith("move-result")) { targetReg = ((OneRegisterInstruction) nextInst).getRegisterA(); hasMoveResult = true; } } if (hasMoveResult) { BuilderInstruction newConstStr = new BuilderInstruction21c( Opcode.CONST_STRING, targetReg, new ImmutableStringReference(NewStr)); BuilderInstruction nop = new BuilderInstruction10x(Opcode.NOP); // 替换 invoke instructions.set(i, newConstStr); instructions.set(i + 1, newConstStr); isMethodModified = true; isClassModified = true; } else { System.err.println("Warning: No move-result after invoke, skipping patch."); } } } } } } if (isMethodModified) { MethodImplementation newImplementation = new ImmutableMethodImplementation( mutableImpl.getRegisterCount(), instructions, // 我们修改后的 List mutableImpl.getTryBlocks(), mutableImpl.getDebugItems()); newMethods.add(new ImmutableMethod( method.getDefiningClass(), method.getName(), method.getParameters(), method.getReturnType(), method.getAccessFlags(), method.getAnnotations(), method.getHiddenApiRestrictions(), newImplementation // 传入新的实现 )); } else { newMethods.add(method); // 未修改,保留原样 } } else { newMethods.add(method); // 其他方法,保留原样 } } if (isClassModified) { return new ImmutableClassDef( classDef.getType(), classDef.getAccessFlags(), classDef.getSuperclass(), classDef.getInterfaces(), classDef.getSourceFile(), classDef.getAnnotations(), classDef.getFields(), newMethods); } else { return classDef; } } } ```
登录后可查看完整内容
冰与火的战歌:Windows内核攻防实战高级班!从零到实战,融合AI与Windows内核攻防全技术栈,打造具备自动化能力的内核开发高手。
最后于
2025-12-3 17:34 被BediveRe_编辑 ,原因:
#逆向分析
#脱壳反混淆
收藏
・
4
点赞
・
3
打赏
分享
分享到微信
分享到QQ
分享到微博
赞赏记录
参与人
雪币
留言
时间
autorun886
谢谢你的细致分析,受益匪浅!
2026-3-16 23:01
iamshy
+1
这个讨论对我很有帮助,谢谢!
2025-12-16 10:34
bananaships
+10
感谢你的贡献,论坛因你而更加精彩!
2025-12-3 22:33
查看更多
赞赏
×
1 雪花
5 雪花
10 雪花
20 雪花
50 雪花
80 雪花
100 雪花
150 雪花
200 雪花
支付方式:
微信支付
赞赏留言:
快捷留言
感谢分享~
精品文章~
原创内容~
精彩转帖~
助人为乐~
感谢分享~
最新回复
(
5
)
mb_asiwnxyv
雪 币:
6
能力值:
( LV1,RANK:0 )
在线值:
发帖
0
回帖
100
粉丝
0
关注
私信
mb_asiwnxyv
2
楼
感谢分享
2025-12-3 21:15
0
bananaships
雪 币:
321
活跃值:
(150)
能力值:
( LV2,RANK:10 )
在线值:
发帖
4
回帖
4
粉丝
12
关注
私信
bananaships
3
楼
tql
2025-12-3 22:34
0
iBa0
雪 币:
1901
活跃值:
(5923)
能力值:
( LV4,RANK:40 )
在线值:
发帖
6
回帖
490
粉丝
37
关注
私信
iBa0
4
楼
1
2025-12-4 11:03
0
uni7corn
雪 币:
0
活跃值:
(2485)
能力值:
( LV2,RANK:10 )
在线值:
发帖
0
回帖
182
粉丝
0
关注
私信
uni7corn
5
楼
感谢分享
2025-12-15 23:27
0
shuangmou
雪 币:
754
能力值:
( LV1,RANK:0 )
在线值:
发帖
0
回帖
68
粉丝
1
关注
私信
shuangmou
6
楼
感谢分享
2026-2-6 17:12
0
游客
登录
|
注册
方可回帖
回帖
表情
雪币赚取及消费
高级回复
返回
BediveRe_
1
发帖
0
回帖
10
RANK
关注
私信
他的文章
[原创]利用frida和dexlib2进行字符串反混淆
59168
关于我们
联系我们
企业服务
看雪公众号
专注于PC、移动、智能设备安全研究及逆向工程的开发者社区
看原图
赞赏
×
雪币:
+
留言:
快捷留言
为你点赞!
返回
顶部