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: int3Due 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