Python distorm3.Decompose() Examples

The following are 28 code examples of distorm3.Decompose(). You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may also want to check out all available functions/classes of the module distorm3 , or try the search function .
Example #1
Source File: Instruction.py    From VMAttack with MIT License 6 votes vote down vote up
def __init__(self, offset, code, type = distorm3.Decode32Bits, feature = 0):
        """
        @param offset Address of the instruction
        @param code Opcode bytes of the instruction
        @param type Dissassemble 32 or 64 bit code
        @param feature Possible settings for distrom3
        not used at the moment
        """
        self.valid = False
        if SV.dissassm_type == 64:
            type = distorm3.Decode64Bits
        else:
            type = distorm3.Decode32Bits
        inst = distorm3.Decompose(offset, code, type, feature)
        if len(inst) == 1:
            self.Instruction = inst[0]
            if self.Instruction.valid:
                self.valid = True
        self.opcode_len = len(code)
        self.opcode_bytes = []
        self.addr = offset
        for x in code:
            self.opcode_bytes.append(ord(x))
        self._len = len(self.Instruction.operands) + 1 
Example #2
Source File: code_parser.py    From writeups with GNU General Public License v3.0 6 votes vote down vote up
def find_rr_writes_distorm3(address, data):
    writes = []
    for insn in distorm3.Decompose(address, data, type=distorm3.Decode64Bits):
        if insn.mnemonic[:3] == 'RET':
            break
        if insn.mnemonic[:3] != 'MOV':
            continue

        # potential write
        opnd = insn.operands[0]
        if opnd.type != 'AbsoluteMemory' or opnd.index is None:
            continue
        # Absolute mov, with target that is register-based
        if distorm3.Registers[opnd.index] != 'RIP':
            continue
        # RIP-relative write, this is what we are looking for
        # distorm3 opnd.size is measured in bits, need to adjust to bytes
        writes.append((insn.address + insn.size + opnd.disp, opnd.size / 8))
    return writes

# Find rip-relative mov using capstone 
Example #3
Source File: static_deobfuscate.py    From VMAttack with MIT License 5 votes vote down vote up
def get_distorm_info(inst_addr):
    """
    @brief Prints whole distrom3 info of the given instruction
    @param inst_addr Address of instruction
    """
    size = ItemSize(inst_addr)
    inst_bytes = GetManyBytes(inst_addr, size)
    inst = distorm3.Decompose(inst_addr,
                              inst_bytes, distorm3.Decode64Bits, 0)
    print inst[0]
    i = inst[0]
    print 'InstBytes ', i.instructionBytes
    print 'Opcode ', i.opcode
    for o in i.operands:
        print 'operand ', o
        print 'operand type', o.type
    for f in i.flags:
        print 'flag ', f
        print 'raw_flags ', i.rawFlags
    print 'inst_class ', i.instructionClass
    print 'flow_control ', i.flowControl
    print 'address ', i.address
    print 'size ', i.size
    print 'dt ', i.dt
    print 'valid ', i.valid
    print 'segment ', i.segment
    print 'unused_Prefixes ', i.unusedPrefixesMask
    print 'mnemonic ', i.mnemonic
    print 'inst_class ', i.instructionClass 
Example #4
Source File: apihooks_kernel.py    From DAMM with GNU General Public License v2.0 5 votes vote down vote up
def isPrologInlined(self, model, distorm_mode, func_addr):
        ##check if function prologs are modified
        inlined = False
        content = self.addr_space.read(func_addr, 24)

        op_cnt = 1
        for op in distorm3.Decompose(func_addr, content, distorm_mode):
            if op_cnt == 2:
                if model == "32bit":
                    if (op.mnemonic == "MOV" and len(op.operands) == 2 and op.operands[0].type == "Register" and
                            op.operands[1].type == "Register" and op.operands[0].name == "EBP" and op.operands[1].name == "ESP" and
                            prev_op.mnemonic == "PUSH" and len(prev_op.operands) == 1 and prev_op.operands[0].type == "Register" and prev_op.operands[0].name == "EBP"):
                        pass
                    else:
                        inlined = True
                elif model == "64bit":
                    if (op.mnemonic == "MOV" and len(op.operands) == 2 and op.operands[0].type == "Register" and
                            op.operands[1].type == "Register" and op.operands[0].name == "RBP" and op.operands[1].name == "RSP" and
                            prev_op.mnemonic == "PUSH" and len(prev_op.operands) == 1 and prev_op.operands[0].type == "Register" and prev_op.operands[0].name == "RBP"):
                        pass
                    elif (prev_op.mnemonic == "PUSH" and len(prev_op.operands) == 1 and prev_op.operands[0].type == "Register" and prev_op.operands[0].name == "RBP" and 
                          op.mnemonic == "PUSH" and len(op.operands) == 1 and op.operands[0].type == "Register" and op.operands[0].name in ["RSP","RBX","R12","R13","R14","R15"]):
                        # Registers preserved across calls, http://people.freebsd.org/~lstewart/references/amd64.pdf
                        pass
                    else:
                        inlined = True
                break
            prev_op = op
            op_cnt += 1

        return inlined

    # NOTES FROM ANDREW
    # This function orignally checked for any call outside the kernel module
    # This produces too many false positives so its modified to check if the call
    # is to a known module or a kernel symbol 
Example #5
Source File: check_syscall.py    From DAMM with GNU General Public License v2.0 5 votes vote down vote up
def _get_table_info_distorm(self):
        """
        Find the size of the system call table by disassembling functions
        that immediately reference it in their first isntruction
        This is in the form 'cmp reg,NR_syscalls'
        """
        table_size = 0

        if not has_distorm:
            return table_size

        memory_model = self.addr_space.profile.metadata.get('memory_model', '32bit')

        if memory_model == '32bit':
            mode = distorm3.Decode32Bits
            func = "sysenter_do_call"
        else:
            mode = distorm3.Decode64Bits
            func = "system_call_fastpath"

        func_addr = self.addr_space.profile.get_symbol(func)

        if func_addr:
            data = self.addr_space.read(func_addr, 6)
            
            for op in distorm3.Decompose(func_addr, data, mode):
                if not op.valid:
                    continue

                if op.mnemonic == 'CMP':
                    table_size = (op.operands[1].value) & 0xffffffff
                    break

        return table_size 
Example #6
Source File: check_syscall.py    From aumfor with GNU General Public License v3.0 5 votes vote down vote up
def _get_table_info_distorm(self):
        """
        Find the size of the system call table by disassembling functions
        that immediately reference it in their first isntruction
        This is in the form 'cmp reg,NR_syscalls'
        """
        table_size = 0

        if not has_distorm:
            return table_size

        memory_model = self.addr_space.profile.metadata.get('memory_model', '32bit')

        if memory_model == '32bit':
            mode = distorm3.Decode32Bits
            func = "sysenter_do_call"
        else:
            mode = distorm3.Decode64Bits
            func = "system_call_fastpath"

        func_addr = self.addr_space.profile.get_symbol(func)

        if func_addr:
            data = self.addr_space.read(func_addr, 6)
            
            for op in distorm3.Decompose(func_addr, data, mode):
                if not op.valid:
                    continue

                if op.mnemonic == 'CMP':
                    table_size = (op.operands[1].value) & 0xffffffff
                    break

        return table_size 
