Time: 2019.02.19
Tags: 逆向分析
在逆向工程的工作中,往往会遇到花指令以抵抗逆向分析,增加逆向分析的难度。近期在分析的一个恶意软件中,使用了非常多、非常复杂的花指令,借此机会对花指令进行一个整理,便于以后方便的使用。
本文针对 80x86 汇编,对该指令集下的常用花指令进行了分类示例,不涉及编程以及自动加花/去花。
junk code
花指令(Junk Code)又称为垃圾代码,程序开发者在完成开发后,在程序中插入大量的花指令,以抵抗逆向分析;这些花指令不会修改到程序的逻辑,但是可以扰乱逆向工具的自动化分析,比如导致反汇编引擎无法正确解析指令、IDA Hex-rays 无法正常工作等。
分类
花指令可以分为两个大类:1.无效指令(or组合),2.跳转指令控制执行流程。
无效指令:加入的花指令虽被执行,但不会修改程序的的逻辑。
example:
单条指令:
[1]add edi, 0 83 C7 00
[2]jg $+2 7F 00
也有组合形式的:
[3]push eax 50
pop eax 58
无效的花指令,会增加逆向分析者的工作量,扰乱其分析思维;其次还会导致 IDA Hex-rays 无法正常工作。
跳转指令控制执行流程:花指令由跳转指令和垃圾数据构成,可以任意打乱原指令在文件中的顺序,由跳转指令进行串联,仍然不会修改程序的逻辑。
example:
(ds:dword_1001BE5C = 0)
[1]83 3D 5C BE 01 10 00 cmp ds:dword_1001BE5C, 0
[2]74 05 jz short loc_10042F25
[3]E9 06 46 00 00 jmp loc_1004752B
loc_10042F25:
[4][....] 其中 `dword_1001BE5C = 0`,`jz` 指令将永远都会进行跳转,那么第三条指令 `jmp` 就是一条垃圾数据,永远不会被执行到。所以第三条指令的 5 个字节可以任意修改,如果精心填充数据,则可以影响反汇编引擎的工作。
这类形式的花指令,如果精心构造垃圾数据,即可导致反汇编引擎发送错误,从而无法显示正确的汇编指令,或直接显示原始数据;增加逆向难度。
对于无效指令,按照指令的类型进行以下分类汇总(以 eax
为主要例子)。
1.无效跳转
使用近跳转,跳转至该行指令的下一句指令,那么无论该行指令是否执行,都将执行下一句指令。
[1]eb 00 jmp $+2
[2]67 e3 00 jcxz $+3
[3]e3 00 jecxz $+2
[4]74 00 je $+2
[5]75 00 jne $+2
[6]78 00 js $+2
[7]79 00 jns $+2
[8]70 00 jo $+2
[9]71 00 jno $+2
[10]77 00 ja $+2
[11]76 00 jbe $+2
[12]73 00 jae $+2
[13]72 00 jb $+2
[14]7f 00 jg $+2
[15]7e 00 jle $+2
[16]7d 00 jge $+2
[17]7c 00 jl $+2
[18]7a 00 jp $+2
[19]7b 00 jnp $+2
......
2.无效算数指令
执行运算指令但没有修改值。
[1]83 c0 00 add eax, 0
[2]83 e8 00 sub eax, 0
[3]f6 e3 mul bl
[4]f6 f3 div bl
[5]c1 e0 00 shl eax, 0
[6]c1 f8 00 sar eax, 0
[7]c1 e8 00 shr eax, 0
[8]c1 c0 00 rol eax, 0
[9]c1 c8 00 ror eax, 0
[10]c1 d0 00 rcl eax, 0
[11]c1 d8 00 rcr eax, 0
[12]c1 c0 20 rol eax, 0x20
......
3.无效算数指令(组合)
由多条算术语句组成,运行完该组合的所有指令后,没有发生值的改变。除此之外,还可以在组合指令之间添加其他花指令。
[1]83 c0 01 add eax, 1
83 e8 01 sub eax, 1
[2]83 f0 11 xor eax, 0x11
83 f0 11 xor eax, 0x11
[3]40 inc eax
48 dec eax
[4]c1 c0 02 rol eax, 2
c1 c8 02 ror eax, 2
[5]83 c0 08 add eax, 8
83 e8 04 sub eax, 4
83 e8 04 sub eax, 4
......
4.无效指令
和「算术无效指令」一样,执行运算指令但没有修改值。
[1]90 nop
[2]89 c0 mov eax, eax
8b 24 24 mov esp, [esp]
[3]85 c0 test eax, eax
......
5.无效指令(组合)
和「算术指令组合」一样,运行完该组合的所有指令后,没有发生值的改变。并且同样可以在组合之间添加其他的花指令。
[1]50 push eax
58 pop eax
[2]66 9c pushfw
66 9d popfw
[3]93 xchg eax, ebx
93 xchg eax, ebx
......
反汇编引擎有两种工作方式,线性扫描算法(以OD为代表)和递归下降算法(以IDA为代表)。
线性扫描算法逐个解析数据并翻译为指令,由于不能解析程序执行流而很容易将垃圾数据翻译为指令,并且后续的工作都将发生错误。
递归下降算法为了解决线性扫描算法的这个问题,参考 CPU 执行指令的原理,解析其中部分的程序流控制指令(如跳转),并进行跳转翻译。
递归下降算法没有进行所有指令的模拟执行,所以无论是那种算法都没有完全解决花指令的问题;递归下降算法表现得稍好一些。
就如「1.junk code简介」中的示例,这里同样也给个示例,并演示反汇编引擎解析错误的效果。
在语句中插入跳转花指令和垃圾数据:
[...]
mov eax, 0x0a
test eax, 0
jnz A
db 0xeb
db 0x12
db 0x34
A:
mov eax, 0x10
[...]
使用 IDA 查看反汇编的结果:
.text:08000000 _text segment para public 'CODE' use32
.text:08000000 assume cs:_text
.text:08000000 ;org 8000000h
.text:08000000 assume es:nothing, ss:nothing, ds:_text, fs:nothing, gs:nothing
.text:08000000 dd 0AB8h, 0A900h, 3750000h
.text:0800000C db 0EBh, 12h, 34h
.text:0800000F ; ---------------------------------------------------------------------------
.text:0800000F
.text:0800000F A:
.text:0800000F mov eax, 10h
.text:0800000F _text ends
如此的语句,可以自由的进行构造,精心设计垃圾数据,就可以使得反汇编引擎发生很多错误,这里不过多举例了。
目前在 OD 中有辅助插件可以处理简单的花指令,但在实际运用中并不是很理想;花指令的构造方式非常随性,组合无穷,目前没有统一的自动化处理工具。
solving
在遇到花指令时,大多情况都需要对花指令进行详细的分析,在摸清楚规律后,自行编写脚本进行去花。
在花指令较少的情况下,也可以直接手工去花,借助反汇编引擎进行分析,对无效指令进行 nop
,并按需修改跳转指令,最后保存到文件即可。
本文大致举例介绍了常用的花指令,在以后逆向分析过程中,可以按照如此的方式,对程序中出现的花指令进行分类总结,以便于编写自动化处理脚本。初次之外,在某些需要花指令的场合,也可以按照本文的示例,进行扩展,形成花指令库,插入程序中以抵抗逆向分析。