예제 #1
0
def test_instruction_hooking():
    """Test instruction hooking mechanism"""
    from kordesii.utils import function_tracing

    emulator = function_tracing.Emulator()

    # Test hooking all "push" instructions, which will be the parameters to the xor decryption.
    pushes = []
    def push_hook(context, ip, mnem, operands):
        pushes.append(operands[0].value)
    emulator.hook_instruction("push", push_hook)
    context = emulator.context_at(0x00401142)
    # fmt: off
    assert pushes == [
        0x117fc00,  # ebp pushed
        # key, enc_data_ptr
        0x1, 0x40c000,
        0x2, 0x40c010,
        0x3, 0x40c02c,
        0x4, 0x40c05c,
        0x5, 0x40c080,
        0x6, 0x40c0a0,
        0x13, 0x40c0c4,
        0x17, 0x40c0f0,
        0x1a, 0x40c114,
        0x23, 0x40c120,
        0x27, 0x40c130,
        0x40, 0x40c138,
        0x46, 0x40c140,
        0x73, 0x40c15c,
        0x75, 0x40c174,
        0x77, 0x40c19c,
        0x7a, 0x40c1c4,
        0x7f, 0x40c1f8,
    ]
예제 #2
0
def test_issue_7():
    """Tests the use of WIDE_STRING for read_data()"""
    from kordesii.utils import function_tracing

    emulator = function_tracing.Emulator()
    context = emulator.new_context()

    wide_string = b"/\x00f\x00a\x00v\x00.\x00i\x00c\x00o\x00"
    context.memory.write(0x123000, wide_string)
    assert context.read_data(0x123000, data_type=function_tracing.WIDE_STRING) == wide_string
    wide_string = b"\x00/\x00f\x00a\x00v\x00.\x00i\x00c\x00o"
    context.memory.write(0x123000, wide_string)
    assert context.read_data(0x123000, data_type=function_tracing.WIDE_STRING) == wide_string
예제 #3
0
def test_objects_and_actions():
    """Test the objects and actions feature."""
    from kordesii.utils import function_tracing
    from kordesii.utils.function_tracing import actions, objects

    emulator = function_tracing.Emulator()

    # NOTE: We are just going to fake adding actions since our strings.exe example
    # doesn't perform any actions.
    offset = 0x00401158
    ctx = emulator.context_at(offset)
    assert not ctx.actions
    handle = ctx.objects.alloc()
    assert handle == 0x80
    ctx.actions.add(
        actions.FileOpened(ip=offset - 3,
                           handle=handle,
                           path=r"C:\dummy\path",
                           mode="w"))
    ctx.actions.add(
        actions.FileWritten(ip=offset - 2,
                            handle=handle,
                            data=b"first bytes\n"))
    # Throw in some other random actions for good measure.
    ctx.actions.add(actions.CommandExecuted(ip=offset - 6, command="hello"))
    ctx.actions.add(
        actions.FileWritten(ip=offset - 1, handle=handle,
                            data=b"second bytes"))
    assert ctx.objects
    assert len(ctx.objects) == 1
    assert ctx.objects.handles == [handle]
    file_obj = ctx.objects[handle]
    assert file_obj
    assert file_obj.handle == handle
    assert isinstance(file_obj, objects.File)
    assert file_obj.path == r"C:\dummy\path"
    assert file_obj.mode == "w"
    assert file_obj.data == b"first bytes\nsecond bytes"
    assert file_obj.closed is False
    assert file_obj.references == [offset - 3, offset - 2, offset - 1]

    # Now test if we can detect when the right file is closed.
    # NOTE: We have to regrab the object for changes to take affect.
    sec_handle = ctx.objects.alloc()
    ctx.actions.add(actions.FileClosed(ip=offset, handle=sec_handle))
    assert len(ctx.objects) == 2
    assert ctx.objects[handle].closed is False
    ctx.actions.add(actions.FileClosed(ip=offset, handle=handle))
    assert ctx.objects[handle].closed is True
    assert ctx.objects.handles == [handle, sec_handle]
def trace_arguments(ea):
    """
    This is a function that would be almost impossible to do proxied due to
    the complexities of function_tracing.
    """
    from kordesii.utils import function_tracing

    emulator = function_tracing.Emulator()

    # Test that we can call other decorated functions.
    ida_assert(get_mnem(ea), 'mov')

    strings = []
    for context in emulator.iter_context_at(ea, depth=1):
        ida_assert(context.ip, ea)
        # mov     eax, [ebp+arg_0]
        strings.append(context.read_data(context.operands[1].value))

    return strings
