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:
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
MmGetSystemRoutineAddress
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 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()