Overview

Sample: 5b56c5d86347e164c6e571c86dbf5b1535eae6b979fede6ed66b01e79ea33b7b

Sample is x64 and is on malshare.

Unpacked sample 2619862c382d3e375f13f3859c6ab44db1a4bce905b4a617df2390fbf36902e7

References

Stage 1 Unpacking

Stage1 is just a modified UPX. We can unpack it with the following steps.

  • removed X permissions from the first PE section memory
  • run till exception
  • excpetion EIP is OEP for PE
  • dump and reconstruct imports with Scylla

Payload Obfuscation

The ransomware has both obfuscated strings and control flow obfuscation. The obfuscated strings can be deobfuscated directly with an emulator call to the deobfuscation function. The cf obfuscation requires special attention.

Control Flow Obfuscation

Many function in the binary have been obfuscated using two layers of obfuscation. First control flow flattening (CFF) has been applied to convert the original code basic blocks into a state machine and force all flow through a dispatcher. For each state the dispatcher determines the control flow. Then a second layer of obfuscation has been applied to the basic blocks that make up the dispatcher. The dispatcher bbs are seperated with conditional jmp statements that require a lookup in a hard coded jmp table. This hard coded jmp table may be an attempt to frustrate symbolic execution (a traditional tool for CFF deobfuscation.) The flow is discribed below.

  • basic blocks (bb) that contain the actual payload code are accessed via a state machine - each bb has a state
  • the dispatcher for the state machine uses the bb state as a key which is used to calculate jumps between it's own (dispatcher) basic blocks
  • the (dispatcher) bb uses a compare with the key/state to generate a conditional lookup in a hard coded jump table
  • the jump table contains an obfuscated address of the next bb, either another dispatcher bb or the original code bb

Deobfuscation Steps

Our approach is to seperate the dispatcher bb from the payload bb. For each dispatcher bb the code is emulated with all conditions to generate the conditional jump addresses. The bb is then replaced with a simple compare and conditional jmp. Emulation is done with Dumpulator.

Once the dispatcher has been deobfuscated we should be able to see the control flow for the payload bb and futher simplify the dispatcher using more traditional tools, possibly removing it completely.

r14 = FFFFFFFFAE6529F8

00007FF6C4B068E3   | 3D E1A53B17           | cmp eax,173BA5E1                                      | not part of cf
00007FF6C4B068E8   | BA D8000000           | mov edx,D8                                            | rdx = 0xd8
00007FF6C4B068ED   | BD 20010000           | mov ebp,120                                           | rbp = 0x120
00007FF6C4B068F2   | 48:0F4CD5             | cmovl rdx,rbp                                         | 
00007FF6C4B068F6   | 48:8B1411             | mov rdx,qword ptr ds:[rcx+rdx]  | table:00007FF6C4B69640 + rdx = rdx=00007FF7164B42E9
00007FF6C4B068FA   | 4C:01F2               | add rdx,r14                     |rdx=00007FF7164B42E9 + r14
00007FF6C4B068FD   | FFE2                  | jmp rdx                         | rdx = 00007FF6C4B06CE1

if the jump is relative it's for a code bb

if the jump is reg then it's a cf bb

if there is a ret this is the end

start = 0x00007FF6C4B067F0 last_jmp = 0x00007FF6C4B0706C end = 0x00007FF6C4B070D0

build a table of bb the start is next head after jmp, end is jmp emulate patch the jmp for each table entry

IDA Produce Basic Block Table

There are two dispatcher bb formats, one that uses cmovl, cmovz, and one that uses setl and setz. We need to match both patterns and determine the type of condition for our jmp statement, and the eax cmp value. For each bb record the following information so it can be used in our emulator.

  • bb start
  • bb end
  • add cmov instruction
  • type of cmov instruction (l,z)
  • eax cmp value
  • jmp register

Results: (bb_start, bb_end, eax_value, jmp_condition,jmp_condition_address, jmp_register)