예제 #5
0
def test_function_arg():
    """Tests FunctionArg object."""
    from kordesii.utils import function_tracing

    emulator = function_tracing.Emulator()

    xor_func_ea = 0x00401000
    xor_func_call = 0x0040103A

    # Basic tests.
    context = emulator.context_at(xor_func_call)
    args = context.get_function_arg_objects()
    assert len(args) == 2
    assert args[0].name == "a1"
    assert args[0].type == "_BYTE *"
    assert args[0].value == 0x0040C000  # pointer to b'Idmmn!Vnsme '
    assert args[0].addr == context.sp + 0
    assert args[1].name == "a2"
    assert args[1].type == "char"
    assert args[1].value == 1  # key
    assert args[1].addr == context.sp + 4
    # Test that we can change the values.
    args[0].value = 0xffff
    assert args[0].value == 0xffff
    assert args[0].addr == context.sp + 0
    assert context.read_data(args[0].addr, 4) == b'\xff\xff\x00\x00'

    # Test pulling in passed in arguments.
    context = emulator.context_at(
        0x00401011)  # somewhere randomly in the xor function
    args = context.passed_in_args
    assert args[0].name == "a1"
    assert args[0].type == "_BYTE *"
    assert args[0].value == 0
    assert args[
        0].addr == context.sp + 0x08  # +8 to account for pushed in return address and ebp
    assert args[1].name == "a2"
    assert args[1].type == "char"
    assert args[1].value == 0
    assert args[1].addr == context.sp + 0x0C
예제 #6
0
def test_context_depth():
    """Tests depth feature in iter_context_at()"""
    from kordesii import utils
    from kordesii.utils import function_tracing

    emulator = function_tracing.Emulator()

    ea = 0x00405901  # Address in function that contains multiple paths.
    num_paths_first_depth = 3
    num_paths_second_depth = 25

    # First ensure paths are calculated correctly.
    flowchart = utils.Flowchart(ea)
    block = flowchart.find_block(ea)
    assert len(list(block.paths())) == num_paths_first_depth

    func = utils.Function(ea)
    call_eas = list(func.calls_to)
    assert len(call_eas) == 1
    call_ea = call_eas[0]
    flowchart = utils.Flowchart(call_ea)
    block = flowchart.find_block(call_ea)
    assert len(list(block.paths())) == num_paths_second_depth

    # Now show that we get the correct number of contexts based on depth and other parameters.

    # Test getting contexts for only the first depth.
    ctxs = list(emulator.iter_context_at(ea))
    assert len(ctxs) == num_paths_first_depth
    # (exhaustive has no affect on final call level)
    ctxs = list(emulator.iter_context_at(ea, exhaustive=False))
    assert len(ctxs) == num_paths_first_depth

    # Test getting contexts with 2 depths.
    ctxs = list(emulator.iter_context_at(ea, depth=1))
    assert len(ctxs) == num_paths_first_depth * num_paths_second_depth
    ctxs = list(emulator.iter_context_at(ea, depth=1, exhaustive=False))
    assert len(ctxs) == num_paths_first_depth
예제 #7
0
def test_func_emulate():
    """Tests full function emulation in Emulator.create_emulated."""
    from kordesii.utils import function_tracing

    emulator = function_tracing.Emulator()

    xor_func_ea = 0x00401000
    enc_data_ptr = 0x0040C000  # pointer to b'Idmmn!Vnsme '
    xor_decrypt = emulator.create_emulated(xor_func_ea)

    # Test decrypting a string in memory.
    context = emulator.new_context()
    ret = xor_decrypt(enc_data_ptr, 1, context=context)
    assert ret == enc_data_ptr + len(b'Idmmn!Vnsme ')  # function returns pointer after decryption.
    assert context.read_data(enc_data_ptr) == b'Hello World!'

    # Test decrypting a string that was never in the sample.
    enc_data = b"!; '0607!)"
    context = emulator.new_context()
    ptr = context.mem_alloc(len(enc_data))
    context.mem_write(ptr, enc_data)
    xor_decrypt(ptr, 0x42, context=context)
    assert context.read_data(ptr) == b'cybertruck'
예제 #8
0
def test_function_case_senstivity():
    """Tests issue with case sensitivity when hooking functions."""
    from kordesii.utils import function_tracing
    from kordesii.utils.function_tracing.call_hooks import stdlib

    emulator = function_tracing.Emulator()

    # Test with known builtin func
    assert emulator.get_call_hook("lstrcpya") == stdlib.builtin_funcs.strcpy
    assert emulator.get_call_hook("lStrcpyA") == stdlib.builtin_funcs.strcpy
    assert emulator.get_call_hook("lstrcpyA") == stdlib.builtin_funcs.strcpy

    # Test user defined
    def dummy(ctx, func_name, func_args):
        return

    assert emulator.get_call_hook("SuperFunc") is None
    assert emulator.get_call_hook("SUPERfunc") is None
    assert emulator.get_call_hook("superfunc") is None
    emulator.hook_call("SuperFunc", dummy)
    assert emulator.get_call_hook("SuperFunc") == dummy
    assert emulator.get_call_hook("SUPERfunc") == dummy
    assert emulator.get_call_hook("superfunc") == dummy
