首页
社区
课程
招聘
[旧帖] [原创]应用层模拟加载PE可执行文件的简单程序 0.00雪花
发表于: 2012-11-30 17:44 1885

[旧帖] [原创]应用层模拟加载PE可执行文件的简单程序 0.00雪花

2012-11-30 17:44
1885
为了加深对PE文件结构的理解,参考了些资料,写了个简单的模拟PE文件加载过程的程序,不过程序有些地方没处理好(资源节处),也存在这些小bug,仅作交流用…………

写的分四个文件写的,如下:

ReadFile.h  :

#include <stdio.h>
#include <windows.h>
#include <assert.h>

HANDLE OpenFile(char *name);
int ReadFile(HANDLE m_fd, size_t offset, size_t size, void *pBuff );
void CloseFile(HANDLE m_fd);


ReadFile.cpp  :

#include "ReadFile.h"


HANDLE OpenFile(char *name)
{
	assert(NULL!=name);
	assert(0!=name[0]);

//////////////////////////////////////////////////////////////////////////
	//打开文件;
	HANDLE m_fd;
	m_fd=CreateFileA(name,GENERIC_READ,
					FILE_SHARE_READ,NULL,
					OPEN_EXISTING,
					FILE_ATTRIBUTE_NORMAL,NULL);
	if(m_fd==INVALID_HANDLE_VALUE)
	{
		printf("OPen file %s failed error,error :%d !\n",name,GetLastError());
		return NULL;
	}
	return m_fd;
}


int ReadFile(HANDLE m_fd, size_t offset, size_t size, void *pBuff )
{
	int ret;
	ULONG rdsize;  //作为热ReadFile的参数之一;
	ret = SetFilePointer(m_fd, offset, NULL, FILE_BEGIN);  //从偏移量offset开始读文件;
	if(ret==INVALID_SET_FILE_POINTER)
	{
		ret=GetLastError();
		printf("Seek file failed error : %d !\n",ret);
		return ret;
	}

	if(!ReadFile(m_fd, pBuff, size, &rdsize, NULL))
	{
		ret = GetLastError();
		printf("Read file failed, error: %d\n", ret);
		return ret;
	}
	return 0;
}


void CloseFile(HANDLE m_fd)
{
	if(m_fd!=NULL)
		CloseHandle(m_fd);
}


PELoader.h :

#include <windows.h>
#include <Psapi.h>
#include <malloc.h>

typedef struct
{
	int sections;    //区块的个数;
	int imageBase;   //程序基址;
	int entryPoint;   //程序入口点;
	int imageSize ;  //程序加载到内存后的大小 ;

	int exportRva;
	int exportSize;
	int importRva;
	int importSize;
	int resourceRva;
	int resourceSize;
	int relocRva;
	int relocSize;
	
	int offsetSection;  //区块的偏移;
	int filetype;
} PeInfo;



PELoader.cpp :

#include "PELoader.h"
#include "ReadFile.h"

/*把该loader.exe的加载地址设置为0x0f400000,从而可以把0x00400000地址
*预留给将要被加载的程序,从而可以避免因地址重定位而带来的性能损耗;
 */

#pragma comment(linker, "/BASE:0x0f400000")

