17.4 内存映射文件实现进程共享数据--<Windows核心编程>
内存映射文件用于进程通信的概念
许多应用程序会在运行过程中创建一些数据,并需要将这些数据传输给其他进程,或与其他进程共享这些数据。如果为了共享数据而必须让应用程序在磁盘上创建数据文件并把数据保存在文件中,那将非常不方便。
Microsoft意识到了这一点,并加入了相应的支持,让系统能够创建以页交换文件为后备存储器的内存映射文件,这样就不需要用磁盘上专门的文件来作为后备存储器了。这种方法和为磁盘文件创建内存映射文件的方法几乎完全相同,甚至更简单。一方面,由于不必创建或打开一个专门的磁盘文件,因此不需要调用CreateFile。我们只需要像原来那样调用CreateFileMapping,并将INVALID_HANDLE_VALUE作为hFile参数传入。这告诉系统我们创建的文件映射对象的物理存储器不是磁盘上的文件,而是希望系统从页交换文件中调拨物理存储器。所需分配的存储器大小由CreateFileMapping的dwMaximumSizeHigh和dwMaxinumSizeLow参数决定。
内存文件映射是Windows的一种内存管理方法,提供了一个统一的内存管理特征,使应用程序可以通过内存指针对磁盘上的文件进行访问,其过程就如同对加载了文件的内存的访问。通过文件映射这种使磁盘文件的全部或部分内容与进程虚拟地址空间的某个区域建立映射关联的能力,可以直接对被映射的文件进行访问,而不必执行文件I/O操作也无需对文件内容进行缓冲处理。
一、内存映射文件可以分为两种
(1)一种是普通的文件,它需要一个普通的文件句柄,用于快速的读写文件,这种文件的数据在进程退出后会保存在硬盘上,所以进程在下次运行时可以得到之前的数据;
(2)另一种是页文件,当创建内存映射文件的时候传入无效的句柄,这时会把页文件当作临时的共享存储空间,当进程退出后这些数据是不会存储下来的。因为共享内存通常只关注执行期间的数据共享,所以一般是使用这种内存映射文件。
二、内存映射文件在用于进程通信时分为两种
命名文件映射对象(为对象命名)、匿名文件映射对象(复制对象句柄)
三、内存映射文件在用于进程通信概念图
内存映射文件用于进程通信的流程
要使用内存映射文件,需要执行下面三个步骤。
(1)创建或打开一个文件内核对象,该对象标识了我们想要用作内存映射文件的那个磁盘文件。
(2)创建一个文件映射内核对象(file-mapping kernel object)来告诉系统文件的大小以及我们打算如何访问文件。
(3)告诉系统把文件映射对象的部分或全部映射到进程的地址空间中。
用完内存映射文件之后,必须执行下面三个步骤来做清理工作。
(1)告诉系统从进程地址空间中取消对文件映射内核对象的映射。
(2)关闭文件映射内核对象。
(3)关闭文件内核对象。
一、创建或打开文件内核对象
我们总是通过调用CreateFile函数来创建或打开一个文件内核对象:
HANDLE CreateFile( LPCTSTR lpFileName, // 想创建的或打开的文件的名称(既可以包含路径,也可以不包含路径) DWORD dwDesiredAccess, // 如何访问文件内容;0(获得文件属性)、GENERIC_READ、GENERIC_WRITE、GENERIC_READ|GENERIC_WRITE DWORD dwShareMode, // 如何共享这个文件 LPSECURITY_ATTRIBUTES lpSecurityAttributes, // 安全属性 DWORD dwCreationDispostion , DWORD dwFlagsAndAttributes, HANDLE hTemplateFile);
关于第二参数:
0 表示该对象的设备查询访问权限。应用程序可以在不访问设备的情况下查询设备属性。
GENERIC_READ 该对象的读访问权限。可以从文件中读取数据并移动文件指针。结合GENERIC_WRITE进行读写访问。
GENERIC_WRITE 对象的写访问权限。数据可以写入文件,文件指针可以移动。结合GENERIC_READ进行读写访问。
关于第三参数:
0 想要独占对文件的访问,使其他进程无法打开同一文件
FILE_SHARE_READ 对象的后续打开操作只有在请求读访问时才会成功。
FILE_SHARE_WRITE 对象的后续打开操作只有在请求写访问时才会成功。
如果CreateFile成功地创建或打开了指定的文件,它会返回一个文件内核对象的句柄。否则,它返回INVALID_HANDLE_VALUE。
二、创建文件映射的内核对象
调用CreateFile是为了告诉操作系统文件映射的物理存储器所在的位置。传入的路径是文件在磁盘(也可以是网络或光盘)上所在的位置,文件映射对象的物理存储器来自该文件。现在我们必须告诉系统文件映射对象需要多大的物理存储器。为了达到这一目的,必须调CreateFileMapping。
这个函数为指定的文件创建一个命名或未命名的文件映射对象。
HANDLE CreateFileMapping( HANDLE hFile, // 需要映射到进程空间的文件的句柄,如果是通过文件映射对象进行进程间通信,这个参数可以传INVALID_HANDLE_VALUE LPSECURITY_ATTRIBUTES lpFileMappingAttributes, // 指向SECURITY_ATTRIBUTES结构体,它用于文件映射内核对象,一般传NULL DWORD flProtect, // 映射的页面要指定的保护属性 DWORD dwMaximumSizeHigh, // 描述内存映射文件最大的大小:由SizeHigh(高32位)和SizeLow(低32位,如下)组成 DWORD dwMaximumSizeLow, // 描述内存映射文件最大的大小:由SizeHigh(高32位,如上)和SizeLow(低32位)组成 LPCTSTR lpName ); // 文件映射对象的名称
内存映射文件的物理存储器来自于磁盘上的文件,而不是从系统的页交换文件中分配的。创建一个文件映射对象的时候,系统不会预订一块地址空间区域并把文件映射到该区域中
关于第三参数:
关于第四五参数:
CreateFileMapping函数的主要目的是为了确保有足够的物理存储器可供文件映射对象使用。这两个参数告诉系统内存映射文件的最大大小,以字节为单位。由于Windows支持的最大文件大小可以用64位整数表示,因此这里必须使用两个32位值,其中参数dwMaximumSizeHigh表示高32位,而参数dwMaximumSizeLow则表示低32位。对小于4GB的文件来说, dwMaximumSizeHigh始终为0。
三、将文件的数据映射到进程的地址空间
在创建了文件映射对象之后,还需要为文件的数据预订一块地址空间区域并将文件的数据作为物理存储器调拨给区域。这可以通过调用MapViewOfFile来实现。这个函数返回这块区域的地址。
这个函数将一个文件的视图映射到调用进程的地址空间。
LPVOID MapViewOfFile( HANDLE hFileMappingObject, // 文件映射对象的句柄,调用Create/OpenFileMapping返回的 DWORD dwDesiredAccess, // 如何访问数据 DWORD dwFileOffsetHigh, // 关于将文件中哪个字节映射到视图中的第一个字节 DWORD dwFileOffsetLow, // 关于将文件中哪个字节映射到视图中的第一个字节 DWORD dwNumberOfBytesToMap ); // 把数据文件中的多少映射到视图中
关于第二参数:
关于第三四参数:
把文件的一个视图映射到进程的地址空间中时,必须告诉系统两件事情。第一,我们必须告诉系统应该把数据文件中的哪个字节映射到视图中的第一个字节。这是通过参数dwFileOffsetHigh和dwFileOfsetLow来指定的。由于Windows支持的文件大小最大可以到16 EB,因此编移量也必须用64位值来指定,其中高32位的部分由dwFileOffsetHigh表示,而低32位的部分则由dwFileOffsetLow表示。注意,文件的偏移量必须是系统分配粒度的整数倍。(到目前为止,在所有版本的Windows中,分配粒度全部都是64 KB)
四、从进程的地址空间撤销对文件数据的映射
不再需要把文件的数据映射到进程的地址空间中时,可以调用下面的函数来释放内存区域:
BOOL UnmapviewofFile (PVOID pvBaseAddress);
这个函数唯一的参数pvBaseAddress用来指定区域的基地址,它必须和MapViewOfFile的返回值相同。确定调用UnmapViewOfFile,如果不这样做,在进程终止之前,区域将得不,到释放。在调用MapViewOFile的时候,系统总是会在进程的地址空间中预订一块新的区域,它不会释放之前预订的任何区域。
如果需要确保所做的修改已经被写入到磁盘中,那么可以调用FlushViewOfFile,这个函数用来强制系统把部分或全部修改过的数据写回到磁盘中
这个函数向磁盘写入文件映射视图内的一个字节范围。
BOOL FlushViewOfFile( LPCVOID lpBaseAddress, // 内存映射文件的视图中第一个字节的地址 DWORD dwNumberOfBytesToFlush ); // 想刷新的字节数
五、关闭文件映射对象和文件对象
调用CloseHandle关闭两个句柄
进程通信 --- 命名内存映射文件
创建命名文件映射对象函数内部调用了CreateFileMapping函数创建一个命名内存对象,并通过MapViewOfFile函数用于映射一块虚拟内存,得到虚拟地址;
打开命名文件映射对象函数内部调用了OpenFileMapping函数打开映射对象,并通过MapViewOfFile函数用于映射一块虚拟内存,得到虚拟地址;
A进程
//创建一个命名内存映射对象 if (SunCreateMemroyMappingEx(ACCESS_WRITE, 0, 0x1000, _T("SHINE"), &MappingHandle, &VirtualAddress) == FALSE) BOOL SunCreateMemroyMappingEx( DWORD ReadOrWrite, DWORD MaximumSizeHigh, DWORD MaximumSizeLow, LPCTSTR ObjectName, _Out_ LPHANDLE MappingHandle, _Out_ ULONG_PTR* VirtualAddress)
参数解释:
//DWORD MaximumSizeHigh,
DWORD MaximumSizeLow,
如果低位MaximumSizeLow存的是0x1000,代表文件大小0x1000(4096bytes),
且如果此时高位MaximumSizeHigh为1,代表低位的0x1000全部填满,向高位进1,代表文件大小4G(4096BYTES),
如果此时高位MaximumSizeHigh为10的话,文件大小共10x4g;
//LPCTSTR ObjectName
对象名ObjectName只能用GUID(全球唯一身法码,网卡)
//LPHANDLE MappingHandle 返回值(不能传空)
//ULONG_PTR* VirtualAddress 被修改的内存(不能传空)
//内部调用
CreateFileMapping函数创建一个命名内存对象
MapViewOfFile函数用于映射一块虚拟内存,得到虚拟地址
B进程
//打开命名文件映射对象 if (SunOpenMemoryMappingEx(ACCESS_WRITE, FALSE, _T("SHINE"), &MappingHandle, &VirtualAddress) == FALSE) BOOL SunOpenMemoryMappingEx( OPERATION ReadOrWrite, BOOL IsInheritHandle, LPCTSTR ObjectName, _Out_ LPHANDLE MappingHandle, _Out_ ULONG_PTR* VirtualAddress)
参数解释:
//LPCTSTR ObjectName(不能传空),传A进程创建时的OjectName
//LPHANDLE MappingHandle:返回值(不能传空)
//ULONG_PTR* VirtualAddress:被修改的内存(不能传空)
//内部调用
OpenFileMapping函数打开映射对象
MapViewOfFile函数用于映射一块虚拟内存,得到虚拟地址
详细代码
A进程:
#include "Process(A).h" void _tmain(int argc, TCHAR *argv[], TCHAR *envp[]) { setlocale(LC_ALL, "chs"); _tprintf(_T("我是进程A:\r\n")); Sub_1(); //命名对象 _tprintf(_T("Input AnyKey To Exit\r\n")); _gettchar(); _gettchar(); return; } void Sub_1() { HANDLE MappingHandle = NULL; ULONG_PTR VirtualAddress = NULL; DWORD LastError = 0; //Mapping 映射 //Image 镜像 //PageSize 0x1000 4096Bytes //创建一个命名内存映射对象 if (SunCreateMemroyMappingEx(ACCESS_WRITE, 0, 0x1000, _T("SHINE"), &MappingHandle, &VirtualAddress) == FALSE) { LastError = GetLastError(); } else { __try { memcpy((LPVOID)VirtualAddress, _T("我将无我,不负人民"), sizeof(TCHAR)*(_tcslen(_T("我将无我,不负人民")) + 1)); //将数据拷贝到内存中 (映射 if (VirtualAddress != NULL) { _tprintf(_T("%s\r\n"), (TCHAR*)VirtualAddress); } } __except(EXCEPTION_EXECUTE_HANDLER) { LastError = GetExceptionCode(); } } _tprintf(_T("Input AnyKey To Continue\r\n")); _gettchar(); if(VirtualAddress!=NULL) { _tprintf(_T("%s\r\n"), (TCHAR*)VirtualAddress); } SunUnmapMemoryEx(MappingHandle, VirtualAddress); //消亡映射对象 return; } /*创建一个命名文件映射对象*/ BOOL SunCreateMemroyMappingEx(OPERATION ReadOrWrite, DWORD MaximumSizeHigh, DWORD MaximumSizeLow, LPCTSTR ObjectName, _Out_ LPHANDLE MappingHandle, _Out_ ULONG_PTR* VirtualAddress) { DWORD DesiredAccess = 0; //期待如何使用 DWORD Protect = 0; //保护属性 HANDLE v1 = NULL; LPVOID v5 = NULL; int LastError = 0; //MappingHandle,VirtualAddress不能传空 if (MappingHandle == NULL || VirtualAddress == NULL) { LastError = ERROR_INVALID_PARAMETER; goto Exit; } //读写属性设置 if (ReadOrWrite == ACCESS_READ) { Protect = PAGE_READONLY; DesiredAccess = SECTION_MAP_READ; } else if (ReadOrWrite == ACCESS_WRITE) { Protect = PAGE_READWRITE; DesiredAccess = SECTION_MAP_READ | SECTION_MAP_WRITE; } else { LastError = ERROR_INVALID_PARAMETER; goto Exit; } __try { //创建一个命名内存对象 //SunctionObject //MappingObject v1 = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, Protect, MaximumSizeHigh, MaximumSizeLow, ObjectName); if (v1 != NULL) { *MappingHandle = v1;//v1赋值给返回值变量 //通过句柄获得映射的虚拟内存 v5 = MapViewOfFile(v1, DesiredAccess, 0, 0, 0); if (v5 != NULL) { (*VirtualAddress) = (ULONG_PTR)v5; return TRUE; } } } __except (EXCEPTION_EXECUTE_HANDLER) { LastError = GetExceptionCode(); goto Exit; } Exit: SetLastError(LastError); return FALSE; } /*消亡映射对象*/ void SunUnmapMemoryEx(_In_ HANDLE MappingHandle, _In_ ULONG_PTR VirtualAddress) { __try { if (UnmapViewOfFile((void*)VirtualAddress)) { SunCloseHandle(MappingHandle); } } __except (EXCEPTION_EXECUTE_HANDLER) { SetLastError(GetExceptionCode()); } } bool SunCloseHandle(HANDLE HandleValue) { DWORD HandleFlags; if (GetHandleInformation(HandleValue, &HandleFlags) && (HandleFlags & HANDLE_FLAG_PROTECT_FROM_CLOSE) != HANDLE_FLAG_PROTECT_FROM_CLOSE) return !!CloseHandle(HandleValue); return false; }
B进程:
#include "Process(B).h" void _tmain(int argc, TCHAR *argv[], TCHAR *envp[]) { setlocale(LC_ALL, "chs"); _tprintf(_T("我是进程B:\r\n")); Sub_1(); //命名对象 _tprintf(_T("Input AnyKey To Exit\r\n")); _gettchar(); _gettchar(); return; } void Sub_1() { HANDLE MappingHandle = NULL; ULONG_PTR VirtualAddress = NULL; DWORD LastError = 0; //打开命名文件映射对象 if (SunOpenMemoryMappingEx(ACCESS_WRITE, FALSE, _T("SHINE"), &MappingHandle, &VirtualAddress) == FALSE) { GetLastError(); } else { __try { _tprintf(_T("%s\r\n"), (TCHAR*)VirtualAddress); //输出A进程曾写入的内存 memcpy((LPVOID)VirtualAddress, _T("为人民服务"), sizeof(TCHAR)*(_tcslen(_T("为人民服务")) + 1)); //B进程重新写这块内存,之后A进程再输出一次,结果为"为人民服务",而不是"我将无我,不负人民" } __except (EXCEPTION_EXECUTE_HANDLER) { LastError = GetExceptionCode(); } } SunUnmapMemoryEx(MappingHandle, VirtualAddress); return; } /*打开命名文件映射对象*/ BOOL SunOpenMemoryMappingEx(OPERATION ReadOrWrite, BOOL IsInheritHandle, LPCTSTR ObjectName, _Out_ LPHANDLE MappingHandle,_Out_ ULONG_PTR* VirtualAddress) { DWORD DesiredAccess = 0; HANDLE v1 = NULL; LPVOID v5 = NULL; int LastError = 0; if (ObjectName == NULL || MappingHandle == NULL || VirtualAddress == NULL) { LastError = ERROR_INVALID_PARAMETER; goto Exit; } if (ReadOrWrite == ACCESS_READ) { DesiredAccess = SECTION_MAP_READ; } else if (ReadOrWrite == ACCESS_WRITE) { DesiredAccess = SECTION_MAP_READ | SECTION_MAP_WRITE; } else { LastError = ERROR_INVALID_PARAMETER; goto Exit; } __try { v1 = OpenFileMapping(DesiredAccess, IsInheritHandle, ObjectName); if (v1 != NULL) { *MappingHandle = v1; //MapViewOfFile函数用于映射一块虚拟内存,得到虚拟地址 v5 = MapViewOfFile(v1, DesiredAccess, 0, 0, 0); if (v5 != NULL) { (*VirtualAddress) = (ULONG_PTR)v5; return TRUE; } } } __except (EXCEPTION_EXECUTE_HANDLER) { LastError = GetExceptionCode(); goto Exit; } Exit: SetLastError(LastError); return FALSE; } void SunUnmapMemoryEx(HANDLE MappingHandle, ULONG_PTR VirtualAddress) { __try { if (UnmapViewOfFile((void*)VirtualAddress)) { SunCloseHandle(MappingHandle); } } __except (EXCEPTION_EXECUTE_HANDLER) { SetLastError(GetExceptionCode()); } } bool SunCloseHandle(HANDLE HandleValue) { DWORD HandleFlags; if (GetHandleInformation(HandleValue, &HandleFlags) && (HandleFlags & HANDLE_FLAG_PROTECT_FROM_CLOSE) != HANDLE_FLAG_PROTECT_FROM_CLOSE) return !!CloseHandle(HandleValue); return false; }
进程通信 --- 匿名内存映射文件
创建匿名文件映射对象和创建命名的相同,只不过ObjectName传NULL;
打开匿名文件映射对象要用到暴力搜索句柄表获得A进程的对象句柄,根据对象句柄获得其创建的匿名文件映射对象的MappingHandle,再通过拷贝DuplicateHandle函数获得映射对象句柄;
A进程
//创建时同命名文件映射对象,只不过ObjectName传NULL //创建一个匿名内存映射对象 if (SunCreateMemroyMappingEx(ACCESS_WRITE, 0, 0x1000, NULL, &MappingHandle, &VirtualAddress) == FALSE) BOOL SunCreateMemroyMappingEx( DWORD ReadOrWrite, DWORD MaximumSizeHigh, DWORD MaximumSizeLow, LPCTSTR ObjectName, _Out_ LPHANDLE MappingHandle, _Out_ ULONG_PTR* VirtualAddress)
参数解释:
//DWORD MaximumSizeHigh,
DWORD MaximumSizeLow,
如果低位MaximumSizeLow存的是0x1000,代表文件大小0x1000(4096bytes),
且如果此时高位MaximumSizeHigh为1,代表低位的0x1000全部填满,向高位进1,代表文件大小4G(4096BYTES),
如果此时高位MaximumSizeHigh为10的话,文件大小共10x4g;
//LPCTSTR ObjectName 对象名传NULL
//LPHANDLE MappingHandle 返回值(不能传空)
//ULONG_PTR* VirtualAddress 被修改的内存(不能传空)
//内部调用
CreateFileMapping函数创建一个命名内存对象
MapViewOfFile函数用于映射一块虚拟内存,得到虚拟地址
B进程
暴力搜索句柄表获得A进程的对象句柄,根据对象句柄获得其创建的匿名文件映射对象的MappingHandle(本代码中还未涉及暴力搜索),这里就先拿A进程Pid获取A进程句柄,并且传入A进程中的MappingHandle。
//根据目标进程的ID打开目标进程 ProcessHandle = SunOpenProcess(PROCESS_DUP_HANDLE, FALSE, ProcessIdentify);
拷贝DuplicateHandle函数获得映射对象句柄
根据A进程句柄和A的MappingHandle句柄值,从A句柄表
//DuplicateHandle获取进程虚拟空间地址 BOOL IsOk = DuplicateHandle(ProcessHandle, MappingHandle, GetCurrentProcess(), &v1, 0, FALSE, DUPLICATE_SAME_ACCESS);
详细代码
A进程:
#include "Process(A).h" void _tmain(int argc, TCHAR *argv[], TCHAR *envp[]) { setlocale(LC_ALL, "chs"); Sub_1(); _tprintf(_T("Input AnyKey To Exit\r\n")); _gettchar(); _gettchar(); return; } void Sub_1() { HANDLE MappingHandle = NULL; ULONG_PTR VirtualAddress = NULL; __try { //创建一个匿名内存映射对象 if (SunCreateMemroyMappingEx(ACCESS_WRITE, 0, 0x1000, NULL, &MappingHandle, &VirtualAddress) == FALSE) { goto Exit; } else { //获得当前进程ID _tprintf(_T("当前进程Identify:%d\r\n"), GetCurrentProcessId()); _tprintf(_T("当前进程创建(SunctionObject)的内核对象句柄:%d\r\n"),MappingHandle); _tprintf(_T("运行Process(B)程序\r\n")); memcpy((LPVOID)VirtualAddress, _T("我将无我,不负人民"), sizeof(TCHAR)*(_tcslen(_T("我将无我,不负人民")) + 1)); _tprintf(_T("Input AnyKey To Continue\r\n")); _gettchar(); _tprintf(_T("%s\r\n"), (TCHAR*)VirtualAddress); } } __except (EXCEPTION_EXECUTE_HANDLER) { SetLastError(GetExceptionCode()); } Exit: if (MappingHandle!=NULL&&VirtualAddress!=NULL) { SunUnmapMemoryEx(MappingHandle,VirtualAddress); } return; } BOOL SunCreateMemroyMappingEx(DWORD ReadOrWrite,DWORD MaximumSizeHigh, DWORD MaximumSizeLow,LPCTSTR ObjectName,LPHANDLE MappingHandle,ULONG_PTR* VirtualAddress) { DWORD DesiredAccess = 0; //期待如何使用 DWORD Protect = 0; //保护属性 HANDLE v1 = NULL; LPVOID v5 = NULL; int LastError = 0; //MappingHandle,VirtualAddress不能传空 if (MappingHandle == NULL || VirtualAddress == NULL) { LastError = ERROR_INVALID_PARAMETER; goto Exit; } //读写属性设置 if (ReadOrWrite == ACCESS_READ) { Protect = PAGE_READONLY; DesiredAccess = SECTION_MAP_READ; } else if (ReadOrWrite == ACCESS_WRITE) { Protect = PAGE_READWRITE; DesiredAccess = SECTION_MAP_READ | SECTION_MAP_WRITE; } else { LastError = ERROR_INVALID_PARAMETER; goto Exit; } __try { //创建一个匿名内存对象 //Protect内存读写属性 v1 = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, Protect, MaximumSizeHigh,MaximumSizeLow,ObjectName); if (v1 != NULL) { //函数调用成功 *MappingHandle = v1; //通过句柄获取虚拟地址 v5 = MapViewOfFile(v1, DesiredAccess, 0, 0, 0); if (v5 != NULL) { (*VirtualAddress) = (ULONG_PTR)v5; return TRUE; } } } __except (EXCEPTION_EXECUTE_HANDLER) { LastError = GetExceptionCode(); goto Exit; } Exit: SetLastError(LastError); return FALSE; } /*消亡映射对象*/ void SunUnmapMemoryEx(HANDLE MappingHandle, ULONG_PTR VirtualAddress) { __try { //UnmapViewOfFile函数:回收虚拟地址 if (UnmapViewOfFile((void*)VirtualAddress)) { SunCloseHandle(MappingHandle); } } __except (EXCEPTION_EXECUTE_HANDLER) { SetLastError(GetExceptionCode()); } } bool SunCloseHandle(HANDLE HandleValue) { DWORD HandleFlags; if (GetHandleInformation(HandleValue, &HandleFlags) && (HandleFlags & HANDLE_FLAG_PROTECT_FROM_CLOSE) != HANDLE_FLAG_PROTECT_FROM_CLOSE) return !!CloseHandle(HandleValue); return false; }
B进程:
#include "Process(B).h" BOOL __EnableDebugPrivilege = TRUE; void _tmain(int argc, TCHAR *argv[], TCHAR *envp[]) { setlocale(LC_ALL, "chs"); Sub_1(); _tprintf(_T("Input AnyKey To Exit\r\n")); _gettchar(); _gettchar(); return; } void Sub_1() { HANDLE ProcessIdentify = 0; HANDLE MappingHandle = NULL; ULONG_PTR VirtualAddress = NULL; HANDLE v1 = NULL; HANDLE ProcessHandle = NULL; _tprintf(_T("输入Process(A)进程Identify与内核对象的句柄值\r\n")); __try { //如何知道进程 _tscanf(_T("%d"), &ProcessIdentify); _tscanf(_T("%d"), &MappingHandle); //根据目标进程的ID打开目标进程 ProcessHandle = SunOpenProcess(PROCESS_DUP_HANDLE, FALSE, ProcessIdentify); if (ProcessHandle == NULL) { goto Exit; } //Anti策略 //DuplicateHandle获取进程虚拟空间地址 BOOL IsOk = DuplicateHandle(ProcessHandle, MappingHandle, GetCurrentProcess(), &v1, 0, FALSE, DUPLICATE_SAME_ACCESS); if ((VirtualAddress = (ULONG_PTR)MapViewOfFile(v1,FILE_MAP_READ|FILE_MAP_WRITE,0,0,0))==NULL) { int LastError = GetLastError(); goto Exit; } else { _tprintf(_T("%s\r\n"), (TCHAR*)VirtualAddress); memcpy((LPVOID)VirtualAddress, _T("为人民服务"), sizeof(TCHAR)*(_tcslen(_T("为人民服务")) + 1)); } } __except (EXCEPTION_EXECUTE_HANDLER) { SetLastError(GetExceptionCode()); } Exit: if (ProcessHandle!=NULL) { SunCloseHandle(ProcessHandle); } if (v1!=NULL) { SunUnmapMemoryEx(v1,VirtualAddress); } return; } void SunUnmapMemoryEx(HANDLE MappingHandle, ULONG_PTR VirtualAddress) { __try { if (UnmapViewOfFile((void*)VirtualAddress)) { SunCloseHandle(MappingHandle); } } __except (EXCEPTION_EXECUTE_HANDLER) { SetLastError(GetExceptionCode()); } } bool SunCloseHandle(HANDLE HandleValue) { DWORD HandleFlags; if (GetHandleInformation(HandleValue, &HandleFlags) && (HandleFlags & HANDLE_FLAG_PROTECT_FROM_CLOSE) != HANDLE_FLAG_PROTECT_FROM_CLOSE) return !!CloseHandle(HandleValue); return false; } DWORD SunEnableSunDebugPrivilege(HANDLE ProcessHandle, BOOL IsEnable) { DWORD LastError; HANDLE TokenHandle = 0; if (!OpenProcessToken(ProcessHandle, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &TokenHandle)) { LastError = GetLastError(); if (TokenHandle) CloseHandle(TokenHandle); return LastError; } TOKEN_PRIVILEGES TokenPrivileges; memset(&TokenPrivileges, 0, sizeof(TOKEN_PRIVILEGES)); LUID v1; if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &v1)) { LastError = GetLastError(); CloseHandle(TokenHandle); return LastError; } TokenPrivileges.PrivilegeCount = 1; TokenPrivileges.Privileges[0].Luid = v1; if (IsEnable) TokenPrivileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; else TokenPrivileges.Privileges[0].Attributes = 0; AdjustTokenPrivileges(TokenHandle, FALSE, &TokenPrivileges, sizeof(TOKEN_PRIVILEGES), NULL, NULL); LastError = GetLastError(); CloseHandle(TokenHandle); return LastError; } HANDLE SunOpenProcess(DWORD DesiredAccess, BOOL IsInheritHandle, HANDLE ProcessIdentify) { if (__EnableDebugPrivilege) { SunEnableSunDebugPrivilege(GetCurrentProcess(), TRUE); } HANDLE ProcessHandle = OpenProcess(DesiredAccess, IsInheritHandle, (DWORD)ProcessIdentify); DWORD LastError = GetLastError(); if (__EnableDebugPrivilege) { SunEnableSunDebugPrivilege(GetCurrentProcess(), FALSE); } SetLastError(LastError); return ProcessHandle; }