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

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

  1  # Rekall Memory Forensics 
  2  # Copyright 2014 Google Inc. All Rights Reserved. 
  3  # 
  4  # This program is free software; you can redistribute it and/or modify 
  5  # it under the terms of the GNU General Public License as published by 
  6  # the Free Software Foundation; either version 2 of the License, or (at 
  7  # your option) any later version. 
  8  # 
  9  # This program is distributed in the hope that it will be useful, but 
 10  # WITHOUT ANY WARRANTY; without even the implied warranty of 
 11  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 
 12  # General Public License for more details. 
 13  # 
 14  # You should have received a copy of the GNU General Public License 
 15  # along with this program; if not, write to the Free Software 
 16  # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 
 17  # 
 18   
 19  """Autodetect struct layout of various Win32k GUI structs. 
 20   
 21  Many win32k structs are undocumented (i.e. are not present in pdb 
 22  symbols). These structures do change a lot between versions of windows. This 
 23  module autodetects the struct layout using various heuristics. 
 24  """ 
 25  import re 
 26   
 27  from rekall.plugins.windows import common 
 28   
 29   
30 -class Win32kAutodetect(common.WindowsCommandPlugin):
31 """Automatically detect win32k struct layout.""" 32 33 name = "win32k_autodetect" 34 35 table_header = [ 36 dict(name="divider", type="Divider"), 37 dict(name="field", width=20), 38 dict(name="offset", style="address"), 39 dict(name="definition") 40 ] 41 42
43 - def collect(self):
44 win32k_module = self.session.address_resolver.GetModuleByName( 45 "win32k") 46 win32k_profile = win32k_module.profile 47 48 overlay = self.GetWin32kOverlay(win32k_profile) 49 50 for struct, definition in overlay.items(): 51 yield dict(divider="Struct %s" % struct) 52 53 for field, (offset, field_def) in sorted(definition[1].items(), 54 key=lambda x: x[1]): 55 yield dict(field=field, offset=offset, 56 definition=str(field_def))
57
58 - def GetWin32kOverlay(self, win32k_profile):
59 # Make a temporary profile to work with. 60 self.temp_profile = win32k_profile 61 self.analyze_struct = self.session.plugins.analyze_struct(0) 62 63 # Start off with an empty overlay. 64 overlay = dict(tagDESKTOP=[None, {}], 65 tagWINDOWSTATION=[None, {}], 66 tagTHREADINFO=[None, {}], 67 ) 68 69 with self.session.plugins.cc() as cc: 70 for task in self.session.plugins.pslist().filter_processes(): 71 cc.SwitchProcessContext(task) 72 73 # Find a process context which makes the symbol valid. 74 if not self.wndstation(): 75 continue 76 77 try: 78 self.Get_tagWINDOWSTATION_overlay(overlay) 79 self.Get_tagDESKTOP_overlay(overlay) 80 self.Get_tagTHREADINFO_overlay(overlay) 81 82 return overlay 83 except RuntimeError: 84 continue 85 86 return overlay
87
88 - def wndstation(self):
89 return self.temp_profile.get_constant_object( 90 "grpWinStaList", 91 target="Pointer", 92 target_args=dict( 93 target="tagWINDOWSTATION") 94 ).deref()
95
96 - def _Match(self, regex, info):
97 for item in info: 98 if re.match(regex, item): 99 return True
100
101 - def _AddField(self, regex, info, field_name, fields, description):
102 if field_name not in fields and self._Match(regex, info): 103 fields[field_name] = description 104 self.session.logging.debug( 105 "Detected field %s: %s @ %#x", field_name, info, description[0]) 106 return True
107
108 - def Get_tagWINDOWSTATION_overlay(self, overlay):
109 """Derive the tagWINDOWSTATION overlay.""" 110 fields = {} 111 offset = self.wndstation() 112 113 required_fields = set([ 114 "rpwinstaNext", "rpdeskList", "pGlobalAtomTable"]) 115 116 stations = set() 117 118 while not offset == None and offset not in stations: 119 stations.add(offset) 120 121 self.session.logging.debug("Checking tagWINDOWSTATION at %#x", 122 offset) 123 for o, info in self.analyze_struct.GuessMembers(offset, size=0x200): 124 if self._AddField( 125 "Tag:Win", info, "rpwinstaNext", fields, 126 [o, ["Pointer", dict( 127 target="tagWINDOWSTATION" 128 )]]): 129 continue 130 131 elif self._AddField( 132 "Tag:Des", info, "rpdeskList", fields, 133 [o, ["Pointer", dict( 134 target="tagDESKTOP" 135 )]]): 136 continue 137 138 elif self._AddField( 139 "Tag:AtmT", info, "pGlobalAtomTable", fields, 140 [o, ["Pointer", dict( 141 target="_RTL_ATOM_TABLE" 142 )]]): 143 continue 144 145 elif self._AddField( 146 "Const:win32k!gTerm", info, "pTerm", fields, 147 [o, ["Pointer", dict( 148 target="tagTERMINAL" 149 )]]): 150 continue 151 152 else: 153 self.session.logging.debug( 154 "Unhandled field %#x, %s" % (o, info)) 155 continue 156 157 # Add the derived overlay to the profile so we can walk the list 158 # of window stations. 159 self.temp_profile.add_overlay(overlay) 160 161 offset = self.temp_profile.tagWINDOWSTATION(offset).rpwinstaNext 162 163 # We worked out all the fields, return the overlay. 164 if required_fields.issubset(fields): 165 overlay["tagWINDOWSTATION"][1].update(fields) 166 return overlay 167 168 self.session.logging.debug( 169 "tagWINDOWSTATION: Missing required fields %s", 170 required_fields.difference(fields)) 171 172 raise RuntimeError("Unable to guess tagWINDOWSTATION")
173
174 - def Get_tagDESKTOP_overlay(self, overlay):
175 fields = {} 176 required_fields = set([ 177 "rpdeskNext", "rpwinstaParent", "hsectionDesktop"]) 178 179 # Iterate over all tagDESKTOP objects. 180 desktops = set() 181 182 offset = self.wndstation().rpdeskList.v() 183 184 while not offset == None and offset not in desktops: 185 self.session.logging.debug("Checking tagDESKTOP at %#x", offset) 186 desktops.add(offset) 187 188 for o, info in self.analyze_struct.GuessMembers( 189 offset, search=0x400): 190 191 if self._AddField("Tag:Des", info, "rpdeskNext", fields, 192 [o, ["Pointer", dict( 193 target="tagDESKTOP" 194 )]]): 195 continue 196 197 elif self._AddField("Tag:Win", info, "rpwinstaParent", fields, 198 [o, ["Pointer", dict( 199 target="tagWINDOWSTATION" 200 )]]): 201 continue 202 203 elif self._AddField("Tag:Sec", info, "hsectionDesktop", fields, 204 [o, ["Pointer", dict( 205 target="_SECTION_OBJECT" 206 )]]): 207 continue 208 209 # The PtiList is a _LIST_ENTRY to a tagTHREADINFO (Usti tag). 210 elif ("_LIST_ENTRY" in info and 211 self._AddField("Tag:Usti", info, "PtiList", fields, 212 [o, ["_LIST_ENTRY"]])): 213 continue 214 215 # On WinXP a tagTHREADINFO allocation contains ProcessBilled. 216 elif ("_LIST_ENTRY" in info and not self._Match("Tag:", info) 217 and self._AddField( 218 "ProcessBilled:", info, "PtiList", fields, 219 [o, ["_LIST_ENTRY"]])): 220 continue 221 222 else: 223 self.session.logging.debug( 224 "Unhandled field %#x %s" % (o, info)) 225 continue 226 227 # Add the derived overlay to the profile so we can walk the list 228 # of window stations. 229 self.temp_profile.add_overlay(overlay) 230 231 offset = self.temp_profile.tagDESKTOP(offset).rpdeskNext 232 233 # We worked out all the fields, return the overlay. 234 if required_fields.issubset(fields): 235 overlay["tagDESKTOP"][1].update(fields) 236 return overlay 237 238 self.session.logging.debug( 239 "tagDESKTOP: Missing required fields %s", 240 required_fields.difference(fields)) 241 242 raise RuntimeError("Unable to guess tagDESKTOP")
243
244 - def _Check_tagPROCESSINFO(self, offset):
245 """Checks if a pointer points to tagPROCESSINFO.""" 246 pointer = self.profile.Pointer(offset) 247 pool = self.analyze_struct.SearchForPoolHeader(pointer.v()) 248 if pool.Tag == "Uspi": 249 return True 250 251 # Its definitely not a tagPROCESSINFO if it is a tagTHREADINFO. 252 if pool.Tag in ["Usti"]: 253 return False 254 255 # In windows XP tagPROCESSINFO allocations contain the _EPROCESS 256 # address in the ProcessBilled field of the allocation. 257 if pool.m("ProcessBilled").Peb: 258 return True 259 260 return False
261
262 - def _AnalyzeTagTHREADINFO(self, offset, fields):
263 self.session.logging.debug("Checking tagTHREADINFO at %#x", offset) 264 for o, info in self.analyze_struct.GuessMembers( 265 offset, size=0x400, search=0x600): 266 267 if self._AddField("Tag:Thr", info, "pEThread", fields, 268 [o, ["Pointer", dict( 269 target="_ETHREAD" 270 )]]): 271 continue 272 273 elif self._AddField("Tag:Usqu", info, "pq", fields, 274 [o, ["Pointer", dict( 275 target="tagQ" 276 )]]): 277 continue 278 279 elif self._AddField("Tag:Uskb", info, "spklActive", fields, 280 [o, ["Pointer", dict( 281 target="tagKL" 282 )]]): 283 continue 284 285 elif self._AddField("Tag:Des", info, "rpdesk", fields, 286 [o, ["Pointer", dict( 287 target="tagDESKTOP" 288 )]]): 289 continue 290 291 elif ("_LIST_ENTRY" in info and 292 self._AddField("Tag:Usti", info, "GdiTmpTgoList", fields, 293 [o, ["_LIST_ENTRY"]])): 294 continue 295 296 elif (self._Check_tagPROCESSINFO(offset + o) and 297 self._AddField(".", info, "ppi", fields, 298 [o, ["Pointer", dict( 299 target="tagPROCESSINFO" 300 )]])): 301 continue 302 303 else: 304 self.session.logging.debug("Unhandled field %#x %s" % (o, info)) 305 continue
306
307 - def Get_tagTHREADINFO_overlay(self, overlay):
308 fields = {} 309 required_fields = set([ 310 "pEThread", "pq", "spklActive", "rpdesk", "PtiLink", "ppi" 311 ]) 312 313 # Iterate over all tagTHREADINFO objects. 314 thread_infos = set() 315 for wndstation in self.wndstation().rpwinstaNext.walk_list( 316 "rpwinstaNext"): 317 for desktop in wndstation.rpdeskList.walk_list("rpdeskNext"): 318 thread_info_pool = self.analyze_struct.SearchForPoolHeader( 319 desktop.PtiList.Flink.v(), search=0x600) 320 321 if thread_info_pool and thread_info_pool not in thread_infos: 322 thread_infos.add(thread_info_pool) 323 324 # We can already determine the tagTHREADINFO's PtiLink: 325 PtiLink_offset = (desktop.PtiList.Flink.v() - 326 thread_info_pool.obj_end) 327 fields["PtiLink"] = [PtiLink_offset, ["_LIST_ENTRY"]] 328 329 self._AnalyzeTagTHREADINFO(thread_info_pool.obj_end, fields) 330 self.temp_profile.add_overlay(overlay) 331 332 # We worked out all the fields, return the overlay. 333 if required_fields.issubset(fields): 334 overlay["tagTHREADINFO"][1].update(fields) 335 return overlay 336 337 self.session.logging.debug( 338 "tagTHREADINFO: Missing required fields %s", 339 required_fields.difference(fields)) 340 341 raise RuntimeError("Unable to guess tagTHREADINFO")
342