内存加载Exe原理,PELoder
一丶原理
原理是模拟Window 双击 Exe进行操作.
而对于我们来说.其实就是 针对PE文件的 NT头 节表数据 重定位表 导入表 等进行操作.
步骤如下:
总共分为几大步骤
- 文件数据转内存数据 1.拷贝文件中NT头数据大小. 按照SizeofHeder大小拷贝 2.申请SizeofImage大小内存. 用于存储文件中的PE数据
- 数据操作阶段 1.拷贝节表数据到内存中.按照内存对齐. 其实就是拷贝节表中记录数据大小 到内存起始位置 2.修复重定位表,有的情况下. 根据重定位表的结构. 按照每一页进行重定位表的遍历. 来进行修复即可. 3.修复导入表. 根据导入表的双桥结构. INT IAT Name 根据name加载PE所需要的DLL 根据INT 判断是序号导入还是名字导入并且使用 GetProcAddress 加载对应的函数. 加载的函数填写到IAT表中
- 调用阶段 调用阶段就是获取PE的OEP入口点的RVA 然后修改内存中的ImageBase 入口点RVA与内存的起始点相加得到OEP的VA. 然后内联汇编进行调用即可.
二丶代码
1.代码分布讲解
代码很简单.如下:
第一步.读取PE数据.用于后续操作
代码语言:javascript复制//第一步 文件操作.获取PE中文件的数据,返回PE读取的PE数据
char* SepOne_RetPeFileData()
{
/*
第一步 文件以及内存操作
1.只读打开文件
2.获取文件大小
3.返回文件数据
*/
HANDLE hFile = INVALID_HANDLE_VALUE;
LARGE_INTEGER laFileSize = { 0 };
hFile =
CreateFile(
FILE_NAME,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (NULL == INVALID_HANDLE_VALUE)
{
if (hFile != INVALID_HANDLE_VALUE)
{
CloseHandle(hFile);
return FALSE;
}
return FALSE;
}
if (0 == GetFileSizeEx(hFile, &laFileSize))
{
if (hFile != INVALID_HANDLE_VALUE)
{
CloseHandle(hFile);
return FALSE;
}
}
//获取文件大小
char* pszFileData = NULL;
pszFileData = new char[laFileSize.QuadPart]();
if (pszFileData == NULL)
{
if (hFile != INVALID_HANDLE_VALUE)
{
CloseHandle(hFile);
return FALSE;
}
return FALSE;
}
DWORD dwReadOrWriteByes = 0;
if (0 == ReadFile(
hFile,
pszFileData,
laFileSize.QuadPart,
&dwReadOrWriteByes,
NULL))
{
if (pszFileData != NULL)
{
delete[] pszFileData;
pszFileData = NULL;
}
if (hFile != INVALID_HANDLE_VALUE)
{
CloseHandle(hFile);
return FALSE;
}
return FALSE;
}
CloseHandle(hFile);
hFile = INVALID_HANDLE_VALUE;
if (pszFileData != NULL)
{
return pszFileData;
}
return NULL;
}
第二步,PE文件数据映射,并且进行修复
代码语言:javascript复制//传入对齐值,以及大小进行对其拷贝
DWORD GetAgenSizeOfRawData(DWORD Agine,DWORD Value)
{
while ((Value % Agine) != 0)
{
Value ;
}
return Value;
}
BOOL CopyPeFileSectionDataToPeMmSectionData(
LPVOID PeFileStatusAddress, //文件的内存地址保存了文件中PE的数据
LPVOID PEMmAddress)
{
PIMAGE_DOS_HEADER pDosHeder = NULL;
PIMAGE_NT_HEADERS pNtHeder = NULL;
PIMAGE_FILE_HEADER pFileHeder = NULL;
PIMAGE_OPTIONAL_HEADER pOptHeder = NULL;
PIMAGE_SECTION_HEADER pSecHeder = NULL;
DWORD SectionCount = GetSectionNumber(PeFileStatusAddress);
if (SectionCount == 0)
{
return FALSE;
}
//赋值头
pDosHeder = (PIMAGE_DOS_HEADER)PeFileStatusAddress;
pNtHeder = (PIMAGE_NT_HEADERS)((char*)PeFileStatusAddress pDosHeder->e_lfanew);
pFileHeder = (PIMAGE_FILE_HEADER)&pNtHeder->FileHeader;
pOptHeder = (PIMAGE_OPTIONAL_HEADER)&pNtHeder->OptionalHeader;
pSecHeder = (PIMAGE_SECTION_HEADER)&pNtHeder[1];
for (DWORD index = 0; index < SectionCount; index )
{
if (pSecHeder == NULL)
{
return FALSE;
}
//开始遍历文件中节表中的数据. 然后拷贝到内存中的数据
char* pSrcData = (char*)PeFileStatusAddress pSecHeder[index].PointerToRawData; //文件中节表开始的位置
//这里需要内存对齐,而且 VirtualAddress还可能为0 不过一般不会为0 除非畸形PE
char* pDestDataAddress = (char*)PEMmAddress pSecHeder[index].VirtualAddress;
//DWORD dwSizeOfRawData = GetAgenSizeOfRawData(pOptHeder->SectionAlignment,pSecHeder[index].SizeOfRawData); //获取文件大小.按照内存对齐来对其来进行拷贝
//DWORD TempValue = (DWORD)((char*)pDestDataAddress dwSizeOfRawData);
RtlCopyMemory(pDestDataAddress, pSrcData, pSecHeder[index].SizeOfRawData); //拷贝文件中记录的节数据的大小
//pSecHeder ;
}
return TRUE;
}
//重定位数组表记录的所有偏移
typedef struct _RELOC_ARRAY
{
WORD ArrayOffset : 12;
WORD mark : 3;
}RELOC_ARRAY, * PRELOC_ARRAY;
//修复重定位表
BOOL RepairReloc(LPVOID PeFileStatusAddress, LPVOID PEMmAddress)
{
/*
文件以及内存中都已经有了数据了.所以这里直接以内存为起始点 来获取重定位表.并且修复自身内存的重定位表
当然也可以使用文件来便利文件中的重定位表来修复内存中的重定位表
*/
PIMAGE_DOS_HEADER pDosHeder = NULL;
PIMAGE_NT_HEADERS pNtHeder = NULL;
PIMAGE_FILE_HEADER pFileHeder = NULL;
PIMAGE_OPTIONAL_HEADER pOptHeder = NULL;
PIMAGE_SECTION_HEADER pSec = NULL;
PIMAGE_BASE_RELOCATION pRelocTable = NULL;
//赋值各个相关字段
pDosHeder = (PIMAGE_DOS_HEADER)PEMmAddress;
pNtHeder = (PIMAGE_NT_HEADERS)((char*)PEMmAddress pDosHeder->e_lfanew);
pFileHeder = (PIMAGE_FILE_HEADER)&pNtHeder->FileHeader;
pOptHeder = (PIMAGE_OPTIONAL_HEADER)&pNtHeder->OptionalHeader;
pSec = (PIMAGE_SECTION_HEADER)&pNtHeder[1];
//获取RVA以及内存中真正重定位表的位置
DWORD dwRelocTalbeRva = pOptHeder->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress;
pRelocTable = (PIMAGE_BASE_RELOCATION)((char*)PEMmAddress dwRelocTalbeRva);
if (pRelocTable == PEMmAddress) //代表没有重定位表
return FALSE;
//遍历重定位表进行内存中重定位表的修复
while (true)
{
if (pRelocTable->SizeOfBlock == 0)
{
break;
}
//获取每一个重定位表中重定位表数组. 继续遍历这个数组 来获取offset 来获取那个地方需要重定位. 然后再把里面的内容修改掉
//获取地n项 重定位数组表 以及计算出它的大小
PRELOC_ARRAY pRelocArray = (PRELOC_ARRAY)((PBYTE)(pRelocTable) 8);
int nRelocArrayCount = (pRelocTable->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(RELOC_ARRAY);
for (int index = 0; index < nRelocArrayCount; index )
{
//是需要进行重定位的一项
if (pRelocArray[index].mark == 3)
{
//计算出需要重定位的位置
PVOID pRelocaLocation = (char*)PEMmAddress pRelocTable->VirtualAddress pRelocArray[index].ArrayOffset;
//修改里面重定位的数值为 新的Imagebase 偏移 偏移计算方式 偏移 = 当前地址 - 原ImageBase = 偏移
DWORD RVA = *(DWORD*)pRelocaLocation - pOptHeder->ImageBase;
DWORD Offset = (DWORD)(char*)PEMmAddress RVA;
*(DWORD*)pRelocaLocation = Offset;
continue;
}
//否则重定位表移动到下一页进行操作
}
pRelocTable = (PIMAGE_BASE_RELOCATION)((char*)pRelocTable pRelocTable->SizeOfBlock);
}
return TRUE;
// PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)PEMmAddress;
// PIMAGE_NT_HEADERS pNtHeaders = (PIMAGE_NT_HEADERS)((ULONG32)pDosHeader pDosHeader->e_lfanew);
// PIMAGE_BASE_RELOCATION pLoc = (PIMAGE_BASE_RELOCATION)((unsigned long)pDosHeader pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress);
//
// // 判断是否有 重定位表
// if ((PVOID)pLoc == (PVOID)pDosHeader)
// {
// // 重定位表 为空
// return TRUE;
// }
//
// while ((pLoc->VirtualAddress pLoc->SizeOfBlock) != 0) //开始扫描重定位表
// {
// WORD* pLocData = (WORD*)((PBYTE)pLoc sizeof(IMAGE_BASE_RELOCATION));
// //计算本节需要修正的重定位项(地址)的数目
// int nNumberOfReloc = (pLoc->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(WORD);
//
// for (int i = 0; i < nNumberOfReloc; i )
// {
// // 每个WORD由两部分组成。高4位指出了重定位的类型,WINNT.H中的一系列IMAGE_REL_BASED_xxx定义了重定位类型的取值。
// // 低12位是相对于VirtualAddress域的偏移,指出了必须进行重定位的位置。
///*
// #ifdef _WIN64
// if ((DWORD)(pLocData[i] & 0x0000F000) == 0x0000A000)
// {
// // 64位dll重定位,IMAGE_REL_BASED_DIR64
// // 对于IA-64的可执行文件,重定位似乎总是IMAGE_REL_BASED_DIR64类型的。
//
// ULONGLONG* pAddress = (ULONGLONG *)((PBYTE)pNewBase pLoc->VirtualAddress (pLocData[i] & 0x0FFF));
// ULONGLONG ullDelta = (ULONGLONG)pNewBase - m_pNTHeader->OptionalHeader.ImageBase;
// *pAddress = ullDelta;
//
// }
// #endif
//*/
// if ((DWORD)(pLocData[i] & 0x0000F000) == 0x00003000) //这是一个需要修正的地址
// {
// // 32位dll重定位,IMAGE_REL_BASED_HIGHLOW
// // 对于x86的可执行文件,所有的基址重定位都是IMAGE_REL_BASED_HIGHLOW类型的。
// DWORD* pAddress = (DWORD*)((PBYTE)pDosHeader pLoc->VirtualAddress (pLocData[i] & 0x0FFF));
// DWORD dwDelta = (DWORD)pDosHeader - pNtHeaders->OptionalHeader.ImageBase;
// *pAddress = dwDelta;
//
// }
// }
//
// //转移到下一个节进行处理
// pLoc = (PIMAGE_BASE_RELOCATION)((PBYTE)pLoc pLoc->SizeOfBlock);
// }
return TRUE;
}
//将wstring转换成string
string wstring2string(wstring wstr)
{
string result;
//获取缓冲区大小,并申请空间,缓冲区大小事按字节计算的
int len = WideCharToMultiByte(CP_ACP, 0, wstr.c_str(), wstr.size(), NULL, 0, NULL, NULL);
char* buffer = new char[len 1];
//宽字节编码转换成多字节编码
WideCharToMultiByte(CP_ACP, 0, wstr.c_str(), wstr.size(), buffer, len, NULL, NULL);
buffer[len] = '