Re练习之花指令

debu8ger Lv3

一些概念的介绍

反编译器原理

从exe文件的入口AddressOfEntryPoint开始,依序扫描字节码,并将其转换为汇编代码。

比如第一个16进制字节码为0xE8(其一般代表CALL指令),且后面跟着的4个字节数据都与地址有关,则反编译器就读取该5个字节,而且反编译为CALL 0x地址

线性反编译

若反编译过程中只要一个地方反编译出错,例如两条指令之间多了0xE8,那么反编译器则会将其后跟着的4个字节处理为CALL指令地址相关数据给反编译成CALL 0x地址指令。

但实际上0xE8后面的四个字节为单独的字节码指令。

线性扫描

从入口开始,一次解析一条指令,遇到分支指令不会递归进入分支

递归下降

当使用线性扫描时,遇到calljmp时,不会跳转到对应地址进行反编译,而是反编译其后下一条指令。

递归下降分析,当遇到分支指令时,会递归进入分支进行反汇编。

基本介绍

花指令,从字面上看就是干扰正常程序内容阅读、反编译的一些代码块。是CTF中企图隐藏出题人不想被基础逆向操作所反编译出的一种方法,在正常代码之中加入垃圾代码,又可以保证原有程序正确执行。

花指令不会阻碍动态调试,但会加大了静态分析的难度

从出题人的角度看,构造有效花指令的思路就是构造使源程序逻辑不受影响的内联汇编代码,同时在内联汇编代码中嵌入jmpcallret之类的对应机器码,使反汇编器在反汇编时错误识别,影响正常流程。

典型的花指令一般是一些操作指令具有相同的操作数,但执行无作用的运算:

mov eax,eax;
xchg esp,esp;
jmp rva=0; //E9 00 00 00 00
xor eax,0; //任何数与0异或等于本身,相当于没运算

db thunkcode1; //垃圾数据

分类

可执行的花指令

指能够正常运行,不改变原始程序逻辑性以及任何寄存器的值、同时反汇编器等能正确反汇编的无用代码块。

这种花指令常常在病毒的变形引擎中(病毒在传播时通过其随机产生一组此花指令来改变特征码)

不可执行的花指令

指插入到原始代码中的能正常运行、不改变程序逻辑以及任何寄存器值、反汇编器会错误反汇编的无用字节块。

也是最常见、最常考的。

根据反汇编的原理,只有花指令同正常指令的开始几个字节被反汇编器识别成一条指令时,才能有效破坏反汇编,所以插入的花指令为不完整的指令

堆栈的平衡

pushpop指令都遵循出入栈规则后进先出

push ebp ----把基址指针寄存器压入堆栈
pop ebp ----把基址指针寄存器弹出堆栈
push eax ----把数据寄存器压入堆栈
pop eax ----把数据寄存器弹出堆栈
nop -----不执行(最常用)
add esp,1 -----指针寄存器加1
sub esp,-1 -----指针寄存器加1
add esp,-1 --------指针寄存器减1
sub esp,1 -----指针寄存器减1
inc ecx -----计数器加1
dec ecx -----计数器减1
sub esp,1 ----指针寄存器-1
sub esp,-1 ----指针寄存器加1
jmp 入口地址 ----跳到程序入口地址
push 入口地址 ---把入口地址压入堆栈
retn ------ 反回到入口地址,效果与jmp 入口地址一样
mov eax,入口地址 ------把入口地址转送到数据寄存器中.
jmp eax ----- 跳到程序入口地址
jb 入口地址
jnb 入口地址 ------效果和jmp 入口地址一样,直接跳到程序入口地址
xor eax,eax ------寄存器EAX清0
CALL 空白命令的地址 ------无效call

简单跳转

对以下汇编代码,很明显看得出,把ebx压入栈后,先对其进行xor,再进行test比较,zf标志位肯定为1,就一定执行jz LABEL8,也就是说明byte_401161之间的0xC7h永远不会执行。

要注意的是,保存ebx的值时先将ebx压入栈,即push ebx,最后得pop ebx把其pop出来。

train_re2

永真&永假

通过设置永真或永假,使程序一定会执行,由于IDA反汇编会优先反汇编false分支

也可以调用函数会返回确定值,来达到永真或永假条件。

train_re3

其中call指令的本质:push 函数返回地址,然后jmp 函数地址

ret指令的本质:pop eip

