【转载】C++实现EXE加载到内存执行

MrLee5月前 353

本文代码很受用,而且还有代码段讲解,特此收藏!

目标

写一个程序,读取磁盘上exe程序,并且在内存中展开,且运行

在这里插入图片描述

代码

我先给全部代码,下面是解释部分

// RunExeThread.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <windows.h>
PIMAGE_DOS_HEADER dosHeader;
PIMAGE_NT_HEADERS ntHeader;
PIMAGE_FILE_HEADER fileHeader;
PIMAGE_EXPORT_DIRECTORY exportDirectory;
PIMAGE_SECTION_HEADER  pSectionHeader;
PIMAGE_IMPORT_DESCRIPTOR importDescriptor;
PIMAGE_THUNK_DATA thunkData;
PIMAGE_IMPORT_BY_NAME ordAndName;
PIMAGE_BASE_RELOCATION baseRelocation;
char file[]="路径\\game.exe"; //这是读取文件的路径
LPBYTE lpBaseAddress;
int _tmain(int argc, _TCHAR* argv[])
{
	//将磁盘文件映射到内存中
	HANDLE hcf = CreateFileA(file,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
	HANDLE hcfm = CreateFileMappingA(hcf,NULL,PAGE_READONLY,0,0,0);
	lpBaseAddress=(LPBYTE)MapViewOfFile(hcfm,FILE_MAP_READ,0,0,0);
	//获取一些基本变量
	dosHeader = (PIMAGE_DOS_HEADER)lpBaseAddress;
	int lfanew = dosHeader->e_lfanew;
	ntHeader = (PIMAGE_NT_HEADERS)(lpBaseAddress+lfanew);
	int sizeOfImage = ntHeader->OptionalHeader.SizeOfImage;
	int sizeOfHeader = ntHeader->OptionalHeader.SizeOfHeaders;
	pSectionHeader = (PIMAGE_SECTION_HEADER)(lpBaseAddress+lfanew+sizeof(IMAGE_NT_HEADERS));
	//创建内存空间
	LPBYTE loca = (LPBYTE)VirtualAlloc(NULL,sizeOfImage,MEM_COMMIT,PAGE_EXECUTE_READWRITE);
	//拷贝内存空间
	LPBYTE virseek = (LPBYTE)loca;
	memcpy(loca,lpBaseAddress,sizeOfHeader);
	if(sizeOfHeader%0x1000!=0)
	{
		virseek+=0x1000;
	}
	virseek += (sizeOfHeader/0x1000)*0x1000;
	LPBYTE sectionStartAddr = lpBaseAddress+sizeOfHeader;
	for(int i=0;i<(ntHeader->FileHeader.NumberOfSections);i++,pSectionHeader++)
	{
		int sectionSize = pSectionHeader->Misc.VirtualSize/0x1000;
		if(pSectionHeader->Misc.VirtualSize%0x1000!=0)
		{
			sectionSize++;
		}
		memcpy(virseek,sectionStartAddr,pSectionHeader->SizeOfRawData);
		sectionStartAddr+=pSectionHeader->SizeOfRawData;
		virseek += sectionSize*0x1000;
	}
	//修复IAT表
	ntHeader = (PIMAGE_NT_HEADERS)(loca+lfanew);
	int vAddr = ntHeader->OptionalHeader.DataDirectory[1].VirtualAddress;
	importDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)(loca + vAddr);
	while(importDescriptor->Name)
	{
		HMODULE moduleHandle = LoadLibraryA((LPCSTR)(loca+importDescriptor->Name));
		thunkData = (PIMAGE_THUNK_DATA)(importDescriptor->FirstThunk+loca);
		DWORD * indexAddr = (DWORD *)(importDescriptor->FirstThunk+loca);
		while(thunkData->u1.AddressOfData)
		{
			if(IMAGE_SNAP_BY_ORDINAL(thunkData->u1.AddressOfData))
			{
				LPCSTR addr = (LPCSTR)thunkData->u1.AddressOfData-0x80000000;
				*indexAddr = (DWORD)GetProcAddress(moduleHandle,addr);
			}else{
				ordAndName = (PIMAGE_IMPORT_BY_NAME)(loca + thunkData->u1.AddressOfData);
				*indexAddr = (DWORD)GetProcAddress(moduleHandle,(LPCSTR)(ordAndName->Name));
			}
			thunkData++;
			indexAddr++;
		}
		importDescriptor++;
	}
	//修复重定位表
	baseRelocation = (PIMAGE_BASE_RELOCATION)(loca + (ntHeader->OptionalHeader.DataDirectory[5].VirtualAddress));
	int num;
	WORD *data;
	while(baseRelocation->VirtualAddress+baseRelocation->SizeOfBlock != 0)
	{
		data = (WORD *)((LPBYTE)baseRelocation+0x8);
		num = (baseRelocation->SizeOfBlock-0x8)/sizeof(WORD);
		for(int i = 0;i<num;i++)
		{
			if((DWORD)(data[i]&0x0000F000)==0x00003000)
			{
				DWORD* addr = (DWORD *)(loca+baseRelocation->VirtualAddress+(data[i]&0x00000FFF));
				*addr +=(DWORD)(loca - ntHeader->OptionalHeader.ImageBase);
			}
		}
		baseRelocation = (PIMAGE_BASE_RELOCATION)((LPBYTE)baseRelocation + baseRelocation->SizeOfBlock);
	}
	HANDLE h =CreateThread(0,0,(LPTHREAD_START_ROUTINE)(loca+ntHeader->OptionalHeader.AddressOfEntryPoint),0,0,0);
	DWORD d = WaitForSingleObject(h,INFINITE);
	VirtualFree(loca,0,MEM_RELEASE);
	CloseHandle(hcfm);
	CloseHandle(hcf);
	return 0;
}

