# IDAPython script to parse loaded modules list when debugging a Windows kernel in VMWare with GDB plugin # Copyright 2009 Hex-Rays # get the address of PsLoadedModuleList structure def get_PsLoadedModuleList(): #send "r fs" command to get FS segment base fs_str = Eval('SendGDBMonitor("r fs")') #'fs 0x30 base 0x82744a00 limit 0x00002008 type 0x3 s 1 dpl 0 p 1 db 1' if not fs_str.startswith('fs '): Message("Error requesting fs segment info. Are you running VMWare and are in protected mode?") return None kpcr = int(fs_str[13:23], 16) if kpcr != Dword(kpcr+0x1C): # SelfPcr Message("fs base doesn't look like KPCR. Are you in Windows kernel?") return None # Message("%08X: KPCR", kpcr) kdversionblock = Dword(kpcr+0x34) # PVOID KdVersionBlock PsLoadedModuleList = Dword(kdversionblock+0x18) # PLIST_ENTRY PsLoadedModuleList if PsLoadedModuleList==BADADDR: Message("Could not retrieve PsLoadedModuleList.") return None return PsLoadedModuleList #read a string from UNICODE_STRING structure def get_unistr(addr): len = Word(addr) #USHORT Length; start = Dword(addr+4) #PWSTR Buffer; if len>1000: raise Exception("%08X: String too long (%d)"%(addr, len)) res = u'' while len>0: c = Word(start) if c==0: break res += unichr(c) start += 2 len -= 1 return res #walk the PsLoadedModuleList list and call callback for each module def walk_modulelist(list, callback): # get the first module cur_mod = Dword(list) # loop until we come back to the beginning while cur_mod != list and cur_mod != BADADDR: #print "%08X"%cur_mod BaseAddress = Dword(cur_mod+0x18) EntryPoint = Dword(cur_mod+0x1C) SizeOfImage = Dword(cur_mod+0x20) FullDllName = get_unistr(cur_mod+0x24).encode('utf-8') BaseDllName = get_unistr(cur_mod+0x2C).encode('utf-8') callback(BaseAddress, BaseDllName, FullDllName, SizeOfImage, EntryPoint) #get next module (FLink) next_mod = Dword(cur_mod) #check that BLink points to the previous structure if Dword(next_mod+4)!=cur_mod: print "%08X: List error!"%cur_mod break cur_mod = next_mod #add a segment for the module def addSegmentCallback(BaseAddress, BaseDllName, FullDllName, SizeOfImage, EntryPoint): print "%08X: %s (%s, size %08X, entry %08X)"%(BaseAddress, BaseDllName, FullDllName, SizeOfImage, EntryPoint) #do we already have a segment for this module? if SegStart(BaseAddress) != BaseAddress or SegEnd(BaseAddress) != BaseAddress+SizeOfImage: #if not, create one SegCreate(BaseAddress, BaseAddress+SizeOfImage, 0, 1, saRelByte, scPriv) SegRename(BaseAddress, BaseDllName) #path to local copy of System32 directory local_sys32 = r"D:\VmWareShared\w7\System32" def loadPdbCallback(BaseAddress, BaseDllName, FullDllName, SizeOfImage, EntryPoint): #only load modules from System32 if FullDllName.lower().startswith(r"\systemroot\system32"): #translate into local filename filename = local_sys32 + FullDllName[20:] penode = idaapi.netnode() penode.create("$ PE header") #save old values save_base = penode.altval(0xFFFFFFFE) save_name = idaapi.get_input_file_path() #set parameters for PDB plugin penode.altset(0xFFFFFFFE, BaseAddress) idaapi.set_root_filename(filename) #load symbols print "Trying to load symbols for %s from %s"%(BaseDllName, filename) RunPlugin("pdb", 3) #use 1 to get a confirmation prompt pdbnode = idaapi.netnode("$ pdb") ok = pdbnode.altval(0) if not ok: print "Could not load symbols" #restore previous values penode.altset(0xFFFFFFFE, save_base) idaapi.set_root_filename(save_name) else: print "%s is not in System32 directory"%BaseDllName # do the work list = get_PsLoadedModuleList() if list: walk_modulelist(list, addSegmentCallback) walk_modulelist(list, loadPdbCallback)