首页
社区
课程
招聘
[原创]看雪.深信服 2021 KCTF 春季赛 第七题 千里寻根
发表于: 2021-5-23 06:19 10920

[原创]看雪.深信服 2021 KCTF 春季赛 第七题 千里寻根

2021-5-23 06:19
10920

多层自解密,每一层会先清空上一层,解密下一层,然后跳过去。每层的解密逻辑都不完全相同
真实指令分散在每一层中,还有call-pop-add之类的混淆

有反调试,不能直接调试器启动,但可以启动到输入之后再x64dbg附加。

发现输入之后会进入0x140001000的函数对serial做hexdecode,这个函数位置总是固定的。
在它的ret指令处(0x140001084)下断点,r10寄存器指示了serial保存的地方,在这里下硬件读写断点。

一次F9到达断点,是一句rep movsq指令,在此时开始运行下面的脚本:跟踪rep movsq指令对serial的复制,并对复制的目标位置下硬件断点。

(注:$result+0xc是调整后的结果,因为真正处理serial的代码读的并不是第0个字节,而是第0xc个字节)

几秒钟后脚本停下来,是movzx r12d, byte ptr [r13 + 0x25]指令,这是真正开始处理serial的地方。

dump内存,记录下所有寄存器的值(以及gs:[0x8]和gs:[0x10]两个位置的值)

考虑用unicorn模拟执行+capstone反汇编,目的是方便的跟踪程序执行流程以及后续的分析。
直接写了一个基础版本,发现根本跑不完(毕竟原始程序直接执行都需要几秒钟)

基于模拟执行的分析肯定要反复尝试,速度必须要足够快,因此做了很多努力来加速:

现在,程序能够在1分钟以内生成完整的trace,这个速度差不多可以接受了。