예제 #9
0
def test_function_hooking():
    """Tests function hooking mechanism."""
    from kordesii.utils import function_tracing

    emulator = function_tracing.Emulator()

    xor_func_ea = 0x00401000
    end_ea = 0x00401141  # address in caller after all xor functions have been called.

    args = []
    # First test hooking with standard function.
    def xor_hook(context, func_name, func_args):
        args.append(func_args)
    emulator.hook_call(xor_func_ea, xor_hook)
    context = emulator.context_at(end_ea)
    expected_args = [
        [0x40c000, 0x1],
        [0x40c010, 0x2],
        [0x40c02c, 0x3],
        [0x40c05c, 0x4],
        [0x40c080, 0x5],
        [0x40c0a0, 0x6],
        [0x40c0c4, 0x13],
        [0x40c0f0, 0x17],
        [0x40c114, 0x1a],
        [0x40c120, 0x23],
        [0x40c130, 0x27],
        [0x40c138, 0x40],
        [0x40c140, 0x46],
        [0x40c15c, 0x73],
        [0x40c174, 0x75],
        [0x40c19c, 0x77],
        [0x40c1c4, 0x7a],
        [0x40c1f8, 0x7f],
    ]
    assert args == expected_args
    assert [_args for _, _args in context.get_call_history(xor_func_ea)] == expected_args

    # Now test with the function emulated to see our data getting decrypted.
    emulator.reset_hooks()
    emulator.emulate_call(xor_func_ea)
    context = emulator.context_at(end_ea)
    assert [_args for _, _args in context.get_call_history(xor_func_ea)] == expected_args
    strings = [(context.read_data(args[0]), args[1]) for _, args in context.get_call_history(xor_func_ea)]
    assert strings == [
        (b'Hello World!', 0x01),
        (b'Test string with key 0x02', 0x02),
        (b'The quick brown fox jumps over the lazy dog.', 0x03),
        (b'Oak is strong and also gives shade.', 0x04),
        (b'Acid burns holes in wool cloth.', 0x05),
        (b'Cats and dogs each hate the other.', 0x06),
        (b"Open the crate but don't break the glass.", 0x13),
        (b'There the flood mark is ten inches.', 0x17),
        (b'1234567890', 0x1a),
        (b'CreateProcessA', 0x23),
        (b'StrCat', 0x27),
        (b'ASP.NET', 0x40),
        (b'kdjsfjf0j24r0j240r2j09j222', 0x46),
        (b'32897412389471982470', 0x73),
        (b'The past will look brighter tomorrow.', 0x75),
        (b'Cars and busses stalled in sand drifts.', 0x77),
        (b'The jacket hung on the back of the wide chair.', 0x7a),
        (b'32908741328907498134712304814879837483274809123748913251236598123056231895712', 0x7f),
    ]
