论坛: 黑客进阶 标题: 进程内存篇[待续] 复制本贴地址    
作者: tabris17 [tabris17]    论坛用户   登录
一、进程的内存分配

对于一个进程的内存空间而言,可以在逻辑上分成3个部份:代码区,静态数据区和动态数据区。对于 windows 操作系统而言,动态数据区有堆(HEAP)内存区域和栈(STACK)内存区域,这里的栈内存区域就相当于我们平时说的“堆栈”,每个线程都有自己的栈,而“堆”在这里是一个新的概念,它是由操作系统管理的,堆的分配和回收可以由操作系统自动实现。每个进程都有自己的默认堆,可以通过 GetProcessHeaps 来得到堆句柄,也可以通过 Heap 函数族可以实现对堆的操作。进程的堆信息可以通过查询进程 PEB(1) 结构得到。对于 Visual C++ 编译器,C++ 语言中的 new,delete 操作符就是通过堆来实现内存动态分配的。Windows 操作系统有它自己的内存动态分配函数 VirtualAlloc,相当于 C 语言中的 malloc 函数,堆的动态内存分配最终就是通过它实现的,这些会在后文讲到。


├―――――――┤
│    ……      │
├―――――――┤
│  动态数据区  │
├―――――――┤
│    ……      │
├―――――――┤
│    代码区    │
├―――――――┤
│  静态数据区  │
├―――――――┤
│    ……      │
├―――――――┤



首先,来了解一下 C 语言的变量是如何在内存分部的。C 语言有全局变量(Global)、本地变量(Local),静态变量(Static)、寄存器变量(Regeister)。其中全局变量和静态变量是存放在静态数据区的,存放他们的内存地址是绝对的,进程中任何代码都可以访问他们;本地变量是在堆、栈中分配的;寄存器变量存放在 CPU 的寄存器中,实现高速存取,但是否真的这样分配,还要视硬件环境和编译器而异,所以并不太用到。例如:

void func()
{
char szText[100]="hello,China!";
char *lpText="hello,world!";
MessageBox(NULL,"text","title",MB_OK);
}

在上面那段代码中,字符串 "hello,China!" 将被分配(请注意这里的用词,事实上在 szText 这个字符串被初始化前,"hello,China!"这个字符串数据本身是在静态内存空间中的)在栈内存中的,而 "hello,world!"、"text"、"title" 都在静态内存区里的。还有一点要提一下,如果是用 VC 编译的话,那么变量在内存中的分配并不是连续的,而是按四字节(DOUBLE WORD)对齐的,即使是一个 char,它也要占用4各字节。而结构体则默认按八字节(QUAD WORD)分段,内部的成员是紧密排列的,但结构体的大小必须是 8 字节的整数倍,这就是用 sizeof() 得到的大小和实际占用空间大小不一致的原因。

说到这里,就不得不提到“缓冲区溢出”了。在 windows 下,缓中溢出分为堆溢出(heap overflow)和栈溢出(stack overflow),造成溢出的原因都是由于分配的内存空间小于实际使用的空间,导致输入的数据覆盖了相邻的内存空间。栈溢出导致执行任意代码的过程是这样的:栈溢出是发生在函数调用过程中的,调用函数时 CPU 执行 call 指令,它先把函数的返回地址压入堆栈,再把 EBP(2) 址压入堆栈。函数的返回地址是函数执行结束后调用的第一个指令的地址,即是说,函数返回后,CPU 就将执行返回地址指向的代码。而堆栈的顺序和内存顺序是相反的,内存中数据是由低端开始,堆栈中的数据由高端开始,栈顶的地址随着数据的压入而变小,这就导致了函数中的动态数据可能覆盖函数的返回指针,导致函数返回后没有执行原来压入的返回地址指向的代码,而是执行了可能是恶意构造的返回地址指向的代码,这个返回地址指针就指向了一段 Shellcode。


二、可执行文件到内存的映射

windows 下主要的可执行文件格式就是 PE 格式。它的结构如下图:

�q―――――――�r
│ DOS MZ header│
├―――――――┤
│ DOS stub    │
├―――――――┤
│ PE header    │
├―――――――┤
│ Section table│
├―――――――┤
│ Section 1    │
├―――――――┤
│ Section 2    │
├―――――――┤
│ ……        │
├―――――――┤
│ Section n    │
�t―――――――�s

在 PE header 中存放了一些包含运行方式,文件属性等等信息,在这里就不做详解。当操作系统运行一个 PE 文件时,会将 PE 文件映射到改进程的内存空间里,当然,并不是原封不动地映射,而是会根据 PE header 中的信息做一些处理,比如函数导入表,偏移量等等。可执行文件的代码、数据会根据不同的逻辑属性分段存放在各个 Section 中,每个 Section 都有自己的名字,每个 Section 都有自己的属性,这些属性会被对应到内存的属性,如:可读、可写、可执行、写入复制。一般来说,名为“.text”的节存放可执行代码,名为“.data”的节存放数据,名为“.rdata”的节存放只读数据,名为“.rsrc”的节存放资源(菜单、对话框、字符串等)。PE header 有2个重要的信息,一个是“ImageBase”,一个是“AddressOfEntryPoint”。“ImageBase”指定了该 PE 文件的映射的基地址,“AddressOfEntryPoint”指定了可执行代码的入口地址。DLL 也是 PE 格式文件,它能被动态地加载到进程的内存空间中,DLL 文件也有“ImageBase”,但问题是 DLL 并不一定能加载到这个基地址,它可能只能加载到另外一个内存空间中。那么,DLL 中的一些代码就可能不能正确执行了,如以下代码:

#include <windows.h>

char szText[]="hey,Juliey!";

BOOL APIENTRY DllMain(HANDLE hModule,DWORD ul_reason_for_call,LPVOID lpReserved)
{
MessageBox(NULL,szText,"",MB_OK);
return TRUE;
}

其中 szText 这个指针的内容在代码被编译时就决定了,这个 DLL 也就必须加载到它的基地址,MessaeBox 函数才能得到正确执行。为了应对 DLL 可能不被加载到基地址的情况,szText 必须被重定位,PE 文件的“.reloc”节就是存放重定位信息的。

(先写到这里,待续)

三、进程内存空间的布局

四、内存的分页机制

五、进程间通信











(1)PEB 进程环境块,存放进程信息的。
(1)EBP 堆栈基地址。

地主 发表时间: 04-01-14 20:42

回复: newmyth21 [newmyth21]   论坛用户   登录
很好啊,顶了。

B1层 发表时间: 04-01-15 00:11

回复: hannyu [hannyu]   论坛用户   登录
能再讲得通俗的么,不是说你讲的不好,我感觉没头没尾的。

B2层 发表时间: 04-01-17 14:30

回复: mooncry [mooncry]   论坛用户   登录
原来是教程呀,
让我白高兴一场

B3层 发表时间: 04-06-16 10:28

回复: tianya2003 [tianya2003]   论坛用户   登录
楼上的怎么拉,不喜欢教程吗?

B4层 发表时间: 04-06-16 14:34

回复: lijingxi [lijingxi]   见习版主   登录
我也不是很喜欢这样的教程 枯燥!乏味!看着就想睡觉!

B5层 发表时间: 04-06-18 13:13

论坛: 黑客进阶

20CN网络安全小组版权所有
Copyright © 2000-2010 20CN Security Group. All Rights Reserved.
论坛程序编写:NetDemon

粤ICP备05087286号