-
-
[旧帖] [原创]应用层模拟加载PE可执行文件的简单程序 0.00雪花
-
发表于: 2012-11-30 17:44 1886
-
为了加深对PE文件结构的理解,参考了些资料,写了个简单的模拟PE文件加载过程的程序,不过程序有些地方没处理好(资源节处),也存在这些小bug,仅作交流用…………
写的分四个文件写的,如下:
ReadFile.h :
ReadFile.cpp :
PELoader.h :
PELoader.cpp :
PS: 努力学习,争取早日成为看雪正式会员
写的分四个文件写的,如下:
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: 努力学习,争取早日成为看雪正式会员
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)
赞赏
他的文章
- [求助]Win7 CreateRemoteThread 的问题 1195
- 破解版迅雷,无限高速通道 2336
- [原创]应用层模拟加载PE可执行文件的简单程序 1887
- [求助]分析一小程序,找到中的加密算法,还原密文 1605
看原图
赞赏
雪币:
留言: