예제 #1
0
def patch_word(ea, value, wordsize=WORD_SIZE):
    """Patch the word at the given address.

    Words are patched using PatchByte(), PatchWord(), PatchDword(), or PatchQword(), as
    appropriate.
    """
    if wordsize == 1:
        ida_bytes.patch_byte(ea, value)
    elif wordsize == 2:
        ida_bytes.patch_word(ea, value)
    elif wordsize == 4:
        ida_bytes.patch_dword(ea, value)
    elif wordsize == 8:
        ida_bytes.patch_qword(ea, value)
    else:
        raise ValueError('Invalid argument: wordsize={}'.format(wordsize))
예제 #2
0
    def req_patch(self, hash):
        addr, value, length = hash['addr'], hash['value'], hash['len']

        if length == 4:
            prev_value = idc.get_wide_dword(addr)
            if not ida_bytes.create_data(ea, FF_DWORD, 4, ida_idaapi.BADADDR):
                rs_log('[x] ida_bytes.create_data FF_DWORD failed')
            if not ida_bytes.patch_dword(addr, value):
                rs_log('[x] patch_dword failed')
            if not idc.op_plain_offset(addr, 0, 0):
                rs_log('[x] op_plain_offset failed')

        elif length == 8:
            prev_value = idc.get_qword(addr)
            if not ida_bytes.create_data(addr, FF_QWORD, 8,
                                         ida_idaapi.BADADDR):
                rs_log('[x] ida_bytes.create_data FF_QWORD failed')
            if not ida_bytes.patch_qword(addr, value):
                rs_log('[x] patch_qword failed')
            if not idc.op_plain_offset(addr, 0, 0):
                rs_log('[x] op_plain_offset failed')

        else:
            rs_log("[x] unsupported length: %d" % length)
            return

        rs_log("patched 0x%x = 0x%x (previous was 0x%x)" %
               (addr, value, prev_value))
예제 #3
0
    def add_method_xref(self, xref):
        msg("Adding cross reference to method implementation for %s\n" %
            get_func_name(self.method_pointer))

        add_dref(xref.frm, self.method_pointer, dr_I | XREF_USER)

        offset = self.method_pointer - xref.frm

        instruction_bytes = get_bytes(xref.frm, self.ARM64_INSTRUCTION_SIZE)
        #TODO: are there other instructions that could reference a method selector
        #and then move the selector reference into a register?
        arm64_ldr = AArch64LDRInstruction(instruction_bytes)

        arm64_ldr.patch_offset(offset)

        patch_dword(xref.frm, arm64_ldr.instruction_int)
        return ObjcMethodXref(xref.frm, self.method_pointer, xref.to)
예제 #4
0
    def set_dword(ea, value):
        """
            Static method allowing to set the value of one dwordat an address.

            :param ea: The address at which changing the value.
            :param value: The value to set at the address.
            :raise RuntimeError: If it was not possible to change the value.
        """
        if not ida_bytes.patch_dword(ea, value):
            raise RuntimeError("Unable to set value {} at {}".format(ea, value))
예제 #5
0
def resolve_opaque_push_retn():

    PATTERNS = ["68 ?? ?? ?? ?? C3"]

    count_patched = 0

    for pattern in PATTERNS:

        ea = 0

        while ea != BADADDR:
            ea = ida_search.find_binary(
                ea, BADADDR, pattern, 16,
                SEARCH_NEXT | SEARCH_DOWN | SEARCH_CASE)
            ''' 
            pattern: 68 ?? ?? ?? ?? C3
            .text:00406922 68 B2 6A 00 10       push    0x10006ab2
            .text:00406927 C3                   retn
            
            patched:
            .text:00406922 E9 B2 6A 00 10       jmp     0x10006ab2
            .text:00406927 90                   nop
            '''

            if ea_in_bounds(ea):
                j_pos = ea
                pos_jmp = idc.get_wide_dword(j_pos + 1)  # absolute address

                offset = pos_jmp - 5 - j_pos

                # Patch the push and retn instructions with NOPs (6 bytes)
                for i in range(0, 6):
                    ida_bytes.patch_byte(j_pos + i, 0x90)

                ida_bytes.patch_byte(j_pos, 0xE9)
                ida_bytes.patch_dword(j_pos + 0x1, offset)

                idc.create_insn(ea)

                count_patched += 1

    print("\tPatched resolve_opaque_push_retn: {0}".format(count_patched))
def resolve_obf_calls(ea):
    next_instr_addr = idc.get_wide_dword(ea + 1)

    first_jmp_target = (ea + idc.get_wide_dword(ea + 0x7) + 0xB) & 0xFFFFFFFF
    second_jmp_target = (ea + idc.get_wide_dword(ea + 0xD) + 0x11) & 0xFFFFFFFF

    if first_jmp_target != second_jmp_target:
        return

    call_param = (first_jmp_target - ea - 5) & 0xFFFFFFFF

    # Now we can replace all code till next instruction's address with NOPs
    fill_with_nops(ea, next_instr_addr)

    # Insert CALL
    ida_bytes.patch_byte(ea, 0xE8)                      # CALL
    ida_bytes.patch_dword(ea + 1, call_param)
    idc.create_insn(ea)

    return True
