抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

花指令的作用是用来隐藏反汇编后的代码,对于动态调试来说无法隐藏,但是对于静态分析来说就成为必须绕过去的一个问题。

花指令如何实现代码隐藏

花指令能成功干扰反编译器识别代码,主要是与反编译器的实现技术有关。主流反编译器使用两种方法进行反汇编操作,分别是线性扫描和递归下降。

线性扫描

之所以叫线性扫描,就是说像一条线一样从头扫到尾。线性扫描反汇编从一个代码段的第一个字节开始,以线性模式扫描整个代码段,逐条反汇编每条指令,直到完成整个代码段

  • 优点:能够完全覆盖程序的所有代码段
  • 缺点:没有考虑到代码中可能混有数据

递归下降

递归下降反汇编强调控制流的概念。根据一条指令是否被另一条指令引用来决定是否进行反汇编。

  • 优点:它具有区分代码与数据的强大能力。作为一种基于控制流的算法,它很少会在反汇编过程中错误地将数据值作为代码处理。
  • 缺点:它无法处理间接代码路径,如利用指针表来查找目标地址的跳转或调用。

而花指令就是利用这两种方法的缺点来实现。我觉得大家反编译的工具一般是IDA Pro,在设计这类花指令时要通过构造 必然条件 或者 互补条件,使得程序在实际执行时绕过垃圾数据,这样不会影响程序正常执行

常见花指令

相同目标的跳转指令

在反汇编过程中遇到 jz(跳转到目标地址为零的情况下执行)和 jnz(跳转到目标地址不为零的情况下执行)指令时,如果它们的目标地址相同,那么程序实际上就是要执行一个无条件跳转,相当于 jmp 指令。然而,IDA Pro 在反汇编时可能会误将 jnz 后面的指令也反汇编出来,尽管实际上这些指令并不会被执行,因为程序在遇到 jnz 指令时已经跳转了。这种情况下,如果后面紧跟着的是一些字节指令(如 calljmp 指令),IDA Pro 可能会将它们错误地解释为与 jnz 相关的指令,导致反汇编结果出现问题。

例如假设原始机器码如下

1
2
3
4
5
74 03    ; jz 目标地址为当前地址 + 3
75 01 ; jnz 目标地址为当前地址 + 1
E8 58 ; call 目标地址为当前地址 + 58
C3 ; ret
90 90 ; nop

这里的call指令即为无用数据,只需要将call指令转为data数据再反编译即可

固定条件的跳转指令

当条件满足时,相应的判断语句后面的代码将会执行,而当条件不满足时,与之对应的代码则不会执行。如果设置了一个永真语句,后面的代码一定会执行,若是将要执行的代码后继续跟无条件跳转语句,ida则会识别出错。

例如

1
2
3
4
33 C0    ; xor eax, eax
74 01 ; jz 目标地址为当前地址 + 1
E9 58 ; jmp 目标地址为当前地址 + 58
C3 ; ret

在这个例子中xor结束zf标志位一定会被置1,jz跳转成立,而jmp则不会执行。更改方法是将jmp 这行代码nop或改为data

函数跳转

函数调用时将调用指令的下一条指令地址压栈,然后跳转到函数位置执行,相当于:PUSH 下一条指令地址MOV EIP,函数位置,相应地,函数返回时将函数调用时的压栈地址恢复给EIP,相当于POP EIP。如果破环了其中的堆栈平衡,ida就会报错。

例如:

1
2
3
4
call label		;调用label函数
label: ;函数体
add [esp],5 ;栈顶加5
ret ;返回

至于栈顶的返回地址要加多少,这取决于跨过语句的长度。此处构造RET返回的位置和正常RET返回的位置相差5个字节,因此栈顶数据加上5个字节