# Basic blocks for dispatcher
bb_table = []
# List of addresses of original code basic blocks
bb_orig_table = []
ptr = 0x00007FF6C4B067C8
end = 0x00007FF6C4B070D0 
bb_start = ptr
while ptr <= end:
    if print_insn_mnem(ptr) == 'jmp': 
        op_type = idc.get_operand_type(ptr, 0)
        if op_type == o_reg:
            # This is a cf bb save it
            reg_name = print_operand(ptr, 0)
            # Get eax value
            first_instruction = print_insn_mnem(bb_start)
            if first_instruction == 'cmp':
                eax_cmp_value = get_operand_value(bb_start,1)
                # Find cmov instruction
                bb_ptr = bb_start
                jmp_condition = None
                jmp_condition_address = None
                while bb_ptr < ptr:
                    if 'cmovl' == print_insn_mnem(bb_ptr):
                        jmp_condition = 'cmovl'
                        break
                    elif 'cmovz' == print_insn_mnem(bb_ptr):
                        jmp_condition = 'cmovz'
                        break
                    bb_ptr  = next_head(bb_ptr)
                jmp_condition_address = bb_ptr
                # Check results of cmov find
                if jmp_condition is None:
                    # This bb doesn't match our pattern skip it
                    print(f"BB at {hex(bb_start)} doesn't contain cmovl or cmovz, skip it")
                    ptr = next_head(ptr)
                    while not is_code(ida_bytes.get_full_flags(ptr)):
                        ptr = next_head(ptr)
                    bb_start = ptr
                    continue
            elif first_instruction == 'xor':
                # assume next instruction is the cmp, we should check
                ptr_cmp = next_head(bb_start)
                if "cmp" != print_insn_mnem(ptr_cmp):
                    # This bb doesn't match our pattern skip it
                    print(f"BB at {hex(bb_start)} doesn't contain have cmp after xor, skip it")
                    ptr = next_head(ptr)
                    while not is_code(ida_bytes.get_full_flags(ptr)):
                        ptr = next_head(ptr)
                    bb_start = ptr
                    continue
                eax_cmp_value = get_operand_value(ptr_cmp,1)
                # Find cmov instruction
                bb_ptr = bb_start
                jmp_condition = None
                jmp_condition_address = None
                while bb_ptr < ptr:
                    if 'setl' == print_insn_mnem(bb_ptr):
                        jmp_condition = 'setl'
                        break
                    elif 'setz' == print_insn_mnem(bb_ptr):
                        jmp_condition = 'setz'
                        break
                    elif 'setnz' == print_insn_mnem(bb_ptr):
                        jmp_condition = 'setnz'
                        break
                    bb_ptr  = next_head(bb_ptr)
                jmp_condition_address = bb_ptr
                # Check results of cmov find
                if jmp_condition is None:
                    # This bb doesn't match our pattern skip it
                    print(f"BB at {hex(bb_start)} doesn't contain setl or setz, skip it")
                    ptr = next_head(ptr)
                    while not is_code(ida_bytes.get_full_flags(ptr)):
                        ptr = next_head(ptr)
                    bb_start = ptr
                    continue
            else:
                # This bb doesn't match our pattern skip it
                print(f"BB at {hex(bb_start)} doesn't match pattern, skip it")
                ptr = next_head(ptr)
                while not is_code(ida_bytes.get_full_flags(ptr)):
                    ptr = next_head(ptr)
                bb_start = ptr
                continue

            print(f"Dispatcher bb {hex(bb_start)}")
            bb_table.append((bb_start,ptr,eax_cmp_value,jmp_condition,jmp_condition_address,reg_name))
            ptr = next_head(ptr)
            while not is_code(ida_bytes.get_full_flags(ptr)):
                ptr = next_head(ptr)
            bb_start = ptr
        else:
            # This is code bb don't save it
            print(f"Original code bb {hex(bb_start)}")
            bb_orig_table.append(bb_start)
            ptr = next_head(ptr)
            while not is_code(ida_bytes.get_full_flags(ptr)):
                ptr = next_head(ptr)
            bb_start = ptr
    else:
        ptr = next_head(ptr)

Emulate Basic Blocks

For each dispatcher bb emulate the block both satisfying the condition and not satisfying it to produce both the jmp address for the conditional jmp and the jmp address for the unconditional jmp.

DUMP_FILE = '/tmp/pandora.dmp'

from dumpulator import Dumpulator
dp = Dumpulator(DUMP_FILE, quiet=True)

# temp_addr = dp.allocate(256)
# dp.call(0x140001000, [temp_addr, 0x140017000])
# decrypted = dp.read_str(temp_addr)
# print(f"decrypted: '{decrypted}'")

