花指令
花指令又称脏字节,英文为"junkcode",顾名思义,即在程序中加入的一些垃圾指令,其目的是在不妨碍原有程序执行的前提下,阻碍程序反编译,增加静态分析难度,隐匿不想被逆向分析的代码块,混淆代码,绕过特征识别。本文将常见的花指令进行归纳总结,作学习记录。
文章目录
一、不可执行式花指令 1.多字节指令 2.破坏堆栈平衡 二、可执行花指令 1.利用函数调用 2.混淆特征码花指令可分为两大类:可执行花指令和不可执行花指令,具体区分如下:
一、不可执行式花指令
不可执行花指令指这部分花指令在程序执行过程中不会被执行,它是利用反汇编器 静态分析算法的缺陷 使得代码在反编译解析时出错。其原理使反汇编分析执行流命中执行会出错的垃圾数据,就会造成解析错误,而实际执行过程中垃圾数据并不会执行。反汇编器静态分析算法一般有两种:
一是线性扫描,从程序入口处依次读取机器码并进行反汇编,逐行命令进行线性扫描,在于在冯诺依曼体系结构下,无法区分数据与代码,从而导致将代码段中嵌入的数据误解释为指令的操作码,采用线性扫描技术的反汇编工具如OD、Windbg; 二是递归下降,从程序入口开始读取机器码进行反汇编,通过程序的控制流确定反汇编的下一条指令,遇到无条件跳转则从跳转目的地址处继续解析,遇到条件跳转则从两个命令执行分支处进行解析 (优先解析顺序执行分支),即采用模拟程序运行的方式增加反汇编的准确度,采用递归下降反汇编的如IDA。不同的调试环境采用的反汇编分析算法不同,导致加花效果也是不同的,这里实验环境主要为IDA Pro。在设计这类花指令时要通过构造 必然条件 或者 互补条件,使得程序在实际执行时绕过垃圾数据,这样不会影响程序正常执行,以下是常见的花指令形式:
1.多字节指令
在条件跳转指令之后插入垃圾数据,这些数据是多字节指令的机器指令,产生的根本原因是x86指令集指令长度不定,通过跳转使反汇编分析执行流命中到另一条指令的中间时就会造成解析错误。由于IDA采用递归下降算法,其会优先分析顺序执行的条件分支,从而将插入数据后的指令与数据相混淆,导致反汇编分析失败。如下插入数据:
jz Label // 采用条件互补的方法跳转绕过垃圾数据 jnz Label junkcode Label: ...
其中 junkcode 可以是任何的多字节指令的硬编码,常用的脏字节编码如下:
call immed16 ----> E8 // 3字节指令,immed16为2字节,代表跳转指令的目的地址与下一条指令地址的距离 call immed32 ----> 9A // 5字节指令,immed32为4字节,代表跳转指令的目的地址与下一条指令地址的距离 jmp immed8 ----> EB jmp immed16 ----> E9 jmp immed32 ----> EA loop immed8 ----> E2 ret ----> C2 retn ----> C3
下面以最简单的一段代码为例,在输出字符串之前插入花指令:
注意:不同的编译环境插入汇编指令的格式不同,这里以Visual Studio为例
#include "stdio.h" int main(int argc, char const* argv[]){_asm{jz labeljnz label_emit 0xe9label:}printf("Hello world!n");return 0; }
出现这类花指令的特征是 程序会跳转到指令执行的中间位置,如下所示:patch方法是将00401047位置代码转换为数据 (D) 或者在Edit -> Patch program -> Change byte选项中改为NOP (0x90),然后从00401047位置代码开始转换为代码 ©:
可能连续嵌套多层类似的指令,手动patch逐层将其修正即可,如果太复杂则需要使用idc和idaPython脚本。
注意,如果插入汇编指令的位置条件跳转指令换成无条件跳转jmp,则会发现IDA反汇编不会发现错误,这是因为其采用递归下降的分析方法,分析过程针对无条件跳转指令会直接跳转到目的地址继续进行解析。
2.破坏堆栈平衡
汇编中函数如果有参数或局部变量,在调用前会对堆栈进行保护 ,在返回前要还原函数调用前的堆栈,这一过程程序在编译时会自动加上,如果反编译器检测到指令破坏了堆栈平衡,即函数返回时与调用时堆栈状态发生了变化,就会报错。可以利用这一点构造破坏堆栈平衡的花指令,如下代码:
#include "stdio.h" int main(int argc, char const* argv[]){_asm{test eax,0 // 构造必然条件实现跳转,绕过破坏堆栈平衡的指令jz labeladd esp,0x1label:}printf("Hello world!n");return 0; }
如下图所示,这类破环堆栈平衡的指令实际不会执行,但是由于IDA在反汇编分析时会分别从两个条件跳转处开始分析,因此判定堆栈不平衡导致反汇编失败,解决方法是NOP掉破坏堆栈平衡的指令。
二、可执行花指令
花指令在程序执行过程中会被执行,但执行这些代码没有任何意义,执行前后不改变任何寄存器的值,也不改变程序执行逻辑和结果,目的是加大静态分析的难度,或是混淆特征码,绕过特征检测。
1.利用函数调用
函数调用时将调用指令的下一条指令地址压栈,然后跳转到函数位置执行,相当于:PUSH 下一条指令地址、MOV EIP,函数位置,相应地,函数返回时将函数调用时的压栈地址恢复给EIP,相当于POP EIP。
可以利用函数调用和返回的原理构造花指令,如下例代码利用CALL&RET指令构造花指令:
#include "stdio.h" int main(int argc, char const* argv[]){_asm{ call label label: add [esp],5 ret}printf("Hello world!n");return 0; }
如下图所示,之所以分析失败是因为IDA检测出堆栈操作破坏堆栈平衡,而这正是构造者有意为之,只有改变了返回地址,函数返回后才能正常向下执行,至于栈顶的返回地址要加多少,这取决于跨过语句的长度,如下所示,需要构造RET返回的位置和正常RET返回的位置相差5个字节,因此栈顶数据加上5个字节:patch方法也很简单,直接将调用语句和改变堆栈平衡的语句NOP掉,或者将调用语句修改成跳转语句。
根据函数调用的原理可以灵活构造花指令,不一定使用CALL和RET组合,如下命令:
#include "stdio.h" int main(int argc, char const* argv[]) { _asm { push eax push ecx jmp label_1 label_2: mov eax,[esp] add [esp],8 jmp eax label_1: call label_2 pop eax } printf("Hello world!n"); return 0; }
通过压栈、退栈和跳转混淆代码,IDA将退栈指令识别为破坏堆栈平衡而报错,如下所示:识别这类花指令后,将跳转和无效压栈等操作指令全部NOP掉即可。
2.混淆特征码
这种类别的花指令组合形式很多,用于混淆木马、病毒的特征码,躲避AV查杀,不一定会造成反汇编失败,但是会对反汇编分析造成干扰。
如下一些指令功能可以用替代指令完成,目的即增加反汇编分析的复杂程度:
mov op1,op2 ----> push op2 / pop op1 jmp label ----> push label / ret call label ----> push label_next_instruction / push label / ret push op ----> sub esp,4 / mov [esp],op
一些AV查杀引擎在可执行文件一定偏移范围内进行扫描查杀,在扫描区段加入一些花指令,使恶意代码偏离引擎识别的偏移范围,再使用工具修改程序入口 (OEP),就可以逃避这种方式的查杀。
相关知识
花指令的原理、常用花指令收集及花指令示例
android 花指令防止反编译 花指令原理
android 加花指令 花指令的作用
花指令总结
给dll加花指令
花指令及反混淆
用花指令优化花卉生产管理
花指令在医疗健康应用研究
Reverse花指令及反混淆
【逆向学习】花指令的去除
网址: 花指令 https://www.huajiangbk.com/newsview1535313.html
上一篇: 蜜蜂快速发现花朵之谜揭开 |
下一篇: 五年级科学上册(大象版)第2.1 |
推荐分享

- 1君子兰什么品种最名贵 十大名 4012
- 2世界上最名贵的10种兰花图片 3364
- 3花圈挽联怎么写? 3286
- 4迷信说家里不能放假花 家里摆 1878
- 5香山红叶什么时候红 1493
- 6花的意思,花的解释,花的拼音 1210
- 7教师节送什么花最合适 1167
- 8勿忘我花图片 1103
- 9橄榄枝的象征意义 1093
- 10洛阳的市花 1039