PE-自实现拉伸运行dll

admin 2023年10月13日14:11:29评论9 views字数 8920阅读29分44秒阅读模式

实现功能:

  • 不借助Windows

  • 将dll注入到本进程

  • 将dll进行拉伸

  • 并执行dll导出函数


MemLoadDll.cpp文件:

#include "stdafx.h"#include "MemLoadDll.h"
VOID ShowError(const char* pszErrorMsg){ char szError[MAX_PATH] = { 0 }; wsprintf(szError, "%s ERROR [%d]", pszErrorMsg); MessageBox(0, szError, "ERROR", NULL);}
//将DLL读取到运行程序,模拟PE加载LPVOID MemLoadDll(LPVOID lpData, DWORD dwFileSize){ /* 步骤是: 1.先得到SizeOfImage 2.申请得到的SizeOfImage 3.复制PE头和节标信息 4.修正重定位表 5.填写PE文件导入表函数表地址信息 6.修改内存属性 7.修改加载基址 8.调用dllmain的入口函数 */
//1.先得到SizeOfImage DWORD dwSizeOfImage = GetSizeOfImage(lpData);
//2.申请得到的SizeOfImage LPVOID lpBaseAddress = NULL; lpBaseAddress = VirtualAlloc(NULL, dwSizeOfImage, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); if (lpBaseAddress == NULL) { ShowError("VirtualAlloc"); return lpBaseAddress; }
//3.复制PE头和节标信息 MemCopy(lpBaseAddress,lpData);
//4.修正PE重定位表 DoRelocationTable(lpBaseAddress);
//5.填写导入表Function表的地址 DoImportTable(lpBaseAddress);
//6.修改页面保护属性 DWORD dwVirtualProtect = 0; if (VirtualProtect(lpBaseAddress, dwSizeOfImage, PAGE_EXECUTE_READWRITE, &dwVirtualProtect) == FALSE) { ShowError("VirtualProtect"); return lpBaseAddress; }
//7.修改加载基址 SetImageBase(lpBaseAddress);
//8.调用dllmain的入口函数 if (CallDllMain(lpBaseAddress) == FALSE) { ShowError("CallDllMain"); return lpBaseAddress; } return lpBaseAddress;
}
//得到PE头文件大小DWORD GetSizeOfImage(LPVOID lpData){ DWORD dwSizeOfImage = 0; PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)lpData; PIMAGE_NT_HEADERS pNtHeaders = (PIMAGE_NT_HEADERS)((DWORD_PTR)pDosHeader + pDosHeader->e_lfanew); dwSizeOfImage = pNtHeaders->OptionalHeader.SizeOfImage; return dwSizeOfImage;}
//将文件复制到程序内存中//也就是FileBufferToImageBufferBOOL MemCopy(LPVOID lpBaseAddress,LPVOID lpData){ PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)lpData; PIMAGE_NT_HEADERS pNtHeaders = (PIMAGE_NT_HEADERS)((DWORD_PTR)pDosHeader + pDosHeader->e_lfanew); PIMAGE_SECTION_HEADER pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD_PTR)pNtHeaders + sizeof(IMAGE_NT_HEADERS)); DWORD dwNumberOfSections = pNtHeaders->FileHeader.NumberOfSections; DWORD dwSizeOfHeaders = pNtHeaders->OptionalHeader.SizeOfHeaders;
//现复制头部 RtlCopyMemory(lpBaseAddress, lpData, dwSizeOfHeaders); //复制节表信息 for (int i = 0; i < dwNumberOfSections; i++) { DWORD dwCopySize = pSectionHeader->SizeOfRawData; DWORD pSrc = ((DWORD_PTR)lpData + pSectionHeader->PointerToRawData); DWORD pDest = ((DWORD_PTR)lpBaseAddress + pSectionHeader->VirtualAddress); RtlCopyMemory((LPVOID)pDest, (LPVOID)pSrc, dwCopySize); pSectionHeader++; } return TRUE;}

