灵感来源:http://schnappi.games/2022/04/09/手脱花指令及ida脚本编写/
在中难题中,出题者往往通过ollvm、花指令、vm等手段妨碍正常逆向分析过程,本篇将介绍使用自动化脚本进行去花。
简单例子
这是一个简单的tea加密
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| #include <stdio.h> #include <stdint.h>
void encrypt(uint32_t* v, uint32_t* k) { uint32_t v0 = v[0], v1 = v[1], sum = 0, i; uint32_t delta = 0x9e3779b9; uint32_t k0 = k[0], k1 = k[1], k2 = k[2], k3 = k[3]; for (i = 0; i < 32; i++) { sum += delta; v0 += ((v1 << 4) + k0) ^ (v1 + sum) ^ ((v1 >> 5) + k1); v1 += ((v0 << 4) + k2) ^ (v0 + sum) ^ ((v0 >> 5) + k3); } v[0] = v0; v[1] = v1; } int main() { int a = 1; uint32_t flag[] = { 1234,5678 }; uint32_t key[] = { 9,9,9,9 }; encrypt(flag, key); printf("%d,%d", flag[0], flag[1]); return 0; }
|
我们可以通过加入些花指令来妨碍分析
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
| #include <stdio.h> #include <stdint.h>
void encrypt(uint32_t* v, uint32_t* k) { uint32_t v0 = v[0], v1 = v[1], sum = 0, i; uint32_t delta = 0x9e3779b9; uint32_t k0 = k[0], k1 = k[1], k2 = k[2], k3 = k[3]; for (i = 0; i < 32; i++) { __asm { jmp junk1 __emit 0x12 junk2: ret __emit 0x34 junk1 : call junk2 } sum += delta; v0 += ((v1 << 4) + k0) ^ (v1 + sum) ^ ((v1 >> 5) + k1); v1 += ((v0 << 4) + k2) ^ (v0 + sum) ^ ((v0 >> 5) + k3); } v[0] = v0; v[1] = v1; } int main() { int a = 1; uint32_t flag[] = { 1234,5678 }; uint32_t key[] = { 9,9,9,9 }; __asm { _emit 075h _emit 2h _emit 0E9h _emit 0EDh } encrypt(flag, key); printf("%d,%d", flag[0], flag[1]); return 0; }
|
在vs中编译后丢到ida里。

不仅汇编分析失败,也无法f5看main的反编译。
回到源码,对第二处汇编代码进行分析,75 02是jnz $+2,即如果结果不为0则跳转到两个指令后,所以e9 ed不会被执行。
对于这种可以手动patch,但是如果数量较多,我们可以采用自动化分析
先上脚本
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
| def nop(addr, endaddr): while addr < endaddr: patch_byte(addr, 0x90) addr += 1
def undefine(addr, endaddr): while addr < endaddr: del_items(addr, 0) addr += 1
def dejunkcode(addr, endaddr): while addr < endaddr: create_insn(addr) if print_insn_mnem(addr) == 'jnz' and \ get_operand_value(addr, 0) == addr + 4: next = addr + 4 nop(addr, next) addr = next continue addr += get_item_size(addr)
dejunkcode(0x00401160, 0x004011D4) undefine(0x00401160, 0x004011D4) add_func(0x00401160, -1)
|
1 2 3 4 5 6 7 8
| create_insn(ea) get_item_size(ea) print_insn_mnem(ea) get_operand_value(ea,n) patch_byte(ea, value) get_wide_byte(ea) del_items(ea,0) add_func(ea,end)
|
我们对main函数的范围进行dejunkcode和undefine,最后重新创建函数。
在dejunkcode的过程中,我们找到jnz的代码,然后判断跳转到的字节,再进行nop。
运行脚本是在file中

跑python脚本就可以了