def mem_hook(unicornObject, accessType, memAccessAddress, memAccessSize,
             memValue, userData):
    #if accessType == UC.UC_MEM_READ:
    #    print("Read: ", hex(memAccessAddress), memAccessSize, hex(memValue))
    if accessType == UC.UC_MEM_WRITE:
        #print("Write: ", hex(memAccessAddress), memAccessSize, hex(memValue))
        if memAccessSize == 1:
            idc.PatchByte(memAccessAddress, memValue)
        elif memAccessSize == 2:
            idc.PatchWord(memAccessAddress, memValue)
        elif memAccessSize == 4:
            idc.PatchDword(memAccessAddress, memValue)
Ejemplo n.º 2
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:
        idc.PatchByte(ea, value)
    elif wordsize == 2:
        idc.PatchWord(ea, value)
    elif wordsize == 4:
        idc.PatchDword(ea, value)
    elif wordsize == 8:
        idc.PatchQword(ea, value)
    else:
        raise ValueError('Invalid argument: wordsize={}'.format(wordsize))
Ejemplo n.º 3
0
def fix_callgraph(msgsend, segname, class_param, sel_param):
    '''
    fix_callgraph: msgsend, segname, class_param, sel_param

    Given the msgsend flavour address as a parameter, looks
    for the parameters (class and selector, identified by
    class_param and sel_param) and creates a new segment where
    it places a set of dummy calls named as classname_methodname
    (we use method instead of selector most of the time).
    '''

    t1 = time.time()
    if not msgsend:
        print 'ERROR: msgSend not found'
        return

    total = 0
    resolved = 0
    call_table = dict()

    for xref in idautils.XrefsTo(msgsend, idaapi.XREF_ALL):
        total += 1
        ea_call = xref.frm
        func_start = idc.GetFunctionAttr(ea_call, idc.FUNCATTR_START)
        if not func_start or func_start == idc.BADADDR:
            continue
        ea = ea_call
        method_name_ea = trace_param(ea, func_start, idc.o_reg, sel_param)
        if method_name_ea and idc.isASCII(idc.GetFlags(method_name_ea)):
            method_name = idc.GetString(method_name_ea, -1, idc.ASCSTR_C)
            if not method_name:
                method_name = '_unk_method'
        else:
            method_name = '_unk_method'

        class_name_ea = trace_param(ea, func_start, idc.o_reg, class_param)
        if class_name_ea:
            class_name = idc.Name(class_name_ea)
            if not class_name:
                class_name = '_unk_class'
        else:
            class_name = '_unk_class'

        if method_name == '_unk_method' and class_name == '_unk_class':
            continue

        # Using this name convention, if the class and method
        # are identified by IDA, the patched call will point to
        # the REAL call and not one of our dummy functions
        #
        class_name = class_name.replace('_OBJC_CLASS_$_', '')
        class_name = class_name.replace('_OBJC_METACLASS_$_', '')
        new_name = '_[' + class_name + '_' + method_name + ']'
        print '%08x: %s' % (ea_call, new_name)
        call_table[ea_call] = new_name
        resolved += 1

    print '\nFinal stats:\n\t%d total calls, %d resolved' % (total, resolved)
    print '\tAnalysis took %.2f seconds' % (time.time() - t1)

    if resolved == 0:
        print 'Nothing to patch.'
        return

    print 'Adding new segment to store new nullsubs'

    # segment size = opcode ret (4 bytes) * num_calls
    seg_size = resolved * 4
    seg_start = idc.MaxEA() + 4
    idaapi.add_segm(0, seg_start, seg_start + seg_size, segname, 'CODE')

    print 'Patching database...'
    seg_ptr = seg_start
    for ea, new_name in call_table.items():
        if idc.LocByName(new_name) != idc.BADADDR:
            offset = idc.LocByName(new_name) - ea
        else:
            # create code and name it
            idc.PatchDword(seg_ptr, 0xE12FFF1E) # BX LR
            idc.MakeName(seg_ptr, new_name)
            idc.MakeCode(seg_ptr)
            idc.MakeFunction(seg_ptr, seg_ptr + 4)
            idc.MakeRptCmt(seg_ptr, new_name)
            offset = seg_ptr - ea
            seg_ptr += 4

        # patch the msgsend call
        if idc.GetReg(ea, "T") == 1:
            if offset > 0 and offset & 0xFF800000:
                print 'Offset too far for Thumb (%08x) Stopping [%08x]' % (offset, ea)
                return

            off1 = (offset & 0x7FF000) >> 12
            off2 = (offset & 0xFFF) / 2
            w1 = (0xF000 | off1)
            w2 = (0xE800 | off2) - 1
            idc.PatchWord(ea, w1)
            idc.PatchWord(ea + 2, w2)
        else:
            if offset > 0 and offset & 0xFF000000:
                print 'Offset too far (%08x) Stopping [%08x]' % (offset, ea)
            dw = (0xFA000000 | (offset - 8 >> 2))
            if dw < 0:
                dw = dw & 0xFAFFFFFF
            idc.PatchDword(ea, dw)