Example #7
Source File: apihooks_kernel.py    From vortessence with GNU General Public License v2.0 5 votes vote down vote up
def isPrologInlined(self, model, distorm_mode, func_addr):
        ##check if function prologs are modified
        inlined = False
        content = self.addr_space.read(func_addr, 24)

        op_cnt = 1
        for op in distorm3.Decompose(func_addr, content, distorm_mode):
            if op_cnt == 2:
                if model == "32bit":
                    if (op.mnemonic == "MOV" and len(op.operands) == 2 and op.operands[0].type == "Register" and
                            op.operands[1].type == "Register" and op.operands[0].name == "EBP" and op.operands[1].name == "ESP" and
                            prev_op.mnemonic == "PUSH" and len(prev_op.operands) == 1 and prev_op.operands[0].type == "Register" and prev_op.operands[0].name == "EBP"):
                        pass
                    else:
                        inlined = True
                elif model == "64bit":
                    if (op.mnemonic == "MOV" and len(op.operands) == 2 and op.operands[0].type == "Register" and
                            op.operands[1].type == "Register" and op.operands[0].name == "RBP" and op.operands[1].name == "RSP" and
                            prev_op.mnemonic == "PUSH" and len(prev_op.operands) == 1 and prev_op.operands[0].type == "Register" and prev_op.operands[0].name == "RBP"):
                        pass
                    elif (prev_op.mnemonic == "PUSH" and len(prev_op.operands) == 1 and prev_op.operands[0].type == "Register" and prev_op.operands[0].name == "RBP" and 
                          op.mnemonic == "PUSH" and len(op.operands) == 1 and op.operands[0].type == "Register" and op.operands[0].name in ["RSP","RBX","R12","R13","R14","R15"]):
                        # Registers preserved across calls, http://people.freebsd.org/~lstewart/references/amd64.pdf
                        pass
                    else:
                        inlined = True
                break
            prev_op = op
            op_cnt += 1

        return inlined

    # NOTES FROM ANDREW
    # This function orignally checked for any call outside the kernel module
    # This produces too many false positives so its modified to check if the call
    # is to a known module or a kernel symbol 
Example #8
Source File: check_syscall.py    From vortessence with GNU General Public License v2.0 5 votes vote down vote up
def _get_table_info_distorm(self):
        """
        Find the size of the system call table by disassembling functions
        that immediately reference it in their first isntruction
        This is in the form 'cmp reg,NR_syscalls'
        """
        table_size = 0

        if not has_distorm:
            return table_size

        memory_model = self.addr_space.profile.metadata.get('memory_model', '32bit')

        if memory_model == '32bit':
            mode = distorm3.Decode32Bits
            func = "sysenter_do_call"
        else:
            mode = distorm3.Decode64Bits
            func = "system_call_fastpath"

        func_addr = self.addr_space.profile.get_symbol(func)

        if func_addr:
            data = self.addr_space.read(func_addr, 6)
            
            for op in distorm3.Decompose(func_addr, data, mode):
                if not op.valid:
                    continue

                if op.mnemonic == 'CMP':
                    table_size = (op.operands[1].value) & 0xffffffff
                    break

        return table_size 
Example #9
Source File: check_syscall.py    From volatility with GNU General Public License v2.0 5 votes vote down vote up
def _get_table_info_distorm(self):
        """
        Find the size of the system call table by disassembling functions
        that immediately reference it in their first isntruction
        This is in the form 'cmp reg,NR_syscalls'
        """
        table_size = 0

        if not has_distorm:
            return table_size

        memory_model = self.addr_space.profile.metadata.get('memory_model', '32bit')

        if memory_model == '32bit':
            mode = distorm3.Decode32Bits
            func = "sysenter_do_call"
        else:
            mode = distorm3.Decode64Bits
            func = "system_call_fastpath"

        func_addr = self.addr_space.profile.get_symbol(func)

        if func_addr:
            data = self.addr_space.read(func_addr, 6)
            
            for op in distorm3.Decompose(func_addr, data, mode):
                if not op.valid:
                    continue

                if op.mnemonic == 'CMP':
                    table_size = (op.operands[1].value) & 0xffffffff
                    break

        return table_size 
Example #10
Source File: apihooks_kernel.py    From volatility with GNU General Public License v2.0 5 votes vote down vote up
def isPrologInlined(self, model, distorm_mode, func_addr):
        ##check if function prologs are modified
        inlined = False
        content = self.addr_space.read(func_addr, 24)

        op_cnt = 1
        for op in distorm3.Decompose(func_addr, content, distorm_mode):
            if op_cnt == 2:
                if model == "32bit":
                    if (op.mnemonic == "MOV" and len(op.operands) == 2 and op.operands[0].type == "Register" and
                            op.operands[1].type == "Register" and op.operands[0].name == "EBP" and op.operands[1].name == "ESP" and
                            prev_op.mnemonic == "PUSH" and len(prev_op.operands) == 1 and prev_op.operands[0].type == "Register" and prev_op.operands[0].name == "EBP"):
                        pass
                    else:
                        inlined = True
                elif model == "64bit":
                    if (op.mnemonic == "MOV" and len(op.operands) == 2 and op.operands[0].type == "Register" and
                            op.operands[1].type == "Register" and op.operands[0].name == "RBP" and op.operands[1].name == "RSP" and
                            prev_op.mnemonic == "PUSH" and len(prev_op.operands) == 1 and prev_op.operands[0].type == "Register" and prev_op.operands[0].name == "RBP"):
                        pass
                    elif (prev_op.mnemonic == "PUSH" and len(prev_op.operands) == 1 and prev_op.operands[0].type == "Register" and prev_op.operands[0].name == "RBP" and 
                          op.mnemonic == "PUSH" and len(op.operands) == 1 and op.operands[0].type == "Register" and op.operands[0].name in ["RSP","RBX","R12","R13","R14","R15"]):
                        # Registers preserved across calls, http://people.freebsd.org/~lstewart/references/amd64.pdf
                        pass
                    else:
                        inlined = True
                break
            prev_op = op
            op_cnt += 1

        return inlined

    # NOTES FROM ANDREW
    # This function orignally checked for any call outside the kernel module
    # This produces too many false positives so its modified to check if the call
    # is to a known module or a kernel symbol 
