Skip to main content
added 4796 characters in body
Source Link
Peanut
  • 141
  • 6

I've written a smallan IDAPython script that searches for a string in the bytes preceding MmGetSystemRoutineAddress and FltGetRoutineAddress calls and then marks that call with the function being imported. It worked on the x64 and x86 drivers I had to hand but I've left my commented out debug prints in just in case.

Here's an example of the disassembly after running the script: output

And of the output:

Getting string xrefs
Trying to find MmGetSystemRoutineAddress...
Found MmGetSystemRoutineAddress @ 1C00092E0
Found 7 xrefs to MmGetSystemRoutineAddress

MmGetSystemRoutineAddress called @ 1C000B022 in func sub_1C000B000
    1C000B00D  lea     rdx, aPsgetversion; "PsGetVersion"
  Found an Xref to a string in preceding 50 bytes
  Searching for use of returned func pointer...
    1C000B022  call    cs:MmGetSystemRoutineAddress; PsGetVersion  - auto added
    1C000B028  lea     rdx, aWmitracemessag; "WmiTraceMessage"
    1C000B02F  mov     cs:PsGetVersion, rax
  Found mov to global data
  Found the pointer being stored! Marking it
  • Make sure that the MmGetSystemRoutineAddressdynamic importing function xref is an actual call
  • The function pointer result is often put into a global pointer (i.e. mov dword_444E0, eax), having this pointer automatically named would be very useful.The function pointer result is often put into a global pointer (i.e. mov dword_444E0, eax), having this pointer automatically named would be very useful.
  • The other common use case is seeing it being called straight after (i.e. call eax) so having this call marked would be nice.The other common use case is seeing it being called straight after (i.e. call eax) so having this call marked would be nice.
#Using Sark would have made the line iteration a bit nicer but I wanted 
#something I could use anywhere without any external dependencies

import idc
import idautils
import idaapi

print "Trying'''
Returns to- findString MmGetSystemRoutineAddress..xrefs."
 Dict of  
getsys_addrxref_location[tuple =of idc.LocByName("MmGetSystemRoutineAddress")
print "Found MmGetSystemRoutineAddress @str %08X"address, %str getsys_addrvalue)]
'''
num_xrefs = sum(1 for i indef idautils.CodeRefsToget_string_xrefs(getsys_addr, 0)):
print "Found %d xrefs to MmGetSystemRoutineAddress" % num_xrefs

#Get the strings so we can see what might have been passed in
    print "Getting string xrefs"
    sc = idautils.Strings(default_setup = False)
    # we want C & Unicode strings, and *only* existing strings.
    sc.setup(strtypes=Stringsstrtypes=(Strings.STR_C | Strings.STR_UNICODE, ignore_instructions = True), display_only_existing_strings = True)

#Make a list of all string locations
string_locs = []
for s in sc:
  ignore_instructions = string_locs.append((s.eaTrue, str(s)))
    #print "%x: len=%d type=%d -> '%s'" % (s.ea, s.length,display_only_existing_strings s.type,= str(s)True)

    #Make a dictlist of all places strings arestring Xrefslocations
string_xrefs = {}
for loc in string_locs:
    #print "%08X  %s" % (loc[0],= loc[1])[]
    for xrefs in idautils.XrefsTo(loc[0])sc:
        #print "Xref @ %08X" % xrefstring_locs.frm
        string_xrefs[xrefappend((s.frm] = loc
       ea, str(s)))
#Iterate through each Xref to MmGetSystemRoutineAddress and see if a#print string"%x: islen=%d usedtype=%d in-> the'%s'" preceding% instructions..(s.
for getsys_refea, ins.length, idautilss.CodeRefsTo(getsys_addrtype, 0str(s):
    print "\nMmGetSystemRoutineAddress called @ %08X" % getsys_ref)
    
    #Start#Make ata linedict aboveof MmGetSystemRoutineAddressall callplaces strings are Xrefs
    new_eastring_xrefs = idaapi.get_item_head{}
    for loc in string_locs:
        #print "%08X  %s" % (getsys_refloc[0], -loc[1])
 1       for xref in idautils.XrefsTo(loc[0]):
            #print "Xref @ %08X" % xref.frm
            string_xrefs[xref.frm] = loc
    
    #Continuereturn untilstring_xrefs
 we've gone back 50
'''
dynam_loading_func_name bytes- orThe foundfunction aused stringto dynamically load the functions
string_xrefs    while new_ea > getsys_ref     - 50:String xrefs. Dict of  
 xref_location[tuple of (str address, str value)]
