|
马上注册,结交更多好友,享用更多功能^_^
您需要 登录 才可以下载或查看,没有账号?立即注册
x
急急急,编写PELoader时候,用VC6编译时候没有问题,但是运行程序时候出现问题,具体情况请大家自行编译一下,帮忙看看问题出现在哪里,谢谢大牛们了!详情请见下方代码:
为了加深对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;
}
|
|