Driver Inspector

Heyhey!

I’ve been on windows drivers vulnerabilites a lot lately and i’ve seen that estadistically, as in usermode, there are a lot of driver developers who doesnt know how to implement a secure intercommunication channel between usermode and kernel mode when handling IOCTLs.

That is way worst when handling a particular kind of communication method called METHOD_NEITHER.

After disassembling a lot of different drivers repeating the same procedure over and over again (always looking for the same things; the driver entry procedure, the MajorFunction assignation, the ioctl_handler function..) i saw that usually it has an almost-signaturable code pattern that can be used to do an automated analysis of the disassembled binary.
So, i thought that could be very useful to have a tool to help me in the analysis, looking for code patterns to recognize where the functions used to handle the ioctls are, to identify the communication method, and maybe to find some user buffers reference (i know im the lazyest :) .

And of course, IDAPython came to my mind.

After a quick look of the excelent Ero’s IDAPython documentation to refresh my memory i started to write the script and a few hours later i managed to get an almost usable piece of code to find the ioctl handler and the method used.

It does a crappy binary analysis looking for some code patterns to find the ioctl_handler. Then, it looks for the most common asm representations of a “switch-case” sentence including an ioctl-alike dword :D
By applying some simple bitwise operations we can determine the METHOD used in the defined IOCTL, allowing the script to know what pattern must be used to find the code who i’m interested on, the one who references the buffer supplied by the user.
I’ve writed just the minimum to find the user buffer reference for the METHOD_NEITHER method, but should be trivial to do the same for the other methods. I will do it later :D

Now, ive cleaned the code a little, deprecated the original “find_ioctl_handler_heur” function that was replaced for another one who recognizes the ioctl function handler by it reference at Driver_Entry(), and im thinking in adding more functionality, but the code will be here “as is” since now.

oh, and it has a name now: Driver Inspector

This is just a quick draft, just to test some IDAPython features and see if i’m in the right way.

#
# Driver Inspector - An IDAPython script to help the analysis of windows drivers
# Copyright (C) 2007 Anibal L. Sacco <anibal.sacco@coresecurity.com>
#

'''
Author:       Anibal Sacco (aLS)
Contact:      anibal.sacco@coresecurity.com
Organization: Core Security Technologies
'''

log_file = 0

def Log(text):
    global log_file
    print text
    if log_file:
        file = open(log_file, "a")
        file.write(text + "\r\n")
        file.close()

def ask_for_log():
    global log_file
    choose = AskYN(1, "Do you wanna save a Log?")
    if choose:
        log_file = AskFile(1, ("log-%s.log" % GetInputFile()), "Where do you want to save the log file?" )
        if log_file:
            return True
    print "[+] Not logging"
    return False

def analyze_ioctl_handler(function_ea):
    function_end   = FindFuncEnd(function_ea)
    matches = []
    method = 0xff

    for head in Heads(function_ea, function_end):
        if isCode(GetFlags(head)):
            code = "%s %s %s" % (GetMnem(head),GetOpnd(head,0),GetOpnd(head,1) )
            if ("sub" in code) or ("cmp" in code) and (GetOpType(head, 1) == 5) and (GetOperandValue(head, 1) > 0xffff):
                is_an_ioctl_func = True                  # If the function has a possible ioctl it's flagged
                Log("[+] Possible IOCTL switch-case in function %s at %08x" % (GetFunctionName(function_ea), head))
                ioctl = GetOperandValue(head, 1)
                method = ioctl & 3
                if method == 3:
                    Log("[-] Method: METHOD_NEITHER")
                elif method == 2:
                    Log("[-] Method: METHOD_OUT_DIRECT")
                elif method == 1:
                    Log("[-] Method: METHOD_IN_DIRECT")
                elif method == 0:
                    Log("[-] Method: METHOD_BUFFERED")

            if method == 3:                 # If the function was flagged, find on it some reference to the user buffer
                if GetOpType(head, 1) == 5:
                    if ("3Ch" in GetOpnd(head, 1)) or ("3Ch" in GetOpnd(head, 2)):
                        Log("[-] Possible user buffer reference in function %s at %08x" % (GetFunctionName(function_ea),head))

