-
Notifications
You must be signed in to change notification settings - Fork 42
Debugging
Many times during the development of the framework we needed to assess our results. Fortunately, we have the source code of our test-case (Chuck - the kernel ROP rootkit), and the VM to execute it (provided by the Chuck's author).
In this page I show you how we used GDB, extended with a set of Python3 plugins to extract information about the running rootkit and compare them with the results of our framework. Keep in mind that the assessment framework is working on the live virtual machine while ROPMEMU is working on a memory dump.
The scenario is the following. We log in the VM. We attach GDB and we run the rootkit. We want to skip the copy chain and directly go to the dispatcher chain.
First step, we have to attach to the running VM.
> gdb -q -ex "set disassembly-flavor intel" -ex "set architecture i386:x86-64:intel" -ex "target remote:1234"
The target architecture is assumed to be i386:x86-64:intel
Remote debugging using :1234
0xffffffff81043fa6 in ?? ()
Second, we need the pointer to the copy chain
and we use the GDB Python script chuckgetcopyptr
.
In the guest OS, this pointer changes at every reboot.
Of course in the VM we have to run the rootkit :-)
(gdb) source chuckgetcopyptr.py
--[ ROPMEMU framework - GDB utils ]--
[+] Patching...
[+] Setting the breakpoint...
Breakpoint 1 at 0xffffffff810039a0
[+] Back to the VM
[+] Reading RAX...
[+] Copy Chain initial ptr: ffff880024400000
-----
0xffff880024400000: 0xffffffff8100a4de 0xffff880024800000
0xffff880024400010: 0xffffffff8115c832 0xffffffff8100a4de
0xffff880024400020: 0xffff880024800008 0xffffffff81060147
0xffff880024400030: 0xffffffff8115c832 0xffffffff810051ae
0xffff880024400040: 0xffffffff816a9434 0xffffffff812ce029
-----
Breakpoint 1, 0xffffffff810039a0 in ?? ()
Then we set a breakpoint on POP RSP; RET
, the switching sequence.
We want to detect the switching to a new chain.
(gdb) b *0xffffffff81423f82
Breakpoint 2 at 0xffffffff81423f82
(gdb) c
Continuing.
Breakpoint 2, 0xffffffff81423f82 in ?? ()
We load boundary
to detect a new chain by monitoring the SP deltas.
(gdb) source boundary.py
(gdb) boundary System.map-3.8.0-19-generic
--[ ROPMEMU framework - GDB utils ]--
[+] Chasing the dispatcher chain...
1) 0xffff88002461b770 - 0xffff88002461b770
0xffffffff81423f83 in ?? ()
2) 0xffff880028382000 - 0xffff88002461b770
[+] last_sp: 0xffff88002461b770 - current_sp: 0xffff880028382000
[+] 2 instructions executed!
We use unropandroll
to generate traces and compare the assembly instructions and the CPU context.
(gdb) source unropandroll.py
(gdb) unrop
--[ ROPMEMU framework - GDB utils ]--
+-------------------------------------------------------------------------------------
| Usage: unrop <num_of_instrs> <output_file> <System.map> <mode> <multipath>
| Mode: 0 Normal txt trace - 1: JSON output with registers
| Multipath: NULL, default:0, default:1 - sp1:zf,sp2:zf - Pushf based
+-------------------------------------------------------------------------------------
(gdb) unrop 300 /tmp/gdb_dispatacher_300_0.json System.map-3.8.0-19-generic 1 default:0
Another interesting script is spmonitor
. This script monitors the evolution of the stack pointer, in this way it is possible to figure out possible mistakes during the stack emulation. We run it from GDB:
(gdb) source spmonitor.py
(gdb) spmonitor
--[ ROPMEMU framework - GDB utils ]--
+--------------------------------------------------------------------+
| Usage: spmonitor <num_of_instrs> <output_file> <System.map> |
+--------------------------------------------------------------------+
(gdb) spmonitor 10 /tmp/spmonitor_10.log System.map-3.8.0-19-generic
it generates /tmp/spmonitor_10.log
that it looks like:
pop rax | 0xffff880024c00008 | 0xffff880024c00010 | 0x8
ret | 0xffff880024c00010 | 0xffff880024c00018 | 0x8
mov [rax], rdx | 0xffff880024c00018 | 0xffff880024c00018 | 0x0
ret | 0xffff880024c00018 | 0xffff880024c00020 | 0x8
ROPMEMU Framework