Courses/Computer Science/CPSC 601.29.ISSA/20110204CodeSession
We first executed a slight modification (for output purposes only) of the Red Pill:
[michael@proton redpill]$ gcc -Wall -g -O0 -o redpill redpill.c [michael@proton redpill]$ execstack -s redpill [michael@proton redpill]$ ./redpill idt base: 0xc0805000 (3229634560) idt limit: 0x7ff (2047) m[0] = 0xff m[1] = 0x07 m[2] = 0x00 m[3] = 0x50 m[4] = 0x80 m[5] = 0xc0 IDTR: ff 07 00 50 80 c0 Not in Matrix [michael@proton redpill]$
We also executed the 'nopill', which tested the GDT and LDT in addition to the IDT, and did it using inline assembly, e.g.:
static unsigned char loc[6]; inline idt idtcheck() { __asm__("sidt %0\n" :"=m"(loc) : ); //... }
For an explanation of inline assembly (not great, but a start), see: http://www.ibm.com/developerworks/library/l-ia.html
Keep in mind little-endian nature of output:
[michael@proton redpill]$ ./nopill IDTR: ff 07 00 50 80 c0 GDTR: ff 00 00 c0 00 c2 LDTR: 00 00 00 c0 00 c2 native machine detected [michael@proton redpill]$
The above execution within a VMware Fusion VM fails to "detect" the VM. We then attempted to see if the nopill or redpill would operate "properly" on a native machine (in this case, Mac OS X). We first need a build environment, so in our Makefile, we have the following target:
rp: rp.asm nasm -f macho rp.asm ld -o rp -e _start rp.o
Making system calls on Mac OS X is a bit different than Linux. This tutorial was helpful: http://michaux.ca/articles/assembly-hello-world-for-os-x
So, we start to write out code. We want to capture information from the IDTR using the SIDT instruction and store it to memory. We can store it to our data area or store it on the stack, or both. We then need to output it somehow, so we issue a write(2) system call to place the bytes on stdout. Note that one of the first things we do is some stack manipulation (not for any real good reason except for a few instructions that save space on the stack for use by SIDT later, but we will easily spot these effects in GDB later).
BITS 32 GLOBAL _start SECTION .data ;; declare space for storing IDTR value m32 db 0x00,0x00,0x00,0x00,0x00,0x00 ;6 bytes, lower 2=limit; upper 4=base m64 db 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 SECTION .text _start: xor ecx, ecx ;clear ECX push ecx ; \0 null byte push 0xDEAD push 0xBEEF push ecx ; 0 push 0x0a ; \n push ecx ; 4 bytes push ecx ; 4 bytes push ecx ; 4 bytes (12 bytes to store 2+8 in) ;; now esp points to a part of memory that has 6 open bytes sidt [m64] ;one instruction is all it takes sidt [esp] ;store it here, too: we made space... push dword 0x0a ;write 10 bytes (10 of IDTR) push dword m64 ;IDTR push dword 0x01 ;stdout mov eax, 0x4 ;write sub esp, 4 ;BSD space on stack int 0x80 ;issue syscall add esp, 16 ;clean up stack push eax ;write's return value mov eax, 0x01 ; sys_exit sub esp, 4 ; ? int 0x80
Running our assembly implementation of Red Pill and passing the output to hexdump produces:
[michael@xorenduex code]$ ./rp | hexdump 0000000 01 10 00 40 56 7a 00 00 00 00 000000a [michael@xorenduex code]$
These values change for various subsequent invocations; each processor has its own IDT.
For a closer look at the executing binary (and as a way of coming full circle to use a program (gdb) that uses a facility (ptrace) that in turn uses the IDT (INT 3) to inspect another process), we run rp in gdb:
[michael@xorenduex code]$ gdb ./rp GNU gdb 6.3.50-20050815 (Apple version gdb-1472) (Wed Jul 21 10:53:12 UTC 2010) Copyright 2004 Free Software Foundation, Inc. GDB is free software, covered by the GNU General Public License, and you are welcome to change it and/or distribute copies of it under certain conditions. Type "show copying" to see the conditions. There is absolutely no warranty for GDB. Type "show warranty" for details. This GDB was configured as "x86_64-apple-darwin"...Reading symbols for shared libraries . done (gdb) info reg The program has no registers now.
The program has no registers because we haven't asked gdb to run it. GDB is giving us the opportunity to instrument the program first. We ask gdb to stop execution at the address "start" (a symbol we defined in our assembly program). GDB agrees, and kindly reports the actual address rather than the label name.
(gdb) break start Breakpoint 1 at 0x1fac
We then ask GDB to show us what the assembly code at start looks like (this should match what we explicitly wrote above):
(gdb) disassemble start Dump of assembler code for function start: 0x00001fac <start+0>: xor %ecx,%ecx 0x00001fae <start+2>: push %ecx 0x00001faf <start+3>: push $0xdead 0x00001fb4 <start+8>: push $0xbeef 0x00001fb9 <start+13>: push %ecx 0x00001fba <start+14>: push $0xa 0x00001fbf <start+19>: push %ecx 0x00001fc0 <start+20>: push %ecx 0x00001fc1 <start+21>: push %ecx 0x00001fc2 <start+22>: sidtl 0x201d 0x00001fc9 <start+29>: sidtl (%esp) 0x00001fcd <start+33>: push $0xa 0x00001fd2 <start+38>: push $0x201d 0x00001fd7 <start+43>: push $0x1 0x00001fdc <start+48>: mov $0x4,%eax 0x00001fe1 <start+53>: sub $0x4,%esp 0x00001fe7 <start+59>: int $0x80 0x00001fe9 <start+61>: add $0x10,%esp 0x00001fef <start+67>: push %eax 0x00001ff0 <start+68>: mov $0x1,%eax 0x00001ff5 <start+73>: sub $0x4,%esp 0x00001ffb <start+79>: int $0x80 End of assembler dump.
This output has the nice side effect of giving us some opportunities for inserting other breakpoints. We had a discussion in class here about the limited support for runtime, at-speed process inspection facilities available on x86, and how this is a gaping mismatch between the expectations of security policies, monitoring, and analysis and the actual hardware primitives for a trapping model (point to work with Sean and Sergey at Dartmouth).
(gdb) break 0x1fc2 Function "0x1fc2" not defined. Make breakpoint pending on future shared library load? (y or [n]) n
The problem exists between the keyboard and chair on this one. GDB thinks I'm asking for a breakpoint at symbol (e.g., a function name). I need to give the correct syntax to indicate that I want a breakpoint at a specific address. I continue to set up breakpoints before each SIDT instruction and before the write system call gets dispatched.
(gdb) break *0x1fc2 Breakpoint 2 at 0x1fc2 (gdb) break *0x1fc9 Breakpoint 3 at 0x1fc9 (gdb) break *0x1fcd Breakpoint 4 at 0x1fcd (gdb) break *0x1fe7 Breakpoint 5 at 0x1fe7
I can ask for the register values again, but the program still isn't running. But I don't have to type `info reg', I can abbreviate.
(gdb) i r The program has no registers now.
Well, let's run it.
(gdb) run Starting program: /Users/michael/data/cvs/Calgary/teaching/ISSA/2011/code/rp Breakpoint 1, 0x00001fac in start ()
OK, now can I ask for the register values? Yes! Note that EIP is pointing to the next instruction to be executed, and that instruction happens to be the first instruction because GDB interrupts control before executing an instruction.
(gdb) i r eax 0x1fac 8108 ecx 0x603c2a4f 1614555727 edx 0x0 0 ebx 0x1000 4096 esp 0xbffff3f0 0xbffff3f0 ebp 0x0 0x0 esi 0x0 0 edi 0x0 0 eip 0x1fac 0x1fac <start> eflags 0x296 662 cs 0x17 23 ss 0x1f 31 ds 0x1f 31 es 0x1f 31 fs 0x0 0 gs 0x37 55
Well, nothing much to see here. Let's keep moving along with the "continue" command. This command tells GDB to let the program go away and execute instructions until the next breakpoint.
(gdb) cont Continuing. Breakpoint 2, 0x00001fc2 in start ()
Let me just ask for a subset of "interesting" registers for now.
(gdb) i r eax ebx ecx edx eip esp eax 0x1fac 8108 ebx 0x1000 4096 ecx 0x0 0 edx 0x0 0 eip 0x1fc2 0x1fc2 <start+22> esp 0xbffff3d0 0xbffff3d0
Hmmmm. The value in ESP is the top of the stack, so let's examine memory there using the `x' command.
(gdb) x/16 esp No symbol table is loaded. Use the "file" command. (gdb) x/16 %esp A syntax error in expression, near `%esp'.
Again, PEBKAC. Consult the gdb manual for syntax! Once we know how to speak to gdb, we see that our stack has all the results of pushing \n, deadbeef, and the cleared ecx.
(gdb) x/16 $esp 0xbffff3d0: 0x00000000 0x00000000 0x00000000 0x0000000a 0xbffff3e0: 0x00000000 0x0000beef 0x0000dead 0x00000000 0xbffff3f0: 0x00000001 0xbffff4cc 0x00000000 0xbffff507 0xbffff400: 0xbffff576 0xbffff592 0xbffff5bc 0xbffff5c7 (gdb) cont Continuing. Breakpoint 3, 0x00001fc9 in start ()
We just executed the SIDT instruction involving the store to m64, our memory location in .data. Perhaps we can look at it?
(gdb) x/16 m64 Value can't be converted to integer.
Well, we actually know the address.
(gdb) x/16 0x201d 0x201d <m64>: 0x30001002 0x00007a77 0x00000000 0x00000000 0x202d: 0x00000000 0x00000000 0x00000000 0x00000000 0x203d: 0x00000000 0x00000000 0x00000000 0x00000000 0x204d: 0x00000000 0x00000000 0x00000000 0x00000000 (gdb) cont Continuing. Breakpoint 4, 0x00001fcd in start ()
After the next SIDT instruction, we should see the result on the stack. We do see a result, but it differs from what is in m64. What's going on?
(gdb) x/16 $esp 0xbffff3d0: 0xf0001003 0x00007a8b 0x00000000 0x0000000a 0xbffff3e0: 0x00000000 0x0000beef 0x0000dead 0x00000000 0xbffff3f0: 0x00000001 0xbffff4cc 0x00000000 0xbffff507 0xbffff400: 0xbffff576 0xbffff592 0xbffff5bc 0xbffff5c7 (gdb) cont Continuing. Breakpoint 5, 0x00001fe7 in start ()
Let's look at the stack before executing the system call. Looks like we are going to write 10 bytes (0x0000000a) starting at 0x0000201d to stdout (0x00000001).
(gdb) x/16 $esp 0xbffff3c0: 0x00000000 0x00000001 0x0000201d 0x0000000a 0xbffff3d0: 0xf0001003 0x00007a8b 0x00000000 0x0000000a 0xbffff3e0: 0x00000000 0x0000beef 0x0000dead 0x00000000 0xbffff3f0: 0x00000001 0xbffff4cc 0x00000000 0xbffff507 (gdb) cont Continuing. 0wz Program exited with code 012.
Looks like our output (remember, we're writing m64, not what was stored on esp) translates to ASCII 0wz (recall we passed this directly to hexdump before). The program also reports that it printed 012 characters. Remember in the assembly we saved the return value of write(2), which is the number of bytes written. This should be 10. It is -- note that 012 is octal notation, and that translates to 10 decimal. We're done here.
(gdb) exit Undefined command: "exit". Try "help".
It is always useful to know how to get out of some environment.
(gdb) quit [michael@xorenduex code]$