内部包含了一些有关PE结构的变量和一些函数函数列表如下
ALIGN_SIZE_UP(Size,Alignment) (((ULONG_PTR)(Size) + Alignment - 1) & ~(Alignment - 1))
利用CreateProcess
并以挂起的方式启动需要注入程序
这里采用的方法是遍历全部内存地址,并且以dwpagesize
为单位,VirtualQueryEX
判断页面状态,如果为MEM_COMMIT
(提交)状态,在判断内存种类如果为MEM_IMAGE
,则利用函数GetMappedFileName
,判断该内存的名称与我们是否一致。如果一致则成功得到基址。
首先ReadProcessMemory
读取PE程序,并初始化PE头,根据基地址获取PIMAGE_DOS_HEADER
根据DOS头e_lfanew
获取PIMAGE_NT_HEADER
地址
根据NT头FileHeader
获取文件头地址,OptionalHeader
获取可选头地址
根据文件头NumberOfSections
获取节区个数
根据可选头DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC]
获取基址重定位表,加上可选头大小即可得到节区头地址。AddressOfEntryPoint
获取程序入口地址。SizeOfImage
获取镜像大小。ImageBase
获取基址DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]
获取导入表,DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]
获取导出表。
根据导入表VirtualAddress
获取导入表虚拟地址,size
获取大小
根据导出表VirtualAddress
获取导出表虚拟地址。
遍历各个节区头的VirtualAddress
,来判断。
计算好新节区大小(导入表原始大小+一个导入表结构体+4*sizeof(ULONG_PTR)
+strlen(szDllName)
+1+sizeof(word)
+strlen(szDllExportFunName)
+1)
其中4个(ULONG_PTR)的指针分别为Import_BY_NAME结构体VA和0填充再加上IAT和0填充
计算新节区的内存偏移
dwSectionVA = pLastSecHeader->VirtualAddress + ALIGN_SIZE_UP(pLastSecHeader->Misc.VirtualSize,m_pOptHeader->SectionAlignment);
计算新节区头偏移
PIMAGE_SECTION_HEADER pNewSecHeader = m_pSecHeader + m_SectionCnt
VirtualAllocEX
以dwSectionVA+Imagebase
开始分配内存,大小为新节区大小
之后将节区头大小偏移。并且将节区头各属性一一赋值。
再更新文件头的节区个数,可选头的SizeOfImage大小要加上新节区大小。
首先填充DLLname,之后填充PIMPORT_BY_NAME结构,之后再填充OriginalFirst其中OringalFirst指向PIMPORT_BY_NAME偏移。FirstThunk就用0来填充。
将更新导入表的大小,设立新导入表的偏移
Image.m_pImpDataDir->Size = dwNewIIDSize;
Image.m_pImpDataDir->VirtualAddress = dwVAToStoreNewIID;
Image.m_pOptHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].VirtualAddress = 0;
Image.m_pOptHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].Size = 0;
强迫加载器重新加载IAT。
将改写的数据(文件头,可选头,节区头,新节区)全部写入进程内存。
最后执行主线程。注入完毕,程序加载该dll。
代码参考:加密与解密4