#include "Image.h"
#include<Shlwapi.h>
#pragma comment(lib,"shlwapi.lib")
CImage::CImage()
{
m_hFile = INVALID_HANDLE_VALUE;
m_hModule = NULL;
m_pDosHeader = NULL;
m_pFileHeader = NULL;
m_pRelocTable = NULL;
m_pSecHeader = NULL;
m_pExportDir = NULL;
m_pOptHeader = NULL;
SYSTEM_INFO sysinfo;
GetSystemInfo(&sysinfo);
m_dwPageSize = sysinfo.dwPageSize;
}
PBYTE CImage::LoadImage(HANDLE hFile, BOOL bDoReloc, ULONG_PTR RelocBase, BOOL bDoImport)
{
WORD i = 0;
BYTE* pMemory = NULL;
BYTE* MappedBase = NULL;
PIMAGE_SECTION_HEADER pTmpSecHeader = NULL;
BOOL bResult = FALSE;
//一般PE文件大小不会超过4G
DWORD dwFileSize = 0;
DWORD dwIoCnt = 0;
__try
{
m_hFile = hFile;
//获取文件大小
dwFileSize = GetFileSize(m_hFile, NULL);
if (dwFileSize == 0)
{
lstrcpy(m_szErrorMsg, TEXT("文件大小为0!"));
__leave;
}
//读取PE头
DWORD dwSizeToRead = (dwFileSize > PEHEADER_SIZE)? PEHEADER_SIZE : dwFileSize;
ZeroMemory(m_HeaderData, PEHEADER_SIZE);
//读dwSizeToRead个字节,也就是读取PE头
bResult = ReadFile(m_hFile, m_HeaderData, dwSizeToRead, &dwIoCnt, NULL);
if (!bResult)
{
FormatErrorMsg(TEXT("读取文件失败!"), GetLastError());
__leave;
}
if (!VerifyImage(m_HeaderData))
{
lstrcpy(m_szErrorMsg, TEXT("不是有效的PE映像"));
__leave;
}
//解析各个PE头部结构
InitializePEHeaders(m_HeaderData);
pTmpSecHeader = m_pSecHeader;
//开始申请内存,为避免麻烦,这里直接申请可读可写可执行的内存
pMemory = m_hModule = (BYTE*)VirtualAlloc(NULL, m_TotalImageSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (m_hModule == NULL)
{
lstrcpy(m_szErrorMsg, TEXT("内存不足,申请内存失败!"));
__leave;
}
//先拷贝PE头到申请的内存(MS-DOS头、PE文件头、区块表)
memcpy(pMemory, m_HeaderData, m_pOptHeader->SizeOfHeaders);
pMemory += GetAlignedSize(m_pOptHeader->SizeOfHeaders, m_pOptHeader->SectionAlignment);
LARGE_INTEGER liFileOffset;
for (int i = 0; i < m_SectionCnt; i++)
{
//得到文件偏移
liFileOffset.QuadPart = pTmpSecHeader->PointerToRawData;
//设置文件指针指向第一个区块
bResult = SetFilePointerEx(m_hFile, liFileOffset, NULL, FILE_BEGIN);
if (!bResult)
{
FormatErrorMsg(TEXT("设置文件读写位置失败!"), GetLastError());
__leave;
}
//读取各个节
bResult = ReadFile(m_hFile, pMemory, pTmpSecHeader->SizeOfRawData, &dwIoCnt, NULL);
if (!bResult)
{
FormatErrorMsg(TEXT("读取文件失败!"), GetLastError());
__leave;
}
//pMemory指向下一个节存入的缓冲区的地址
pMemory += GetAlignedSize(pTmpSecHeader->Misc.VirtualSize, m_pOptHeader->SectionAlignment);
//pTmpSecHeader指向下一个区块表
pTmpSecHeader++;
}
//重新解析PE头
InitializePEHeaders(m_hModule);
//开始处理重定位数据
if (bDoReloc)
{
//如果RelocBase为0,则按实际加载位置进行重定位
ULONG_PTR BaseToReloc = (RelocBase == 0) ? (DWORD)m_hModule : RelocBase;
ProcessRelocTable(BaseToReloc);
}
//处理导入表
if (bDoImport)
{
ProcessImportTable();
}
bResult = TRUE;//加载成功
}
__finally
{
if (!bResult)
{
if (m_hFile != INVALID_HANDLE_VALUE)
{
CloseHandle(m_hFile);
m_hFile = INVALID_HANDLE_VALUE;
}
}
}
return m_hModule;
}
//以文件路径方式打开PE
PBYTE CImage::LoadImage(TCHAR* szPEPath, BOOL bDoReloc, ULONG_PTR RelocBase, BOOL bDoImport)
{
//保存PE路径
lstrcpy(m_szPEPath, szPEPath);
//以只读方式打开文件
m_hFile = CreateFile(szPEPath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (m_hFile == INVALID_HANDLE_VALUE)
{
FormatErrorMsg(TEXT("打开文件失败!"), GetLastError());
return NULL;
}
return LoadImage(m_hFile, bDoReloc, RelocBase, bDoImport);
}
//释放PE
VOID CImage::FreePE()
{
VirtualFree(m_hModule, 0, MEM_RELEASE);
m_hModule = NULL;
}
//得到指定大小对齐后的大小
DWORD CImage::GetAlignedSize(DWORD theSize, DWORD Alignment)
{
DWORD dwAlignedVirtualSize = 0;
dwAlignedVirtualSize = ALIGN_SIZE_UP(theSize, Alignment);
return dwAlignedVirtualSize;//返回对齐后的大小
}
//得到指定指针对齐后的指针
ULONG_PTR CImage::GetAlignedPointer(ULONG_PTR uPointer, DWORD Alignment)
{
DWORD dwAlignedAddress = 0;
dwAlignedAddress = ALIGN_SIZE_UP(uPointer, Alignment);
return dwAlignedAddress;//对齐后的指针
}
DWORD CImage::_GetProcAddress(PBYTE pModule, char* szFuncName)
{
//自己实现GetProcAddress
DWORD retAddr = 0;
DWORD* namerav, * funrav;
DWORD cnt = 0;
DWORD max, min, mid;
WORD* nameOrdinal;
WORD nIndex = 0;
int cmpresult = 0;
char* ModuleBase = (char*)pModule;
char* szMidName = NULL;
PIMAGE_DOS_HEADER pDosHeader;
PIMAGE_NT_HEADERS pNtHeader;
PIMAGE_OPTIONAL_HEADER pOptHeader;
PIMAGE_EXPORT_DIRECTORY pExportDir;
if (ModuleBase == NULL)
return 0;
pDosHeader = (PIMAGE_DOS_HEADER)ModuleBase;
if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE)
return 0;
pNtHeader = (PIMAGE_NT_HEADERS)(ModuleBase + pDosHeader->e_lfanew);
if (pNtHeader->Signature != IMAGE_NT_SIGNATURE)
return 0;
pOptHeader = &(pNtHeader->OptionalHeader);
pExportDir = (PIMAGE_EXPORT_DIRECTORY)(ModuleBase + pOptHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
//得到函数名称指针数组的虚拟地址
namerav = (DWORD*)(ModuleBase + pExportDir->AddressOfNames);
//函数地址数组
funrav = (DWORD*)(ModuleBase + pExportDir->AddressOfFunctions);
//得到输出序列号数组的虚拟地址
nameOrdinal = (WORD*)(ModuleBase + pExportDir->AddressOfNameOrdinals);
//如果要查询的函数地址小于64KB,那么输入的函数地址作为序列号使用
if ((DWORD)szFuncName < 0x0000FFFF)
{
//通过序列号得到输出函数地址
retAddr = (DWORD)(ModuleBase + funrav[(WORD)szFuncName]);
}
else
{
//二分法查找
max = pExportDir->NumberOfFunctions;
min = 0;
mid = (max + min) / 2;
while (min<max)
{
szMidName = ModuleBase + namerav[mid];
cmpresult = strcmp(szFuncName, szMidName);
if (cmpresult < 0)
{
//比中值小,则取中值-1为最大值
max = mid - 1;
}
else if(cmpresult>0)
{
//比中值大,则取中值+1为最小值
min = mid + 1;
}
else
{
break;
}
mid = (max + min) / 2;
}
//如果找到就得到了该函数地址
if (strcmp(szFuncName, ModuleBase + namerav[mid]) == 0)
{
//得到序列号
nIndex = nameOrdinal[mid];
//通过序列号得到函数地址
retAddr = (DWORD)(ModuleBase + funrav[nIndex]);
}
}
return retAddr;
}
//得到整个映射文件的大小
DWORD CImage::GetTotalImageSize(DWORD Alignment)
{
DWORD TotalSize = 0;
DWORD tmp = 0;
PIMAGE_SECTION_HEADER pTmpSecHeader = m_pSecHeader;
TotalSize += GetAlignedSize(m_pOptHeader->SizeOfHeaders, Alignment);
for (int i = 0; i < m_SectionCnt; i++)
{
tmp = GetAlignedSize(pTmpSecHeader->Misc.VirtualSize, Alignment);
TotalSize += tmp;
pTmpSecHeader++;
}
return TotalSize;
}
//虚拟RVA转换为文件偏移
DWORD CImage::Rav2Raw(DWORD VirtualAddr)
{
DWORD RawAddr = 0;
//如果给的虚拟地址小于文件头的大小,那么虚拟RVA等于文件偏移
if (VirtualAddr < m_pOptHeader->SizeOfHeaders)
{
RawAddr = VirtualAddr;
return RawAddr;
}
//得到虚拟RVA位于哪个区块,并得到该区块头
PIMAGE_SECTION_HEADER pTmpSecHeader = LocateSectionByRVA(VirtualAddr);
if (pTmpSecHeader != NULL)
{
//FileOffset = RVA(不用怀疑就是RVA,相对于映射基地址的RVA) - 区块Rva + Raw
RawAddr = VirtualAddr - pTmpSecHeader->VirtualAddress + pTmpSecHeader->PointerToRawData;
}
return RawAddr;
}
//文件偏移转换为虚拟RVA
DWORD CImage::Raw2Rav(DWORD RawAddr)
{
DWORD RavAddr = 0;
//同上面一样,小于PE头的大小,代表文件偏移等于虚拟RVA
if (RawAddr < m_pOptHeader->SizeOfHeaders)
{
RavAddr = RawAddr;
return RavAddr;
}
PIMAGE_SECTION_HEADER pTmpSecHeader = LocateSectionByRawOffset(RawAddr);
if (pTmpSecHeader != NULL)
{
//文件偏移转换为虚拟RVA
RavAddr = RawAddr - pTmpSecHeader->PointerToRawData + pTmpSecHeader->VirtualAddress;
}
return RavAddr;
}
VOID CImage::FormatErrorMsg(CONST TCHAR* szPrompt, DWORD ErrCode)
{
LPVOID lpMsgBuf;
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
ErrCode,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR)&lpMsgBuf,
0,
NULL
);
wsprintf(m_szErrorMsg, TEXT("%s 错误代码:%d 原因:%s"), szPrompt, ErrCode, (LPCTSTR)lpMsgBuf);
LocalFree(lpMsgBuf);
}
VOID CImage::Cleanup()
{
if (m_hFile != INVALID_HANDLE_VALUE);
{
CloseHandle(m_hFile);
m_hFile = INVALID_HANDLE_VALUE;
}
if (m_hModule != NULL)
{
FreePE();
}
}
VOID CImage::InitializePEHeaders(PBYTE pBase)
{
//解析各个PE头部结构
m_hModule = pBase;
m_pDosHeader = (PIMAGE_DOS_HEADER)pBase;
m_pNtHeaders = (PIMAGE_NT_HEADERS)(pBase + m_pDosHeader->e_lfanew);
m_pFileHeader = &m_pNtHeaders->FileHeader;
m_SectionCnt = m_pFileHeader->NumberOfSections;
m_pOptHeader = &m_pNtHeaders->OptionalHeader;
m_pRelocTable = &(m_pOptHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC]);
m_pSecHeader = (PIMAGE_SECTION_HEADER)((BYTE*)m_pOptHeader + sizeof(IMAGE_OPTIONAL_HEADER));
m_dwEntryPoint = m_pOptHeader->AddressOfEntryPoint;
m_TotalImageSize = m_pOptHeader->SizeOfImage;
m_ImageBase = (ULONG_PTR)m_pOptHeader->ImageBase;
//导入表
m_pImpDataDir = &m_pOptHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
//因为导入表可能会被修改,所以先保存旧的导入表数据
m_OldImpDir.VirtualAddress = m_pImpDataDir->VirtualAddress;
m_OldImpDir.Size = m_pImpDataDir->Size;
if (m_pImpDataDir->VirtualAddress != NULL)
{
m_pImportDesp = (PIMAGE_IMPORT_DESCRIPTOR)(pBase + m_pImpDataDir->VirtualAddress);
}
//导出表
m_pExpDataDir = &m_pOptHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
if (m_pExpDataDir->VirtualAddress != NULL)
{
m_pExportDir = (PIMAGE_EXPORT_DIRECTORY)(pBase + m_pExpDataDir->VirtualAddress);
}
}
//处理重定位
VOID CImage::ProcessRelocTable(ULONG_PTR RelocBase)
{
PIMAGE_BASE_RELOCATION pRelocBlock = NULL;
if (m_pRelocTable->VirtualAddress != NULL)
{
//得到重定位数据块的虚拟地址
pRelocBlock = (PIMAGE_BASE_RELOCATION)(m_hModule + m_pRelocTable->VirtualAddress);
do
{
//处理一个接一个的重定位块,最后一个重定位块以RVA = 0结束
//需要重定位的个数,是本块的大小减去块头的大小,结果是以DWORD表示的大小
//而重定位数据是16位的,也就是2字节,除以2就是数据项个数
int numofReloc = (pRelocBlock->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / 2;
//重定位数据是16位的
WORD offset = 0;
//指向重定位数据指针数组
WORD* pRelocData = (WORD*)((BYTE*)pRelocBlock + sizeof(IMAGE_BASE_RELOCATION));
for (int i = 0; i < numofReloc; i++)
{
ULONG_PTR* RelocAddress = 0;//需要重定位的地址
#ifdef _WIN64
WORD RelocFlag = IMAGE_REL_BASED_DIR64;
#else
WORD RelocFlag = IMAGE_REL_BASED_HIGHLOW;
#endif
//IMAGE_REL_BASED_DIR64
//重定位的高4位是重定位类型
if (((*pRelocData) >> 12) == RelocFlag)//判断重定位类型是否为IMAGE_REL_BASED_HIGHLOW,x86
{
//计算需要进行重定位的地址
//高4位清零得到小偏移
offset = (*pRelocData) & 0xFFF;
//重定位数据的低12位再加上本重定位块头的RAV即真正需要重定位的数据的RAV,RAV+IMAGEBASE得到需要重定位数据指针的虚拟地址
RelocAddress = (ULONG_PTR*)(m_hModule + pRelocBlock->VirtualAddress + offset);
//m_pOptHeader->ImageBase为程序默认的载入地址,重定位的数据等于 默认值+(重定位基地址-默认基地址)
*RelocAddress = *RelocAddress + RelocBase - m_pOptHeader->ImageBase;
}
pRelocData++;
}
//指向下一个重定位块
pRelocBlock = (PIMAGE_BASE_RELOCATION)((char*)pRelocBlock+pRelocBlock->SizeOfBlock);
} while (pRelocBlock->VirtualAddress);//最后一个重定位块以RVA=0结束
}
}
TCHAR* AsciiToUnicode(char* str)
{
DWORD dwNum = 0;
dwNum = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0);
TCHAR* pwHostName = new TCHAR[dwNum];
MultiByteToWideChar(CP_ACP, 0, str, -1, pwHostName, dwNum);
return pwHostName;
}
//处理导入表
BOOL CImage::ProcessImportTable()
{
BOOL bResult = TRUE;
TCHAR szPreDirectory[MAX_PATH] = { 0 };
TCHAR szCurDirectory[MAX_PATH] = { 0 };
TCHAR szPrompt[256] = { 0 };
PIMAGE_IMPORT_DESCRIPTOR pImportDescriptor = m_pImportDesp;
PIMAGE_THUNK_DATA NameThunk = NULL, AddrThunk = NULL;
PIMAGE_IMPORT_BY_NAME pImpName = NULL;
HMODULE hMod = NULL;
TCHAR szImpModName[MAX_PATH] = {0};
if (pImportDescriptor == NULL)
{
//无导入表,不需要处理
return TRUE;
}
//更改当前路径,否则加载某些第三方DLL时将找不到模块
//得到本进程的目录给szPreDirectory
GetCurrentDirectory(MAX_PATH, szPreDirectory);
//把加载的模块的路径赋值给szCurDirectory
lstrcpy(szCurDirectory, m_szPEPath);
/*
PathRemoveFileSpec 函数的作用:将路径末尾的文件名和反斜杠去掉。
我们可以获取EXE文件自身所在的文件夹
*/
//得到加载DLL的目录
PathRemoveFileSpec(szCurDirectory);
//(临时)设置工作目录为加载DLL的工作目录
SetCurrentDirectory(szCurDirectory);
//遍历IAT表,然后处理每一项
while (pImportDescriptor->Name&&pImportDescriptor->OriginalFirstThunk)
{
//得到模块名,并转换为UNICODE字符
char* tmp = (char*)m_hModule + pImportDescriptor->Name;
TCHAR* Ttmp = AsciiToUnicode(tmp);
lstrcpy(szImpModName, Ttmp);
delete[] Ttmp;
//加载模块
hMod = LoadLibrary(szImpModName);
if (hMod == NULL)
{
swprintf(szPrompt, TEXT("加载导入表模块 %s 失败!"), szImpModName);
FormatErrorMsg(szImpModName, GetLastError());
return FALSE;
}
//得到INT表
NameThunk = (PIMAGE_THUNK_DATA)(m_hModule + (ULONG)pImportDescriptor->OriginalFirstThunk);
//IAT表
AddrThunk = (PIMAGE_THUNK_DATA)(m_hModule + (ULONG)pImportDescriptor->FirstThunk);
//遍历IAT表
while (NameThunk->u1.AddressOfData)
{
//处理IAT表
bResult = SnapThunk(hMod, szImpModName, m_hModule, NameThunk, AddrThunk);
if (!bResult)
{
bResult = FALSE;
break;
}
NameThunk++;
AddrThunk++;
}
if (!bResult)
{
break;
}
//指向下一个导入IID
pImportDescriptor++;
}
//恢复工作目录
SetCurrentDirectory(szPreDirectory);
return bResult;
}
BOOL CImage::SnapThunk(HMODULE hImpMode, TCHAR* szImpModeName, PBYTE ImageBase, PIMAGE_THUNK_DATA NameThunk, PIMAGE_THUNK_DATA AddrThunk)
{
BOOL bResult = FALSE;
PIMAGE_IMPORT_BY_NAME pImpName = NULL;
DWORD dwFunAddr = 0;
ULONG Ordinal = 0;
//如果ul的最高位为1,就代表函数是以序号方式输入的
if (NameThunk->u1.AddressOfData & IMAGE_ORDINAL_FLAG32)
{
//得到函数序号
Ordinal = IMAGE_ORDINAL(NameThunk->u1.Ordinal);
//通过序号得到函数地址
dwFunAddr = (DWORD)GetProcAddress(hImpMode, (LPCSTR)Ordinal);
if (dwFunAddr == 0)
{
swprintf(m_szErrorMsg, TEXT("无法在导入模块%S中定位导入函数:%d(序号)"), szImpModeName, Ordinal);
}
}
//如果ul的最高位为0,就代表函数是以名称方式导入的
else
{
pImpName = (PIMAGE_IMPORT_BY_NAME)(m_hModule + (ULONG)NameThunk->u1.AddressOfData);
dwFunAddr = (DWORD)GetProcAddress(hImpMode, (LPCSTR)pImpName->Name);
if (dwFunAddr == 0)
{
swprintf(m_szErrorMsg, TEXT("无法在导入模块%S中定位导入函数:%s(名称)"), szImpModeName, pImpName->Name);
}
}
//如果得到的地址存在就写入对应的IAT
if (dwFunAddr != 0)
{
AddrThunk->u1.Function = dwFunAddr;
bResult = TRUE;
}
return bResult;
}
//根据PE头文件,简单判断PE文件的有效性
BOOL CImage::VerifyImage(PVOID pBase)
{
//解析各个PE头部结构
m_pDosHeader = (PIMAGE_DOS_HEADER)pBase;
if (m_pDosHeader->e_magic != IMAGE_DOS_SIGNATURE)
{
return FALSE;
}
m_pNtHeaders = (PIMAGE_NT_HEADERS)((BYTE*)pBase + m_pDosHeader->e_lfanew);
if (m_pNtHeaders->Signature != IMAGE_NT_SIGNATURE)
{
return FALSE;
}
return TRUE;
}
LPWSTR CImage::GetErrorMsg(TCHAR* szBuf, int BufSize)
{
int len = lstrlen(m_szErrorMsg);
if (len <= BufSize)
{
lstrcpy(szBuf, m_szErrorMsg);
return szBuf;
}
return NULL;
}
//根据相对虚拟地址查找所在的节
PIMAGE_SECTION_HEADER CImage::LocateSectionByRVA(DWORD dwRVA)
{
PIMAGE_SECTION_HEADER pTemp = m_pSecHeader;
for (int i = 0; i < m_SectionCnt; i++)
{
if (pTemp->VirtualAddress <= dwRVA && dwRVA < (pTemp->VirtualAddress + pTemp->Misc.VirtualSize))
{
return pTemp;
}
pTemp++;
}
return NULL;
}
//根据文件偏移确定所在的节
PIMAGE_SECTION_HEADER CImage::LocateSectionByRawOffset(DWORD dwRawOffset)
{
PIMAGE_SECTION_HEADER pTemp = m_pSecHeader;
for (int i = 0; i < m_SectionCnt; i++)
{
if (pTemp->PointerToRawData <= dwRawOffset&&dwRawOffset<(pTemp->PointerToRawData+pTemp->SizeOfRawData))
{
return pTemp;
}
pTemp++;
}
return NULL;
}
//计算某个节按虚拟地址对齐后内存中的空隙大小
DWORD CImage::GetSectionVirtualPaddingSize(PIMAGE_SECTION_HEADER pSecHeader)
{
DWORD AlignedSize = GetAlignedSize(pSecHeader->Misc.VirtualSize, m_pOptHeader->SectionAlignment);
return AlignedSize - pSecHeader->Misc.VirtualSize;
}
//计算某个节按虚拟地址对齐后磁盘中的空隙大小
//VirtualSize和RawSize不确定那个比较大的情况
DWORD CImage::GetSectionPhysialPaddingSize(PIMAGE_SECTION_HEADER pSecHeader)
{
DWORD dwPaddingSize = 0;
if (pSecHeader->Misc.VirtualSize < pSecHeader->SizeOfRawData)
{
//节的内存大小小于文件大小
/*
.text name
*/
dwPaddingSize = pSecHeader->SizeOfRawData - pSecHeader->Misc.VirtualSize;
}
else
{
//节的内存大小大于等于文件中的大小,则认为不存在空隙(因为不好计算)
dwPaddingSize = 0;
}
return dwPaddingSize;
}
//默认情况下是以只读方式打开目标文件的
BOOL CImage::MakeFileHandleWritable()
{
BOOL bResult = FALSE;
HANDLE hNew = INVALID_HANDLE_VALUE;
//OpenProcess 函数用来打开一个已存在的进程对象,并返回进程的句柄。
HANDLE hProc = OpenProcess(PROCESS_DUP_HANDLE, FALSE, GetCurrentProcessId());
if (hProc = NULL)
{
return FALSE;
}
bResult = DuplicateHandle(
hProc,
m_hFile,
hProc,
&hNew,
GENERIC_READ,
FALSE,
0
);
if (bResult)
{
CloseHandle(m_hFile);
m_hFile = hNew;
}
else
{
FormatErrorMsg(TEXT("更改句柄权限失败"), GetLastError());
}
CloseHandle(hProc);
return bResult;
}
//解析当前内存中的PE结构
VOID CImage::AttachToMemory(PVOID pMemory)
{
if (pMemory != NULL)
InitializePEHeaders((PBYTE)pMemory);
}
//解析其他进程中的PE结构
BOOL CImage::AttachToProcess(HANDLE hProc, PVOID ProcessImageBase)
{
BOOL bResult = FALSE;
SIZE_T dwIoCnt = 0;
m_hProc = hProc;
m_ImageBase = (ULONG_PTR)ProcessImageBase;
bResult = ReadProcessMemory(m_hProc, (LPVOID)m_ImageBase, m_HeaderData, 0x1000, &dwIoCnt);
if (!bResult)
{
FormatErrorMsg(TEXT("ReadProcessMemory失败!"), GetLastError());
return FALSE;
}
//初始化PE结构
InitializePEHeaders(m_HeaderData);
return bResult;
}
//在文件中添加一个新区块并返回新节的数据
PIMAGE_SECTION_HEADER CImage::AddNewSectionToFile(char* szSectionName, DWORD SectionSize)
{
//pNewSecHeader指向区块表的最后一项+1,把新区块的IMAGE_SECTION_HEADER添加到区块表的末尾(给区块表增加一项)
PIMAGE_SECTION_HEADER pNewSecHeader = m_pSecHeader + m_SectionCnt;
//pLastSecHeader指向区块表的最后一项
PIMAGE_SECTION_HEADER pLastSecHeader = m_pSecHeader + m_SectionCnt - 1;
DWORD dwSectionVA, dwSectionRawOffset, dwSectionSize;
LARGE_INTEGER liFileOffset;
BOOL bResult = FALSE;
DWORD dwIoCnt = 0;
//计算新节的起始虚拟内存偏移
dwSectionVA = pLastSecHeader->VirtualAddress + GetAlignedSize(pLastSecHeader->Misc.VirtualSize, m_pOptHeader->SectionAlignment);
//计算新节的物理地址起始偏移
dwSectionRawOffset = pLastSecHeader->PointerToRawData + GetAlignedSize(pLastSecHeader->SizeOfRawData, m_pOptHeader->FileAlignment);
//计算新节的大小,按文件对齐粒度对齐
dwSectionSize = GetAlignedSize(SectionSize, m_pOptHeader->FileAlignment);
//设置文件指针位置
liFileOffset.QuadPart = dwSectionRawOffset + dwSectionSize;
bResult = SetFilePointerEx(m_hFile, liFileOffset, NULL, FILE_BEGIN);
if (!bResult)
{
FormatErrorMsg(TEXT("添加新节时设置文件指针错误!"), GetLastError());
return NULL;
}
//设置文件尾
bResult = SetEndOfFile(m_hFile);
if (!bResult)
{
FormatErrorMsg(TEXT("添加新节时设置文件结束位置错误"), GetLastError());
return NULL;
}
//填充SectionHeader
ZeroMemory(pNewSecHeader, sizeof(IMAGE_SECTION_HEADER));
strncpy((char*)pNewSecHeader->Name, szSectionName, 8);
pNewSecHeader->Misc.VirtualSize = dwSectionSize;
pNewSecHeader->VirtualAddress = dwSectionVA;
pNewSecHeader->PointerToRawData = dwSectionRawOffset;
pNewSecHeader->SizeOfRawData = dwSectionSize;
//区块属性
pNewSecHeader->Characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_MEM_EXECUTE;
//更新PE头中的节个数
m_pFileHeader->NumberOfSections += 1;
m_SectionCnt++;
//更新PE头中的总映像大小
m_pOptHeader->SizeOfImage += dwSectionSize;
//保存PE头到文件中
liFileOffset.QuadPart = 0;
bResult = SetFilePointerEx(m_hFile, liFileOffset, NULL, FILE_BEGIN);
if (!bResult)
{
FormatErrorMsg(TEXT("添加新节保存PE时设置文件指针错误!"), GetLastError());
return NULL;
}
bResult = WriteFile(m_hFile, m_hModule, m_pOptHeader->SizeOfHeaders, &dwIoCnt, NULL);
if (!bResult)
{
FormatErrorMsg(TEXT("添加新节保存PE时写入文件错误"), GetLastError());
return NULL;
}
FlushFileBuffers(m_hFile);
return pNewSecHeader;
}
//在内存中添加一个新节并返回新节的数据
PIMAGE_SECTION_HEADER CImage::AddNewSectionToMemory(char* szSectionName, DWORD SectionSize)
{
PIMAGE_SECTION_HEADER pNewSecHeader = m_pSecHeader + m_SectionCnt;
PIMAGE_SECTION_HEADER pLastSecHeader = m_pSecHeader + m_SectionCnt - 1;
DWORD dwSectionVA, dwSectionRawOffset, dwSectionSize;
BOOL bResult = FALSE;
SIZE_T dwIoCnt = 0;
HANDLE hProc = (m_hProc == NULL) ? GetCurrentProcess() : m_hProc;
ULONG_PTR HighestUserAddress = 0;
BYTE PEHeader[0x1000] = { 0 };
//获取基本的地址空间信息
SYSTEM_INFO sysinfo;
ZeroMemory(&sysinfo, sizeof(SYSTEM_INFO));
GetSystemInfo(&sysinfo);
//应用程序可以访问的最高内存地址。
HighestUserAddress = (ULONG_PTR)sysinfo.lpMaximumApplicationAddress;
//计算新节的起始虚拟内存偏移
dwSectionVA = pLastSecHeader->VirtualAddress + ALIGN_SIZE_UP(pLastSecHeader->Misc.VirtualSize, m_pOptHeader->SectionAlignment);
//计算新节的物理起始偏移
dwSectionRawOffset = pLastSecHeader->PointerToRawData + GetAlignedSize(pLastSecHeader->SizeOfRawData, m_pOptHeader->FileAlignment);
//计算新节的大小,按文件对齐粒度对齐
dwSectionSize = GetAlignedSize(SectionSize, m_pOptHeader->FileAlignment);
//新节的起始内存地址
ULONG_PTR dwNewSectionStartAddr = m_ImageBase + dwSectionVA;
//dwNewSectionStartAddr 虚拟内存空间的粒度....按虚拟内存空间粒度对齐节的起始地址
ULONG_PTR AddressToAlloc = NULL;//= GetAlignedPointer(dwNewSectionStartAddr, sysinfo.dwAllocationGranularity);
PBYTE AllocatedMem = NULL;
//从PE最后一个节开始,向后申请内存
for (AddressToAlloc = dwNewSectionStartAddr; AddressToAlloc < HighestUserAddress; AddressToAlloc += sysinfo.dwAllocationGranularity)
{
//申请地址
AllocatedMem = (PBYTE)VirtualAllocEx(hProc, (PVOID)AddressToAlloc, dwSectionSize, MEM_RESERVE | MEM_COMMIT | PAGE_EXECUTE_READWRITE);
if (AllocatedMem != NULL)
{
break;
}
}
if (AllocatedMem == NULL)
{
FormatErrorMsg(TEXT("添加新节时在目标进程中申请内存失败!"), GetLastError());
return NULL;
}
//求出新分配的内存起始地址相对于m_ImageBase的偏移,也就是新节的RVA
dwSectionVA = FILED_OFFSET(AllocatedMem, m_ImageBase);
//填充SectionHeader
ZeroMemory(pNewSecHeader, sizeof(IMAGE_SECTION_HEADER));
strncpy((char*)pNewSecHeader->Name, szSectionName, 8);
pNewSecHeader->Misc.VirtualSize = dwSectionSize;
pNewSecHeader->VirtualAddress = dwSectionSize;
pNewSecHeader->PointerToRawData = dwSectionRawOffset;
pNewSecHeader->SizeOfRawData = dwSectionSize;
pNewSecHeader->Characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_MEM_EXECUTE;
//更新PE头的节点个数
m_pFileHeader->NumberOfSections += 1;
//更新节的数量
m_SectionCnt++;
//更新PE头中的总映像大小
m_pOptHeader->SizeOfImage += dwSectionSize;
//更新目标进程的PE头
DWORD dwOldProtect = 0;
//修改目标进程内存属性为可写
bResult = VirtualProtectEx(hProc, (LPVOID)m_ImageBase, m_pOptHeader->SizeOfHeaders, PAGE_READWRITE, &dwOldProtect);
if (!bResult)
{
FormatErrorMsg(TEXT("修改目标进程内存属性"), GetLastError());
return NULL;
}
//向目标进程的内存写入PE头
bResult = WriteProcessMemory(hProc, (LPVOID)m_ImageBase, m_HeaderData, m_pOptHeader->SizeOfHeaders, &dwIoCnt);
if (!bResult)
{
FormatErrorMsg(TEXT("向目标进程写入PE头数据时错误!"), GetLastError());
return NULL;
}
return pNewSecHeader;
}
//对文件最后一个节进行扩展
PIMAGE_SECTION_HEADER CImage::ExtraLastSectionSizeToFile(DWORD SectionAddSize)
{
//得到最后一个节的区块表
PIMAGE_SECTION_HEADER pLastSecHeader = m_pSecHeader + m_SectionCnt - 1;
DWORD dwSectionNewVirtualSize, dwSectionNewRawOffset, dwSectionNewRawSize;
DWORD dwOldSectionVirtualSize = 0;
LARGE_INTEGER liFileOffset;
BOOL bResult = FALSE;
DWORD dwIoCnt = 0;
/*在扩展最后一个节的情况下,需要更改最后一个节的RawSize和VirtualSize,起始偏移均不变
计算新节的物理大小,按文件对齐粒度对齐*/
//得到最后一个节的文件偏移
dwSectionNewRawOffset = pLastSecHeader->PointerToRawData;
//得到最后一个节扩展后的大小
dwSectionNewRawSize = GetAlignedSize(pLastSecHeader->SizeOfRawData + SectionAddSize, m_pOptHeader->FileAlignment);
//得到最后一个节在内存中对齐后的大小(未扩展,也就是Old)
dwOldSectionVirtualSize = dwSectionNewVirtualSize = GetAlignedSize(pLastSecHeader->Misc.VirtualSize, m_pOptHeader->SectionAlignment);
//计算出新节的VirtualSize大小
if (pLastSecHeader->Misc.VirtualSize < dwSectionNewRawSize)
{
dwSectionNewVirtualSize += SectionAddSize;
}
//设置文件指针位置为扩展后的末尾
liFileOffset.QuadPart = dwSectionNewRawOffset + pLastSecHeader->SizeOfRawData + SectionAddSize;
bResult = SetFilePointerEx(m_hFile, liFileOffset, NULL, FILE_BEGIN);
if (!bResult)
{
FormatErrorMsg(TEXT("添加新节时设置文件指针错误"), GetLastError());
return NULL;
}
//设置文件尾
bResult = SetEndOfFile(m_hFile);
if (!bResult)
{
FormatErrorMsg(TEXT("添加新节时设置文件结束位置错误!"), GetLastError());
return NULL;
}
//填充SectionHeader
pLastSecHeader->Misc.VirtualSize = dwSectionNewVirtualSize;
pLastSecHeader->SizeOfRawData = dwSectionNewRawSize;
pLastSecHeader->Characteristics |= IMAGE_SCN_MEM_READ;
//更新PE头中的总映像大小
m_pOptHeader->SizeOfImage = m_pOptHeader->SizeOfImage - dwOldSectionVirtualSize + GetAlignedSize(pLastSecHeader->Misc.VirtualSize, m_pOptHeader->SectionAlignment);
//保存PE头到文件中
liFileOffset.QuadPart = 0;
bResult = SetFilePointerEx(m_hFile, liFileOffset, NULL, FILE_BEGIN);
if (!bResult)
{
FormatErrorMsg(TEXT("添加新节保存PE时设置文件指针错误!"), GetLastError());
return NULL;
}
bResult = WriteFile(m_hFile, m_hModule, m_pOptHeader->SizeOfHeaders, &dwIoCnt, NULL);
if (!bResult)
{
FormatErrorMsg(TEXT("添加新节保存PE时写入文件错误!"), GetLastError());
return NULL;
}
FlushFileBuffers(m_hFile);
return pLastSecHeader;
}
CImage辅助类
最新推荐文章于 2020-07-01 16:41:00 发布