![]() |
|
|
|
#1
|
||||
|
||||
|
Any links to software?
|
|
#2
|
|||
|
|||
|
THE TARGET
Main EXE, 31 MB, x64. Detect It Easy identifies it as Themida/WinLicense 3.x. Packed entry point is at RVA 0x528A058 (.boot section). There's a 15 MB .themida section with the VM interpreter, and about 647 calls/jumps from .text into .themida — a good chunk of code is virtualized. There's also a DLL (7 MB, Delphi x64, same Themida version) that handles license checking via CodeMeter. The license check chain: - EXE loads the DLL - DLL's DllMain loads WibuCm64.dll dynamically (LoadLibrary/GetProcAddress, not static imports) - DllMain spawns a Delphi background thread: OemRegUtils.TLicenseChecker.Execute - TLiceChecker calls CodeMeter APIs: CmGetRemoteContext2, CmCryptSimS, CmGetInfo, CmGetBoxesS - CmCryptSimS does hardware challenge-response against the dongle - Without the dongle's private key: DllMain returns FALSE, app shows error 6/902, exits --- WHAT I'VE TRIED ===== 1. WibuCm64.dll PROXY DLL ===== Replaced WibuCm64.dll with my own DLL that exports all 182 Cm* functions but returns fake data. Extracted the real export ordinals from System32\WibuCm64.dll. Key ordinals: ord_33: CmCryptSimS ord_44: CmGetRemoteContext2 ord_46: CmGetBoxContentsS ord_48: CmGetBoxes ord_49: CmGetBoxesS ord_50: CmGetInfo ord_51: CmGetInfoS ord_52: CmDecryptPioDataS Compiled with MinGW (-nostdlib, direct kernel32 calls to keep it small). Key interceptors: CmCryptSimS: return 0, zero-fill output buffer CmGetRemoteContext2: forward to real DLL, inject fake context if empty CmGetBoxesS: return 2 fake boxes CmGetInfo: return 0, zero-fill output Problem: MinGW exports functions alphabetically, so ord_50 became CmCryptSimS instead of CmGetInfo. Wrote a Python script (fix_ordinals.py) that parses the PE export directory and swaps address table entries + name ordinals to match the real DLL's ordinal layout. Result: Splash screen appears. But TLiceChecker's CmCryptSimS validation can't be satisfied without the dongle's private key — error 6 still fires. The proxy approach gets past the initial check but fails the background thread's deeper crypto validation. Also had to handle Themida's CRC integrity checks. Themida uses rep movsb/rep movsd for CRC — any runtime patch triggers a mismatch. Installed a VEH handler that catches access violations from these instructions and skips them: VehHandler: if exception address points to F3 A4 or F3 A5, skip the instruction This prevents Themida from detecting patches, but doesn't solve the core crypto problem. ===== 2. winspool.drv PROXY FOR OxyCheck64 ===== OxyCheck64.dll is another license-checking DLL. Replaced it by proxying winspool.drv (DLL proxy technique — rename real winspool, place our DLL). Our proxy has a DllMain that: - Installs CRC VEH bypass - Starts a polling thread (every 5ms for 5 seconds) - The thread finds CKEngine.dll and OxyCheck64.dll via GetModuleHandle - Patches their exports via VirtualProtect + memcpy Patches applied: InternalCheck -> mov eax, 1; ret CheckConnect -> xor eax, eax; ret CodemeterReconnect -> mov eax, 1; ret DllFunc11 -> mov eax, 1; ret DllFunc12 -> mov eax, 1; ret DllFunc13 -> xor eax, eax; ret SetOemCheckThreadMode -> ret (nop the thread spawner) Race condition: the polling thread races against Themida's unpacking. If we patch too early (before Themida finishes), CRC catches us. If too late, DllMain has already returned FALSE. Timing is unreliable. Tried improving this with LdrRegisterDllNotification callback (fires BEFORE DllMain of loaded DLLs), but even with pre-DllMain patches, TLiceChecker still fails CmCryptSimS validation. ===== 3. x64dbg MANUAL UNPACKING ===== Setup: x64dbg snapshot, ScyllaHide with "Themida x64" profile, sti exceptions passed through. OEP found at RVA 0x2A866C0 using the LCF-AT method (HW BP on last IAT write, then memory BP on .text access). Themida 3.x IAT patterns found: Pattern A (6 bytes): 90 E8 xx xx xx xx -> replaceable with FF 15 [IAT] Pattern B (6 bytes): E8 xx xx xx xx 90 -> replaceable with FF 15 [IAT] Pattern C (5 bytes): E8 xx xx xx xx -> NOT replaceable in-place. FF 15 needs 6 bytes. Scale of the problem: - 35 calls using Pattern A/B (patchable) - 877 calls using Pattern C (NOT patchable in-place) - 1242 total thunks (621 FF 25 + 621 push rdx/lea rdx pairs) - IAT at RVA 0x32EA320 Dumped with Scylla, fixed OEP, fixed import directory pointer, patched 35 calls. Dump doesn't start. Two issues: 1. ASLR: IAT entries contain resolved 0x7FFD addresses from dump session 2. 877 E8 calls still reference old thunk-backed IAT → broken on restart Scylla IAT autosearch finds nothing at OEP — expected for Themida. ===== 4. MAGICMIDA ===== Tried Magicmida (Themida/WinLicense unpacker). It produced a partial dump — the splash screen shows briefly, then the EXE crashes. The dump had: - 1 broken IAT entry: one kernel32 API that Magicmida couldn't resolve, stubbed with ExitProcess/Sleep placeholder - 647 VM references left unresolved: JMPs/CALLs from .text into .themida that Magicmida couldn't handle - The crash happens right after splash — likely the broken IAT entry or a VM integrity check Result: half-working dump, not useful for further analysis. The VM virtualization side wasn't addressed at all. ===== 5. BOBALKKAGI ===== Found on GitHub, uses Unicorn Engine to emulate Themida's entry point and rebuild imports. Had to modify it: - Replaced distorm3 with capstone (distorm3 needs MSVC build tools) - Downgraded setuptools<70 (pkg_resources removed in newer versions) Result: Finds OEP correctly (0x2A866C0), but unwrapping phase crashes with UC_ERR_FETCH_UNMAPPED. Unicorn follows a call into .themida and hits unmapped addresses. Also, bobalkkagi is EXE-only — doesn't support DLL targets. ===== 6. UNLICENSE (Frida-based dynamic unpacker) ===== Found https://github.com/ergrelet/unlicense — uses Frida to dynamically unpack Themida 2.x/3.x targets, handles x64 EXE and DLL. Downloaded the PyInstaller release (v0.4.0, 49MB). On the EXE (OxyDetective.exe): Successfully found OEP (0x2E866C0), resolved 883 imports, rebuilt IAT, produced unpacked_OxyDetective.exe (91.5 MB). Proper descriptor-based import table with 10 DLLs. The unpacked EXE ran for 10-15 seconds then crashed with "EJSONParseException" — got past Qt initialization to where it tries parsing config files. On the DLL (CKEngine.dll): Found OEP at 0x410D90, resolved 116 imports, produced unpacked_CKEngine.dll (18.1 MB). Exports were correctly patched by the runtime proxies during unlicense's run. But: Problems: 1. Stolen OEP: DllEntryPoint is Themida's trampoline (lea rcx, [rcx+0x38]; jmp stub), not real DllMain 2. IAT placed at wrong RVA (0x814000): overwrites initialization data 3. VM code untouched: .themida section (8.6 MB) still present, VM integrity checks detect modified binary 4. Only 7 DLLs in import table: missing many needed APIs (unlicense couldn't resolve Themida-encrypted entries) Result: error 902 on load --- WHERE I'M STUCK 1. IAT at scale: 877 5-byte E8 calls can't be converted in-place. The themida-rebuilder trampoline approach is the right solution but I need to complete the pipeline. 2. VM integrity: Even with imports fixed, Themida's VM code still checksums the binary. The unpacked binary from unlicense ran for 10-15 seconds before crashing — further than anything else — but VM integrity kills it. 3. Timing the patches: Patching CKEngine exports before TLiceChecker spawns. DLL load notification (LdrRegisterDllNotification) helps but the crypto validation in CmCryptSimS remains the root issue. 4. Full unpack vs. live patching: Is it even realistic to produce a clean unpacked PE for this target? Or should I focus on runtime instrumentation (hook GetProcAddress/LdrLoadDll at load time)? --- WHAT I'D LOVE HELP WITH 1. Has anyone successfully used the module-list format to generate a correct module map? Any common mistakes that cause unresolved IAT entries? 2. For the VM integrity problem — if Themida's "VM integrity checks" option is enabled, does any unpacked binary survive? Or is the only path to keep the VM runtime intact and patch around it? 3. Any other tools or techniques for Themida 3.x x64 that I've missed? I've tried: Magicmida, manual Scylla dump, bobalkkagi, unlicense, proxy DLLs, and runtime patching. Key addresses (main EXE - OxyDetective.exe): Packed EP: RVA 0x528A058 OEP: RVA 0x2A866C0 .themida section: RVA 0x434E000, ~15.6 MB VM cross-references: ~647 Pattern C calls (5-byte): 877 IAT: RVA 0x32EA320 Import descriptors: RVA 0x4326390 Key addresses (license DLL - CKEngine.dll): Packed: 7 MB, same Themida 3.x version OEP (from unlicense): RVA 0x10D90 (stolen — trampoline to real init) IAT (from unlicense): RVA 0x814000 Exports: 26 (InternalCheck, CheckConnect, CodemeterReconnect, DllFunc1-13, etc.) Thanks for reading. Any pointers appreciated. Download: hxxps://mega.nz/file/KXwxhJhZ#TzoBW1F8Jbfbm9BQk_gsGAn1gbaE7a-dADQkDv3tLWQ |
![]() |
| Thread Tools | |
| Display Modes | |
|
|