# # Obfuscation key for table values
# dp.regs.r14 = 0xFFFFFFFFAE6529F8
# # Pointer to the jmp table 
# dp.regs.rcx = 0x00007FF6C4B69640
# dp.start(0x00007FF6C4B068E8,end=0x00007FF6C4B068FD)
dp = Dumpulator(DUMP_FILE, quiet=True)
#(bb_start, bb_end, eax_value, jmp_condition,jmp_condition_address, jmp_register)
#bb_table = [(0x7ff6c4b067f0, 0x7ff6c4b06817, 0x10bc6c78, 'cmovl', 0x7ff6c4b067fa, 'rdx'), (0x7ff6c4b06819, 0x7ff6c4b0682e, 0xffffffffc30bae2e, 'cmovl', 0x7ff6c4b06823, 'rdx'), (0x7ff6c4b06830, 0x7ff6c4b06845, 0xffffffffa2992627, 'cmovl', 0x7ff6c4b0683a, 'rdx'), (0x7ff6c4b06847, 0x7ff6c4b06861, 0xffffffffa22a16af, 'cmovl', 0x7ff6c4b06856, 'rdx'), (0x7ff6c4b06863, 0x7ff6c4b0687d, 0xffffffff8cbc0434, 'cmovz', 0x7ff6c4b06872, 'rcx'), (0x7ff6c4b068b0, 0x7ff6c4b068c5, 0x6c249751, 'cmovl', 0x7ff6c4b068ba, 'rdx'), (0x7ff6c4b068c7, 0x7ff6c4b068e1, 0x3b2b8a1e, 'cmovl', 0x7ff6c4b068d6, 'rdx'), (0x7ff6c4b068e3, 0x7ff6c4b068fd, 0x173ba5e1, 'cmovl', 0x7ff6c4b068f2, 'rdx'), (0x7ff6c4b068ff, 0x7ff6c4b06918, 0x10bc6c78, 'setz', 0x7ff6c4b06906, 'rcx'), (0x7ff6c4b069b0, 0x7ff6c4b069ca, 0xffffffffd43fb344, 'cmovl', 0x7ff6c4b069bf, 'rdx'), (0x7ff6c4b069cc, 0x7ff6c4b069e6, 0xffffffffcef7092e, 'cmovl', 0x7ff6c4b069db, 'rdx'), (0x7ff6c4b069e8, 0x7ff6c4b06a02, 0xffffffffc30bae2e, 'cmovz', 0x7ff6c4b069f7, 'rcx'), (0x7ff6c4b06a30, 0x7ff6c4b06a4a, 0x7d71a1e3, 'cmovl', 0x7ff6c4b06a3f, 'rdx'), (0x7ff6c4b06a4c, 0x7ff6c4b06a66, 0x7a980236, 'cmovl', 0x7ff6c4b06a5b, 'rdx'), (0x7ff6c4b06a68, 0x7ff6c4b06a82, 0x6c249751, 'cmovz', 0x7ff6c4b06a77, 'rcx'), (0x7ff6c4b06a99, 0x7ff6c4b06ab3, 0xffffffffc094d6c9, 'cmovl', 0x7ff6c4b06aa8, 'rdx'), (0x7ff6c4b06ab5, 0x7ff6c4b06acf, 0xffffffffa2992627, 'cmovz', 0x7ff6c4b06ac4, 'rcx'), (0x7ff6c4b06b09, 0x7ff6c4b06b1e, 0x3cd69d30, 'setl', 0x7ff6c4b06b10, 'rdx'), (0x7ff6c4b06b20, 0x7ff6c4b06b39, 0x3b2b8a1e, 'setnz', 0x7ff6c4b06b27, 'rcx'), (0x7ff6c4b06b63, 0x7ff6c4b06b7d, 0xffffffffecce8ff1, 'cmovl', 0x7ff6c4b06b72, 'rdx'), (0x7ff6c4b06b7f, 0x7ff6c4b06b99, 0xffffffffd43fb344, 'cmovz', 0x7ff6c4b06b8e, 'rcx'), (0x7ff6c4b06bbb, 0x7ff6c4b06bd5, 0x7d9d86f3, 'cmovl', 0x7ff6c4b06bca, 'rdx'), (0x7ff6c4b06bd7, 0x7ff6c4b06bf1, 0x7d71a1e3, 'cmovz', 0x7ff6c4b06be6, 'rcx'), (0x7ff6c4b06c45, 0x7ff6c4b06c5f, 0xffffffffa22a16af, 'cmovz', 0x7ff6c4b06c54, 'rcx'), (0x7ff6c4b06ce1, 0x7ff6c4b06cfb, 0x173ba5e1, 'cmovz', 0x7ff6c4b06cf0, 'rcx'), (0x7ff6c4b06e83, 0x7ff6c4b06e9d, 0xffffffffcef7092e, 'cmovz', 0x7ff6c4b06e92, 'rcx'), (0x7ff6c4b06edc, 0x7ff6c4b06ef6, 0x7a980236, 'cmovz', 0x7ff6c4b06eeb, 'rcx'), (0x7ff6c4b06f3d, 0x7ff6c4b06f56, 0xffffffffc094d6c9, 'setz', 0x7ff6c4b06f44, 'rcx'), (0x7ff6c4b07008, 0x7ff6c4b07022, 0x3cd69d30, 'cmovz', 0x7ff6c4b07017, 'rcx'), (0x7ff6c4b07052, 0x7ff6c4b0706c, 0xffffffffecce8ff1, 'cmovz', 0x7ff6c4b07061, 'rcx')]
# Above is the old table that was missing one bb because we started our search too late
bb_table = [(0x7ff6c4b067c8, 0x7ff6c4b067e2, 0x7d9d86f3, 'cmovz', 0x7ff6c4b067d7, 'rcx'), (0x7ff6c4b067f0, 0x7ff6c4b06817, 0x10bc6c78, 'cmovl', 0x7ff6c4b067fa, 'rdx'), (0x7ff6c4b06819, 0x7ff6c4b0682e, 0xffffffffc30bae2e, 'cmovl', 0x7ff6c4b06823, 'rdx'), (0x7ff6c4b06830, 0x7ff6c4b06845, 0xffffffffa2992627, 'cmovl', 0x7ff6c4b0683a, 'rdx'), (0x7ff6c4b06847, 0x7ff6c4b06861, 0xffffffffa22a16af, 'cmovl', 0x7ff6c4b06856, 'rdx'), (0x7ff6c4b06863, 0x7ff6c4b0687d, 0xffffffff8cbc0434, 'cmovz', 0x7ff6c4b06872, 'rcx'), (0x7ff6c4b068b0, 0x7ff6c4b068c5, 0x6c249751, 'cmovl', 0x7ff6c4b068ba, 'rdx'), (0x7ff6c4b068c7, 0x7ff6c4b068e1, 0x3b2b8a1e, 'cmovl', 0x7ff6c4b068d6, 'rdx'), (0x7ff6c4b068e3, 0x7ff6c4b068fd, 0x173ba5e1, 'cmovl', 0x7ff6c4b068f2, 'rdx'), (0x7ff6c4b068ff, 0x7ff6c4b06918, 0x10bc6c78, 'setz', 0x7ff6c4b06906, 'rcx'), (0x7ff6c4b069b0, 0x7ff6c4b069ca, 0xffffffffd43fb344, 'cmovl', 0x7ff6c4b069bf, 'rdx'), (0x7ff6c4b069cc, 0x7ff6c4b069e6, 0xffffffffcef7092e, 'cmovl', 0x7ff6c4b069db, 'rdx'), (0x7ff6c4b069e8, 0x7ff6c4b06a02, 0xffffffffc30bae2e, 'cmovz', 0x7ff6c4b069f7, 'rcx'), (0x7ff6c4b06a30, 0x7ff6c4b06a4a, 0x7d71a1e3, 'cmovl', 0x7ff6c4b06a3f, 'rdx'), (0x7ff6c4b06a4c, 0x7ff6c4b06a66, 0x7a980236, 'cmovl', 0x7ff6c4b06a5b, 'rdx'), (0x7ff6c4b06a68, 0x7ff6c4b06a82, 0x6c249751, 'cmovz', 0x7ff6c4b06a77, 'rcx'), (0x7ff6c4b06a99, 0x7ff6c4b06ab3, 0xffffffffc094d6c9, 'cmovl', 0x7ff6c4b06aa8, 'rdx'), (0x7ff6c4b06ab5, 0x7ff6c4b06acf, 0xffffffffa2992627, 'cmovz', 0x7ff6c4b06ac4, 'rcx'), (0x7ff6c4b06b09, 0x7ff6c4b06b1e, 0x3cd69d30, 'setl', 0x7ff6c4b06b10, 'rdx'), (0x7ff6c4b06b20, 0x7ff6c4b06b39, 0x3b2b8a1e, 'setnz', 0x7ff6c4b06b27, 'rcx'), (0x7ff6c4b06b63, 0x7ff6c4b06b7d, 0xffffffffecce8ff1, 'cmovl', 0x7ff6c4b06b72, 'rdx'), (0x7ff6c4b06b7f, 0x7ff6c4b06b99, 0xffffffffd43fb344, 'cmovz', 0x7ff6c4b06b8e, 'rcx'), (0x7ff6c4b06bbb, 0x7ff6c4b06bd5, 0x7d9d86f3, 'cmovl', 0x7ff6c4b06bca, 'rdx'), (0x7ff6c4b06bd7, 0x7ff6c4b06bf1, 0x7d71a1e3, 'cmovz', 0x7ff6c4b06be6, 'rcx'), (0x7ff6c4b06c45, 0x7ff6c4b06c5f, 0xffffffffa22a16af, 'cmovz', 0x7ff6c4b06c54, 'rcx'), (0x7ff6c4b06ce1, 0x7ff6c4b06cfb, 0x173ba5e1, 'cmovz', 0x7ff6c4b06cf0, 'rcx'), (0x7ff6c4b06e83, 0x7ff6c4b06e9d, 0xffffffffcef7092e, 'cmovz', 0x7ff6c4b06e92, 'rcx'), (0x7ff6c4b06edc, 0x7ff6c4b06ef6, 0x7a980236, 'cmovz', 0x7ff6c4b06eeb, 'rcx'), (0x7ff6c4b06f3d, 0x7ff6c4b06f56, 0xffffffffc094d6c9, 'setz', 0x7ff6c4b06f44, 'rcx'), (0x7ff6c4b07008, 0x7ff6c4b07022, 0x3cd69d30, 'cmovz', 0x7ff6c4b07017, 'rcx'), (0x7ff6c4b07052, 0x7ff6c4b0706c, 0xffffffffecce8ff1, 'cmovz', 0x7ff6c4b07061, 'rcx')]
bb_jmp_table = []


