花指令
花指令是一种反静态调试的最基础手段(对于动态调试来说就没有用处),我们可以通过在程序的代码中添加一些不影响程序运行的垃圾机器码,进而影响反汇编结果的准确性,达到程序保护的目的
花指令分类
1、可执行花指令
见字知其意,即花指令在程序正常运行的时候被执行,通用寄存器的值不发生改变,但不影响程序原有的功能执行
2、不可执行花指令
见字知其意,即花指令在程序正常运行的时候不会被执行,不影响程序原有的功能
花指令编写
原则:保持堆栈的平衡
常用花指令
汇编小知识:
mov eax, 1 eax赋值为1 pop 1 将1从栈顶弹出 pop ebp 将栈顶的值弹出赋给寄存器ebp push 1 将1压入栈中 push ebp 将ebp的值压入栈中 sub eax, 1 eax的值减1 add eax, 1 eax的值加1 inc eax eax的值加1 dec eax eax的值减1 call [x] 调用地址为x的函数,call对应的硬编码为0xE8 jmp x 跳转到x地址处,jmp对应的硬编码为0xE9 nop 不做任何事情,相当于python中的pass,对应的硬编码为0x90 _emit 相当于db,byte类型,1字节
以下方式均通过内联汇编实现
标签方式的花指令
1、单节方式
#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改为0x90(nop指令)
2、多节方式
#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; }
3、多层乱序(疯狂套娃)
#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; }
4.开辟堆栈的花指令
push 1 push ebp mov ebp, esp sub esp, 0x8 push eax push ecx pop ecx pop eax add esp, 0x8 pop ebp je xxx jne xxx
花指令多的情况就需要自己写个IDA python脚本进行去除
5.花指令免杀
一些反病毒软件依靠特征码来判断文件是否有毒,其识别引擎在文件镜像(filebuffer)一定的偏移范围内进行扫描,比如在0x00001000~0x00006000之间,我们在其中加入一些花指令,使恶意代码偏离引擎识别的偏移范围,再使用工具修改程序入口(OEP),就可以逃避这种方式的特征码识别