文件映射到内存中,且初始化一些变量

	//将磁盘文件映射到内存中
	//打开文件
	HANDLE hcf = CreateFileA(file,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
	//创建文件映射
	HANDLE hcfm = CreateFileMappingA(hcf,NULL,PAGE_READONLY,0,0,0);
	//将文件映射视图映射到调用进程的地址空间。基址
	lpBaseAddress=(LPBYTE)MapViewOfFile(hcfm,FILE_MAP_READ,0,0,0);
	//获取一些基本变量
	dosHeader = (PIMAGE_DOS_HEADER)lpBaseAddress;
	int lfanew = dosHeader->e_lfanew;
	ntHeader = (PIMAGE_NT_HEADERS)(lpBaseAddress+lfanew);
	int sizeOfImage = ntHeader->OptionalHeader.SizeOfImage;
	int sizeOfHeader = ntHeader->OptionalHeader.SizeOfHeaders;
	pSectionHeader = (PIMAGE_SECTION_HEADER)(lpBaseAddress+lfanew+sizeof(IMAGE_NT_HEADERS));

以0x1000粒度拷贝到内存

拷贝过程

1.拷贝各个头部,即节区头以上的部分。(PE结构中在可选头中有SizeOfHeaders指明了大小)

2.各个节区块,如下图

在这里插入图片描述

	//创建内存空间
	LPBYTE loca = (LPBYTE)VirtualAlloc(NULL,sizeOfImage,MEM_COMMIT,PAGE_EXECUTE_READWRITE);
	//拷贝内存空间
	LPBYTE virseek = (LPBYTE)loca;
	//拷贝头
	memcpy(loca,lpBaseAddress,sizeOfHeader);
	//第一个头拷贝完后要先对齐一下,再进行拷贝
	if(sizeOfHeader%0x1000!=0)
	{
		virseek+=0x1000;
	}
	virseek += (sizeOfHeader/0x1000)*0x1000;
	//拷贝节区块
	LPBYTE sectionStartAddr = lpBaseAddress+sizeOfHeader;
	for(int i=0;i<(ntHeader->FileHeader.NumberOfSections);i++,pSectionHeader++)
	{
		int sectionSize = pSectionHeader->Misc.VirtualSize/0x1000;
		if(pSectionHeader->Misc.VirtualSize%0x1000!=0)
		{
			sectionSize++;
		}
		memcpy(virseek,sectionStartAddr,pSectionHeader->SizeOfRawData);
		sectionStartAddr+=pSectionHeader->SizeOfRawData;
		virseek += sectionSize*0x1000;
	}

修改导出表

有人会问为什么要修复导出表呢~

这就跟PE文件展开有关

导入表在磁盘中的存储值如下图

在这里插入图片描述

导出表中有两个成员,OriginalFirstThunk和FirstThunk。他们一开始指向的是不同的IMAGE_THUNK_DATA结构体数组。但是这两个数组指向的内容却是相同的。

在程序运行后
在这里插入图片描述

OriginalFirstThunk没有发生变化
但是FirstThunk却发生了改变
他在运行完成后指向的地方变成了函数的入口地址。
因为它的改变,我们就要去修改它。

	//修复IAT表
	ntHeader = (PIMAGE_NT_HEADERS)(loca+lfanew);
	int vAddr = ntHeader->OptionalHeader.DataDirectory[1].VirtualAddress;
	importDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)(loca + vAddr);
	while(importDescriptor->Name)
	{
		HMODULE moduleHandle = LoadLibraryA((LPCSTR)(loca+importDescriptor->Name));
		thunkData = (PIMAGE_THUNK_DATA)(importDescriptor->FirstThunk+loca);
		DWORD * indexAddr = (DWORD *)(importDescriptor->FirstThunk+loca);
		while(thunkData->u1.AddressOfData)
		{
			if(IMAGE_SNAP_BY_ORDINAL(thunkData->u1.AddressOfData))
			{
				LPCSTR addr = (LPCSTR)thunkData->u1.AddressOfData-0x80000000;
				*indexAddr = (DWORD)GetProcAddress(moduleHandle,addr);
			}else{
				ordAndName = (PIMAGE_IMPORT_BY_NAME)(loca + thunkData->u1.AddressOfData);
				*indexAddr = (DWORD)GetProcAddress(moduleHandle,(LPCSTR)(ordAndName->Name));
			}
			thunkData++;
			indexAddr++;
		}
		importDescriptor++;
	}

