def set_execstack(self, enabled: bool) -> bool: """ 开启或关闭execstack(只支持ELF) :param enabled: 是否开启execstack :return: """ for seg in self.ldr.binary.segments: # 查找GNU_STACK类型的segment if seg.type == lief.ELF.SEGMENT_TYPES.GNU_STACK: # dump ph_data,并修改flags old_ph_data = self._dump_segment(seg) if enabled: seg.flags = lief.ELF.SEGMENT_FLAGS(7) # rwx else: seg.flags = lief.ELF.SEGMENT_FLAGS(6) # rw- new_ph_data = self._dump_segment(seg) # 更新ph_data if not self._update_segment(old_ph_data, new_ph_data): log.fail("Modify segment failed") return False if enabled: log.success("Set execstack -> enabled") else: log.success("Set execstack -> disabled") return True log.fail("Can't find GNU_STACK segment") return False
def save(self, filename: str = None) -> bool: if not filename: bin_name = self.ldr.binary_name bin_postfix = os.path.splitext(bin_name)[-1].lower() bin_true_name = ''.join(os.path.splitext(bin_name)[:-1]) filename = "{}.patched{}".format(bin_true_name, bin_postfix) open(filename, "wb").write(self.ldr.binary_data) os.chmod(filename, 0o755) log.success("Binary saved @ {}".format( log.underline(os.path.abspath(filename)))) return True
def add_asm(self, asm: str, label: str = None) -> int: try: target_vaddr = self._add_asm(asm) except AddException as ex: log.fail(ex) return 0 if not target_vaddr: log.fail("Add asm @ {} failed".format(hex(target_vaddr))) return 0 self._label(target_vaddr, label) log.success("Add asm @ {}".format(hex(target_vaddr))) return target_vaddr
def patch_asm(self, vaddr: int, asm: str, label: str = None) -> bool: byte_code, inst_cnt = self._asm(asm, vaddr) if not inst_cnt: return False try: self._patch_byte(vaddr, byte_code) except PatchException as ex: log.fail(ex) return False self._label(vaddr, label) log.success("Patch {} asm @ {}".format(hex(inst_cnt), hex(vaddr))) return True
def patch_byte(self, vaddr: int, byte: str or bytes or bytearray, label: str = None) -> bool: try: self._patch_byte(vaddr, byte) except PatchException as ex: log.fail(ex) return False self._label(vaddr, label) log.success("Patch {} bytes @ {}".format(hex(len(byte)), hex(vaddr))) return True
def add_c(self, c_code: str, label: str = None) -> int: try: compiled_bytes = self._call_scc(c_code) target_vaddr = self._add_byte(compiled_bytes) except SccException as ex: log.fail(ex) return 0 except AddException as ex: log.fail(ex) return 0 self._label(target_vaddr, label) log.success("Add c_code @ {}".format(hex(target_vaddr))) return target_vaddr
def _init_code_cave(self) -> None: # 尝试将.eh_frame加入code cave buffer if self.ldr.binary.has_section(".eh_frame"): eh_frame_section = self.ldr.binary.get_section(".eh_frame") self.code_cave.append( [eh_frame_section.virtual_address, eh_frame_section.size]) # 如果 .eh_frame的权限为只读,需要patch一下segment的权限+x eh_frame_segment = list(eh_frame_section.segments)[0] if not eh_frame_segment.flags & lief.ELF.SEGMENT_FLAGS.X: log.success("Modify .eh_frame section prot +x") old_ph_data = self._dump_segment(eh_frame_segment) eh_frame_segment.flags = lief.ELF.SEGMENT_FLAGS( int(lief.ELF.SEGMENT_FLAGS.X) | int(eh_frame_segment.flags)) new_ph_data = self._dump_segment(eh_frame_segment) self._update_segment(old_ph_data, new_ph_data)
def add_byte(self, byte: str or bytes or bytearray, label: str = None) -> int: try: target_vaddr = self._add_byte(byte) except AddException as ex: log.fail(ex) return 0 if not target_vaddr: log.fail("Add {} bytes @ {} failed".format(hex(len(byte)), hex(target_vaddr))) return 0 self._label(target_vaddr, label) log.success("Add {} bytes @ {}".format(hex(len(byte)), hex(target_vaddr))) return target_vaddr
def set_norelro(self) -> bool: """ 取消ELF的relro :return: """ for seg in self.ldr.binary.segments: if seg.type == lief.ELF.SEGMENT_TYPES.GNU_RELRO: old_ph_data = self._dump_segment(seg) seg.type = lief.ELF.SEGMENT_TYPES.NULL new_ph_data = self._dump_segment(seg) if not self._update_segment(old_ph_data, new_ph_data): log.fail("Modify segment failed") return False log.success("Set norelro -> enabled") return True log.fail("Can't find GNU_RELRO segment") return False
def _add_segment(self, addr: int = 0, length: int = 0x1000, prot: int = 7) -> int: if not self.new_phdr_faddr: self._alloc_new_segment_for_phdr() if not addr: # 自动计算地址 new_seg = self._auto_next_segment(length, prot) if not new_seg: return 0 else: new_seg = lief.ELF.Segment() new_seg.type = lief.ELF.SEGMENT_TYPES.LOAD new_seg.flags = lief.ELF.SEGMENT_FLAGS(prot) new_seg.alignment = 0x1000 new_seg.physical_size = length new_seg.virtual_size = length new_seg.physical_address = addr new_seg.virtual_address = addr new_seg.file_offset = self._align_page(self.ldr.binary_size()) # segment内容用全0填充 self._cover_binary_data(new_seg.file_offset, bytearray(new_seg.virtual_size)) pht_data = bytearray() for seg in self.ldr.binary.segments: if seg.type == lief.ELF.SEGMENT_TYPES.PHDR: # 修改PHDR的size seg.physical_size += self.ldr.phentsize seg.virtual_size += self.ldr.phentsize pht_data += self._dump_segment(seg) pht_data += self._dump_segment(new_seg) # 在new_phdr_segment写入新pht_data self._cover_binary_data(self.new_phdr_faddr, pht_data) # 更新header self.ldr.binary.header.numberof_segments += 1 self._update_header(self._dump_header()) self.ldr.lief_reload_binary() log.success("Add segment @ {}({}) {}{}{}".format( hex(new_seg.virtual_address), hex(new_seg.virtual_size), "r" if new_seg.flags & 4 else "-", "w" if new_seg.flags & 2 else "-", "x" if new_seg.flags & 1 else "-", )) return new_seg.virtual_address
def patch_c(self, vaddr: int, c_code: str, label: str = None) -> bool: try: compiled_bytes = self._call_scc(c_code) except SccException as ex: log.fail(ex) return False if not compiled_bytes: log.fail("call scc to generate binary code failed") return False try: self._patch_byte(vaddr, compiled_bytes) except PatchException as ex: log.fail(ex) return False self._label(vaddr, label) log.success("Patch c code @ {}".format(hex(vaddr))) return True
def hook_asm(self, vaddr: int, asm: str, label: str = None) -> bool: """ 在指定地址插入asm作为hook代码 :param vaddr: 虚拟地址 :param asm: 汇编代码 :param label: 此hook的名字,后续在asm中可以以{name}的方式引用这个hook的地址 :return: """ if self.ldr.arch in [Arch.I386, Arch.AMD64]: if self._hook_asm_intel(vaddr, asm): self._label(vaddr, label) log.success("Hook @ {}".format(hex(vaddr))) return True else: log.fail("Hook @ {} failed".format(hex(vaddr))) return False else: raise TODOException("Only support hook i386 & amd64 for now")
def _hook_intel(self, vaddr: int) -> bool: x86_jmp_inst_length = 5 disasm_data = bytearray( self.ldr.binary.get_content_from_virtual_address(vaddr, 0x20)) cs_disasm = self.ldr.cs.disasm(disasm_data, vaddr) origin1_jmp_addr = vaddr tmp_len = 0 for disasm in cs_disasm: if tmp_len < x86_jmp_inst_length: tmp_len += len(disasm.bytes) origin1_jmp_length = tmp_len origin2_jmp_addr = vaddr + origin1_jmp_length backup_length = origin1_jmp_length + x86_jmp_inst_length backup1 = self._add_byte(bytearray(backup_length)) if not backup1: return False self._label(backup1, "backup1_{}".format(hex(vaddr))) backup2 = self._add_byte(bytearray(backup_length)) if not backup2: return False self._label(backup2, "backup2_{}".format(hex(vaddr))) if self.ldr.arch == Arch.I386: detour1_asm = ''' pushad call get_pc pc: mov esi,edi sub edi, pc - {} sub esi, pc - {{backup2_{}}} mov ecx, {} /* patch_byte_len */ cld rep movsb popad call {{user_hook_{}}} /* hook_func */ jmp {} /* origin1 */ get_pc: mov edi, [esp] ret '''.format(vaddr, hex(vaddr), backup_length, hex(vaddr), origin1_jmp_addr) detour2_asm = ''' pushf pushad call get_pc pc: mov esi,edi sub edi, pc - {} sub esi, pc - {{backup1_{}}} mov ecx, {} /* patch_byte_len */ cld rep movsb popad popf jmp {} /* origin2 */ get_pc: mov edi, [esp] ret '''.format(vaddr, hex(vaddr), backup_length, origin2_jmp_addr) elif self.ldr.arch == Arch.AMD64: detour1_asm = ''' push rdi /* backup reg */ push rsi push rcx lea rdi, [{}] lea rsi, [{{backup2_{}}}] mov rcx, {} /* patch_byte_len */ cld rep movsb pop rcx /* restore reg */ pop rsi pop rdi call {{user_hook_{}}} /* hook_func */ jmp {} /* origin1 */ '''.format(vaddr, hex(vaddr), backup_length, hex(vaddr), origin1_jmp_addr) detour2_asm = ''' pushf push rdi /* backup reg */ push rsi push rcx lea rdi, [{}] lea rsi, [{{backup1_{}}}] mov rcx, {} /* patch_byte_len */ cld rep movsb pop rcx /* restore reg */ pop rsi pop rdi popf jmp {} /* origin2 */ '''.format(vaddr, hex(vaddr), backup_length, origin2_jmp_addr) else: # should not be here return False detour1 = self._add_asm(detour1_asm) if not detour1: return False self._label(detour1, "detour1_{}".format(hex(vaddr))) detour2 = self._add_asm(detour2_asm) if not detour2: return False self._label(detour2, "detour2_{}".format(hex(vaddr))) origin1_jmp, inst_cnt = self._asm( "jmp {{detour1_{}}}".format(hex(vaddr)), origin1_jmp_addr) origin2_jmp, inst_cnt = self._asm( "jmp {{detour2_{}}}".format(hex(vaddr)), origin2_jmp_addr) backup1_bytes = bytearray( self.ldr.binary.get_content_from_virtual_address( vaddr, backup_length)) backup1_bytes[:origin1_jmp_length] = origin1_jmp backup2_bytes = bytearray( self.ldr.binary.get_content_from_virtual_address( vaddr, backup_length)) backup2_bytes[origin1_jmp_length:] = origin2_jmp self._patch_byte(backup1, backup1_bytes) self._patch_byte(backup2, backup2_bytes) self._patch_byte(vaddr, backup1_bytes) segment = self.ldr.binary.segment_from_virtual_address(vaddr) if not segment.flags & lief.ELF.SEGMENT_FLAGS.W: log.success("Modify hook segment prot +w") old_ph_data = self._dump_segment(segment) segment.flags = lief.ELF.SEGMENT_FLAGS( int(lief.ELF.SEGMENT_FLAGS.W) | int(segment.flags)) new_ph_data = self._dump_segment(segment) self._update_segment(old_ph_data, new_ph_data) return True
def label(self, vaddr: int, label_name: str) -> bool: if self._label(vaddr, label_name): log.success("name {} with {}".format(hex(vaddr), label_name)) return True log.fail("name {} with {} failed".format(hex(vaddr), label_name)) return False