Package rekall :: Package plugins :: Package windows :: Module kdbgscan
[frames] | no frames]

Source Code for Module rekall.plugins.windows.kdbgscan

  1  # Rekall Memory Forensics 
  2  # 
  3  # Copyright 2013 Google Inc. All Rights Reserved. 
  4  # 
  5  # Authors: 
  6  # Michael Cohen <scudette@gmail.com> 
  7  # 
  8  # This program is free software; you can redistribute it and/or modify 
  9  # it under the terms of the GNU General Public License as published by 
 10  # the Free Software Foundation; either version 2 of the License, or (at 
 11  # your option) any later version. 
 12  # 
 13  # This program is distributed in the hope that it will be useful, but 
 14  # WITHOUT ANY WARRANTY; without even the implied warranty of 
 15  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 
 16  # General Public License for more details. 
 17  # 
 18  # You should have received a copy of the GNU General Public License 
 19  # along with this program; if not, write to the Free Software 
 20  # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 
 21  # 
 22  # pylint: disable=protected-access 
 23   
 24  from rekall import obj 
 25  from rekall import scan 
 26  from rekall import plugin 
 27   
 28  from rekall.plugins.windows import common 
 29  from rekall_lib import utils 
 30   
 31   
32 -class KDBGScanner(scan.BaseScanner):
33 """Scans for _KDDEBUGGER_DATA64 structures. 34 35 Note that this does not rely on signatures, as validity of hits is 36 calculated through list reflection. 37 """ 38 checks = [("StringCheck", dict(needle="KDBG"))] 39
40 - def scan(self, offset=0, maxlen=None):
41 maxlen = maxlen or self.profile.get_constant("MaxPointer") 42 43 # How far into the struct the OwnerTag is. 44 owner_tag_offset = self.profile.get_obj_offset( 45 "_DBGKD_DEBUG_DATA_HEADER64", "OwnerTag") 46 47 # Depending on the memory model this behaves slightly differently. 48 architecture = self.profile.metadata("arch", "I386") 49 50 # This basically iterates over all hits on the string "KDBG". 51 for offset in super(KDBGScanner, self).scan(offset, maxlen): 52 # For each hit we overlay a _DBGKD_DEBUG_DATA_HEADER64 on it and 53 # reflect through the "List" member. 54 result = self.profile.Object("_KDDEBUGGER_DATA64", 55 offset=offset - owner_tag_offset, 56 vm=self.address_space) 57 58 # We verify this hit by reflecting through its header list. 59 list_entry = result.Header.List 60 61 # On 32 bit systems the Header.List member seems to actually be a 62 # LIST_ENTRY32 instead of a LIST_ENTRY64, but it is still padded to 63 # take the same space: 64 if architecture == "I386": 65 list_entry = list_entry.cast("LIST_ENTRY32") 66 67 if list_entry.reflect(): 68 yield result 69 70 elif (list_entry.Flink == list_entry.Blink and 71 not list_entry.Flink.dereference()): 72 self.session.logging.debug( 73 "KDBG list_head is not mapped, assuming its valid.") 74 75 yield result
76 77
78 -class KDBGScan(plugin.KernelASMixin, common.AbstractWindowsCommandPlugin):
79 """Scan for possible _KDDEBUGGER_DATA64 structures. 80 81 The scanner is detailed here: 82 http://moyix.blogspot.com/2008/04/finding-kernel-global-variables-in.html 83 84 The relevant structures are detailed here: 85 http://doxygen.reactos.org/d3/ddf/include_2psdk_2wdbgexts_8h_source.html 86 87 We can see that _KDDEBUGGER_DATA64.Header is: 88 89 typedef struct _DBGKD_DEBUG_DATA_HEADER64 { 90 LIST_ENTRY64 List; 91 ULONG OwnerTag; 92 ULONG Size; 93 } 94 95 We essentially search for an owner tag of "KDBG", then overlay the 96 _KDDEBUGGER_DATA64 struct on it. We test for validity by reflecting 97 through the Header.List member. 98 """ 99 100 __name = "kdbgscan" 101 102 __args = [ 103 dict(name="full_scan", type="Boolean", 104 help="Scan the full address space.") 105 ] 106
107 - def hits(self):
108 if self.plugin_args.full_scan: 109 start, end = 0, 2**64 110 else: 111 # The kernel image is always loaded in the same range called the 112 # "Initial Loader Mappings". Narrowing the possible range makes 113 # scanning much faster. (See 114 # http://www.codemachine.com/article_x64kvas.html) 115 if self.session.profile.metadata("arch") == "AMD64": 116 start, end = 0xFFFFF80000000000, 0xFFFFF87FFFFFFFFF 117 else: 118 start, end = 0x80000000, 0xFFFFFFFF 119 120 scanner = KDBGScanner( 121 session=self.session, profile=self.profile, 122 address_space=self.kernel_address_space) 123 124 # Yield actual objects here 125 for kdbg in scanner.scan( 126 obj.Pointer.integer_to_address(start), 127 end - start): 128 yield kdbg
129 130 table_header = [ 131 dict(name="Key", width=50), 132 dict(name="Value") 133 ] 134 135 table_options = dict( 136 suppress_headers=True 137 ) 138
139 - def collect(self):
140 """Renders the KPCR values as text""" 141 142 for kdbg in self.hits(): 143 yield "Offset (V)", utils.HexInteger(kdbg.obj_offset) 144 yield "Offset (P)", utils.HexInteger(kdbg.obj_vm.vtop( 145 kdbg.obj_offset)) 146 147 # These fields can be gathered without dereferencing 148 # any pointers, thus they're available always 149 yield "KDBG owner tag check", kdbg.is_valid() 150 151 verinfo = kdbg.dbgkd_version64() 152 if verinfo: 153 yield "Version64", "{0:#x} (Major: {1}, Minor: {2})\n".format( 154 verinfo.obj_offset, verinfo.MajorVersion, 155 verinfo.MinorVersion) 156 157 yield "Service Pack (CmNtCSDVersion)", kdbg.ServicePack 158 yield "Build string (NtBuildLab)", kdbg.NtBuildLab.dereference() 159 160 # Count the total number of tasks from PsActiveProcessHead. 161 try: 162 163 pslist = kdbg.PsActiveProcessHead.list_of_type( 164 "_EPROCESS", "ActiveProcessLinks") 165 num_tasks = len([x for x in pslist if x.pid > 0]) 166 except AttributeError: 167 num_tasks = 0 168 169 try: 170 modules = self.session.plugins.modules(session=self.session) 171 num_modules = len(list(modules.lsmod())) 172 except AttributeError: 173 num_modules = 0 174 175 yield "PsActiveProcessHead", "{0:#x} ({1} processes)".format( 176 kdbg.PsActiveProcessHead, num_tasks) 177 178 yield "PsLoadedModuleList", "{0:#x} ({1} modules)".format( 179 kdbg.PsLoadedModuleList, num_modules) 180 181 yield "KernelBase", "{0:#x} (Matches MZ: {1})".format( 182 kdbg.KernBase, kdbg.obj_vm.read(kdbg.KernBase, 2) == "MZ") 183 184 # Parse the PE header of the kernel. 185 pe_profile = self.session.LoadProfile("pe") 186 187 dos_header = pe_profile._IMAGE_DOS_HEADER( 188 offset=kdbg.KernBase, vm=kdbg.obj_vm) 189 nt_header = dos_header.NTHeader 190 if nt_header: 191 yield ("Major (OptionalHeader)", 192 nt_header.OptionalHeader.MajorOperatingSystemVersion) 193 194 yield("Minor (OptionalHeader)", 195 nt_header.OptionalHeader.MinorOperatingSystemVersion) 196 197 # The CPU block. 198 for kpcr in kdbg.kpcrs(): 199 yield "KPCR", "{0:#x} (CPU {1})".format( 200 kpcr.obj_offset, kpcr.ProcessorBlock.Number)
201