Example #11
Source File: apihooks_kernel.py    From volatility with GNU General Public License v2.0 5 votes vote down vote up
def isPrologInlined(self, model, distorm_mode, func_addr):
        ##check if function prologs are modified
        inlined = False
        content = self.addr_space.read(func_addr, 24)

        op_cnt = 1
        for op in distorm3.Decompose(func_addr, content, distorm_mode):
            if op_cnt == 2:
                if model == "32bit":
                    if (op.mnemonic == "MOV" and len(op.operands) == 2 and op.operands[0].type == "Register" and
                            op.operands[1].type == "Register" and op.operands[0].name == "EBP" and op.operands[1].name == "ESP" and
                            prev_op.mnemonic == "PUSH" and len(prev_op.operands) == 1 and prev_op.operands[0].type == "Register" and prev_op.operands[0].name == "EBP"):
                        pass
                    else:
                        inlined = True
                elif model == "64bit":
                    if (op.mnemonic == "MOV" and len(op.operands) == 2 and op.operands[0].type == "Register" and
                            op.operands[1].type == "Register" and op.operands[0].name == "RBP" and op.operands[1].name == "RSP" and
                            prev_op.mnemonic == "PUSH" and len(prev_op.operands) == 1 and prev_op.operands[0].type == "Register" and prev_op.operands[0].name == "RBP"):
                        pass
                    elif (prev_op.mnemonic == "PUSH" and len(prev_op.operands) == 1 and prev_op.operands[0].type == "Register" and prev_op.operands[0].name == "RBP" and 
                          op.mnemonic == "PUSH" and len(op.operands) == 1 and op.operands[0].type == "Register" and op.operands[0].name in ["RSP","RBX","R12","R13","R14","R15"]):
                        # Registers preserved across calls, http://people.freebsd.org/~lstewart/references/amd64.pdf
                        pass
                    else:
                        inlined = True
                break
            prev_op = op
            op_cnt += 1

        return inlined

    # NOTES FROM ANDREW
    # This function orignally checked for any call outside the kernel module
    # This produces too many false positives so its modified to check if the call
    # is to a known module or a kernel symbol 
Example #12
Source File: check_syscall.py    From volatility with GNU General Public License v2.0 5 votes vote down vote up
def _get_table_info_distorm(self):
        """
        Find the size of the system call table by disassembling functions
        that immediately reference it in their first isntruction
        This is in the form 'cmp reg,NR_syscalls'
        """
        table_size = 0

        if not has_distorm:
            return table_size

        memory_model = self.addr_space.profile.metadata.get('memory_model', '32bit')

        if memory_model == '32bit':
            mode = distorm3.Decode32Bits
            funcs = ["sysenter_do_call"]
        else:
            mode = distorm3.Decode64Bits
            funcs = ["system_call_fastpath", "do_int80_syscall_32"]

        for func in funcs:
            func_addr = self.addr_space.profile.get_symbol(func)
            if func_addr:
                data = self.addr_space.read(func_addr, 64)

                for op in distorm3.Decompose(func_addr, data, mode):
                    if not op.valid:
                        continue

                    if op.mnemonic == 'CMP':
                        table_size = (op.operands[1].value) & 0xffffffff
                        break

                break

        return table_size 
Example #13
Source File: apihooks_kernel.py    From aumfor with GNU General Public License v3.0 5 votes vote down vote up
def isPrologInlined(self, model, distorm_mode, func_addr):
        ##check if function prologs are modified
        inlined = False
        content = self.addr_space.read(func_addr, 24)

        op_cnt = 1
        for op in distorm3.Decompose(func_addr, content, distorm_mode):
            if op_cnt == 2:
                if model == "32bit":
                    if (op.mnemonic == "MOV" and len(op.operands) == 2 and op.operands[0].type == "Register" and
                            op.operands[1].type == "Register" and op.operands[0].name == "EBP" and op.operands[1].name == "ESP" and
                            prev_op.mnemonic == "PUSH" and len(prev_op.operands) == 1 and prev_op.operands[0].type == "Register" and prev_op.operands[0].name == "EBP"):
                        pass
                    else:
                        inlined = True
                elif model == "64bit":
                    if (op.mnemonic == "MOV" and len(op.operands) == 2 and op.operands[0].type == "Register" and
                            op.operands[1].type == "Register" and op.operands[0].name == "RBP" and op.operands[1].name == "RSP" and
                            prev_op.mnemonic == "PUSH" and len(prev_op.operands) == 1 and prev_op.operands[0].type == "Register" and prev_op.operands[0].name == "RBP"):
                        pass
                    elif (prev_op.mnemonic == "PUSH" and len(prev_op.operands) == 1 and prev_op.operands[0].type == "Register" and prev_op.operands[0].name == "RBP" and 
                          op.mnemonic == "PUSH" and len(op.operands) == 1 and op.operands[0].type == "Register" and op.operands[0].name in ["RSP","RBX","R12","R13","R14","R15"]):
                        # Registers preserved across calls, http://people.freebsd.org/~lstewart/references/amd64.pdf
                        pass
                    else:
                        inlined = True
                break
            prev_op = op
            op_cnt += 1

        return inlined

    # NOTES FROM ANDREW
    # This function orignally checked for any call outside the kernel module
    # This produces too many false positives so its modified to check if the call
    # is to a known module or a kernel symbol 
Example #14
Source File: apihooks_kernel.py    From volatility with GNU General Public License v2.0 4 votes vote down vote up
def isCallReferenceModified(self, model, distorm_mode, func_addr, kernel_syms, kmods):
        # check if CALL targets are within the kernel/kext range to detect possible call reference modification

        modified = False

        #modified malware/apihooks.py/check_inline function
        data = self.addr_space.read(func_addr, 750)

        # Number of instructions disassembled so far
        n = 0
        # Destination address of hooks
        d = None
        # Save the last PUSH before a CALL
        push_val = None
        # Save the general purpose registers
        regs = {}
        ops = []

        for op in distorm3.Decompose(func_addr, data, distorm_mode):
            ops.append(op)

        for op in distorm3.Decompose(func_addr, data, distorm_mode):
            # Quit when a decomposition error is encountered
            # or when reach function end
            if not op.valid or op.mnemonic == "NOP":
                break

            if op.flowControl == 'FC_CALL':
                # Clear the push value
                if push_val:
                    push_val = None
                if op.mnemonic == "CALL" and op.operands[0].type == 'AbsoluteMemoryAddress':
                    # Check for CALL [ADDR]
                    if model == '32bit':
                        const = op.operands[0].disp & 0xFFFFFFFF
                        d = obj.Object("unsigned int", offset = const, vm = self.addr_space)
                    else: 
                        const = op.operands[0].disp
                        d = obj.Object("unsigned long long", offset = const, vm = self.addr_space)
                    if self.outside_module(d, kernel_syms, kmods):
                        break
                elif op.operands[0].type == 'Immediate':
                    # Check for CALL ADDR
                    d = op.operands[0].value
                    if self.outside_module(d, kernel_syms, kmods):
                        break
                elif op.operands[0].type == 'Register':
                    # Check for CALL REG
                    d = regs.get(op.operands[0].name)
                    if d and self.outside_module(d, kernel_syms, kmods):
                        break
            n += 1

        # filtering out false positives due to structs, you can tweak this as needed 
        if d and self.outside_module(d, kernel_syms, kmods) == True and str(ops[n+1].mnemonic) not in ["DB 0xff", "ADD", "XCHG", "OUTS"]:
            modified = True

        return (modified, d) 
