https://bbs.kanxue.com/thread-279604.htm#msg_header_h2_5

https://blog.csdn.net/abel_big_xu/article/details/117927674

花指令一直是逆向的一个比较常见而多变的东西,对于逆向人员来说也是一个比较大的阻碍,随着反编译工具的升级,现在的IDA9.0已经可以忽视一些花指令正常反编译,不过对于逆向人员还是要掌握

下面我用原理->形式->去除来讲解CTF比赛中经常的花指令

原理

花指令的核心原理是通过在代码中插入不影响程序实际行为的指令,来迷惑分析者或调试器。它们一般不执行有用的操作,但能让程序看起来更复杂或者改变程序的行为,从而导致逆向工程的困难。

也就是IDA在反编译程序的时候是从函数的入口至上而下一一把机器码转换成汇编语言再变成c语言程序

形式

永真永假跳转

这种花指令可以说是最基础也是最常见的花指令,

#include <stdio.h>

void dummy_function() {
// 这个函数仅作为花指令的目标
printf("This is a dummy function.\n");
}

int main() {
int x = 0;
__asm {
xor eax, eax
test eax, eax
jz target_label
jnz target_label
_emit 0xe8


target_label:
call dummy_function
}
return 0;
}

如以上代码,这里正常运行是不会接触到第15行代码,而我们说了IDA的线性扫描,自然会把0xe8当作机器码反编译,这个时候IDA就会看不懂了,就会出现这里的爆红

去除方法

既然会错误的把junkcode反编译,那我们的应对措施自然就是删除这个机器码了,在FA那个标签出先按U,转化成无定义,再把0E8h指令的下面按C,最后删除nop掉这个0E8h就行,记得在函数开头按P正常反编译为函数就行

永真永假跳转2

#include <stdio.h>

void dummy_function() {
// 这个函数仅作为花指令的目标
printf("This is a dummy function.\n");
}

int main() {
int x = 0;
_asm {
test eax, 0 // 构造必然条件实现跳转,绕过破坏堆栈平衡的指令
jz label
add esp, 0x1
label:
call dummy_function
}
return 0;
}

这个test eax,0

将寄存器 eax 和常数 0 进行按位与运算,但由于与 0 按位与的结果总是 0

所以会直接跳转到label处,不会执行这个add esp,0x1

去除方法

其实这种花指令都不需要怎么去除,一般现在IDA都会自己识别出来,当然,也可以直接删除它绕过的部分

call&ret

_asm{
call label
label:
add [esp],5
ret
}

这里用极客大挑战的CPP-flower为例

可以看见,这里的call419DA6,会跳到这个函数,而且这个函数没有做其他的操作,

仔细观察这里,esp+4是在返回地址处,也就是call指令的下面的地址,之后这个值+12,说明返回地址加了12h,这里手动计算一下,本来的返回地址是

00419DA1,现在变成了00419DB3,所以正常流程就是这样

去除方法

因为返回地址直接跳到了B3地址,所以我们直接nop掉9C-B2的全部指令就行

这里可以看出红色的地址没有连接上,这样直接去函数开头按u,c,p的话就会只能一半,这里的办法是删除这个sub_419DA6函数

点击delte function就可以了

二次计算

我自己取的名字,这种花是比较难的了,有幸在VN赛题里面看到过

普通的返回值,这个背景是初始化函数,我一直绕不过反调试,所以去找的初始化函数,但是点开汇编却另有玄机

直接爆红了,有反调试

像这种call $+5指令的意思就是跳转到call的下一条指令去执行,并且把下面的地址压入栈中当返回地址,这里前面还有push eax 和pop eax

去除方法

所以直接nop掉这个push 到pop

之后正常反编译出来了,这里插一个细节,如果想要修改指令去去除反调试的话直接

Apply patches to 保存就可以了。

花指令+异常

这里分析了之后发现如果是调试的话就会直接jmp eax,如果没有的话就是先加再jmp

像这种花的话是会影响程序运行的,直接看懂,能调试就行了

堆区过大

参考文章

https://hex-rays.com/blog/igors-tip-of-the-week-147-fixing-stack-frame-is-too-big

这种的代码,入栈的数据太大了,考虑去代码里面找找这个参数,考虑删掉,因为堆区太大影响力编译,当然,也有可能是因为中间在删掉花指令的时候误删了导致的

idapython自动去除花指令

如果一个程序花指令过于多的花,就可以尝试idapython直接手动去除

import idc
import ida_bytes

begin = xxxxxx
end = xxxxxx

for i in range(begin, end):
if idc.get_wide_byte(i) == 0x74: #修改为机器码
if idc.get_wide_byte(i + 1) == 0xFA:
ida_bytes.patch_byte(i, 0x90)
print("junkcode noped at address: 0x{:X}".format(i))

print("done")