一、简介
libemu是一款用C语言实现的基于x86的shellcode检测的库。
它可以支持:
1、解析x86指令、寄存器模拟、FPU模拟
2、静态分析、动态分析、Win32API hook
使用libemu你可以:
1、判断一段字串是不是shellcode
2、可以用libemu得到指令执行流程图(类似于IDA等调试工具)
libemu可以被用于IDS、蜜罐等安全产品中
二、使用
下面是一个使用libemu的例子
/*libemu test*/
#include <emu/emu.h>
#include <emu/emu_shellcode.h>
#include <emu/emu_memory.h>
struct emu *emu;
char shellcode[] =
"\xbe\x1f\x5e\x89\x76\x09\x31\xc0\x88\x46\x08\x89\x46\x0d\xb0\x0b"
"\x89\xf3\x8d\x4e\x09\x8d\x56\x0d\xcd\x80\x31\xdb\x89\xd8\x40\xcd"
"\x80\xe8\xdc\xff\xff\xff\x2f\x62\x69\x6e\x2f\x6c\x73\x00\xc9\xc3";
int main()
{
emu = emu_new();
if ( emu_shellcode_test(emu, (uint8_t *)shellcode, 48) >= 0 )
{
fprintf(stderr, "suspecting shellcode\n");
}
emu_free(emu);
return 0;
}
在上例中,执行过程中会打印 suspecting shellcode,表示这个是一段可以字符串,而实际上这段字符串是linux下的shellcode,完成的功能是在当前路径下执行“/bin/ls”。
三、实现原理
libemu是基于对x86汇编语言的解析和模拟执行。和Bochs、qemu不同的是libemu只是模拟器,不是虚拟机。只能完成对内存和CPU的简单模拟,不能完全模拟执行。
3.1 一个基本假设
libemu的一个基本假设是,如果字串是一段shellcode,那么其中一定包含”call”(0xe8)或者“fnstenv”(0xd9)指令(GetPC code)。
这里边涉及到shellcode的编写技巧,在shellcode的编写一般都要进行地址定位,而地址定位就很难绕过call/ret或者类fnstenv的浮点数指令。比如说:
一个例子:
jmp 0x2a popl %esi movl %esi,0x9(%esi) movb $0x0,0x8(%esi) movl $0x0,0xd(%esi) movl $0xb,%eax movl %esi,%ebx leal 0x9(%esi),%ecx leal 0xd(%esi),%edx int $0x80 movl $0x1, %eax
movl $0x0, %ebx int $0x80 call -0x2f .string \"/bin/ksh\"
上边这段汇编实际上就是执行了两个系统调用exec和exit,但是这执行过程中需要把字符串"/bin/ksh"的地址传递给exec,但是这个地址在编写shellcode的时候是不知道的。 这里就是利用了"call"指令的特性:call的时候push eip(实际上就是call指令的下一条指令的地址入栈,在这个shellcode中就是符串"/bin/ksh"的地址),然后跳转到"popl %esi"指令处,执行之后就是把刚刚push进栈的地址,pop给了esi寄存器。
另一个例子:编写shellcode的另外一个技巧—Delta offset。一个shellcode写好之后,其中的指令和数据的相对位置是固定的。那么该shellcode在shellcode开发机和被攻击的机器上的不同在于,shellcode的加载位置不同(eip不同)。如果把shellcode的所用到的地址都硬编码为开发机中实际的地址,那么理论上上只需要知道Delta offset,就可以计算出被攻击机器的地址。如下图:
这时问题就被转化为如何获得被攻击机器的eip的值,可以这样做:
call delta
delta:
pop ebp
或者:
fpu_addr:
fnop
call GetPhAddr
sub ebp,fpu_addr
GetPhAddr:
sub esp,16
fnstenv [esp-12]
pop ebp
add esp,12
ret
3.2 三个基本动作:
1、模拟memory,模拟CPU
memory:模拟两级页表(写时复制)。
CPU:模拟寄存器、段、当前指令及其描述。
2、静态分析
是否是合法的x86汇编格式:
梳理指令流程:顺序执行,跳转,条件跳转, 得到令执行流程图。
判断指令的数据地址有没有超出”shellcode”的控制范围。
3、动态执行
判断在指令执行时,该指令所用到的寄存器、内存地址的值,是不是由shellcode控制的。这里需要用到静态分析中得到的指令执行流程图。
3.3、判断是不是shellcode的标准:
这个字符串,是否能够作为一段汇编语言动态执行?
四、伪代码
emu_shellcode_test(XXX,uint8_t *data, uint16_t size
{
if(data不包含“0xe8”和“0xd9”)
{
this is safe ;
return;
}
从data开始找第一个合法的汇编语句,并进行静态分析;
if(静态分析出错)//语法错误或者数据不可控
{
this is safe;
return;
}
进行动态分析;
if(data中的汇编可连续动态执行N步)
{
this is suspecting shellcode;
return;
}
}
五、局限性
平台:只局限于X86
性能:比较低,libemu自己提供的nids demo 吞吐为2Mbps
误报漏报:shellcode中不一定会有"call"
检测手段:libeum实际上并没有真正的检测攻击
编码/加密:libemu对编码或加密的数据无能为力