手脱花指令及IDA脚本编写
综合许多篇帖子而成,大多参考以末尾参考文章形式给出,适用于初学者,感谢大家提出意见。可结合末尾源码参考学习。感觉有用的话感谢大佬们点个赞。## 简介
### 作用
- 欺骗反汇编器,让反汇编器无法正确反汇编出汇编代码,具体来说是破坏了反编译的分析,使得栈指针在反编译引擎中出现异常
- 从而加大静态分析的难度,使得逆向分析人员难以识别代码的真正意图
### 原理
由于反编译器的工作原理一般是线性扫描算法或递归下降反汇编算法。
- 线性扫描反汇编算法从程序的入口点开始反汇编,然后对整个代码进行扫描,反汇编扫描其过程中所遇到的每条指令。那么线性扫描算法的缺点也就显而易见了,由于其不定常的指令格式,在反汇编扫描过程中无法区分数据与代码,从而导致将代码段中嵌入的数据误解释为指令的操作码,以致最后得到错误的反汇编结果。
- 递归下降算法,递归下降算法通过程序的控制流来确定反汇编的下一条指令,遇到非控制转移指令时顺序进行反汇编,而遇到控制转移指令时则从转移地址处开始进行反汇编。该算法的缺点在于难于准确确定间接转移的目的地址。
### PATCH方法
先在IDA中开启字节码的显示,我这里设置显示的字节码是8。
以下图中的花指令为例,我们鼠标光标点到0x004560FF,然后按快捷键`D`,在下方为指令的地址0x00456100地址处,按快捷键`C`将其转换为指令。然后将0x004560FF处的`0xE8`Patch为`0x90`,也就是打补丁为nop指令。
然后保持副本即可。
## 实现案例
### 简单花指令-多层JMP嵌套
如下是单层的JMP形式:
```asm
jmp LABEL1
db junk_code;
LABEL1:
```
甚至如下的多层嵌套:
```c
//简单花指令-多层JMP嵌套
void example1()
{
__asm {
jmp LABEL1;
_emit 68h;
LABEL1:
jmp LABEL2;
_emit 0CDh;
_emit 20h;
LABEL2:
jmp LABEL3;
_emit 0E8h;
LABEL3:
}
a = 99;
}
```
如下图,因为IDA使用的是递归下降算法进行反汇编,所以这种花指令可以被IDA轻松识别。
### 互补条件代替JMP跳转
类似如下形式,无论如何都会跳转到LABEL1处:
```asm
jz LABEL1
jnz LABEL1
db junk_code
LABEL1:
```
在如下代码中,先对eax进行xor之后,再进行test比较,zf标志位肯定为1,就肯定执行`jz LABEL2;`,也就是说中间0xC7永远不会执行。要记得:先压栈保存eax的值,最后再把eax的值pop出来。
```c
void example2_1()
{
__asm {
push eax;
xor eax, eax;
test eax, eax;
jnzLABEL1;
jz LABEL2;
LABEL1:
_emit 0xC7;
LABEL2:
pop eax;
}
a = 21;
}
```
如下图,我们可以看到,IDA虽然识别栈帧错误,但是正确的程序流还是比较清晰的。
再如下,我们将中间填充代码改为`0x21`,混淆效果明显了一些,结果如下。
那么我们再来一个加强版,代码如下:
```c
void example2_3()
{
__asm {
xor eax, eax;
test eax, eax;
je LABEL1;
jne LABEL2;
LABEL2 :
_emit 0x5e;
and eax, ebx;
_emit 0x50;
xor eax, ebx;
_emit 0x74;
add eax, edx;
LABEL1:
}
a = 23;
}
```
### call&ret构造花指令
如下案例,代码中的esp存储的就是函数返回地址,对+8,就是函数的返回地址+8,正好盖过代码中的函数指令和垃圾数据。
```c
void example3()
{
__asm {
call LABEL9;
_emit 0x83;
LABEL9:
add dword ptr ss : , 8;
ret;
__emit 0xF3;
}
a = 3;
}
```
如下图所示,是上述花指令代码的效果,这里最好自己动态跟以下。
> call 指令的直观理解:push 函数返回地址; jmp 立即数
> ret 指令的直观理解:pop eip; add esp,4
### 利用函数返回确定值
有些函数返回值是确定的,比如我们自己写的函数,返回值可以是任意非零整数,就可以自己构造永恒跳转。
还有些API函数也是如此,比如在Win下`HMODULE LoadLibraryA(LPCSTR lpLibFileName);`函数,如果
我们故意传入一个不存在的模块名称,那么他就会返回一个确定的值`NULL`,此时就可以通过这个函数来构造永恒跳转。如下例子:
```c
void example4_1()
{
LoadLibrary(L"./hhhh");//函数返回值存储于eax中
__asm{
cmp eax, 0;
jc LABEL6_1;
jnc LABEL6_2;
LABEL6_1:
_emit 0xE8;
LABEL6_2:
}
a = 41;
}
```
混淆效果如下:
### call和ret的组合
如下代码:
```c
void __declspec(naked)__cdecl example5(int* a)//裸函数,开辟和释放堆栈由我们自己写。
{//55 8b ec 83
__asm
{
push ebp
mov ebp, esp
sub esp, 0x40
push ebx
push esi
push edi
mov eax, 0xCCCCCCCC
mov ecx, 0x10
lea edi, dword ptr ds :
rep stos dword ptr es :
}
*a = 5;
__asm
{
call LABEL9;
_emit 0xE8;
_emit 0x01;
_emit 0x00;
_emit 0x00;
_emit 0x00;
LABEL9:
push eax;
push ebx;
leaeax, dword ptr ds : ; //将ebp的地址存放于eax
add dword ptr ss : , 26;//该地址存放的值正好是函数返回值,
//不过该地址并不固定,根据调试所得。加26正好可以跳到下面的mov指令,该值也是调试计算所得
pop eax;
pop ebx;
pop eax;
jmp eax;
__emit 0xE8;
_emit 0x03;
_emit 0x00;
_emit 0x00;
_emit 0x00;
mov eax, dword ptr ss : ;//将原本的eax值返回eax寄存器
}
__asm
{
pop edi
pop esi
pop ebx
mov esp, ebp
pop ebp
ret
}
}
```
也就是说思路有很多种,按照自己喜欢的方式组合,只要不影响其他正常代码的运行就可以,如下也是比较好的两种思路
#### call嵌套的其他思路1
```assembly
call LABEL1
db 0E8h
LABEL2:
jmp LABEL3
db 0
db 0
db 0E8h
db 0F6h
db 0FFh
db OFFh
db OFFh
LABEL1:
:call LABEL2
LABEL3:
add esp,8
```
#### call嵌套的其他思路2
```assembly
push eax
call LABEL1
db 29h
db 5Ah
LABEL1:
POP eax
imul eax,3
call LABEL2
db 29h
db5Ah
LABEL2:
add esp,4
pop eax
```
## 花指令原理另类利用
当我们理解了花指令的原理后,我们可以在将花指令中的垃圾数据替换为一些特定的特征码,可以对应的$“定位功能”$,尤其在(https://blog.csdn.net/dontbecoder/article/details/8754729?utm_source=app)这个反调试技术中可以运用。例如:
```
asm
{
Jz Label
Jnz Label
_emit 'h'
_emit 'E'
_emit 'l'
_emit 'L'
_emit 'e'
_emit 'w'
_emit 'o'
_emit 'R'
_emit 'l'
_emit 'D'
Label:
}
```
将这串特征码hElLowoRlD嵌入到代码中,那我们只需要在当前进程中搜索hElLowoRlD字符串,就可以定位到当前代码位置,然后对下面的代码进行SMC自解密。
### 小结
构造永恒跳转,添加垃圾数据
## 综合题目案例
### IDC脚本去花
我们以题目`.exe`为例子,分析一段花指令,使用IDA中的快捷键`D`和`C`来手动过花指令。手动分析清楚其流程后,我们可以写一个脚本,来批量匹配花指令的模式`NOP`掉影响静态分析的代码。
写好的IDC脚本如下:
```c
//文件名:test.idc
#include <idc.idc>
static main()
{
auto x,FBin,ProcRange;
FBin = "E8 0A 00 00 00 E8 EB 0C 00 00 E8 F6 FF FF FF";
//目标 = "E8 0A tel:00 00 00 90 EB 0C tel:90 90 90 90 90 90 90";
//花指令1的特征码
for (x = FindBinary(MinEA(),0x03,FBin);x != BADADDR;x = FindBinary(x,0x03,FBin))
{
x=x+5; //返回的x是第一个E8的地址,
//加上5是第二个E8的地址
PatchByte (x,0x90);//nop掉
x = x + 3; //00
PatchByte (x,0x90);
x++;//00 E8
PatchWord (x,0x9090);
x =x + 2 ; //F6 FF FF FF
PatchDword(x,0x90909090);
}
}
```
### 使用IDAPython去花
```python
import idautils
import idc
def my_nop(addr, endaddr):
while addr < endaddr:
patch_byte(addr, 0x90)
addr += 1
pattern = "E8 0A 00 00 00 E8 EB 0C 00 00 E8 F6 FF FF FF"
cur_addr = 0x456000
end_addr = 0x467894
while cur_addr<end_addr:
cur_addr = idc.find_binary(cur_addr,SEARCH_DOWN,pattern)
print("patch address: " + str(cur_addr)) # 打印提示信息
if cur_addr == idc.BADADDR:
break
else:
my_nop(cur_addr+5,cur_addr+6)
my_nop(cur_addr+8,cur_addr+14)
cur_addr = idc.next_head(cur_addr)
```
附带源码:
## 参考
- 反汇编基础:<https://tinytracer.com/archives/反汇编基础/>
- 自动获取驱动程序IO控制码初级版:<https://bbs.pediy.com/thread-153965.htm>
- 花指令总结:<https://www.anquanke.com/post/id/236490>
- 反编译系列教程(上):<http://drops.xmd5.com/static/drops/papers-13686.html>
- _emit伪指令:<https://docs.microsoft.com/zh-cn/cpp/assembler/inline/emit-pseudoinstruction?view=msvc-160>
- IDAPython官方文档:<https://hex-rays.com/products/ida/support/idapython_docs/>
- IDAPython脚本示例:<https://www.cnblogs.com/shenshuoyaoyouguang/p/13841078.html>
- IDAPython动态调试开发技巧:<https://www.bbsmax.com/A/x9J27Lgg56/> 本帖最后由 heartingrass 于 2022-9-19 15:24 编辑
flatcc 发表于 2022-9-16 17:49
我想想啊,有遇到实例嘛,可以探讨下楼主可以试一试,感觉这个长跳转就无法进行模式匹配了,需要消除长跳转,然后拼接代码,可能要利用到一些功能模块,另外完全借助IDA分析,也存在指令识别错误或未识别的情况。
dump2.exe:第二段代码是32bit,里面有大量花指令,需要进行清除之后,才方便进行程序逻辑分析。花指令的特征主要有如下几处:
一、连续的指令
1、ebp、esp还原【是后面的特例】
seg000:000CC824 014 55 push ebp
seg000:000CC825 018 89 E5 mov ebp, esp
seg000:000CC827 018 8D 64 24 FC lea esp,
seg000:000CC82B 01C 89 EC mov esp, ebp
seg000:000CC82D 01C 5D pop ebp
2、esp还原【是后面的特例】
seg000:000C5FBA 01C 52 push edx
seg000:000C5FBB 020 8D 64 24 04 lea esp,
3、无用指令【待核实,只有一处】
seg000:0021C40D 84 56 C7 test , dl
seg000:0021C410 08 00 or , al
二、存有大量跳转指令
1、ebp、esp还原【中间嵌入长跳转】
seg000:000CC824 014 55 push ebp
seg000:000CC825 018 89 E5 mov ebp, esp
seg000:000CC827 018 8D 64 24 FC lea esp,
seg000:000CC82B 01C 89 EC mov esp, ebp
seg000:000CC82D 01C 5D pop ebp
2、esp还原【中间嵌入长跳转】
seg000:000C5FBA 01C 52 push edx
seg000:000C5FBB 020 8D 64 24 04 lea esp,
3、pushf、popf还原【中间嵌入长跳转】
seg000:002A0496 loc_2A0496: ; CODE XREF: seg000:00000001↑j
seg000:002A0496 9C pushf
seg000:002A0497 51 push ecx
seg000:002A0498 50 push eax
seg000:002A0499 31 C0 xor eax, eax
seg000:002A049B
seg000:002A049B loc_2A049B: ; CODE XREF: seg000:002A04B3↓j
seg000:002A049B 83 F8 03 cmp eax, 3
seg000:002A049E 74 15 jz short loc_2A04B5
seg000:002A04A0 40 inc eax
seg000:002A04A1 81 E9 2E CD 9B A2 sub ecx, 0A29BCD2Eh
seg000:002A04A7 81 C9 07 48 0C 20 or ecx, 200C4807h
seg000:002A04AD 81 D9 2B 22 BB B1 sbb ecx, 0B1BB222Bh
seg000:002A04B3 72 E6 jb short loc_2A049B
seg000:002A04B5
seg000:002A04B5 loc_2A04B5: ; CODE XREF: seg000:002A049E↑j
seg000:002A04B5 58 pop eax
seg000:002A04B6 59 pop ecx
seg000:002A04B7 9D popf
4、pushf、popf还原【中间嵌入长跳转、还嵌套自身】
seg000:002AE278 9C pushf
seg000:002AE279 E9 5E 37 FB FF jmp loc_2619DC
seg000:002619DC loc_2619DC: ; CODE XREF: seg000:002AE279↓j
seg000:002619DC 52 push edx
seg000:002619DD 51 push ecx
seg000:002619DE 9C pushf 【此处 嵌套了自身】
seg000:002619DF 50 push eax
seg000:002619E0 52 push edx
seg000:002619E1 31 D2 xor edx, edx
seg000:002619E3
seg000:002619E3 loc_2619E3: ; CODE XREF: seg000:002619F8↓j
seg000:002619E3 83 FA 0A cmp edx, 0Ah
seg000:002619E6 74 12 jz short loc_2619FA
seg000:002619E8 42 inc edx
seg000:002619E9 05 3F 86 3A 6F add eax, 6F3A863Fh
seg000:002619EE 0D 99 24 DF 25 or eax, 25DF2499h
seg000:002619F3 05 D0 BA 30 4B add eax, 4B30BAD0h
seg000:002619F8 7E E9 jle short loc_2619E3
seg000:002619FA
seg000:002619FA loc_2619FA: ; CODE XREF: seg000:002619E6↑j
seg000:002619FA 5A pop edx
seg000:002619FB 58 pop eax
seg000:002619FC 9D popf
seg000:002619FD E9 B6 62 EC FF jmp loc_127CB8
seg000:00127CB8 loc_127CB8: ; CODE XREF: seg000:002619FD↓j
seg000:00127CB8 31 C9 xor ecx, ecx
seg000:00127CBA
seg000:00127CBA loc_127CBA: ; CODE XREF: seg000:000FAD33↑j
seg000:00127CBA 83 F9 06 cmp ecx, 6
seg000:00127CBD 55 push ebp
seg000:00127CBE 89 E5 mov ebp, esp
seg000:00127CC0 8D 64 24 FC lea esp,
seg000:00127CC4 89 EC mov esp, ebp
seg000:00127CC6 5D pop ebp
seg000:00127CC7 E9 E0 48 F4 FF jmp loc_6C5AC
seg000:0006C5AC loc_6C5AC: ; CODE XREF: seg000:00127CC7↓j
seg000:0006C5AC 0F 84 15 0A FF FF jz loc_5CFC7
seg000:0006C5B2 41 inc ecx
seg000:0006C5B3 E9 78 7B 1B 00 jmp loc_224130
seg000:00224130 loc_224130: ; CODE XREF: seg000:0006C5B3↑j
seg000:00224130 81 FA 6B 15 C8 6D cmp edx, 6DC8156Bh
seg000:00224136 81 E2 38 42 C7 AF and edx, 0AFC74238h
seg000:0022413C E9 EC 6B ED FF jmp loc_FAD2D
seg000:000FAD2D loc_FAD2D: ; CODE XREF: seg000:0022413C↓j
seg000:000FAD2D 81 DA FA A1 7F 86 sbb edx, 867FA1FAh
seg000:000FAD33 0F 80 81 CF 02 00 jo loc_127CBA
seg000:000FAD39 E9 89 22 F6 FF jmp loc_5CFC7
seg000:0005CFC7 loc_5CFC7: ; CODE XREF: seg000:loc_6C5AC↓j
seg000:0005CFC7 ; seg000:000FAD39↓j
seg000:0005CFC7 59 pop ecx
seg000:0005CFC8 5A pop edx
seg000:0005CFC9 E9 54 CF 1E 00 jmp loc_249F22
seg000:00249F22 loc_249F22: ; CODE XREF: seg000:0005CFC9↑j
seg000:00249F22 9D popf
seg000:00249F23 8D 64 24 04 lea esp,
seg000:00249F27 E9 4B AA E9 FF jmp loc_E4977
seg000:000E4977 loc_E4977: ; CODE XREF: seg000:00249F27↓j
seg000:000E4977 E9 C5 2E 1A 00 jmp loc_287841
seg000:00287841 loc_287841: ; CODE XREF: seg000:loc_E4977↑j
seg000:00287841 9D popf
这两个花指令的简略特征是:
9C pushf
52 push edx
51 push ecx
31 C9 xor ecx, ecx
83 F9 08 cmp ecx, 8 【JZ 后续代码 的最后一个有条件跳转会 跳转到此处】
0F 84 A2 17 E4 FF jz loc_E9020
59 pop ecx
5A pop edx
9D popf
【JZ】后续代码特征1:
47 inc edi
81 EA 4A 0A 6A C8 sub edx, 0C86A0A4Ah
81 F2 F7 12 2D EE xor edx, 0EE2D12F7h
81 EA C6 07 02 1C sub edx, 1C0207C6h
0F 89 FB E3 13 00 jns loc_1D2296
特征2:
43 inc ebx
1D F3 63 58 CC sbb eax, 0CC5863F3h
25 DC C9 C5 7F and eax, 7FC5C9DCh
05 C8 9D D9 2E add eax, 2ED99DC8h
0F 8D FB 01 FA FF jge loc_1A7C7E
特征3:
41 inc ecx
3D 7C AA 0E E6 cmp eax, 0E60EAA7Ch
35 93 5A FA 84 xor eax, 84FA5A93h
1D 64 15 D9 23 sbb eax, 23D91564h
0F 88 B0 7F EA FF js loc_158A72
特征4:
41 inc ecx
81 D2 B7 D6 07 45 adc edx, 4507D6B7h
81 CA 5A 03 73 78 or edx, 7873035Ah
81 C2 10 EA 7D 71 add edx, 717DEA10h
0F 88 E1 61 03 00 js loc_1CC00A
特征5:
41 inc ecx
15 CB 30 92 03 adc eax, 39230CBh
25 86 2C C9 70 and eax, 70C92C86h
15 17 1C 96 C5 adc eax, 0C5961C17h
0F 86 FE 34 11 00 jbe loc_2077A4
附件是:x32程序片段,不可执行,里面是一大堆的花指令。
学习学习! 总结的很好。研究很有深度 vmp的代码能还原不 知识盲区了 好复杂,看着头晕 学习学习,谢谢分享 感谢分享 除了案例都看懂了 感谢分享 学习了 感谢分享