예제 #7
0
    def add_method_xref(self, xref):
        msg("Adding cross reference to method implementation for %s\n" %
            get_func_name(self.method_pointer))

        #TODO: clean this up so it's more clear how we're parsing and patching the instruction
        #TODO: handle other potential instructions that could place a method selref into a register
        #TODO: sanity check what instruction we're actually working with before blindly deciding
        #       it's a 7-byte mov instruction

        add_dref(xref.frm, self.method_pointer, dr_I | XREF_USER)

        #offset is a rip-relative offset that gets added to rip and dereferenced
        #when this instruction is executed, rip will be pointing to the next instruction
        #meaning it has been incremented by 7 (the length of the mov instruction)
        offset = self.method_pointer - xref.frm - self.X86_64_MOV_INSTRUCTION_SIZE

        #this replaces  mov RSI, &selector with:
        #               mov RSI, &method
        #xref.frm is the address of the mov instruction
        #+3 (4th byte of the instruction)
        #is where the RIP-relative operand is that
        #will get dereferenced as a pointer
        patch_dword(xref.frm + 3, offset)
        return ObjcMethodXref(xref.frm, self.method_pointer, xref.to)
예제 #8
0
    def value(self, value):
        """
            Property setter which allow to set the value of this object.
            This property works only if the :meth:`~BipData.is_numerable`
            property returned True. If this object has no data
            (:meth:`~BipData.has_data` property return False) or is unknown
            (:meth:`~BipData.is_unknwon` return True) the value set is
            considered to be on 1 byte.

            For setting non numerical value or value on more than 8 bytes use
            the :meth:`~BipElt.bytes` property setter.

            This property is link to the type defined or guessed by IDA and
            it is a good idea to assure you have the proper type before using
            it.

            :param int value: An integer to which set the value of the
                current data element.
            :raise RuntimeError: If the setting of the value failed or if
                the value could not be set because of an unknown type.
        """
        if self.value == value: # case where we are setting at the same value
            return
        if (not self.has_data) or self.is_unknown or self.is_byte:
            if not ida_bytes.patch_byte(self.ea, value):
                raise RuntimeError("Unable to patch value: {}".format(self))
        elif self.is_word:
            if not ida_bytes.patch_word(self.ea, value):
                raise RuntimeError("Unable to patch value: {}".format(self))
        elif self.is_dword:
            if not ida_bytes.patch_dword(self.ea, value):
                raise RuntimeError("Unable to patch value: {}".format(self))
        elif self.is_qword:
            if not ida_bytes.patch_qword(self.ea, value):
                raise RuntimeError("Unable to patch value: {}".format(self))
        else:
            raise RuntimeError("Unable to patch value: {}".format(self))
예제 #9
0
def resolve_opaque_jnz_jz():

    PATTERNS = [
        "0F 84 ?? ?? ?? ?? 0F 85 ?? ?? ?? ??",  # jz + jnz
        "0F 85 ?? ?? ?? ?? 0F 84 ?? ?? ?? ??",  # jnz + jz
        "74 ?? 0F 85 ?? ?? ?? ??",  # jz short + jnz
        "0F 84 ?? ?? ?? ?? 75 ??",  # jz + jnz short
        "75 ?? 0F 84 ?? ?? ?? ??",  # jnz short + jz
        "0F 85 ?? ?? ?? ?? 74 ??",  # jnz + jz short
        "74 ?? 75 ??",  # jz short + jnz short
        "75 ?? 74 ??"  # jnz short + jz short
    ]

    count_patched = 0
    count_not_patched = 0

    for pattern in PATTERNS:

        ea = 0

        while ea != BADADDR:
            ea = ida_search.find_binary(
                ea, BADADDR, pattern, 16,
                SEARCH_NEXT | SEARCH_DOWN | SEARCH_CASE)
            ''' 
            pattern: 0F 85 ?? ?? ?? ?? 0F 84 ?? ?? ?? ??
            .text:0040690E 66 21 CF            and     di, cx
            .text:00406911 0F 85 AE F4 FF FF   jnz     loc_405DC5 <- j_1_pos
            .text:00406917 0F 84 A8 F4 FF FF   jz      loc_405DC5 <- j_2_pos
            
            patched:
            .text:0040690E 66 21 CF            and     di, cx
            .text:00406911 E9 A9 F4 FF FF      jmp     loc_405DC5
            .text:00406916 90                  nop
            .text:00406917 90                  nop
            .text:00406918 90                  nop
            .text:00406919 90                  nop
            .text:0040691a 90                  nop
            .text:0040691b 90                  nop
            .text:0040691c 90                  nop
            '''

            if ea_in_bounds(ea):
                # .text:00406911 0F 85 AE F4 FF FF  jnz loc_405DC5 <- j_1_pos
                #                      AE F4 FF FF                 <- j_1_value Relative offset value
                j_1_pos = ea
                j_1_value, j_1_size = get_j_val(j_1_pos)

                j_2_pos = ea + j_1_size
                j_2_value, j_2_size = get_j_val(j_2_pos)

                pos_jmp = j_1_pos + j_1_value + j_1_size

                if j_1_value - j_2_value == j_2_size:

                    # Patch the jz and jnz instructions with NOPs
                    for i in range(0, j_1_size + j_2_size):
                        ida_bytes.patch_byte(j_1_pos + i, 0x90)

                    if j_1_size == 2:  # jz short or jnz short
                        # Patch with a relative short jmp (size = 2) in the position of the first conditional jmp
                        addr_to_jmp = j_1_value
                        ida_bytes.patch_byte(j_1_pos, 0xEB)
                        ida_bytes.patch_byte(j_1_pos + 0x1, addr_to_jmp)
                    else:  # jz or jnz
                        # Patch with a relative jmp (size = 5) in the position of the first conditional jmp
                        addr_to_jmp = j_1_value + 1
                        ida_bytes.patch_byte(j_1_pos, 0xE9)
                        ida_bytes.patch_dword(j_1_pos + 0x1, addr_to_jmp)

                    idc.create_insn(ea)

                    count_patched += 1

                else:

                    count_not_patched += 1

    print("\tPatched resolve_opaque_jnz_jz: {0}".format(count_patched))
    print("\tNot Patched resolve_opaque_jnz_jz: {0}".format(count_not_patched))