예제 #10
0
def test_flowchart():
    from kordesii.utils import function_tracing

    emulator = function_tracing.Emulator()

    # Test on simple 1 block function.
    flowchart = function_tracing.Flowchart(0x004011AA)
    blocks = list(flowchart.blocks())
    assert len(blocks) == 1
    block = blocks[0]
    assert block.start_ea == 0x00401150
    assert block.end_ea == 0x004012A0
    assert list(block.heads()) == (
        [0x00401150, 0x00401151, 0x00401153, 0x00401158, 0x0040115D, 0x00401162, 0x00401167]
        + [0x0040116A, 0x0040116F, 0x00401174, 0x00401179, 0x0040117C, 0x00401181, 0x00401186]
        + [0x0040118B, 0x0040118E, 0x00401193, 0x00401198, 0x0040119D, 0x004011A0, 0x004011A5]
        + [0x004011AA, 0x004011AF, 0x004011B2, 0x004011B7, 0x004011BC, 0x004011C1, 0x004011C4]
        + [0x004011C9, 0x004011CE, 0x004011D3, 0x004011D6, 0x004011DB, 0x004011E0, 0x004011E5]
        + [0x004011E8, 0x004011ED, 0x004011F2, 0x004011F7, 0x004011FA, 0x004011FF, 0x00401204]
        + [0x00401209, 0x0040120C, 0x00401211, 0x00401216, 0x0040121B, 0x0040121E, 0x00401223]
        + [0x00401228, 0x0040122D, 0x00401230, 0x00401235, 0x0040123A, 0x0040123F, 0x00401242]
        + [0x00401247, 0x0040124C, 0x00401251, 0x00401254, 0x00401259, 0x0040125E, 0x00401263]
        + [0x00401266, 0x0040126B, 0x00401270, 0x00401275, 0x00401278, 0x0040127D, 0x00401282]
        + [0x00401287, 0x0040128A, 0x0040128F, 0x00401294, 0x00401299, 0x0040129C, 0x0040129E]
        + [0x0040129F]
    )
    # Ensure we create a path of just the 1 block.
    path_blocks = list(flowchart.get_paths(0x004011AA))
    assert len(path_blocks) == 1
    path_block = path_blocks[0]
    assert path_block.path() == [path_block]
    # Ensure cpu context gets created correctly.
    cpu_context = path_block.cpu_context(init_context=emulator.new_context())
    assert cpu_context.ip == block.end_ea
    cpu_context = path_block.cpu_context(0x0040115D, init_context=emulator.new_context())
    assert cpu_context.ip == 0x0040115D

    # Test read_data()
    data_ptr = cpu_context.read_data(cpu_context.registers.esp, data_type=function_tracing.DWORD)
    assert cpu_context.read_data(data_ptr) == b"Idmmn!Vnsme "
    # Test write_data()
    cpu_context.write_data(cpu_context.registers.esp, data_ptr + 3, data_type=function_tracing.DWORD)
    data_ptr = cpu_context.read_data(cpu_context.registers.esp, data_type=function_tracing.DWORD)
    assert cpu_context.read_data(data_ptr) == b"mn!Vnsme "

    # Test on slightly more complex function with 5 blocks
    flowchart = function_tracing.Flowchart(0x004035BB)

    found_block = flowchart.find_block(0x004035AD)
    assert found_block
    assert found_block.start_ea == 0x004035AB

    blocks = list(flowchart.blocks(start=0x004035AB, reverse=True))
    assert len(blocks) == 2
    assert [(block.start_ea, block.end_ea) for block in blocks] == [(0x004035AB, 0x004035B1), (0x00403597, 0x004035AB)]

    blocks = list(flowchart.blocks(start=0x004035AB))
    assert len(blocks) == 4
    assert [(block.start_ea, block.end_ea) for block in blocks] == [
        (0x004035AB, 0x004035B1),
        (0x004035BA, 0x004035BD),
        (0x004035B1, 0x004035B3),
        (0x004035B3, 0x004035BA),
    ]

    blocks = list(flowchart.blocks())
    assert len(blocks) == 5
    assert [(block.start_ea, block.end_ea) for block in blocks] == [
        (0x00403597, 0x004035AB),
        (0x004035AB, 0x004035B1),
        (0x004035BA, 0x004035BD),
        (0x004035B1, 0x004035B3),
        (0x004035B3, 0x004035BA),
    ]
    blocks = list(flowchart.blocks(reverse=True))
    assert len(blocks) == 5
    assert [(block.start_ea, block.end_ea) for block in blocks] == [
        (0x004035BA, 0x004035BD),
        (0x004035B3, 0x004035BA),
        (0x00403597, 0x004035AB),
        (0x004035B1, 0x004035B3),
        (0x004035AB, 0x004035B1),
    ]
    blocks = list(flowchart.blocks(dfs=True))
    assert len(blocks) == 5
    assert [(block.start_ea, block.end_ea) for block in blocks] == [
        (0x00403597, 0x004035AB),
        (0x004035AB, 0x004035B1),
        (0x004035B1, 0x004035B3),
        (0x004035B3, 0x004035BA),
        (0x004035BA, 0x004035BD),
    ]
    blocks = list(flowchart.blocks(reverse=True, dfs=True))
    assert len(blocks) == 5
    assert [(block.start_ea, block.end_ea) for block in blocks] == [
        (0x004035BA, 0x004035BD),
        (0x004035B3, 0x004035BA),
        (0x004035B1, 0x004035B3),
        (0x004035AB, 0x004035B1),
        (0x00403597, 0x004035AB),
    ]

    path_blocks = list(flowchart.get_paths(0x004035B1))
    assert len(path_blocks) == 1
    assert [path_block.bb.start_ea for path_block in path_blocks[0].path()] == [0x00403597, 0x004035AB, 0x004035B1]

    path_blocks = list(flowchart.get_paths(0x004035BC))
    assert len(path_blocks) == 3
    assert sorted([_path_block.bb.start_ea for _path_block in path_block.path()] for path_block in path_blocks) == [
        [0x00403597, 0x004035AB, 0x004035B1, 0x004035B3, 0x004035BA],
        [0x00403597, 0x004035AB, 0x004035B3, 0x004035BA],
        [0x00403597, 0x004035BA],
    ]
