DEV Community

Cover image for How I Reverse-Engineered a Radiology Portal and Built an Open-Source MRI Analyzer
Liohtml
Liohtml

Posted on

How I Reverse-Engineered a Radiology Portal and Built an Open-Source MRI Analyzer

The Problem

Two weeks ago, I got a knee MRI after a Brazilian Jiu-Jitsu injury (a "knee reaping" - massive valgus stress + external rotation). My doctor sent me a link to view my images on an easyRadiology portal.

I clicked the link. Login screen. Entered my code. The viewer loaded. My knee MRI appeared on screen.

But here's the thing - I wanted to download my own images and analyze them. The portal had a download button, but the files were completely encrypted. I couldn't open them in any DICOM viewer.

So I did what any engineer would do: I opened DevTools.

Reverse-Engineering the Encryption

The portal uses a multi-layer encryption scheme:

  1. Access code (K6P-8ZT-M9X-9JE) is split into a ViewCodeName and a password
  2. A scrypt key verification proves you know the password without sending it
  3. The server returns an encrypted access key (AES-CBC with scrypt-derived key)
  4. This access key decrypts the patient data JSON (which contains yet another password)
  5. That final password decrypts the actual DICOM image data (AES-256, WinZip format)
Access Code -> scrypt -> KeyVerification -> API -> Encrypted AccessKey
                                                        |
                                                   scrypt + AES-CBC
                                                        |
                                                   AccessKey (plain)
                                                        |
                                            Decrypt PatientData JSON
                                                        |
                                            PasswordForDicomZip
                                                        |
                                            AES-256 decrypt DICOM entries
Enter fullscreen mode Exit fullscreen mode

I traced every network request in Playwright, read the minified JavaScript, and implemented the full decryption chain in Python:

# Derive key using scrypt (matching the portal's JS implementation)
key = hashlib.scrypt(password.encode('utf-8'), salt=salt, n=16384, r=8, p=1, dklen=32)

# Decrypt with AES-CBC
cipher = AES.new(key, AES.MODE_CBC, iv)
decrypted = cipher.decrypt(ciphertext)

# Result: {"PasswordForDicomZip": "rqNRwRnB-aU6H4fM3-..."}
Enter fullscreen mode Exit fullscreen mode

140 DICOM files decrypted. My knee images, finally accessible.

From Decryption to AI Analysis

With the images in hand, I thought: what if I could analyze them automatically?

Step 1: Local ML Analysis

I used a pretrained ResNet18 (ImageNet weights) as a feature extractor. For each MRI slice:

  1. Extract a 512-dimensional feature vector
  2. Compute anomaly scores (distance from series mean)
  3. Analyze signal intensity patterns (high signal on PD FS = fluid/edema)
  4. Identify the top suspicious slices
# Feature extraction per slice
features = resnet18_features(volume)  # (28, 512)
anomaly_scores = normalized_distance_from_mean(features)  # (28,)
top_slices = argsort(anomaly_scores)[-5:]  # Most suspicious
Enter fullscreen mode Exit fullscreen mode

Step 2: Vision-LLM Analysis

Then I fed the key slices to Claude Opus 4.7 with a structured medical prompt including my clinical context:

"Knee Reaping in BJJ 10 days ago. Valgus stress + external rotation. Audible pop. Point tenderness at tibial MCL insertion."

Claude analyzed each structure systematically - ACL, MCL, meniscus, bone bruise, cartilage, effusion - and returned structured JSON findings.

Result: MCL Grade I-II at the tibial insertion (matching my point tenderness exactly), intact ACL, no bone bruise, mild effusion. The clinical correlation noted that the "pop" was unusual for an isolated MCL injury.

Step 3: Professional PDF Report

I generated a 6-page PDF with:

  • Annotated MRI images (arrows, circles, color-coded findings)
  • Per-structure findings tables
  • Traffic-light summary (normal/borderline/pathological)
  • Clinical correlation and recommendations

Making It Open-Source: MedCheck

I realized this could help others. So I packaged everything into MedCheck - an open-source toolkit that anyone can use.

Architecture

Ingest -> Preprocess -> ML Analysis -> Vision AI -> Report
  |           |             |              |           |
  v           v             v              v           v
DICOM      Normalize    ResNet18      Claude/GPT    PDF/HTML
Portal     Detect       Anomaly       GPT-5.5      Annotated
Local      anatomy      scores        Gemini       images
Enter fullscreen mode Exit fullscreen mode

Key Features

  • Multiple data sources: Local DICOM files, ZIP archives, or fetch directly from radiology portals
  • Local ML: ResNet18 anomaly detection - no API key needed
  • Vision-LLMs: Claude Opus 4.7, GPT-5.5, Gemini 3.5 Flash with automatic fallback
  • Clinical context: Input your symptoms and trauma history for targeted analysis
  • Professional reports: PDF with annotated images, HTML for interactive viewing, JSON for APIs
  • Docker-ready: docker run and open your browser
  • YAML workflows: Define custom analysis pipelines

Quick Start

# Install
pip install medcheck

# Analyze local DICOM files
medcheck analyze ./my-dicom-folder \
  --symptoms "Medial knee pain" \
  --trauma "Valgus stress injury" \
  --model claude \
  --report pdf

# Or use Docker
docker run -p 8080:8080 \
  -e ANTHROPIC_API_KEY=sk-... \
  ghcr.io/liohtml/medcheck:lite
Enter fullscreen mode Exit fullscreen mode

Supported Anatomy

Region Structures Analyzed
Knee ACL, PCL, MCL, LCL, menisci, cartilage, bone bruise, effusion
Shoulder Rotator cuff, labrum, biceps tendon, AC joint
Spine Discs, stenosis, foramina, vertebral bodies, ligaments
More coming Hip (#10), Ankle (#11), Wrist (#12)

Want to Contribute?

MedCheck has 9 open issues labeled good first issue - perfect for first-time contributors:

  • Add anatomy templates (hip, ankle, wrist) - if you know anatomy, you can help
  • New data providers (Orthanc, DICOMweb) - if you work with DICOM servers
  • Translations (French, Spanish) - if you speak these languages
  • Test coverage - always welcome
  • Local LLM (LLaVA-Med) - for fully offline analysis
# Get started in 30 seconds
git clone https://github.com/Liohtml/MedCheck.git
cd MedCheck
uv sync --all-extras
uv run pytest  # 65 tests, all passing
Enter fullscreen mode Exit fullscreen mode

Important Disclaimer

MedCheck is NOT a medical device. It's a research and educational tool. All analysis results must be verified by a qualified radiologist. Don't make medical decisions based solely on MedCheck output.

Links


Have you worked with medical imaging or DICOM data? I'd love to hear about your experiences in the comments.

Top comments (0)