汇编代码里的esp存储的即为函数返回地址,对[esp] + 8,就是指函数的返回地址+ 8,同时也可以刚好盖过其中的函数指令&垃圾数据。

例题

一、[HZNUCTF 2023 final]虽然他送了我玫瑰花

拿到附件,拖到IDA中查看:

train_re4

可以从旁边的函数栏中看到并没有_main函数,推测有花指令阻碍了反汇编,

定位到_main函数的反汇编代码,框出来的就是典型的简单永真条件跳转

既然一定会执行jz LABEL,说明byte_401155中的0xC7h根本没执行

把涉及跳转的0x4011510x401156NOP掉,

train_re5

然后再对整个_main函数按p键重新创建函数,可以看到函数列表出现了_main函数

train_re6

F5反汇编,成功了!

_main函数

{
char v3; // bl
int i; // esi
int v5; // eax
char *v7; // [esp-8h] [ebp-100h]
char v8; // [esp+0h] [ebp-F8h]
_BYTE v9[100]; // [esp+Ch] [ebp-ECh]
__int128 v10; // [esp+70h] [ebp-88h]
int v11; // [esp+80h] [ebp-78h]
int v12; // [esp+84h] [ebp-74h]
int v13; // [esp+88h] [ebp-70h]
char v14; // [esp+8Ch] [ebp-6Ch]
char Arglist[100]; // [esp+90h] [ebp-68h] BYREF

sub_401020((char *)&Format, v8);
sub_401050("%s", (char)Arglist);
v11 = -171171450;
v12 = -669748952;
v13 = 1651994351;
v14 = -6;
v10 = xmmword_402170; //下断点
if ( strlen(Arglist) == 29 )
{
for ( i = 0; i < 29; ++i )
v9[i] = funcs_40117E[i % 5u](Arglist[i]);
v5 = 0;
while ( v9[v5] == *((_BYTE *)&v10 + v5) )
{
if ( ++v5 >= 29 )
{
sub_401020(v7, (char)"Congratulations!!\n");
return 0;
}
}
sub_401020(v7, (char)"try again\n");
}
else
{
sub_401020("wwwhhhaaattt???\n", v3);
}
return 0;
}

代码中v10代表的xmmword_402170即为密文,点进去,结合后面的长度29发现实际并不足29

.rdata:00402170 xmmword_402170  xmmword 6CE1CA267159DFE8247A4EFBCE517E7Fh

于是,动态调试

v10 = xmmword_402170;下断点,F8单步跳过,查看完整的xmmword_402170

train_re7

008FF704一直数29位到008FF720,即为完整的密文,右键点Convert提取数据:[0x7F, 0x7E, 0x51, 0xCE, 0xFB, 0x4E, 0x7A, 0x24, 0xE8, 0xDF, 0x59, 0x71, 0x26, 0xCA, 0xE1, 0x6C, 0x86, 0x21, 0xCC, 0xF5, 0x28, 0x71, 0x14, 0xD8, 0xEF, 0x6E, 0x77, 0x62, 0xFA]

回到汇编代码上,看到funcs_40117E对密文进行了一番操作

点开看有五个不同运算的函数,

因为是逆向,所以原来正的,现在解密,得反着函数来

train_re1

编写最终EXP:

enc_data = [0x7F, 0x7E, 0x51, 0xCE, 0xFB, 0x4E, 0x7A, 0x24, 0xE8, 0xDF, 0x59, 0x71, 0x26, 0xCA, 0xE1, 0x6C, 
0x86, 0x21, 0xCC, 0xF5, 0x28, 0x71, 0x14, 0xD8, 0xEF, 0x6E, 0x77, 0x62, 0xFA]
flag = ''

for i in range(len(enc_data)):
a = i % 5
if a == 0:
flag += chr(enc_data[i] ^ 0x19)
elif a == 1:
flag += chr(enc_data[i] - 18)
elif a == 2:
flag += chr(enc_data[i] + 16)
elif a == 3:
flag += chr((enc_data[i]>>1) & 0x7F)
else:
flag += chr(enc_data[i] ^ 0x80)
print(flag)

#flag: flag{Wh4t_@_6eaut1fu1_$lower}

To be continued

之前联合战队面试,遇到了一个花指令的题,还没完全搞透,所以直接爆零 :(

以后遇到了不同类型的,争取继续补上…

Comments