예제 #11
0
def test_function_signature():
    """Tests FunctionSignature object."""
    import idc
    from kordesii import utils
    from kordesii.utils import function_tracing

    emulator = function_tracing.Emulator()

    xor_func_ea = 0x00401000

    # Basic tests.
    context = emulator.context_at(xor_func_ea)
    func_sig = context.get_function_signature(func_ea=xor_func_ea)
    assert func_sig.declaration == "_BYTE *__cdecl sub_401000(_BYTE *a1, char a2);"
    assert func_sig.arg_types == ("_BYTE * a1", "char a2")
    args = func_sig.args
    assert len(args) == 2
    assert args[0].name == "a1"
    assert args[0].type == "_BYTE *"
    assert args[0].value == 0
    assert args[1].name == "a2"
    assert args[1].type == "char"
    assert args[1].value == 0

    # Test that we can manipulate signature.
    func_sig.arg_types += ("int new_arg",)
    assert func_sig.declaration == "_BYTE *__cdecl sub_401000(_BYTE *a1, char a2, int new_arg);"
    args = func_sig.args
    assert len(args) == 3
    assert args[2].name == "new_arg"
    assert args[2].type == "int"
    assert args[2].value == 0

    # Now test using iter_function_args

    # First force an incorrect number of arguments.
    idc.SetType(xor_func_ea, " _BYTE *__cdecl sub_401000(_BYTE *a1)")
    func = utils.Function(xor_func_ea)

    # Then test we can force 2 arguments anyway.
    results = []
    for ea in func.calls_to:
        for context in emulator.iter_context_at(ea):
            # The function signature only gives 1 argument now.
            func_sig = context.get_function_signature()
            assert len(func_sig.args) == 1
            # But we force 2.
            args = context.get_function_args(num_args=2)
            assert len(args) == 2
            results.append(args)
    assert results == [
        [4243456, 1],
        [4243472, 2],
        [4243500, 3],
        [4243548, 4],
        [4243584, 5],
        [4243616, 6],
        [4243652, 19],
        [4243696, 23],
        [4243732, 26],
        [4243744, 35],
        [4243760, 39],
        [4243768, 64],
        [4243776, 70],
        [4243804, 115],
        [4243828, 117],
        [4243868, 119],
        [4243908, 122],
        [4243960, 127],
    ]

    # Test that we can force function signatures.
    with pytest.raises(RuntimeError):
        context.get_function_args(0xFFF)
    with pytest.raises(RuntimeError):
        context.get_function_signature(0xFFF)
    assert len(context.get_function_args(0xFFF, num_args=3)) == 3
    func_sig = context.get_function_signature(0xFFF, force=True)
    assert func_sig.declaration == 'int __cdecl no_name();'
예제 #12
0
def test_builtin_funcs():
    """Tests the emulated builtin_funcs."""
    from kordesii.utils import function_tracing
    from kordesii.utils.function_tracing.cpu_context import ProcessorContext
    from kordesii.utils.function_tracing.call_hooks import stdlib

    emulator = function_tracing.Emulator()

    src = 0x123000
    dst = 0x124000

    # test strcat
    context = emulator.new_context()
    assert context.ARCH_NAME == "metapc"
    context.memory.write(src, b"world")
    context.memory.write(dst, b"hello")
    assert stdlib.builtin_funcs.strcat(context, "strcat", [dst, src]) == dst
    assert context.read_data(dst) == b"helloworld"
    for encoding in ["utf-16-le", "utf-16-be"]:
        context = emulator.new_context()
        context.memory.write(src, u"world".encode(encoding))
        context.memory.write(dst, u"hello".encode(encoding))
        assert stdlib.builtin_funcs.strcat(context, "wcscat", [dst, src]) == dst
        assert context.read_data(dst, data_type=function_tracing.WIDE_STRING) == u"helloworld".encode(encoding)

    # test strncat
    context = emulator.new_context()
    context.memory.write(src, b"world")
    context.memory.write(dst, b"hello")
    assert stdlib.builtin_funcs.strncat(context, "strncat", [dst, src, 10]) == dst
    assert context.read_data(dst) == b"helloworld"
    assert stdlib.builtin_funcs.strncat(context, "strncat", [dst, src, 2]) == dst
    assert context.read_data(dst) == b"helloworldwo"
    for encoding in ["utf-16-le", "utf-16-be"]:
        context = emulator.new_context()
        context.memory.write(src, u"world".encode(encoding))
        context.memory.write(dst, u"hello".encode(encoding))
        assert stdlib.builtin_funcs.strncat(context, "wcsncat", [dst, src, 10]) == dst
        assert context.read_data(dst, data_type=function_tracing.WIDE_STRING) == u"helloworld".encode(encoding)
        assert stdlib.builtin_funcs.strncat(context, "wcsncat", [dst, src, 2]) == dst
        assert context.read_data(dst, data_type=function_tracing.WIDE_STRING) == u"helloworldwo".encode(encoding)

    # test strcpy
    context = emulator.new_context()
    context.memory.write(src, b"world")
    context.memory.write(dst, b"hello!!!")
    assert stdlib.builtin_funcs.strcpy(context, "strcpy", [dst, src]) == dst
    assert context.read_data(dst) == b"world"
    for encoding in ["utf-16-le", "utf-16-be"]:
        context = emulator.new_context()
        context.memory.write(src, u"world".encode(encoding))
        context.memory.write(dst, u"hello!!!".encode(encoding))
        assert stdlib.builtin_funcs.strcpy(context, "wcscpy", [dst, src]) == dst
        assert context.read_data(dst, data_type=function_tracing.WIDE_STRING) == u"world".encode(encoding)

    # test strncpy
    context = emulator.new_context()
    context.memory.write(src, b"world")
    context.memory.write(dst, b"hello!!!")
    assert stdlib.builtin_funcs.strncpy(context, "strncpy", [dst, src, 2]) == dst
    # Since we are only copying 2 characters over, the null doesn't get sent over and therefore get
    # some of the original string in the copy.
    assert context.read_data(dst) == b"wollo!!!"
    for encoding in ["utf-16-le", "utf-16-be"]:
        context = emulator.new_context()
        context.memory.write(src, u"world".encode(encoding))
        context.memory.write(dst, u"hello!!!".encode(encoding))
        assert stdlib.builtin_funcs.strncpy(context, "wcsncpy", [dst, src, 2]) == dst
        assert context.read_data(dst, data_type=function_tracing.WIDE_STRING) == u"wollo!!!".encode(encoding)

    # test strdup/strndup
    heap_ptr = context.memory.HEAP_BASE
    context = emulator.new_context()
    context.memory.write(src, b"hello")
    # should return a newly allocated string
    assert stdlib.builtin_funcs.strdup(context, "strdup", [src]) == heap_ptr
    assert context.read_data(heap_ptr) == b"hello"
    context = emulator.new_context()
    context.memory.write(src, b"hello")
    assert stdlib.builtin_funcs.strndup(context, "strndup", [src, 2]) == heap_ptr
    assert context.read_data(heap_ptr) == b"he"

    # test strlen
    context = emulator.new_context()
    context.memory.write(src, b"hello")
    assert stdlib.builtin_funcs.strlen(context, "strlen", [src]) == 5
    for encoding in ["utf-16-le", "utf-16-be"]:
        context = emulator.new_context()
        context.memory.write(src, u"hello".encode(encoding))
        assert stdlib.builtin_funcs.strlen(context, "wcslen", [src]) == 5
