mactype/dll.cpp
2020-12-06 11:38:08 +01:00

347 lines
13 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "dll.h"
CMemLoadDll::CMemLoadDll():m_bInitDllMain(true)
{
isLoadOk = FALSE;
pImageBase = NULL;
pDllMain = NULL;
}
CMemLoadDll::~CMemLoadDll()
{
if(isLoadOk)
{
//ASSERT(pImageBase != NULL);
//ASSERT(pDllMain != NULL);
//脱钩准备卸载dll
if (m_bInitDllMain)
pDllMain((HINSTANCE)pImageBase,DLL_PROCESS_DETACH,0);
VirtualFree((LPVOID)pImageBase, 0, MEM_RELEASE);
}
}
//MemLoadLibrary函数从内存缓冲区数据中加载一个dll到当前进程的地址空间缺省位置0x10000000
//返回值: 成功返回TRUE , 失败返回FALSE
//lpFileData: 存放dll文件数据的缓冲区
//DataLength: 缓冲区中数据的总长度
BOOL CMemLoadDll::MemLoadLibrary(void* lpFileData, int DataLength, bool bInitDllMain, bool bFreeOnRavFail)
{
this->m_bInitDllMain = bInitDllMain;
if(pImageBase != NULL)
{
return FALSE; //已经加载一个dll还没有释放不能加载新的dll
}
//检查数据有效性,并初始化
if(!CheckDataValide(lpFileData, DataLength))return FALSE;
//计算所需的加载空间
int ImageSize = CalcTotalImageSize();
if(ImageSize == 0) return FALSE;
// 分配虚拟内存
void *pMemoryAddress = VirtualAlloc((LPVOID)0, ImageSize,
MEM_COMMIT|MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if(pMemoryAddress == NULL) return FALSE;
else
{
CopyDllDatas(pMemoryAddress, lpFileData); //复制dll数据并对齐每个段
//重定位信息
/*if(pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress >0
&& pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size>0)
{
DoRelocation(pMemoryAddress);
}
//填充引入地址表
if(!FillRavAddress(pMemoryAddress) && bFreeOnRavFail) //修正引入地址表失败
{
VirtualFree(pMemoryAddress,0,MEM_RELEASE);
return FALSE;
}*/
//修改页属性。应该根据每个页的属性单独设置其对应内存页的属性。这里简化一下。
//统一设置成一个属性PAGE_EXECUTE_READWRITE
unsigned long old;
VirtualProtect(pMemoryAddress, ImageSize, PAGE_EXECUTE_READWRITE,&old);
}
//修正基地址
pNTHeader->OptionalHeader.ImageBase = (DWORD)pMemoryAddress;
//接下来要调用一下dll的入口函数做初始化工作。
pDllMain = (ProcDllMain)(pNTHeader->OptionalHeader.AddressOfEntryPoint +(DWORD_PTR) pMemoryAddress);
BOOL InitResult = !bInitDllMain || pDllMain((HINSTANCE)pMemoryAddress,DLL_PROCESS_ATTACH,0);
if(!InitResult) //初始化失败
{
pDllMain((HINSTANCE)pMemoryAddress,DLL_PROCESS_DETACH,0);
VirtualFree(pMemoryAddress,0,MEM_RELEASE);
pDllMain = NULL;
return FALSE;
}
isLoadOk = TRUE;
pImageBase = (DWORD_PTR)pMemoryAddress;
return TRUE;
}
//MemGetProcAddress函数从dll中获取指定函数的地址
//返回值: 成功返回函数地址 , 失败返回NULL
//lpProcName: 要查找函数的名字或者序号
FARPROC CMemLoadDll::MemGetProcAddress(LPCSTR lpProcName)
{
if(pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress == 0 ||
pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size == 0)
return NULL;
if(!isLoadOk) return NULL;
DWORD OffsetStart = pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
DWORD Size = pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size;
PIMAGE_EXPORT_DIRECTORY pExport = (PIMAGE_EXPORT_DIRECTORY)((DWORD_PTR)pImageBase + pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
DWORD iBase = pExport->Base;
DWORD iNumberOfFunctions = pExport->NumberOfFunctions;
DWORD iNumberOfNames = pExport->NumberOfNames; //<= iNumberOfFunctions
LPDWORD pAddressOfFunctions = (LPDWORD)(pExport->AddressOfFunctions + pImageBase);
LPWORD pAddressOfOrdinals = (LPWORD)(pExport->AddressOfNameOrdinals + pImageBase);
LPDWORD pAddressOfNames = (LPDWORD)(pExport->AddressOfNames + pImageBase);
int iOrdinal = -1;
if(((DWORD)lpProcName & 0xFFFF0000) == 0) //IT IS A ORDINAL!
{
iOrdinal = (DWORD)lpProcName & 0x0000FFFF - iBase;
}
else //use name
{
int iFound = -1;
for(int i=0;i<iNumberOfNames;i++)
{
char* pName= (char* )(pAddressOfNames[i] + pImageBase);
if(strcmp(pName, lpProcName) == 0)
{
iFound = i; break;
}
}
if(iFound >= 0)
{
iOrdinal = (DWORD)(pAddressOfOrdinals[iFound]);
}
}
if(iOrdinal < 0 || iOrdinal >= iNumberOfFunctions ) return NULL;
else
{
DWORD pFunctionOffset = pAddressOfFunctions[iOrdinal];
if(pFunctionOffset > OffsetStart && pFunctionOffset < (OffsetStart+Size))//maybe Export Forwarding
return NULL;
else return (FARPROC)(pFunctionOffset + pImageBase);
}
}
// 重定向PE用到的地址
void CMemLoadDll::DoRelocation( void *NewBase)
{
/* 重定位表的结构:
// DWORD sectionAddress, DWORD size (包括本节需要重定位的数据)
// 例如 1000节需要修正5个重定位数据的话重定位表的数据是
// 00 10 00 00 14 00 00 00 xxxx xxxx xxxx xxxx xxxx 0000
// ----------- ----------- ----
// 给出节的偏移 总尺寸=8+6*2 需要修正的地址 用于对齐4字节
// 重定位表是若干个相连如果address 和 size都是0 表示结束
// 需要修正的地址是12位的高4位是形态字intel cpu下是3
*/
//假设NewBase是0x600000,而文件中设置的缺省ImageBase是0x400000,则修正偏移量就是0x200000
DWORD Delta = (DWORD)NewBase - pNTHeader->OptionalHeader.ImageBase;
//注意重定位表的位置可能和硬盘文件中的偏移地址不同,应该使用加载后的地址
PIMAGE_BASE_RELOCATION pLoc = (PIMAGE_BASE_RELOCATION)((DWORD_PTR)NewBase
+ pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress);
while((pLoc->VirtualAddress + pLoc->SizeOfBlock) != 0) //开始扫描重定位表
{
WORD *pLocData = (WORD *)((DWORD_PTR)pLoc + sizeof(IMAGE_BASE_RELOCATION));
//计算本节需要修正的重定位项(地址)的数目
int NumberOfReloc = (pLoc->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION))/sizeof(WORD);
for( int i=0 ; i < NumberOfReloc; i++)
{
if( (DWORD)(pLocData[i] & 0xF000) == 0x00003000) //这是一个需要修正的地址
{
// 举例:
// pLoc->VirtualAddress = 0x1000;
// pLocData[i] = 0x313E; 表示本节偏移地址0x13E处需要修正
// 因此 pAddress = 基地址 + 0x113E
// 里面的内容是 A1 ( 0c d4 02 10) 汇编代码是: mov eax , [1002d40c]
// 需要修正1002d40c这个地址
DWORD * pAddress = (DWORD *)((DWORD_PTR)NewBase + pLoc->VirtualAddress + (pLocData[i] & 0x0FFF));
*pAddress += Delta;
}
}
//转移到下一个节进行处理
pLoc = (PIMAGE_BASE_RELOCATION)((DWORD)pLoc + pLoc->SizeOfBlock);
}
}
//填充引入地址表
BOOL CMemLoadDll::FillRavAddress(void *pImageBase)
{
// 引入表实际上是一个 IMAGE_IMPORT_DESCRIPTOR 结构数组全部是0表示结束
// 数组定义如下:
//
// DWORD OriginalFirstThunk; // 0表示结束否则指向未绑定的IAT结构数组
// DWORD TimeDateStamp;
// DWORD ForwarderChain; // -1 if no forwarders
// DWORD Name; // 给出dll的名字
// DWORD FirstThunk; // 指向IAT结构数组的地址(绑定后这些IAT里面就是实际的函数地址)
unsigned long Offset = pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress ;
if(Offset == 0) return TRUE; //No Import Table
PIMAGE_IMPORT_DESCRIPTOR pID = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD_PTR) pImageBase + Offset);
while(pID->Characteristics != 0 )
{
PIMAGE_THUNK_DATA32 pRealIAT = (PIMAGE_THUNK_DATA32)((DWORD_PTR)pImageBase + pID->FirstThunk);
PIMAGE_THUNK_DATA32 pOriginalIAT = (PIMAGE_THUNK_DATA32)((DWORD_PTR)pImageBase + pID->OriginalFirstThunk);
//获取dll的名字
WCHAR buf[256]; //dll name;
BYTE* pName = (BYTE*)((DWORD_PTR)pImageBase + pID->Name);
int i;
for(i=0;i<256;i++)
{
if(pName[i] == 0)break;
buf[i] = pName[i];
}
if(i>=256) return FALSE; // bad dll name
else buf[i] = 0;
HMODULE hDll = GetModuleHandle(buf);
if(hDll == NULL)return FALSE; //NOT FOUND DLL
//获取DLL中每个导出函数的地址填入IAT
//每个IAT结构是
// union { PBYTE ForwarderString;
// PDWORD Function;
// DWORD Ordinal;
// PIMAGE_IMPORT_BY_NAME AddressOfData;
// } u1;
// 长度是一个DWORD ,正好容纳一个地址。
for(i=0; ;i++)
{
if(pOriginalIAT[i].u1.Function == 0)break;
FARPROC lpFunction = NULL;
if(pOriginalIAT[i].u1.Ordinal & IMAGE_ORDINAL_FLAG) //这里的值给出的是导出序号
{
lpFunction = GetProcAddress(hDll, (LPCSTR)(pOriginalIAT[i].u1.Ordinal & 0x0000FFFF));
}
else //按照名字导入
{
//获取此IAT项所描述的函数名称
PIMAGE_IMPORT_BY_NAME pByName = (PIMAGE_IMPORT_BY_NAME)
((DWORD_PTR)pImageBase + (DWORD)(pOriginalIAT[i].u1.AddressOfData));
// if(pByName->Hint !=0)
// lpFunction = GetProcAddress(hDll, (LPCSTR)pByName->Hint);
// else
lpFunction = GetProcAddress(hDll, (char *)pByName->Name);
}
if(lpFunction != NULL) //找到了!
{
pRealIAT[i].u1.Function = (DWORD) lpFunction;
}
else return FALSE;
}
//move to next
pID = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD_PTR)pID + sizeof(IMAGE_IMPORT_DESCRIPTOR));
}
return TRUE;
}
//CheckDataValide函数用于检查缓冲区中的数据是否有效的dll文件
//返回值: 是一个可执行的dll则返回TRUE否则返回FALSE。
//lpFileData: 存放dll数据的内存缓冲区
//DataLength: dll文件的长度
BOOL CMemLoadDll::CheckDataValide(void* lpFileData, int DataLength)
{
//检查长度
if(DataLength < sizeof(IMAGE_DOS_HEADER)) return FALSE;
pDosHeader = (PIMAGE_DOS_HEADER)lpFileData; // DOSͷ
//检查dos头的标记
if(pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) return FALSE; //0x5A4D : MZ
//检查长度
if((DWORD)DataLength < (pDosHeader->e_lfanew + sizeof(IMAGE_NT_HEADERS32)) ) return FALSE;
//取得pe头
pNTHeader = (PIMAGE_NT_HEADERS32)( (DWORD_PTR)lpFileData + (DWORD_PTR)pDosHeader->e_lfanew); // PEͷ
//检查pe头的合法性
if(pNTHeader->Signature != IMAGE_NT_SIGNATURE) return FALSE; //0x00004550 : PE00
if((pNTHeader->FileHeader.Characteristics & IMAGE_FILE_DLL) == 0) //0x2000 : File is a DLL
return FALSE;
if((pNTHeader->FileHeader.Characteristics & IMAGE_FILE_EXECUTABLE_IMAGE) == 0) //0x0002 : 指出文件可以运行
return FALSE;
if(pNTHeader->FileHeader.SizeOfOptionalHeader != sizeof(IMAGE_OPTIONAL_HEADER32)) return FALSE;
//取得节表(段表)
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD_PTR)pNTHeader + sizeof(IMAGE_NT_HEADERS32));
//验证每个节表的空间
for(int i=0; i< pNTHeader->FileHeader.NumberOfSections; i++)
{
if((pSectionHeader[i].PointerToRawData + pSectionHeader[i].SizeOfRawData) > (DWORD)DataLength)return FALSE;
}
return TRUE;
}
//计算对齐边界
int CMemLoadDll::GetAlignedSize(int Origin, int Alignment)
{
return (Origin + Alignment - 1) / Alignment * Alignment;
}
//计算整个dll映像文件的尺寸
int CMemLoadDll::CalcTotalImageSize()
{
int Size;
if(pNTHeader == NULL)return 0;
int nAlign = pNTHeader->OptionalHeader.SectionAlignment; //段对齐字节数
// 计算所有头的尺寸。包括dos, coff, pe头 和 段表的大小
Size = GetAlignedSize(pNTHeader->OptionalHeader.SizeOfHeaders, nAlign);
// 计算所有节的大小
for(int i=0; i < pNTHeader->FileHeader.NumberOfSections; ++i)
{
//得到该节的大小
int CodeSize = pSectionHeader[i].Misc.VirtualSize ;
int LoadSize = pSectionHeader[i].SizeOfRawData;
int MaxSize = (LoadSize > CodeSize)?(LoadSize):(CodeSize);
int SectionSize = GetAlignedSize(pSectionHeader[i].VirtualAddress + MaxSize, nAlign);
if(Size < SectionSize)
Size = SectionSize; //Use the Max;
}
return Size;
}
//CopyDllDatas函数将dll数据复制到指定内存区域并对齐所有节
//pSrc: 存放dll数据的原始缓冲区
//pDest:目标内存地址
void CMemLoadDll::CopyDllDatas(void* pDest, void* pSrc)
{
// 计算需要复制的PE头+段表字节数
int HeaderSize = pNTHeader->OptionalHeader.SizeOfHeaders;
int SectionSize = pNTHeader->FileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER);
int MoveSize = HeaderSize + SectionSize;
//复制头和段信息
memmove(pDest, pSrc, MoveSize);
//复制每个节
for(int i=0; i < pNTHeader->FileHeader.NumberOfSections; ++i)
{
if(pSectionHeader[i].VirtualAddress == 0 || pSectionHeader[i].SizeOfRawData == 0)continue;
// 定位该节在内存中的位置
void *pSectionAddress = (void *)((DWORD_PTR)pDest + pSectionHeader[i].VirtualAddress);
// 复制段数据到虚拟内存
memmove((void *)pSectionAddress,
(void *)((DWORD_PTR)pSrc + pSectionHeader[i].PointerToRawData),
pSectionHeader[i].SizeOfRawData);
}
//修正指针,指向新分配的内存
//新的dos头
pDosHeader = (PIMAGE_DOS_HEADER)pDest;
//新的pe头地址
pNTHeader = (PIMAGE_NT_HEADERS32)((DWORD_PTR)pDest + (DWORD_PTR)(pDosHeader->e_lfanew));
//新的节表地址
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD_PTR)pNTHeader + sizeof(IMAGE_NT_HEADERS32));
return ;
}