2. Process Hollowing

#processhollowing#golang #maldev #malwaredevelopment

Process Hollowing is commonly performed by creating a process in a suspended state then unmapping/hollowing its memory, which can then be replaced with malicious code.

The Windows APIs required to perform this technique are the following:

Create suspended process

Starting a process can be achieved by calling the CreateProcess API.

BOOL CreateProcessA(
  [in, optional]      LPCSTR                lpApplicationName,
  [in, out, optional] LPSTR                 lpCommandLine,
  [in, optional]      LPSECURITY_ATTRIBUTES lpProcessAttributes,
  [in, optional]      LPSECURITY_ATTRIBUTES lpThreadAttributes,
  [in]                BOOL                  bInheritHandles,
  [in]                DWORD                 dwCreationFlags,
  [in, optional]      LPVOID                lpEnvironment,
  [in, optional]      LPCSTR                lpCurrentDirectory,
  [in]                LPSTARTUPINFOA        lpStartupInfo,
  [out]               LPPROCESS_INFORMATION lpProcessInformation
);

The most important parameter is the dwCreationFlags should be set to CREATE_SUSPENDED (0x04)

Process Created
Suspended Chrome.exe process

Identify Image Base address

To get the base address of the image we need to perform the following:

  • Call NtQueryInformationProcess -> This will return the ProcessInfromation Struct. This struct includes the PEB Address

  • The base address is stored at offset PEB address + 0x10

  • We then read the contents of the address at memory PEB+0x10 using ReadProcessMemory.

Let's break that down

Calling NTQueryInfromationProcess

  • ProcessHandle: This can be obtained from the ProcessInformation returned from the CreateProcess API

  • ProcessInformationClass: We will set that to ProcessBasicInformation (0x00)

  • ProcessInfromation: A pointer to the PROCESS_BASIC_INFORMATION structure

  • ProcessInformationLength: Length of the sturcture

  • ReturnLength: A pointer to a variable in which the function returns the size of the requested information

The windows package definition of PROCESS_BASIC_STRUCTURE didn't work well in this case so we go ahead and define our own struct.

We now have the address containing the image base address. Since we cannot read it directly we need to use ReadProcessMemory winapi. This API allows us to read the memory of a remote process.

  • hProcess: This can be obtained from the ProcessInformation returned from the CreateProcess API

  • lpBaseAddress: Target address, we will use the imageBaseAddress from the previous step.

  • lpBuffer: We will define a byte slice for the to store the bytes of the remote memory

  • nSize: Here we will read 8-bytes in the case of a 64-bit process. size(uintptr)

  • lpNumberOfBytesRead: Returns the size of bytes read in a variable

A quick look in process hacker shows that the base address of the target Process is indeed the one returned by our code.

Image base address matching the output of the script in ProcessHacker

Identify the Entry Point

Let's break that down with some code:

Find PE Signature Address

Let's read the contents of the headers from memory using ReadProcessMemory.

Having a look at PE-Bear we can confirm that the PE Signature Offset is 0x78 is correct.

PE Signature Offset 0x78

We now add the 0x28 offset to 0x78 to get the entry point of the executable

Once again we confirm with PE-Bear

Entry Point Correctly Identified

Overwrite code with our shellcode

Similar to process injection we now use WriteProcessMemory winapi to write our shellcode to the target region. The benefit is that we don't have to create a new thread but we just overwrite the contents in memory and resume the suspended thread.

WriteProcessMemory winapi will be used to write shellcode to the remote memory

  • hProcess: This can be obtained from the ProcessInformation returned from the CreateProcess API

  • lpBaseAddress: entrypointAddress identified in the previous step

  • lpBuffer: A pointer to the beginning of our shellcode byte array

  • nSize: Size of our shellcode

  • lpNumberOfBytesWritten: Ouputs the number of bytes written to the destination address

Resume Execution

To resume execution we only have to resume the suspended thread. The ResumeThread API will be used to achive that.

We simply pass the handle returned by the CreateProcess API.

Calculator Executed

Complete Code

Last updated

Was this helpful?