예제 #13
0
def test_registers():
    """Tests registers"""
    from kordesii.utils import function_tracing
    from kordesii.utils.function_tracing.cpu_context import ProcessorContext
    from kordesii.utils.function_tracing.registers import Register

    # Basic register tests.
    reg = Register(8, rax=0xFFFFFFFFFFFFFFFF, eax=0xFFFFFFFF, ax=0xFFFF, al=0xFF, ah=0xFF00)
    assert sorted(reg.names) == ["ah", "al", "ax", "eax", "rax"]
    assert reg.rax == 0
    assert reg.ax == 0
    assert reg["rax"] == 0
    assert reg["ax"] == 0
    reg.ah = 0x23
    assert reg.ah == 0x23
    assert reg.al == 0x00
    assert reg.ax == 0x2300
    assert reg.eax == 0x00002300
    reg.eax = 0x123
    assert reg.ah == 0x01
    assert reg.al == 0x23
    assert reg.rax == 0x0000000000000123

    emulator = function_tracing.Emulator()
    context = emulator.new_context()
    registers = context.registers

    # fmt: off
    # Test getting all register names.
    assert sorted(registers.names) == [
        'ac', 'af', 'ah', 'al', 'ax', 'b', 'bh', 'bl', 'bp', 'bpl', 'bx',
        'c0', 'c1', 'c2', 'c3', 'cf', 'ch', 'cl', 'cs', 'cx', 'd', 'df',
        'dh', 'di', 'dil', 'dl', 'dm', 'ds', 'dx', 'eax', 'ebp', 'ebx',
        'ecx', 'edi', 'edx', 'eflags', 'es', 'esi', 'esp', 'flags', 'fs', 'gs', 'i', 'ic',
        'id', 'iem', 'if', 'im', 'iopl', 'ir', 'nt', 'o', 'of', 'om', 'p',
        'pc', 'pf', 'pm', 'r10', 'r10b', 'r10d', 'r10w', 'r11', 'r11b',
        'r11d', 'r11w', 'r12', 'r12b', 'r12d', 'r12w', 'r13', 'r13b', 'r13d',
        'r13w', 'r14', 'r14b', 'r14d', 'r14w', 'r15', 'r15b', 'r15d', 'r15w',
        'r8', 'r8b', 'r8d', 'r8w', 'r9', 'r9b', 'r9d', 'r9w', 'rax', 'rbp',
        'rbx', 'rc', 'rcx', 'rdi', 'rdx', 'rf', 'rip', 'rsi', 'rsp', 'sf',
        'sf', 'si', 'sil', 'sp', 'spl', 'ss',
        'st', 'st0', 'st1', 'st2', 'st3', 'st4', 'st5', 'st6', 'st7',
        'tag0', 'tag1', 'tag2', 'tag3', 'tag4', 'tag5', 'tag6', 'tag7',
        'tf', 'top', 'u', 'um', 'vif', 'vip', 'vm',
        'xmm0', 'xmm1', 'xmm10', 'xmm11', 'xmm12', 'xmm13', 'xmm14', 'xmm15',
        'xmm2', 'xmm3', 'xmm4', 'xmm5', 'xmm6', 'xmm7', 'xmm8', 'xmm9',
        'z', 'zf', 'zm',
    ]
    # Test getting register names for FPU.
    assert sorted(registers.fpu.names) == [
        "b", "c0", "c1", "c2", "c3", "d", "dm", "i", "ic", "iem", "im", "ir",
        "o", "om", "p", "pc", "pm", "rc", "sf",
        "st", "st0", "st1", "st2", "st3", "st4", "st5", "st6", "st7",
        "tag0", "tag1", "tag2", "tag3", "tag4", "tag5", "tag6", "tag7",
        "top", "u", "um", "z", "zm",
    ]
    # fmt: on

    # Test FPU registers.
    # TODO: Add tests for flags
    assert registers.st0 is None
    assert registers["st0"] is None
    assert registers.fpu.st0 is None
    assert registers.fpu["st0"] is None
    registers.fpu.push(-12.3)
    assert registers.st0 == -12.3
    assert registers.st1 is None
    registers.fpu.push(34)
    assert registers.st0 == 34
    assert registers.st1 == -12.3
    registers.fpu.pop()
    assert registers.st0 == -12.3
    assert registers.st1 is None
    registers.fpu.push(registers.fpu.INFINITY)
    assert registers.st0 == registers.fpu.INFINITY
    assert registers.st1 == -12.3
