CTF逆向基础----花指令总结

什么是花指令?

花指令实质就是一串垃圾指令,它与程序本身的功能无关,并不影响程序本身的逻辑。在软件保护中,花指令被作为一种手段来增加静态分析的难度,花指令也可以被用在病毒或木马上,通过加入花指令改变程序的特征码,躲避杀软的扫描,从而达到免杀的目的,本文将介绍一些常见的花指令的形式,花指令一般被分为两类,被执行的和不会被执行的。

不会被执行的花指令

花指令虽然被插入到了正常代码的中间,但是并不意味着它一定会得到执行,这类花指令通常形式为在代码中出现了类似数据的代码,或者IDA反汇编后为jmupout(xxxxx).

这类花指令一般不属于CPU可以识别的操作码,那么就需要在上面用跳转跳过这些花指令才能保证程序的正常运行。

反汇编引擎主要有两种算法,一种是线性扫描算法,一种是递归行进算法。

线性扫描算法将遇到的每一条指令都解析成汇编指令,没有对反汇编的内容进行判断,因而无法正确区分代码和数据,一些数据也会被当成代码来解码,从而导致反汇编出现错误,这种错误将会影响对下一条指令的正确识别。

递归行进算法按照代码可能的执行顺序来反汇编程序,对每条可能的路径进行扫描,当解码出分支指令后,反汇编工具就将这么地址记录下来,并分别反汇编各个分支中的指令,这种算法比较灵活,可以避免将代码中的数据作为指令来解码。

形式一:

如果我们插入的花指令是一个操作码,那么后面程序原本的机器码就会被误认为是这个操作码的操作数,从而导致反汇编引擎的解析错误。

示例代码:

int main()
{
	_asm {
		xor eax, eax;
		jz s;
		_emit 0x11;
		_emit 0x22;
		_emit 0x33;//0x33是xor指令的操作码,会导致后面正常的Push指令被错误解析
	s:
	}
    printf("Hello World!\n");
}

_emit 指令为插入字节码

由于经过xor eax,eax后,ZF标志位被置为1,那么jz这条跳转指令必定会被执行,后面插入的0x11,0x22,0x33就会被跳过,程序正常输出Hello World!

我们把程序放到IDA中查看下花指令的干扰效果。

在这里插入图片描述
在这里插入图片描述

此时IDA已经无法正常解析这个函数了,由于我们实验代码是写在main函数中的还能找到这段代码,在实际做题的过程中,如果出题人把关键代码写在其他函数并添加类似花指令,那么我们就无法顺利找到关键函数,尝试下用搜索Hello World字符串通过交叉引用的方式定位代码,发现也是定位不到的。

在这里插入图片描述

对抗方法

在加了这类花指令的情况下,我们想要定位关键函数只能借助动态调试的方法,首先在OD的内存窗口中搜索到Hello World字符串(p.s:可能会出现多个相同的字符串,在C语言中常量字符串会被存放在rdata段,所以可以在这个范围内进行字符串的查找)
在这里插入图片描述

然后在该地址设置内存访问断点,运行程序后使用快捷键ctrl+F9进行堆栈回溯,即可定位到关键位置
在这里插入图片描述
针对一些字符串加密的情况,可以尝试在控制台的输入输出函数下断点,然后再进行堆栈回溯,本题可在ucrtbase.__stdio_common_vfprintf

函数设置断点

观察汇编代码可以发现从401043-40103F都属于花指令,我们可以用NOP指令对其进行填充后保存文件,然后再放到IDA中进行静态分析。
在这里插入图片描述

形式二:

插入的花指令也可以是改变堆栈平衡的汇编代码,跟形式一相同在这些花指令上面写上跳转指令,虽然花指令不会被执行,但是IDA进行解析时会认为该函数堆栈不平衡,从而使F5功能失效

示例代码;

int main()
{
	_asm {
		xor eax, eax;
		jz s;
		add esp, 0x11;
	s:
	}
	printf("Hello World!\n");
}

效果:

在这里插入图片描述

对抗方法同上,将造成堆栈不平衡的软件NOP掉即可。

会被执行的花指令

这类花指令本身是正常的汇编指令,它们运行完后不会改变原来程序的堆栈,寄存器,但能起到干扰静态分析的作用,一般分为两种,一种是改变堆栈操作,另一种是利用call指令或Jmp指令增加执行流程复杂度。

形式一:

示例代码:

int main()
{
	_asm {
		push eax;
		add esp, 4;

	}
	printf("Hello World!\n");
}

在32位下,push eax分为两个步骤,1.esp=esp-4 2.将eax值放入esp地址中,正常情况下,push操作需要对应一个pop操作来保持堆栈的平衡。

这里后面跟着的add esp,4起到了pop 指令的部分功能,也就是恢复了堆栈的平衡,使得程序能够正常运行。

但是在IDA中却无法正常识别这种操作。
在这里插入图片描述

形式二:(2018网鼎杯_give_a_try)

利用call指令来增加程序执行流程的复杂度,我们知道,执行call执行时会向堆栈中压入返回地址,我们可以修改这个返回地址,配合ret指令跳转到任意一个我们想去的地方,这里具体例子位2018网鼎杯的一道题,本文仅分析花指令部分
在这里插入图片描述

程序入口有该花指令,在402006位置有个call指令,call到了call指令所在的下一行,然后add [esp],0x17

也就是将返回地址加了0x17然后ret 原先的返回地址应该为40200B 加上0x17后为402022,我们跳转过去看下

在这里插入图片描述

后面代码也很乱是因为后面有相同做法的花指令。像这种花指令在程序中多次出现,但是他们都有相似的特征,靠手动清除需要耗费大量的时间,所以可以通过编写脚本来清楚这些花指令。

编写IDC脚本清除花指令

IDC脚本是IDA的一项功能,他的语法与C语言类似所以叫IDC,另外还有一种IDAPYTHON使用的是pyhton的语法,具体可以参考IDA权威指南,这里介绍下编写IDC脚本清除花指令需要用到的一些函数。

首先是一个编写IDC脚本的框架

#include <idc.idc>

static main()
{
    
    
}

在IDC脚本中,变量的声明使用的是auto关键字,例如:auto i=0;

for,if语句等语法与C语言相同,值得注意的是在IDC脚本中不支持+=;++等写法,需要老老实实的写全。

PatchByte(地址,内容)

Byte(地址)

IDC中提供了以上两个函数,第一个是修改字节,Byte是指定修改大小,同理还有PatchWord等等

Byte()是向地址中读取一个字节,Word就是读取双字节…

了解了这些后就可以写一个简单的脚本来清除花指令了,具体的代码就不贴了,等大家遇到花指令时动手编写下吧。

总结

花指令相对来说还是比较容易处理的,需要对堆栈变化,函数调用等知识有一定的了解,才能分辨出哪些是花指令,然后做出相应的处理,如果不太确定的话就可以配合动态调试来进行分析,一般来说一个程序中的花指令都是属于同一种,提取他的特征后就可以通过编写脚本来清除花指令,还原出程序本身的面目了。

  • 18
    点赞
  • 74
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值