Pandora Ransomware
Analysis of Pandora ransomware including some fun unpacking
There was a 'Not Found' error fetching URL: 'https://twitter.com/kienbigmummy/status/1504750051956240384/photo/1'
Overview
Sample: 5b56c5d86347e164c6e571c86dbf5b1535eae6b979fede6ed66b01e79ea33b7b
Sample is x64 and is on malshare.
Unpacked sample 2619862c382d3e375f13f3859c6ab44db1a4bce905b4a617df2390fbf36902e7
References
- f0wl blog Pandora Ransomware
- Control Flow Flattening
- Deobfuscation - Recovering an ollvm
- stadeo deobfuscation tool
- Control Flow Unflattening
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)
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)
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