(我提前道歉,缺少一些重要信息。我真的不知道该包括什么。所以如果有人要我添加任何信息,请告诉我,我会尽力而为。谢谢大家!)
我尝试按照本教程,结合自己的知识,在C:中实现一个进程挖空(RunPE)技术
基本上,我创建一个处于挂起状态的进程,找到它是PEB,然后是Image基地址,Unmap它是内存。
然后我继续阅读源文件(在我的例子中是 notepad.exe),定义它的标题等...... 然后通过重新分配表将所有内容(包括标头、部分和修补字节)写入旧进程(未映射)的内存中。
然后,我只获取进程的上下文并将其 eax 寄存器更改为新进程的入口点,然后恢复主线程。
我尝试在WinDbg中查看,所有内存似乎都被正确写入。 但是,当以 eax 为入口点恢复主线程时,会显示此错误(在 WinDbg 中):
(216c.3d84): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=02fc1860 ebx=030a4000 ecx=00000000 edx=00000000 esi=00000000 edi=00000000
eip=76f35080 esp=02f6fe58 ebp=00000000 iopl=0 nv up ei pl nz na po nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000202
ntdll!RtlUserThreadStart:
76f35080 833ddc69fe7600 cmp dword ptr [ntdll!LdrDelegatedRtlUserThreadStart (76fe69dc)],0 ds:002b:76fe69dc=00000000
我很乐意听到关于如何进一步调试它的任何想法,因为我对它很陌生。
下面是我的
main.c
#include <Windows.h>
#include <stdio.h>
#include <winternl.h>
typedef NTSTATUS(NTAPI* NtUnmapViewOfSection)(
HANDLE ProcessHandle,
PVOID BaseAddress
);
int main(int argc, char* argv[])
{
printf("Creating process\r\n");
LPSTARTUPINFOA si = (LPSTARTUPINFOA)calloc(1, sizeof(STARTUPINFOA));
LPPROCESS_INFORMATION pi = (LPPROCESS_INFORMATION)calloc(1, sizeof(PROCESS_INFORMATION));
// Start process in suspended state
if (!CreateProcessA
(
"C:\\Windows\\sysWOW64\\calc.exe",
NULL,
NULL,
NULL,
NULL,
CREATE_SUSPENDED,
NULL,
NULL,
si,
pi
))
{
printf("Error with CreateProcessA - %d", GetLastError());
return 1;
}
if (!pi->hProcess)
{
printf("Error creating process - %d", GetLastError());
return 1;
}
HANDLE hDestProcess = pi->hProcess;
PROCESS_BASIC_INFORMATION* pbi = (PROCESS_BASIC_INFORMATION*)calloc(1, sizeof(PROCESS_BASIC_INFORMATION));
DWORD retLen = 0;
// Find PEB
if (NtQueryInformationProcess(hDestProcess, ProcessBasicInformation, pbi, sizeof(PROCESS_BASIC_INFORMATION), &retLen))
{
printf("Error finding peb - %d", GetLastError());
return 1;
}
printf("PEB address: %p\n", pbi->PebBaseAddress);
DWORD pebImageBaseOffset = (DWORD)pbi->PebBaseAddress + 0x8;
LPVOID destImageBase = 0;
SIZE_T bytesRead;
// Find image base by PEB's offset 0x8
if (!ReadProcessMemory(hDestProcess, (LPCVOID)pebImageBaseOffset, &destImageBase, 0x4, &bytesRead))
{
printf("Error getting process's image base - %d", GetLastError());
return 1;
}
printf("Process image base: %p\n", destImageBase);
// Read other exe file
HANDLE sourceFile =
CreateFileA("C:\\Windows\\sysWOW64\\notepad.exe", GENERIC_READ, NULL, NULL, OPEN_EXISTING, NULL, NULL);
DWORD sourceFileSize = GetFileSize(sourceFile, NULL);
DWORD fileBytesRead = 0;
LPVOID sourceFileStart = (LPVOID)malloc(sourceFileSize);
ReadFile(sourceFile, sourceFileStart, sourceFileSize, &fileBytesRead, NULL);
PIMAGE_DOS_HEADER sourceDosHeader = (PIMAGE_DOS_HEADER)sourceFileStart;
PIMAGE_NT_HEADERS sourceNtHeaders = (PIMAGE_NT_HEADERS)((DWORD)sourceFileStart + sourceDosHeader->e_lfanew);
SIZE_T sourceSize = sourceNtHeaders->OptionalHeader.SizeOfImage;
// Get the NtUnmapViewOfSection function and unmap the memory
NtUnmapViewOfSection NtUnmapViewOfSectionFunc =
(NtUnmapViewOfSection)GetProcAddress(GetModuleHandleA("ntdll"), "NtUnmapViewOfSection");
if (NtUnmapViewOfSectionFunc == NULL)
{
printf("Problem finding NtUnmapViewOfSection - %d", GetLastError());
return 1;
}
if (NtUnmapViewOfSectionFunc(hDestProcess, destImageBase))
{
printf("Problem unmapping process virtual memory");
return 1;
}
printf("Memory unammped\n");
PVOID newDestImageBase =
VirtualAllocEx(hDestProcess, NULL, sourceSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
printf("New allocated image base: %p\n", newDestImageBase);
sourceNtHeaders->OptionalHeader.ImageBase = (DWORD)destImageBase;
// Write headers
if (!WriteProcessMemory(hDestProcess, newDestImageBase, sourceFileStart, sourceNtHeaders->OptionalHeader.SizeOfHeaders, NULL))
{
printf("Problem writing headers - %d", GetLastError());
return 1;
}
PIMAGE_SECTION_HEADER sourceSection = IMAGE_FIRST_SECTION(sourceNtHeaders);
PIMAGE_SECTION_HEADER sourceSectionOld = sourceSection;
// Save the reloc table pointer for later
DWORD sourceRelocTableRaw;
// Write sections
for (int i = 0; i < sourceNtHeaders->FileHeader.NumberOfSections; i++)
{
if (!sourceSection->PointerToRawData)
continue;
PVOID destSectionAddr = (PVOID)((DWORD)newDestImageBase + sourceSection->VirtualAddress);
PVOID sourceSectionAddr = (PVOID)((DWORD)sourceFileStart + sourceSection->PointerToRawData);
printf("Writing %s section to %p\r\n", sourceSection->Name, destSectionAddr);
if (!WriteProcessMemory(hDestProcess, destSectionAddr, sourceSectionAddr, sourceSection->SizeOfRawData, NULL))
{
printf("Problem writing sections - %d", GetLastError());
return 1;
}
if (memcmp(sourceSection->Name, ".reloc", 6) == 0)
{
sourceRelocTableRaw = sourceSection->PointerToRawData;
}
sourceSection++;
}
// Getting the difference between the real based address and the preffered base address, for relocations
DWORD deltaImageBase = (DWORD)newDestImageBase - sourceNtHeaders->OptionalHeader.ImageBase;
IMAGE_DATA_DIRECTORY relocTable = sourceNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
DWORD relocOffset = 0;
while (relocOffset < relocTable.Size)
{
PIMAGE_BASE_RELOCATION relocBlock = (PIMAGE_BASE_RELOCATION)((DWORD)sourceFileStart + sourceRelocTableRaw + relocOffset);
relocOffset += sizeof(IMAGE_BASE_RELOCATION);
DWORD relocEntryCount = (relocBlock->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(WORD);
PWORD relocEntries = (PWORD)((DWORD)sourceFileStart + sourceRelocTableRaw + relocOffset);
for (DWORD i = 0; i < relocEntryCount; i++)
{
relocOffset += sizeof(WORD);
if (relocEntries[i] & 0xF000 == IMAGE_REL_BASED_ABSOLUTE)
{
// Skip this entry, since it represents an absolute address
continue;
}
DWORD patchAddrRVA = relocBlock->VirtualAddress + (relocEntries[i] & 0x0FFF);
DWORD patchedBuff = 0;
if (!ReadProcessMemory(hDestProcess, (DWORD)newDestImageBase + patchAddrRVA, &patchedBuff, sizeof(DWORD), &bytesRead))
{
printf("Problem reading bytes to patch - %d", GetLastError());
return 1;
}
// Add the difference between the actual image base and the preffered image base
patchedBuff += deltaImageBase;
if (!WriteProcessMemory(hDestProcess, (DWORD)newDestImageBase + patchAddrRVA, &patchedBuff, sizeof(DWORD), NULL))
{
printf("Problem writing patched bytes - %d", GetLastError());
return 1;
}
}
}
printf("Relocation bytes patched\n");
// Get context of the main thread
LPCONTEXT context = (LPCONTEXT)malloc(sizeof(CONTEXT));;
context->ContextFlags = CONTEXT_INTEGER;
GetThreadContext(pi->hThread, context);
// Change entry point
DWORD newEntryPoint = (DWORD)newDestImageBase + sourceNtHeaders->OptionalHeader.AddressOfEntryPoint;
context->Eax = newEntryPoint;
printf("New entry point addr: %p\n", newEntryPoint);
// Set the main thread context with eax containing the new entry point
SetThreadContext(pi->hThread, context);
// Finally, resume the main thread
ResumeThread(pi->hThread);
printf("Process main thread resumed");
// Close handles
CloseHandle(pi->hProcess);
CloseHandle(pi->hThread);
return 0;
}
非常感谢!
PEB的部分.
您已调用
NtQueryInformationProcess
传入目标进程对象句柄。 destImageBase
必须是分配的 PEB 结构内存指针。您需要将 LPVOID destImageBase
替换为 PROCESS_BASIC_INFORMATION destImageBase
。然后在NtUnmapViewOfSection
.中引用它
修改后:
PROCESS_BASIC_INFORMATION destImageBase;
destImageBase.PebBaseAddress;
sourceNtHeaders->OptionalHeader
没有初始化,所以它指向的内存位置是未知的,这将不可避免地导致像read violation等错误
修改后:
PIMAGE_NT_HEADERS32 sourceNtHeaders =(PIMAGE_NT_HEADERS32)malloc(sizeof(*sourceNtHeaders));
IMAGE_OPTIONAL_HEADER32 sourceSize;
sourceSize=(sourceNtHeaders->OptionalHeader);
SIZE_T szsz = sizeof(sourceSize.SizeOfImage);
写头部分.
在
WriteProcessMemory
中,sourceNtHeaders->OptionalHeader.SizeOfHeaders
表示跨越两个block的结构体类型指针,所以返回299错误。
同理,
PIMAGE_SECTION_HEADER
也需要初始化指针和分配内存块
sourceSection= (PIMAGE_SECTION_HEADER)malloc(sizeof(sourceSection));
(PIMAGE_SECTION_HEADER)malloc(sourceSection->VirtualAddress);
sizeof(sourceSection->VirtualAddress);
至于写作部分,我想你还没有走到这一步。而且这部分代码也没有完全改正,即使参考了你贴的process hollowing(RunPE),我也看不懂
例如,在
relocationBlock->BlockSize - sizeof(BASE_RELOCATION_BLOCK)) / sizeof(BASE_RELOCATION_ENTRY)
中,BASE_RELOCATION_ENTRY
被简单地替换为WORD
。所以我暂时不会修改这部分,除非你稍后更新它。
这是完整的代码。希望这会有所帮助。
#include <Windows.h>
#include <stdio.h>
#include <winternl.h>
typedef
NTSTATUS(WINAPI* pfnNtQueryInformationProcess)
(HANDLE ProcessHandle, ULONG ProcessInformationClass,
PVOID ProcessInformation, UINT32 ProcessInformationLength,
UINT32* ReturnLength);
typedef NTSTATUS(NTAPI* NtUnmapViewOfSection)(
HANDLE ProcessHandle,
PVOID BaseAddress
);
int main(int argc, char* argv[])
{
printf("Creating process\r\n");
LPSTARTUPINFOA si = (LPSTARTUPINFOA)calloc(1, sizeof(STARTUPINFOA));
LPPROCESS_INFORMATION pi = (LPPROCESS_INFORMATION)calloc(1, sizeof(PROCESS_INFORMATION));
// Start process in suspended state
BOOL cpa = CreateProcessA
(
"C:\\Users\\Administrator\\Desktop\\calc.exe",
NULL,
NULL,
NULL,
NULL,
CREATE_SUSPENDED,
NULL,
NULL,
si,
pi
);
if (!cpa)
{
printf("Error with CreateProcessA - %d", GetLastError());
return 1;
}
if (!pi->hProcess)
{
printf("Error creating process - %d", GetLastError());
return 1;
}
HANDLE hDestProcess = pi->hProcess;
PROCESS_BASIC_INFORMATION* pbi = (PROCESS_BASIC_INFORMATION*)calloc(1, sizeof(PROCESS_BASIC_INFORMATION));
DWORD retLen = 0;
HMODULE hModule = GetModuleHandleA("ntdll.dll");
pfnNtQueryInformationProcess NtQueryInformationProcess =
(pfnNtQueryInformationProcess)GetProcAddress(hModule, "NtQueryInformationProcess");
// Find PEB
if (NtQueryInformationProcess(hDestProcess, ProcessBasicInformation, pbi, sizeof(PROCESS_BASIC_INFORMATION), (UINT32*) & retLen))
{
printf("Error finding peb - %d", GetLastError());
return 1;
}
printf("PEB address: %p\n", pbi->PebBaseAddress);
DWORD pebImageBaseOffset = (DWORD)pbi->PebBaseAddress + 0x8;
PROCESS_BASIC_INFORMATION destImageBase;
destImageBase.PebBaseAddress;
SIZE_T bytesRead;
// Find image base by PEB's offset 0x8
BOOL rc1 = ReadProcessMemory(hDestProcess, (LPCVOID)pebImageBaseOffset, &destImageBase, 0xD, &bytesRead);
if (!rc1)
{
printf("Error getting process's image base - %d", GetLastError());
return 1;
}
printf("Process image base: %p\n", destImageBase);
// Read other exe file
HANDLE sourceFile =
CreateFileA("C:\\Users\\Administrator\\Desktop\\notepad.exe", GENERIC_READ, NULL, NULL, OPEN_ALWAYS, NULL, NULL);
DWORD sourceFileSize = GetFileSize(sourceFile, NULL);
DWORD fileBytesRead ;
LPVOID sourceFileStart = malloc(sizeof(sourceFileSize));
ReadFile(sourceFile, sourceFileStart, sourceFileSize, &fileBytesRead, NULL);
PIMAGE_DOS_HEADER sourceDosHeader = (PIMAGE_DOS_HEADER)sourceFileStart;
PIMAGE_NT_HEADERS32 sourceNtHeaders =(PIMAGE_NT_HEADERS32)malloc(sizeof(*sourceNtHeaders));
IMAGE_OPTIONAL_HEADER32 sourceSize;
sourceSize=(sourceNtHeaders->OptionalHeader);
SIZE_T szsz = sizeof(sourceSize.SizeOfImage);
sourceNtHeaders = (PIMAGE_NT_HEADERS32)((DWORD)sourceFileStart + sourceDosHeader->e_lfanew);
LPVOID newDestImageBase =
(LPVOID*)VirtualAllocEx(hDestProcess, 0, szsz, MEM_COMMIT| MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if (newDestImageBase == NULL)
{
printf("VirtualAllocEx error :%X\n", (BYTE*)newDestImageBase);
}
// Get the NtUnmapViewOfSection function and unmap the memory
NtUnmapViewOfSection NtUnmapViewOfSectionFunc =
(NtUnmapViewOfSection)GetProcAddress(GetModuleHandleA("ntdll"), "NtUnmapViewOfSection");
if (NtUnmapViewOfSectionFunc(hDestProcess, pi->hProcess) == NULL)
{
printf("Problem finding NtUnmapViewOfSection - %d", GetLastError());
return 1;
}
printf("Memory unammped\n");
printf("New allocated image base: %p\n", newDestImageBase);
//szsz = (DWORD)(destImageBase = {});
DWORD lpflOldProtect;
// Write headers
//VirtualProtectEx(hDestProcess, newDestImageBase, sizeof(sourceSize.SizeOfHeaders), PAGE_EXECUTE_READWRITE, &lpflOldProtect);
BOOL res2= WriteProcessMemory(hDestProcess, newDestImageBase, (LPCVOID)&szsz, sizeof(sourceSize.SizeOfHeaders), NULL);
if (!res2)
{
printf("Problem writing headers - %d", GetLastError());
return 1;
}
//VirtualProtectEx(hDestProcess, newDestImageBase, sizeof(sourceSize.SizeOfHeaders), lpflOldProtect, &lpflOldProtect);
sourceNtHeaders = (PIMAGE_NT_HEADERS32)malloc(sizeof(sourceNtHeaders));
PIMAGE_SECTION_HEADER sourceSection = IMAGE_FIRST_SECTION(sourceNtHeaders);
PIMAGE_SECTION_HEADER sourceSectionOld = sourceSection;
sourceSection= (PIMAGE_SECTION_HEADER)malloc(sizeof(sourceSection));
(PIMAGE_SECTION_HEADER)malloc(sourceSection->VirtualAddress);
sizeof(sourceSection->VirtualAddress);
// Save the reloc table pointer for later
DWORD sourceRelocTableRaw = {};
// Write sections
// for (int i = 0; i < sourceNtHeaders->FileHeader.NumberOfSections; i++)
// {
// if (!sourceSection->PointerToRawData)
// continue;
// PVOID destSectionAddr = (PVOID)((DWORD)newDestImageBase + sourceSection->VirtualAddress);
// sizeof(destSectionAddr);
//destSectionAddr =
// (LPVOID*)VirtualAllocEx(hDestProcess, 0, 4, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
// PVOID sourceSectionAddr = (PVOID)((DWORD)sourceFileStart + sourceSection->PointerToRawData);
// sizeof(sourceSectionAddr);
// printf("Writing %d section to %p\r\n", sourceSection->Name, destSectionAddr);
// SIZE_T ssection = sizeof(sourceSection->SizeOfRawData);
// VirtualProtectEx(hDestProcess, destSectionAddr, sizeof(ssection), PAGE_EXECUTE_READWRITE, &lpflOldProtect);
// if (!WriteProcessMemory(hDestProcess, destSectionAddr, &sourceSectionAddr, ssection, NULL))
// {
// printf("Problem writing sections - %d", GetLastError());
// return 1;
// }
// VirtualProtectEx(hDestProcess, destSectionAddr, sizeof(ssection), lpflOldProtect, &lpflOldProtect);
// if (memcmp(sourceSection->Name, (BYTE*)".reloc", 6) == 0)
// {
// sourceRelocTableRaw = sourceSection->PointerToRawData;
// }
// sourceSection++;
// }
// // Getting the difference between the real based address and the preffered base address, for relocations
// DWORD deltaImageBase = (DWORD)newDestImageBase - sourceNtHeaders->OptionalHeader.ImageBase;
// IMAGE_DATA_DIRECTORY relocTable = sourceNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
// DWORD relocOffset = 0;
// while (relocOffset < relocTable.Size)
// {
//
// PIMAGE_BASE_RELOCATION relocBlock = (PIMAGE_BASE_RELOCATION)((DWORD)sourceFileStart + sourceRelocTableRaw + relocOffset);
// relocOffset += sizeof(IMAGE_BASE_RELOCATION);
// SIZE_T rb = sizeof(relocBlock->SizeOfBlock);
// DWORD relocEntryCount = (rb - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(WORD);
// PWORD relocEntries = (PWORD)((DWORD)sourceFileStart + sourceRelocTableRaw + relocOffset);
// relocEntries = (PWORD)malloc(sizeof(relocEntries));
// for (DWORD i = 0; i < relocEntryCount; i++)
// {
// relocOffset += sizeof(WORD);
// if (relocEntries[i] & 0xF000 == IMAGE_REL_BASED_ABSOLUTE)
// {
// // Skip this entry, since it represents an absolute address
// continue;
// }
//
// PIMAGE_BASE_RELOCATION relocBlock = (PIMAGE_BASE_RELOCATION)malloc(sizeof(relocBlock));
// DWORD patchAddrRVA = relocBlock->VirtualAddress + (relocEntries[i] & 0x0FFF);
// LPVOID np = (LPVOID)sizeof((DWORD)newDestImageBase + patchAddrRVA);
// np =
// (LPVOID*)VirtualAllocEx(hDestProcess, 0, 4, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
// DWORD patchedBuff = 0;
// //VirtualProtectEx(hDestProcess, (LPVOID)(sizeof((DWORD)newDestImageBase + patchAddrRVA)), sizeof((DWORD)newDestImageBase + patchAddrRVA), PAGE_EXECUTE_READWRITE, &lpflOldProtect);
// BOOL btp=ReadProcessMemory(hDestProcess, (LPVOID)np, &patchedBuff, sizeof(DWORD), &bytesRead);
// if (!btp)
// {
// printf("Problem reading bytes to patch1 - %d", GetLastError());
// return 1;
// }
// //VirtualProtectEx(hDestProcess, (LPVOID)(sizeof((DWORD)newDestImageBase + patchAddrRVA)), sizeof(DWORD), PAGE_EXECUTE_READWRITE, &lpflOldProtect);
// // Add the difference between the actual image base and the preffered image base
// patchedBuff += deltaImageBase;
// if (!WriteProcessMemory(hDestProcess, (LPVOID)np, &patchedBuff, sizeof(DWORD), NULL))
// {
// printf("Problem writing patched bytes2 - %d", GetLastError());
// return 1;
// }
// }
// }
printf("Relocation bytes patched\n");
// Get context of the main thread
LPCONTEXT context = (LPCONTEXT)malloc(sizeof(CONTEXT));;
context->ContextFlags = CONTEXT_INTEGER;
GetThreadContext(pi->hThread, context);
// Change entry point
DWORD newEntryPoint = (DWORD)newDestImageBase + sourceNtHeaders->OptionalHeader.AddressOfEntryPoint;
context->Eax = newEntryPoint;
printf("New entry point addr: %p\n", newEntryPoint);
// Set the main thread context with eax containing the new entry point
SetThreadContext(pi->hThread, context);
// Finally, resume the main thread
ResumeThread(pi->hThread);
printf("Process main thread resumed");
// Close handles
CloseHandle(pi->hProcess);
CloseHandle(pi->hThread);
return 0;
}