ollvm有哪几种混淆方式?
指令替换
将简单的运算指令替换为功能等价但更复杂的表达式,增加逆向难度。
原始代码
1 2 3 4 5 6 7
| int calculate(int a, int b) { int sum = a + b; * int diff = a - b; * int xor = a ^ b; * return sum + diff + xor; }
|
OLLVM -sub 混淆后
1 2 3 4 5 6 7 8 9 10 11 12 13
| int calculate(int a, int b) { int sum = a - (-b); int diff = a + (~b + 1); int xor_val = (a | b) - (a & b); return sum + diff + xor_val; }
|
💡 **核心思想:**利用数学恒等式,如 a + b ≡ a - (-b)、a ^ b ≡ (a|b) - (a&b)、a - b ≡ a + (~b + 1) 等,让代码逻辑不变但形式变得晦涩。
1、简单的 人工还原
2、复杂 MBA 会考虑借助 Z3 或 angr 等符号执行工具
3、特别麻烦直接unicron 模拟执行
虚假控制流
插入永远不会执行的虚假分支,配合不透明谓词(Opaque Predicates)干扰静态分析。
1 2 3 4 5 6
| int check_password(int input) { if (input == 0x1234) { return 1; } return 0; }
|
OLLVM -bcf 混淆后
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| int check_password(int input) { if ((input * input) >= 0) { if (input == 0x1234) { return 1; } } else { int garbage = 0xDEADBEEF; garbage = garbage * 3 + 7; if (garbage == 0x1234) { fake_function(); } } return 0; }
|
**💡 核心思想:*利用不透明谓词——对人类/编译器显而易见为真/假,但对静态分析工具难以判断的条件。例如 (xx) >= 0 对整数恒真,但符号执行可能无法确定。
最简单 直接nop掉无用流程 用unidbg 修改寄存器或者frida
控制流扁平化
将函数的所有基本块放入一个循环+switch结构中,彻底打乱原始控制流。
1 2 3 4 5 6 7 8 9 10 11 12 13
| int login(int user, int pass) { if (user == 0x1111) { if (pass == 0x2222) { return 1; } else { log_error("bad pass"); } } else { log_error("bad user"); } return 0; }
|
OLLVM -fla 混淆后
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| int login(int user, int pass) { int state = 0; int result = 0; while (1) { switch (state) { case 0: state = (user == 0x1111) ? 1 : 4; break; case 1: state = (pass == 0x2222) ? 2 : 3; break; case 2: result = 1; state = 5; break; case 3: log_error("bad pass"); state = 5; break; case 4: log_error("bad user"); state = 5; break; case 5: return result; } } }
|
**💡 核心思想:**把所有基本块变成”平级”的case,通过state变量控制跳转。原本清晰的嵌套if-else变成了状态机,逆向工程师无法直观看出逻辑层次关系。
你插桩得到的执行顺序,变回正常的控制流
字符串加密
- 对程序中的字符串常量进行加密,运行时动态解密
- 防止通过字符串搜索快速定位关键逻辑
反调试/反分析技术
- 虽然OLLVM核心主要以上述三种为主,但一些扩展版本或配合使用的工具还会加入:
Unidbg问题
补环境用Unidbg 怎么可以保证分支正确 ,如果出现异常你如何定位是系统函数缺失还是库函数缺失,软中断怎么看?
怎么保证分支正确?
答: “对齐流向,缺啥补啥”。先用 traceCode() 打印执行轨迹,对比真机。如果是走错分支,说明环境被风控识别了。最快的方法是直接在 Unidbg 里用 write() 打补丁(Patch),强行把分支的跳转条件(比如 CBZ、CBNZ 指令)改掉。
什么是 traceCode?
emulator.traceCode(baseAddress, baseAddress + size);
dm.callFunction(emulator, “target_encrypt_func”);
traceCode 就是把 SO 文件的内存范围传进去,让 Unidbg 把执行过的汇编指令一行行打在控制台上。
怎么用 write 打补丁(Patch)?
如果检测到 Root,就跳到“退出程序”分支;如果没检测到,就继续。
你想让它不管检测没检测到,都当做没检测到,最简单的办法就是把这行指令变成 NOP
1 2 3 4 5
| long targetAddress = dm.base + 0x1234;
byte[] nopInstructions = new byte[]{ 0x1F, 0x20, 0x03, (byte) 0xD5 };
emulator.getMemory().pointer(targetAddress).write(nopInstructions);
|
怎么分清系统函数还是库函数缺失?
答: 看报错的“第一现场”。
- 系统函数(JNI/Syscall)缺失: 报
Unsupported virtual method(Java 层的库没补全)或 Unknown syscall。
- 库函数(SO 依赖)缺失: 报
cannot locate symbol(说明找不到其他 SO 里的 C 函数)。
只有这两种方法吗?
1、底层异常: UnicornException Fetch memory failed Invalid memory read/write
2、库函数缺失: Illegal instruction
3、看Unidbg的 warning警告
软中断(SVC)怎么看?
答: 软中断就是底层系统调用。在 ARM 中,看特定的寄存器就行。ARM32 看 R7,ARM64 看 X8,里面存的就是系统调用号(比如 open、read)。Unidbg 里开启 setVerbose(true) 就能在控制台直接看到它们。
Unidbg 如何处理属性访问(Property)?
Android 获取设备属性主要有两种方式。
第一种是 Java 层,通过 android.os.Build 或 SystemProperties.get() 获取,例如 Build.MODEL、Build.BRAND、Build.FINGERPRINT。
在 Unidbg 中,这类访问一般通过实现 AbstractJni 的 getStaticObjectField()、getStaticIntField() 等方法返回对应值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| SystemPropertyHook systemPropertyHook = new SystemPropertyHook(emulator);
systemPropertyHook.setPropertyProvider(new SystemPropertyProvider() {
@Override public String getProperty(String key) {
System.out.println("SystemProperty: " + key);
switch (key) {
case "ro.board.platform": return "sdm845";
case "ro.product.brand": return "Xiaomi";
case "ro.product.manufacturer": return "Xiaomi";
case "ro.product.model": return "MI 8";
case "ro.product.device": return "dipper";
case "ro.product.name": return "dipper";
case "ro.build.version.release": return "10";
case "ro.build.version.sdk": return "29";
case "ro.build.id": return "QKQ1.190828.002";
case "ro.build.fingerprint": return "Xiaomi/dipper/dipper:10/QKQ1.190828.002/V12.0.3.0:user/release-keys";
default: return null; } } });
|
评论