由简到难解析花指令
花指令
本文重点标题:
- 原理—反编译器的线性反编译(理解花指令的重点)※
- 原理—对于出题人
- 栈指针平衡(引子)
- 花指令分类—进阶花指令(自定义花指令)
- 花指令分析实操
- 花指令练习※
- 编写脚本自动化去除花指令
前言:因为备课的时间比较短,笔者之前又没有系统学习整理过花指令,所以该篇略微混乱。但对于初学者来说仍是很好的阅读资料,笔者下次讲课时会再仔细整理的有条理一些。
对初学者说的话:时间比较紧的初学者建议阅读完原理直接转到文中较为偏后的花指令分类及花指令练习阅读,当然还是建议完整阅读完本文,你会有很大的收获。
对出题人说的话:对于像我一样只会做题不会出题的师傅,建议直接阅读文中较为偏后的花指令练习部分,里面重点阐述了如何由编写含有花指令的程序到反汇编分析花指令程序,并且该部分含有些我自己的一些出题的理解和思路及一些对疑问的解答,阅读完该部分后你也可以完成独立出题的工作。
为了使目录稍微完整,笔者在花指令练习里已经出现过的IDC脚本自动去花又加在了后面的目录中,意图是使脚本自动去花更为醒目一些防止读者学习时的遗漏。
关于指令类型推荐阅读如下两篇
https://blog.csdn.net/abel_big_xu/article/details/117927674
https://blog.csdn.net/m0_46296905/article/details/117336574
概念
花指令是企图隐藏掉不想被逆向工程的代码块(或其它功能)的一种方法, 在真实代码中插入一些垃圾代码的同时还保证原有程序的正确执行, 而程序无法很好地反编译, 难以理解程序内容, 达到混淆视听的效果。
花指令通常用于加大静态分析的难度。
原理
反编译器的线性反编译(理解花指令的重点)
反编译器的工作原理是,从exe的入口AddressOfEntryPoint处开始,依序扫描字节码,并转换为汇编,比如第一个16进制字节码是0xE8,一般0xE8代表汇编里的CALL指令,且后面跟着的4个字节数据跟地址有关,那么反编译器就读取这一共5个字节,反编译为CALL 0x地址
。
对应的,有些字节码只需要一个字节就可以反编译为一条指令,例如0x55对应的是push ebp
,这条语句每个函数开始都会有。同样,有些字节码又需要两个、三个、四个字节来反编译为一条指令。
也就是说,如果中间只要一个地方反编译出错,例如两条汇编指令中间突然多了一个字节0xE8,那反编译器就会将其跟着的4个字节处理为CALL指令地址相关数据给反编译成一条CALL 0x地址
指令。但实际上0xE8后面的四个字节是单独的字节码指令。这大概就是线性反编译。
线性扫描和递归下降
线性扫描:
线性扫描的特点:从入口开始,一次解析每一条指令,遇到分支指令不会递归进入分支。
递归下降:
当使用线性扫描时,比如遇到call或者jmp的时候,不会跳转到对应地址进行反汇编,而是反汇编call指令的下一条指令,这就会导致出现很多问题。
递归下降分析当遇到分支指令时,会递归进入分支进行反汇编。
使反汇编引擎解析错误
X86指令集的长度是不固定的,有一些指令很短,只有1个字节,有些指令比较长,可以达到5字节,指令长度不是固定的。如果通过巧妙的构造,引导反汇编引擎解析一条错误的指令,扰乱指令的长度,就能使反汇编引擎无法按照正常的指令长度一次解析邻接未解析的指令,最终使反汇编引擎输出错误的反汇编结果。
机器码
0xE8 CALL 后面的四个字节是地址 0xE9 JMP 后面的四个字节是偏移 0xEB JMP 后面的二个字节是偏移 0xFF15 CALL 后面的四个字节是存放地址的地址 0xFF25 JMP 后面的四个字节是存放地址的地址
0x68 PUSH 后面的四个字节入栈 0x6A PUSH 后面的一个字节入栈
对于出题人
从出题人的角度来看,构造有效花指令的关键思路就是构造使源程序逻辑不受影响的内联汇编代码,同时在内联汇编代码中嵌入jmp call+ret之类的对应机器码指令,使反汇编软件在反汇编时错误地识别这些机器码为汇编指令,从而影响反汇编出来的程序的正常流程。
写花指令的原则
保持堆栈的平衡
常用指令含义
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
栈指针平衡(引子)
当使用IDA分析伪代码时,有花指令会发生