花指令基础
偶尔更新。。。
默认会一点cpp和汇编。
文章目录
- 花指令基础
- 1. 简介
- 2. 常见类型
- 1. jx+jnx
- 2. call+add [esp]+ret
- 3. 等价解析
- 1. push pop
- 2. jmp call retn
- 3. enter leave
- 4. and
- MoeCTF 2022 chicken_soup
- 参考
1. 简介
花指令可以误导反汇编,但不影响程序正常执行(运行时花指令位于不可执行的代码路径)。
反汇编算法主要可以分为两类:递归下降算法和线性扫描算法。
线性扫描算法扫描整个数据段,对每条指令进行反汇编,无法区分数据与代码,导致代码段的数据误解释为指令。
递归下降法强调控制流的概念,控制流根据一条指令是否被另一条指令引用来决定是否对其进行反汇编,遇到非控制转移指令时顺序进行反汇编,而遇到控制转移指令时则从转移地址处开始进行反汇编。
call机器码0E8h
nop机器码90h
retn先进行esp加n操作,而ret没有此项操作
先来个简单例子
visual studio 2022编译一下,exe用ida打开
#include<iostream>
using namespace std;
int main() {
int x;
_asm {//vc内联汇编插入花指令
jz label;
jnz label;
_emit 0xE8;//立即数_emit:表示在这插入一个数据
label:
}
cin>>x;
cout<<x;
system("pause");
return 0;
}
main函数一个明显的JUMPOUT
,应当是有花指令
按U,然后把E8
patch成90
,再按C转成code
在开头按p,再按F5
,反汇编成功
2. 常见类型
1. jx+jnx
jnz和jz的互补跳转
_asm{
jz Label;
jnz Label;
_emit 0xC7;//这行patch成90,然后按C
Label:
}
跳转指令构造花指令
__asm {
push ebx;
xor ebx, ebx;
test ebx, ebx;//zf为1,执行jz
jnz LABEL7;
jz LABEL8;//永真条件跳转
LABEL7:
_emit 0xC7;//显示为db,影响反汇编(p和F5失效)
//上面这行patch成90,然后按C
LABEL8:
pop ebx;
}
2. call+add [esp]+ret
call与ret构造花指令
__asm {
call LABEL9;//下一条"指令" db 0x83地址压栈
_emit 0x83;
LABEL9:
add dword ptr ss : [esp] , 8;//这行改了esp指向的栈顶数据
//003D186A+8=003D1872
ret;//但是ret从栈顶拿出数据给eip,所以能正常到mov esi,esp处运行
__emit 0xF3;
}
如下图,在爆红那一行按U,把83h patch掉,
然后看 0F3,8B,0F4 ->rep mov esi, esp,前面没有操作ecx寄存器,估计F3是花指令,patch掉。
然后成功报错,Options-General-Disassembly-Stack pointer,打开堆栈指针,没看出什么东西
把36h到C3h那一段全nop掉就行,然后在main开头按U,C,P,F5。
3. 等价解析
1. push pop
push 0x1111
在x86等价于
sub esp,0x4;
mov [esp],0x11111;
pop eax;
在x86程序里面就等价于
mov eax,[esp];
add esp,0x4;
//可以用多个pop代替add esp,0x4
2. jmp call retn
jmp addr
相当于
mov eip,addr
call addr
相当于
push next opcodeAddr; 一般4字节
jmp addr
call 0h
16进制为:E8 00 00 00 00
用于获取下一行地址,也可获取EIP的值
retn 0x5;
相当于
mov eip,[esp]
add esp,0x4+0x5
3. enter leave
enter 8,0
相当于
push ebp;
mov ebp,esp;
sub esp,8;
leave
相当于
mov esp, ebp;
pop ebp;
4. and
常用来栈对齐
16位对齐
and esp,0xfffffff0
MoeCTF 2022 chicken_soup
ida打开,找到main函数
loc_401000和loc_401080加了花指令,两处E9改成90
unsigned int __cdecl sub_401000(const char *a1)
{
unsigned int result; // eax
unsigned int i; // [esp+18h] [ebp-8h]
for ( i = 0; ; ++i )
{
result = strlen(a1) - 1;
if ( i >= result )
break;
a1[i] += a1[i + 1];
}
return result;
}
unsigned int __cdecl sub_401080(const char *a1)
{
unsigned int result; // eax
unsigned int i; // [esp+18h] [ebp-8h]
for ( i = 0; ; ++i )
{
result = i;
if ( i >= strlen(a1) )
break;
a1[i] = (16 * a1[i]) | ((int)(unsigned __int8)a1[i] >> 4);
}
return result;
}
sub_401110是个字符串比较函数
易得
c=[0xCD, 0x4D, 0x8C, 0x7D, 0xAD, 0x1E, 0xBE, 0x4A, 0x8A, 0x7D, 0xBC, 0x7C, 0xFC, 0x2E, 0x2A, 0x79, 0x9D, 0x6A, 0x1A, 0xCC, 0x3D, 0x4A, 0xF8, 0x3C, 0x79, 0x69, 0x39, 0xD9, 0xDD, 0x9D, 0xA9, 0x69, 0x4C, 0x8C, 0xDD, 0x59, 0xE9, 0xD7]
for i in range(len(c)):
c[i] = ( c[i]//16) | (c[i] << 4)&0xFF;#实际是交换高4位与低4位
for i in range(len(c)-2,-1,-1):
c[i]-=c[i+1]
print(bytes(c))
#moectf{p4tch_pr0gr4m_t0_d3c0mpi1e_it!}
参考
-
CTF逆向Reverse 花指令介绍 and NSSCTF靶场入门题目复现 znonono
-
逆向专辑–花指令阅读与分析 ZhaoWu