//////////////////////////////////////////////////////////////////////////
//判断文件是否为PE文件;
BOOL IsPeFile(HANDLE m_fd)
{
	IMAGE_DOS_HEADER dos_header;
	IMAGE_NT_HEADERS nt_header;

	ReadFile(m_fd,0,sizeof(IMAGE_DOS_HEADER),&dos_header);   //读dos首部结构到dos_header;
	if(dos_header.e_magic!=IMAGE_DOS_SIGNATURE)
		return FALSE;

	ReadFile(m_fd,dos_header.e_lfanew,sizeof(IMAGE_NT_HEADERS),&nt_header);
	if(nt_header.Signature!=IMAGE_NT_SIGNATURE)
		return FALSE;

	return TRUE;
}
//////////////////////////////////////////////////////////////////////////
//获取Pe文件的一些结构信息;
BOOL GetPeNtHeader(HANDLE m_fd, PeInfo &PeInformation)
{
	IMAGE_DOS_HEADER dos_header;
	IMAGE_NT_HEADERS nt_header;

	PIMAGE_FILE_HEADER file_header;    //指针的形式;
	PIMAGE_OPTIONAL_HEADER options_header;  //指针的形式;

	ReadFile(m_fd,0,sizeof(IMAGE_DOS_HEADER),&dos_header);
	ReadFile(m_fd, dos_header.e_lfanew, sizeof(IMAGE_NT_HEADERS),&nt_header);

	PeInformation.offsetSection=dos_header.e_lfanew+sizeof(IMAGE_NT_HEADERS);  //区块表的位置的偏移;

	file_header=&nt_header.FileHeader;
	options_header=&nt_header.OptionalHeader;

	assert(file_header->Machine==IMAGE_FILE_MACHINE_I386);   //判断运行平台;
	assert(file_header->SizeOfOptionalHeader==sizeof(IMAGE_OPTIONAL_HEADER32));

	//////////////////////////////////////////////////////////////////////////
	//对PeInfo结构赋值;
	PeInformation.sections=file_header->NumberOfSections;   //区块的数目;
	PeInformation.entryPoint=options_header->AddressOfEntryPoint;  //程序的入口点;
	PeInformation.imageBase=options_header->ImageBase;   //程序默认装入的基址;
	PeInformation.imageSize=options_header->SizeOfImage;  //映像装入内存后的总尺寸;
	//数据目录表相关信息;
	PeInformation.exportRva=options_header->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
	PeInformation.exportSize=options_header->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size;
	PeInformation.importRva=options_header->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
	PeInformation.importSize=options_header->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size;
	PeInformation.resourceRva=options_header->DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress;
	PeInformation.resourceSize=options_header->DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].Size;
	PeInformation.relocRva=options_header->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress;
	PeInformation.relocSize=options_header->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size;

	return TRUE;
}
//////////////////////////////////////////////////////////////////////////
//加载sections区块带内存函数;
BOOL LoadSections(HANDLE m_fd, PeInfo &PeInformation, MODULEINFO &PeExe)
{
	PIMAGE_SECTION_HEADER psec_header,psec_tmp;   //指向区块表结构的指针;
	char section_name[9]="\0"; 
	psec_header=(PIMAGE_SECTION_HEADER)_alloca(PeInformation.sections*sizeof(IMAGE_SECTION_HEADER));      //_alloca()  函数在堆栈空间申请内存;为区块表申请内存;
	
	if(psec_header==NULL)
	{
		printf("Memory not enough !!!\n");
		return FALSE;
	}

	ReadFile(m_fd,PeInformation.offsetSection,PeInformation.sections*sizeof(IMAGE_SECTION_HEADER),psec_header); //将区块表读到申请的内存空间中;
	psec_tmp=psec_header;
	//将区块内容读到申请的内存中;
	for(int i=0;i<PeInformation.sections;i++,psec_tmp++)
	{
		memcpy(section_name,psec_tmp->Name,8);
		if(psec_tmp->PointerToRawData!=NULL && psec_tmp->SizeOfRawData!=0)
		{
			ReadFile(m_fd,
				     psec_tmp->PointerToRawData,
				     psec_tmp->SizeOfRawData,
					 (void*)((DWORD)PeExe.lpBaseOfDll+psec_tmp->VirtualAddress));   //程序基址 +  在内存中的RVA;
		}
		printf("SectionName--->%s\n",section_name);
	}

	return TRUE;
}
//////////////////////////////////////////////////////////////////////////
//根据重定位表对,内存模块进行重定位;
BOOL RelocPemodule(PIMAGE_BASE_RELOCATION prelocBase, DWORD reImageBase ,int offsetReloc)
{
		DWORD reloca_addr,count, offset, type;
		WORD *item = NULL ;

		while(prelocBase->VirtualAddress!=NULL)
		{
			reloca_addr=prelocBase->VirtualAddress+reImageBase;  //要被重定位的数据的地址部分;

			count=(prelocBase->SizeOfBlock-sizeof(IMAGE_BASE_RELOCATION))>>1;      //数组每项大小两个字节,除以2,即为数组项目个数;
			item= (WORD *)((char*)prelocBase+sizeof(IMAGE_BASE_RELOCATION));     //数组项目的开始地址;

			for(int i=0; i<count;i++)  //每个重定位表中有N项;
			{
				offset = item[0] & 0x0fff ;   //低12位,重定位地址;
				type = item[0] >> 12 ;    //重定位类型;
				if(type==3)
				{
					*(DWORD*)(reloca_addr+offset)+=offsetReloc;   //重定位地址加上  便宜量;
				}
			}
			prelocBase=(PIMAGE_BASE_RELOCATION)(item+count*2); //指针指向下一个重定位结构;
		}
		return TRUE;
}
//////////////////////////////////////////////////////////////////////////
//导出表修正函数;
BOOL FixExport(PIMAGE_EXPORT_DIRECTORY pexport_addr,DWORD reImageBase)
{
	return TRUE;                                     //exe文件一般无导出表;
}
//////////////////////////////////////////////////////////////////////////
//导入表修正函数;
BOOL FixImport(PIMAGE_IMPORT_DESCRIPTOR pimport_addr,DWORD reImageBase)
{
	PIMAGE_THUNK_DATA pOrgThunk, pFirstThunk;
	PIMAGE_IMPORT_BY_NAME pImporBytName;

	printf("\n--------------------------import table info-------------------------------\n");
	while(pimport_addr->OriginalFirstThunk!=NULL)
	{
		pimport_addr->Name+=reImageBase;    //内存中导入表中动态链接库的名字的地址;
		printf("Dll:%s\n",pimport_addr->Name);   //打印动态链接库的名字;

		FARPROC fpFun;         //动态链接库中函数的地址;
		HINSTANCE hInstance = LoadLibraryA((LPCTSTR)pimport_addr->Name);  //加载动态链接库;
		if(hInstance==NULL) 
		{
			printf("Load Librabry %sa failed ,error :%d\n",pimport_addr->Name,GetLastError());
			return FALSE;
		}

		pOrgThunk = (PIMAGE_THUNK_DATA)(reImageBase+pimport_addr->OriginalFirstThunk);
		pFirstThunk = (PIMAGE_THUNK_DATA)(reImageBase+pimport_addr->FirstThunk);

		while(*(DWORD*)pOrgThunk!=NULL)
		{
			if(pOrgThunk->u1.Ordinal & IMAGE_ORDINAL_FLAG32)   //最高位为1,则函数以序号的方式输入;
				fpFun = GetProcAddress(hInstance,(LPCSTR)(pOrgThunk->u1.Ordinal & 0x0000ffff));
			else      //函数以名字输入的;
			{
				pImporBytName = (PIMAGE_IMPORT_BY_NAME)(reImageBase+pOrgThunk->u1.AddressOfData);
				fpFun = GetProcAddress(hInstance, (LPCSTR)(pImporBytName->Name));  //得到函数地址;
			}

			pFirstThunk->u1.Ordinal=(LONG)fpFun;    //将的到的函数地址填入到pfirstthunk中;
			pOrgThunk++;
			pFirstThunk++;
		}

	//	FreeLibrary(hInstance);
		pimport_addr++;
		
	}
	
	return TRUE;
}
//////////////////////////////////////////////////////////////////////////
//资源处理函数;
BOOL FixResource(PIMAGE_RESOURCE_DIRECTORY presource_addr,DWORD reImageBase)
{
	PIMAGE_RESOURCE_DIRECTORY_ENTRY presource_entry;
	DWORD nEntrys;    //每个目录结构的资源个数 ById + ByName;
	nEntrys = presource_addr->NumberOfIdEntries + presource_addr->NumberOfNamedEntries ;
	presource_entry = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)((DWORD)presource_addr + sizeof(IMAGE_RESOURCE_DIRECTORY));   //第一层目录入口;

	for(int i=0; i<nEntrys; i++, presource_entry++)
	{
		if(presource_entry->OffsetToData & IMAGE_RESOURCE_DATA_IS_DIRECTORY)
		{
			PIMAGE_RESOURCE_DIRECTORY presource_addr2;
			PIMAGE_RESOURCE_DIRECTORY_ENTRY presource_entry2;
			DWORD nEntrys2;

			presource_addr2 = (PIMAGE_RESOURCE_DIRECTORY)((DWORD)presource_addr + (~IMAGE_RESOURCE_DATA_IS_DIRECTORY & presource_entry->OffsetToData)); //取低位地址;
			presource_entry2 = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)((DWORD)presource_addr2 + sizeof(IMAGE_RESOURCE_DIRECTORY));
			nEntrys2 = presource_addr2->NumberOfIdEntries + presource_addr2->NumberOfNamedEntries;   //资源个数;

			for(int j=0;j<nEntrys2;j++, presource_entry2++)
			{
				if(presource_entry2->Name & IMAGE_RESOURCE_NAME_IS_STRING)  //判断资源名称是ID号还是字符串;
				{
					PIMAGE_RESOURCE_DIR_STRING_U pNameDirString;  //资源名称字符穿;
					pNameDirString =(PIMAGE_RESOURCE_DIR_STRING_U) ((DWORD)(presource_addr) + ~IMAGE_RESOURCE_NAME_IS_STRING & presource_entry2->Name);
				}
				if(presource_entry2->OffsetToData & IMAGE_RESOURCE_DATA_IS_DIRECTORY)
				{
					PIMAGE_RESOURCE_DIRECTORY presource_addr3; //第三层目录地址;
					PIMAGE_RESOURCE_DIRECTORY_ENTRY presource_entry3;
					DWORD nEntrys3;         //第三层资源个数;

					presource_addr3 = (PIMAGE_RESOURCE_DIRECTORY)((DWORD)(presource_addr) + (~IMAGE_RESOURCE_DATA_IS_DIRECTORY & presource_entry2->OffsetToData));
					presource_entry3 = (PIMAGE_RESOURCE_DIRECTORY_ENTRY) ((DWORD)presource_addr3 + sizeof(IMAGE_RESOURCE_DIRECTORY)); //第三层目录入口地址;
					nEntrys3 = presource_addr3->NumberOfIdEntries + presource_addr3->NumberOfNamedEntries;   

						for(int k=0 ;k<nEntrys3;k++)
						{
							PIMAGE_RESOURCE_DATA_ENTRY pDataEntry;  //最终资源的入口地址结构;
							assert(~IMAGE_RESOURCE_DATA_IS_DIRECTORY & presource_entry3->OffsetToData);  //资源结构地址为空,则异常;

							pDataEntry=(PIMAGE_RESOURCE_DATA_ENTRY)((DWORD)presource_addr + (~IMAGE_RESOURCE_DATA_IS_DIRECTORY & presource_entry3->OffsetToData));
							//pDataEntry->OffsetToData += (DWORD)reImageBase;
						}
					}
				}

			}

	}
	return TRUE;
}
//////////////////////////////////////////////////////////////////////////
int LoadPeModule(char *name)
{
	HANDLE m_fd;
	PeInfo PeInformation;   //pe信息结构;
	LPVOID addr;           //为程序申请的地址空间的开始地址;

	MODULEINFO  PeExe;     //镜像的模块信息,加载模块结构;

	m_fd=OpenFile(name);      //打开文件;
	//////////////////////////////////////////////////////////////////////////
	//判断是否为合法的PE文件;
	assert(IsPeFile(m_fd));
	//获取Pe文件的相关信息;
	GetPeNtHeader(m_fd, PeInformation);
	/*为image按照imageBase优先原则分配空间地址。;
	 *如该空间地址已被reserved或commit,则重新分配一个可用的空间地址,;
	 *但此种情况下需要做基址重定位处理。*/;

	addr=VirtualAlloc((LPVOID)(PeInformation.imageBase),
						   	    PeInformation.imageSize,
								MEM_RESERVE | MEM_COMMIT,
								PAGE_EXECUTE_READWRITE);     //申请可读可写可执行 内存区域;
	if(addr==NULL)
	{
		printf("VirtualAlloc failed , error :%d \n",GetLastError());
		 //申请的起始地址被占用,系统随机分配起始地址;
		addr=VirtualAlloc(NULL,
						  PeInformation.imageSize,
						  MEM_RESERVE | MEM_COMMIT, 
						  PAGE_EXECUTE_READWRITE);  
		if(addr==NULL)
		{
			printf("VirtualAlloc failed , error :%d \n",GetLastError());
			return -1;
		}
	}
//////////////////////////////////////////////////////////////////////////
	memset((void*)addr,0,PeInformation.imageSize);  //将申请的内存区域填充0;

	PeExe.lpBaseOfDll=(LPVOID)addr;    //模块在内存中基址;
	PeExe.EntryPoint=(LPVOID)PeInformation.entryPoint;   //入口地址;
	PeExe.SizeOfImage=PeInformation.imageSize;  //镜像在内存中的大小;

	//获取Pe文件的一些基本信息后,将sections区块加载到内存;
	LoadSections(m_fd,PeInformation,PeExe);

	//////////////////////////////////////////////////////////////////////////
	//进行后期处理,重定位,IAT地址表填写;
	//如果实际分配的空间地址和pe文件的基址imagebase不一样,则需要做基址重定位处理 ;
	if((int)PeExe.lpBaseOfDll!=PeInformation.imageBase)
	{
		if(PeInformation.relocSize==0)
		{
			printf("cannot reloc address!!!\n");
			return -1;
		}
		PIMAGE_BASE_RELOCATION prelocBase=(PIMAGE_BASE_RELOCATION)((DWORD)PeExe.lpBaseOfDll+PeInformation.relocRva);
		RelocPemodule(prelocBase,(DWORD)PeExe.lpBaseOfDll,(DWORD)PeExe.lpBaseOfDll-PeInformation.imageBase);
	}
	//////////////////////////////////////////////////////////////////////////
	//如果文件有导出表,则进行处理导出表段;
	if(PeInformation.exportSize!=0)
	{
		PIMAGE_EXPORT_DIRECTORY pexport_addr=(PIMAGE_EXPORT_DIRECTORY)((DWORD)PeExe.lpBaseOfDll+PeInformation.exportRva);  //导出表的起始位置;
		FixExport(pexport_addr,(DWORD)PeExe.lpBaseOfDll);     //修正导出表;
	}

	//如果文件有导入表,则处理导入表;
	if(PeInformation.importSize!=0)
	{
		PIMAGE_IMPORT_DESCRIPTOR pimport_addr=(PIMAGE_IMPORT_DESCRIPTOR)((DWORD)PeExe.lpBaseOfDll+PeInformation.importRva);  //导入表的起始位置;
		FixImport(pimport_addr,(DWORD)PeExe.lpBaseOfDll);  //修正导入表;
	}
	//如果文件有资源表,则处理资源表;
	if(PeInformation.resourceSize!=0)
	{
		PIMAGE_RESOURCE_DIRECTORY presource_addr=(PIMAGE_RESOURCE_DIRECTORY)((DWORD)PeExe.lpBaseOfDll+PeInformation.resourceRva); //资源表的起始位置;
	//	FixResource(presource_addr,(DWORD)PeExe.lpBaseOfDll);
	}

	int entrypoint = (int)PeExe.lpBaseOfDll+PeInformation.entryPoint;
	printf("程序在内存的中起始地址:0x%x\n",(int)PeExe.lpBaseOfDll);
	printf("程序的入口点:0x%x\n\n",entrypoint);
	//程序跳到入口点开始执行程序;
	__asm
	{
		mov eax,entrypoint;
		call eax ;
	}
	return 1;
}


int main()
{
	LoadPeModule("cmd.exe");  //加载cmd程序;
	return 1;
}


PS:  努力学习,争取早日成为看雪正式会员

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

收藏
免费 0
支持
分享
最新回复 (1)
雪    币: 77
活跃值: (48)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
mark
2014-1-18 12:28
0
游客
登录 | 注册 方可回帖
返回
//