Package rekall :: Package plugins :: Package windows :: Package gui :: Module userhandles
[frames] | no frames]

Source Code for Module rekall.plugins.windows.gui.userhandles

  1  # Copyright (C) 2007-2013 Volatility Foundation 
  2  # Copyright (C) 2010,2011,2012 Michael Hale Ligh <michael.ligh@mnin.org> 
  3  # Copyright 2014 Google Inc. All Rights Reserved. 
  4  # 
  5  # This program is free software; you can redistribute it and/or modify it under 
  6  # the terms of the GNU General Public License as published by the Free Software 
  7  # Foundation; either version 2 of the License, or (at your option) any later 
  8  # version. 
  9  # 
 10  # This program is distributed in the hope that it will be useful, but WITHOUT 
 11  # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 
 12  # FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more 
 13  # details. 
 14  # 
 15  # You should have received a copy of the GNU General Public License 
 16  # along with Rekall.  If not, see <http://www.gnu.org/licenses/>. 
 17  # 
 18   
 19  """Analyzes User handles registered with the Win32k Subsystem. 
 20   
 21  Windows allows user applications to register handles with the GUI subsystem. The 
 22  GUI subsystem can then call back into the user code for various purposes. The 
 23  Win32k allocates tag* structures for each user object that is registered. These 
 24  allocations exist on the win32k heap. 
 25   
 26  In this module we enumerate the heap and extract the tag* objects which 
 27  correspond to each heap allocation. This allows us to examine these allocations 
 28  in more detail. 
 29   
 30  One of the user handles is tagEVENTHOOK. A user application can register a hook 
 31  callback with SetWindowsHookEx(). This invokes a callback when an event is seen 
 32  (e.g. keyboard press - for a key logger) or desktop switch. Since tagEVENTHOOK 
 33  is just another user object, we can leverage the yser handles plugin to retrieve 
 34  all hooks. 
 35   
 36  References: 
 37  http://mista.nu/research/mandt-win32k-paper.pdf 
 38   
 39  http://volatility-labs.blogspot.de/2012/09/movp-31-detecting-malware-hooks-in.html 
 40  """ 
 41   
 42  from rekall import obj 
 43  from rekall.plugins.windows import common 
 44  from rekall.plugins.windows.gui import constants 
 45  from rekall.plugins.windows.gui import win32k_core 
 46   
 47   