예제 #14
0
def test_cpu_context():
    """Tests function_tracer and and cpu_context."""

    from kordesii.utils import function_tracing

    emulator = function_tracing.Emulator()

    # Test on encryption function.
    context = emulator.context_at(0x00401024)

    operands = context.operands
    assert len(operands) == 2
    assert operands[0].text == "[ebp+a1]"
    assert operands[0].value == 0
    # arg_0 should be 8 bytes from stack pointer.
    assert operands[0].addr == context.registers.esp + 8 == 0x117F804
    assert operands[1].text == "eax"
    assert operands[1].value == context.registers.eax == 1

    # Test variables
    data_ptr = operands[0].addr
    assert sorted(context.variables.names) == ["a1", "a2", "loc_401029"]
    assert data_ptr in context.variables
    var = context.variables[data_ptr]
    assert var.name == "a1"
    assert not var.history
    assert var.size == 4
    assert var.data_type == "dword"
    assert var.data_type_size == 4
    assert var.count == 1
    # test changing the variable
    assert var.data == b"\x00\x00\x00\x00"
    assert var.value == context.operands[0].value == 0
    var.value = 21
    assert var.value == context.operands[0].value == 21
    assert var.data == b"\x15\x00\x00\x00"
    assert context.mem_read(var.addr, 4) == b"\x15\x00\x00\x00"

    # Now execute this instruction and see if a1 has be set with the 1 from eax.
    context.execute(context.ip)
    assert operands[0].value == 1

    # Test getting all possible values passed into arg_0 using depth.
    strings = []
    for context in emulator.iter_context_at(0x00401003, depth=1):
        assert context.ip == 0x00401003
        # mov     eax, [ebp+arg_0]
        strings.append(context.read_data(context.operands[1].value))
    assert strings == [
        b"Idmmn!Vnsme ",
        b'Vgqv"qvpkle"ukvj"ig{"2z20',
        b"Wkf#rvj`h#aqltm#el{#ivnsp#lufq#wkf#obyz#gld-",
        b"Keo$mw$wpvkjc$ej`$ehwk$cmraw$wle`a*",
        b"Dfla%gpwkv%mji`v%lk%rjji%fijqm+",
        b"Egru&ghb&biau&cgen&ngrc&rnc&irnct(",
        b"\\cv}3g{v3pargv3qfg3w|}4g3qavrx3g{v3t\x7fr``=",
        b"C\x7frer7c\x7fr7q{xxs7zve|7~d7cry7~yt\x7frd9",
        b'+()./,-"#*',
        b"`QFBWFsQL@FPPb",
        b"tSUdFS",
        b"\x01\x13\x10n\x0e\x05\x14",
        b'-",5 , v,tr4v,trv4t,v\x7f,ttt',
        b"@AKJDGBA@KJGDBJKAGDC",
        (
            b"!\x1d\x10U\x05\x14\x06\x01U\x02\x1c\x19\x19U\x19\x1a\x1a\x1eU\x17\x07\x1c"
            b"\x12\x1d\x01\x10\x07U\x01\x1a\x18\x1a\x07\x07\x1a\x02["
        ),
        (
            b"4\x16\x05\x04W\x16\x19\x13W\x15\x02\x04\x04\x12\x04W\x04\x03\x16\x1b\x1b"
            b"\x12\x13W\x1e\x19W\x04\x16\x19\x13W\x13\x05\x1e\x11\x03\x04Y"
        ),
        (
            b".\x12\x1fZ\x10\x1b\x19\x11\x1f\x0eZ\x12\x0f\x14\x1dZ\x15\x14Z\x0e\x12\x1f"
            b"Z\x18\x1b\x19\x11Z\x15\x1cZ\x0e\x12\x1fZ\r\x13\x1e\x1fZ\x19\x12\x1b\x13\x08T"
        ),
        b"LMFOGHKNLMGFOHKFGNLKHNMLOKGNKGHFGLHKGLMHKGOFNMLHKGFNLMJNMLIJFGNMLOJIMLNGFJHNM",
    ]

    # Test pulling arguments from a call.
    context = emulator.context_at(0x0040103A)
    operands = context.operands
    assert len(operands) == 1
    assert operands[0].is_func_ptr
    assert operands[0].value == 0x00401000
    # First, attempt to pull the arguments from the stack without get_function_args()
    first_arg_ptr = context.read_data(context.registers.esp, data_type=function_tracing.DWORD)
    second_arg = context.read_data(context.registers.esp + 4, data_type=function_tracing.BYTE)
    assert context.read_data(first_arg_ptr) == b"Idmmn!Vnsme "
    assert second_arg == 1
    # Now try with get_function_args()
    args = context.get_function_args()
    assert len(args) == 2
    assert context.read_data(args[0]) == b"Idmmn!Vnsme "
    assert args[1] == 1

    assert sorted(context.variables.names) == ["aIdmmnVnsme", "sub_401000"]

    # Test getting context with follow_loops by pulling context at end of xor algorithm.

    # first without follow_loops off to show we get non-decrypted data
    context = emulator.context_at(0x00401029, follow_loops=False, depth=1)
    assert context.passed_in_args[1].value == 0x1  # key
    assert context.read_data(context.passed_in_args[0].value) == b"Idmmn!Vnsme "  # data

    # now with follow_loops on to show we get decrypted data
    context = emulator.context_at(0x00401029, follow_loops=True, depth=1)
    assert context.passed_in_args[1].value == 0x1
    # The way the xor function works is that it takes and MODIFIES the
    # pointer argument passed in, unhelpfully returning a pointer to the end of the
    # decrypted data, not the start, with no way knowing the size...
    # This is obviously a typo when creating strings.exe, but let's just say
    # this is good practice for dealing with some gnarly malware sample :)
    # Therefore, we are going to iteratively decrease the pointer until we find a
    # valid address in the variable map. This variable was the variable used by the caller.
    result = context.registers.eax
    result -= 1
    while result not in context.variables:
        result -= 1
    assert context.read_data(result) == b"Hello World!"

    # Alright, one more time, but with ALL strings.
    # Testing we can successfully decrypt the strings and get the key used.
    strings = []
    for context in emulator.iter_context_at(0x00401029, follow_loops=True, depth=1):
        result = context.registers.eax
        result -= 1
        while result not in context.variables:
            result -= 1
        strings.append((context.read_data(result), context.passed_in_args[1].value))
    assert strings == [
        (b'Hello World!', 0x01),
        (b'Test string with key 0x02', 0x02),
        (b'The quick brown fox jumps over the lazy dog.', 0x03),
        (b'Oak is strong and also gives shade.', 0x04),
        (b'Acid burns holes in wool cloth.', 0x05),
        (b'Cats and dogs each hate the other.', 0x06),
        (b"Open the crate but don't break the glass.", 0x13),
        (b'There the flood mark is ten inches.', 0x17),
        (b'1234567890', 0x1a),
        (b'CreateProcessA', 0x23),
        (b'StrCat', 0x27),
        (b'ASP.NET', 0x40),
        (b'kdjsfjf0j24r0j240r2j09j222', 0x46),
        (b'32897412389471982470', 0x73),
        (b'The past will look brighter tomorrow.', 0x75),
        (b'Cars and busses stalled in sand drifts.', 0x77),
        (b'The jacket hung on the back of the wide chair.', 0x7a),
        (b'32908741328907498134712304814879837483274809123748913251236598123056231895712', 0x7f),
    ]
