#Find dynamically resolved IAT locations and apply labels from input file
#@author https://AGDCServices.com
#@category AGDCservices
#@keybinding
#@menupath
#@toolbar
#@toolbar

'''
Script will search program for all dynamically resolved
imports and label them with the appropriate API name pulled
from a provided labeled IAT dump file.  Only resolved imports
stored in global variables will be identified. This script will
not label every resolved global variable, but only those that 
are used inside a call instruction

The labeled IAT dump file must be generated by an associated
program, "Dump_Labeled_Iat_Memory.exe". This program is located
in another repo on this github site called "Misc Malware Anaysis Tools"

usage:
    Run file inside a debugger up to the point where all 
    dynamically resolved imports are resolved.  At that point,
    run the associated "Dump_Labeled_Iat_Memory.exe" to create
    the labeled Iat dump file.
    
    Once you have the labeled IAT dump file, run this script.
    The script must be run prior to renaming any of the global
    IAT variables.  The script will not overwrite any manually
    named global variables.

'''


def main():

    try:
        fileObject = askFile('Select Labeled Iat Dump File', 'Open')
    except:
        print('file could not be opened')
        quit()

    iatList = Get_Dynamically_Resolved_Iat_Addresses()
    Label_Dynamically_Resolved_Iat_Addresses(iatList, fileObject.getPath())
    
    
def Get_Dynamically_Resolved_Iat_Addresses():
    '''
    function will search current program for all 
    calls to unresolved global variables and return
    a list of all the global variable addresses.
    '''
    
    instructions = currentProgram.getListing().getInstructions(True)
    iatSet = set()
    for curInstr in instructions:
        curMnem = curInstr.getMnemonicString().lower()
        if curMnem == 'call':
            operandRef = curInstr.getOperandReferences(0)
            if len(operandRef) != 0:
                operandRefEa = operandRef[0].getToAddress()
                curLabel = getSymbolAt(operandRefEa)
                if curLabel != None:  # accounts for non memory references
                    if curLabel.getName().lower().startswith( ('dat_', 'byte_', 'word_', 'dword_', 'qword_') ):
                        iatSet.add(operandRefEa)
                        
    
    return list(iatSet)

        
def Label_Dynamically_Resolved_Iat_Addresses(iatList, labeledIatDumpFileName):
    '''
    function will read in file with a format of:
    iatRva\tapiString 
    each address in the iatList will be checked to 
    see if there is an entry in the labeled Iat Dump File.
    If so, the iat label will be set to the api string 
    from the input file
    
    iatList should be list of address objects
    '''
    
    with open(labeledIatDumpFileName, 'r') as fp:
        labeledIatList = fp.read().splitlines()
    
    imageBase = currentProgram.getImageBase().getOffset()
    labeledIatDict = dict()
    for i in labeledIatList:
        curRva, curIatLabel = i.split('\t')
        labeledIatDict[imageBase + int(curRva, 16)] = curIatLabel
    
    labeledCount = 0
    unresolvedList = []
    for entry in iatList:
        curIatLabel = labeledIatDict.get(entry.getOffset(), None)
        if curIatLabel != None:
            getSymbolAt(entry).setName(curIatLabel, ghidra.program.model.symbol.SourceType.USER_DEFINED)
            labeledCount += 1
        else:
            unresolvedList.append('could not resolve address 0x{:x}'.format(entry.getOffset()))
    
    print('labeled {:x} dynamically resolved IAT entries'.format(labeledCount))
    
    if len(unresolvedList) != 0:
        print('[*] ERROR, was not able to resolve {:x} entries'.format(len(unresolvedList)))
        print('\n'.join(unresolvedList))    
 
            
            
            
            
if __name__ == '__main__':
    main()