48 -class UserHandles(win32k_core.Win32kPluginMixin, 49 common.WinProcessFilter):
50 """Dump the USER handle tables""" 51 52 name = "userhandles" 53 54 __args = [ 55 dict(name="type", default=".", type="RegEx", 56 help="Filter handle type by this Regular Expression."), 57 58 dict(name="free", type="Boolean", 59 help="Also include free handles.") 60 ] 61 62 table_header = [ 63 dict(name="divider", type="Divider"), 64 dict(name="SessionId", hidden=True), 65 dict(name="SharedInfo", hidden=True), 66 dict(name="_HANDLEENTRY", style="address"), 67 dict(name="_HEAD", style="address"), 68 dict(name="handle", style="address"), 69 dict(name="type", width=20), 70 dict(name="flags", width=8, align="c"), 71 dict(name="thread", width=8, align="c"), 72 dict(name="process"), 73 ] 74
75 - def handles(self):
76 """A Generator of filtered handles.""" 77 for session in self.session.plugins.sessions().session_spaces(): 78 pids = set() 79 if self.filtering_requested: 80 pids = set([x.pid for x in self.filter_processes()]) 81 82 shared_info = self.win32k_profile.get_constant_object( 83 "gSharedInfo", 84 target="tagSHAREDINFO", 85 vm=session.obj_vm) 86 87 for handle in shared_info.aheList: 88 # Do not show free handles if requested. 89 if handle.bType == "TYPE_FREE" and not self.plugin_args.free: 90 continue 91 92 # Skip pids that do not match. 93 if (self.filtering_requested and 94 handle.Process.UniqueProcessId not in pids): 95 continue 96 97 # Allow the user to match of handle type. 98 if not self.plugin_args.type.search(str(handle.bType)): 99 continue 100 101 yield session, shared_info, handle
102
103 - def collect(self):
104 current_session = None 105 for session, shared_info, handle in self.handles(): 106 if current_session != session.SessionId: 107 current_session = session.SessionId 108 109 divider = ( 110 "SharedInfo: {0:#x}, SessionId: {1} " 111 "Shared delta: {2}\n".format( 112 shared_info, session.SessionId, 113 shared_info.ulSharedDelta)) 114 115 divider += ( 116 "aheList: {0:#x}, Table size: {1:#x}, " 117 "Entry size: {2:#x}".format( 118 shared_info.aheList, 119 shared_info.psi.cbHandleTable, 120 shared_info.m("HeEntrySize") or 121 shared_info.obj_vm.profile.get_obj_size("_HANDLEENTRY") 122 )) 123 124 yield dict(divider=divider) 125 126 yield dict(SessionId=session.SessionId, 127 SharedInfo=shared_info, 128 _HANDLEENTRY=handle, 129 _HEAD=handle.phead.deref(), 130 handle=handle.phead.h or 0, 131 type=handle.bType, 132 flags=handle.bFlags, 133 thread=handle.Thread.Cid.UniqueThread, 134 process=handle.Process)
135 136
137 -class WinEventHooks(win32k_core.Win32kPluginMixin, 138 common.WinProcessFilter):
139 """Print details on windows event hooks""" 140 141 name = "eventhooks" 142
143 - def render(self, renderer):
144 handle_plugin = self.session.plugins.userhandles(type="WINEVENTHOOK") 145 for session, _, handle in handle_plugin.handles(): 146 renderer.section() 147 148 renderer.format( 149 "Handle: {0:#x}, Object: {1:#x}, Session: {2}\n", 150 handle.phead.h, 151 handle.phead, 152 session.SessionId) 153 154 renderer.format( 155 "Type: {0}, Flags: {1}, Thread: {2}, Process: {3} ({4})\n", 156 handle.bType, 157 handle.bFlags, 158 handle.Thread.Cid.UniqueThread, 159 handle.Process.UniqueProcessId, 160 handle.Process.name, 161 ) 162 163 event_hook = handle.reference_object() 164 renderer.format( 165 "eventMin: {0:#x} {1}\neventMax: {2:#x} {3}\n", 166 event_hook.eventMin, 167 event_hook.eventMin, 168 event_hook.eventMax, 169 event_hook.eventMax, 170 ) 171 172 renderer.format( 173 "Flags: {0}, offPfn: {1:#x}, idProcess: {2}, idThread: {3}\n", 174 event_hook.dwFlags, 175 event_hook.offPfn, 176 event_hook.idProcess, 177 event_hook.idThread, 178 ) 179 180 ## Work out the WindowStation\Desktop path by the handle 181 ## owner (thread or process) 182 183 renderer.format("ihmod: {0}\n", event_hook.ihmod)
184 185
186 -class Gahti(win32k_core.Win32kPluginMixin, 187 common.WindowsCommandPlugin):
188 """Dump the USER handle type information.""" 189 190 name = "gahti" 191
192 - def gahti(self, session):
193 return self.win32k_profile.get_constant_object( 194 "gahti", 195 target="IndexedArray", 196 target_args=dict( 197 index_table=constants.HANDLE_TYPE_ENUM_SEVEN, 198 target="tagHANDLETYPEINFO", 199 count=20 if self.profile.metadata("version") < 6.1 else 22 200 ), 201 vm=session.obj_vm, 202 )
203
204 - def render(self, renderer):
205 renderer.table_header( 206 [("Session", "session", ">8"), 207 ("Type", "type", "20"), 208 ("Tag", "tag", "8"), 209 ("fnDestroy", "fnDestroy", "[addrpad]"), 210 ("Flags", "flags", ""), 211 ]) 212 213 for session in self.session.plugins.sessions().session_spaces(): 214 for handle in self.gahti(session): 215 renderer.table_row( 216 session.SessionId, 217 handle.obj_name, 218 handle.dwAllocTag, 219 handle.fnDestroy, 220 handle.bObjectCreateFlags)
221 222 223
224 -class WinMessageHooks(win32k_core.Win32kPluginMixin, 225 common.WindowsCommandPlugin):
226 """List desktop and thread window message hooks.""" 227 228 name = "messagehooks" 229 230 table_header = [ 231 dict(name="tagHOOK", style="address"), 232 dict(name="session"), 233 dict(name="owner", width=30), 234 dict(name="thread", width=30), 235 dict(name="filter", width=15), 236 dict(name="flags", width=10), 237 dict(name="function", style="address"), 238 dict(name="module"), 239 ] 240
241 - def __init__(self, **kwargs):
242 super(WinMessageHooks, self).__init__(**kwargs) 243 self.handles = {} 244 self.cc = self.session.plugins.cc()
245
246 - def atom_number_from_ihmod(self, session, ihmod):
247 """Resolve the module name from the ihmod field. 248 249 The ihmod is an index into the array located at 250 "win32k!aatomSysLoaded". This array contains the atom number. We need to 251 use the atom number to resolve the string which is the module name. 252 """ 253 atom_list = self.win32k_profile.get_constant_object( 254 "aatomSysLoaded", 255 target="Array", 256 target_args=dict( 257 target="unsigned short", 258 ), 259 vm=session.obj_vm 260 ) 261 262 return atom_list[ihmod]
263
264 - def module_name_from_hook(self, global_atom_table, session, hook):
265 ihmod = hook.ihmod 266 if ihmod == -1: 267 # Return the owner process. 268 process = self.get_owner(session, hook) 269 if process: 270 # We need to resolve the address using the process AS. 271 self.cc.SwitchProcessContext(process) 272 return self.session.address_resolver.format_address( 273 hook.offPfn, max_distance=1e6) 274 275 return obj.NoneObject() 276 277 atom_num = self.atom_number_from_ihmod(session, ihmod) 278 module_name = global_atom_table.get(atom_num) 279 if module_name: 280 module_name = module_name.Name 281 else: 282 module_name = ihmod 283 284 return module_name
285
286 - def get_owner(self, session, hook):
287 owner = hook.m("head.pti.pEThread.Tcb.Process").dereference_as( 288 "_EPROCESS") 289 if owner: 290 return owner 291 292 session_id = session.SessionId.v() 293 self._build_handle_cache() 294 handle = hook.head.h.v() 295 handle_entry = self.handles.get( 296 (session_id, handle), obj.NoneObject( 297 "Unknown handle %s", handle)) 298 299 return handle_entry.pOwner.ppi.Process
300
301 - def get_owner_string(self, session, hook):
302 owner = self.get_owner(session, hook) 303 if owner: 304 return "%s (%s)" % (owner.name, owner.pid)
305
306 - def _build_handle_cache(self):
307 """Builds a cache of user handles for hooks.""" 308 if not self.handles: 309 userhandles = self.session.plugins.userhandles() 310 for s, _, handle in userhandles.handles(): 311 key = (s.SessionId.v(), handle.phead.h.v()) 312 self.handles[key] = handle
313
314 - def collect(self):
315 atoms_plugin = self.session.plugins.atoms() 316 for session in self.session.plugins.sessions().session_spaces(): 317 318 global_atom_table = dict( 319 (x.Atom, x) for _, x in atoms_plugin.session_atoms(session)) 320 321 # Find the hooks in each desktop. 322 windows_stations_plugin = self.session.plugins.windows_stations() 323 for station in windows_stations_plugin.stations_in_session(session): 324 for desktop in station.desktops(): 325 326 # First report all global hooks in the desktop. 327 for hook_name, hook in desktop.hooks(): 328 module_name = self.module_name_from_hook( 329 global_atom_table, session, hook) 330 331 yield dict(tagHOOK=hook, 332 session=station.dwSessionId, 333 owner=self.get_owner_string(session, hook), 334 thread="<any>", 335 filter=hook_name, 336 flags=hook.flags, 337 function=hook.offPfn, 338 module=module_name, 339 ) 340 341 # Now report all thread hooks in this desktop. 342 for thrd in desktop.threads(): 343 info = "{0} ({1} {2})".format( 344 thrd.pEThread.Cid.UniqueThread, 345 thrd.ppi.Process.ImageFileName, 346 thrd.ppi.Process.UniqueProcessId 347 ) 348 349 for name, hook in thrd.hooks(): 350 module_name = self.module_name_from_hook( 351 global_atom_table, session, hook) 352 353 yield dict(tagHOOK=hook, 354 session=session.SessionId, 355 owner=self.get_owner_string(session, hook), 356 thread=info, 357 filter=name, 358 flags=hook.flags, 359 function=hook.offPfn, 360 module=module_name, 361 )
362