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 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)