|
模拟PE加载器加载PE文件,对导入表以及重定位表的操作过程。
PE加载器的步骤:
1.将PE文件用ReadFile读取数据
char szFileName[] = "1.exe";//打开文件,设置属性可读可写HANDLE hFile = CreateFileA(szFileName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_ARCHIVE, NULL);if (INVALID_HANDLE_VALUE == hFile){ printf("文件打开失败\n"); return 1;}//获取文件大小DWORD dwFileSize = GetFileSize(hFile, NULL);//申请空间将exe读取到内存中char *pData = new char[dwFileSize];if (NULL == pData){ printf("空间申请失败\n"); return 2;}DWORD dwRet = 0;ReadFile(hFile, pData, dwFileSize, &dwRet, NULL);CloseHandle(hFile);2.根据PE结构获取镜像大小,在自己的程序中申请可读可写可执行的内存。
char* chBaseAddress = NULL;//获取镜像大小DWORD dwSizeOfImage = GetSizeOfImage(pFileBuff);//在进程中开辟一块内存空间chBaseAddress = (char*)VirtualAlloc(NULL, dwSizeOfImage, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);if (NULL == chBaseAddress){ printf("申请进程空间失败\n"); return NULL;}3.将申请的空间全部填为0
RtlZeroMemory(chBaseAddress, dwSizeOfImage);4.将用ReadFile读取的数据映射到内存中
由上图可知,PE文件在加载到内存中对齐粒度不同,因此在写代码时要注意这个问题
PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)pFileBuff;PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(pFileBuff + pDos->e_lfanew);PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION(pNt);//所有头 + 结表头的大小DWORD dwSizeOfHeaders = pNt->OptionalHeader.SizeOfHeaders;//获取区段数量int nNumerOfSections = pNt->FileHeader.NumberOfSections;// 将前一部分都拷贝过去RtlCopyMemory(chBaseAddress, pFileBuff, dwSizeOfHeaders);char* chSrcMem = NULL;char* chDestMem = NULL;DWORD dwSizeOfRawData = 0;for (int i = 0; i < nNumerOfSections; i++){ if ((0 == pSection->VirtualAddress) || (0 == pSection->SizeOfRawData)) { pSection++; continue; } chSrcMem = (char*)((DWORD)pFileBuff + pSection->PointerToRawData); chDestMem = (char*)((DWORD)chBaseAddress + pSection->VirtualAddress); dwSizeOfRawData = pSection->SizeOfRawData; RtlCopyMemory(chDestMem, chSrcMem, dwSizeOfRawData); pSection++;}return TRUE;5.修复重定位
重定位后的地址 = 需要重定位的地址 - 默认加载基址 + 当前加载基址
PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)chBaseAddress;PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(chBaseAddress + pDos->e_lfanew);PIMAGE_BASE_RELOCATION pLoc = (PIMAGE_BASE_RELOCATION)(chBaseAddress + pNt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress);//判断是否有重定位表if ((char*)pLoc == (char*)pDos){ 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域的偏移,指出了必须进行重定位的位置。 if ((DWORD)(pLocData & 0x0000F000) == 0x00003000) //这是一个需要修正的地址 { DWORD* pAddress = (DWORD *)((PBYTE)pDos + pLoc->VirtualAddress + (pLocData & 0x0FFF)); DWORD dwDelta = (DWORD)pDos - pNt->OptionalHeader.ImageBase; *pAddress += dwDelta; } } //转移到下一个节进行处理 pLoc = (PIMAGE_BASE_RELOCATION)((PBYTE)pLoc + pLoc->SizeOfBlock);}return TRUE;6.根据PE结构的导入表,加载所需的dll,并获取导入函数的地址并写入导入表中
PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)chBaseAddress;PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(chBaseAddress + pDos->e_lfanew);PIMAGE_IMPORT_DESCRIPTOR pImportTable = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)pDos + pNt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);// 循环遍历DLL导入表中的DLL及获取导入表中的函数地址char *lpDllName = NULL;HMODULE hDll = NULL;PIMAGE_THUNK_DATA lpImportNameArray = NULL;PIMAGE_IMPORT_BY_NAME lpImportByName = NULL;PIMAGE_THUNK_DATA lpImportFuncAddrArray = NULL;FARPROC lpFuncAddress = NULL;DWORD i = 0;while (TRUE){ if (0 == pImportTable->OriginalFirstThunk) { break; } // 获取导入表中DLL的名称并加载DLL lpDllName = (char *)((DWORD)pDos + pImportTable->Name); hDll = GetModuleHandleA(lpDllName); if (NULL == hDll) { hDll = LoadLibraryA(lpDllName); if (NULL == hDll) { pImportTable++; continue; } } i = 0; // 获取OriginalFirstThunk以及对应的导入函数名称表首地址 lpImportNameArray = (PIMAGE_THUNK_DATA)((DWORD)pDos + pImportTable->OriginalFirstThunk); // 获取FirstThunk以及对应的导入函数地址表首地址 lpImportFuncAddrArray = (PIMAGE_THUNK_DATA)((DWORD)pDos + pImportTable->FirstThunk); while (TRUE) { if (0 == lpImportNameArray.u1.AddressOfData) { break; } // 获取IMAGE_IMPORT_BY_NAME结构 lpImportByName = (PIMAGE_IMPORT_BY_NAME)((DWORD)pDos + lpImportNameArray.u1.AddressOfData); // 判断导出函数是序号导出还是函数名称导出 if (0x80000000 & lpImportNameArray.u1.Ordinal) { // 序号导出 lpFuncAddress = GetProcAddress(hDll, (LPCSTR) (lpImportNameArray.u1.Ordinal & 0x0000FFFF)); } else { // 名称导出 lpFuncAddress = GetProcAddress(hDll, (LPCSTR)lpImportByName->Name); } lpImportFuncAddrArray.u1.Function = (DWORD)lpFuncAddress; i++; } pImportTable++;}7.修改PE文件的加载基址
PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)chBaseAddress;PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(chBaseAddress + pDos->e_lfanew);pNt->OptionalHeader.ImageBase = (ULONG32)chBaseAddress;8.跳转到PE的入口点处执行
PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)chBaseAddress;PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(chBaseAddress + pDos->e_lfanew);char* ExeEntry = (char*)(chBaseAddress + pNt->OptionalHeader.AddressOfEntryPoint);// 跳转到入口点处执行__asm{ mov eax, ExeEntry jmp eax}通过上述代码,成功加载并运行程序:
来源:http://www.12558.net
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|