http://blog.oxff.net/#hozqp5np2wdtpl7wlegq

2009-04-03 00:00

PE Sandboxing

Because I'm frustrated about the poor scalability of most sandboxes out there, which quite frankly don't deserve their name anyway, because they're just VMs with some Ring 0 rootkit, I've started some work on dynamic code analysis by a per-process level PE sandbox. So far, I've got the execution of arbitrary code with virtual memory; I had PE loading, but deleted it and could not recover from ext3 (vlashef being my witness).

There are two testcases of interest, the first regarding performance:

$ cat testcases/simple-loop.asm 
[bits 32]

start:
 mov ecx, 0x100000
 xor eax, eax

.1:
 inc eax
 dec ecx
 jnz .1

end:
 int3
Due to some fancy analysis (admittedly, so far it only supports backward jumps), we can put more than one basic block directly on the CPU:
$ time ./libcpu-test 100 < testcases/simple-loop.bin 
bool libcpu::VirtualMemory::allocatePages(uint32_t, uint32_t, int): 1000 -> 0x37cd2000, 1000
bool libcpu::VirtualMemory::allocatePages(uint32_t, uint32_t, int): 400000 -> 0x380d1000, 1000
bool libcpu::VirtualMemory::allocatePages(uint32_t, uint32_t, int): 3fc000 -> 0x380cd000, 4000
edi: 00000000
esi: 00000000
ebp: 00000000
esp: 00400000
ebx: 00000000
edx: 00000000
ecx: 00000000
eax: 00000000
eflags: 00000000
eip: 00400000
  > mov ecx,0x100000
  > xor eax,eax
  > inc eax
  > dec ecx
  > jnz 0x400007
bool libcpu::VirtualMemory::allocatePages(uint32_t, uint32_t, int): 400000 -> 0x380d1000, 1000
void libcpu::CpuEmulator::analyzeBlock(): 0x380d1000 b
edi: 00000000
esi: 00000000
ebp: 00000000
esp: 00400000
ebx: 00000000
edx: 00000000
ecx: 00000000
eax: 00100000
eflags: 00000246
eip: 0040000b
---
edi: 00000000
esi: 00000000
ebp: 00000000
esp: 00400000
ebx: 00000000
edx: 00000000
ecx: 00000000
eax: 00100000
eflags: 00000246
eip: 0040000b
Unrecognized instruction for emulation: int3 cc 1

real 0m0.005s
user 0m0.004s
sys 0m0.000s

The second example is more interesting, because it shows the ability to handle self-modifying code. The following snippet copies itself right behind itself and then runs into the copy without any jump:

$ cat testcases/copysled.asm 
[bits 32]

start:
 mov ebx, 0x10

sled:
 call $+5
 pop esi
 sub esi, 5

mov edi, esi
 mov ecx, end-sled
 add edi, ecx

rep movsb

dec ebx
 jnz end

int3

end:
 hlt

For obvious reasons, I've cut out some of the output in the middle:

bool libcpu::VirtualMemory::allocatePages(uint32_t, uint32_t, int): 1000 -> 0x37c6f000, 1000
bool libcpu::VirtualMemory::allocatePages(uint32_t, uint32_t, int): 400000 -> 0x3806e000, 1000
bool libcpu::VirtualMemory::allocatePages(uint32_t, uint32_t, int): 3fc000 -> 0x3806a000, 4000
edi: 00000000
esi: 00000000
ebp: 00000000
esp: 00400000
ebx: 00000000
edx: 00000000
ecx: 00000000
eax: 00000000
eflags: 00000000
eip: 00400000
  > mov ebx,0x10
bool libcpu::VirtualMemory::allocatePages(uint32_t, uint32_t, int): 400000 -> 0x3806e000, 1000
void libcpu::CpuEmulator::analyzeBlock(): 0x3806e000 5
edi: 00000000
esi: 00000000
ebp: 00000000
esp: 00400000
ebx: 00000010
edx: 00000000
ecx: 00000000
eax: 00000000
eflags: 00000206
eip: 00400005
---
edi: 00000000
esi: 00000000
ebp: 00000000
esp: 00400000
ebx: 00000010
edx: 00000000
ecx: 00000000
eax: 00000000
eflags: 00000206
eip: 00400005
Emulating CALL instruction from 40000a.
void libcpu::CpuEmulator::analyzeBlock(): 0x3806e005 0
edi: 00000000
esi: 00000000
ebp: 00000000
esp: 003ffffc
ebx: 00000010
edx: 00000000
ecx: 00000000
eax: 00000000
eflags: 00000206
eip: 0040000a
---
edi: 00000000
esi: 00000000
ebp: 00000000
esp: 003ffffc
ebx: 00000010
edx: 00000000
ecx: 00000000
eax: 00000000
eflags: 00000206
eip: 0040000a
  > pop esi
  > sub esi,0x5
  > mov edi,esi
  > mov ecx,0x1b
  > add edi,ecx
  > rep movsb 
  > dec ebx
void libcpu::CpuEmulator::analyzeBlock(): 0x3806e00a 13
edi: 00400020
esi: 00400005
ebp: 00000000
esp: 00400000
ebx: 00000010
edx: 00000000
ecx: 0000001b
eax: 00000000
eflags: 00010212
eip: 0040001a
---
edi: 00400020
esi: 00400005
ebp: 00000000
esp: 00400000
ebx: 00000010
edx: 00000000
ecx: 0000001b
eax: 00000000
eflags: 00010212
eip: 0040001a
  > rep movsb 
bool libcpu::VirtualMemory::allocatePages(uint32_t, uint32_t, int): 400000 -> 0x3806e000, 1000
void libcpu::CpuEmulator::analyzeBlock(): 0x3806e01a 2
edi: 0040003b
esi: 00400020
ebp: 00000000
esp: 00400000
ebx: 00000010
edx: 00000000
ecx: 00000000
eax: 00000000
eflags: 00000206
eip: 0040001c
---
edi: 0040003b
esi: 00400020
ebp: 00000000
esp: 00400000
ebx: 00000010
edx: 00000000
ecx: 00000000
eax: 00000000
eflags: 00000206
eip: 0040001c
  > dec ebx
bool libcpu::VirtualMemory::allocatePages(uint32_t, uint32_t, int): 400000 -> 0x3806e000, 1000
void libcpu::CpuEmulator::analyzeBlock(): 0x3806e01c 1
edi: 0040003b
esi: 00400020
ebp: 00000000
esp: 00400000
ebx: 0000000f
edx: 00000000
ecx: 00000000
eax: 00000000
eflags: 00000216
eip: 0040001d
---
[[[ CUT ]]]
---
edi: 004001d0
esi: 004001b5
ebp: 00000000
esp: 00400000
ebx: 00000000
edx: 00000000
ecx: 00000000
eax: 00000000
eflags: 00000246
eip: 004001b2
Emulating Jcc 1: 0
void libcpu::CpuEmulator::analyzeBlock(): 0x3806e1b2 0
edi: 004001d0
esi: 004001b5
ebp: 00000000
esp: 00400000
ebx: 00000000
edx: 00000000
ecx: 00000000
eax: 00000000
eflags: 00000246
eip: 004001b4
---
edi: 004001d0
esi: 004001b5
ebp: 00000000
esp: 00400000
ebx: 00000000
edx: 00000000
ecx: 00000000
eax: 00000000
eflags: 00000246
eip: 004001b4
Unrecognized instruction for emulation: int3 cc 1

real 0m0.006s
user 0m0.004s
sys 0m0.000s