File size: 5,740 Bytes
ce847d4 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 |
"""
Disassemble the actual BCrypt crypto operations at 0x18015ba45+
and map all indirect calls to IAT entries.
"""
import struct
from capstone import Cs, CS_ARCH_X86, CS_MODE_64
DLL_PATH = r"c:\Users\MattyMroz\Desktop\PROJECTS\ONEOCR\ocr_data\oneocr.dll"
IMAGE_BASE = 0x180000000
TEXT_VA = 0x1000
TEXT_FILE_OFFSET = 0x400
def rva_to_file(rva):
return rva - TEXT_VA + TEXT_FILE_OFFSET
def file_to_rva(foff):
return foff - TEXT_FILE_OFFSET + TEXT_VA
with open(DLL_PATH, "rb") as f:
dll_data = f.read()
md = Cs(CS_ARCH_X86, CS_MODE_64)
md.detail = False
def disasm_region(name, file_start, file_end):
rva_start = file_to_rva(file_start)
va_start = IMAGE_BASE + rva_start
code = dll_data[file_start:file_end]
print(f"\n{'='*100}")
print(f"{name}")
print(f"File: 0x{file_start:08x}-0x{file_end:08x}, RVA: 0x{rva_start:08x}")
print(f"{'='*100}")
for insn in md.disasm(code, va_start):
foff = rva_to_file(insn.address - IMAGE_BASE)
line = f" {insn.address - IMAGE_BASE:08x} ({foff:08x}): {insn.bytes.hex():<40s} {insn.mnemonic:<14s} {insn.op_str}"
# Annotate indirect calls
if insn.mnemonic == 'call' and insn.bytes[0] == 0xFF and insn.bytes[1] == 0x15:
disp = struct.unpack_from('<i', bytes(insn.bytes), 2)[0]
target_rva = (insn.address - IMAGE_BASE) + insn.size + disp
line += f" ; IAT@0x{target_rva:08x}"
print(line)
# First, let's identify ALL BCrypt IAT entries
# From previous analysis:
# BCryptOpenAlgorithmProvider → IAT 0x0081a5e0
# BCryptGetProperty → IAT 0x0081a5d0
# BCryptSetProperty → IAT 0x0081a608
# Let's find the rest by looking at the import section
# Parse PE to find BCrypt imports
print("="*100)
print("FINDING ALL BCRYPT IAT ENTRIES")
print("="*100)
# Parse PE headers
e_lfanew = struct.unpack_from('<I', dll_data, 0x3c)[0]
opt_hdr_off = e_lfanew + 24
import_dir_rva = struct.unpack_from('<I', dll_data, opt_hdr_off + 120)[0] # Import RVA
import_dir_size = struct.unpack_from('<I', dll_data, opt_hdr_off + 124)[0]
# Find sections for RVA to file offset mapping
num_sections = struct.unpack_from('<H', dll_data, e_lfanew + 6)[0]
sections_off = e_lfanew + 24 + struct.unpack_from('<H', dll_data, e_lfanew + 20)[0]
sections = []
for i in range(num_sections):
sec_off = sections_off + i * 40
name = dll_data[sec_off:sec_off+8].rstrip(b'\x00').decode('ascii', errors='replace')
vsize = struct.unpack_from('<I', dll_data, sec_off + 8)[0]
vrva = struct.unpack_from('<I', dll_data, sec_off + 12)[0]
rawsize = struct.unpack_from('<I', dll_data, sec_off + 16)[0]
rawoff = struct.unpack_from('<I', dll_data, sec_off + 20)[0]
sections.append((name, vrva, vsize, rawoff, rawsize))
def rva_to_foff(rva):
for name, vrva, vsize, rawoff, rawsize in sections:
if vrva <= rva < vrva + vsize:
return rawoff + (rva - vrva)
return None
if import_dir_rva:
ioff = rva_to_foff(import_dir_rva)
if ioff:
idx = 0
while True:
desc_off = ioff + idx * 20
ilt_rva = struct.unpack_from('<I', dll_data, desc_off)[0]
name_rva = struct.unpack_from('<I', dll_data, desc_off + 12)[0]
iat_rva = struct.unpack_from('<I', dll_data, desc_off + 16)[0]
if ilt_rva == 0 and name_rva == 0:
break
name_off = rva_to_foff(name_rva)
if name_off:
dname = dll_data[name_off:name_off+64].split(b'\x00')[0].decode('ascii', errors='replace')
if 'bcrypt' in dname.lower():
print(f"\nDLL: {dname}, ILT RVA: 0x{ilt_rva:08x}, IAT RVA: 0x{iat_rva:08x}")
# Walk the ILT to find function names
ilt_off = rva_to_foff(ilt_rva)
iat_entry_rva = iat_rva
j = 0
while ilt_off:
entry = struct.unpack_from('<Q', dll_data, ilt_off + j * 8)[0]
if entry == 0:
break
if entry & (1 << 63):
ordinal = entry & 0xFFFF
print(f" IAT 0x{iat_entry_rva:08x}: Ordinal {ordinal}")
else:
hint_rva = entry & 0x7FFFFFFF
hint_off = rva_to_foff(hint_rva)
if hint_off:
hint = struct.unpack_from('<H', dll_data, hint_off)[0]
fname = dll_data[hint_off+2:hint_off+66].split(b'\x00')[0].decode('ascii', errors='replace')
print(f" IAT 0x{iat_entry_rva:08x}: {fname} (hint {hint})")
iat_entry_rva += 8
j += 1
idx += 1
# Now disassemble the actual BCrypt crypto operation block
# This is at RVA 0x0015ba45 = file 0x0015ae45
disasm_region(
"BCrypt crypto operations (key gen + decrypt)",
0x0015ae45, 0x0015b200
)
# Find all indirect calls in extended range
print("\n" + "="*100)
print("ALL INDIRECT CALLS (ff 15) in range 0x0015ae00-0x0015c200")
print("="*100)
for i in range(0x0015ae00, 0x0015c200):
if dll_data[i] == 0xFF and dll_data[i+1] == 0x15:
rva = file_to_rva(i)
disp = struct.unpack_from('<i', dll_data, i + 2)[0]
target_rva = rva + 6 + disp
print(f" File 0x{i:08x} (RVA 0x{rva:08x}): call [rip+0x{disp:x}] -> IAT@0x{target_rva:08x}")
# Also disassemble the function at 0x18015abd0 (called to process data when r14b=true)
# RVA 0x0015abd0, file 0x00159fd0
disasm_region(
"Function at 0x18015abd0 (called on data when r14b=true)",
0x00159fd0, 0x0015a0c0
)
|