Last active
July 4, 2019 19:52
-
-
Save MarkBaggett/1a168c026746a627e6abae2141052742 to your computer and use it in GitHub Desktop.
Python Windows DLLs finding and calling
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import pefile | |
import sys | |
import ctypes | |
import glob | |
import argparse | |
import itertools | |
def search_tables(thefile, pename, search = []): | |
if hasattr(thefile, "DIRECTORY_ENTRY_IMPORT"): | |
if args.verbose or args.dump: | |
print("Processing Import Table...") | |
for dllimport in thefile.DIRECTORY_ENTRY_IMPORT: | |
fname = dllimport.dll.decode() | |
for eachfunc in dllimport.imports: | |
if args.dump: | |
name = eachfunc.name if eachfunc.name else b"BLANK" | |
print("{0: >} in {1} at offset 0x{2:0>16X}".format(name.decode(),fname,eachfunc.address )) | |
continue | |
for eachterm in search: | |
if args.inexact: | |
if eachfunc.name and (eachterm in eachfunc.name.decode().lower()) or not search: | |
print("{} uses {} in dll {}".format(pename, eachfunc.name, fname)) | |
else: | |
if eachfunc.name and eachterm == eachfunc.name.decode(): | |
print("{} uses {} in dll {}".format(pename, eachfunc.name, fname)) | |
#else: | |
# print("{} not in {}".format(eachterm, eachfunc.name) | |
if hasattr(thefile, "DIRECTORY_ENTRY_EXPORT"): | |
if args.verbose or args.dump: | |
print("Processing the Export Table...") | |
for symbol in thefile.DIRECTORY_ENTRY_EXPORT.symbols: | |
fname = thefile.DIRECTORY_ENTRY_EXPORT.name.decode() | |
if args.dump: | |
name = symbol.name if symbol.name else b"BLANK" | |
print("{0} in {1} at offset 0x{2:0>16X} in {}".format(name.decode(), fname, symbol.address_offset)) | |
continue | |
for eachterm in search: | |
if symbol.name: | |
if args.inexact: | |
if symbol.name and (eachterm in symbol.name.decode().lower()) or not search: | |
print("{0} exports {1} at 0x{2:0>16X} ".format(pename, symbol.name, symbol.address_offset)) | |
else: | |
if symbol.name and eachterm == symbol.name.decode(): | |
print("{0} exports {1} at 0x{2:0>16X}".format(pename, symbol.name, symbol.address_offset)) | |
def bits2flag(flags,byte_value): | |
bits_array = map(int, format(byte_value,"016b")) | |
return "|".join(itertools.compress(flags[::-1],bits_array)) | |
search = ["printf", "gets", "lstrcpyW"] | |
dll_characteristics = ['reserved0', 'reserved1', 'reserved2', 'reserved3', 'undefined1', 'High_Entropy_VA', 'Dynamic_Base', 'Force_Integrity', 'NX_Compat', 'No_isolation', 'No_SEH', 'No_bind', 'App_Container', 'WDM_Driver', 'Guard_CF', 'Terminal_Server_Aware'] | |
if __name__ == "__main__": | |
parser = argparse.ArgumentParser() | |
parser.add_argument("-d", "--dump", action= "store_true", help = "Dump all information about the pe. No filters or search applied.") | |
parser.add_argument("-a","--add", nargs='*', help = "In addition to defaults also search for these functions being used by the exectuables. The function names are separated by spaces and terminated with two dashes. Example: -a modulea moduleb --") | |
parser.add_argument("-o","--only", nargs='*', help = "Only search for these functions being used by the executables. The function names are separated by spaces and terminated with two dashes. Example: -e modulea moduleb --") | |
parser.add_argument("-i","--inexact", action="store_true", help = "Ignore case sensitive and allow substring match of function names rather than exact names.") | |
parser.add_argument("-f","--flags", action="store_true", help = "Display the DLL Characteristic flags.") | |
parser.add_argument("-v","--verbose", action="store_true", help = "MAKE SOME NOISE UP IN HERE!!!") | |
parser.add_argument("-t", "--toggle", nargs="*", choices = dll_characteristics, help = "A list of Header Characteristics bit to toggle. Flags are spece separated and end with two dashes. Example --toggle NO_SEH Guard_CF --") | |
parser.add_argument("pefiles", help= "The name of an EXE or DLL is a required argument. Wildcards are supported.") | |
args = parser.parse_args() | |
pefiles = glob.glob(args.pefiles) | |
if len(pefiles)==0: | |
print("No files found.") | |
sys.exit(1) | |
if args.add: | |
search.extend(args.add) | |
if args.only: | |
search = args.only | |
for pe in pefiles: | |
try: | |
thefile = pefile.PE(pe) | |
except Exception as e: | |
print("Unable to open file {}".format(str(e))) | |
continue | |
if args.dump or args.flags: | |
file_options = thefile.OPTIONAL_HEADER.DllCharacteristics | |
print("{} flags : {}".format(pe, bits2flag(dll_characteristics, file_options))) | |
if args.toggle: | |
current_flags = thefile.OPTIONAL_HEADER.DllCharacteristics | |
bitsmask = 0 | |
for eachflag in args.toggle: | |
bitsmask = bitsmask ^ (2**dll_characteristics.index(eachflag)) | |
thefile.OPTIONAL_HEADER.DllCharacteristics = current_flags ^ bitsmask | |
thefile.write(filename = "{}.new".format(pe)) | |
print("Flags Changes written to {}.new".format(pe)) | |
if args.verbose: | |
print("Searching EXE {} for vulnerable functions {}".format(pe, str(search))) | |
search_tables(thefile,pe, search) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import pefile | |
import sys | |
import ctypes | |
import glob | |
import argparse | |
import itertools | |
def search_tables(thefile, pename, search = []): | |
if hasattr(thefile, "DIRECTORY_ENTRY_IMPORT"): | |
if args.verbose or args.dump: | |
print("Processing Import Table...") | |
for dllimport in thefile.DIRECTORY_ENTRY_IMPORT: | |
fname = dllimport.dll.decode() | |
for eachfunc in dllimport.imports: | |
if args.dump: | |
name = eachfunc.name if eachfunc.name else b"BLANK" | |
print("{0: >} in {1} at offset 0x{2:0>16X}".format(name.decode(),fname,eachfunc.address )) | |
continue | |
for eachterm in search: | |
if args.inexact: | |
if eachfunc.name and (eachterm in eachfunc.name.decode().lower()) or not search: | |
print("{} uses {} in dll {}".format(pename, eachfunc.name, fname)) | |
else: | |
if eachfunc.name and eachterm == eachfunc.name.decode(): | |
print("{} uses {} in dll {}".format(pename, eachfunc.name, fname)) | |
#else: | |
# print("{} not in {}".format(eachterm, eachfunc.name) | |
if hasattr(thefile, "DIRECTORY_ENTRY_EXPORT"): | |
if args.verbose or args.dump: | |
print("Processing the Export Table...") | |
for symbol in thefile.DIRECTORY_ENTRY_EXPORT.symbols: | |
fname = thefile.DIRECTORY_ENTRY_EXPORT.name.decode() | |
if args.dump: | |
name = symbol.name if symbol.name else b"BLANK" | |
print("{0} in {1} at offset 0x{2:0>16X} in {}".format(name.decode(), fname, symbol.address_offset)) | |
continue | |
for eachterm in search: | |
if symbol.name: | |
if args.inexact: | |
if symbol.name and (eachterm in symbol.name.decode().lower()) or not search: | |
print("{0} exports {1} at 0x{2:0>16X} ".format(pename, symbol.name, symbol.address_offset)) | |
else: | |
if symbol.name and eachterm == symbol.name.decode(): | |
print("{0} exports {1} at 0x{2:0>16X}".format(pename, symbol.name, symbol.address_offset)) | |
def bits2flag(flags,byte_value): | |
bits_array = map(int, format(byte_value,"016b")) | |
return "|".join(itertools.compress(flags[::-1],bits_array)) | |
search = ["printf", "gets", "lstrcpyW"] | |
dll_characteristics = ['reserved0', 'reserved1', 'reserved2', 'reserved3', 'undefined1', 'High_Entropy_VA', 'Dynamic_Base', 'Force_Integrity', 'NX_Compat', 'No_isolation', 'No_SEH', 'No_bind', 'App_Container', 'WDM_Driver', 'Guard_CF', 'Terminal_Server_Aware'] | |
if __name__ == "__main__": | |
parser = argparse.ArgumentParser() | |
parser.add_argument("-d", "--dump", action= "store_true", help = "Dump all information about the pe. No filters or search applied.") | |
parser.add_argument("-a","--add", nargs='*', help = "In addition to defaults also search for these functions being used by the exectuables. The function names are separated by spaces and terminated with two dashes. Example: -a modulea moduleb --") | |
parser.add_argument("-o","--only", nargs='*', help = "Only search for these functions being used by the executables. The function names are separated by spaces and terminated with two dashes. Example: -e modulea moduleb --") | |
parser.add_argument("-i","--inexact", action="store_true", help = "Ignore case sensitive and allow substring match of function names rather than exact names.") | |
parser.add_argument("-f","--flags", action="store_true", help = "Display the DLL Characteristic flags.") | |
parser.add_argument("-v","--verbose", action="store_true", help = "MAKE SOME NOISE UP IN HERE!!!") | |
parser.add_argument("-t", "--toggle", choices = dll_characteristics, help = "The name of a single PE DLL Characteristics bit to toggle.") | |
parser.add_argument("pefiles", help= "The name of an EXE or DLL is a required argument. Wildcards are supported.") | |
args = parser.parse_args() | |
pefiles = glob.glob(args.pefiles) | |
if len(pefiles)==0: | |
print("No files found.") | |
sys.exit(1) | |
if args.add: | |
search.extend(args.add) | |
if args.only: | |
search = args.only | |
for pe in pefiles: | |
try: | |
thefile = pefile.PE(pe) | |
except Exception as e: | |
print("Unable to open file {}".format(str(e))) | |
continue | |
if args.dump or args.flags: | |
file_options = thefile.OPTIONAL_HEADER.DllCharacteristics | |
print("{} flags : {}".format(pe, bits2flag(dll_characteristics, file_options))) | |
if args.toggle: | |
current_flags = thefile.OPTIONAL_HEADER.DllCharacteristics | |
bitsmask = 2**dll_characteristics.index(args.toggle) | |
thefile.OPTIONAL_HEADER.DllCharacteristics = current_flags ^ bitsmask | |
thefile.write(filename = "{}.new".format(pe)) | |
print("Flags Changes written to {}.new".format(pe)) | |
if args.verbose: | |
print("Searching EXE {} for vulnerable functions {}".format(pe, str(search))) | |
search_tables(thefile,pe, search) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import pefile | |
import sys | |
import ctypes | |
import glob | |
import argparse | |
import itertools | |
def search_tables(thefile, pename, search = []): | |
if hasattr(thefile, "DIRECTORY_ENTRY_IMPORT"): | |
if args.verbose or args.dump: | |
print("Processing Import Table...") | |
for dllimport in thefile.DIRECTORY_ENTRY_IMPORT: | |
fname = dllimport.dll.decode() | |
for eachfunc in dllimport.imports: | |
if args.dump: | |
name = eachfunc.name if eachfunc.name else b"BLANK" | |
print("{0: >} in {1} at offset 0x{2:0>16X}".format(name.decode(),fname,eachfunc.address )) | |
continue | |
for eachterm in search: | |
if args.inexact: | |
if eachfunc.name and (eachterm in eachfunc.name.decode().lower()) or not search: | |
print("{} uses {} in dll {}".format(pename, eachfunc.name, fname)) | |
else: | |
if eachfunc.name and eachterm == eachfunc.name.decode(): | |
print("{} uses {} in dll {}".format(pename, eachfunc.name, fname)) | |
#else: | |
# print("{} not in {}".format(eachterm, eachfunc.name) | |
if hasattr(thefile, "DIRECTORY_ENTRY_EXPORT"): | |
if args.verbose or args.dump: | |
print("Processing the Export Table...") | |
for symbol in thefile.DIRECTORY_ENTRY_EXPORT.symbols: | |
fname = thefile.DIRECTORY_ENTRY_EXPORT.name.decode() | |
if args.dump: | |
name = symbol.name if symbol.name else b"BLANK" | |
print("{0} in {1} at offset 0x{2:0>16X} in {}".format(name.decode(), fname, symbol.address_offset)) | |
continue | |
for eachterm in search: | |
if symbol.name: | |
if args.inexact: | |
if symbol.name and (eachterm in symbol.name.decode().lower()) or not search: | |
print("{0} exports {1} at 0x{2:0>16X} ".format(pename, symbol.name, symbol.address_offset)) | |
else: | |
if symbol.name and eachterm == symbol.name.decode(): | |
print("{0} exports {1} at 0x{2:0>16X}".format(pename, symbol.name, symbol.address_offset)) | |
def bits2flag(flags,byte_value): | |
bits_array = map(int, format(byte_value,"016b")) | |
return "|".join(itertools.compress(flags[::-1],bits_array)) | |
def togglebit(flags, toggle_flag, byte_value): | |
bits_mask = 2**flags.index(toggle_flag) | |
return byte_value ^ bits_mask | |
search = ["printf", "gets", "lstrcpyW"] | |
dll_characteristics = ['reserved0', 'reserved1', 'reserved2', 'reserved3', 'undefined1', 'High_Entropy_VA', 'Dynamic_Base', 'Force_Integrity', 'NX_Compat', 'No_isolation', 'No_SEH', 'No_bind', 'App_Container', 'WDM_Driver', 'Guard_CF', 'Terminal_Server_Aware'] | |
if __name__ == "__main__": | |
parser = argparse.ArgumentParser() | |
parser.add_argument("-d", "--dump", action= "store_true", help = "Dump all information about the pe. No filters or search applied.") | |
parser.add_argument("-a","--add", nargs='*', help = "In addition to defaults also search for these functions being used by the exectuables. The function names are separated by spaces and terminated with two dashes. Example: -a modulea moduleb --") | |
parser.add_argument("-o","--only", nargs='*', help = "Only search for these functions being used by the executables. The function names are separated by spaces and terminated with two dashes. Example: -e modulea moduleb --") | |
parser.add_argument("-i","--inexact", action="store_true", help = "Ignore case sensitive and allow substring match of function names rather than exact names.") | |
parser.add_argument("-f","--flags", action="store_true", help = "Display the DLL Characteristic flags.") | |
parser.add_argument("-v","--verbose", action="store_true", help = "MAKE SOME NOISE UP IN HERE!!!") | |
parser.add_argument("-t", "--toggle", choices = dll_characteristics, help = "The name of a single Header Characteristics bit to toggle.") | |
parser.add_argument("pefiles", help= "The name of an EXE or DLL is a required argument. Wildcards are supported.") | |
args = parser.parse_args() | |
pefiles = glob.glob(args.pefiles) | |
if len(pefiles)==0: | |
print("No files found.") | |
sys.exit(1) | |
if args.add: | |
search.extend(args.add) | |
if args.only: | |
search = args.only | |
for pe in pefiles: | |
try: | |
thefile = pefile.PE(pe) | |
except Exception as e: | |
print("Unable to open file {}".format(str(e))) | |
continue | |
if args.dump or args.flags: | |
file_options = thefile.OPTIONAL_HEADER.DllCharacteristics | |
print("{} flags : {}".format(pe, bits2flag(dll_characteristics, file_options))) | |
if args.toggle: | |
#LAB: Put your code here to toggle the PE flags here | |
#variable pe contains a string that is the file name you are processing | |
#variable thefile contains the pefile.PE data structure you need | |
#variable args.toggle contains what is passed after the -t or --toggle argument on the command line | |
print("Toggle is not currently supported.") | |
if args.verbose: | |
print("Searching EXE {} for vulnerable functions {}".format(pe, str(search))) | |
search_tables(thefile,pe, search) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import ctypes | |
x = ctypes.cdll.LoadLibrary(r'c:\windows\system32\crtdll.dll') | |
# | |
#Interestingly this will crash this program.... | |
#C:\Users\mark\Desktop>python -c "print('A'*15685)"| python gets.py | |
#Type something for me!b'AAAAAAAAAAAAAAAAAAAA' | |
#But this will not | |
#C:\Users\mark\Desktop>python -c "print('A'*15684)"| python gets.py | |
#gets returns a pointer to value in memory and updates the buffer mystr | |
x.printf(b'Type something for me!') | |
mystr = ctypes.create_string_buffer(20) | |
respaddr = x.gets(mystr) | |
print(mystr.value) | |
#turn response address into a string | |
actualmem = ctypes.cast(respaddr, ctypes.c_char_p) | |
print(actualmem.value) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import pefile | |
import sys | |
import ctypes | |
import glob | |
import argparse | |
def dllinfo(dllpath, search=b""): | |
try: | |
thedll = pefile.PE(dllpath) | |
except Exception as e: | |
print("{} {}".format(dllpath,str(e))) | |
return | |
if hasattr(thedll, "DIRECTORY_ENTRY_EXPORT"): | |
for symbol in thedll.DIRECTORY_ENTRY_EXPORT.symbols: | |
if symbol.name: | |
if not search or (search.decode().lower() in symbol.name.decode().lower()): | |
print("Name {} is at offset {} in {}".format(symbol.name, symbol.address_offset,dllpath)) | |
else: | |
print("{} has no exports.".format(dllpath)) | |
def search_imports(infile, search = []): | |
try: | |
thefile = pefile.PE(infile) | |
except Exception as e: | |
print("Unable to open file {}",format(str(e))) | |
if hasattr(thefile, "DIRECTORY_ENTRY_IMPORT"): | |
for dllimport in thefile.DIRECTORY_ENTRY_IMPORT: | |
for eachfunc in dllimport.imports: | |
for eachterm in search: | |
if not args.exact: | |
if eachfunc.name and (eachterm in eachfunc.name.decode().lower()) or not search: | |
print("*"*100) | |
print("Name {} is using function {}".format(infile, eachfunc.name)) | |
print("*"*100) | |
else: | |
if eachfunc.name and eachterm == eachfunc.name.decode(): | |
print("*"*100) | |
print("Name {} is using function {}".format(infile, eachfunc.name)) | |
print("*"*100) | |
#else: | |
# print("{} not in {}".format(eachterm, eachfunc.name) | |
search = ["printf", "gets", "lstrcpyw"] | |
parser = argparse.ArgumentParser() | |
parser.add_argument("-a","--add", nargs='*', help = "In addition to defaults also search for these functions being used by the exectuables.") | |
parser.add_argument("-o","--only", nargs='*', help = "Only search for these functions being used by the executables.") | |
parser.add_argument("-e","--exact", action="store_true", help = "Search for exact match of function names rather than as substrings.") | |
parser.add_argument("-v","--verbose", action="store_true", help = "MAKE SOME NOISE UP IN HERE!!!") | |
parser.add_argument("executable", help= "The name of an executable is a required argument.") | |
args = parser.parse_args() | |
exes = glob.glob(args.executable) | |
if len(exes)==0: | |
print("No Executables found.") | |
sys.exit(1) | |
if args.add: | |
search.extend(args.add) | |
if args.only: | |
search = args.only | |
for exe in exes: | |
if args.verbose: | |
print("Searching EXE {} for vulnerable functions {}".format(exe, str(search))) | |
search_imports(exe, search) | |
#x = ctypes.cdll.LoadLibrary(r'c:\windows\system32\crtdll.dll') | |
#x.printf("WHATS UP GOOD OLD PRINTF VULNERABILTIY!!! %x %x %x") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment