Here's a story of how I wrote a very simple "hypervisor" for defcon ctf, or at
least emulate a kernel mode code so that I can debug.
Problem
I wanted to be able to do some dynamic analysis on kernel-mode program for
RTOoOS challenge. I wished I could've stuck it in like a GDB instance and it
just worked.
Except the problem was:
The kernel had very weird hyper-calling mechanisms which were totally
incompatible to linux syscalls.
The kernel was loaded at address 0, and some of the unique bugs of this was
entirely dependent on being at address 0.
So I first started with tackling the first issue. I simply just monkey patched
the essential hypercalls (fortunately they are all in the same location):
seg000:0000000000000055; __int64 __fastcall sys_putchar(char) seg000:0000000000000055 sys_putchar proc near; CODE XREF: sys_write:sys_ls↓j seg000:0000000000000055; put_str+37↓p seg000:0000000000000055pushrdi seg000:0000000000000056movrsi, rsp seg000:0000000000000059movedi, 1 seg000:000000000000005E movedx, edi seg000:0000000000000060moveax, edi seg000:0000000000000062syscall; Low latency system call seg000:0000000000000064poprdi seg000:0000000000000065retn seg000:0000000000000065 sys_putchar endp seg000:0000000000000065 seg000:0000000000000065; --------------------------------------------------------------------------- seg000:0000000000000066db0 seg000:0000000000000067db0 seg000:0000000000000068; --------------------------------------------------------------------------- seg000:0000000000000068retn seg000:0000000000000069 seg000:0000000000000069; =============== S U B R O U T I N E ======================================= seg000:0000000000000069 seg000:0000000000000069 seg000:0000000000000069; __int64 __fastcall sys_read_input(__int64 a1, __int64 a2) seg000:0000000000000069 sys_read_input proc near; CODE XREF: _start+8E↓p seg000:0000000000000069 seg000:0000000000000069; FUNCTION CHUNK AT seg000:0000000000000083 SIZE 00000006 BYTES seg000:0000000000000069 seg000:0000000000000069movedx, esi seg000:000000000000006B movrsi, rdi seg000:000000000000006E xoredi, edi seg000:0000000000000070xoreax, eax seg000:0000000000000072syscall; Low latency system call seg000:0000000000000074jmp short loc_83 seg000:0000000000000074 sys_read_input endp seg000:0000000000000074 seg000:0000000000000076 seg000:0000000000000076; =============== S U B R O U T I N E ======================================= seg000:0000000000000076 seg000:0000000000000076 seg000:0000000000000076; __int64 __fastcall sys_write(char *a1) seg000:0000000000000076 sys_write proc near; CODE XREF: do_command+82↓p seg000:0000000000000076; do_command+D7↓p ... seg000:0000000000000076call put_str seg000:000000000000007B movedi, 0Ah seg000:0000000000000080 seg000:0000000000000080 sys_ls: ; CODE XREF: do_command+AA↓p seg000:0000000000000080jmp short sys_putchar seg000:0000000000000082; --------------------------------------------------------------------------- seg000:0000000000000082retn seg000:0000000000000082 sys_write endp seg000:0000000000000082 seg000:0000000000000083; --------------------------------------------------------------------------- seg000:0000000000000083; START OF FUNCTION CHUNK FOR sys_read_input seg000:0000000000000083 seg000:0000000000000083 loc_83: ; CODE XREF: sys_read_input+B↑j seg000:0000000000000083; do_command+18D↓p seg000:0000000000000083movbyteptr [rsi+rax-1], 0 seg000:0000000000000088retn seg000:0000000000000088; END OF FUNCTION CHUNK FOR sys_read_input
Essentially the code up there is just rewritting the sys_putchar,
sys_write_line, and sys_read hypercalls.
Now time to do the second part of this scheme: loading the code into address 0
\. I remembered a technique that you can actually mmap
address 0, but it will require root to do so.
Without ado, here's my hypervisor code for that. It isn't a full-blown
hypervisor, but it was good enough to locally debug it in gdb, hehehehe....