逆向分析基础 --- 花指令实现及清除

一、基本概念

花指令:目的是干扰ida和od等软件对程序的静态分析。使这些软件无法正常反汇编出原始代码。

常用的两类反汇编算法:
1.线性扫描算法:逐行反汇编(无法将数据和内容进行区分)
2.递归行进算法:按照代码可能的执行顺序进行反汇编程序。

简单的花指令 0xe8是跳转指令,可以对线性扫描算法进行干扰,但是递归扫描算法可以正常分析。
在这里插入图片描述
两个跳转一个指向无效数据,一个指向正常数据来干扰递归扫描算法。
在这里插入图片描述

二、花指令实现

这里的实验基础代码是参考安全客上一个师傅代码进行的

//源码
#include<stdio.h>
#include<windows.h>

int main(int argc, char* argv[])
{

	int a = MessageBox(NULL,"Hello","main",MB_OK);
	int b,c,d,e;
   //这部分为花指令部分
	__asm{_emit 0xe8}
   //花指令结束
	b=1;
	c=2;
	d=3;
	e=4;
	return 0;
}

1.简单jmp

这是最简单的花指令。OD还是能被骗过去。但是因为ida采用的是递归扫描的办法所以能够正常识别

__asm{
    jmp label1
    db junkcode
label1:    
}

在这里插入图片描述

2.多层跳转

本质上和简单跳转是一样的,只是加了几层跳转。显然无法干扰ida

start://花指令开始
    jmp label1
    DB junkcode
label1:
     jmp label2
     DB junkcode
label2:
    jmp label3
    DB junkcode
label3   

在这里插入图片描述

3.jnx和jx条件跳转

利用jz和jnz的互补条件跳转指令来代替jmp。竟然没有骗过OD(是因为吾爱的这个有插件吗)。但是ida竟然没有正常识别

_asm{
    jz label1
    jnz label1
    db junkcode
label1:    
}

在这里插入图片描述
在这里插入图片描述

4.永真条件跳转

通过设置永真或者永假的,导致程序一定会执行,由于ida反汇编会优先反汇编接下去的部分(false分支)。也可以调用某些函数会返回确定值,来达到构造永真或永假条件。ida和OD都被骗过去了

__asm{
    push ebx
    xor ebx,ebx
    test ebx,ebx
    jnz label1
    jz label2
label1:
    _emit junkcode
label2:
   pop ebx//需要恢复ebx寄存器    
}

__asm{
	clc
	jnz label1:
	_emit junkcode
label1:
}

在这里插入图片描述

5.call&ret构造花指令

这里利用call和ret,在函数中修改返回地址,达到跳过thunkcode到正常流程的目的。可以干扰ida的正常识别

__asm{
    call label1
    _emit junkcode
label1:
    add dword ptr ss:[esp],8//具体增加多少根据调试来
    ret
    _emit junkcode
}

call指令:将下一条指令地址压入栈,再跳转执行
ret指令:将保存的地址取出,跳转执行

在这里插入图片描述

6.汇编指令共用opcode

jmp的条指令是inc eax的第一个字节,inc eax和dec eax抵消影响。这种共用opcode确实比较麻烦
在这里插入图片描述
在这里插入图片描述

三、清除花指令

1.手动清除

找到所有的花指令,重新设置数据和代码地址。或者将花指令设置为nop(0x90)
在这里插入图片描述
在0x401051设置为数据类型(快捷键D),在0x401052设置为代码类型(快捷键C)
在这里插入图片描述
这里用一个ida python脚本添加ALT+N快捷键来将指令的第一个字节设置为NOP

from idaapi import *
from idc import *

def nopIt():
	start = get_screen_ea()
	patch_byte(start,0x90)
	refresh_idaview_anyway()

add_hotkey("alt-N",nopIt)

2.自动清楚花指令

上面有3个类别ida无法正常识别

  1. 互补条件跳转(比较好处理)
  2. 永真条件跳转 (各种永真条件比较难匹配)
  3. call&ret跳转(比较难处理)

所以就只对第一种jnx和jx的花指令进行自动化处理
所有的跳转指令,互补跳转指令只有最后一个bit位不同

70 <> JO(O标志位为1跳转)
71 <> JNO
72 <> JB/JNAE/JC
73 <> JNB/JAE/JNC
74 <> JZ/JE
75 <> JNZ/JNE
76 <> JBE/JNA
77 <> JNBE/JA
78 <> JS
79 <> JNS
7A <> JP/JPE
7B <> JNP/JPO
7C <> JL/JNGE
7D <> JNL/JGE
7E <> JLE/JNG
7F <> JNLE/JG

第一条指令跳转距离=第二条跳转距离+2。简单一点可以是\x03和\x01
在这里插入图片描述
抄的代码

from ida_bytes import get_bytes,patch_bytes
start= 0x401000#start addr
end = 0x422000
buf = get_bytes(start,end-start)

def patch_at(p,ln):
    global buf
    buf = buf[:p]+b"\x90"*ln+buf[p+ln:]

fake_jcc=[]
for opcode in range(0x70,0x7f,2):
    pattern = chr(opcode)+"\x03"+chr(opcode|1)+"\x01"
    fake_jcc.append(pattern.encode())
    pattern = chr(opcode|1)+"\x03"+chr(opcode)+"\x01"
    fake_jcc.append(pattern.encode())

print(fake_jcc)
for pattern in fake_jcc:
    p = buf.find(pattern)
    while p != -1:
        patch_at(p,5)
        p = buf.find(pattern,p+1)

patch_bytes(start,buf)
print("Done") 

总结:

1.各种构造花式跳转。
2.也可以是将汇编进行等价替换。
3.增加无意义的汇编代码,前后除了改变EIP其他什么都不改变。

对于不同的程序可能有独特的花指令,自动生成的话,可以提取特定规则来方便自动化去花指令。
很多花指令的类型只能慢慢积累。
也可以用正则表达式的方法(感觉也不好用)。
很多时候需要记录运行时的条件(临近跳转jcc的几条汇编模拟执行判断jcc跳转是否一定会发生)(clc;jne|xor eax.eax;test eax,eax,jnz这种需要记录临近几条)

参考链接
花指令总结
https://www.anquanke.com/post/id/236490
逆向学习笔记之花指令
https://www.anquanke.com/post/id/208682
逆向-花指令去除(脚本)
https://blog.csdn.net/whklhhhh/article/details/88730934
《恶意代码分析实战》
Intel硬编码(一):Opcode Map、定长指令与指令前缀
https://blog.csdn.net/Apollon_krj/article/details/77508073
ida python函数查询
https://hex-rays.com/products/ida/support/idapython_docs/

  • 23
    点赞
  • 100
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值