def emulate_bb(bb_start, bb_end, eax_value, jmp_reg):
    dp.regs.eflags = 0
    dp.regs.r15 = 0x190
    dp.regs.r14 = 0x0FFFFFFFFAE6529F8
    dp.regs.r13 = 0x10
    dp.regs.r12 = 0x1B0
    dp.regs.rcx = 0x00007FF6C4B69640
    dp.regs.rax = eax_value
    dp.start(bb_start,end=bb_end)
    jmp_reg_value = dp.regs.__getattr__(jmp_reg)
    return jmp_reg_value
    
    

## We need to calculate two jmp addresses conditional on the eax compare
## based on these we can replace the control bb with conditional jmps based on the compare
# def get_jmp(start_ea, end_ea, reg_name, jmp_cond):
#     dp.regs.r15 = 0x190
#     dp.regs.r14 = 0x0FFFFFFFFAE6529F8
#     dp.regs.r13 = 0x10
#     dp.regs.r12 = 0x1B0
#     dp.regs.rcx = 0x00007FF6C4B69640
#     if jmp_cond:
        
#     else:
        
#     dp.start(start_ea,end=end_ea)
#     print(hex(dp.regs.__getattr__(reg_name)))


for bb in bb_table:
    bb_start = bb[0]
    bb_end = bb[1]
    eax_value = bb[2]
    jmp_condition = bb[3]
    jmp_condition_address = bb[4]
    jmp_register = bb[5]
    # One emulation to satisfy conditon and one without
    if jmp_condition == 'cmovl' or jmp_condition == 'setl':
        jmp_addr_satisfied = emulate_bb(bb_start, bb_end, eax_value - 1, jmp_register)
        jmp_addr_unsatisfied = emulate_bb(bb_start, bb_end, eax_value + 1, jmp_register)
    elif jmp_condition == 'cmovz' or jmp_condition == 'setz':
        jmp_addr_satisfied = emulate_bb(bb_start, bb_end, eax_value, jmp_register)
        jmp_addr_unsatisfied = emulate_bb(bb_start, bb_end, eax_value + 1, jmp_register)
    elif jmp_condition == 'setnz':
        jmp_addr_satisfied = emulate_bb(bb_start, bb_end, eax_value + 1, jmp_register)
        jmp_addr_unsatisfied = emulate_bb(bb_start, bb_end, eax_value, jmp_register)
    print(f"BB {hex(bb_start)}:{hex(bb_end)} - jmp_addr_satisfied: {hex(jmp_addr_satisfied)} - jmp_addr_unsatisfied: {hex(jmp_addr_unsatisfied)}")
    bb_jmp_table.append((bb_start,bb_end,eax_value,jmp_condition,jmp_condition_address,jmp_register,jmp_addr_satisfied,jmp_addr_unsatisfied))