Example #15
Source File: callbacks.py    From DAMM with GNU General Public License v2.0 4 votes vote down vote up
def get_registry_callbacks_legacy(nt_mod):
        """
        Enumerate registry change callbacks.

        This method of finding a global variable via disassembly of the 
        CmRegisterCallback function is only for XP systems. If it fails on 
        XP you can still find the callbacks using PoolScanGenericCallback. 

        On Vista and Windows 7, these callbacks are registered using the 
        CmRegisterCallbackEx function. 
        """

        if not has_distorm3:
            return

        symbol = "CmRegisterCallback"

        # Get the RVA of the symbol from NT's EAT
        symbol_rva = nt_mod.getprocaddress(symbol)
        if symbol_rva == None:
            return

        # Absolute VA to the symbol code 
        symbol_address = symbol_rva + nt_mod.DllBase

        # Read the function prologue 
        data = nt_mod.obj_vm.zread(symbol_address, 200)

        c = 0
        vector = None

        # Looking for MOV EBX, CmpCallBackVector
        # This may be the first or second MOV EBX instruction
        for op in distorm3.Decompose(symbol_address, data, distorm3.Decode32Bits):
            if (op.valid and op.mnemonic == "MOV" 
                        and len(op.operands) == 2 
                        and op.operands[0].name == 'EBX'):
                vector = op.operands[1].value
                if c == 1:
                    break
                else:
                    c += 1

        # Can't find the global variable 
        if vector == None:
            return

        # The vector is an array of 100 _EX_FAST_REF objects
        addrs = obj.Object("Array", count = 100, offset = vector,
                    vm = nt_mod.obj_vm, targetType = "_EX_FAST_REF")

        for addr in addrs:
            callback = addr.dereference_as("_EX_CALLBACK_ROUTINE_BLOCK")
            if callback:
                yield symbol, callback.Function, None 
Example #16
Source File: check_syscall_shadow.py    From volatility with GNU General Public License v2.0 4 votes vote down vote up
def shadowedSyscalls(self, model, distorm_mode, sysents_addr):
        #looks like these syscall functions end with a call to _thread_exception_return
        thread_exc_ret_addr = self.addr_space.profile.get_symbol('_thread_exception_return')

        prev_op = None
        sysent_funcs = ['_unix_syscall_return', '_unix_syscall64', '_unix_syscall']
        for func in sysent_funcs:
            func_addr = self.addr_space.profile.get_symbol(func)
            content = self.addr_space.read(func_addr, 1024)
            for op in distorm3.Decompose(func_addr, content, distorm_mode):
                if not op.valid:
                    break

                if op.mnemonic == "CALL" and op.operands[0].value == thread_exc_ret_addr:
                    break

                if model == "64bit":
                    #callp = &sysent[63] OR &sysent[code] OR callp == sysent
                    if op.mnemonic in ['ADD','CMP'] and op.operands[0].type == 'Register' and op.operands[0].name in ["RSP","RBX","R12","R13","R14","R15"] and 'FLAG_RIP_RELATIVE' in op.flags:
                        #compare actual sysent tbl address to the one in the instruction, calculated per distorm3 INSTRUCTION_GET_RIP_TARGET

                        op_sysent_ptr = obj.Object('Pointer', offset = (op.address + op.operands[1].disp + op.size), vm = self.addr_space)
 
                        if sysents_addr != op_sysent_ptr.v():
                            print "not same: %x | %x" % (sysents_addr, op_sysent_ptr.v())
                            yield (op_sysent_ptr.v(), func, op)
 
                elif model == "32bit":
                    #LEA EAX, [EAX*8+0x82ef20]
                    if op.mnemonic == 'LEA' and op.operands[0].type == 'Register' and op.operands[0].name in ['EDI','EAX'] and distorm3.Registers[op.operands[1].index] == "EAX" and op.operands[1].scale == 8:
                        if op.operands[1].disp != sysents_addr:
                            shadowtbl_addr = op.operands[1].disp
                            yield (shadowtbl_addr, func, op) 
                            break
                    #CMP EAX, 0x82ef20
                    elif op.mnemonic == 'CMP' and op.operands[0].type == 'Register' and op.operands[0].name in ['EDI','EAX'] and prev_op.mnemonic in ['LEA','MOV'] and self.addr_space.is_valid_address(op.operands[1].value) == True:
                        if op.operands[1].value != sysents_addr:
                            shadowtbl_addr = op.operands[1].value
                            yield (shadowtbl_addr, func, op)

                    #CMP DWORD [EBP-0x20], 0x82ef20
                    elif op.mnemonic == 'CMP' and op.operands[0].index != None and distorm3.Registers[op.operands[0].index] == "EBP" and op.operands[0].disp == -32 and op.operands[0].type == "Immediate":
                        if op.operands[1].value != sysents_addr:
                            shadowtbl_addr = op.operands[1].value
                            yield (shadowtbl_addr, func, op)
 
                prev_op = op 
Example #17
Source File: callbacks.py    From volatility with GNU General Public License v2.0 4 votes vote down vote up
def get_registry_callbacks_legacy(nt_mod):
        """
        Enumerate registry change callbacks.

        This method of finding a global variable via disassembly of the 
        CmRegisterCallback function is only for XP systems. If it fails on 
        XP you can still find the callbacks using PoolScanGenericCallback. 

        On Vista and Windows 7, these callbacks are registered using the 
        CmRegisterCallbackEx function. 
        """

        if not has_distorm3:
            return

        symbol = "CmRegisterCallback"

        # Get the RVA of the symbol from NT's EAT
        symbol_rva = nt_mod.getprocaddress(symbol)
        if symbol_rva == None:
            return

        # Absolute VA to the symbol code 
        symbol_address = symbol_rva + nt_mod.DllBase

        # Read the function prologue 
        data = nt_mod.obj_vm.zread(symbol_address, 200)

        c = 0
        vector = None

        # Looking for MOV EBX, CmpCallBackVector
        # This may be the first or second MOV EBX instruction
        for op in distorm3.Decompose(symbol_address, data, distorm3.Decode32Bits):
            if (op.valid and op.mnemonic == "MOV" 
                        and len(op.operands) == 2 
                        and op.operands[0].name == 'EBX'):
                vector = op.operands[1].value
                if c == 1:
                    break
                else:
                    c += 1

        # Can't find the global variable 
        if vector == None:
            return

        # The vector is an array of 100 _EX_FAST_REF objects
        addrs = obj.Object("Array", count = 100, offset = vector,
                    vm = nt_mod.obj_vm, targetType = "_EX_FAST_REF")

        for addr in addrs:
            callback = addr.dereference_as("_EX_CALLBACK_ROUTINE_BLOCK")
            if callback:
                yield symbol, callback.Function, None 
