Courses/Computer Science/CPSC 601.29.ISSA/20110204CodeSession

Jump to: navigation, search

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:

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:

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).

GLOBAL _start
        ;; 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
        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                  
[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
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
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
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
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
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]$