Beispiel #1
0
 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
Beispiel #2
0
 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
Beispiel #3
0
 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
Beispiel #4
0
 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
Beispiel #5
0
 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
Beispiel #6
0
 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
Beispiel #7
0
 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)
Beispiel #8
0
 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
Beispiel #9
0
 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
Beispiel #10
0
 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
Beispiel #11
0
 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
Beispiel #12
0
 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")
Beispiel #13
0
    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
Beispiel #14
0
 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