0%

花指令实现与对抗

花指令(junk code)是一种专门用来迷惑反编译器的指令片段,这些指令片段不会影响程序的原有功能,但会使得反汇编器的结果出现偏差,从而使破解者分析失败。比较经典的花指令技巧有利用 jmpcallret 指令改变执行流,从而使得反汇编器解析出与运行时不相符的错误代码。

—CTF Wiki

简单来讲花指令分为两大类,一种是会被执行的花指令,另一种是不会被执行的花指令。

花指令出现的意义就是干扰机器进行反汇编,迫使人工进行分析。

不会被执行的花指令

1
2
3
4
5
6
7
8
9
10
11
12
#include<stdio.h>
int main()
{
asm (
"xor %eax,%eax;"
"jz s;"
"add $0x11,%esp;" //让ida误认为栈不平衡,实则根本没有执行
"s:;"
);
printf("hello,junkcode\n");
return 0;
}

内联汇编的语法,语句放在()内,每条指令被””包裹,以分号结尾,如果要使用寄存器则要在寄存器前加上**%,如果使用立即数则在前面加上$**。

可以看到上方爆红。在汇编界面,直接将红框里的内容nop掉

修复后如下

可以执行的花指令

破坏栈

1
2
3
4
5
6
7
8
9
10
11
12
13
#include<stdio.h>
int main()
{
asm (
"call next;\n"
"next:\n"
"movl $continue,(%esp);\n"
"ret;\n"
"continue:\n"
);
printf("hello,junkcodes\n");
return 0;
}

内联汇编的内容实际上就是使用call进行跳转,实现的效果就是跳转到continue打印hello,junkcodes。这样为什么能使ida异常呢?我的理解是:call可以看作是push ip + jmp 指令,ret可以看作是pop ip指令,第一个call next在我们看来相当于一个简单的jmp跳转到了下一条指令,而ida肯定会将它当作函数进行分析,函数内执行了**movl $continue,(%esp);**这样一个操作,将continue的地址放入了esp寄存器中,然后ret指令将IP设为continue的地址,于是顺利执行printf指令。

也就是在ret指令执行过后,esp并没有复原,但是不影响程序运行,这就导致ida认为堆栈不平衡,导致不能反汇编。我们直接将这样的垃圾指令nop掉即可

然后f5一下即可

代码从之前的

变为了

参考:https://nnnewb.github.io/blog/p/learning-packer-07/

插入数据

在vs上,选择x86,编译

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include<stdio.h>
int main()
{
_asm
{
call sub7
_emit 0xE8
jmp label7
sub7 :
add dword ptr[esp], 1
retn
label7 :
}
printf("hello,world");
}

这个emit相当于汇编中的db,作用是在当前位置直接插入数据,而我们插入的0xE8是call指令的机器码,于是紧挨着的几个字节会被当作地址处处理,这会导致反汇编的失败。

这段代码的逻辑:调用sub7,执行add dword ptr[esp], 1,给[esp]+1,而【esp】保存的是call下面一条指令也就是_emit 0xE8的地址,经过这样的操作,在执行retn语句的时候,不会返回_emit 0xE8而是直接执行下面的jmp label7,于是程序正常运行。

直接把红框部分nop掉,然后在这个位置按下U也就是undefine

然后再按下c,将数据解析为代码,按下p创建函数,最后F5即可。

赏心悦目

话有一种方法是将E8后面的数据给还原,源代码我们知道,E8后面是一条跳转指令,而不应该像现在这样被解析为数据,在此处按下U

光标放在这个位置按下C

得到

woc,又不行了(刚才不知道为什么阴差阳错这种办法也行),无语了。。。