for bb in bb_jmp_table:
    print(bb)
BB 0x7ff6c4b067c8:0x7ff6c4b067e2 - jmp_addr_satisfied: 0x7ff6c4b070d1 - jmp_addr_unsatisfied: 0x7ff6c4b067f0
BB 0x7ff6c4b067f0:0x7ff6c4b06817 - jmp_addr_satisfied: 0x7ff6c4b06819 - jmp_addr_unsatisfied: 0x7ff6c4b068b0
BB 0x7ff6c4b06819:0x7ff6c4b0682e - jmp_addr_satisfied: 0x7ff6c4b06830 - jmp_addr_unsatisfied: 0x7ff6c4b069b0
BB 0x7ff6c4b06830:0x7ff6c4b06845 - jmp_addr_satisfied: 0x7ff6c4b06847 - jmp_addr_unsatisfied: 0x7ff6c4b06a99
BB 0x7ff6c4b06847:0x7ff6c4b06861 - jmp_addr_satisfied: 0x7ff6c4b06863 - jmp_addr_unsatisfied: 0x7ff6c4b06c45
BB 0x7ff6c4b06863:0x7ff6c4b0687d - jmp_addr_satisfied: 0x7ff6c4b0687f - jmp_addr_unsatisfied: 0x7ff6c4b067f0
BB 0x7ff6c4b068b0:0x7ff6c4b068c5 - jmp_addr_satisfied: 0x7ff6c4b068c7 - jmp_addr_unsatisfied: 0x7ff6c4b06a30
BB 0x7ff6c4b068c7:0x7ff6c4b068e1 - jmp_addr_satisfied: 0x7ff6c4b068e3 - jmp_addr_unsatisfied: 0x7ff6c4b06b09
BB 0x7ff6c4b068e3:0x7ff6c4b068fd - jmp_addr_satisfied: 0x7ff6c4b068ff - jmp_addr_unsatisfied: 0x7ff6c4b06ce1
BB 0x7ff6c4b068ff:0x7ff6c4b06918 - jmp_addr_satisfied: 0x7ff6c4b0691a - jmp_addr_unsatisfied: 0x7ff6c4b067f0
BB 0x7ff6c4b069b0:0x7ff6c4b069ca - jmp_addr_satisfied: 0x7ff6c4b069cc - jmp_addr_unsatisfied: 0x7ff6c4b06b63
BB 0x7ff6c4b069cc:0x7ff6c4b069e6 - jmp_addr_satisfied: 0x7ff6c4b069e8 - jmp_addr_unsatisfied: 0x7ff6c4b06e83
BB 0x7ff6c4b069e8:0x7ff6c4b06a02 - jmp_addr_satisfied: 0x7ff6c4b06a04 - jmp_addr_unsatisfied: 0x7ff6c4b067f0
BB 0x7ff6c4b06a30:0x7ff6c4b06a4a - jmp_addr_satisfied: 0x7ff6c4b06a4c - jmp_addr_unsatisfied: 0x7ff6c4b06bbb
BB 0x7ff6c4b06a4c:0x7ff6c4b06a66 - jmp_addr_satisfied: 0x7ff6c4b06a68 - jmp_addr_unsatisfied: 0x7ff6c4b06edc
BB 0x7ff6c4b06a68:0x7ff6c4b06a82 - jmp_addr_satisfied: 0x7ff6c4b06a84 - jmp_addr_unsatisfied: 0x7ff6c4b067f0
BB 0x7ff6c4b06a99:0x7ff6c4b06ab3 - jmp_addr_satisfied: 0x7ff6c4b06ab5 - jmp_addr_unsatisfied: 0x7ff6c4b06f3d
BB 0x7ff6c4b06ab5:0x7ff6c4b06acf - jmp_addr_satisfied: 0x7ff6c4b06ad1 - jmp_addr_unsatisfied: 0x7ff6c4b067f0
BB 0x7ff6c4b06b09:0x7ff6c4b06b1e - jmp_addr_satisfied: 0x7ff6c4b06b20 - jmp_addr_unsatisfied: 0x7ff6c4b07008
BB 0x7ff6c4b06b20:0x7ff6c4b06b39 - jmp_addr_satisfied: 0x7ff6c4b067f0 - jmp_addr_unsatisfied: 0x7ff6c4b06b3b
BB 0x7ff6c4b06b63:0x7ff6c4b06b7d - jmp_addr_satisfied: 0x7ff6c4b06b7f - jmp_addr_unsatisfied: 0x7ff6c4b07052
BB 0x7ff6c4b06b7f:0x7ff6c4b06b99 - jmp_addr_satisfied: 0x7ff6c4b06b9b - jmp_addr_unsatisfied: 0x7ff6c4b067f0
BB 0x7ff6c4b06bbb:0x7ff6c4b06bd5 - jmp_addr_satisfied: 0x7ff6c4b06bd7 - jmp_addr_unsatisfied: 0x7ff6c4b067c8
BB 0x7ff6c4b06bd7:0x7ff6c4b06bf1 - jmp_addr_satisfied: 0x7ff6c4b06bf3 - jmp_addr_unsatisfied: 0x7ff6c4b067f0
BB 0x7ff6c4b06c45:0x7ff6c4b06c5f - jmp_addr_satisfied: 0x7ff6c4b06c61 - jmp_addr_unsatisfied: 0x7ff6c4b067f0
BB 0x7ff6c4b06ce1:0x7ff6c4b06cfb - jmp_addr_satisfied: 0x7ff6c4b06cfd - jmp_addr_unsatisfied: 0x7ff6c4b067f0
BB 0x7ff6c4b06e83:0x7ff6c4b06e9d - jmp_addr_satisfied: 0x7ff6c4b06e9f - jmp_addr_unsatisfied: 0x7ff6c4b067f0
BB 0x7ff6c4b06edc:0x7ff6c4b06ef6 - jmp_addr_satisfied: 0x7ff6c4b06ef8 - jmp_addr_unsatisfied: 0x7ff6c4b067f0
BB 0x7ff6c4b06f3d:0x7ff6c4b06f56 - jmp_addr_satisfied: 0x7ff6c4b06f58 - jmp_addr_unsatisfied: 0x7ff6c4b067f0
BB 0x7ff6c4b07008:0x7ff6c4b07022 - jmp_addr_satisfied: 0x7ff6c4b07024 - jmp_addr_unsatisfied: 0x7ff6c4b067f0
BB 0x7ff6c4b07052:0x7ff6c4b0706c - jmp_addr_satisfied: 0x7ff6c4b0706e - jmp_addr_unsatisfied: 0x7ff6c4b067f0
(140697838577608, 140697838577634, 2107475699, 'cmovz', 140697838577623, 'rcx', 140697838579921, 140697838577648)
(140697838577648, 140697838577687, 280783992, 'cmovl', 140697838577658, 'rdx', 140697838577689, 140697838577840)
(140697838577689, 140697838577710, 18446744072686906926, 'cmovl', 140697838577699, 'rdx', 140697838577712, 140697838578096)
(140697838577712, 140697838577733, 18446744072142530087, 'cmovl', 140697838577722, 'rdx', 140697838577735, 140697838578329)
(140697838577735, 140697838577761, 18446744072135251631, 'cmovl', 140697838577750, 'rdx', 140697838577763, 140697838578757)
(140697838577763, 140697838577789, 18446744071775716404, 'cmovz', 140697838577778, 'rcx', 140697838577791, 140697838577648)
(140697838577840, 140697838577861, 1814337361, 'cmovl', 140697838577850, 'rdx', 140697838577863, 140697838578224)
(140697838577863, 140697838577889, 992709150, 'cmovl', 140697838577878, 'rdx', 140697838577891, 140697838578441)
(140697838577891, 140697838577917, 389785057, 'cmovl', 140697838577906, 'rdx', 140697838577919, 140697838578913)
(140697838577919, 140697838577944, 280783992, 'setz', 140697838577926, 'rcx', 140697838577946, 140697838577648)
(140697838578096, 140697838578122, 18446744072975528772, 'cmovl', 140697838578111, 'rdx', 140697838578124, 140697838578531)
(140697838578124, 140697838578150, 18446744072886880558, 'cmovl', 140697838578139, 'rdx', 140697838578152, 140697838579331)
(140697838578152, 140697838578178, 18446744072686906926, 'cmovz', 140697838578167, 'rcx', 140697838578180, 140697838577648)
(140697838578224, 140697838578250, 2104599011, 'cmovl', 140697838578239, 'rdx', 140697838578252, 140697838578619)
(140697838578252, 140697838578278, 2056782390, 'cmovl', 140697838578267, 'rdx', 140697838578280, 140697838579420)
(140697838578280, 140697838578306, 1814337361, 'cmovz', 140697838578295, 'rcx', 140697838578308, 140697838577648)
(140697838578329, 140697838578355, 18446744072645564105, 'cmovl', 140697838578344, 'rdx', 140697838578357, 140697838579517)
(140697838578357, 140697838578383, 18446744072142530087, 'cmovz', 140697838578372, 'rcx', 140697838578385, 140697838577648)
(140697838578441, 140697838578462, 1020697904, 'setl', 140697838578448, 'rdx', 140697838578464, 140697838579720)
(140697838578464, 140697838578489, 992709150, 'setnz', 140697838578471, 'rcx', 140697838577648, 140697838578491)
(140697838578531, 140697838578557, 18446744073387544561, 'cmovl', 140697838578546, 'rdx', 140697838578559, 140697838579794)
(140697838578559, 140697838578585, 18446744072975528772, 'cmovz', 140697838578574, 'rcx', 140697838578587, 140697838577648)
(140697838578619, 140697838578645, 2107475699, 'cmovl', 140697838578634, 'rdx', 140697838578647, 140697838577608)
(140697838578647, 140697838578673, 2104599011, 'cmovz', 140697838578662, 'rcx', 140697838578675, 140697838577648)
(140697838578757, 140697838578783, 18446744072135251631, 'cmovz', 140697838578772, 'rcx', 140697838578785, 140697838577648)
(140697838578913, 140697838578939, 389785057, 'cmovz', 140697838578928, 'rcx', 140697838578941, 140697838577648)
(140697838579331, 140697838579357, 18446744072886880558, 'cmovz', 140697838579346, 'rcx', 140697838579359, 140697838577648)
(140697838579420, 140697838579446, 2056782390, 'cmovz', 140697838579435, 'rcx', 140697838579448, 140697838577648)
(140697838579517, 140697838579542, 18446744072645564105, 'setz', 140697838579524, 'rcx', 140697838579544, 140697838577648)
(140697838579720, 140697838579746, 1020697904, 'cmovz', 140697838579735, 'rcx', 140697838579748, 140697838577648)
(140697838579794, 140697838579820, 18446744073387544561, 'cmovz', 140697838579809, 'rcx', 140697838579822, 140697838577648)