Example #18
Source File: check_syscall_shadow.py    From DAMM with GNU General Public License v2.0 4 votes vote down vote up
def shadowedSyscalls(self, model, distorm_mode, sysents_addr):
        #looks like these syscall functions end with a call to _thread_exception_return
        thread_exc_ret_addr = self.addr_space.profile.get_symbol('_thread_exception_return')

        prev_op = None
        sysent_funcs = ['_unix_syscall_return', '_unix_syscall64', '_unix_syscall']
        for func in sysent_funcs:
            func_addr = self.addr_space.profile.get_symbol(func)
            content = self.addr_space.read(func_addr, 1024)
            for op in distorm3.Decompose(func_addr, content, distorm_mode):
                if not op.valid:
                    break

                if op.mnemonic == "CALL" and op.operands[0].value == thread_exc_ret_addr:
                    break

                if model == "64bit":
                    #callp = &sysent[63] OR &sysent[code] OR callp == sysent
                    if op.mnemonic in ['ADD','CMP'] and op.operands[0].type == 'Register' and op.operands[0].name in ["RSP","RBX","R12","R13","R14","R15"] and 'FLAG_RIP_RELATIVE' in op.flags:
                        #compare actual sysent tbl address to the one in the instruction, calculated per distorm3 INSTRUCTION_GET_RIP_TARGET

                        op_sysent_ptr = obj.Object('Pointer', offset = (op.address + op.operands[1].disp + op.size), vm = self.addr_space)
 
                        if sysents_addr != op_sysent_ptr.v():
                            print "not same: %x | %x" % (sysents_addr, op_sysent_ptr.v())
                            yield (op_sysent_ptr.v(), func, op)
 
                elif model == "32bit":
                    #LEA EAX, [EAX*8+0x82ef20]
                    if op.mnemonic == 'LEA' and op.operands[0].type == 'Register' and op.operands[0].name in ['EDI','EAX'] and distorm3.Registers[op.operands[1].index] == "EAX" and op.operands[1].scale == 8:
                        if op.operands[1].disp != sysents_addr:
                            shadowtbl_addr = op.operands[1].disp
                            yield (shadowtbl_addr, func, op) 
                            break
                    #CMP EAX, 0x82ef20
                    elif op.mnemonic == 'CMP' and op.operands[0].type == 'Register' and op.operands[0].name in ['EDI','EAX'] and prev_op.mnemonic in ['LEA','MOV'] and self.addr_space.is_valid_address(op.operands[1].value) == True:
                        if op.operands[1].value != sysents_addr:
                            shadowtbl_addr = op.operands[1].value
                            yield (shadowtbl_addr, func, op)

                    #CMP DWORD [EBP-0x20], 0x82ef20
                    elif op.mnemonic == 'CMP' and op.operands[0].index != None and distorm3.Registers[op.operands[0].index] == "EBP" and op.operands[0].disp == -32 and op.operands[0].type == "Immediate":
                        if op.operands[1].value != sysents_addr:
                            shadowtbl_addr = op.operands[1].value
                            yield (shadowtbl_addr, func, op)
 
                prev_op = op 
Example #19
Source File: check_syscall_shadow.py    From vortessence with GNU General Public License v2.0 4 votes vote down vote up
def shadowedSyscalls(self, model, distorm_mode, sysents_addr):
        #looks like these syscall functions end with a call to _thread_exception_return
        thread_exc_ret_addr = self.addr_space.profile.get_symbol('_thread_exception_return')

        prev_op = None
        sysent_funcs = ['_unix_syscall_return', '_unix_syscall64', '_unix_syscall']
        for func in sysent_funcs:
            func_addr = self.addr_space.profile.get_symbol(func)
            content = self.addr_space.read(func_addr, 1024)
            for op in distorm3.Decompose(func_addr, content, distorm_mode):
                if not op.valid:
                    break

                if op.mnemonic == "CALL" and op.operands[0].value == thread_exc_ret_addr:
                    break

                if model == "64bit":
                    #callp = &sysent[63] OR &sysent[code] OR callp == sysent
                    if op.mnemonic in ['ADD','CMP'] and op.operands[0].type == 'Register' and op.operands[0].name in ["RSP","RBX","R12","R13","R14","R15"] and 'FLAG_RIP_RELATIVE' in op.flags:
                        #compare actual sysent tbl address to the one in the instruction, calculated per distorm3 INSTRUCTION_GET_RIP_TARGET

                        op_sysent_ptr = obj.Object('Pointer', offset = (op.address + op.operands[1].disp + op.size), vm = self.addr_space)
 
                        if sysents_addr != op_sysent_ptr.v():
                            print "not same: %x | %x" % (sysents_addr, op_sysent_ptr.v())
                            yield (op_sysent_ptr.v(), func, op)
 
                elif model == "32bit":
                    #LEA EAX, [EAX*8+0x82ef20]
                    if op.mnemonic == 'LEA' and op.operands[0].type == 'Register' and op.operands[0].name in ['EDI','EAX'] and distorm3.Registers[op.operands[1].index] == "EAX" and op.operands[1].scale == 8:
                        if op.operands[1].disp != sysents_addr:
                            shadowtbl_addr = op.operands[1].disp
                            yield (shadowtbl_addr, func, op) 
                            break
                    #CMP EAX, 0x82ef20
                    elif op.mnemonic == 'CMP' and op.operands[0].type == 'Register' and op.operands[0].name in ['EDI','EAX'] and prev_op.mnemonic in ['LEA','MOV'] and self.addr_space.is_valid_address(op.operands[1].value) == True:
                        if op.operands[1].value != sysents_addr:
                            shadowtbl_addr = op.operands[1].value
                            yield (shadowtbl_addr, func, op)

                    #CMP DWORD [EBP-0x20], 0x82ef20
                    elif op.mnemonic == 'CMP' and op.operands[0].index != None and distorm3.Registers[op.operands[0].index] == "EBP" and op.operands[0].disp == -32 and op.operands[0].type == "Immediate":
                        if op.operands[1].value != sysents_addr:
                            shadowtbl_addr = op.operands[1].value
                            yield (shadowtbl_addr, func, op)
 
                prev_op = op 