BOOL DoRelocationTable(LPVOID lpBaseAddress){ //修正PE重定位表 /* PE重定位表前置知识 1.重定位表的结构: 4字节的VirtualAddress 4字节的SizeOfBlock (SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(WORD) 个需要修改的数据(2字节) 2.如何判断重定位表结束? 最后一个VirtualAddress和SizeOfBlock都为0 3.如何判断数据需要重定位? 2字节的数据中,高4位的值为3 */ PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)lpBaseAddress; PIMAGE_NT_HEADERS pNtHeaders = (PIMAGE_NT_HEADERS)((DWORD_PTR)pDosHeader + pDosHeader->e_lfanew); PIMAGE_BASE_RELOCATION pReloc = (PIMAGE_BASE_RELOCATION)((DWORD_PTR)pDosHeader + pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress);
//现判断是否有重定位表 if ((PWORD)pReloc == (PWORD)pDosHeader) { return TRUE; }
while ((pReloc->SizeOfBlock + pReloc->VirtualAddress) != 0) { //拿到需要修正的数据 PWORD pwRelocData = (PWORD)((DWORD_PTR)pReloc + sizeof(IMAGE_BASE_RELOCATION)); //计算需要修正的数据 DWORD dwNumberOfRelocData = (pReloc->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(WORD);
//开始修正 for (size_t i = 0; i < dwNumberOfRelocData; i++) { if ((DWORD)(pwRelocData[i] & 0x0000F000) == 0x00003000) { //拿到需要修改的低12位 DWORD* pdwAddress = (DWORD*)((DWORD_PTR)pDosHeader + pReloc->VirtualAddress + (pwRelocData[i] & 0xFFF)); //与之前ImageOfBase的偏移 DWORD pdwRepAddress = (DWORD)((DWORD_PTR)pDosHeader - pNtHeaders->OptionalHeader.ImageBase); *pdwAddress += pdwRepAddress; } } pReloc = (PIMAGE_BASE_RELOCATION)((DWORD_PTR)pReloc + pReloc->SizeOfBlock); } return TRUE;
}
//填写导入表函数地址BOOL DoImportTable(LPVOID lpBaseAddress){ PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)lpBaseAddress; PIMAGE_NT_HEADERS pNtHeaders = (PIMAGE_NT_HEADERS)((DWORD_PTR)pDosHeader + pDosHeader->e_lfanew); PIMAGE_IMPORT_DESCRIPTOR pImport = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD_PTR)pDosHeader + pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
PIMAGE_THUNK_DATA pImportNameArray = NULL; PIMAGE_THUNK_DATA pImportFuncArray = NULL; PIMAGE_IMPORT_BY_NAME pImportByName = NULL;
FARPROC lpFuncAddress = NULL;
char* dllName = NULL; HMODULE hDll = NULL; int i = 0; while (TRUE) { if (pImport->OriginalFirstThunk == 0) { break; }
dllName = (char*)((DWORD_PTR)pDosHeader + pImport->Name); hDll = GetModuleHandle(dllName); if (hDll == NULL) { hDll = LoadLibrary(dllName); if (hDll == NULL) { pImport++; continue; } }
i = 0; //获取函数名的地址 pImportNameArray = (PIMAGE_THUNK_DATA)((DWORD_PTR)pDosHeader + pImport->OriginalFirstThunk); pImportFuncArray = (PIMAGE_THUNK_DATA)((DWORD_PTR)pDosHeader + pImport->FirstThunk); while (true) { if (pImportNameArray[i].u1.AddressOfData == 0) { break; } //在判断是函数名还是函数序号 //最高位为1,则为函数序号 pImportByName = (PIMAGE_IMPORT_BY_NAME)((DWORD_PTR)pDosHeader + pImportNameArray[i].u1.AddressOfData); if (0x80000000 & pImportNameArray[i].u1.Ordinal) { // 序号导出 // 当IMAGE_THUNK_DATA值的最高位为1时,表示函数以序号方式输入,这时,低位被看做是一个函数序号 lpFuncAddress = GetProcAddress(hDll, (LPCSTR)(pImportNameArray[i].u1.Ordinal & 0x0000FFFF)); } else { // 名称导出 lpFuncAddress = GetProcAddress(hDll, (LPCSTR)pImportByName->Name); } pImportFuncArray[i].u1.Function = (DWORD)lpFuncAddress; i++; } pImport++; } return TRUE;}

BOOL SetImageBase(LPVOID lpBaseAddress){ PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)lpBaseAddress; PIMAGE_NT_HEADERS pNtHeaders = (PIMAGE_NT_HEADERS)((DWORD_PTR)pDosHeader + pDosHeader->e_lfanew); pNtHeaders->OptionalHeader.ImageBase = (DWORD_PTR)lpBaseAddress;
return TRUE;}

BOOL CallDllMain(LPVOID lpBaseAddress){ PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)lpBaseAddress; PIMAGE_NT_HEADERS pNtHeaders = (PIMAGE_NT_HEADERS)((DWORD_PTR)pDosHeader + pDosHeader->e_lfanew); typedef_CallDllMain dllMain = NULL; dllMain = (typedef_CallDllMain)((DWORD_PTR)pDosHeader + pNtHeaders->OptionalHeader.AddressOfEntryPoint); BOOL bRet = dllMain((HINSTANCE)lpBaseAddress, DLL_PROCESS_ATTACH, NULL);
if (bRet == FALSE) { ShowError("dllMain"); } return bRet;}

