File size: 6,227 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 145 146 147 148 149 150 151 152 153 154 155 156 157 |
"""
Disassemble the Cipher function in oneocr.dll to find the exact crypto parameters.
Find code references to the crypto strings we identified.
"""
import struct
import re
dll_path = r"c:\Users\MattyMroz\Desktop\PROJECTS\ONEOCR\ocr_data\oneocr.dll"
with open(dll_path, "rb") as f:
data = f.read()
# Parse PE headers to find section info
pe_sig_offset = struct.unpack_from("<I", data, 0x3C)[0]
assert data[pe_sig_offset:pe_sig_offset+4] == b"PE\x00\x00"
# COFF header
coff_start = pe_sig_offset + 4
num_sections = struct.unpack_from("<H", data, coff_start + 2)[0]
opt_header_size = struct.unpack_from("<H", data, coff_start + 16)[0]
# Optional header
opt_start = coff_start + 20
magic = struct.unpack_from("<H", data, opt_start)[0]
assert magic == 0x20B # PE32+
image_base = struct.unpack_from("<Q", data, opt_start + 24)[0]
# Sections
section_start = opt_start + opt_header_size
sections = []
for i in range(num_sections):
s_off = section_start + i * 40
name = data[s_off:s_off+8].rstrip(b"\x00").decode("ascii", errors="replace")
vsize = struct.unpack_from("<I", data, s_off + 8)[0]
va = struct.unpack_from("<I", data, s_off + 12)[0]
raw_size = struct.unpack_from("<I", data, s_off + 16)[0]
raw_ptr = struct.unpack_from("<I", data, s_off + 20)[0]
sections.append((name, va, vsize, raw_ptr, raw_size))
print(f"Section: {name:10s} VA=0x{va:08x} VSize=0x{vsize:08x} RawPtr=0x{raw_ptr:08x} RawSize=0x{raw_size:08x}")
print(f"\nImage base: 0x{image_base:016x}")
def rva_to_file_offset(rva):
for name, va, vsize, raw_ptr, raw_size in sections:
if va <= rva < va + vsize:
return raw_ptr + (rva - va)
return None
def file_offset_to_rva(offset):
for name, va, vsize, raw_ptr, raw_size in sections:
if raw_ptr <= offset < raw_ptr + raw_size:
return va + (offset - raw_ptr)
return None
# Key string offsets we found
crypto_strings = {
"SHA256 (wide)": 0x02724b60,
"AES (wide)": 0x02724b70,
"BlockLength (wide)": 0x02724b78,
"ChainingModeCFB (wide)": 0x02724b90,
"meta->magic_number == MAGIC_NUMBER": 0x02724bb0,
"Unable to uncompress": 0x02724bd8,
"Crypto.cpp": 0x02724c08,
"Error returned from crypto API": 0x02724c40,
"ChainingMode (wide)": 0x02724c80,
"MessageBlockLength (wide)": 0x02724ca0,
}
# Calculate RVAs of these strings
print("\n=== String RVAs ===")
for name, file_off in crypto_strings.items():
rva = file_offset_to_rva(file_off)
if rva:
print(f" {name}: file=0x{file_off:08x} RVA=0x{rva:08x}")
# Find code references to these strings via LEA instruction patterns
# In x64, LEA reg, [rip+disp32] is encoded as:
# 48 8D xx yy yy yy yy (where xx determines the register)
# or 4C 8D xx yy yy yy yy
# The target address = instruction_address + 7 + disp32
print("\n=== Searching for code references to crypto strings ===")
# Focus on the most important strings
key_strings = {
"ChainingModeCFB (wide)": 0x02724b90,
"SHA256 (wide)": 0x02724b60,
"AES (wide)": 0x02724b70,
"Crypto.cpp": 0x02724c08,
"MessageBlockLength (wide)": 0x02724ca0,
"meta->magic_number": 0x02724bb0,
}
# Find the .text section (code)
text_section = None
for name, va, vsize, raw_ptr, raw_size in sections:
if name == ".text":
text_section = (va, vsize, raw_ptr, raw_size)
break
if text_section:
text_va, text_vsize, text_raw, text_rawsize = text_section
print(f"\n.text section: VA=0x{text_va:08x} size=0x{text_vsize:08x}")
for string_name, string_file_off in key_strings.items():
string_rva = file_offset_to_rva(string_file_off)
if string_rva is None:
continue
# Search for LEA instructions referencing this RVA
# LEA uses RIP-relative addressing: target = RIP + disp32
# RIP at instruction = instruction_RVA + instruction_length (typically 7 for LEA)
refs_found = []
for code_off in range(text_raw, text_raw + text_rawsize - 7):
# Check for LEA patterns
b0 = data[code_off]
b1 = data[code_off + 1]
# 48 8D 0D/15/05/1D/25/2D/35/3D = LEA with REX.W
# 4C 8D 05/0D/15/1D/25/2D/35/3D = LEA with REX.WR
if b0 in (0x48, 0x4C) and b1 == 0x8D:
modrm = data[code_off + 2]
if (modrm & 0xC7) == 0x05: # mod=00, rm=101 (RIP-relative)
disp32 = struct.unpack_from("<i", data, code_off + 3)[0]
instr_rva = file_offset_to_rva(code_off)
if instr_rva is None:
continue
target_rva = instr_rva + 7 + disp32
if target_rva == string_rva:
reg_idx = (modrm >> 3) & 7
if b0 == 0x4C:
reg_idx += 8
reg_names = ["rax","rcx","rdx","rbx","rsp","rbp","rsi","rdi",
"r8","r9","r10","r11","r12","r13","r14","r15"]
reg = reg_names[reg_idx]
refs_found.append((code_off, instr_rva, reg))
if refs_found:
print(f"\n References to '{string_name}' (RVA=0x{string_rva:08x}):")
for code_off, instr_rva, reg in refs_found[:5]:
print(f" at file=0x{code_off:08x} RVA=0x{instr_rva:08x}: LEA {reg}, [{string_name}]")
# Dump surrounding code
ctx_start = max(text_raw, code_off - 64)
ctx_end = min(text_raw + text_rawsize, code_off + 128)
# Simple bytecode dump with some x64 instruction markers
print(f" Context (file offset 0x{ctx_start:08x} - 0x{ctx_end:08x}):")
for i in range(ctx_start, ctx_end, 16):
chunk = data[i:i+16]
hex_part = " ".join(f"{b:02x}" for b in chunk)
rva_i = file_offset_to_rva(i)
marker = " <<<" if i <= code_off < i + 16 else ""
print(f" {rva_i:08x}: {hex_part}{marker}")
else:
print(f"\n No code references found for '{string_name}'")
|