Example #20
Source File: apihooks_kernel.py    From DAMM with GNU General Public License v2.0 4 votes vote down vote up
def isCallReferenceModified(self, model, distorm_mode, func_addr, kernel_syms, kmods):
        # check if CALL targets are within the kernel/kext range to detect possible call reference modification

        modified = False

        #modified malware/apihooks.py/check_inline function
        data = self.addr_space.read(func_addr, 750)

        # Number of instructions disassembled so far
        n = 0
        # Destination address of hooks
        d = None
        # Save the last PUSH before a CALL
        push_val = None
        # Save the general purpose registers
        regs = {}
        ops = []

        for op in distorm3.Decompose(func_addr, data, distorm_mode):
            ops.append(op)

        for op in distorm3.Decompose(func_addr, data, distorm_mode):
            # Quit when a decomposition error is encountered
            # or when reach function end
            if not op.valid or op.mnemonic == "NOP":
                break

            if op.flowControl == 'FC_CALL':
                # Clear the push value
                if push_val:
                    push_val = None
                if op.mnemonic == "CALL" and op.operands[0].type == 'AbsoluteMemoryAddress':
                    # Check for CALL [ADDR]
                    if model == '32bit':
                        const = op.operands[0].disp & 0xFFFFFFFF
                        d = obj.Object("unsigned int", offset = const, vm = self.addr_space)
                    else: 
                        const = op.operands[0].disp
                        d = obj.Object("unsigned long long", offset = const, vm = self.addr_space)
                    if self.outside_module(d, kernel_syms, kmods):
                        break
                elif op.operands[0].type == 'Immediate':
                    # Check for CALL ADDR
                    d = op.operands[0].value
                    if self.outside_module(d, kernel_syms, kmods):
                        break
                elif op.operands[0].type == 'Register':
                    # Check for CALL REG
                    d = regs.get(op.operands[0].name)
                    if d and self.outside_module(d, kernel_syms, kmods):
                        break
            n += 1

        # filtering out false positives due to structs, you can tweak this as needed 
        if d and self.outside_module(d, kernel_syms, kmods) == True and str(ops[n+1].mnemonic) not in ["DB 0xff", "ADD", "XCHG", "OUTS"]:
            modified = True

        return (modified, d) 
Example #21
Source File: callbacks.py    From vortessence with GNU General Public License v2.0 4 votes vote down vote up
def get_registry_callbacks_legacy(nt_mod):
        """
        Enumerate registry change callbacks.

        This method of finding a global variable via disassembly of the 
        CmRegisterCallback function is only for XP systems. If it fails on 
        XP you can still find the callbacks using PoolScanGenericCallback. 

        On Vista and Windows 7, these callbacks are registered using the 
        CmRegisterCallbackEx function. 
        """

        if not has_distorm3:
            return

        symbol = "CmRegisterCallback"

        # Get the RVA of the symbol from NT's EAT
        symbol_rva = nt_mod.getprocaddress(symbol)
        if symbol_rva == None:
            return

        # Absolute VA to the symbol code 
        symbol_address = symbol_rva + nt_mod.DllBase

        # Read the function prologue 
        data = nt_mod.obj_vm.zread(symbol_address, 200)

        c = 0
        vector = None

        # Looking for MOV EBX, CmpCallBackVector
        # This may be the first or second MOV EBX instruction
        for op in distorm3.Decompose(symbol_address, data, distorm3.Decode32Bits):
            if (op.valid and op.mnemonic == "MOV" 
                        and len(op.operands) == 2 
                        and op.operands[0].name == 'EBX'):
                vector = op.operands[1].value
                if c == 1:
                    break
                else:
                    c += 1

        # Can't find the global variable 
        if vector == None:
            return

        # The vector is an array of 100 _EX_FAST_REF objects
        addrs = obj.Object("Array", count = 100, offset = vector,
                    vm = nt_mod.obj_vm, targetType = "_EX_FAST_REF")

        for addr in addrs:
            callback = addr.dereference_as("_EX_CALLBACK_ROUTINE_BLOCK")
            if callback:
                yield symbol, callback.Function, None 
Example #22
Source File: apihooks_kernel.py    From vortessence with GNU General Public License v2.0 4 votes vote down vote up
def isCallReferenceModified(self, model, distorm_mode, func_addr, kernel_syms, kmods):
        # check if CALL targets are within the kernel/kext range to detect possible call reference modification

        modified = False

        #modified malware/apihooks.py/check_inline function
        data = self.addr_space.read(func_addr, 750)

        # Number of instructions disassembled so far
        n = 0
        # Destination address of hooks
        d = None
        # Save the last PUSH before a CALL
        push_val = None
        # Save the general purpose registers
        regs = {}
        ops = []

        for op in distorm3.Decompose(func_addr, data, distorm_mode):
            ops.append(op)

        for op in distorm3.Decompose(func_addr, data, distorm_mode):
            # Quit when a decomposition error is encountered
            # or when reach function end
            if not op.valid or op.mnemonic == "NOP":
                break

            if op.flowControl == 'FC_CALL':
                # Clear the push value
                if push_val:
                    push_val = None
                if op.mnemonic == "CALL" and op.operands[0].type == 'AbsoluteMemoryAddress':
                    # Check for CALL [ADDR]
                    if model == '32bit':
                        const = op.operands[0].disp & 0xFFFFFFFF
                        d = obj.Object("unsigned int", offset = const, vm = self.addr_space)
                    else: 
                        const = op.operands[0].disp
                        d = obj.Object("unsigned long long", offset = const, vm = self.addr_space)
                    if self.outside_module(d, kernel_syms, kmods):
                        break
                elif op.operands[0].type == 'Immediate':
                    # Check for CALL ADDR
                    d = op.operands[0].value
                    if self.outside_module(d, kernel_syms, kmods):
                        break
                elif op.operands[0].type == 'Register':
                    # Check for CALL REG
                    d = regs.get(op.operands[0].name)
                    if d and self.outside_module(d, kernel_syms, kmods):
                        break
            n += 1

        # filtering out false positives due to structs, you can tweak this as needed 
        if d and self.outside_module(d, kernel_syms, kmods) == True and str(ops[n+1].mnemonic) not in ["DB 0xff", "ADD", "XCHG", "OUTS"]:
            modified = True

        return (modified, d) 
