diff --git a/pykd-0.3-2010.sln b/pykd-0.3-2010.sln index 77fe0d9..5fb569d 100644 --- a/pykd-0.3-2010.sln +++ b/pykd-0.3-2010.sln @@ -53,11 +53,13 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "snippets", "snippets", "{AA snippets\cr4.py = snippets\cr4.py snippets\ctlcode.py = snippets\ctlcode.py snippets\export.py = snippets\export.py + snippets\findhandle.py = snippets\findhandle.py snippets\findtag.py = snippets\findtag.py snippets\gdt.py = snippets\gdt.py snippets\help.py = snippets\help.py snippets\iat.py = snippets\iat.py snippets\ndis.py = snippets\ndis.py + snippets\ntobj.py = snippets\ntobj.py snippets\pytowiki.py = snippets\pytowiki.py EndProjectSection EndProject diff --git a/pykd/pymod.cpp b/pykd/pymod.cpp index 60fc79a..21bc1da 100644 --- a/pykd/pymod.cpp +++ b/pykd/pymod.cpp @@ -34,6 +34,7 @@ BOOST_PYTHON_FUNCTION_OVERLOADS( startProcess_, startProcess, 1, 2 ); BOOST_PYTHON_FUNCTION_OVERLOADS( detachProcess_, kdlib::detachProcess, 0, 1 ); BOOST_PYTHON_FUNCTION_OVERLOADS( terminateProcess_, kdlib::terminateProcess, 0, 1 ); BOOST_PYTHON_FUNCTION_OVERLOADS( attachKernel_, attachKernel, 0, 1 ); +BOOST_PYTHON_FUNCTION_OVERLOADS( evaluate_, evaluate, 1, 2 ); BOOST_PYTHON_FUNCTION_OVERLOADS( dprint_, kdlib::dprint, 1, 2 ); BOOST_PYTHON_FUNCTION_OVERLOADS( dprintln_, kdlib::dprintln, 1, 2 ); @@ -119,8 +120,8 @@ BOOST_PYTHON_MODULE( pykd ) python::def( "breakin", &targetBreak, "Break into debugger" ); - python::def( "expr", &evaluate, - "Evaluate windbg expression" ); + python::def( "expr", &evaluate, evaluate_( boost::python::args( "expression", "cplusplus" ), + "Evaluate windbg expression" ) ); python::def( "dbgCommand", &debugCommand, "Run a debugger's command and return it's result as a string" ); python::def( "go", &targetGo, diff --git a/snippets/findhandle.py b/snippets/findhandle.py new file mode 100644 index 0000000..864c004 --- /dev/null +++ b/snippets/findhandle.py @@ -0,0 +1,49 @@ +from pykd import * +import ntobj +import sys + +nt = module("nt") + + +def findHanle(objaddr): + + processList = typedVarList( nt.PsActiveProcessHead, "nt!_EPROCESS", "ActiveProcessLinks" ) + + for process in processList: + + dprintln( "search in process %x " % process.UniqueProcessId + "".join( [chr(i) for i in process.ImageFileName if i != 0] ) ) + + if process.ObjectTable == 0: + continue + + objects = ntobj.getListByHandleTable( process.ObjectTable ) + for obj in objects: + if obj[0] == objaddr: + dprintln("\tHandle: %x" % ( obj[1],) ) + + +def usage(): + dprintln("!py findhandle object_address") + +def main(): + + if not isKernelDebugging(): + dprintln("This script for kernel debugging only") + return + + if len(sys.argv) < 2: + usage(); + return; + + objaddr = expr(sys.argv[1]) + + objectType = ntobj.getType(objaddr) + + dprintln("Object Type: " + ntobj.getObjectName(objectType) ) + dprintln("Object Name: "+ ntobj.getObjectName(objaddr) ) + dprintln("") + + findHanle( objaddr ) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/snippets/ntobj.py b/snippets/ntobj.py new file mode 100644 index 0000000..0c49883 --- /dev/null +++ b/snippets/ntobj.py @@ -0,0 +1,439 @@ +""" +Work with NT Object tree manager + + getType(p) + Return object type address by object address + + getObjectNameInfo(p) + Return typedVar of nt!_OBJECT_HEADER_NAME_INFO or None + + getObjectName(p) + Return string of object name. If can not get name - empty string + + buildObjectName(p) + Return string of full object name. If can not get name - empty string + + getListByHandleTable(tableHandles=None, objTypeAddr=0, containHeaders=True) + Return list of objects from table of handles + + getListByDirectoryObject(p, objTypeAddr=0) + Return list of objects from object directory + + getObjectByName(name, caseSensitive=False) + Return address of object by full name. If error (f.e. not exist) - None + +""" + +from pykd import * + +nt = module("nt") + +# optimization +OBJECT_HEADER = nt.type("_OBJECT_HEADER") +HANDLE_TABLE_ENTRY = nt.type("_HANDLE_TABLE_ENTRY") +OBJECT_DIRECTORY_ENTRY = nt.type("_OBJECT_DIRECTORY_ENTRY") +OBJECT_HEADER_NAME_INFO = nt.type("_OBJECT_HEADER_NAME_INFO") + + + +def getTypeWin7(p): + """ + Get object header by object pointer + Implementation for Win7+ + """ + objHeader = containingRecord(p, OBJECT_HEADER, "Body") + tableTypeIndex = nt.ObTypeIndexTable + return ptrPtr(tableTypeIndex + (ptrSize() * objHeader.TypeIndex)) + +def getTypeLegacy(p): + """ + Get object header by object pointer + Implementation for before Win7 + """ + objHeader = containingRecord(p, OBJECT_HEADER, "Body") + return objHeader.Type + +# Select platform-specific function for getting object header +# Select key body type: nt!CmpKeyObjectType or nt!CmKeyObjectType +if (ptrWord( nt.NtBuildNumber ) >= 7600): + getType = getTypeWin7 + # _kcbObjectType = expr("poi(nt!CmKeyObjectType)") +else: + getType = getTypeLegacy + # _kcbObjectType = expr("poi(nt!CmpKeyObjectType)") + + +def getObjectNameInfoFromHeader(p): + """ + Get object name information from field NameInfoOffset of object header + """ + objHeader = containingRecord(p, OBJECT_HEADER, "Body") + if (0 == objHeader.NameInfoOffset): + return None + return typedVar(OBJECT_HEADER_NAME_INFO, objHeader.getAddress() - objHeader.NameInfoOffset) + +def getObjectNameInfoFromInfoMask(p): + """ + Get object name information from field NameInfoOffset of object header + """ + objHeader = containingRecord(p, OBJECT_HEADER, "Body") + if (0 == (objHeader.InfoMask & 2)): + return None + offsetNameInfo = ptrByte( nt.ObpInfoMaskToOffset + (objHeader.InfoMask & 3) ) + if (0 == offsetNameInfo): + return None + return typedVar(OBJECT_HEADER_NAME_INFO, objHeader.getAddress() - offsetNameInfo) + + +# Select platform-specific function for getting name of object +getObjectNameInfo = None +try: + OBJECT_HEADER.NameInfoOffset + getObjectNameInfo = getObjectNameInfoFromHeader +except TypeException: + getObjectNameInfo = getObjectNameInfoFromInfoMask + + +def getObjectName(p): + """ + Get object name by name information + """ + nameInfo = getObjectNameInfo(p) + if (None == nameInfo): + return "" + return loadUnicodeString(nameInfo.Name.getAddress()) + +def buildObjectName(p): + """ + Get object name by name information (parent directory handled) + """ + objectFullName = "" + + nameInfo = getObjectNameInfo(p) + if None == nameInfo: + return objectFullName + namePart = loadUnicodeString(nameInfo.Name.getAddress()) + if namePart != "\\": + objectFullName = namePart + "\\" + objectFullName + else: + objectFullName = "\\" + objectFullName + + while (0 != nameInfo.Directory): + p = nameInfo.Directory + nameInfo = getObjectNameInfo(p) + if (None == nameInfo): + return "...\\" + objectFullName + namePart = loadUnicodeString(nameInfo.Name.getAddress()) + if namePart != "\\": + objectFullName = namePart + "\\" + objectFullName + else: + objectFullName = "\\" + objectFullName + + return objectFullName + + +HANDLE_VALUE_INC = 4 +HT_PAGE_SIZE = 4096 +HT_ENTRY_SIZE = (2 * ptrSize()) +HT_LOWLEVEL_COUNT = HT_PAGE_SIZE // HT_ENTRY_SIZE +HT_MIDLEVEL_COUNT = HT_PAGE_SIZE // ptrSize() + +def getListByHandleTable(tableHandles=None, objTypeAddr=0, containHeaders=True): + """ + Build list of objects from target handle table + + Parameter objTypeAddr if not 0 used for getting object of specific type, + otherwise get object of all types + + Parameter containHeaders used to interpret table contents: + if containHeaders=True then table contains pointers to nt!_OBJECT_HEADER, + otherwise table contains pointers to objects + """ + + def getByHandleEntry(entryHandle, containHeader): + """ + Query object pointer by handle entry from handle table + """ + if (0 == entryHandle): + return 0 + + HandleEntry = typedVar( HANDLE_TABLE_ENTRY, entryHandle) + if (0xFFFFFFFE == HandleEntry.NextFreeTableEntry): + return 0 + + p = ptrPtr(HandleEntry.getAddress()) & 0xFFFFFFFFFFFFFFF8 + if (0 == p): + return 0 + + if (containHeader): + objHeader = typedVar( OBJECT_HEADER, p) + p = objHeader.Body.getAddress() + return p + + def getListByHandleTableL0(pTableContent, nMaxHandleIndex, objTypeAddr, containHeaders): + """ + Build list of objects from target handle table level 0 + """ + lstObjects = list() + nTableLevel0Count = nMaxHandleIndex // HANDLE_VALUE_INC + for HandleEntryIndex in range(nTableLevel0Count): + + pHandleEntry = pTableContent + (HT_ENTRY_SIZE * HandleEntryIndex) + p = getByHandleEntry(pHandleEntry, containHeaders) + if (0 == p): + continue + + if (0 == objTypeAddr): + lstObjects.append( ( p, HandleEntryIndex*HANDLE_VALUE_INC) ) + else: + pCurrentType = getType(p) + if (addr64(objTypeAddr) == addr64(pCurrentType)): + lstObjects.append( ( p, HandleEntryIndex*HANDLE_VALUE_INC) ) + + return lstObjects + + def getListByHandleTableL1(pTableContent, nMaxHandleIndex, objTypeAddr, containHeaders): + """ + Build list of objects from target handle table level 1 + """ + lstObjects = list() + nTableLevel1Count = (nMaxHandleIndex // HANDLE_VALUE_INC) // HT_LOWLEVEL_COUNT + for Index in range(nTableLevel1Count): + pTableLevel0 = ptrPtr(pTableContent + (Index * ptrSize())) + lstObjects += getListByHandleTableL0(pTableLevel0, HT_LOWLEVEL_COUNT * HANDLE_VALUE_INC, objTypeAddr, containHeaders) + + return lstObjects + + def getListByHandleTableL2(pTableContent, nMaxHandleIndex, objTypeAddr, containHeaders): + """ + Build list of objects from target handle table level 2 + """ + lstObjects = list() + nTableLevel2Count = ((nMaxHandleIndex // HANDLE_VALUE_INC) // HT_LOWLEVEL_COUNT) // HT_MIDLEVEL_COUNT + for Index in range(nTableLevel2Count): + pTableLevel1 = ptrPtr(pTableContent + (Index * ptrSize())) + lstObjects += getListByHandleTableL1(pTableLevel1, HT_MIDLEVEL_COUNT * HT_LOWLEVEL_COUNT * HANDLE_VALUE_INC, objTypeAddr, containHeaders) + + return lstObjects + + if (None == tableHandles): + currProcess = nt.typedVar("_EPROCESS", getCurrentProcess()) + if (None == currProcess): + dprintln("Get current process failed") + return + tableHandles = currProcess.ObjectTable + + tableHandles = nt.typedVar("_HANDLE_TABLE", tableHandles) + nMaxHandleIndex = tableHandles.NextHandleNeedingPool & 0xFFFFFFFF + nTableLevel = (tableHandles.TableCode & 3) + pTableContent = tableHandles.TableCode - nTableLevel + + if (0 == nTableLevel): + return getListByHandleTableL0(pTableContent, nMaxHandleIndex, objTypeAddr, containHeaders) + elif (1 == nTableLevel): + return getListByHandleTableL1(pTableContent, nMaxHandleIndex, objTypeAddr, containHeaders) + elif (2 == nTableLevel): + return getListByHandleTableL2(pTableContent, nMaxHandleIndex, objTypeAddr, containHeaders) + + dprintln("ERROR: Unknown handle table level: %u" % nTableLevel) + return list() + + + +NUMBER_HASH_BUCKETS = 37 + +def getListByDirectoryObject(p, objTypeAddr=0): + """ + Build list of objects from object directory + + Parameter objTypeAddr if not 0 used for getting object of specific type, + otherwise get object of all types + """ + + if getType(p) != ptrPtr( nt.ObpDirectoryObjectType ): + return None + + result = list() + + for i in range(0, NUMBER_HASH_BUCKETS): + bucket = ptrPtr( p + (i * ptrSize()) ) + while bucket: + bucketVar = typedVar( OBJECT_DIRECTORY_ENTRY, bucket) + if objTypeAddr and (getType(bucketVar.Object) == objTypeAddr): + result.append(bucketVar.Object) + elif (not objTypeAddr): + result.append(bucketVar.Object) + bucket = bucketVar.ChainLink + + return result + +def getObjectByName(name, caseSensitive=False): + """ + Query address of object by full name + """ + + def cmpCase(s1, s2): return s1 == s2 + def cmpNoCase(s1, s2): return s1.lower() == s2.lower() + + if not len(name): + return None + + if name[0] != '\\': + return None + + object = ptrPtr( nt.ObpRootDirectoryObject ) + + cmpFunc = cmpNoCase + if caseSensitive: + cmpFunc = cmpCase + + while True: + name = name[1:] + if not len(name): + break + + tok = name.find("\\") + if -1 != tok: + namePart = name[:tok] + name = name[tok:] + else: + namePart = name + + if 0 == len(namePart): + return None + + # FIXME: use name/index hash + lstObjects = getListByDirectoryObject(object) + if None == lstObjects: + return None + + found = False + for p in lstObjects: + objName = getObjectName(p) + if len(objName) and cmpFunc( namePart, objName ): + object = p + found = True + break + + if not found: + return None + + if -1 == tok: + break + + return object + + +import sys + +# Display object kd-command +ViewObjectCommand = { + addr64( expr("poi(nt!IoFileObjectType)") ) : "!fileobj", + addr64( expr("poi(nt!PsProcessType)") ) : "!process", + addr64( expr("poi(nt!PsThreadType)") ) : "!thread", + addr64( expr("poi(nt!IoDeviceObjectType)") ) : "!devobj", + addr64( expr("poi(nt!SeTokenObjectType)") ) : "!token", + # addr64( _kcbObjectType ) : "!reg kcb", + addr64( expr("poi(nt!IoDriverObjectType)") ) : "!drvobj 7" +} + +def main(): + """ + Print content of object table (handle table) + + Usage: + !py ntobj [table= ] [type= ] [headers= ] + + When (options can be specified in any order): + table= : table of handle address. F.e. address object table + contained in field ObjectTable structure nt!_EPROCESS + default: nt!_EPROCESS.ObjectTable of current processes + + type= : address of object type. If specified (!= 0) then print + object of target type only. + default: 0 + + headers= : table of handles format: contain pointer to object or to + object header (nt!_OBJECT_HEADER): True or Flase + F.e. poi(nt!PspCidTable) contains objects (processes and + threads), and nt!_EPROCESS.ObjectTable contains headers + default: True + Examples: + !py ntobj + Print object table of current process + + !py ntobj type= poi(nt!IoFileObjectType) + Print object table (only _FILE_OBJECT *) of current process + + !py ntobj table= poi(poi(nt!PsInitialSystemProcess)+@@C++(#FIELD_OFFSET(nt!_EPROCESS,ObjectTable))) + Print object table for SYSTEM process. + + !py ntobj table= poi(nt!PspCidTable) headers= False type= poi(nt!PsProcessType) + Print all process objects from nt!PspCidTable + """ + + if not isWindbgExt(): + print "Script is launch out of WinDBG" + return + + if not isKernelDebugging(): + dprintln("This script for kernel debugging only") + return + + argc = len(sys.argv) + + if (2 == argc) and ("help" == sys.argv[1] or "?" == sys.argv[1]): + dprintln(main.__doc__, True) + return + + if (0 == (argc % 2)): + dprintln("Invalid number of comand line arguments") + dprintln(main.__doc__, True) + return + + tableHandles = None + objTypeAddr = 0 + containHeaders = True + + for i in range(1, argc, 2): + if ("table=" == sys.argv[i]): + tableHandles = expr(sys.argv[i + 1]) + elif ("type=" == sys.argv[i]): + objTypeAddr = expr(sys.argv[i + 1]) + elif ("headers=" == sys.argv[i]): + containHeaders = ("True" == sys.argv[i + 1]) + else: + dprintln("Unknown option `" + sys.argv[i] + "'") + dprintln(main.__doc__, True) + return + + ObjectHandlePairs = [ (p[0], p[1]) for p in getListByHandleTable(tableHandles, objTypeAddr, containHeaders) ] + + dprintln("%u objects:" % len(ObjectHandlePairs)) + for objectHandle in ObjectHandlePairs: + objectType = getType(objectHandle[0]) + if objectType in ViewObjectCommand: + viewCommand = ViewObjectCommand[objectType] + else: + viewCommand = "!object" + + dprint("\t" % objectHandle[0] + viewCommand + " 0x%x" % objectHandle[0], True) + objectName = buildObjectName(objectHandle[0]) + if len(objectName): + dprint( ", name=`" + objectName + "'" ) + elif nt.typedVar("_OBJECT_TYPE", getType(objectHandle[0])).TypeInfo.QueryNameProcedure: + dprint(", custom name", True) + else: + dprint(" , <_unnamed_>") + + dprint(", !handle 0x%x\n" % (objectHandle[1], objectHandle[1]), True) + + dprint("\ttype is `" + getObjectName(objectType) + "' (0x%x)" % (objectType, objectType), True) + + dprintln( "\n" ) + + if (1 == argc): + dprintln("\nView help for ntobj", True) + +if __name__ == "__main__": + main()