IDA Patch Basic Blocks

Copy the output table from our emulator and use it to patch out each bb with a conditional jmp and an unconditional jmp based on the emulation results.

import struct 

bb_jmp_table = [(140697838577608, 140697838577634, 2107475699, 'cmovz', 140697838577623, 'rcx', 140697838579921, 140697838577648),
(140697838577648, 140697838577687, 280783992, 'cmovl', 140697838577658, 'rdx', 140697838577689, 140697838577840),
(140697838577689, 140697838577710, 18446744072686906926, 'cmovl', 140697838577699, 'rdx', 140697838577712, 140697838578096),
(140697838577712, 140697838577733, 18446744072142530087, 'cmovl', 140697838577722, 'rdx', 140697838577735, 140697838578329),
(140697838577735, 140697838577761, 18446744072135251631, 'cmovl', 140697838577750, 'rdx', 140697838577763, 140697838578757),
(140697838577763, 140697838577789, 18446744071775716404, 'cmovz', 140697838577778, 'rcx', 140697838577791, 140697838577648),
(140697838577840, 140697838577861, 1814337361, 'cmovl', 140697838577850, 'rdx', 140697838577863, 140697838578224),
(140697838577863, 140697838577889, 992709150, 'cmovl', 140697838577878, 'rdx', 140697838577891, 140697838578441),
(140697838577891, 140697838577917, 389785057, 'cmovl', 140697838577906, 'rdx', 140697838577919, 140697838578913),
(140697838577919, 140697838577944, 280783992, 'setz', 140697838577926, 'rcx', 140697838577946, 140697838577648),
(140697838578096, 140697838578122, 18446744072975528772, 'cmovl', 140697838578111, 'rdx', 140697838578124, 140697838578531),
(140697838578124, 140697838578150, 18446744072886880558, 'cmovl', 140697838578139, 'rdx', 140697838578152, 140697838579331),
(140697838578152, 140697838578178, 18446744072686906926, 'cmovz', 140697838578167, 'rcx', 140697838578180, 140697838577648),
(140697838578224, 140697838578250, 2104599011, 'cmovl', 140697838578239, 'rdx', 140697838578252, 140697838578619),
(140697838578252, 140697838578278, 2056782390, 'cmovl', 140697838578267, 'rdx', 140697838578280, 140697838579420),
(140697838578280, 140697838578306, 1814337361, 'cmovz', 140697838578295, 'rcx', 140697838578308, 140697838577648),
(140697838578329, 140697838578355, 18446744072645564105, 'cmovl', 140697838578344, 'rdx', 140697838578357, 140697838579517),
(140697838578357, 140697838578383, 18446744072142530087, 'cmovz', 140697838578372, 'rcx', 140697838578385, 140697838577648),
(140697838578441, 140697838578462, 1020697904, 'setl', 140697838578448, 'rdx', 140697838578464, 140697838579720),
(140697838578464, 140697838578489, 992709150, 'setnz', 140697838578471, 'rcx', 140697838577648, 140697838578491),
(140697838578531, 140697838578557, 18446744073387544561, 'cmovl', 140697838578546, 'rdx', 140697838578559, 140697838579794),
(140697838578559, 140697838578585, 18446744072975528772, 'cmovz', 140697838578574, 'rcx', 140697838578587, 140697838577648),
(140697838578619, 140697838578645, 2107475699, 'cmovl', 140697838578634, 'rdx', 140697838578647, 140697838577608),
(140697838578647, 140697838578673, 2104599011, 'cmovz', 140697838578662, 'rcx', 140697838578675, 140697838577648),
(140697838578757, 140697838578783, 18446744072135251631, 'cmovz', 140697838578772, 'rcx', 140697838578785, 140697838577648),
(140697838578913, 140697838578939, 389785057, 'cmovz', 140697838578928, 'rcx', 140697838578941, 140697838577648),
(140697838579331, 140697838579357, 18446744072886880558, 'cmovz', 140697838579346, 'rcx', 140697838579359, 140697838577648),
(140697838579420, 140697838579446, 2056782390, 'cmovz', 140697838579435, 'rcx', 140697838579448, 140697838577648),
(140697838579517, 140697838579542, 18446744072645564105, 'setz', 140697838579524, 'rcx', 140697838579544, 140697838577648),
(140697838579720, 140697838579746, 1020697904, 'cmovz', 140697838579735, 'rcx', 140697838579748, 140697838577648),
(140697838579794, 140697838579820, 18446744073387544561, 'cmovz', 140697838579809, 'rcx', 140697838579822, 140697838577648)]