##            if method == 0:
##                f

    return matches

def find_ioctl_by_xref():
    for head in Heads(Segments()[0], Segments()[-1:][0]):
        if isCode(GetFlags(head)):
            for ref_ea in DataRefsTo(head):
                opnd = GetOpnd(ref_ea, 0)
                if "+70h]" in opnd:
                    MakeFunction(head, BADADDR)
                    print "FUNCTION MADE!"                      # Debug
                    return head
    return False

def has_xref_majorversion(function_ea):
    for ref_ea in DataRefsTo(function_ea):
        opnd = GetOpnd(ref_ea, 0)
        if "+70h]" in opnd:
            print "XREF from: %s" % GetFunctionName(ref_ea)
            return True
        return False

def find_opcode_in_func(function_ea, opcode):

    function_start = function_ea
    function_end   = FindFuncEnd(function_ea) - function_ea
    matches = []

    for head in Heads(function_ea, function_end):
        if isCode(GetFlags(head)):
            code = "%s %s %s" % (GetMnem(head),GetOpnd(head,0),GetOpnd(head,1) )
            for each in opcode:
                if each in code:
                    matches.append(head)
    return matches

def find_ioctl_handler_heur(function_ea):

    function_start = function_ea
    function_end   = FindFuncEnd(function_ea) - function_ea
    magic_delta    = 0x70
    has_irp = 0
    has_iosl = 0
    has_ioctl = 0

    for head in Heads(function_ea, FindFuncEnd(function_ea)):
        if isCode(GetFlags(head)):
##                code = "%s %s %s" % (GetMnem(head),GetOpnd(head,0),GetOpnd(head,1) )
            if ("Irp" in GetOpnd(head,1)) and (head < (function_ea + magic_delta)):        # Finding Irp as an argument
                has_irp = function_ea
            if ("+60]" in GetOpnd(head,1)) and (head < (function_ea + magic_delta)):       # Finding IO_STACK_LOCATION
                has_iosl = function_ea
            if ("+0Ch]" in GetOpnd(head,1)) and (head < (function_ea + magic_delta)) :      # Finding IoControlCode
                has_ioctl = function_ea

    if has_irp or (has_ioctl and has_iosl):
        return True
##        print "[+] Possible IOCTL Handler at %08x" % function_ea
    return None

def main():
    splash = """
    ##################################################################################
    # Driver Inspector - An IDAPython script to help the analysis of windows drivers #
    # Author:       Anibal Sacco (aLS)                                               #
    # Contact:      anibal.sacco@coresecurity.com                                    #
    # Organization: Core Security Technologies                                       #
    ----------------------------------------------------------------------------------
    """

    possible_functions = []

    print "------------------------------------------------------"
    print "[+] Starting Driver Analysys"

    Wait()                                  # Get the text segment starting address
    ask_for_log()
    Log(splash)

    ioctl_handler = find_ioctl_by_xref()    # Try to get the ioctl_handler by it xref
    if not ioctl_handler:
        for ea in Segments():
            #print "Analyzing %s segment" % SegName(ea)
            for function_ea in Functions(SegStart(ea), SegEnd(ea)):
                if find_ioctl_handler_heur(function_ea) == True:                  # Try to identify it looking at the code
                    possible_functions.append(function_ea)
                if has_xref_majorversion(function_ea) == True:                    # Try to identify it by its xref
                    ioctl_handler = function_ea
                    break

    if ioctl_handler:
        Log("[+] IOCTL Handler found at %08x" % ioctl_handler)
        analyze_ioctl_handler(ioctl_handler)
    else:
        if possible_functions:
            print "[-] There are a few possible functions used as ioctl_handlers at:"
            for function in possible_functions: print "%08x" % function  # Let the user choose the function to be used
        else:
            print "[-] Didn't find any function, sorry."

    # *Look if there are more than one possible function and in that case,
    # let the user select one.
    # *Analyze that function finding the METHOD and the buffers

    return 0

if __name__ == '__main__':
    main()

~ by aLS -- on March 22, 2007.

Leave a Reply