'''
def markup_dynamically_loaded_funcs(dynam_loading_func_name, string_xrefs, printnum_search_bytes_string "= 50, num_search_bytes_use = %08X30):
  %s" % (new_ea,print idc"Trying to find %s.GetDisasm(new_ea)).." % dynam_loading_func_name
        
    getsys_addr = idc.LocByName(dynam_loading_func_name)
  #Go to theprint line"Found above
%s @ %08X" % (dynam_loading_func_name, getsys_addr)

   new_ea num_xrefs = idaapi.get_item_headsum(new_ea-1 for i in idautils.CodeRefsTo(getsys_addr, 0))
    print "Found %d xrefs to %s" % (num_xrefs, dynam_loading_func_name)
        
    #Iterate through each Xref to dynamic loading func and see if a string is used in the preceding instructions...
    for dynam_xref in idautils.CodeRefsTo(getsys_addr, 0):
        print "\n%s called @ %08X in func %s" % (dynam_loading_func_name, dynam_xref, idc.GetFunctionName(dynam_xref))
        
        #Start at line above dynamic loading func call
        new_ea = idaapi.get_item_head(dynam_xref - 1)
        
        #Continue until we've gone back 50 bytes or found a string
        while new_ea > dynam_xref - num_search_bytes_string:        
            #print "    %08X  %s" % (new_ea, idc.GetDisasm(new_ea))
            
            #Go to the line above
            new_ea = idaapi.get_item_head(new_ea-1)
            
            #Check if the address is an xref to a string
            if new_ea in string_xrefs:
                print "    %08X  %s" % (new_ea, idc.GetDisasm(new_ea))
                print "  Found an Xref to a string in preceding 50%d bytes" % num_search_bytes_string
                
                #Make the comment to add
                imported_func_name = string_xrefs[new_ea][1]
                comment = imported_func_name + "  - auto added"
                
                #Add the comment to the dynamic loading func call
                idc.MakeComm(dynam_xref, comment)
                
                print "  Searching for use of returned func pointer..."
                
                #Start at line past dynamic loading func, go forward one at a time (up to 30 bytes!) Marktill itwe up"find either:
                #  -  a call to rax or eax
                #  -  a mov of rax or eax into a global pointer
                ptr_search_addr = dynam_xref
                while ptr_search_addr < dynam_xref + num_search_bytes_use:
                    print "    %08X  %s" % (new_eaptr_search_addr, idc.GetDisasm(new_eaptr_search_addr))
                    ptr_search_addr += idaapi.get_item_size(ptr_search_addr)
                    
                    #Check for 'call rax' or 'call eax'
                    if idc.GetMnem(ptr_search_addr) == "call" and idc.GetOpnd(ptr_search_addr,0) in ["rax", "eax"]:
                        print "    %08X  %s" % (ptr_search_addr, idc.GetDisasm(ptr_search_addr))
                        print "  Found a call! Marking it"
                        
                        ##Comment it and stop checking for this string
                        idc.MakeComm(getsys_refptr_search_addr, string_xrefs[new_ea][1]comment)
 +                       break
                    
                    data_sec_start = idaapi.get_segm_by_name(".data").startEA
  - auto added"                data_sec_end = idaapi.get_segm_by_name(".data").endEA
                    
                    #Need to check for mov into global data, avoid situations like:
                    #call MmGetSystemRoutineAddress
                    #mov ecx, eax
                    #mov dword_12345, ecx
                    #Check for first 'mov' instruction and what it's moving to is in global data
                    if idc.GetMnem(ptr_search_addr) == "mov" and \
                    data_sec_start < GetOperandValue(ptr_search_addr, 0) < data_sec_end:
                        print "  Found mov to global data"
                        #print "    %08X" % GetOperandValue(ptr_search_addr, 0)
                        
                        print "  Found the pointer being stored! Marking it"
                        
                        ptr_addr = GetOperandValue(ptr_search_addr, 0)
                
                        #TODO: Validate address is in right section
                        
                        #Change the name, can't reuse names so keep trying till it works
                        count = 1
                        new_name = imported_func_name
                        while not idc.MakeNameEx(ptr_addr, new_name, SN_NOWARN):
                            new_name = imported_func_name + "_" + str(count)
                            print "%s is in use, using %s instead" % (imported_func_name, new_name)
                            count += 1
                        break
                else:
                    print "**************************************************"
                    print "*****                                        *****"
                    print "***** Didn't find a call or pointer storage! *****"
                    print "*****                                        *****"
                    print "**************************************************"
                
                
                #End the search for the string
                break
    
def main():
    string_xrefs = get_string_xrefs()
    
    markup_dynamically_loaded_funcs("MmGetSystemRoutineAddress", string_xrefs)
    print "\n\n\n\n\n\n\n\n\n\n\n\n\n"
    markup_dynamically_loaded_funcs("FltGetRoutineAddress", string_xrefs)
main()
        

I've written a small IDAPython script that searches for a string in the bytes preceding MmGetSystemRoutineAddress calls and then marks that call with the function being imported. It worked on the x64 and x86 drivers I had to hand but I've left my commented out debug prints in just in case.

  • Make sure that the MmGetSystemRoutineAddress xref is an actual call
  • The function pointer result is often put into a global pointer (i.e. mov dword_444E0, eax), having this pointer automatically named would be very useful.
  • The other common use case is seeing it being called straight after (i.e. call eax) so having this call marked would be nice.
#Using Sark would have made the line iteration a bit nicer but I wanted 
#something I could use anywhere without any external dependencies

import idc
import idautils
import idaapi

print "Trying to find MmGetSystemRoutineAddress..."
    
getsys_addr = idc.LocByName("MmGetSystemRoutineAddress")
print "Found MmGetSystemRoutineAddress @ %08X" % getsys_addr

num_xrefs = sum(1 for i in idautils.CodeRefsTo(getsys_addr, 0))
print "Found %d xrefs to MmGetSystemRoutineAddress" % num_xrefs

#Get the strings so we can see what might have been passed in
sc = idautils.Strings(default_setup = False)
# we want C & Unicode strings, and *only* existing strings.
sc.setup(strtypes=Strings.STR_C | Strings.STR_UNICODE, ignore_instructions = True, display_only_existing_strings = True)

#Make a list of all string locations
string_locs = []
for s in sc:
    string_locs.append((s.ea, str(s)))
    #print "%x: len=%d type=%d -> '%s'" % (s.ea, s.length, s.type, str(s))

#Make a dict of all places strings are Xrefs
string_xrefs = {}
for loc in string_locs:
    #print "%08X  %s" % (loc[0], loc[1])
    for xref in idautils.XrefsTo(loc[0]):
        #print "Xref @ %08X" % xref.frm
        string_xrefs[xref.frm] = loc
        
#Iterate through each Xref to MmGetSystemRoutineAddress and see if a string is used in the preceding instructions...
for getsys_ref in idautils.CodeRefsTo(getsys_addr, 0):
    print "\nMmGetSystemRoutineAddress called @ %08X" % getsys_ref
    
    #Start at line above MmGetSystemRoutineAddress call
    new_ea = idaapi.get_item_head(getsys_ref - 1)
    
    #Continue until we've gone back 50 bytes or found a string
    while new_ea > getsys_ref - 50:     
         print "    %08X  %s" % (new_ea, idc.GetDisasm(new_ea))
        
        #Go to the line above
        new_ea = idaapi.get_item_head(new_ea-1)
        
        if new_ea in string_xrefs:
            print "  Found an Xref to a string in preceding 50 bytes! Mark it up"
            print "    %08X  %s" % (new_ea, idc.GetDisasm(new_ea))
            idc.MakeComm(getsys_ref, string_xrefs[new_ea][1] + "  - auto added")
            break
    

I've written an IDAPython script that searches for a string in the bytes preceding MmGetSystemRoutineAddress and FltGetRoutineAddress calls and then marks that call with the function being imported. It worked on the x64 and x86 drivers I had to hand but I've left my commented out debug prints in just in case.

Here's an example of the disassembly after running the script: output

And of the output:

Getting string xrefs
Trying to find MmGetSystemRoutineAddress...
Found MmGetSystemRoutineAddress @ 1C00092E0
Found 7 xrefs to MmGetSystemRoutineAddress

MmGetSystemRoutineAddress called @ 1C000B022 in func sub_1C000B000
    1C000B00D  lea     rdx, aPsgetversion; "PsGetVersion"
  Found an Xref to a string in preceding 50 bytes
  Searching for use of returned func pointer...
    1C000B022  call    cs:MmGetSystemRoutineAddress; PsGetVersion  - auto added
    1C000B028  lea     rdx, aWmitracemessag; "WmiTraceMessage"
    1C000B02F  mov     cs:PsGetVersion, rax
  Found mov to global data
  Found the pointer being stored! Marking it
  • Make sure that the dynamic importing function xref is an actual call
  • The function pointer result is often put into a global pointer (i.e. mov dword_444E0, eax), having this pointer automatically named would be very useful.
  • The other common use case is seeing it being called straight after (i.e. call eax) so having this call marked would be nice.
#Using Sark would have made the line iteration a bit nicer but I wanted 
#something I could use anywhere without any external dependencies

import idc
import idautils
import idaapi

'''
Returns - String xrefs. Dict of  xref_location[tuple of (str address, str value)]
'''
def get_string_xrefs():
    #Get the strings so we can see what might have been passed in
    print "Getting string xrefs"
    sc = idautils.Strings(default_setup = False)
    # we want C & Unicode strings, and *only* existing strings.
    sc.setup(strtypes=(Strings.STR_C | Strings.STR_UNICODE), 
            ignore_instructions = True, 
            display_only_existing_strings = True)

    #Make a list of all string locations
    string_locs = []
    for s in sc:
        string_locs.append((s.ea, str(s)))
        #print "%x: len=%d type=%d -> '%s'" % (s.ea, s.length, s.type, str(s))
    
    #Make a dict of all places strings are Xrefs
    string_xrefs = {}
    for loc in string_locs:
        #print "%08X  %s" % (loc[0], loc[1])
        for xref in idautils.XrefsTo(loc[0]):
            #print "Xref @ %08X" % xref.frm
            string_xrefs[xref.frm] = loc
    
    return string_xrefs
    
'''
dynam_loading_func_name - The function used to dynamically load the functions
string_xrefs            - String xrefs. Dict of  xref_location[tuple of (str address, str value)]
'''
def markup_dynamically_loaded_funcs(dynam_loading_func_name, string_xrefs, num_search_bytes_string = 50, num_search_bytes_use = 30):
    print "Trying to find %s..." % dynam_loading_func_name
        
    getsys_addr = idc.LocByName(dynam_loading_func_name)
    print "Found %s @ %08X" % (dynam_loading_func_name, getsys_addr)

    num_xrefs = sum(1 for i in idautils.CodeRefsTo(getsys_addr, 0))
    print "Found %d xrefs to %s" % (num_xrefs, dynam_loading_func_name)
        
    #Iterate through each Xref to dynamic loading func and see if a string is used in the preceding instructions...
    for dynam_xref in idautils.CodeRefsTo(getsys_addr, 0):
        print "\n%s called @ %08X in func %s" % (dynam_loading_func_name, dynam_xref, idc.GetFunctionName(dynam_xref))
        
        #Start at line above dynamic loading func call
        new_ea = idaapi.get_item_head(dynam_xref - 1)
        
        #Continue until we've gone back 50 bytes or found a string
        while new_ea > dynam_xref - num_search_bytes_string:        
            #print "    %08X  %s" % (new_ea, idc.GetDisasm(new_ea))
            
            #Go to the line above
            new_ea = idaapi.get_item_head(new_ea-1)
            
            #Check if the address is an xref to a string
            if new_ea in string_xrefs:
                print "    %08X  %s" % (new_ea, idc.GetDisasm(new_ea))
                print "  Found an Xref to a string in preceding %d bytes" % num_search_bytes_string
                
                #Make the comment to add
                imported_func_name = string_xrefs[new_ea][1]
                comment = imported_func_name + "  - auto added"
                
                #Add the comment to the dynamic loading func call
                idc.MakeComm(dynam_xref, comment)
                
                print "  Searching for use of returned func pointer..."
                
                #Start at line past dynamic loading func, go forward one at a time (up to 30 bytes) till we find either:
                #  -  a call to rax or eax
                #  -  a mov of rax or eax into a global pointer
                ptr_search_addr = dynam_xref
                while ptr_search_addr < dynam_xref + num_search_bytes_use:
                    print "    %08X  %s" % (ptr_search_addr, idc.GetDisasm(ptr_search_addr))
                    ptr_search_addr += idaapi.get_item_size(ptr_search_addr)
                    
                    #Check for 'call rax' or 'call eax'
                    if idc.GetMnem(ptr_search_addr) == "call" and idc.GetOpnd(ptr_search_addr,0) in ["rax", "eax"]:
                        print "    %08X  %s" % (ptr_search_addr, idc.GetDisasm(ptr_search_addr))
                        print "  Found a call! Marking it"
                        
                        ##Comment it and stop checking for this string
                        idc.MakeComm(ptr_search_addr, comment)
                        break
                    
                    data_sec_start = idaapi.get_segm_by_name(".data").startEA
                    data_sec_end = idaapi.get_segm_by_name(".data").endEA
                    
                    #Need to check for mov into global data, avoid situations like:
                    #call MmGetSystemRoutineAddress
                    #mov ecx, eax
                    #mov dword_12345, ecx
                    #Check for first 'mov' instruction and what it's moving to is in global data
                    if idc.GetMnem(ptr_search_addr) == "mov" and \
                    data_sec_start < GetOperandValue(ptr_search_addr, 0) < data_sec_end:
                        print "  Found mov to global data"
                        #print "    %08X" % GetOperandValue(ptr_search_addr, 0)
                        
                        print "  Found the pointer being stored! Marking it"
                        
                        ptr_addr = GetOperandValue(ptr_search_addr, 0)
                
                        #TODO: Validate address is in right section
                        
                        #Change the name, can't reuse names so keep trying till it works
                        count = 1
                        new_name = imported_func_name
                        while not idc.MakeNameEx(ptr_addr, new_name, SN_NOWARN):
                            new_name = imported_func_name + "_" + str(count)
                            print "%s is in use, using %s instead" % (imported_func_name, new_name)
                            count += 1
                        break
                else:
                    print "**************************************************"
                    print "*****                                        *****"
                    print "***** Didn't find a call or pointer storage! *****"
                    print "*****                                        *****"
                    print "**************************************************"
                
                
                #End the search for the string
                break
    
def main():
    string_xrefs = get_string_xrefs()
    
    markup_dynamically_loaded_funcs("MmGetSystemRoutineAddress", string_xrefs)
    print "\n\n\n\n\n\n\n\n\n\n\n\n\n"
    markup_dynamically_loaded_funcs("FltGetRoutineAddress", string_xrefs)
main()
        
Source Link
Peanut
  • 141
  • 6

I've written a small IDAPython script that searches for a string in the bytes preceding MmGetSystemRoutineAddress calls and then marks that call with the function being imported. It worked on the x64 and x86 drivers I had to hand but I've left my commented out debug prints in just in case.

There's definitely improvements that could be made:

  • Make sure that the MmGetSystemRoutineAddress xref is an actual call
  • The function pointer result is often put into a global pointer (i.e. mov dword_444E0, eax), having this pointer automatically named would be very useful.
  • The other common use case is seeing it being called straight after (i.e. call eax) so having this call marked would be nice.

I'll have a look at making these improvements and updating when I can.

#Using Sark would have made the line iteration a bit nicer but I wanted 
#something I could use anywhere without any external dependencies

import idc
import idautils
import idaapi

print "Trying to find MmGetSystemRoutineAddress..."
    
getsys_addr = idc.LocByName("MmGetSystemRoutineAddress")
print "Found MmGetSystemRoutineAddress @ %08X" % getsys_addr

num_xrefs = sum(1 for i in idautils.CodeRefsTo(getsys_addr, 0))
print "Found %d xrefs to MmGetSystemRoutineAddress" % num_xrefs

#Get the strings so we can see what might have been passed in
sc = idautils.Strings(default_setup = False)
# we want C & Unicode strings, and *only* existing strings.
sc.setup(strtypes=Strings.STR_C | Strings.STR_UNICODE, ignore_instructions = True, display_only_existing_strings = True)

#Make a list of all string locations
string_locs = []
for s in sc:
    string_locs.append((s.ea, str(s)))
    #print "%x: len=%d type=%d -> '%s'" % (s.ea, s.length, s.type, str(s))

#Make a dict of all places strings are Xrefs
string_xrefs = {}
for loc in string_locs:
    #print "%08X  %s" % (loc[0], loc[1])
    for xref in idautils.XrefsTo(loc[0]):
        #print "Xref @ %08X" % xref.frm
        string_xrefs[xref.frm] = loc
        
#Iterate through each Xref to MmGetSystemRoutineAddress and see if a string is used in the preceding instructions...
for getsys_ref in idautils.CodeRefsTo(getsys_addr, 0):
    print "\nMmGetSystemRoutineAddress called @ %08X" % getsys_ref
    
    #Start at line above MmGetSystemRoutineAddress call
    new_ea = idaapi.get_item_head(getsys_ref - 1)
    
    #Continue until we've gone back 50 bytes or found a string
    while new_ea > getsys_ref - 50:     
        print "    %08X  %s" % (new_ea, idc.GetDisasm(new_ea))
        
        #Go to the line above
        new_ea = idaapi.get_item_head(new_ea-1)
        
        if new_ea in string_xrefs:
            print "  Found an Xref to a string in preceding 50 bytes! Mark it up"
            print "    %08X  %s" % (new_ea, idc.GetDisasm(new_ea))
            idc.MakeComm(getsys_ref, string_xrefs[new_ea][1] + "  - auto added")
            break