实现功能:
-
不借助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;
}
//将文件复制到程序内存中
//也就是FileBufferToImageBuffer
BOOL 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 : 定义控制台应用程序的入口点。
//
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文件:
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
免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论