修复重定向

这里又要了解下为啥修复重定向咯

我们程序有个基址重定向机制,有了这个机制,我们才能在一台电脑中跑多个程序。不然默认的exe基址都是0x400000,会有冲突。

在带来了更好的体验的同时,也给我带来了小困扰 --无明

总而言之,言而总之。就是基址变化,重定位表里面存放的是针对默认的基址准备的VA地址

现在我们基址改变了。我们就要改变他。

//修复重定位表
	baseRelocation = (PIMAGE_BASE_RELOCATION)(loca + (ntHeader->OptionalHeader.DataDirectory[5].VirtualAddress));
	int num;
	WORD *data;
	while(baseRelocation->VirtualAddress+baseRelocation->SizeOfBlock != 0)
	{
		data = (WORD *)((LPBYTE)baseRelocation+0x8);
		num = (baseRelocation->SizeOfBlock-0x8)/sizeof(WORD);
		for(int i = 0;i<num;i++)
		{
			if((DWORD)(data[i]&0x0000F000)==0x00003000)
			{
				DWORD* addr = (DWORD *)(loca+baseRelocation->VirtualAddress+(data[i]&0x00000FFF));
				*addr +=(DWORD)(loca - ntHeader->OptionalHeader.ImageBase);
			}
		}
		baseRelocation = (PIMAGE_BASE_RELOCATION)((LPBYTE)baseRelocation + baseRelocation->SizeOfBlock);
	}


本文链接:https://www.it72.com/12671.htm

推荐阅读
最新回复 (0)
    返回