Reverse Engineering Clean Architecture

A deep dive into reverse engineering CardioSmart, a proprietary Holter monitor application with hardware dongle protection, revealing its clean architecture and bypassing license checks through targeted binary patching.

I have a Holter monitor at home. Not a professional medical-grade one, but an older CardioSmart device that records ECG data onto compact flash cards. The problem? The proprietary software to read the .xcm files requires a hardware USB dongle that I don't have. The dongle costs more than the device itself on the secondary market. So naturally, I decided to reverse engineer the software instead.

The Goal

The objective was simple: open and read .xcm cardiogram files without the hardware dongle. CardioSmart v7.5.4.0 is a C++ MFC application that stores Holter monitoring data in a proprietary compressed format. Without a valid license (verified by the dongle), the program refuses to start, let alone import or display any data.

First Look: The License Dialog

When you launch CardioSmart without the dongle, you're greeted by a license dialog demanding a serial number. No serial, no entry. Time to look under the hood.

CardioSmart license dialog

Tooling Up

The primary tools for this investigation were:

  • IDA Pro — for static disassembly and analysis of the executable and its DLLs
  • ProcessMonitor — for tracing file system and registry access at runtime
  • A hex editor — for applying binary patches

Architecture Discovery: Clean Architecture in the Wild

One of the first surprises was the application's architecture. CardioSmart is remarkably well-structured for a niche medical application from the early 2000s. It follows what we'd now call "clean architecture" principles:

  • Modular DLL separation — each functional area lives in its own library
  • Disguised module files — some DLLs are renamed with .cmm extensions, but they're standard PE binaries
  • Clear layering — MfcEx.dll handles UI extensions, AppsBase.dll manages application lifecycle, ModulesBase.dll provides the plugin framework

The licensing logic lives in its own module, cleanly separated from the business logic. Ironically, this clean separation made it easier to identify and patch the protection — each concern was isolated in a predictable location.

Bypass #1: The License Validity Check

The first barrier was ChlpLicenseManager::IsLicenseValid(), located at address 0x106183F0 in the licensing DLL. This function checks the dongle status and returns a boolean. The key instruction was at address 0x10618683:

; Original
mov al, bl    ; al = result from dongle check

; Patched
mov al, 1     ; al = always true

One byte change. The function now always reports a valid license, regardless of whether the dongle is present.

Bypass #2: The Demo Mode Restriction

With the license check bypassed, the program launched — but in a restricted demo mode. Import functionality was disabled. The culprit was CdtCardioMg::AssertForExample() at 0x00CC8A40, which checked whether the application was running in example/demo mode and blocked certain operations.

; Original (at 0x00CC8A66)
xor al, al    ; al = 0 (demo mode)

; Patched
mov al, 1     ; al = 1 (full mode)

Another single-byte patch. Now the import menu items became active.

Bypass #3: Component License Level

Even with import working, attempting to actually open an imported document triggered yet another check. ChlpLicenseManager::GetComponentLicenseLevel() at 0x10617AE0 verified license levels for individual components. The conditional branch at 0x10617B11 was the gatekeeper:

; Original
jnz short loc_10617B1F  ; jump if licensed

; Patched
jz short loc_10617B1F   ; jump if NOT licensed (inverted)

By inverting the conditional jump from jnz to jz, the function now grants access when it would normally deny it, and vice versa. Since only unlicensed access was being attempted, this effectively unlocked everything.

IDA Pro disassembly view

The File Format: .xcm Under the Hood

With all three patches applied, the software ran fully unlocked. Now I could examine how it actually processes .xcm files. The import pipeline works as follows:

  • CappMain::ImportExam — entry point for the import operation
  • ChlpImportExam::Read — deserializes the file using MFC's CArchive streaming
  • CarcDiskHeaderAbs::Read and CarcDiskHeaderFD — parse the file header structure
  • Data is written to a MySQL database through CsetBase-derived classes

The .xcm format is essentially a serialized CArchive container — MFC's built-in object persistence mechanism. The file contains a header with patient metadata, followed by compressed ECG signal data organized in blocks.

Verification Through ProcessMonitor

To confirm the patches didn't break anything, I traced the application with ProcessMonitor. The file access patterns showed clean sequential reads of the .xcm file, followed by writes to the MySQL database — exactly the expected behavior for a successful import. No crashes, no error dialogs, no partial imports.

ProcessMonitor trace

Lessons Learned

Several takeaways from this exercise:

  • Clean architecture is a double-edged sword for protection. The well-organized module structure made it trivially easy to locate the licensing logic. In a monolithic spaghetti codebase, the same checks might have been scattered across dozens of files, requiring far more effort to find and patch.
  • Three bytes was all it took. Three single-byte patches across two binaries — that's the entire delta between "completely locked" and "fully functional." Hardware dongles add inconvenience but not meaningful security against a determined reverse engineer.
  • MFC's CArchive is remarkably transparent. Once you recognize the serialization pattern, the entire file format becomes readable. The class names are embedded in the binary, the vtable layout is predictable, and the archive structure is well-documented in Microsoft's own references.
  • Medical software shouldn't rely on obscurity. If the data format were open, none of this reverse engineering would have been necessary. Patients should be able to access their own medical data without jumping through hoops.

Conclusion

What started as a practical problem — reading my own ECG data — turned into an interesting case study in software architecture. The irony of the title is intentional: the "clean architecture" that made CardioSmart a well-engineered application is the same clean architecture that made it straightforward to reverse engineer. Every principle that makes code maintainable — separation of concerns, clear interfaces, modular design — also makes it easier to understand, analyze, and modify from the outside.

The .xcm files are now readable. The cardiograms are accessible. And I have a new appreciation for why some software architects deliberately introduce coupling as a defensive measure — even though it goes against everything we teach about good design.