//模拟GetProcAddress函数,通过dll地址和函数名,来得到所在函数的地址LPVOID MemGetProcAddress(LPVOID lpBaseAddress, const char* funcName){ PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)lpBaseAddress; PIMAGE_NT_HEADERS pNtHeaders = (PIMAGE_NT_HEADERS)((DWORD_PTR)pDosHeader + pDosHeader->e_lfanew); PIMAGE_EXPORT_DIRECTORY pExportTable = (PIMAGE_EXPORT_DIRECTORY)((DWORD_PTR)pDosHeader + pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
LPVOID lpFunc = NULL; PDWORD lpAddressOfNamesArray = (PDWORD)((DWORD)pDosHeader + pExportTable->AddressOfNames); PCHAR lpFuncName = NULL; PWORD lpAddressOfNameOrdinalsArray = (PWORD)((DWORD)pDosHeader + pExportTable->AddressOfNameOrdinals); WORD wHint = 0; PDWORD lpAddressOfFunctionsArray = (PDWORD)((DWORD)pDosHeader + pExportTable->AddressOfFunctions);
DWORD dwNumberOfNames = pExportTable->NumberOfNames; DWORD i = 0; // 遍历导出表的导出函数的名称, 并进行匹配 for (i = 0; i < dwNumberOfNames; i++) { lpFuncName = (PCHAR)((DWORD)pDosHeader + lpAddressOfNamesArray[i]); if (0 == ::lstrcmpi(lpFuncName, funcName)) { // 获取导出函数地址 wHint = lpAddressOfNameOrdinalsArray[i]; lpFunc = (LPVOID)((DWORD)pDosHeader + lpAddressOfFunctionsArray[wHint]); break; } } return lpFunc;
}
BOOL MemFree(LPVOID lpBaseAddress){ BOOL bRet = FALSE; if (lpBaseAddress == NULL) { return TRUE; } bRet = VirtualFree(lpBaseAddress, 0, MEM_RELEASE); return bRet;}

内存直接加载DLL.cpp

// 内存直接加载DLL.cpp : 定义控制台应用程序的入口点。//
#include "stdafx.h"#include "MemLoadDll.h"
int _tmain(int argc, _TCHAR* argv[]){ /* 内存直接加载DLL步骤: 1. 打开文件 2. 计算文件大小 3. 申请内存空间 4. 将文件以磁盘格式读取到内存中 5. 对文件进行拉伸 6. 得到DLL导出函数地址,并调用 7. 释放Dll内存 8. 释放其他需要释放的东西 */
char pszFileName[MAX_PATH] = "C:\Project\TestDll.dll"; byte btRet = 0; //1.打开文件 HANDLE hFile = CreateFile(pszFileName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_ARCHIVE, NULL); if (hFile == INVALID_HANDLE_VALUE) { ShowError("CreateFile"); btRet = 1; return btRet; }
//2.得到文件大小 DWORD dwFileSize = 0; dwFileSize = GetFileSize(hFile, NULL);
//3.将DLL文件加载到内存,这里的加载是安装fileBuffer的形式来的 BYTE* lpData = new BYTE[dwFileSize]; DWORD dwRet = 0; ReadFile(hFile, lpData, dwFileSize, &dwRet, NULL);
//4.将文件以磁盘格式读取到内存中,模拟加载 //5. 对文件进行拉伸 LPVOID lpBaseAddress = MemLoadDll(lpData, dwFileSize); if (lpBaseAddress == NULL) { ShowError("MemLoadDll"); btRet = 3; return btRet; }
//6. 得到DLL导出函数地址,并调用 typedef BOOL(*typedef_ShowMessage)(const char* lpszText, const char* lpszCaption); typedef_ShowMessage ShowMessage = (typedef_ShowMessage)MemGetProcAddress(lpBaseAddress, "ShowMessage"); if (ShowMessage == NULL) { ShowError("ShowMessage"); btRet = 4; return btRet; } ShowMessage("Hello world!", "TEST");
//7.释放Dll内存 if (MemFree(lpBaseAddress) == FALSE) { ShowError("MemFree"); btRet = 5; return 5; } //8.释放和关闭其他资源 delete[] lpData; CloseHandle(hFile);
return 0;}

MemLoadDll.h文件:

#pragma once#include <Windows.h>

typedef BOOL(__stdcall* typedef_CallDllMain)(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved);
VOID ShowError(const char* pszErrorMsg);
LPVOID MemLoadDll(LPVOID lpData, DWORD dwFileSize);
DWORD GetSizeOfImage(LPVOID lpData);
BOOL MemCopy(LPVOID lpBaseAddress, LPVOID lpData);
BOOL DoRelocationTable(LPVOID lpBaseAddress);
BOOL DoImportTable(LPVOID lpBaseAddress);
BOOL SetImageBase(LPVOID lpBaseAddress);
BOOL CallDllMain(LPVOID lpBaseAddress);
LPVOID MemGetProcAddress(LPVOID lpBaseAddress,const char* funcName);
BOOL MemFree(LPVOID lpBaseAddress);


原文始发于微信公众号(loochSec):PE-自实现拉伸运行dll

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年10月13日14:11:29
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   PE-自实现拉伸运行dllhttp://cn-sec.com/archives/2104055.html

发表评论

匿名网友 填写信息