File size: 5,642 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
"""
Full disassembly of the Cipher function from AES setup through BCryptDecrypt.
Based on findings:
- SHA256 provider at file 0x0015a3e2 (RVA 0x0015afe2)
- AES provider at file 0x0015a702 (RVA 0x0015b302)
- ChainingModeCFB at file 0x0015a7cd (RVA 0x0015b3cd)
- MessageBlockLength at file 0x0015a7fc (RVA 0x0015b3fc)
- BCryptGenerateSymmetricKey import at ~0x027ef0a2
- Need to find: key handling, IV passing, BCryptDecrypt call
"""
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  # .text section file offset

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}, VA: 0x{va_start:016x}")
    print(f"{'='*100}")
    for insn in md.disasm(code, va_start):
        foff = rva_to_file(insn.address - IMAGE_BASE)
        print(f"  {insn.address - IMAGE_BASE:08x} ({foff:08x}): {insn.bytes.hex():<40s} {insn.mnemonic:<14s} {insn.op_str}")

# The Cipher function appears to start before the AES setup.
# Let's find the function prologue by scanning backwards from the AES setup.
# The AES LEA is at file 0x0015a702. Let's look for a typical function prologue.

# First, let's find the actual function start
# Look for common prologues (push rbp, sub rsp, mov [rsp+...], etc.) before the AES reference
print("\n" + "="*100)
print("SCANNING FOR FUNCTION PROLOGUE before AES setup (file 0x0015a702)")
print("="*100)

# Search backwards from 0x0015a702 for push rbp or sub rsp patterns
search_start = 0x0015a500  # Start from after SHA256Hash function
search_end = 0x0015a710
search_region = dll_data[search_start:search_end]

# Look for common x64 function prologues
# 48 89 5C 24 xx = mov [rsp+xx], rbx
# 48 89 74 24 xx = mov [rsp+xx], rsi  
# 55 = push rbp
# 40 55 = push rbp (with REX prefix)
# 48 8B EC = mov rbp, rsp
# 48 81 EC xx xx xx xx = sub rsp, imm32

for i in range(len(search_region) - 4):
    b = search_region[i:i+8]
    foff = search_start + i
    rva = file_to_rva(foff)
    
    # Look for function start patterns
    if b[:5] == bytes([0x48, 0x89, 0x5C, 0x24, 0x08]):  # mov [rsp+8], rbx
        print(f"  Possible prologue at file 0x{foff:08x} (RVA 0x{rva:08x}): mov [rsp+8], rbx")
    elif b[:2] == bytes([0x40, 0x55]):  # push rbp with REX
        print(f"  Possible prologue at file 0x{foff:08x} (RVA 0x{rva:08x}): REX push rbp")
    elif b[:1] == bytes([0x55]) and (i == 0 or search_region[i-1] in (0xC3, 0xCC, 0x90)):
        print(f"  Possible prologue at file 0x{foff:08x} (RVA 0x{rva:08x}): push rbp (after ret/nop/int3)")
    elif b[:4] == bytes([0x48, 0x83, 0xEC, 0x28]):  # sub rsp, 0x28
        print(f"  Possible prologue at file 0x{foff:08x} (RVA 0x{rva:08x}): sub rsp, 0x28")
    elif b[:3] == bytes([0x48, 0x81, 0xEC]):  # sub rsp, imm32
        val = struct.unpack_from('<I', b, 3)[0]
        print(f"  Possible prologue at file 0x{foff:08x} (RVA 0x{rva:08x}): sub rsp, 0x{val:X}")

# Now disassemble the ENTIRE Cipher function region - from after SHA256Hash to well past all setup
# The function is large, so let's do it in meaningful chunks

# Region 1: Function start to AES provider setup
disasm_region(
    "Cipher function part 1: prologue to AES provider",
    0x0015a500, 0x0015a720
)

# Region 2: AES provider setup through ChainingMode and MessageBlockLength
disasm_region(
    "Cipher function part 2: AES provider, ChainingModeCFB, MessageBlockLength",
    0x0015a720, 0x0015a880
)

# Region 3: After IV extraction, BCryptGenerateSymmetricKey, BCryptDecrypt calls
# This is the critical region we need
disasm_region(
    "Cipher function part 3: key gen and decrypt (extended)",
    0x0015abd0, 0x0015ae00
)

# Also check what's around the BCryptDecrypt import call
# BCrypt imports are indirect calls through IAT
# Let's find all indirect calls (FF 15) in the cipher function range
print("\n" + "="*100)
print("ALL INDIRECT CALLS (ff 15) in Cipher function region 0x0015a500-0x0015ae00")
print("="*100)

search_start = 0x0015a500
search_end = 0x0015ae00
for i in range(search_end - search_start - 6):
    foff = search_start + i
    if dll_data[foff] == 0xFF and dll_data[foff+1] == 0x15:
        rva = file_to_rva(foff)
        disp = struct.unpack_from('<i', dll_data, foff + 2)[0]
        target_rva = rva + 6 + disp  # RIP-relative
        target_foff = rva_to_file(target_rva)
        # Read the IAT entry (8 bytes at the target)
        iat_value = struct.unpack_from('<Q', dll_data, target_foff)[0] if target_foff + 8 <= len(dll_data) else 0
        print(f"  File 0x{foff:08x} (RVA 0x{rva:08x}): call [rip+0x{disp:x}] -> IAT at RVA 0x{target_rva:08x}")

# Also disassemble the region between IV handling (0x0015abdb) and magic number check (0x0015a170)
# This might contain the actual BCryptDecrypt call
disasm_region(
    "Cipher function part 4: from end of IV path to function cleanup",
    0x0015ac00, 0x0015ae00
)

# Look for the region right before the magic number check function
# The Cipher function should return, and then a caller invokes the magic check
disasm_region(
    "Pre-magic-check function caller",
    0x0015a0c0, 0x0015a170
)