Example #23
Source File: callbacks.py    From volatility with GNU General Public License v2.0 4 votes vote down vote up
def get_registry_callbacks_legacy(nt_mod):
        """
        Enumerate registry change callbacks.

        This method of finding a global variable via disassembly of the 
        CmRegisterCallback function is only for XP systems. If it fails on 
        XP you can still find the callbacks using PoolScanGenericCallback. 

        On Vista and Windows 7, these callbacks are registered using the 
        CmRegisterCallbackEx function. 
        """

        if not has_distorm3:
            return

        symbol = "CmRegisterCallback"

        # Get the RVA of the symbol from NT's EAT
        symbol_rva = nt_mod.getprocaddress(symbol)
        if symbol_rva == None:
            return

        # Absolute VA to the symbol code 
        symbol_address = symbol_rva + nt_mod.DllBase

        # Read the function prologue 
        data = nt_mod.obj_vm.zread(symbol_address, 200)

        c = 0
        vector = None

        # Looking for MOV EBX, CmpCallBackVector
        # This may be the first or second MOV EBX instruction
        for op in distorm3.Decompose(symbol_address, data, distorm3.Decode32Bits):
            if (op.valid and op.mnemonic == "MOV" 
                        and len(op.operands) == 2 
                        and op.operands[0].name == 'EBX'):
                vector = op.operands[1].value
                if c == 1:
                    break
                else:
                    c += 1

        # Can't find the global variable 
        if vector == None:
            return

        # The vector is an array of 100 _EX_FAST_REF objects
        addrs = obj.Object("Array", count = 100, offset = vector,
                    vm = nt_mod.obj_vm, targetType = "_EX_FAST_REF")

        for addr in addrs:
            callback = addr.dereference_as("_EX_CALLBACK_ROUTINE_BLOCK")
            if callback:
                yield symbol, callback.Function, None 
Example #24
Source File: check_syscall_shadow.py    From volatility with GNU General Public License v2.0 4 votes vote down vote up
def shadowedSyscalls(self, model, distorm_mode, sysents_addr):
        #looks like these syscall functions end with a call to _thread_exception_return
        thread_exc_ret_addr = self.addr_space.profile.get_symbol('_thread_exception_return')

        prev_op = None
        sysent_funcs = ['_unix_syscall_return', '_unix_syscall64', '_unix_syscall']
        for func in sysent_funcs:
            func_addr = self.addr_space.profile.get_symbol(func)
            content = self.addr_space.read(func_addr, 1024)
            for op in distorm3.Decompose(func_addr, content, distorm_mode):
                if not op.valid:
                    break

                if op.mnemonic == "CALL" and op.operands[0].value == thread_exc_ret_addr:
                    break

                if model == "64bit":
                    #callp = &sysent[63] OR &sysent[code] OR callp == sysent
                    if op.mnemonic in ['ADD','CMP'] and op.operands[0].type == 'Register' and op.operands[0].name in ["RSP","RBX","R12","R13","R14","R15"] and 'FLAG_RIP_RELATIVE' in op.flags:
                        #compare actual sysent tbl address to the one in the instruction, calculated per distorm3 INSTRUCTION_GET_RIP_TARGET

                        op_sysent_ptr = obj.Object('Pointer', offset = (op.address + op.operands[1].disp + op.size), vm = self.addr_space)
 
                        if sysents_addr != op_sysent_ptr.v():
                            print "not same: %x | %x" % (sysents_addr, op_sysent_ptr.v())
                            yield (op_sysent_ptr.v(), func, op)
 
                elif model == "32bit":
                    #LEA EAX, [EAX*8+0x82ef20]
                    if op.mnemonic == 'LEA' and op.operands[0].type == 'Register' and op.operands[0].name in ['EDI','EAX'] and distorm3.Registers[op.operands[1].index] == "EAX" and op.operands[1].scale == 8:
                        if op.operands[1].disp != sysents_addr:
                            shadowtbl_addr = op.operands[1].disp
                            yield (shadowtbl_addr, func, op) 
                            break
                    #CMP EAX, 0x82ef20
                    elif op.mnemonic == 'CMP' and op.operands[0].type == 'Register' and op.operands[0].name in ['EDI','EAX'] and prev_op.mnemonic in ['LEA','MOV'] and self.addr_space.is_valid_address(op.operands[1].value) == True:
                        if op.operands[1].value != sysents_addr:
                            shadowtbl_addr = op.operands[1].value
                            yield (shadowtbl_addr, func, op)

                    #CMP DWORD [EBP-0x20], 0x82ef20
                    elif op.mnemonic == 'CMP' and op.operands[0].index != None and distorm3.Registers[op.operands[0].index] == "EBP" and op.operands[0].disp == -32 and op.operands[0].type == "Immediate":
                        if op.operands[1].value != sysents_addr:
                            shadowtbl_addr = op.operands[1].value
                            yield (shadowtbl_addr, func, op)
 
                prev_op = op 
Example #25
Source File: apihooks_kernel.py    From volatility with GNU General Public License v2.0 4 votes vote down vote up
def isCallReferenceModified(self, model, distorm_mode, func_addr, kernel_syms, kmods):
        # check if CALL targets are within the kernel/kext range to detect possible call reference modification

        modified = False

        #modified malware/apihooks.py/check_inline function
        data = self.addr_space.read(func_addr, 750)

        # Number of instructions disassembled so far
        n = 0
        # Destination address of hooks
        d = None
        # Save the last PUSH before a CALL
        push_val = None
        # Save the general purpose registers
        regs = {}
        ops = []

        for op in distorm3.Decompose(func_addr, data, distorm_mode):
            ops.append(op)

        for op in distorm3.Decompose(func_addr, data, distorm_mode):
            # Quit when a decomposition error is encountered
            # or when reach function end
            if not op.valid or op.mnemonic == "NOP":
                break

            if op.flowControl == 'FC_CALL':
                # Clear the push value
                if push_val:
                    push_val = None
                if op.mnemonic == "CALL" and op.operands[0].type == 'AbsoluteMemoryAddress':
                    # Check for CALL [ADDR]
                    if model == '32bit':
                        const = op.operands[0].disp & 0xFFFFFFFF
                        d = obj.Object("unsigned int", offset = const, vm = self.addr_space)
                    else: 
                        const = op.operands[0].disp
                        d = obj.Object("unsigned long long", offset = const, vm = self.addr_space)
                    if self.outside_module(d, kernel_syms, kmods):
                        break
                elif op.operands[0].type == 'Immediate':
                    # Check for CALL ADDR
                    d = op.operands[0].value
                    if self.outside_module(d, kernel_syms, kmods):
                        break
                elif op.operands[0].type == 'Register':
                    # Check for CALL REG
                    d = regs.get(op.operands[0].name)
                    if d and self.outside_module(d, kernel_syms, kmods):
                        break
            n += 1

        # filtering out false positives due to structs, you can tweak this as needed 
        if d and self.outside_module(d, kernel_syms, kmods) == True and str(ops[n+1].mnemonic) not in ["DB 0xff", "ADD", "XCHG", "OUTS"]:
            modified = True

        return (modified, d) 
