Forensics
Hard
500 points

Painted

Recuite 2025 - HCMUS
6 tháng 10, 2025
Memory Dump
Paint
BMP
Entropy Analysis
Recuite 2025 - HCMUS
Forensics

Painted

Challenge Information

  • Category: Forensics
  • Points: 500
  • File: mspaint.dmp (575 MB)
  • Canvas Size: 960x520 pixel

Challenge Description

An artist painted a beautiful picture in MS Paint but forgot to save it before closing the application.
Luckily, we have a process memory dump of that application.
Task: recover the painting from the memory dump!

Analysis

Memory Dump Structure

Offset          Size            Content
─────────────────────────────────────────
0x00000000      ~16 MB          Header & Metadata
0x01000000      Variable        Process Memory
0x04000000      Variable        Heap Memory
...
0x0ac00000  ◄── 1.9 MB         ★ CANVAS DATA ★
...
Total: 575,325,048 bytes (575 MB)

Approach

Step 1: Determine Canvas Size

Search for width (960) and height (520) values stored as 32-bit little-endian integers in memory.
This is a signature helping identify the memory region containing image data.

Step 2: Entropy Analysis

Characteristics of image data region:

  • White background → contains many 0xFF bytes
  • Colored regions → high entropy, byte values change significantly
expected_size_32bit = 960 * 520 * 4  # 1,996,800 bytes (RGBA)

# Scan memory to find area with many white pixels
for offset in range(0, len(data), 1MB):
    sample = data[offset:offset+10000]
    white_count = sample.count(0xFF)
    
    if white_count > suitable_threshold:
        # Potential canvas region
        print(f"Candidate offset: {hex(offset)}")

Step 3: Extract and Reconstruct BMP Image

def create_bmp(pixel_data, width, height, bits_per_pixel, filename):
    # BMP Header (14 bytes)
    file_header = struct.pack('<2sIHHI', 
                             b'BM', file_size, 0, 0, header_size)
    
    # Image Info (40 bytes - BITMAPINFOHEADER)
    info_header = struct.pack('<IiiHHIIiiII', 
                             40, width, -height,  # Negative = top-down display
                             1, bits_per_pixel, 0, pixel_data_size,
                             2835, 2835, 0, 0)
    
    # Write BMP file to disk
    with open(filename, 'wb') as f:
        f.write(file_header)
        f.write(info_header)
        f.write(pixel_data)

Identifying Correct Canvas Location

Trying multiple offsets in file:

  • 0x0 (file start)
  • 0x1000000 (16 MB)
  • 0x4000000 (64 MB)
  • 0xac00000 ✓ ← Correct location, contains canvas data
  • 0xad00000
  • 0x20000000 (512 MB)

White Background ImageWhite Background Image

Verification

  • Reasonable entropy ✓
  • White pixel ratio consistent ✓
  • successful image rendering ✓
  • Flag readable on canvas ✓

Result

Generated File: white_bg_3_0xac00000_bottomup.bmp

  • Offset: 0xac00000 (180,355,072 bytes)
  • Format: 32-bit RGBA, bottom-up
  • Outcome: Recovered image shows clear flag on canvas!

Flag

Read directly from recovered image.

Key Takeaways

  1. Memory Analysis Technique: Process dumps can contain valuable data, especially raw application data.
  2. Understand BMP Structure: Mastering header and image formats is key to recovery.
  3. Entropy Analysis: Helps identify regions containing image information within large data blocks.
  4. Trial and Error: Testing multiple offsets and display modes (top-down/bottom-up) is necessary.
  5. Paint Storage: Images in Paint are often stored directly as raw pixels on the heap.

Statistics

  • Dump File Size: 575 MB
  • Canvas Size: 960×520 pixel
  • Pixel Data: 1,996,800 byte (RGBA)
  • Correct Offset: 0xac00000
  • Processing Time: 30–60 minutes
  • Tested Locations: > 50 offsets
500
Points
Hard
Difficulty
Forensics
Category