trace有大约80万行,关键指令又分散在垃圾指令中,还是不能人工分析。
下一步考虑基于trace做数据流跟踪(受到 https://bbs.pediy.com/thread-258262.htm 这篇文章启发),看输入的serial从内存中被读出来以及后续计算的流程。
对于任何一条语句,都有来源操作数(内存/寄存器/常量)和目的操作数(内存/寄存器),如果任何一个来源操作数与serial有关("tainted"),则相关的目的操作数也要标记为"tainted",相反的,如果所有来源操作数都没有tainted,则所有目的操作数的tainted标记都要取消。
实现的难度很大,capstone好像没有办法直接获得一条指令相关的源寄存器和目的寄存器(也可能有办法,只是我不知道),只好手动处理部分指令,目标是零漏报的基础上尽可能减少误报。

代码如下:

代码写的不好,修了很久的bug,但是生成的trace还存在问题。

加上数据流跟踪之后,跑一遍trace大约需要2分钟。
附件trace.rar里的t6.txt文件是以KCTF为name、以给出的公开serial为serial作为输入得到的结果。用下面的代码筛选出trace到的指令:

筛选结果在tt6.txt中,大约2500行,这个长度人工分析是可以接受的。

基于数据流分析的结果是可以做死代码消除的,由于没有做,还是有很多垃圾指令。(希望有一种方便的办法能够生成可执行的程序让ida帮忙做这件事)
突破口是读写内存的指令,从这些地方逐渐向上找。

最终分析下来的流程是:

(数据流跟踪的代码有bug导致这里有些指令的trace丢失了(特别是第3步里对前8字节的处理),只好根据上下文去猜)

serial的验证以及从name生成serial的程序如下:

附件:
memdump.rar:运行到真正的验证逻辑首次开始读取serial时的内存状态以及寄存器状态,用于模拟执行
trace.rar:基于模拟执行生成的完整trace文件以及过滤结果。t6.txt和tt6.txt直接对应memdump,输入为KCTF / 公开的序列号;t7.txt和tt7.txt则是在unicorn中把serial对应的内存覆盖为真正的serial后得到的结果。

 
 
 
loop:
 
find rip, "F348A5", 3
cmp $result, 0
je out
 
bphc
sti
sti
find rdi-0x60, "7D4DBA7A", 0xc0
bph $result+0xc, r, 1
 
run
 
jmp loop
 
out:
loop:
 
find rip, "F348A5", 3
cmp $result, 0
je out
 
bphc
sti
sti
find rdi-0x60, "7D4DBA7A", 0xc0
bph $result+0xc, r, 1
 
run
 
jmp loop
 
out:
 
 
 
 
 
 
# https://github.com/unicorn-engine/unicorn/blob/master/bindings/python/sample_x86.py
# https://github.com/aquynh/capstone/blob/master/bindings/python/capstone/x86.py
# https://github.com/aquynh/capstone/blob/master/bindings/python/capstone/x86_const.py
 
from unicorn import *
from unicorn.x86_const import *
from capstone import *
from capstone.x86_const import *
import traceback
import ctypes
 
 
'''
// cl /O2 -c dodecryptblock.c
// link -DLL -out:libdodecryptblock.dll dodecryptblock.obj
 
__declspec(dllexport) void dodecryptblock(unsigned char *buf, int len, unsigned char table[256]) {
    for (int i = 0; i < len; i++) {
        buf[i] = table[buf[i]];
    }
}
'''
libdodecryptblock = ctypes.CDLL("./libdodecryptblock.dll")
 
#global_serial_start = None
#global_serial_len = 32
 
global_tainted_memlist = set()
global_tainted_reglist = set()
 
global_last_inst = None
 
class LoopContext:
    __slots__ = ["tracing", "tracecount", "operations", "direction", "address", "memreg"]
    def __init__(self):
        self.clear()
    def clear(self):
        self.tracing = False
        self.tracecount = 0
        self.operations = []
        self.direction = None
        self.address = None
        self.memreg = None
 
class HoopPrintContext:
    __slots__ = ["initial_rsp", "last_rsp", "last_address", "last_inst"]
    def __init__(self):
        self.initial_rsp = None
        self.last_rsp = None
        self.last_address = None
        self.last_inst = None
 
def handlekeyboardinterupt(func):
    def wrapper_func(*args, **kwargs):
        try:
            r = func(*args, **kwargs)
        except KeyboardInterrupt:
            import os
            os._exit(0)
        except Exception:
            traceback.print_exc()
            import os
            import sys
            sys.stdout.flush()
            sys.stderr.flush()
            os._exit(0)
        return r
    return wrapper_func
 
def capstone_reg_to_unicorn_reg(i):
    d = {
        X86_REG_RAX : UC_X86_REG_RAX,
        X86_REG_RBX : UC_X86_REG_RBX,
        X86_REG_RCX : UC_X86_REG_RCX,
        X86_REG_RDX : UC_X86_REG_RDX,
        X86_REG_RBP : UC_X86_REG_RBP,
        X86_REG_RSP : UC_X86_REG_RSP,
        X86_REG_RSI : UC_X86_REG_RSI,
        X86_REG_RDI : UC_X86_REG_RDI,
        X86_REG_R8 : UC_X86_REG_R8,
        X86_REG_R9 : UC_X86_REG_R9,
        X86_REG_R10 : UC_X86_REG_R10,
        X86_REG_R11 : UC_X86_REG_R11,
        X86_REG_R12 : UC_X86_REG_R12,
        X86_REG_R13 : UC_X86_REG_R13,
        X86_REG_R14 : UC_X86_REG_R14,
        X86_REG_R15 : UC_X86_REG_R15,
 
        X86_REG_AL : UC_X86_REG_AL,
        X86_REG_BL : UC_X86_REG_BL,
        X86_REG_CL : UC_X86_REG_CL,
        X86_REG_DL : UC_X86_REG_DL,
        X86_REG_SIL : UC_X86_REG_SIL,
        X86_REG_DIL : UC_X86_REG_DIL,
 
        X86_REG_RIP : UC_X86_REG_RIP,
    }
    return d[i]
 
def capstone_reg_to_normal_reg(i):
    d = {
        X86_REG_RAX : X86_REG_RAX,
        X86_REG_RBX : X86_REG_RBX,
        X86_REG_RCX : X86_REG_RCX,
        X86_REG_RDX : X86_REG_RDX,
        X86_REG_RBP : X86_REG_RBP,
        X86_REG_RSP : X86_REG_RSP,
        X86_REG_RSI : X86_REG_RSI,
        X86_REG_RDI : X86_REG_RDI,
        X86_REG_R8 : X86_REG_R8,
        X86_REG_R9 : X86_REG_R9,
        X86_REG_R10 : X86_REG_R10,
        X86_REG_R11 : X86_REG_R11,
        X86_REG_R12 : X86_REG_R12,
        X86_REG_R13 : X86_REG_R13,
        X86_REG_R14 : X86_REG_R14,
        X86_REG_R15 : X86_REG_R15,
 
        X86_REG_EAX : X86_REG_RAX,
        X86_REG_EBX : X86_REG_RBX,
        X86_REG_ECX : X86_REG_RCX,
        X86_REG_EDX : X86_REG_RDX,
        X86_REG_EBP : X86_REG_RBP,
        X86_REG_ESP : X86_REG_RSP,
        X86_REG_ESI : X86_REG_RSI,
        X86_REG_EDI : X86_REG_RDI,
        X86_REG_R8D : X86_REG_R8,
        X86_REG_R9D : X86_REG_R9,
        X86_REG_R10D : X86_REG_R10,
        X86_REG_R11D : X86_REG_R11,
        X86_REG_R12D : X86_REG_R12,
        X86_REG_R13D : X86_REG_R13,
        X86_REG_R14D : X86_REG_R14,
        X86_REG_R15D : X86_REG_R15,
 
        X86_REG_AX : X86_REG_RAX,
        X86_REG_BX : X86_REG_RBX,
        X86_REG_CX : X86_REG_RCX,
        X86_REG_DX : X86_REG_RDX,
        X86_REG_BP : X86_REG_RBP,
        X86_REG_SP : X86_REG_RSP,
        X86_REG_SI : X86_REG_RSI,
        X86_REG_DI : X86_REG_RDI,
        X86_REG_R8W : X86_REG_R8,
        X86_REG_R9W : X86_REG_R9,
        X86_REG_R10W : X86_REG_R10,
        X86_REG_R11W : X86_REG_R11,
        X86_REG_R12W : X86_REG_R12,
        X86_REG_R13W : X86_REG_R13,
        X86_REG_R14W : X86_REG_R14,
        X86_REG_R15W : X86_REG_R15,
 
        X86_REG_AL : X86_REG_RAX,
        X86_REG_AH : X86_REG_RAX,
        X86_REG_BL : X86_REG_RBX,
        X86_REG_BH : X86_REG_RBX,
        X86_REG_CL : X86_REG_RCX,
        X86_REG_CH : X86_REG_RCX,
        X86_REG_DL : X86_REG_RDX,
        X86_REG_DH : X86_REG_RDX,
        X86_REG_BPL : X86_REG_RBP,
        X86_REG_SIL : X86_REG_RSI,
        X86_REG_DIL : X86_REG_RDI,
        X86_REG_R8B : X86_REG_R8,
        X86_REG_R9B : X86_REG_R9,
        X86_REG_R10B : X86_REG_R10,
        X86_REG_R11B : X86_REG_R11,
        X86_REG_R12B : X86_REG_R12,
        X86_REG_R13B : X86_REG_R13,
        X86_REG_R14B : X86_REG_R14,
        X86_REG_R15B : X86_REG_R15,
 
        X86_REG_RIP : X86_REG_RIP,
        X86_REG_EFLAGS : X86_REG_EFLAGS,
    }
    return d[i]
 
def p64(n):
    n &= 0xffffffffffffffff
    return n.to_bytes(8, 'little')
 
def u64(s):
    assert(len(s) == 8)
    return int.from_bytes(s, 'little')
 
def ror8(n, c):
    return ((n>>c)|(n<<(8-c))) & 0xff
 
def rol8(n, c):
    return ((n<<c)|(n>>(8-c))) & 0xff
 
def sar8(n, c):
    if n & 0x80:
        return n >> c
    else:
        return ((n >> c) | (0xff << (8-c))) & 0xff
 
with open("MEM5_143948F5D_0000000140000000_0691E000.mem", "rb") as f:
    X86_CODE64 = f.read()
    assert(len(X86_CODE64) == 0x691E000)
 
md = Cs(CS_ARCH_X86, CS_MODE_64)
md.detail = True
 
global_rdtsc_value = 0
 
@handlekeyboardinterupt
def hook_code64(uc, address, size, user_data):
    global global_rdtsc_value
    global global_serial_start
    #print(hex(address), hex(size), uc.mem_read(address, 16).hex())
    instbytes = uc.mem_read(address, size)
    inst = list(md.disasm(instbytes, address))[0]
    #print(">>> Tracing instruction at 0x%x, instruction size = 0x%x, inst %s %s" %(address, size, inst.mnemonic, op_str))
    #print("0x%x\t%s %s" %(address, inst.mnemonic, inst.op_str) + "\t\t\t"+instbytes.hex()+"\t"+hex(uc.reg_read(UC_X86_REG_RDX)))
    print("0x%x\t%s %s" %(address, inst.mnemonic, inst.op_str))
    rip = address
    #rip = uc.reg_read(UC_X86_REG_RIP)
    #print(">>> RIP is 0x%x" %rip)
 
    if instbytes == b"\x0f\x31":    # rdtsc
        uc.reg_write(UC_X86_REG_RDX, 0)
        uc.reg_write(UC_X86_REG_RAX, global_rdtsc_value)
        global_rdtsc_value += 1000000
        uc.reg_write(UC_X86_REG_RIP, rip+size)
    elif instbytes == b"\xF3\x48\xA5":    # rep movsq
        rsi = uc.reg_read(UC_X86_REG_RSI)
        rdi = uc.reg_read(UC_X86_REG_RDI)
        rcx = uc.reg_read(UC_X86_REG_RCX)
#        print("rep movsq: ", hex(rip), hex(rsi), hex(rdi), hex(rcx))
        old_global_tainted_memlist = set(global_tainted_memlist)
        for a in old_global_tainted_memlist:
            if rdi <= a < (rdi+rcx*8):
                global_tainted_memlist.remove(a)
        for a in old_global_tainted_memlist:     # cannot merge the two loops
            if rsi <= a < (rsi+rcx*8):
                global_tainted_memlist.add(a + rdi-rsi)
                #print("\tchangeed: ", hex(a+(rdi-rsi)), "->", hex(a))
        uc.mem_write(rdi, bytes(uc.mem_read(rsi, rcx*8)))
        uc.reg_write(UC_X86_REG_RCX, 0)
        uc.reg_write(UC_X86_REG_RDI, rdi+rcx*8)
        uc.reg_write(UC_X86_REG_RSI, rsi+rcx*8)
        uc.reg_write(UC_X86_REG_RIP, rip+size)
    elif instbytes == b"\xF3\x48\xAB":    # rep stosq
        rax = uc.reg_read(UC_X86_REG_RAX)
        rdi = uc.reg_read(UC_X86_REG_RDI)
        rcx = uc.reg_read(UC_X86_REG_RCX)
#        print("rep stosq: ", hex(rip), hex(rax), hex(rdi), hex(rcx))
        for a in set(global_tainted_memlist):
            if rdi <= a < rdi+rcx*8:
                global_tainted_memlist.remove(a)
        uc.mem_write(rdi, p64(rax)*rcx)
        uc.reg_write(UC_X86_REG_RDI, rdi+rcx*8)
        uc.reg_write(UC_X86_REG_RIP, rip+size)
    elif instbytes in [b"\x48\x83\xe9\x01", b"\x83\xe9\x01", b"\x48\xff\xc9", b"\xff\xc9"]:    # sub rcx, 1    sub ecx, 1    dec rcx    dec ecx
        if not user_data.tracing:
            user_data.clear()
            user_data.tracing = True
            user_data.address = address
        else:
            assert(address == user_data.address)
            user_data.tracing = False
            rcx = uc.reg_read(UC_X86_REG_RCX)
#            print(user_data.memreg)
            memreg = uc.reg_read(capstone_reg_to_unicorn_reg(user_data.memreg))
            tmp = uc.mem_read(memreg, rcx-1)
#            print("\n".join(user_data.operations))
            jitcode = compile("\n".join(user_data.operations), "", mode="exec")
            '''
            for i in range(len(tmp)):
                c = tmp[i]
                g = {"c":c, "ror8":ror8, "rol8":rol8}
                exec(jitcode, g)
                c = g["c"]
                if (i == 0):
                    print(tmp[i], c)
                tmp[i] = c
            '''
            buf = bytearray(256)
            for i in range(256):
                c = i
                g = {"c":c, "ror8":ror8, "rol8":rol8}
                exec(jitcode, g)
                c = g["c"]
                buf[i] = c
            '''
            for i in range(len(tmp)):
                tmp[i] = buf[tmp[i]]
            '''
            tmpb = ctypes.create_string_buffer(bytes(tmp), len(tmp))
            libdodecryptblock.dodecryptblock(ctypes.byref(tmpb), len(tmp), ctypes.byref(ctypes.create_string_buffer(bytes(buf),len(buf))))
            tmp = tmpb.raw
 
            uc.mem_write(memreg, bytes(tmp))
            uc.reg_write(capstone_reg_to_unicorn_reg(user_data.memreg), memreg+user_data.direction*(rcx-1))
            uc.reg_write(UC_X86_REG_RCX, 1)    # let  sub rcx, 1  to execute
    elif user_data.tracing:
        print("tracing")
        if user_data.tracecount >= 100:
            user_data.tracing = False
        else:
            user_data.tracecount += 1
            inst = list(md.disasm(instbytes, address))[0]
            operands = list(inst.operands)
            if inst.id == X86_INS_MOV:
                if operands[0].type == X86_OP_REG and operands[1].type == X86_OP_MEM:
                    if operands[0].reg == X86_REG_AL:
                        # find the load inst
                        user_data.memreg = operands[1].mem.base
            elif inst.id in [X86_INS_ADD, X86_INS_SUB, X86_INS_XOR, X86_INS_AND, X86_INS_OR, X86_INS_ROR, X86_INS_ROL]:
                if operands[0].type == X86_OP_REG and operands[0].reg == X86_REG_AL:
                    if operands[1].type == X86_OP_REG:
                        v = uc.reg_read(capstone_reg_to_unicorn_reg(operands[1].reg))
                    elif operands[1].type == X86_OP_IMM:
                        v = operands[1].imm
                    else:
                        assert(0)
                    if inst.id == X86_INS_ADD:
                        pyinst = f"c = (c + {v}) & 0xff"
                    elif inst.id == X86_INS_SUB:
                        pyinst = f"c = (c - {v}) & 0xff"
                    elif inst.id == X86_INS_XOR:
                        pyinst = f"c = (c ^ {v}) & 0xff"
                    elif inst.id == X86_INS_AND:
                        pyinst = f"c = (c & {v}) & 0xff"
                    elif inst.id == X86_INS_OR:
                        pyinst = f"c = (c | {v}) & 0xff"
                    elif inst.id == X86_INS_ROR:
                        pyinst = f"c = ror8(c, {v})"
                    elif inst.id == X86_INS_ROL:
                        pyinst = f"c = rol8(c, {v})"
                    else:
                        assert(0)
                    user_data.operations.append(pyinst)
            elif inst.id in [X86_INS_NOT, X86_INS_NEG, X86_INS_INC, X86_INS_DEC]:
                if operands[0].type == X86_OP_REG and operands[0].reg == X86_REG_AL:
                    if inst.id == X86_INS_NOT:
                        pyinst = f"c = (~c) & 0xff"
                    elif inst.id == X86_INS_NEG:
                        pyinst = f"c = (-c) & 0xff"
                    elif inst.id == X86_INS_INC:
                        pyinst = f"c = (c + 1) & 0xff"
                    elif inst.id == X86_INS_DEC:
                        pyinst = f"c = (c - 1) & 0xff"
                    else:
                        assert(0)
                    user_data.operations.append(pyinst)
                elif operands[0].type == X86_OP_REG and operands[0].reg == user_data.memreg:
                    if inst.id == X86_INS_INC:
                        user_data.direction = 1
                    elif inst.id == X86_INS_DEC:
                        user_data.direction = -1
                    else:
                        #assert(0)
                        pass
            else:
                assert("al" not in inst.op_str.split(',')[0])
    pass
 
 
@handlekeyboardinterupt
def hook_code64_2(uc, address, size, user_data):
    global global_last_inst
    instbytes = uc.mem_read(address, size)
    inst = list(md.disasm(instbytes, address))[0]
    operands = list(inst.operands)
    global_last_inst = inst
 
    src_regs = set()
    target_regs = set()
    if len(operands) > 0:
        leftopreand = operands[0]
        rightopreand = operands[1] if len(operands) > 1 else operands[0]
        tainted = False
        if rightopreand.type == X86_OP_REG:
            rr = rightopreand.reg
            src_regs.add(rr)
        elif rightopreand.type == X86_OP_MEM:
            segment = rightopreand.mem.segment
            base = rightopreand.mem.base
            index = rightopreand.mem.index
            scale = rightopreand.mem.scale
            disp = rightopreand.mem.disp
            if base != 0:
                src_regs.add(base)
            if index != 0:
                src_regs.add(index)
            memaddr = disp + (uc.reg_read(capstone_reg_to_unicorn_reg(base)) if base != 0 else 0) + (uc.reg_read(capstone_reg_to_unicorn_reg(index)) if index != 0 else 0) * scale
            memvalue = u64(uc.mem_read(memaddr, 8)) if inst.id != X86_INS_LEA and segment == 0 else memaddr
            if inst.id != X86_INS_LEA and ((base != 0 and capstone_reg_to_normal_reg(base) in global_tainted_reglist) or (index != 0 and capstone_reg_to_normal_reg(index) in global_tainted_reglist)) and memaddr not in global_tainted_memlist:
                global_tainted_memlist.add(memaddr)    # important!
        if leftopreand.type == X86_OP_REG:
            rw = leftopreand.reg
            target_regs.add(rw)
            if inst.id != X86_INS_MOV and inst.id != X86_INS_LEA:
                src_regs.add(rw)
 
    tainted = False
    for rr in src_regs:
        rr_n = capstone_reg_to_normal_reg(rr)
        if rr_n in global_tainted_reglist:
            tainted = True
            break
 
    # special for xor rdi, rdi    sub rdi, rdi    and rdi, rdi
    if (inst.id in [X86_INS_XOR, X86_INS_SUB, X86_INS_AND]) and operands[0].type == X86_OP_REG and operands[1].type == X86_OP_REG and operands[0].reg == operands[1].reg:
        tainted = False
        if inst.id == X86_INS_AND:
            target_regs = set()
    # special for rsp
    if len(operands) > 0 and operands[0].type == X86_OP_REG and operands[0].reg == X86_REG_RSP:
        tainted = False
        target_regs = set()
    # special for xchg nottainted, tainted
    if inst.id == X86_INS_XCHG and operands[0].type == X86_OP_REG and operands[1].type == X86_OP_REG:
        tainted = False
        target_regs = set()
        r1 = operands[0].reg
        r2 = operands[1].reg
        if (not r1 in global_tainted_reglist) and r2 in global_tainted_reglist:
            r1, r2 = r2, r1
 
        if r1 in global_tainted_reglist and not r2 in global_tainted_reglist:
            tainted = False
            if r1 in global_tainted_reglist:
                global_tainted_reglist.remove(r1)
            if r2 not in global_tainted_reglist:
                global_tainted_reglist.add(r2)
        elif r1 in global_tainted_reglist and r2 in global_tainted_reglist:
            tainted = True
            target_regs.add(r1, r2)
    if inst.id in [X86_INS_CMP, X86_INS_NOT, X86_INS_POP, X86_INS_PUSH] or "cmov" in inst.mnemonic:
        tainted = False
        target_regs = set()
 
    for rw in target_regs:
        rw_n = capstone_reg_to_normal_reg(rw)
        if tainted:
            lv = uc.reg_read(leftopreand.reg) if leftopreand.type == X86_OP_REG and inst.id != X86_INS_MOV else 0xdeadbeefdeadbeefdeadbeef
            rv = uc.reg_read(rightopreand.reg) if rightopreand.type == X86_OP_REG else (rightopreand.imm if rightopreand.type == X86_OP_IMM else memvalue)
            print("\ttainted: 0x%x, 0x%x" % (lv, rv))
            if rw_n not in global_tainted_reglist:
                global_tainted_reglist.add(rw_n)
        else:
            if rw_n in global_tainted_reglist:
                global_tainted_reglist.remove(rw_n)
 
@handlekeyboardinterupt
def hook_mem_access(uc, access, address, size, value, user_data):
    #print(global_tainted_reglist)
    #print(len(global_tainted_memlist))
    global global_last_inst
    inst = global_last_inst
    operands = list(inst.operands)
    if access == UC_MEM_WRITE:
        rr = None
        if inst.id == X86_INS_PUSH:
            if operands[0].type == X86_OP_REG:
                rr = operands[0].reg
        elif inst.id == X86_INS_MOV:
            if operands[1].type == X86_OP_REG:
                rr = operands[1].reg
        tainted = False
 
        #print(rr, tainted, global_tainted_reglist)
        if rr and capstone_reg_to_normal_reg(rr) in global_tainted_reglist:
            tainted = True
            print("\ttainted >>> Memory is being WRITE at 0x%x, data size = %u, data value = 0x%x" \
                    %(address, size, value & 0xffffffffffffffff))
        else:
            pass
            #print("\tnottainted >>> Memory is being WRITE at 0x%x, data size = %u, data value = 0x%x" \
            #        %(address, size, value))
 
        for i in range(size):
            if tainted:
                if (address+i) not in global_tainted_memlist:
                    global_tainted_memlist.add(address+i)
            else:
                if (address+i) in global_tainted_memlist:
                    global_tainted_memlist.remove(address+i)
 
    else:   # READ
        tainted = False
        if address in global_tainted_memlist:
            tainted = True
 
        rw = None
        if len(operands) > 0 and operands[0].type == X86_OP_REG:
            rw = operands[0].reg
 
        if rw:
            rw_n = capstone_reg_to_normal_reg(rw)
 
            if tainted:
                print("\ttainted >>> Memory is being READ at 0x%x, data size = %u, data value = 0x%x" \
                        %(address, size, int.from_bytes(uc.mem_read(address, size), 'little')))
                if rw_n not in global_tainted_reglist:
                        global_tainted_reglist.add(rw_n)
            else:
                if rw_n in global_tainted_reglist:
                        global_tainted_reglist.remove(rw_n)
 
ADDRESS = 0x140000000
ADDRESS_SPACESIZE = 0x691E000
GS_BASE = 0x1000
 
mu = Uc(UC_ARCH_X86, UC_MODE_64)
mu.mem_map(ADDRESS, ADDRESS_SPACESIZE)
mu.mem_write(ADDRESS, X86_CODE64)
 
mu.reg_write(UC_X86_REG_GS_BASE, 0x1000)
mu.mem_map(GS_BASE, 0x1000)
mu.mem_write(GS_BASE+8, p64(0x1443BB3000))
mu.mem_write(GS_BASE+0x10, p64(0x143BB0000))
 
mu.reg_write(UC_X86_REG_RAX, 0xC3C3C3C3C3C3C3DF)
mu.reg_write(UC_X86_REG_RBX, 0x0000000140277000)
mu.reg_write(UC_X86_REG_RCX, 0x0000000000000000)
mu.reg_write(UC_X86_REG_RDX, 0x000000000027BA61)
mu.reg_write(UC_X86_REG_RBP, 0x000000014032C4DE)
mu.reg_write(UC_X86_REG_RSP, 0x0000000143BB2EC0)
mu.reg_write(UC_X86_REG_RSI, 0x000000014027A000)
mu.reg_write(UC_X86_REG_RDI, 0x000000014059DE7A)
mu.reg_write(UC_X86_REG_R8, 0x000000004032C4DE)
mu.reg_write(UC_X86_REG_R9, 0x000000014027E065)
mu.reg_write(UC_X86_REG_R10, 0x0000000140001000)
mu.reg_write(UC_X86_REG_R11, 0x0000000146680000)
mu.reg_write(UC_X86_REG_R12, 0x0000000000000080)
mu.reg_write(UC_X86_REG_R13, 0x0000000143BAF4F3)
mu.reg_write(UC_X86_REG_R14, 0x0000000000000000)
mu.reg_write(UC_X86_REG_R15, 0x000000000000000)
 
mu.reg_write(UC_X86_REG_EFLAGS, 0x0000000000000014)
mu.reg_write(UC_X86_REG_RIP, 0x0000000143948F58)
 
lc = LoopContext()
hpc = HoopPrintContext()
mu.hook_add(UC_HOOK_CODE, hook_code64, lc)#, ADDRESS, ADDRESS+ADDRESS_SPACESIZE)
mu.hook_add(UC_HOOK_CODE, hook_code64_2, None)
mu.hook_add(UC_HOOK_MEM_READ | UC_HOOK_MEM_WRITE, hook_mem_access)
 
for i in range(32):
    global_tainted_memlist.add(0x143BAF50C+i)
#global_serial_start = 0x143BAF50C
#mu.mem_write(0x143BAF50C, bytes.fromhex("B91AE5FCDA57D87406968CBDB8829799790A77302D7E8754B705894489B37A10"))
 
try:
    mu.emu_start(0x0000000143948F58, ADDRESS + ADDRESS_SPACESIZE)
except KeyboardInterrupt:
    import os
    os._exit(0)
# https://github.com/unicorn-engine/unicorn/blob/master/bindings/python/sample_x86.py
# https://github.com/aquynh/capstone/blob/master/bindings/python/capstone/x86.py
# https://github.com/aquynh/capstone/blob/master/bindings/python/capstone/x86_const.py
 
from unicorn import *
from unicorn.x86_const import *
from capstone import *
from capstone.x86_const import *
import traceback
import ctypes
 
 
'''
// cl /O2 -c dodecryptblock.c
// link -DLL -out:libdodecryptblock.dll dodecryptblock.obj
 
__declspec(dllexport) void dodecryptblock(unsigned char *buf, int len, unsigned char table[256]) {
    for (int i = 0; i < len; i++) {
        buf[i] = table[buf[i]];
    }
}
'''
libdodecryptblock = ctypes.CDLL("./libdodecryptblock.dll")
 
#global_serial_start = None
#global_serial_len = 32
 
global_tainted_memlist = set()
global_tainted_reglist = set()
 
global_last_inst = None
 
class LoopContext:
    __slots__ = ["tracing", "tracecount", "operations", "direction", "address", "memreg"]
    def __init__(self):
        self.clear()
    def clear(self):
        self.tracing = False
        self.tracecount = 0
        self.operations = []
        self.direction = None
        self.address = None
        self.memreg = None
 
class HoopPrintContext:
    __slots__ = ["initial_rsp", "last_rsp", "last_address", "last_inst"]
    def __init__(self):
        self.initial_rsp = None
        self.last_rsp = None
        self.last_address = None
        self.last_inst = None
 
def handlekeyboardinterupt(func):
    def wrapper_func(*args, **kwargs):
        try:
            r = func(*args, **kwargs)
        except KeyboardInterrupt:
            import os
            os._exit(0)
        except Exception:
            traceback.print_exc()
            import os
            import sys
            sys.stdout.flush()
            sys.stderr.flush()
            os._exit(0)
        return r
    return wrapper_func
 
def capstone_reg_to_unicorn_reg(i):
    d = {
        X86_REG_RAX : UC_X86_REG_RAX,
        X86_REG_RBX : UC_X86_REG_RBX,
        X86_REG_RCX : UC_X86_REG_RCX,
        X86_REG_RDX : UC_X86_REG_RDX,
        X86_REG_RBP : UC_X86_REG_RBP,
        X86_REG_RSP : UC_X86_REG_RSP,
        X86_REG_RSI : UC_X86_REG_RSI,
        X86_REG_RDI : UC_X86_REG_RDI,
        X86_REG_R8 : UC_X86_REG_R8,
        X86_REG_R9 : UC_X86_REG_R9,
        X86_REG_R10 : UC_X86_REG_R10,
        X86_REG_R11 : UC_X86_REG_R11,
        X86_REG_R12 : UC_X86_REG_R12,
        X86_REG_R13 : UC_X86_REG_R13,
        X86_REG_R14 : UC_X86_REG_R14,
        X86_REG_R15 : UC_X86_REG_R15,
 
        X86_REG_AL : UC_X86_REG_AL,
        X86_REG_BL : UC_X86_REG_BL,
        X86_REG_CL : UC_X86_REG_CL,
        X86_REG_DL : UC_X86_REG_DL,
        X86_REG_SIL : UC_X86_REG_SIL,
        X86_REG_DIL : UC_X86_REG_DIL,
 
        X86_REG_RIP : UC_X86_REG_RIP,
    }
    return d[i]
 
def capstone_reg_to_normal_reg(i):
    d = {
        X86_REG_RAX : X86_REG_RAX,
        X86_REG_RBX : X86_REG_RBX,
        X86_REG_RCX : X86_REG_RCX,
        X86_REG_RDX : X86_REG_RDX,
        X86_REG_RBP : X86_REG_RBP,
        X86_REG_RSP : X86_REG_RSP,
        X86_REG_RSI : X86_REG_RSI,
        X86_REG_RDI : X86_REG_RDI,
        X86_REG_R8 : X86_REG_R8,
        X86_REG_R9 : X86_REG_R9,
        X86_REG_R10 : X86_REG_R10,
        X86_REG_R11 : X86_REG_R11,
        X86_REG_R12 : X86_REG_R12,
        X86_REG_R13 : X86_REG_R13,
        X86_REG_R14 : X86_REG_R14,
        X86_REG_R15 : X86_REG_R15,
 
        X86_REG_EAX : X86_REG_RAX,
        X86_REG_EBX : X86_REG_RBX,
        X86_REG_ECX : X86_REG_RCX,
        X86_REG_EDX : X86_REG_RDX,
        X86_REG_EBP : X86_REG_RBP,
        X86_REG_ESP : X86_REG_RSP,
        X86_REG_ESI : X86_REG_RSI,
        X86_REG_EDI : X86_REG_RDI,
        X86_REG_R8D : X86_REG_R8,
        X86_REG_R9D : X86_REG_R9,
        X86_REG_R10D : X86_REG_R10,
        X86_REG_R11D : X86_REG_R11,
        X86_REG_R12D : X86_REG_R12,
        X86_REG_R13D : X86_REG_R13,
        X86_REG_R14D : X86_REG_R14,
        X86_REG_R15D : X86_REG_R15,
 
        X86_REG_AX : X86_REG_RAX,
        X86_REG_BX : X86_REG_RBX,
        X86_REG_CX : X86_REG_RCX,
        X86_REG_DX : X86_REG_RDX,
        X86_REG_BP : X86_REG_RBP,
        X86_REG_SP : X86_REG_RSP,
        X86_REG_SI : X86_REG_RSI,
        X86_REG_DI : X86_REG_RDI,
        X86_REG_R8W : X86_REG_R8,
        X86_REG_R9W : X86_REG_R9,
        X86_REG_R10W : X86_REG_R10,
        X86_REG_R11W : X86_REG_R11,
        X86_REG_R12W : X86_REG_R12,
        X86_REG_R13W : X86_REG_R13,
        X86_REG_R14W : X86_REG_R14,
        X86_REG_R15W : X86_REG_R15,
 
        X86_REG_AL : X86_REG_RAX,
        X86_REG_AH : X86_REG_RAX,
        X86_REG_BL : X86_REG_RBX,
        X86_REG_BH : X86_REG_RBX,
        X86_REG_CL : X86_REG_RCX,
        X86_REG_CH : X86_REG_RCX,
        X86_REG_DL : X86_REG_RDX,
        X86_REG_DH : X86_REG_RDX,
        X86_REG_BPL : X86_REG_RBP,
        X86_REG_SIL : X86_REG_RSI,
        X86_REG_DIL : X86_REG_RDI,
        X86_REG_R8B : X86_REG_R8,
        X86_REG_R9B : X86_REG_R9,
        X86_REG_R10B : X86_REG_R10,
        X86_REG_R11B : X86_REG_R11,
        X86_REG_R12B : X86_REG_R12,
        X86_REG_R13B : X86_REG_R13,
        X86_REG_R14B : X86_REG_R14,
        X86_REG_R15B : X86_REG_R15,
 
        X86_REG_RIP : X86_REG_RIP,
        X86_REG_EFLAGS : X86_REG_EFLAGS,
    }
    return d[i]
 
def p64(n):
    n &= 0xffffffffffffffff
    return n.to_bytes(8, 'little')
 
def u64(s):
    assert(len(s) == 8)
    return int.from_bytes(s, 'little')
 
def ror8(n, c):
    return ((n>>c)|(n<<(8-c))) & 0xff
 
def rol8(n, c):
    return ((n<<c)|(n>>(8-c))) & 0xff
 
def sar8(n, c):
    if n & 0x80:
        return n >> c
    else:
        return ((n >> c) | (0xff << (8-c))) & 0xff
 
with open("MEM5_143948F5D_0000000140000000_0691E000.mem", "rb") as f:
    X86_CODE64 = f.read()
    assert(len(X86_CODE64) == 0x691E000)
 
md = Cs(CS_ARCH_X86, CS_MODE_64)
md.detail = True
 
global_rdtsc_value = 0
 
@handlekeyboardinterupt
def hook_code64(uc, address, size, user_data):
    global global_rdtsc_value
    global global_serial_start
    #print(hex(address), hex(size), uc.mem_read(address, 16).hex())
    instbytes = uc.mem_read(address, size)
    inst = list(md.disasm(instbytes, address))[0]
    #print(">>> Tracing instruction at 0x%x, instruction size = 0x%x, inst %s %s" %(address, size, inst.mnemonic, op_str))
    #print("0x%x\t%s %s" %(address, inst.mnemonic, inst.op_str) + "\t\t\t"+instbytes.hex()+"\t"+hex(uc.reg_read(UC_X86_REG_RDX)))
    print("0x%x\t%s %s" %(address, inst.mnemonic, inst.op_str))
    rip = address
    #rip = uc.reg_read(UC_X86_REG_RIP)
    #print(">>> RIP is 0x%x" %rip)
 
    if instbytes == b"\x0f\x31":    # rdtsc
        uc.reg_write(UC_X86_REG_RDX, 0)
        uc.reg_write(UC_X86_REG_RAX, global_rdtsc_value)
        global_rdtsc_value += 1000000
        uc.reg_write(UC_X86_REG_RIP, rip+size)
    elif instbytes == b"\xF3\x48\xA5":    # rep movsq
        rsi = uc.reg_read(UC_X86_REG_RSI)
        rdi = uc.reg_read(UC_X86_REG_RDI)
        rcx = uc.reg_read(UC_X86_REG_RCX)
#        print("rep movsq: ", hex(rip), hex(rsi), hex(rdi), hex(rcx))
        old_global_tainted_memlist = set(global_tainted_memlist)
        for a in old_global_tainted_memlist:
            if rdi <= a < (rdi+rcx*8):
                global_tainted_memlist.remove(a)
        for a in old_global_tainted_memlist:     # cannot merge the two loops
            if rsi <= a < (rsi+rcx*8):
                global_tainted_memlist.add(a + rdi-rsi)
                #print("\tchangeed: ", hex(a+(rdi-rsi)), "->", hex(a))
        uc.mem_write(rdi, bytes(uc.mem_read(rsi, rcx*8)))
        uc.reg_write(UC_X86_REG_RCX, 0)
        uc.reg_write(UC_X86_REG_RDI, rdi+rcx*8)
        uc.reg_write(UC_X86_REG_RSI, rsi+rcx*8)
        uc.reg_write(UC_X86_REG_RIP, rip+size)
    elif instbytes == b"\xF3\x48\xAB":    # rep stosq
        rax = uc.reg_read(UC_X86_REG_RAX)
        rdi = uc.reg_read(UC_X86_REG_RDI)
        rcx = uc.reg_read(UC_X86_REG_RCX)
#        print("rep stosq: ", hex(rip), hex(rax), hex(rdi), hex(rcx))
        for a in set(global_tainted_memlist):
            if rdi <= a < rdi+rcx*8:
                global_tainted_memlist.remove(a)
        uc.mem_write(rdi, p64(rax)*rcx)
        uc.reg_write(UC_X86_REG_RDI, rdi+rcx*8)
        uc.reg_write(UC_X86_REG_RIP, rip+size)
    elif instbytes in [b"\x48\x83\xe9\x01", b"\x83\xe9\x01", b"\x48\xff\xc9", b"\xff\xc9"]:    # sub rcx, 1    sub ecx, 1    dec rcx    dec ecx
        if not user_data.tracing:
            user_data.clear()
            user_data.tracing = True
            user_data.address = address
        else:
            assert(address == user_data.address)
            user_data.tracing = False
            rcx = uc.reg_read(UC_X86_REG_RCX)
#            print(user_data.memreg)
            memreg = uc.reg_read(capstone_reg_to_unicorn_reg(user_data.memreg))
            tmp = uc.mem_read(memreg, rcx-1)
#            print("\n".join(user_data.operations))
            jitcode = compile("\n".join(user_data.operations), "", mode="exec")
            '''
            for i in range(len(tmp)):
                c = tmp[i]
                g = {"c":c, "ror8":ror8, "rol8":rol8}
                exec(jitcode, g)
                c = g["c"]
                if (i == 0):
                    print(tmp[i], c)
                tmp[i] = c
            '''
            buf = bytearray(256)
            for i in range(256):
                c = i
                g = {"c":c, "ror8":ror8, "rol8":rol8}
                exec(jitcode, g)
                c = g["c"]
                buf[i] = c
            '''
            for i in range(len(tmp)):
                tmp[i] = buf[tmp[i]]
            '''
            tmpb = ctypes.create_string_buffer(bytes(tmp), len(tmp))
            libdodecryptblock.dodecryptblock(ctypes.byref(tmpb), len(tmp), ctypes.byref(ctypes.create_string_buffer(bytes(buf),len(buf))))
            tmp = tmpb.raw
 
            uc.mem_write(memreg, bytes(tmp))
            uc.reg_write(capstone_reg_to_unicorn_reg(user_data.memreg), memreg+user_data.direction*(rcx-1))
            uc.reg_write(UC_X86_REG_RCX, 1)    # let  sub rcx, 1  to execute
    elif user_data.tracing:
        print("tracing")
        if user_data.tracecount >= 100:
            user_data.tracing = False
        else:
            user_data.tracecount += 1
            inst = list(md.disasm(instbytes, address))[0]
            operands = list(inst.operands)
            if inst.id == X86_INS_MOV:
                if operands[0].type == X86_OP_REG and operands[1].type == X86_OP_MEM:
                    if operands[0].reg == X86_REG_AL:
                        # find the load inst
                        user_data.memreg = operands[1].mem.base
            elif inst.id in [X86_INS_ADD, X86_INS_SUB, X86_INS_XOR, X86_INS_AND, X86_INS_OR, X86_INS_ROR, X86_INS_ROL]:
                if operands[0].type == X86_OP_REG and operands[0].reg == X86_REG_AL:
                    if operands[1].type == X86_OP_REG:
                        v = uc.reg_read(capstone_reg_to_unicorn_reg(operands[1].reg))
                    elif operands[1].type == X86_OP_IMM:
                        v = operands[1].imm
                    else:
                        assert(0)
                    if inst.id == X86_INS_ADD:
                        pyinst = f"c = (c + {v}) & 0xff"
                    elif inst.id == X86_INS_SUB:
                        pyinst = f"c = (c - {v}) & 0xff"
                    elif inst.id == X86_INS_XOR:
                        pyinst = f"c = (c ^ {v}) & 0xff"
                    elif inst.id == X86_INS_AND:
                        pyinst = f"c = (c & {v}) & 0xff"
                    elif inst.id == X86_INS_OR:
                        pyinst = f"c = (c | {v}) & 0xff"
                    elif inst.id == X86_INS_ROR:
                        pyinst = f"c = ror8(c, {v})"
                    elif inst.id == X86_INS_ROL:
                        pyinst = f"c = rol8(c, {v})"
                    else:
                        assert(0)
                    user_data.operations.append(pyinst)
            elif inst.id in [X86_INS_NOT, X86_INS_NEG, X86_INS_INC, X86_INS_DEC]:
                if operands[0].type == X86_OP_REG and operands[0].reg == X86_REG_AL:
                    if inst.id == X86_INS_NOT:
                        pyinst = f"c = (~c) & 0xff"
                    elif inst.id == X86_INS_NEG:
                        pyinst = f"c = (-c) & 0xff"
                    elif inst.id == X86_INS_INC:
                        pyinst = f"c = (c + 1) & 0xff"
                    elif inst.id == X86_INS_DEC:
                        pyinst = f"c = (c - 1) & 0xff"
                    else:
                        assert(0)
                    user_data.operations.append(pyinst)
                elif operands[0].type == X86_OP_REG and operands[0].reg == user_data.memreg:
                    if inst.id == X86_INS_INC:
                        user_data.direction = 1
                    elif inst.id == X86_INS_DEC:
                        user_data.direction = -1
                    else:
                        #assert(0)
                        pass
            else:
                assert("al" not in inst.op_str.split(',')[0])
    pass
 
 
@handlekeyboardinterupt
def hook_code64_2(uc, address, size, user_data):
    global global_last_inst
    instbytes = uc.mem_read(address, size)
    inst = list(md.disasm(instbytes, address))[0]
    operands = list(inst.operands)
    global_last_inst = inst
 
    src_regs = set()
    target_regs = set()
    if len(operands) > 0:
        leftopreand = operands[0]
        rightopreand = operands[1] if len(operands) > 1 else operands[0]
        tainted = False
        if rightopreand.type == X86_OP_REG:
            rr = rightopreand.reg
            src_regs.add(rr)
        elif rightopreand.type == X86_OP_MEM:
            segment = rightopreand.mem.segment
            base = rightopreand.mem.base
            index = rightopreand.mem.index
            scale = rightopreand.mem.scale
            disp = rightopreand.mem.disp
            if base != 0:
                src_regs.add(base)
            if index != 0:
                src_regs.add(index)
            memaddr = disp + (uc.reg_read(capstone_reg_to_unicorn_reg(base)) if base != 0 else 0) + (uc.reg_read(capstone_reg_to_unicorn_reg(index)) if index != 0 else 0) * scale
            memvalue = u64(uc.mem_read(memaddr, 8)) if inst.id != X86_INS_LEA and segment == 0 else memaddr
            if inst.id != X86_INS_LEA and ((base != 0 and capstone_reg_to_normal_reg(base) in global_tainted_reglist) or (index != 0 and capstone_reg_to_normal_reg(index) in global_tainted_reglist)) and memaddr not in global_tainted_memlist:
                global_tainted_memlist.add(memaddr)    # important!
        if leftopreand.type == X86_OP_REG:
            rw = leftopreand.reg
            target_regs.add(rw)
            if inst.id != X86_INS_MOV and inst.id != X86_INS_LEA:
                src_regs.add(rw)
 
    tainted = False
    for rr in src_regs:
        rr_n = capstone_reg_to_normal_reg(rr)
        if rr_n in global_tainted_reglist:
            tainted = True
            break
 
    # special for xor rdi, rdi    sub rdi, rdi    and rdi, rdi
    if (inst.id in [X86_INS_XOR, X86_INS_SUB, X86_INS_AND]) and operands[0].type == X86_OP_REG and operands[1].type == X86_OP_REG and operands[0].reg == operands[1].reg:
        tainted = False
        if inst.id == X86_INS_AND:
            target_regs = set()
    # special for rsp
    if len(operands) > 0 and operands[0].type == X86_OP_REG and operands[0].reg == X86_REG_RSP:
        tainted = False
        target_regs = set()
    # special for xchg nottainted, tainted
    if inst.id == X86_INS_XCHG and operands[0].type == X86_OP_REG and operands[1].type == X86_OP_REG:
        tainted = False
        target_regs = set()
        r1 = operands[0].reg

[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

上传的附件:
收藏
免费 7
支持
分享
最新回复 (4)
雪    币: 163
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
2
2021-5-25 10:45
0
雪    币: 1432
活跃值: (3062)
能力值: ( LV9,RANK:156 )
在线值:
发帖
回帖
粉丝
3
可以把 dodecryptblock.c 分享一下吗?
2021-5-25 15:34
0
雪    币: 23578
活跃值: (12115)
能力值: ( LV15,RANK:1744 )
在线值:
发帖
回帖
粉丝
4
whydbg 可以把 dodecryptblock.c 分享一下吗?
注释里就是这个文件的代码
 
__declspec(dllexport) void dodecryptblock(unsigned char *buf, int len, unsigned char table[256]) {
    for (int i = 0; i < len; i++) {
        buf[i] = table[buf[i]];
    }
}
2021-5-25 23:05
0
雪    币: 1432
活跃值: (3062)
能力值: ( LV9,RANK:156 )
在线值:
发帖
回帖
粉丝
5
谢谢          
2021-5-26 08:32
0
游客
登录 | 注册 方可回帖
返回
//