Example #26
Source File: callbacks.py    From aumfor with GNU General Public License v3.0 4 votes vote down vote up
def get_registry_callbacks_legacy(nt_mod):
        """
        Enumerate registry change callbacks.

        This method of finding a global variable via disassembly of the 
        CmRegisterCallback function is only for XP systems. If it fails on 
        XP you can still find the callbacks using PoolScanGenericCallback. 

        On Vista and Windows 7, these callbacks are registered using the 
        CmRegisterCallbackEx function. 
        """

        if not has_distorm3:
            return

        symbol = "CmRegisterCallback"

        # Get the RVA of the symbol from NT's EAT
        symbol_rva = nt_mod.getprocaddress(symbol)
        if symbol_rva == None:
            return

        # Absolute VA to the symbol code 
        symbol_address = symbol_rva + nt_mod.DllBase

        # Read the function prologue 
        data = nt_mod.obj_vm.zread(symbol_address, 200)

        c = 0
        vector = None

        # Looking for MOV EBX, CmpCallBackVector
        # This may be the first or second MOV EBX instruction
        for op in distorm3.Decompose(symbol_address, data, distorm3.Decode32Bits):
            if (op.valid and op.mnemonic == "MOV" 
                        and len(op.operands) == 2 
                        and op.operands[0].name == 'EBX'):
                vector = op.operands[1].value
                if c == 1:
                    break
                else:
                    c += 1

        # Can't find the global variable 
        if vector == None:
            return

        # The vector is an array of 100 _EX_FAST_REF objects
        addrs = obj.Object("Array", count = 100, offset = vector,
                    vm = nt_mod.obj_vm, targetType = "_EX_FAST_REF")

        for addr in addrs:
            callback = addr.dereference_as("_EX_CALLBACK_ROUTINE_BLOCK")
            if callback:
                yield symbol, callback.Function, None 
Example #27
Source File: check_syscall_shadow.py    From aumfor with GNU General Public License v3.0 4 votes vote down vote up
def shadowedSyscalls(self, model, distorm_mode, sysents_addr):
        #looks like these syscall functions end with a call to _thread_exception_return
        thread_exc_ret_addr = self.addr_space.profile.get_symbol('_thread_exception_return')

        prev_op = None
        sysent_funcs = ['_unix_syscall_return', '_unix_syscall64', '_unix_syscall']
        for func in sysent_funcs:
            func_addr = self.addr_space.profile.get_symbol(func)
            content = self.addr_space.read(func_addr, 1024)
            for op in distorm3.Decompose(func_addr, content, distorm_mode):
                if not op.valid:
                    break

                if op.mnemonic == "CALL" and op.operands[0].value == thread_exc_ret_addr:
                    break

                if model == "64bit":
                    #callp = &sysent[63] OR &sysent[code] OR callp == sysent
                    if op.mnemonic in ['ADD','CMP'] and op.operands[0].type == 'Register' and op.operands[0].name in ["RSP","RBX","R12","R13","R14","R15"] and 'FLAG_RIP_RELATIVE' in op.flags:
                        #compare actual sysent tbl address to the one in the instruction, calculated per distorm3 INSTRUCTION_GET_RIP_TARGET

                        op_sysent_ptr = obj.Object('Pointer', offset = (op.address + op.operands[1].disp + op.size), vm = self.addr_space)
 
                        if sysents_addr != op_sysent_ptr.v():
                            print "not same: %x | %x" % (sysents_addr, op_sysent_ptr.v())
                            yield (op_sysent_ptr.v(), func, op)
 
                elif model == "32bit":
                    #LEA EAX, [EAX*8+0x82ef20]
                    if op.mnemonic == 'LEA' and op.operands[0].type == 'Register' and op.operands[0].name in ['EDI','EAX'] and distorm3.Registers[op.operands[1].index] == "EAX" and op.operands[1].scale == 8:
                        if op.operands[1].disp != sysents_addr:
                            shadowtbl_addr = op.operands[1].disp
                            yield (shadowtbl_addr, func, op) 
                            break
                    #CMP EAX, 0x82ef20
                    elif op.mnemonic == 'CMP' and op.operands[0].type == 'Register' and op.operands[0].name in ['EDI','EAX'] and prev_op.mnemonic in ['LEA','MOV'] and self.addr_space.is_valid_address(op.operands[1].value) == True:
                        if op.operands[1].value != sysents_addr:
                            shadowtbl_addr = op.operands[1].value
                            yield (shadowtbl_addr, func, op)

                    #CMP DWORD [EBP-0x20], 0x82ef20
                    elif op.mnemonic == 'CMP' and op.operands[0].index != None and distorm3.Registers[op.operands[0].index] == "EBP" and op.operands[0].disp == -32 and op.operands[0].type == "Immediate":
                        if op.operands[1].value != sysents_addr:
                            shadowtbl_addr = op.operands[1].value
                            yield (shadowtbl_addr, func, op)
 
                prev_op = op 
Example #28
Source File: apihooks_kernel.py    From aumfor with GNU General Public License v3.0 4 votes vote down vote up
def isCallReferenceModified(self, model, distorm_mode, func_addr, kernel_syms, kmods):
        # check if CALL targets are within the kernel/kext range to detect possible call reference modification

        modified = False

        #modified malware/apihooks.py/check_inline function
        data = self.addr_space.read(func_addr, 750)

        # Number of instructions disassembled so far
        n = 0
        # Destination address of hooks
        d = None
        # Save the last PUSH before a CALL
        push_val = None
        # Save the general purpose registers
        regs = {}
        ops = []

        for op in distorm3.Decompose(func_addr, data, distorm_mode):
            ops.append(op)

        for op in distorm3.Decompose(func_addr, data, distorm_mode):
            # Quit when a decomposition error is encountered
            # or when reach function end
            if not op.valid or op.mnemonic == "NOP":
                break

            if op.flowControl == 'FC_CALL':
                # Clear the push value
                if push_val:
                    push_val = None
                if op.mnemonic == "CALL" and op.operands[0].type == 'AbsoluteMemoryAddress':
                    # Check for CALL [ADDR]
                    if model == '32bit':
                        const = op.operands[0].disp & 0xFFFFFFFF
                        d = obj.Object("unsigned int", offset = const, vm = self.addr_space)
                    else: 
                        const = op.operands[0].disp
                        d = obj.Object("unsigned long long", offset = const, vm = self.addr_space)
                    if self.outside_module(d, kernel_syms, kmods):
                        break
                elif op.operands[0].type == 'Immediate':
                    # Check for CALL ADDR
                    d = op.operands[0].value
                    if self.outside_module(d, kernel_syms, kmods):
                        break
                elif op.operands[0].type == 'Register':
                    # Check for CALL REG
                    d = regs.get(op.operands[0].name)
                    if d and self.outside_module(d, kernel_syms, kmods):
                        break
            n += 1

        # filtering out false positives due to structs, you can tweak this as needed 
        if d and self.outside_module(d, kernel_syms, kmods) == True and str(ops[n+1].mnemonic) not in ["DB 0xff", "ADD", "XCHG", "OUTS"]:
            modified = True

        return (modified, d)