for bb in bb_jmp_table:
    bb_start = bb[0]
    bb_end = bb[1]
    eax_value = bb[2]
    jmp_condition = bb[3]
    jmp_condition_address = bb[4]
    jmp_register = bb[5]
    jmp_addr_satisfied = bb[6]
    jmp_addr_unsatisfied = bb[7]

    patch_jmp_cond_start = jmp_condition_address
    jmp_rel_statisfied =  jmp_addr_satisfied - (patch_jmp_cond_start + 6)
    patch_jmp_start = patch_jmp_cond_start + 6
    jmp_rel =  jmp_addr_unsatisfied - (patch_jmp_start + 5)

    if jmp_condition == 'cmovl' or jmp_condition == 'setl':
        # jl
        patch_jmp_condition = b'\x0f\x8c' +  struct.pack('<i',jmp_rel_statisfied)
        patch_jmp = b'\xe9' +  struct.pack('<i',jmp_rel)
    elif jmp_condition == 'cmovz' or jmp_condition == 'setz':
        # jz
        patch_jmp_condition = b'\x0f\x84' +  struct.pack('<i',jmp_rel_statisfied)
        patch_jmp = b'\xe9' +  struct.pack('<i',jmp_rel)
    elif jmp_condition == 'setnz':
        # jnz
        patch_jmp_condition = b'\x0f\x85' +  struct.pack('<i',jmp_rel_statisfied)
        patch_jmp = b'\xe9' +  struct.pack('<i',jmp_rel)

    # calculat nops for end of patch
    total_bytes = bb_end - jmp_condition_address + 2
    nop_count = total_bytes - 11

    # 11 bytes for the patch + nops to fill space
    patch_bytes = patch_jmp_condition + patch_jmp + b'\x90'* nop_count

    # patch the bytes
    patch_ptr = jmp_condition_address
    for c in patch_bytes:
        patch_byte(patch_ptr, c)
        patch_ptr += 1