Ejemplo n.º 4
0
def obfuscated_jz_jnz():
    '''
    Resolve opaque predicates, in case that opaque predicate points to FF 25 the call will be patched:
    
    Example:

    .text:10001540 68 71 15 00 10      push    offset byte_10001571
    .text:10001545 0F 84 57 5C 03 00   jz      loc_100371A2
    .text:1000154B 0F 85 51 5C 03 00   jnz     loc_100371A2

    Pattern
    68 ?? ?? ?? ?? ??
    0F 84 ?? ?? ?? ??
    0F 85 ?? ?? ?? ??

    68 ?? ?? ?? ?? 0F 84 ?? ?? ?? ?? 0F 85 ?? ?? ?? ??

    Operation -> DWORD( 9C 9F 01 00 ) - DWORD( 96 9F 01 00 ) == 6 

    That means that jumps to the same relative address

    0x19f9C - 0x19F96 = 0x6

    If that address points to FF 25 ?? ?? ?? ??

    .text:100371A2 FF 25 9C 90 03 10        jmp     ds:lstrlenA <----------
    .text:100371A8                          -----------------------------
    .text:100371A8 FF 25 A0 90 03 10        jmp     ds:GetModuleHandleA
    .text:100371AE                          -----------------------------
    .text:100371AE FF 25 A4 90 03 10        jmp     ds:LoadLibraryA
    .text:100371B4                          -----------------------------
    .text:100371B4
    .text:100371B4                          
    .text:100371B4                          
    .text:100371B4 FF 25 A8 90 03 10        jmp     ds:GetLastError
    .text:100371BA                          -----------------------------
    .text:100371BA FF 25 AC 90 03 10        jmp     ds:lstrcpyA

    Patch with CALL PUSH RET formula

    .text:10001540 90                       nop    ; Return addr - 0x10001571
    .text:10001541 90                       nop
    .text:10001542 90                       nop
    .text:10001543 90                       nop
    .text:10001544 90                       nop
    .text:10001545 FF 15 9C 90 03 10        call    ds:lstrlenA
    .text:1000154B 68 71 15 00 10           push    10001571h
    .text:10001550 C3                       retn

    '''

    count_patched = 0
    count_not_patched = 0
    ea = 0

    while ea != BADADDR:

        ea = idc.FindBinary(
            ea, SEARCH_NEXT | SEARCH_DOWN | SEARCH_CASE,
            "68 ?? ?? ?? ?? 0F 84 ?? ?? ?? ?? 0F 85 ?? ?? ?? ??")

        if ea and segment_name == idc.SegName(ea):

            idc.MakeComm(
                ea,
                "Return addr - {0}".format(hex(Dword(ea + 0x1)).split("L")[0]))

            jz_pos = ea + 0x5
            jz_value = Dword(jz_pos + 0x2)

            jnz_pos = jz_pos + 0x6
            jnz_value = Dword(jnz_pos + 0x2)

            # Check same jmp addr
            if jz_value - jnz_value == 0x6:

                pos_jmp = jz_pos + jz_value + 0x6

                # If the jmp points to a FF 25 instruction (absolute jmp)
                if Word(pos_jmp) == 0x25FF:
                    '''
                    .text:1000153F 55                   push    ebp
                    .text:10001540 68 71 15 00 10       push    offset byte_10001571
                    .text:10001545 0F 84 57 5C 03 00    jz      loc_100371A2
                    .text:1000154B 0F 85 51 5C 03 00    jnz     loc_100371A2
                    .text:10001551 56                   push    esi
                    '''

                    # Patch the conditional jmp, copying the absolute jmp into it
                    idc.PatchWord(jz_pos, Word(pos_jmp))
                    idc.PatchDword(jz_pos + 0x2, Dword(pos_jmp + 0x2))

                    # At this point the FF 25 instruction have been copied into the conditional jmp position
                    # Ex: FF 25 3C 70 48 71 jmp     ds:CryptGenRandom
                    # But the absolute jmp will be patched with a call FF 15

                    # FF 15 call
                    idc.PatchByte(jz_pos, 0xFF)
                    idc.PatchByte(jz_pos + 0x1, 0x15)

                    # Copy the push instruction where conditonal jmp was
                    idc.PatchWord(jnz_pos, Word(ea))
                    idc.PatchWord(jnz_pos + 0x1, Word(ea + 0x1))
                    idc.PatchWord(jnz_pos + 0x3, Word(ea + 0x3))
                    idc.PatchByte(jnz_pos + 0x5, 0xC3)

                    # Nop the first 5 bytes.
                    patch_loop(ea, 0x5, 0x90)
                    '''
                    .text:10001540 90                  nop                     ; Return addr - 0x10001571
                    .text:10001541 90                  nop
                    .text:10001542 90                  nop
                    .text:10001543 90                  nop
                    .text:10001544 90                  nop
                    .text:10001545 FF 15 9C 90 03 10   call    ds:lstrlenA
                    .text:1000154B 68 71 15 00 10      push    10001571h
                    .text:10001550 C3                  retn
                    '''

                    idc.MakeCode(ea)

                else:
                    '''
                    .text:10021C0C 68 27 1C 02 10         push    offset loc_10021C27
                    .text:10021C11 0F 84 09 F9 FD FF      jz      loc_10001520
                    .text:10021C17 0F 85 03 F9 FD FF      jnz     loc_10001520

                    .text:10021C0C 90                     nop
                    .text:10021C0D 90                     nop
                    .text:10021C0E 90                     nop
                    .text:10021C0F 90                     nop
                    .text:10021C10 90                     nop
                    .text:10021C11 90                     nop
                    .text:10021C12 90                     nop
                    .text:10021C13 90                     nop
                    .text:10021C14 90                     nop
                    .text:10021C15 90                     nop
                    .text:10021C16 90                     nop
                    .text:10021C17 E9 04 F9 FD FF         jmp     loc_10001520
                    .text:10021C1C 90                     nop   <- last byte of old jnz instr
                    '''

                    # The first 11 bytes to nop

                    for i in range(0, 11):

                        idc.PatchByte(ea + i, 0x90)

                    # Add 1 to the address, because the size of the conditional jmp is 6, and the size of the unconditional jmp is 5
                    addr_to_jmp = Dword(jnz_pos + 0x2) + 0x1

                    # Patch first jz_value with unconditional jmp
                    idc.PatchByte(jnz_pos, 0xE9)

                    # Set the relative address
                    idc.PatchDword(jnz_pos + 0x1, addr_to_jmp)

                    # Last byte of the jnz to NOP (0x90)
                    idc.PatchByte(jnz_pos + 0x5, 0x90)

                    idc.MakeCode(ea)
                    '''
                    .text:10021C0C 90                     nop
                    .text:10021C0D 90                     nop
                    .text:10021C0E 90                     nop
                    .text:10021C0F 90                     nop
                    .text:10021C10 90                     nop
                    .text:10021C11 90                     nop
                    .text:10021C12 90                     nop
                    .text:10021C13 90                     nop
                    .text:10021C14 90                     nop
                    .text:10021C15 90                     nop
                    .text:10021C16 90                     nop
                    .text:10021C17 E9 04 F9 FD FF         jmp     loc_10001520
                    .text:10021C1C 90                     nop   <- last byte of old jnz instr
                    '''

                count_patched += 1

            else:

                count_not_patched += 1

    print "\tPatched obfuscated_jz_jnz: {0}".format(count_patched)
    print "\tNot Patched obfuscated_jz_jnz: {0}".format(count_not_patched)
Ejemplo n.º 5
0
#coding=utf-8

import idc
import idaapi
import idautils

idc.PatchByte(ea, value)  #修改字节
idc.PatchWord(ea, value)  #修改字
idc.PatchDword(ea, value)  #修改双字

# 利用异或解密算法 解密选中的加密字符串
start = idc.SelStart()
end = idc.SelEnd()
print hex(start)
print hex(end)


def xor(size, key, buff):
    for index in range(0, size):
        cur_addr = buff + index
        temp = idc.Byte(cur_addr) ^ key
        idc.PatchByte(cur_addr, temp)


xor(end - start, 0x30, start)
print idc.GetString(start)