反调试之花指令

花指令是一种反静态调试的手段(对于动态调试来说就没有任何用处),我们可以通过在程序的代码中添加一些不影响程序运行的垃圾数据,进而影响反汇编结果的准确性,达到程序保护的目的

花指令分类

1、可执行花指令

见字知其意,即花指令在程序正常运行的时候被执行,不影响程序原有的功能

2、不可执行花指令

见字知其意,即花指令在程序正常运行的时候不会被执行,不影响程序原有的功能

花指令编写

原则:保持堆栈的平衡

常用花指令

汇编小知识:

1
2
3
4
5
6
7
8
9
10
11
mov eax, 1 		 eax赋值为1
pop 1 将1从栈顶弹出
pop ebp 将栈顶的值弹出赋给寄存器ebp
push 1 将1压入栈中
push ebp 将ebp的值压入栈中
add eax, 1 eax的值加1
inc eax eax的值加1
dec eax eax的值减1
call [x] 调用地址为x的函数,call对应的硬编码为0xE8
jmp x 跳转到x地址处,jmp对应的硬编码为0xE9
_emit 相当于db,byte类型,1字节

以下方式均通过内联汇编实现

标签方式的花指令

单节方式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include "stdafx.h"

void Test(){
int a[3] = {1, 2, 3};
_asm{
jz Label;
jnz Label;
_emit 0xE8;
}
Label:
a[0] = 2;
a[1] = 5;
a[2] = 6;
printf("%d\n", a[2]);
}

int main(int argc, char* argv[]){
Test();
return 0;
}

使用IDA打开,可以看到标红的地方就是花指令,因为call指令的存在,使得后面的4字节数据被错误识别成函数地址,进而导致接下来的分析出错

人工Patch花指令的方式很简单:

  • 选中call指令所在行,点击Edit选项>Patch program>Change byte

  • 将call的硬编码E8改为90(nop指令)

多节方式
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
#include "stdafx.h"

void Test(){
int a[3] = {1, 2, 3};
_asm{
jz Label1;
jnz Label1;
_emit 0xE9;
}
Label1:
a[0] = 5;
a[1] = 6;
a[2] = 7;
_asm{
jz Label2;
jnz Label2;
_emit 0xE8;
}
Label2:
a[1] = a[0] + a[2];
a[2] = a[1] + a[0];
printf("%d\n", a[2]);
}

int main(int argc, char* argv[])
{
Test();
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
#include "stdafx.h"

void Test(){
int arr[3] = {1, 2, 3};
_asm{
jz Label3;
jnz Label3;
_emit 0xE8;
}
Label2:
_asm{
jz Label4;
jnz Label4;
_emit 0xE8;
}

Label3:
_asm{
jz Label1;
jnz Label1;
_emit 0xE9;
}
Label1:
_asm{
jz Label2;
jnz Label2;
_emit 0xE9;
}
Label4:
int a = 10;
printf("%d\n",a);

}
int main(int argc, char* argv[])
{
Test();
return 0;
}

开辟堆栈的花指令

保持堆栈平衡

1
2
3
4
5
6
7
8
9
10
11
push 1
push ebp
mov ebp, esp
sub esp, 0x8
push eax
push ecx
pop ecx
pop eax
pop ebp
je xxx
jne xxx

编写去除脚本

如果程序里的花指令较多,我们就得想办法使用IDA Python来帮助我们完成工作

花指令免杀

一些反病毒软件依靠特征码来判断文件是否有毒,其识别引擎在文件镜像(filebuffer)一定的偏移范围内进行扫描,比如在0x00001000~0x00006000之间,我们在其中加入一些花指令,使恶意代码偏离引擎识别的偏移范围,再使用工具修改程序入口(OEP),就可以逃避这种方式的特征码识别