def get_kdbg(addr_space): """A function designed to return the KDBG structure from an address space. First we try scanning for KDBG and if that fails, we try scanning for KPCR and bouncing back to KDBG from there. Also note, both the primary and backup methods rely on the 4-byte KDBG.Header.OwnerTag. If someone overwrites this value, then neither method will succeed. The same is true even if a user specifies --kdbg, because we check for the OwnerTag even in that case. """ kdbgo = obj.VolMagic(addr_space).KDBG.v() kdbg = obj.Object("_KDDEBUGGER_DATA64", offset=kdbgo, vm=addr_space) if kdbg.is_valid(): return kdbg # Fall back to finding it via the KPCR. We cannot # accept the first/best suggestion, because only # the KPCR for the first CPU allows us to find KDBG. for kpcr_off in obj.VolMagic(addr_space).KPCR.generate_suggestions(): kpcr = obj.Object("_KPCR", offset=kpcr_off, vm=addr_space) kdbg = kpcr.get_kdbg() if kdbg.is_valid(): return kdbg return obj.NoneObject( "KDDEBUGGER structure not found using either KDBG signature or KPCR pointer" )
def get_kdbg(addr_space): """A function designed to return the KDBG structure from an address space. First we try scanning for KDBG and if that fails, we try scanning for KPCR and bouncing back to KDBG from there. Also note, both the primary and backup methods rely on the 4-byte KDBG.Header.OwnerTag. If someone overwrites this value, then neither method will succeed. The same is true even if a user specifies --kdbg, because we check for the OwnerTag even in that case. """ # we can use the hard coded KPCR value instead of scanning for KDBG # like back in the old days of version 1.x # this works for XP/2003 x86 # all other machines that do not have hardcoded KPCR values # will fall back on the previous methodology if obj.VolMagic(addr_space).KPCR.value: kpcr = obj.Object("_KPCR", offset=obj.VolMagic(addr_space).KPCR.value, vm=addr_space) kdbg = kpcr.get_kdbg() if kdbg.is_valid(): return kdbg kdbg_magic = obj.VolMagic(addr_space).KDBG for kdbg in kdbg_magic.get_suggestions(): if kdbg.is_valid(): return kdbg # skip the KPCR backup method for x64 memmode = addr_space.profile.metadata.get('memory_model', '32bit') version = ( addr_space.profile.metadata.get('major', 0), addr_space.profile.metadata.get('minor', 0), ) if memmode == '32bit' or version <= (6, 1): # Fall back to finding it via the KPCR. We cannot # accept the first/best suggestion, because only # the KPCR for the first CPU allows us to find KDBG. for kpcr_off in obj.VolMagic(addr_space).KPCR.get_suggestions(): kpcr = obj.Object("_KPCR", offset=kpcr_off, vm=addr_space) kdbg = kpcr.get_kdbg() if kdbg.is_valid(): return kdbg return obj.NoneObject( "KDDEBUGGER structure not found using either KDBG signature or KPCR pointer" )
def calculate(self): """Determines the address space""" profilelist = [ p.__name__ for p in registry.get_plugin_classes(obj.Profile).values() ] encrypted_kdbg_profiles = [] proflens = {} maxlen = 0 origprofile = self._config.PROFILE for p in profilelist: self._config.update('PROFILE', p) buf = addrspace.BufferAddressSpace(self._config) if buf.profile.metadata.get('os', 'unknown') == 'windows': proflens[p] = str(obj.VolMagic(buf).KDBGHeader) maxlen = max(maxlen, len(proflens[p])) if (buf.profile.metadata.get('memory_model', '64bit') == '64bit' and (buf.profile.metadata.get('major', 0), buf.profile.metadata.get('minor', 0)) >= (6, 2)): encrypted_kdbg_profiles.append(p) self._config.update('PROFILE', origprofile) # keep track of the number of potential KDBGs we find count = 0 if origprofile not in encrypted_kdbg_profiles: scanner = KDBGScanner(needles=proflens.values()) aspace = utils.load_as(self._config, astype='any') for offset in scanner.scan(aspace): val = aspace.read(offset, maxlen + 0x10) for l in proflens: if val.find(proflens[l]) >= 0: kdbg = obj.Object("_KDDEBUGGER_DATA64", offset=offset, vm=aspace) yield l, kdbg count += 1 # only perform the special win8/2012 scan if we didn't find # any others and if a virtual x64 address space is available if count == 0: if origprofile in encrypted_kdbg_profiles: encrypted_kdbg_profiles = [origprofile] for profile in encrypted_kdbg_profiles: self._config.update('PROFILE', profile) aspace = utils.load_as(self._config, astype='any') if hasattr(aspace, 'vtop'): for kdbg in obj.VolMagic( aspace).KDBG.generate_suggestions(): yield profile, kdbg
def windows_kdbgscan_fast(dtb): global last_kdbg from utils import ConfigurationManager as conf_m try: config = conf_m.vol_conf config.DTB = dtb try: addr_space = utils.load_as(config) except BaseException: # Return silently conf_m.addr_space = None return 0L conf_m.addr_space = addr_space if obj.VolMagic(addr_space).KPCR.value: kpcr = obj.Object("_KPCR", offset=obj.VolMagic(addr_space).KPCR.value, vm=addr_space) kdbg = kpcr.get_kdbg() if kdbg.is_valid(): last_kdbg = kdbg.obj_offset return long(last_kdbg) kdbg = obj.VolMagic(addr_space).KDBG.v() if kdbg.is_valid(): last_kdbg = kdbg.obj_offset return long(last_kdbg) # skip the KPCR backup method for x64 memmode = addr_space.profile.metadata.get('memory_model', '32bit') version = (addr_space.profile.metadata.get('major', 0), addr_space.profile.metadata.get('minor', 0)) if memmode == '32bit' or version <= (6, 1): # Fall back to finding it via the KPCR. We cannot # accept the first/best suggestion, because only # the KPCR for the first CPU allows us to find KDBG. for kpcr_off in obj.VolMagic(addr_space).KPCR.get_suggestions(): kpcr = obj.Object("_KPCR", offset=kpcr_off, vm=addr_space) kdbg = kpcr.get_kdbg() if kdbg.is_valid(): last_kdbg = kdbg.obj_offset return long(last_kdbg) return 0L except BaseException: traceback.print_exc()
def __init__(self, base, config, dtb=0, skip_as_check=False, *args, **kwargs): ## We must be stacked on someone else: self.as_assert(base, "No base Address Space") addrspace.AbstractVirtualAddressSpace.__init__(self, base, config, *args, **kwargs) ## We can not stack on someone with a dtb self.as_assert( not (hasattr(base, 'paging_address_space') and base.paging_address_space), "Can not stack over another paging address space") self.dtb = dtb or self.load_dtb() # No need to set the base or dtb, it's already been by the inherited class self.as_assert(self.dtb != None, "No valid DTB found") if not skip_as_check: volmag = obj.VolMagic(self) if hasattr(volmag, self.checkname): self.as_assert( getattr(volmag, self.checkname).v(), "Failed valid Address Space check") else: self.as_assert( False, "Profile does not have valid Address Space check") self.name = 'Kernel AS'
def __init__(self, base, config, dtb = 0, *args, **kwargs): ## We must be stacked on someone else: self.as_assert(base, "No base Address Space") ## We allow users to disable us in favour of the old legacy ## modules. self.as_assert(not config.USE_OLD_AS, "Module disabled") standard.AbstractWritablePagedMemory.__init__(self, base, config, *args, **kwargs) addrspace.BaseAddressSpace.__init__(self, base, config, *args, **kwargs) ## We can not stack on someone with a dtb self.as_assert(not (hasattr(base, 'paging_address_space') and base.paging_address_space), "Can not stack over another paging address space") self.dtb = dtb or self.load_dtb() # print(str(hex(self.dtb))) # No need to set the base, it's already been by the inherited class self.as_assert(self.dtb != None, "No valid DTB found") # The caching code must be in a separate function to allow the # PAE code, which inherits us, to have its own code. self.cache = config.CACHE_DTB if self.cache: self._cache_values() volmag = obj.VolMagic(self) if hasattr(volmag, self.checkname): self.as_assert(getattr(volmag, self.checkname).v(), "Failed valid Address Space check") # Reserved for future use #self.pagefile = config.PAGEFILE self.name = 'Kernel AS'
def calculate(self): """Determines the address space""" profilelist = [ p.__name__ for p in registry.get_plugin_classes(obj.Profile).values() ] proflens = {} maxlen = 0 origprofile = self._config.PROFILE for p in profilelist: self._config.update('PROFILE', p) buf = addrspace.BufferAddressSpace(self._config) if buf.profile.metadata.get('os', 'unknown') == 'windows': proflens[p] = str(obj.VolMagic(buf).KDBGHeader) maxlen = max(maxlen, len(proflens[p])) self._config.update('PROFILE', origprofile) scanner = KDBGScanner(needles=proflens.values()) aspace = utils.load_as(self._config, astype='any') for offset in scanner.scan(aspace): val = aspace.read(offset, maxlen + 0x10) for l in proflens: if val.find(proflens[l]) >= 0: kdbg = obj.Object("_KDDEBUGGER_DATA64", offset=offset, vm=aspace) yield l, kdbg
def handles(self): """ A generator which yields this process's handles _HANDLE_TABLE tables are multi-level tables at the first level they are pointers to second level table, which might be pointers to third level tables etc, until the final table contains the real _OBJECT_HEADER table. This generator iterates over all the handles recursively yielding all handles. We take care of recursing into the nested tables automatically. """ magic = obj.VolMagic(self.obj_vm) if hasattr(magic, 'ObHeaderCookie'): cookie = magic.ObHeaderCookie.v() if not cookie: raise StopIteration("Cannot find nt!ObHeaderCookie") # This should work equally for 32 and 64 bit systems LEVEL_MASK = 7 TableCode = self.TableCode.v() & ~LEVEL_MASK table_levels = self.TableCode.v() & LEVEL_MASK offset = TableCode for h in self._make_handle_array(offset, table_levels): yield h
def get_object_bottom_up(self, struct_name, object_type, skip_type_check): """Get the windows object contained within this pool by using the bottom-up approach to finding the object """ if not object_type: return obj.Object(struct_name, vm = self.obj_vm, offset = self.obj_offset + self.obj_vm.profile.get_obj_size("_POOL_HEADER"), native_vm = self.obj_native_vm) pool_alignment = obj.VolMagic(self.obj_vm).PoolAlignment.v() the_object = obj.Object(struct_name, vm = self.obj_vm, offset = (self.obj_offset + self.BlockSize * pool_alignment - common.pool_align(self.obj_vm, struct_name, pool_alignment)), native_vm = self.obj_native_vm) header = the_object.get_object_header() if (skip_type_check or header.get_object_type() == object_type): return the_object else: return obj.NoneObject("Cannot find the object")
def __init__(self, addr_space, scanners = [], scan_virtual = False, show_unalloc = False, use_top_down = False, start_offset = None, max_length = None): """An interface into the multiple concurrent pool scanner. @param addr_space: a Volatility address space @param scanners: a list of PoolScanner classes to scan for. @param scan_virtual: True to scan in virtual/kernel space or False to scan at the physical layer. @param show_unalloc: True to skip unallocated objects whose _OBJECT_TYPE structure are 0xbad0b0b0. @param use_topdown: True to carve objects out of the pool using the top-down approach or False to use the bottom-up trick. @param start_offset: the starting offset to begin scanning. @param max_length: the size in bytes to scan from the start. """ self.scanners = scanners self.scan_virtual = scan_virtual self.show_unalloc = show_unalloc self.use_top_down = use_top_down self.start_offset = start_offset self.max_length = max_length self.address_space = addr_space self.pool_alignment = obj.VolMagic(self.address_space).PoolAlignment.v()
def check(self, found): ## The offset of the object is determined by subtracting the offset ## of the PoolTag member to get the start of Pool Object. This done ## because PoolScanners search for the PoolTag. pool_base = found - self.address_space.profile.get_obj_offset( '_POOL_HEADER', 'PoolTag') pool_obj = obj.Object("_POOL_HEADER", vm=self.address_space, offset=pool_base) ## We work out the _EPROCESS from the end of the ## allocation (bottom up). pool_alignment = obj.VolMagic(self.address_space).PoolAlignment.v() eprocess = obj.Object( "_EPROCESS", vm=self.address_space, offset=pool_base + pool_obj.BlockSize * pool_alignment - common.pool_align(self.address_space, '_EPROCESS', pool_alignment)) if (eprocess.Pcb.DirectoryTableBase == 0): return False if (eprocess.Pcb.DirectoryTableBase % 0x20 != 0): return False list_head = eprocess.ThreadListHead if (list_head.Flink < self.kernel) or (list_head.Blink < self.kernel): return False return True
def generate_suggestions(self): """Generates a list of possible KDBG structure locations""" with UpdateCounterForScope('KDBGScanner'): scanner = kdbg.KDBGScanner(needles = [obj.VolMagic(self.obj_vm).KDBGHeader.v()]) for val in scanner.scan(self.obj_vm): val = obj.Object("_KDDEBUGGER_DATA64", offset = val, vm = self.obj_vm) yield val
def render_text(self, outfd, data): if self._config.DUMP_DIR == None: debug.error("Please specify a dump directory (--dump-dir)") if not os.path.isdir(self._config.DUMP_DIR): debug.error(self._config.DUMP_DIR + " is not a directory") self.table_header(outfd, [ ("Pid", "10"), ("Process", "20"), ("Start", "[addrpad]"), ("End", "[addrpad]"), ("Result", ""), ]) for task in data: # Walking the VAD tree can be done in kernel AS, but to # carve the actual data, we need a valid process AS. task_space = task.get_process_address_space() if not task_space: outfd.write("Unable to get process AS for {0}\n".format( task.UniqueProcessId)) continue max_commit = obj.VolMagic(task_space).MM_MAX_COMMIT.v() # as a first step, we try to get the physical offset of the # _EPROCESS object using the process address space offset = task_space.vtop(task.obj_offset) # if this fails, we'll get its physical offset using kernel space if offset == None: offset = task.obj_vm.vtop(task.obj_offset) # if this fails we'll manually set the offset to 0 if offset == None: offset = 0 for vad in task.VadRoot.traverse(): if not vad.is_valid(): continue if self._config.BASE and vad.Start != self._config.BASE: continue # Open the file and initialize the data vad_start = self.format_value(vad.Start, "[addrpad]") vad_end = self.format_value(vad.End, "[addrpad]") path = os.path.join( self._config.DUMP_DIR, "{0}.{1:x}.{2}-{3}.dmp".format(task.ImageFileName, offset, vad_start, vad_end)) if (task.IsWow64 and vad.CommitCharge == max_commit and vad.End > 0x7fffffff): result = "Skipping Wow64 MM_MAX_COMMIT range" else: result = self.dump_vad(path, vad, task_space) self.table_row(outfd, task.UniqueProcessId, task.ImageFileName, vad.Start, vad.End, result)
def get_system_time(): from volatility import obj from utils import get_addr_space addr_space = get_addr_space() k = obj.Object("_KUSER_SHARED_DATA", offset=obj.VolMagic(addr_space).KUSER_SHARED_DATA.v(), vm=addr_space) return k.SystemTime.as_datetime()
def TypeIndex(self): """Wrap the TypeIndex member with a property that decodes it with the nt!ObHeaderCookie value.""" cook = obj.VolMagic(self.obj_vm).ObHeaderCookie.v() addr = self.obj_offset indx = int(self.m("TypeIndex")) return ((addr >> 8) ^ cook ^ indx) & 0xFF
def check(self, offset): pool_hdr = obj.Object('_POOL_HEADER', vm = self.address_space, offset = offset - 4) block_size = pool_hdr.BlockSize.v() pool_alignment = obj.VolMagic(self.address_space).PoolAlignment.v() return self.condition(block_size * pool_alignment)
def page_file_number(self): if not hasattr(self, "_page_file_number"): page_file_number = obj.VolMagic(self.paged_as).VSPageFileNumber.v() if not page_file_number: raise Exception("Invalid Virtual Store page file number value") self._page_file_number = page_file_number return self._page_file_number
def render_text(self, outfd, data): for pool, buf in data: pool_alignment = obj.VolMagic(pool.obj_vm).PoolAlignment.v() outfd.write("Pool Header: {0:#x}, Size: {1}\n".format( pool.obj_offset, pool.BlockSize * pool_alignment)) outfd.write("{0}\n".format("\n".join( ["{0:#010x} {1:<48} {2}".format(pool.obj_offset + o, h, ''.join(c)) for o, h, c in utils.Hexdump(buf) ]))) outfd.write("\n")
def sm_globals(self): # May raise an exception which causes the AS to fail loading if not hasattr(self, "_sm_globals"): sm_globals = obj.VolMagic(self.paged_as).SmGlobals.v() if not sm_globals: raise Exception("Invalid nt!SmGlobals value") self._sm_globals = sm_globals return self._sm_globals
def __init__(self, address_space): poolscan.PoolScanner.__init__(self, address_space) self.struct_name = "_OBJECT_SYMBOLIC_LINK" self.object_type = "SymbolicLink" self.pooltag = obj.VolMagic(address_space).SymlinkPoolTag.v() size = 0x48 # self.address_space.profile.get_obj_size("_OBJECT_SYMBOLIC_LINK") self.checks = [ ('CheckPoolSize', dict(condition = lambda x: x >= size)), ('CheckPoolType', dict(paged = True, non_paged = True, free = True)), ]
def load_dtb(self): try: ## Try to be lazy and see if someone else found dtb for ## us: return self.base.dtb except AttributeError: ## Ok so we need to find our dtb ourselves: dtb = obj.VolMagic(self.base).DTB.v() if dtb: ## Make sure to save dtb for other AS's self.base.dtb = dtb return dtb
def __init__(self, address_space, **kwargs): poolscan.PoolScanner.__init__(self, address_space, **kwargs) self.struct_name = "_KMUTANT" self.object_type = "Mutant" self.pooltag = obj.VolMagic(address_space).MutexPoolTag.v() size = 0x40 # self.address_space.profile.get_obj_size("_KMUTANT") self.checks = [ ('CheckPoolSize', dict(condition=lambda x: x >= size)), ('CheckPoolType', dict(paged=False, non_paged=True, free=True)), ('CheckPoolIndex', dict(value=lambda x: x < 5)), ]
def __init__(self, address_space, **kwargs): poolscan.PoolScanner.__init__(self, address_space, **kwargs) self.struct_name = "_OBJECT_TYPE" self.object_type = "Type" self.pooltag = obj.VolMagic(address_space).ObjectTypePoolTag.v() size = 0xc8 # self.address_space.profile.get_obj_size("_OBJECT_TYPE") self.checks = [ ('CheckPoolSize', dict(condition=lambda x: x >= size)), ('CheckPoolType', dict(paged=False, non_paged=True, free=True)), #('CheckPoolIndex', dict(value = 0)), ]
def __init__(self, address_space): poolscan.PoolScanner.__init__(self, address_space) self.struct_name = "_FILE_OBJECT" self.object_type = "File" self.pooltag = obj.VolMagic(address_space).FilePoolTag.v() size = 0x98 # self.address_space.profile.get_obj_size("_FILE_OBJECT") self.checks = [ ('CheckPoolSize', dict(condition=lambda x: x >= size)), ('CheckPoolType', dict(paged=False, non_paged=True, free=True)), ('CheckPoolIndex', dict(value=lambda x: x < 5)), ]
def calculate(self): POOLSIZE_X86_AESDIFF = 976 POOLSIZE_X86_AESONLY = 504 POOLSIZE_X64_AESDIFF = 1008 POOLSIZE_X64_AESONLY = 528 OFFSET_DB = { POOLSIZE_X86_AESDIFF: { 'CID': 24, 'FVEK1': 32, 'FVEK2': 504 }, POOLSIZE_X86_AESONLY: { 'CID': 24, 'FVEK1': 32, 'FVEK2': 336 }, POOLSIZE_X64_AESDIFF: { 'CID': 44, 'FVEK1': 48, 'FVEK2': 528 }, POOLSIZE_X64_AESONLY: { 'CID': 44, 'FVEK1': 48, 'FVEK2': 480 }, } addr_space = utils.load_as(self._config) scanner = poolscan.SinglePoolScanner() scanner.checks = [ ('PoolTagCheck', dict(tag='FVEc')), ('CheckPoolSize', dict(condition=lambda x: x in list(OFFSET_DB.keys()))), ] for addr in scanner.scan(addr_space): pool = obj.Object('_POOL_HEADER', offset=addr, vm=addr_space) pool_alignment = obj.VolMagic(pool.obj_vm).PoolAlignment.v() pool_size = int(pool.BlockSize * pool_alignment) cid = addr_space.zread(addr + OFFSET_DB[pool_size]['CID'], 2) fvek1 = addr_space.zread(addr + OFFSET_DB[pool_size]['FVEK1'], 32) fvek2 = addr_space.zread(addr + OFFSET_DB[pool_size]['FVEK2'], 32) if ord(cid[1]) == 0x80 and ord(cid[0]) <= 0x03: fvek = fvek1 + fvek2 yield pool, cid, fvek
def calculate(self): addr_space = utils.load_as(self._config) # Get the version we're analyzing version = (addr_space.profile.metadata.get('major', 0), addr_space.profile.metadata.get('minor', 0)) tag = obj.VolMagic(addr_space).ServiceTag.v() # On systems more recent than XP/2003, the serH marker doesn't # find *all* services, but the ones it does find have linked # lists to the others. We use this variable to track which # ones we've seen so as to not yield duplicates. records = [] for task in tasks.pslist(addr_space): # We only want the Service Control Manager process if str(task.ImageFileName).lower() != "services.exe": continue # Process AS must be valid process_space = task.get_process_address_space() if process_space == None: continue # Find all instances of the record tag for address in task.search_process_memory( [tag], vad_filter=lambda x: x.Length < 0x40000000): if version <= (5, 2): # Windows XP/2003 rec = obj.Object("_SERVICE_RECORD", offset=address - addr_space.profile.get_obj_offset( '_SERVICE_RECORD', 'Tag'), vm=process_space) # Apply our sanity checks if rec.is_valid(): yield rec else: # Windows Vista, 2008, and 7 svc_hdr = obj.Object('_SERVICE_HEADER', offset=address, vm=process_space) # Apply our sanity checks if svc_hdr.is_valid(): # Since we walk the s-list backwards, if we've seen # an object, then we've also seen all objects that # exist before it, thus we can break at that time. for rec in svc_hdr.ServiceRecord.traverse(): if rec in records: break records.append(rec) yield rec
def get_image_time(self, addr_space): """Get the Image Datetime""" result = {} KUSER_SHARED_DATA = obj.VolMagic(addr_space).KUSER_SHARED_DATA.v() k = obj.Object("_KUSER_SHARED_DATA", offset = KUSER_SHARED_DATA, vm = addr_space) if k == None: return k result['ImageDatetime'] = k.SystemTime result['ImageTz'] = timefmt.OffsetTzInfo(-k.TimeZoneBias.as_windows_timestamp() / 10000000) return result
def __init__(self, base, config, dtb=0, skip_as_check=False, *args, **kwargs): ## We must be stacked on someone else: self.as_assert(base, "No base Address Space") addrspace.AbstractVirtualAddressSpace.__init__(self, base, config, *args, **kwargs) ## We can not stack on someone with a dtb self.as_assert( not (hasattr(base, 'paging_address_space') and base.paging_address_space), "Can not stack over another paging address space") #rajesh if self.get_config().dtb: self.dtb = self.get_config().dtb else: self.dtb = dtb or self.load_dtb() if self.get_config().process_id: self.process_id = self.get_config().process_id else: self.process_id = 0 logging.debug("AbstractPagedMemory dtb:{0} process_id:{1}".format( self.dtb, self.process_id)) # No need to set the base or dtb, it's already been by the inherited class self.as_assert(self.dtb != None, "No valid DTB found") if not skip_as_check: volmag = obj.VolMagic(self) if hasattr(volmag, self.checkname): self.as_assert( getattr(volmag, self.checkname).v(), "Failed valid Address Space check") else: self.as_assert( False, "Profile does not have valid Address Space check") # Reserved for future use #self.pagefile = config.PAGEFILE self.name = 'Kernel AS'
def __init__(self, address_space): poolscan.PoolScanner.__init__(self, address_space) self.struct_name = "_ETHREAD" self.object_type = "Thread" # this allows us to find terminated threads self.skip_type_check = True self.pooltag = obj.VolMagic(address_space).ThreadPoolTag.v() size = 0x278 # self.address_space.profile.get_obj_size("_ETHREAD") self.checks = [ ('CheckPoolSize', dict(condition = lambda x: x >= size)), ('CheckPoolType', dict(paged = False, non_paged = True, free = True)), ('CheckPoolIndex', dict(value = lambda x : x < 5)), ]
def __init__(self, address_space, **kwargs): poolscan.PoolScanner.__init__(self, address_space, **kwargs) self.struct_name = "_EPROCESS" self.object_type = "Process" # this allows us to find terminated processes self.skip_type_check = True self.pooltag = obj.VolMagic(address_space).ProcessPoolTag.v() size = 0x1ae # self.address_space.profile.get_obj_size("_EPROCESS") self.checks = [ ('CheckPoolSize', dict(condition = lambda x: x >= size)), ('CheckPoolType', dict(paged = False, non_paged = True, free = True)), ('CheckPoolIndex', dict(value = 0)), ]