예제 #15
0
"""

import io

import idc

import kordesii
from kordesii.utils import EncodedStackString
from kordesii.utils import Function
from kordesii.utils import decoderutils
from kordesii.utils import function_tracing
from kordesii.utils import utils


logger = kordesii.get_logger()
emulator = function_tracing.Emulator(branch_tracking=False)


ENCODINGS = [("utf-8", 1), ("utf-16-le", 2)]


def num_raw_bytes(string):
    """
    Returns the number of raw bytes found in the given unicode string
    """
    count = 0
    for char in string:
        char = char.encode("unicode-escape")
        count += char.startswith(b"\\x") + char.startswith(b"\\u") * 2
    return count
예제 #16
0
"""
Description: Sample decoder
Author: DC3
"""

import kordesii
from kordesii.utils import EncodedString
from kordesii.utils import ida_re
from kordesii.utils import function_tracing

logger = kordesii.get_logger()
emulator = function_tracing.Emulator()


def xor_decrypt(key, enc_data):
    return bytes((x ^ key) for x in enc_data)


def find_strings():
    """
    Extracts and publishes EncodedString objects for the parameters following xor encryption function:

        void encrypt(char *s, char key)
        {
            while (*s)
                *s++ ^= key;
        }
    """
    for encrypt_func in ida_re.find_functions(br"\x8b\x45\x08\x0f\xbe\x08"):
        logger.info("Found XOR encrypt function at: 0x%x",
                    encrypt_func.start_ea)