Package rekall :: Package plugins :: Package windows :: Package malware :: Module svcscan
[frames] | no frames]

Source Code for Module rekall.plugins.windows.malware.svcscan

  1  # Rekall Memory Forensics 
  2  # Copyright (c) 2010, 2011, 2012 Michael Ligh <michael.ligh@mnin.org> 
  3  # Copyright 2013 Google Inc. All Rights Reserved. 
  4  # 
  5  # This program is free software; you can redistribute it and/or modify 
  6  # it under the terms of the GNU General Public License as published by 
  7  # the Free Software Foundation; either version 2 of the License, or (at 
  8  # your option) any later version. 
  9  # 
 10  # This program is distributed in the hope that it will be useful, but 
 11  # WITHOUT ANY WARRANTY; without even the implied warranty of 
 12  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 
 13  # General Public License for more details. 
 14  # 
 15  # You should have received a copy of the GNU General Public License 
 16  # along with this program; if not, write to the Free Software 
 17  # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 
 18  # 
 19   
 20  # pylint: disable=protected-access 
 21   
 22  from rekall import plugin 
 23  from rekall import obj 
 24  from rekall.plugins.windows import common 
 25  from rekall.plugins.windows import vadinfo 
 26  from rekall_lib import utils 
 27   
 28   
 29  SERVICE_TYPE_FLAGS = { 
 30      'SERVICE_KERNEL_DRIVER': 0, 
 31      'SERVICE_FILE_SYSTEM_DRIVER': 1, 
 32      'SERVICE_WIN32_OWN_PROCESS': 4, 
 33      'SERVICE_WIN32_SHARE_PROCESS': 5, 
 34      'SERVICE_INTERACTIVE_PROCESS': 8 
 35  } 
 36   
 37  SERVICE_STATE_ENUM = { 
 38      1: 'SERVICE_STOPPED', 
 39      2: 'SERVICE_START_PENDING', 
 40      3: 'SERVICE_STOP_PENDING', 
 41      4: 'SERVICE_RUNNING', 
 42      5: 'SERVICE_CONTINUE_PENDING', 
 43      6: 'SERVICE_PAUSE_PENDING', 
 44      7: 'SERVICE_PAUSED' 
 45  } 
 46   
 47  svcscan_base_x86 = { 
 48      '_SERVICE_HEADER': [None, { 
 49          'Tag': [0x0, ['array', 4, ['unsigned char']]], 
 50          'ServiceRecord': [0xC, ['pointer', ['_SERVICE_RECORD']]], 
 51          }], 
 52   
 53      '_SERVICE_RECORD': [None, { 
 54          'NextService': [0x0, ['_SERVICE_HEADER']], 
 55          'ServiceName': [0x8, ['pointer', ['UnicodeString', dict(length=512)]]], 
 56          'DisplayName': [0xc, ['pointer', ['UnicodeString', dict(length=512)]]], 
 57          'Order': [0x10, ['unsigned int']], 
 58          'Tag': [0x18, ['array', 4, ['unsigned char']]], 
 59          'DriverName': [0x24, ['pointer', ['UnicodeString', dict( 
 60              length=256)]]], 
 61          'ServiceProcess': [0x24, ['pointer', ['_SERVICE_PROCESS']]], 
 62          'Type': [0x28, ['Flags', {'bitmap': SERVICE_TYPE_FLAGS}]], 
 63          'State': [0x2c, ['Enumeration', dict(target='long', 
 64                                               choices=SERVICE_STATE_ENUM)]], 
 65          }], 
 66   
 67      '_SERVICE_PROCESS': [None, { 
 68          'BinaryPath': [0x8, ['pointer', ['UnicodeString', dict( 
 69              encoding='utf16', length=256)]]], 
 70          'ProcessId': [0xc, ['unsigned int']], 
 71          }], 
 72  } 
 73   
 74  svcscan_base_x64 = { 
 75      '_SERVICE_HEADER': [None, { 
 76          'Tag': [0x0, ['Array', dict( 
 77              count=4, 
 78              target='unsigned char' 
 79              )]], 
 80          'ServiceRecord': [0x10, ['Pointer', dict( 
 81              target='_SERVICE_RECORD' 
 82              )]], 
 83          }], 
 84   
 85      '_SERVICE_RECORD': [None, { 
 86          'NextService': [0x0, ['Pointer', dict( 
 87              target="_SERVICE_RECORD" 
 88              )]], 
 89          'ServiceName': [0x8, ['pointer', ['UnicodeString', dict( 
 90              encoding='utf16', length=512)]]], 
 91   
 92          'DisplayName': [0x10, ['Pointer', dict( 
 93              target='UnicodeString', 
 94              target_args=dict(length=512) 
 95              )]], 
 96          'Order': [0x18, ['unsigned int']], 
 97          'Tag' : [0x20, ['Array', dict( 
 98              count=4, 
 99              target='unsigned char' 
100              )]], 
101          'DriverName': [0x30, ['Pointer', dict( 
102              target='UnicodeString', 
103              target_args=dict( 
104                  length=256 
105                  ) 
106              )]], 
107          'ServiceProcess': [0x30, ['Pointer', dict( 
108              target='_SERVICE_PROCESS' 
109              )]], 
110          'Type': [0x38, ['Flags', {'bitmap': SERVICE_TYPE_FLAGS}]], 
111          'State': [0x3C, ['Enumeration', dict( 
112              target='long', choices=SERVICE_STATE_ENUM)]], 
113          }], 
114   
115      '_SERVICE_PROCESS': [None, { 
116          'BinaryPath': [0x10, ['Pointer', dict( 
117              target='UnicodeString', 
118              target_args=dict( 
119                  length=256 
120                  ) 
121              )]], 
122          'ProcessId': [0x18, ['unsigned int']], 
123          }], 
124  } 
125 126 127 -class _SERVICE_RECORD_LEGACY(obj.Struct):
128 "Service records for XP/2003 x86 and x64" 129 130 @utils.safe_property
131 - def Binary(self):
132 "Return the binary path for a service" 133 134 # No path in memory for services that aren't running 135 # (if needed, query the registry key) 136 if str(self.State) != 'SERVICE_RUNNING': 137 return obj.NoneObject("No path, service isn't running") 138 139 # Depending on whether the service is for a process 140 # or kernel driver, the binary path is stored differently 141 if 'PROCESS' in str(self.Type): 142 return self.ServiceProcess.BinaryPath.dereference() 143 else: 144 return self.DriverName.dereference()
145 146 @utils.safe_property
147 - def Pid(self):
148 "Return the process ID for a service" 149 150 if str(self.State) == 'SERVICE_RUNNING': 151 if 'PROCESS' in str(self.Type): 152 return self.ServiceProcess.ProcessId 153 154 return obj.NoneObject("Cannot get process ID")
155
156 - def is_valid(self):
157 "Check some fields for validity" 158 return (super(_SERVICE_RECORD_LEGACY, self).is_valid() and 159 self.Order > 0 and self.Order < 0xFFFF)
160
161 162 -class _SERVICE_RECORD_RECENT(_SERVICE_RECORD_LEGACY):
163 "Service records for 2008, Vista, 7 x86 and x64"
164
165 166 -class _SERVICE_HEADER(obj.Struct):
167 "Service headers for 2008, Vista, 7 x86 and x64" 168
169 - def is_valid(self):
170 "Check some fields for validity" 171 return (super(_SERVICE_HEADER, self).is_valid() and 172 self.ServiceRecord.is_valid())
173 174 175 _SERVICE_RECORD_VISTA_X86 = { 176 '_SERVICE_RECORD': [None, { 177 'NextService': [0x0, ['pointer', ['_SERVICE_RECORD']]], 178 'ServiceName': [0x4, ['pointer', ['UnicodeString', dict(length=512)]]], 179 'DisplayName': [0x8, ['pointer', ['UnicodeString', dict(length=512)]]], 180 'Order': [0xC, ['unsigned int']], 181 'ServiceProcess': [0x1C, ['pointer', ['_SERVICE_PROCESS']]], 182 'DriverName': [0x1C, ['pointer', ['UnicodeString', dict( 183 length=256)]]], 184 'Type' : [0x20, ['Flags', {'bitmap': SERVICE_TYPE_FLAGS}]], 185 'State': [0x24, ['Enumeration', dict( 186 target='unsigned int', choices=SERVICE_STATE_ENUM)]], 187 }], 188 } 189 190 191 _SERVICE_RECORD_VISTA_X64 = { 192 '_SERVICE_RECORD': [None, { 193 'NextService': [0x00, ['Pointer', dict( 194 target='_SERVICE_RECORD' 195 )]], 196 197 'ServiceName': [0x08, ['pointer', ['UnicodeString', dict( 198 length=512 199 )]]], 200 'DisplayName': [0x10, ['pointer', ['UnicodeString', dict( 201 length=512 202 )]]], 203 'Order': [0x18, ['unsigned int']], 204 'ServiceProcess': [0x28, ['pointer', ['_SERVICE_PROCESS']]], 205 'DriverName': [0x28, ['Pointer', dict( 206 target='UnicodeString', 207 target_args=dict( 208 length=256, 209 ) 210 )]], 211 212 'Type' : [0x30, ['Flags', {'bitmap': SERVICE_TYPE_FLAGS}]], 213 'State': [0x34, ['Enumeration', dict( 214 target='unsigned int', 215 choices=SERVICE_STATE_ENUM 216 )]], 217 }], 218 } 219 220 221 _SERVICE_RECORD_WIN81_X64 = { 222 "_SERVICE_RECORD": [None, { 223 "Tag": [0, ["String", dict(length=4)]], # Signature sErv 224 'NextService': [0x8, ['Pointer', dict( 225 target='_SERVICE_RECORD' 226 )]], 227 228 'ServiceName': [0x10, ['pointer', ['UnicodeString', dict( 229 length=512 230 )]]], 231 'DisplayName': [0x18, ['pointer', ['UnicodeString', dict( 232 length=512 233 )]]], 234 'Order': [0x20, ['unsigned int']], 235 'ServiceProcess': [0x38, ['pointer', ['_SERVICE_PROCESS']]], 236 'DriverName': [0x38, ['Pointer', dict( 237 target='UnicodeString', 238 target_args=dict( 239 length=256, 240 ) 241 )]], 242 243 'Type' : [0x40, ['Flags', {'bitmap': SERVICE_TYPE_FLAGS}]], 244 'State': [0x44, ['Enumeration', dict( 245 target='unsigned int', 246 choices=SERVICE_STATE_ENUM 247 )]], 248 }], 249 250 '_SERVICE_PROCESS': [None, { 251 'Tag': [0, ["String", dict(length=4)]], # Sc16 252 'BinaryPath': [0x18, ['Pointer', dict( 253 target='UnicodeString', 254 target_args=dict( 255 length=256 256 ) 257 )]], 258 'ProcessId': [0x20, ['unsigned int']], 259 }], 260 }
261 262 263 -class ServiceModification(obj.ProfileModification):
264 """A modification for the service control manager.""" 265 266 @classmethod
267 - def modify(cls, profile):
268 if profile.metadata("arch") == "I386": 269 profile.add_overlay(svcscan_base_x86) 270 else: 271 # 32bit Vista profiles 272 profile.add_overlay(svcscan_base_x64) 273 274 # Windows XP, 2003 275 version = profile.metadata("version") 276 if version < 6.0: 277 profile.add_classes({ 278 '_SERVICE_RECORD': _SERVICE_RECORD_LEGACY, 279 '_SERVICE_HEADER': _SERVICE_HEADER, 280 }) 281 profile.add_constants(dict(ServiceTag="sErv")) 282 283 # Vista 2008 and windows 7 284 elif 6.0 <= version <= 6.2: 285 profile.add_classes({ 286 '_SERVICE_RECORD': _SERVICE_RECORD_RECENT, 287 '_SERVICE_HEADER': _SERVICE_HEADER, 288 }) 289 profile.add_constants(dict(ServiceTag="serH")) 290 291 if profile.metadata("arch") == "I386": 292 profile.add_overlay(_SERVICE_RECORD_VISTA_X86) 293 else: 294 profile.add_overlay(_SERVICE_RECORD_VISTA_X64) 295 296 # Windows 8.1 and Windows 10 297 elif 6.2 <= version: 298 profile.add_classes({ 299 '_SERVICE_RECORD': _SERVICE_RECORD_RECENT, 300 '_SERVICE_HEADER': _SERVICE_HEADER, 301 }) 302 profile.add_constants(dict(ServiceTag="serH")) 303 304 if profile.metadata("arch") == "I386": 305 profile.add_overlay(_SERVICE_RECORD_VISTA_X86) 306 else: 307 profile.add_overlay(_SERVICE_RECORD_WIN81_X64) 308 309 else: 310 raise RuntimeError( 311 "Unsupported windows version. Please file a bug.")
312
313 314 315 -class SvcRecordScanner(vadinfo.VadScanner):
316 """A scanner for the service tags.""" 317
318 - def __init__(self, **kwargs):
319 super(SvcRecordScanner, self).__init__(**kwargs) 320 self.checks = [ 321 ('StringCheck', dict( 322 needle=self.profile.get_constant("ServiceTag"))), 323 ] 324 self.tag_offset = self.profile.get_obj_offset('_SERVICE_RECORD', 'Tag')
325
326 - def scan(self, **kwargs):
327 for hit in super(SvcRecordScanner, self).scan(**kwargs): 328 svc_record = self.profile._SERVICE_RECORD( 329 vm=self.address_space, offset=hit - self.tag_offset) 330 331 if svc_record.is_valid(): 332 yield svc_record
333
334 335 -class SvcHeaderScanner(vadinfo.VadScanner):
336 """A scanner for the service tags.""" 337
338 - def __init__(self, **kwargs):
339 super(SvcHeaderScanner, self).__init__(**kwargs) 340 self.checks = [ 341 ('StringCheck', dict( 342 needle=self.profile.get_constant("ServiceTag"))), 343 ] 344 345 # On systems more recent than XP/2003, the serH marker doesn't 346 # find *all* services, but the ones it does find have linked 347 # lists to the others. We use this variable to track which 348 # ones we've seen so as to not yield duplicates. 349 self.records = set()
350
351 - def scan(self, **kwargs):
352 for hit in super(SvcHeaderScanner, self).scan(**kwargs): 353 svc_header = self.profile._SERVICE_HEADER( 354 vm=self.address_space, offset=hit) 355 356 if svc_header.is_valid(): 357 for record in svc_header.ServiceRecord.walk_list("NextService"): 358 if record.is_valid() and record not in self.records: 359 self.records.add(record) 360 361 yield record
362
363 364 -class SvcScan(plugin.KernelASMixin, common.AbstractWindowsCommandPlugin):
365 "Scan for Windows services" 366 367 __name = "svcscan" 368
369 - def __init__(self, scan_in_kernel_address_space=False, **kwargs):
370 """Scan for callbacks. 371 372 Args: 373 scan_in_kernel_address_space: If False we will use the physical 374 address space for scanning, while if true we scan in the kernel 375 address space. 376 """ 377 super(SvcScan, self).__init__(**kwargs) 378 self.scan_in_kernel_address_space = scan_in_kernel_address_space 379 380 # Update the profile. 381 self.profile = ServiceModification(self.profile)
382
383 - def calculate(self):
384 # Get the version we're analyzing 385 version = self.profile.metadatas('major', 'minor') 386 387 pslist = self.session.plugins.pslist(proc_regex="services.exe") 388 for task in pslist.filter_processes(): 389 # Process AS must be valid 390 process_space = task.get_process_address_space() 391 if process_space == None: 392 continue 393 394 # XP/2003 use the _SERVICE_RECORD object. 395 if version <= "5.2": 396 scanner = SvcRecordScanner( 397 task=task, process_profile=self.profile, 398 session=self.session) 399 else: 400 # Windows Vista, 2008, and 7 use the _SERVICE_HEADER 401 scanner = SvcHeaderScanner( 402 task=task, process_profile=self.profile, 403 session=self.session) 404 405 # Find all instances of the record tag 406 for record in scanner.scan(): 407 yield record
408
409 - def render(self, renderer):
410 renderer.table_header([ 411 ("Offset", "offset", "[addrpad]"), 412 ("Order", "order", "5"), 413 ("PID", "pid", "4"), 414 ("Service Name", "service", "30"), 415 ("Display Name", "display_name", "40"), 416 ("Service Type", "type", "30"), 417 ("Service State", "state", "15"), 418 ("Binary Path", "binary_path", "")]) 419 420 for rec in self.calculate(): 421 renderer.table_row( 422 rec, 423 rec.Order, 424 rec.Pid, 425 rec.ServiceName.deref(), 426 rec.DisplayName.deref(), 427 rec.Type, 428 rec.State, 429 rec.Binary)
430