Chinaunix首页 | 论坛 | 博客
  • 博客访问: 942219
  • 博文数量: 146
  • 博客积分: 3321
  • 博客等级: 中校
  • 技术积分: 1523
  • 用 户 组: 普通用户
  • 注册时间: 2008-08-29 10:32
文章分类

全部博文(146)

文章存档

2014年(2)

2013年(5)

2012年(4)

2011年(6)

2010年(30)

2009年(75)

2008年(24)

分类: C/C++

2009-03-07 10:13:32

前言:研究了几天DOS下的溢出原理,最后明白了其实原理都很简单关键是要懂得为什么C语言的strcpy函数有漏洞,为什么对这个函数的不正常使用会造成溢出。
  
  一节:介绍strcpy函数能看到这篇文章的人可能都知道问题很多是出在它的身上吧呵呵。
  
  先看一看在标准的C语言的string.h中对这个函数的申明char *_Cdecl stpcpy (char *dest, const char *src);对于代码看下面的:(这是微软对这个函数的说明)
  
  (%VC%/vc7/crt/src/intel/strcat.asm)
  
  ;***
  ;char *strcpy(dst, src) - copy one string over another
  ;Purpose:
  ; Copies the string src into the spot specified by
  ; dest; assumes enough room.
  ;
  ; Algorithm:
  ; char * strcpy (char * dst, char * src)
  ; {
  ; char * cp = dst;
  ; while( *cp++ = *src++ ); /* Copy src over dst */
  ; return( dst );
  ; }
  ;Entry:
  ; char * dst - string over which "src" is to be copied
  ; const char * src - string to be copied over "dst"
  ;
  ;Exit:
  ; The address of "dst" in EAX
  ;
  ;Uses:
  ; EAX, ECX
  ;
  ;Exceptions:
  ;**********************************************************************
  
  本来想去掉一些注解,不过觉得还是留着好哈:)
  从上面我们可以看到这样的代码有问题有:
  1.没有检查输入的两个指针是否有效。
  2.没有检查两个字符串是否以NULL结尾。
  3.没有检查目标指针的空间是否大于等于原字符串的空间。
  
  好了现在我们知道了对于调用string.h中的这个函数,和我们自已写一个如下的程序没有本质上的区别那么我们就来研究它就可以了.就叫它c4.exe吧.
  main(){j();}
  j()
  {
  char a[]={a,b,};
  char b[1];
  char *c=a;
  char *d=b;
  while(*d++=*c++);
  printf("%sn",b);
  }
  
  
  二节:调试我们的c4.exe
  所用工具W32dasm,debug,tcc,tc
  第一步我们用TC2编绎生成可执行文件c4.exe.
  第二步用TCC -B生成这段C代码的汇编源代码.
  第三步用W32dasm和debug对c4.exe进行静态和动态调试
  
  先分析由TCC生成的c4.asm代码如下:
  先说明一下由于这是一个完整的包括了MAIN函数的C程序,程序刚开始时数据段和堆栈段还有代码都不在一起但是当,执行到我们的J函数时堆栈和数段就在一起了这要特别注意.
  
  ifndef ??version
  ?debug macro
  endm
  endif
  ?debug S "c4.c"
  
  _TEXT segment byte public CODE
  DGROUP group _DATA,_BSS
  assume cs:_TEXT,ds:DGROUP,ss:DGROUP
  _TEXT ends
  
  _DATA segment word public DATA
  d@ label byte
  d@w label word
  _DATA ends
  
  _BSS segment word public BSS
  b@ label byte
  b@w label word
  ?debug C E930A68D2E0463342E63
  _BSS ends
  
  _TEXT segment byte public CODE
  ; ?debug L 1
  _main proc near
  ; ?debug L 3
  call near ptr _j //这儿执行我们的J函数
  @1:
  ; ?debug L 4
  ret
  _main endp
  _TEXT ends
  
  _DATA segment word public DATA //最先在数据段中定义我们的源串ab结尾符
  db 97
  db 98
  db 0
  _DATA ends
  
  _TEXT segment byte public CODE
  ; ?debug L 6
  _j proc near
  push bp //J函数入口
  mov bp,sp
  sub sp,6
  push si
  push di
  push ss
  lea ax,word ptr [bp-6]
  push ax
  push ds
  mov ax,offset DGROUP:d@ //特别注意这是得到源串在数据段中的偏移
  push ax //所有SCOPY@以上的代码的作用是在堆栈中分配源串加目的串那么多个空间
  mov cx,3 //cx=3指定要拷贝的字符数
  call far ptr SCOPY@ //执行了另一个函数作用是把数据段中的源串拷到栈中
  ; ?debug L 10
  lea si,word ptr [bp-6]
  ; ?debug L 11
  lea di,word ptr [bp-2]
  ; ?debug L 12
  jmp short @3
  @5:
  @3:
  ; ?debug L 12
  mov bx,si
  inc si
  mov al,byte ptr [bx]
  mov bx,di
  inc di
  mov byte ptr [bx],al
  or al,al
  jne @5
  @4:
  ; ?debug L 13
  lea ax,word ptr [bp-2]
  push ax
  mov ax,offset DGROUP:s@ //得到printf函数的打印格式参数
  push ax
  call near ptr _printf
  pop cx
  pop cx
  @2:
  ; ?debug L 14
  pop di
  pop si
  mov sp,bp
  pop bp
  ret
  _j endp
  _TEXT ends
  ?debug C E9
  
  _DATA segment word public DATA
  s@ label byte
  db 37 //%
  db 115 //s
  db 10 //换行符:)
  db 0
  _DATA ends
  extrn SCOPY@:far
  _TEXT segment byte public CODE
  extrn _printf:near
  _TEXT ends
  public _main
  public _j
  end
  
  三节:分析W32Dasm得来的静态汇编代码,也就是程序最终的代码同时我们一步步来分析
  这时堆栈的情况.
  文章写到这儿可能大家一定认识都是些看到就头大的代码吧,没事我先分析一下
  这些代码就执行来说可以分为三个部分:
  
  1.从01FE到020B是根据C代码中的定义在堆栈中分配空间例子中分了6个字节,定义多少分多少也没有毛病
  2远跳到0000:1395是把数据段中的源串放到堆栈中由于放入个数在cx中所以这儿也没有毛病
  3在堆栈中把源串拷到目的串所在的内存单元中问题就在这儿了!
  
  
  :0001.01FA E80100 call 01FE //执行我们的j函数
  :0001.01FD C3 ret
  
  :0001.01FE 55 push bp
  :0001.01FF 8BEC mov bp, sp
  :0001.0201 83EC06 sub sp, 0006
  :0001.0204 56 push si
  :0001.0205 57 push di
  :0001.0206 16 push ss
  :0001.0207 8D46FA lea ax, [bp-06]
  :0001.020A 50 push ax
  :0001.020B 1E push ds
  :0001.020C B89401 mov ax, 0194
  :0001.020F 50 push ax
  :0001.0210 B90300 mov cx, 0003
  :0001.0213 9A95130000 call 0000:1395 //这儿先跳到1395去执行了由于它是在0000所以是远跳
  
  :0001.0218 8D76FA lea si, [bp-06]
  :0001.021B 8D7EFE lea di, [bp-02]
  :0001.021E EB00 jmp 0220
  :0001.0220 8BDE mov bx, si
  :0001.0222 46 inc si
  :0001.0223 8A07 mov al , [bx]
  :0001.0225 8BDF mov bx, di
  :0001.0227 47 inc di
  :0001.0228 8807 mov [bx], al
  :0001.022A 0AC0 or al , al
  :0001.022C 75F2 jne 0220
  :0001.022E 8D46FE lea ax, [bp-02]
  :0001.0231 50 push ax
  :0001.0232 B89701 mov ax, 0197
  :0001.0235 50 push ax
  :0001.0236 E8BC08 call 0AF5 //执行打印输出
  :0001.0239 59 pop cx
  :0001.023A 59 pop cx
  :0001.023B 5F pop di
  :0001.023C 5E pop si
  :0001.023D 8BE5 mov sp, bp
  :0001.023F 5D pop bp
  :0001.0240 C3 ret
  //下面的就是我们的SCOPY@
  0001.1395 55 push bp
  :0001.1396 8BEC mov bp, sp
  :0001.1398 56 push si
  :0001.1399 57 push di
  :0001.139A 1E push ds
  :0001.139B C57606 lds si, [bp+06]
  :0001.139E C47E0A les di, [bp+0A]
  :0001.13A1 FC cld
  :0001.13A2 D1E9 shr cx, 01
  :0001.13A4 F3 repz
  :0001.13A5 A5 movsw
  :0001.13A6 13C9 adc cx, cx
  :0001.13A8 F3 repz
  :0001.13A9 A4 movsb
  :0001.13AA 1F pop ds
  :0001.13AB 5F pop di
  :0001.13AC 5E pop si
  :0001.13AD 5D pop bp
  :0001.13AE CA0800 retf 0008
  
  我们现在开始DEBUG动态调试:
  第一步D:turboc2>debug c4.exe
  -g 01FE 通过W32DASM中的查找我们直接跳到J入口处执行
  
  AX=0000 BX=0566 CX=000E DX=067F SP=FFE8 BP=FFF4 SI=00D8 DI=054B
  DS=13DB ES=13DB SS=13DB CS=129F IP=01FE NV UP EI PL ZR NA PE NC
  129F:01FE 55 PUSH BP
  -t
  
  AX=0000 BX=0566 CX=000E DX=1193 SP=FFE6 BP=FFF4 SI=00D8 DI=054B
  DS=13DB ES=13DB SS=13DB CS=129F IP=01FF NV UP EI PL ZR NA PE NC
  129F:01FF 8BEC MOV BP,SP
  
  
  由于上一条指令是CALL O1FE,所以也就有一条POP 01FD,然后又是一个PUSH BP
  -d ss:ffe0
  13DB:FFE0 FF 01 9F 12 F3 0B F4 FF-FD 01 1D 01 01 00 F2 FF ................
  13DB:FFF0 54 05 F6 FF 00 00 43 35-2E 45 58 45 00 00 FB 00 T.....C5.EXE....
  
  现在就来看看栈的情况
  mov bp,sp后BP就成了FFE6
  
  低
  FFE0 | ->SUB SP,0006(空了六个字节为源目的串在堆栈
阅读(1338) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~