花指令基础
花指令是一种反静态调试的最基础手段,其存在是干扰选手静态分析,但不会影响程序的运行。
实质就是一串垃圾指令,它与程序本身的功能无关,并不影响程序本身的逻辑运行。在软件保护中,花指令被作为一种手段来增加静态分析的难度。
IDA并不能正常识别花指令,导致可看可分析代码被破坏,因此需要我们自己去分析一下。
**花指令主要分为两类:
- 可执行花指令
- 不可执行花指令。
汇编基础
需要有一些x86汇编基础,推荐书籍王爽《汇编语言》** 常见的汇编指令:
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 不做任何事情,对应的硬编码为0x90
_emit 相当于db(byte类型,1字节)
常见混淆的字节码:
| 机器码 | 汇编语言 |
|---|---|
| 9A | CALL immed32 |
| E8 | CALL immed16 |
| E9 | JMP immed16 |
| EB | JMP immed8 |
常见花指令形式
1-单字节
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
_asm {
jz start; //jz 和 jnz 同时出现,导致永恒跳转
jnz start;
_emit 0xE8; //这个地方就是故意插入的花指令 CALL + 地址
} //程序正常运行会直接跳到start,但IDA线性扫描会误判下方是call指令,自动将后面的字节码识别为地址
start:
printf("ok!");
return 0;
}
2-永恒跳转
int main()
{
_asm {
xor eax, eax; // eax ^ eax = 0
jz s; // 必然成立跳转,一定会跳转
_emit 0x11; //填充垃圾指令 byte类型
_emit 0x22; //填充垃圾指令 byte类型
_emit 0x33; //填充垃圾指令 byte类型
s:
}
printf("test \n");
}
3-更改ESP
int main()
{
_asm {
xor eax, eax;
jz s; //必定跳转s
add esp, 0x11; // IDA 会把此指令识别对函数堆栈进行操作,导致识别函数失败
s:
}
printf("test \n");
}
4-jmp跳转+插入无效垃圾指令
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
_asm {
jmp $+5 //$表示当前位置,+5即跳过5个字节码,到lable处了
_emit 0x71
_emit 2
_emit 0xE9
_emit 0xED
}
lable:
printf("ok2");
return 0;
}
5-永恒跳转嵌套(需要一层一层恢复)
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
_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:
printf("ok2");
return 0;
}
反混淆基础
方法一:手动恢复
适用条件:混淆少且类型单一 为了尽快解除题目,我们一般选择先手动去除混淆,迅速拿到flag 这里需要掌握IDA的基本快捷键:U、C、P
- U: 在IDA Pro中,按“U”重新定义汇编,转换为字节码格式
- C: 在IDA Pro中,按“C”,转换字节码为汇编形式
- P: 在IDA Pro中,按“P” 转换汇编语言为高级语言函数视图
方法二:IDA-Python脚本恢复
适用条件:混淆大量,手工基本不可去除
需要拿到混淆的字节码组成,利用脚本去除大量混淆。 需要根据实际地址和匹配模式来进行更改
import idc
start_add = 0x400800
end_add = 0x406e22
for i in range(start_add,end_add):
if idc.get_wide_dword(i) == 0x08750a74: #小端序,匹配花指令
for mm in range(9):
ida_bytes.patch_byte(i+mm,0x90)
例子
re_hua1
IDA打开
双击main_0

看到标红,且无法生成伪代码。
jz,jnz永恒跳转
所以在E8(call)处,按“u”,然后右键用nop填充E8

再在nop处按“c”,再在main_0开头处按p即可恢复函数
// comments
评论
正在读取评论…