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

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

  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  from rekall import obj 
 21  from rekall import testlib 
 22  from rekall.plugins import core 
 23  from rekall.plugins.windows import common 
 24   
 25  # pylint: disable=protected-access 
 26   
 27   
28 -class Malfind(core.DirectoryDumperMixin, common.WinProcessFilter):
29 "Find hidden and injected code" 30 31 __name = "malfind" 32 33 dump_dir_optional = True 34 default_dump_dir = None 35
36 - def _is_vad_empty(self, vad, address_space):
37 """ 38 Check if a VAD region is either entirely unavailable 39 due to paging, entirely consiting of zeros, or a 40 combination of the two. This helps ignore false positives 41 whose VAD flags match task._injection_filter requirements 42 but there's no data and thus not worth reporting it. 43 44 @param vad: an MMVAD object in kernel AS 45 @param address_space: the process address space 46 """ 47 48 PAGE_SIZE = 0x1000 49 all_zero_page = "\x00" * PAGE_SIZE 50 51 offset = 0 52 while offset < vad.Length: 53 next_addr = vad.Start + offset 54 if (address_space.is_valid_address(next_addr) and 55 address_space.read(next_addr, PAGE_SIZE) != all_zero_page): 56 return False 57 offset += PAGE_SIZE 58 59 return True
60
61 - def _injection_filter(self, vad, task_as):
62 """Detects injected vad regions. 63 64 This looks for private allocations that are committed, 65 memory-resident, non-empty (not all zeros) and with an 66 original protection that includes write and execute. 67 68 It is important to note that protections are applied at 69 the allocation granularity (page level). Thus the original 70 protection might not be the current protection, and it 71 also might not apply to all pages in the VAD range. 72 73 @param vad: an MMVAD object. 74 75 @returns: True if the MMVAD looks like it might 76 contain injected code. 77 """ 78 # Try to find injections. 79 protect = str(vad.u.VadFlags.ProtectionEnum) 80 write_exec = "EXECUTE" in protect and "WRITE" in protect 81 82 # The Write/Execute check applies to everything 83 if not write_exec: 84 return False 85 86 # This is a typical VirtualAlloc'd injection 87 if ((vad.u.VadFlags.PrivateMemory == 1 and 88 vad.Tag == "VadS") or 89 90 # This is a stuxnet-style injection 91 (vad.u.VadFlags.PrivateMemory == 0 and 92 protect != "EXECUTE_WRITECOPY")): 93 return not self._is_vad_empty(vad, task_as) 94 95 return False
96
97 - def render(self, renderer):
98 cc = self.session.plugins.cc() 99 for task in self.filter_processes(): 100 task_as = task.get_process_address_space() 101 if not task_as: 102 continue 103 104 with cc: 105 cc.SwitchProcessContext(task) 106 for vad in task.RealVadRoot.traverse(): 107 self.session.report_progress("Checking %r of pid %s", 108 vad, task.UniqueProcessId) 109 110 if self._injection_filter(vad, task_as): 111 renderer.section() 112 renderer.format( 113 "Process: {0} Pid: {1} Address: {2:#x}\n", 114 task.ImageFileName, task.UniqueProcessId, vad.Start) 115 116 renderer.format("Vad Tag: {0} Protection: {1}\n", 117 vad.Tag, vad.u.VadFlags.ProtectionEnum) 118 119 renderer.format("Flags: {0}\n", vad.u.VadFlags) 120 renderer.format("\n") 121 122 dumper = self.session.plugins.dump( 123 offset=vad.Start, rows=4) 124 dumper.render(renderer, suppress_headers=True) 125 126 renderer.format("\n") 127 128 disassembler = self.session.plugins.dis( 129 offset=vad.Start, length=0x40) 130 disassembler.render(renderer, suppress_headers=True) 131 132 if self.dump_dir: 133 filename = "{0}.{1:d}.{2:08x}-{3:08x}.dmp".format( 134 task.ImageFileName, task.pid, vad.Start, 135 vad.End) 136 137 with renderer.open(directory=self.dump_dir, 138 filename=filename, 139 mode='wb') as fd: 140 self.session.report_progress( 141 "Dumping %s" % filename) 142 143 self.CopyToFile(task_as, vad.Start, vad.End, fd)
144 145
146 -class LdrModules(common.WinProcessFilter):
147 "Detect unlinked DLLs" 148 149 __name = "ldrmodules" 150 151 table_header = [ 152 dict(name="divider", type="Divider"), 153 dict(name="_EPROCESS", hidden=True), 154 dict(name="base", style="address"), 155 dict(name="in_load", width=5), 156 dict(name="in_load_path", width=80, hidden=True), 157 dict(name="in_init", width=5), 158 dict(name="in_init_path", width=80, hidden=True), 159 dict(name="in_mem", width=5), 160 dict(name="in_mem_path", width=80, hidden=True), 161 dict(name="mapped") 162 ] 163
164 - def column_types(self):
165 return dict( 166 _EPROCESS=self.session.profile._EPROCESS(), 167 base_address=0, 168 in_load=False, 169 in_load_path=self.session.profile._UNICODE_STRING(), 170 in_init=False, 171 in_init_path=self.session.profile._UNICODE_STRING(), 172 in_mem=False, 173 in_mem_path=self.session.profile._UNICODE_STRING(), 174 mapped_filename=self.session.profile._UNICODE_STRING(), 175 )
176
177 - def list_mapped_files(self, task):
178 """Iterates over all vads and returns executable regions. 179 180 Yields: 181 vad objects which are both executable and have a file name. 182 """ 183 self.session.report_progress("Inspecting Pid %s", 184 task.UniqueProcessId) 185 186 for vad in task.RealVadRoot.traverse(): 187 try: 188 file_obj = vad.ControlArea.FilePointer 189 protect = str(vad.u.VadFlags.ProtectionEnum) 190 if "EXECUTE" in protect and "WRITE" in protect: 191 yield vad, file_obj.FileName 192 except AttributeError: 193 pass
194 195
196 - def collect(self):
197 for task in self.filter_processes(): 198 # Build a dictionary for all three PEB lists where the 199 # keys are base address and module objects are the values 200 inloadorder = dict((mod.DllBase.v(), mod) 201 for mod in task.get_load_modules()) 202 203 ininitorder = dict((mod.DllBase.v(), mod) 204 for mod in task.get_init_modules()) 205 206 inmemorder = dict((mod.DllBase.v(), mod) 207 for mod in task.get_mem_modules()) 208 209 # Build a similar dictionary for the mapped files 210 mapped_files = dict((vad.Start, name) 211 for vad, name in self.list_mapped_files(task)) 212 213 yield dict(divider=task) 214 215 # For each base address with a mapped file, print info on 216 # the other PEB lists to spot discrepancies. 217 for base in mapped_files.keys(): 218 yield dict(_EPROCESS=task, 219 base=base, 220 in_load=base in inloadorder, 221 in_load_path=inloadorder.get( 222 base, obj.NoneObject()).FullDllName, 223 in_init=base in ininitorder, 224 in_init_path=ininitorder.get( 225 base, obj.NoneObject()).FullDllName, 226 in_mem=base in inmemorder, 227 in_mem_path=inmemorder.get( 228 base, obj.NoneObject()).FullDllName, 229 mapped=mapped_files[base])
230 231
232 -class TestLdrModules(testlib.SimpleTestCase):
233 PARAMETERS = dict( 234 commandline="ldrmodules %(pids)s" 235 )
236