from pykd import *
from optparse import OptionParser
from fnmatch import fnmatch

nt = None

class PrintOptions:
    def __init__(self):
        self.ignoreNotActiveThread = True
        self.ignoreNotActiveProcess = True

def applayThreadFilter( thread,moduleFilter):

    try:
        setImplicitThread(thread)
        
        stk = getStack()
        
        moduleLst = set()
        for frame in stk:
            m = module( frame.instructionOffset )
            if moduleFilter( m, m.name() ):
                moduleLst.add(m)
           
        if len(moduleLst)==0:
            return False

    except BaseException:
        return False
        
    return True        
    
        
def printThread(process,thread,printopt):

    try:
        setImplicitThread(thread)
        
        stk = getStack()
           
        dprintln( "Thread %x, Process: %s" % ( thread, loadCStr( process.ImageFileName ) ) )                 
        for frame in stk:
            dprintln( findSymbol( frame.instructionOffset ) )
            
        dprintln("")                
        
    except BaseException:
        if not printopt.ignoreNotActiveThread:
            dprintln( "Thread %x, Process: %s" % ( thread, loadCStr( process.ImageFileName ) ) )     
            dprintln( "Failed to switch into thread context\n")
            dprintln("")    

    
    
def printProcess(process,processFilter,moduleFilter,printopt):

    processName = loadCStr( process.ImageFileName )
     
    if not processFilter(process, process.UniqueProcessId, processName ):
        return
    
    try:
        setCurrentProcess(process)
        
        dbgCommand( ".reload /user" )
        
        threadLst = nt.typedVarList(process.ThreadListHead, "_ETHREAD", "ThreadListEntry")
        filteredThreadLst = []
        for thread in threadLst:
            if applayThreadFilter( thread, moduleFilter ):
                filteredThreadLst.append( thread )
                
        if filteredThreadLst == []:
            return        

        dprintln( "Process %x" %  process )
        dprintln( "Name: %s" %  processName )
        dprintln( "" )            

        for thread in filteredThreadLst:
            printThread(process,thread, printopt)        
        
    except BaseException:
        if not printopt.ignoreNotActiveProcess:
            dprintln( "Process %x" %  process )
            dprintln( "Name: %s" %  processName )
            dprintln( "Failed to switch into process context\n")
            dprintln( "" )      
        
    
def main():
    dprintln("Stack walker. ver 1.0")
    
    if not isKernelDebugging():
        dprintln("This script is only for kernel debugging")
        return
    
    global nt
    nt = module("nt")
  
    parser = OptionParser()
    parser.add_option("-p", "--process", dest="processfilter", 
        help="process filter: boolean expression with python syntax" )
    parser.add_option("-m", "--module", dest="modulefilter", 
        help="module filter: boolean expression with python syntax" )
    
    (options, args) = parser.parse_args()
     
    processFilter =  lambda process, pid, name: True
    moduleFilter = lambda module, name: True
     
    if options.processfilter:
        processFilter = lambda process, pid, name: eval( options.processfilter )
        
    if options.modulefilter:
        moduleFilter = lambda module, name: eval(options.modulefilter)
        
    printopt = PrintOptions()
           
    currentProcess = getCurrentProcess()
    currentThread = getImplicitThread()

    processLst = nt.typedVarList( nt.PsActiveProcessHead, "_EPROCESS", "ActiveProcessLinks")  
    for process in processLst:
        printProcess( process, processFilter, moduleFilter, printopt )  
            
    setCurrentProcess(currentProcess)
    setImplicitThread(currentThread)
        

if __name__ == "__main__":
    main()