前言:原本已经打算不继续写博客了,但是今天看到了网上有人说的一句话,还是要不停的催促自己来写博客!
通过自身加载模块来实现进程加载:
需要注意的细节:
1.通过INT表来对IAT表进行修复
2.修复重定位时候的注意差值的运算
3.通过内联汇编的时候JMP 的地址是 ENTRY + IMAGEBASE
// 06_04_Inject_process.cpp : Defines the entry point for the console application.
//
#include "PeTools.h"
int main(int argc, char* argv[])
{
LPVOID pFileBuffer = NULL;
LPVOID pImageBuffer = NULL;
DWORD dwBufferLength = 0;
LPVOID pAllocAddr = NULL;
HANDLE hProcess = NULL;
DWORD dwSizeOfImage;
MyReadFile(&pFileBuffer, &dwBufferLength, "C:\Users\dell\Desktop\ipmsg.exe");
CopyFileBufferToImageBuffer(pFileBuffer, &pImageBuffer);
// get ImageBase
DWORD dwImageBase;
dwImageBase = GetImageBase(pFileBuffer);
// get SizeOfImage
dwSizeOfImage = GetSizeOfImage(pFileBuffer);
// to alloc memory
pAllocAddr = VirtualAlloc((LPVOID)dwImageBase, dwSizeOfImage, MEM_COMMIT, PAGE_READWRITE);
if (pAllocAddr == NULL) {
printf("VirtualAlloc Failed , the error is %d", GetLastError());
}
// memcpy
memcpy(pAllocAddr, pImageBuffer, dwSizeOfImage);
// load pe
PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS pNTHeader = NULL;
PIMAGE_FILE_HEADER pPEHeader = NULL;
PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL;
PIMAGE_SECTION_HEADER pSectionHeader = NULL;
PIMAGE_IMPORT_DESCRIPTOR pIMPORT_DESCRIPTOR = NULL;
PIMAGE_IMPORT_BY_NAME pImage_IMPORT_BY_NAME = NULL;
TCHAR ImportTableDllName[20] = { 0 };
TCHAR FunctionName[20] = { 0 };
PDWORD OriginalFirstThunk_INT = NULL;
PDWORD FirstThunk_IAT = NULL;
PIMAGE_THUNK_DATA pImageThunkData = NULL;
DWORD Original = 0;
pDosHeader = (PIMAGE_DOS_HEADER)dwImageBase;
pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)dwImageBase + pDosHeader->e_lfanew);
pPEHeader = (PIMAGE_FILE_HEADER)(((DWORD)pNTHeader) + 4);
pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + IMAGE_SIZEOF_FILE_HEADER);
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + sizeof(IMAGE_OPTIONAL_HEADER32));
//获取导入表的位置,每个导入表的相关信息占20个字节
pIMPORT_DESCRIPTOR = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)dwImageBase + (DWORD)pOptionHeader->DataDirectory[1].VirtualAddress);
//这里可以进行while操作,这里while的判断依据为pIMPORT_DESCRIPTOR个数
while (pIMPORT_DESCRIPTOR->FirstThunk && pIMPORT_DESCRIPTOR->OriginalFirstThunk) {
HMODULE hModule;
memset(ImportTableDllName, 20, 0);
memset(FunctionName, 20, 0);
strcpy(ImportTableDllName, (PCHAR)((DWORD)pFileBuffer + (DWORD)pIMPORT_DESCRIPTOR->Name));
hModule = LoadLibrary(ImportTableDllName);
//FirstThunk转换FOA
FirstThunk_IAT = (PDWORD)((DWORD)pFileBuffer + (DWORD)pIMPORT_DESCRIPTOR->FirstThunk);
DWORD dwFuncAddr = 0;
while (*FirstThunk_IAT) {
if ((*FirstThunk_IAT) & 0X80000000) {
//高位为1 则 除去最高位的值就是函数的导出序号
Original = *FirstThunk_IAT & 0xFFF; //去除最高标志位。
dwFuncAddr = (DWORD)GetProcAddress(hModule, (char*)Original);
}
else
{
//高位不为1 则指向IMAGE_IMPORT_BY_NAME
pImage_IMPORT_BY_NAME = (PIMAGE_IMPORT_BY_NAME)((DWORD)pFileBuffer + *FirstThunk_IAT);
strcpy(FunctionName, (PCHAR)pImage_IMPORT_BY_NAME->Name);
dwFuncAddr = (DWORD)GetProcAddress(hModule, FunctionName);
}
pImageThunkData = (PIMAGE_THUNK_DATA)FirstThunk_IAT;
pImageThunkData->u1.Function = dwFuncAddr;
FirstThunk_IAT++;
}
// 进行遍历操作
pIMPORT_DESCRIPTOR++;
}
DWORD dwOep = GetOep(pFileBuffer) + dwImageBase;
__asm {
jmp dwOep
}
return 0;
}
第二种:内存写入(将自身进程注入指定进程内存中)实现模块进程
需要注意的细节:
1.通过INT表来对IAT表进行修复
2.修复重定位时候的注意差值的运算
3、CreateRemoteThread的时候如果直接使用已存在的线程函数的时候,计算方式是ThreadProc的地址 - 原Imagebase的地址 + 指定进程申请的内存空间的首地址
#include "PeTools.h"
DWORD WINAPI ThreadProc(LPVOID lpParam) {
// load pe
PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS pNTHeader = NULL;
PIMAGE_FILE_HEADER pPEHeader = NULL;
PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL;
PIMAGE_SECTION_HEADER pSectionHeader = NULL;
PIMAGE_IMPORT_DESCRIPTOR pIMPORT_DESCRIPTOR = NULL;
PIMAGE_IMPORT_BY_NAME pImage_IMPORT_BY_NAME = NULL;
PDWORD OriginalFirstThunk = NULL;
PDWORD FirstThunk = NULL;
PIMAGE_THUNK_DATA pImageThunkData = NULL;
DWORD Original = 0;
pDosHeader = (PIMAGE_DOS_HEADER)lpParam;
pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)lpParam + pDosHeader->e_lfanew);
pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pNTHeader + 4);
pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + IMAGE_SIZEOF_FILE_HEADER);
//每个导入表的相关信息占20个字节
pIMPORT_DESCRIPTOR = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)lpParam + pOptionHeader->DataDirectory[1].VirtualAddress);
//这里可以进行while操作,这里while的判断依据为pIMPORT_DESCRIPTOR个数
DWORD dwFuncAddr = 0;
HMODULE hModule;
while (pIMPORT_DESCRIPTOR->FirstThunk && pIMPORT_DESCRIPTOR->OriginalFirstThunk) {
hModule = LoadLibrary((PCHAR)((DWORD)lpParam + (DWORD)pIMPORT_DESCRIPTOR->Name));
// FirstThunk 指向 IMAGE_THUNK_DATA 结构数组
OriginalFirstThunk = (PDWORD)((DWORD)lpParam + (DWORD)pIMPORT_DESCRIPTOR->OriginalFirstThunk);
FirstThunk = (PDWORD)((DWORD)lpParam + (DWORD)pIMPORT_DESCRIPTOR->FirstThunk);
while (*OriginalFirstThunk) {
if (*OriginalFirstThunk & 0x80000000) {
//高位为1 则 除去最高位的值就是函数的导出序号
Original = *OriginalFirstThunk & 0xFFF; //去除最高标志位。
dwFuncAddr = (DWORD)GetProcAddress(hModule, (PCHAR)Original);
}
else
{
//高位不为1 则指向IMAGE_IMPORT_BY_NAME;
pImage_IMPORT_BY_NAME = (PIMAGE_IMPORT_BY_NAME)((DWORD)lpParam + *OriginalFirstThunk);
dwFuncAddr = (DWORD)GetProcAddress(hModule, (PCHAR)pImage_IMPORT_BY_NAME->Name);
}
*FirstThunk = dwFuncAddr;
OriginalFirstThunk++;
}
pIMPORT_DESCRIPTOR++;
}
MessageBox(0, 0, 0, 0);
return 0;
}
int main() {
// 获取自身句柄
HMODULE hModule = GetModuleHandle(NULL);
// 得到自己的ImageBase / SizeOfImage
PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS pNTHeader = NULL;
PIMAGE_FILE_HEADER pPEHeader = NULL;
PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL;
PIMAGE_SECTION_HEADER pSectionHeader = NULL;
PIMAGE_IMPORT_DESCRIPTOR pIMPORT_DESCRIPTOR;
PIMAGE_IMPORT_BY_NAME pImage_IMPORT_BY_NAME;
PDWORD OriginalFirstThunk_INT = NULL;
PDWORD FirstThunk_IAT = NULL;
DWORD Original = 0;
pDosHeader = (PIMAGE_DOS_HEADER)hModule;
pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)hModule + pDosHeader->e_lfanew);
pPEHeader = (PIMAGE_FILE_HEADER)(((DWORD)pNTHeader) + 4);
pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + IMAGE_SIZEOF_FILE_HEADER);
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + sizeof(IMAGE_OPTIONAL_HEADER));
DWORD dwImageBase = pOptionHeader->ImageBase;
DWORD dwSizeOfImage = pOptionHeader->SizeOfImage;
// 创建一个新的缓冲区,将自己复制进去
LPVOID pNewBufferAddress = malloc(dwSizeOfImage);
memset(pNewBufferAddress, dwSizeOfImage, 0);
memcpy(pNewBufferAddress, (LPVOID)hModule, dwSizeOfImage); //需要注意的是当前的PE已经是拉伸过后的了 所以直接用的就是RVA是正确的
// 打开要注入的A进程
HANDLE hProcess;
hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, 14816);
if (hProcess == NULL) {
printf("OpenProcess Failed, the error is %d", GetLastError());
return -1;
}
// 在A进程中申请内存,大小就是SizeOfImage
LPVOID AllocAddress = VirtualAllocEx(hProcess, NULL, dwSizeOfImage, MEM_COMMIT, PAGE_READWRITE);
if (AllocAddress == NULL) {
printf("VirtualAllocEx Failed, the error is %d", GetLastError());
CloseHandle(hProcess);
return -1;
}
// 修复重定位表
FixRelocation(pNewBufferAddress, (DWORD)AllocAddress - dwImageBase);
// 通过 WriteProcessMemory 写入远程进程的内存空间中
DWORD bytes;
BOOL ret;
ret = WriteProcessMemory(hProcess, AllocAddress, pNewBufferAddress, dwSizeOfImage, &bytes);
if (!ret) {
printf("WriteProcessMemory Failed, the error is %d", GetLastError());
CloseHandle(hProcess);
return -1;
}
// 在指定进程中申请要执行线程函数的内存
/*LPVOID lpRemoteThreadAddr = VirtualAllocEx(hProcess, NULL, 0x400, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
// 写入大小为0x400,内容的是 要在对方进程中进行执行的线程函数的地址
ret = WriteProcessMemory(hProcess, lpRemoteThreadAddr, (LPVOID)ThreadProc, 0x400, &bytes);
if (!ret){
printf("WriteProcessMemory Failed, the error is %d", GetLastError());
CloseHandle(hProcess);
return -1;
}*/
// 得到模块中要运行的函数的地址,并且进行IAT表的修复
// ((DWORD)ThreadProc + (DWORD)AllocAddress - (DWORD)hModule)
HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)((DWORD)ThreadProc + (DWORD)AllocAddress - (DWORD)hModule), (LPVOID)AllocAddress, 0, NULL);
WaitForSingleObject(hThread, INFINITE);
CloseHandle(hThread);
CloseHandle(hProcess);
return 0;
}