Мертвое изображение (Dead Image)
Мертвое изображение (Dead Image)
Platform: hackerlab | Category: Forensics | Type: Challenge | Difficulty: Medium | OS: Linux | Author: XESXOR | Date: 2026-01-05 | Status: Solved Techniques: BMP file header manual reconstruction, Image dimension calculation via factorization, Raw pixel data analysis, RGBA pixel format identification
Summary
Task: Recover an image from raw pixel data after BMP header was lost during PNG to BMP conversion. Solution: Calculate image dimensions via factorization of pixel count, manually reconstruct BMP file header and BITMAPINFOHEADER.
Мертвое изображение (Dead Image) — Hackerlab
Description
Во время конвертации картинки из PNG в BMP затерлась вся структура файла. Поможешь восстановить?
File: task.zip → task
Analysis
1. Initial File Analysis
bash read-only • copy allowed$ file task task: ISO-8859 text, with very long lines (65536), with no line terminators $ ls -la task -rw-r--r-- 1 user staff 95528572 Jan 5 12:00 task
The file is detected as text, not an image — the header is missing.
2. Hex Analysis
bash read-only • copy allowed$ xxd task | head -20 00000000: ffff ffff ffff ffff ffff ffff ffff ffff ................ 00000010: ffff ffff ffff ffff ffff ffff ffff ffff ................ ...
The file starts with 0xFF bytes — these are white pixels in RGBA format (255, 255, 255, 255).
3. Searching for Image Data
bash read-only • copy allowed$ xxd task | grep -v "ffff ffff ffff ffff ffff ffff ffff ffff" | head -20
Non-white bytes were found in the middle of the file — this is the actual image content.
4. Determining Pixel Format
The task description mentions PNG → BMP conversion. PNG uses RGBA (4 bytes per pixel), BMP can store 32-bit data (also 4 bytes).
Verification:
- File size: 95,528,572 bytes
- Bytes per pixel: 4 (RGBA)
- Total pixels: 95,528,572 / 4 = 23,882,143
5. Calculating Image Dimensions
Need to factorize the pixel count to determine width × height:
python read-only • copy allowedimport sympy n = 23882143 factors = sympy.factorint(n) # {5987: 1, 3989: 1} # 23,882,143 = 5987 × 3989
Image dimensions: 5987 × 3989 pixels
Solution
Step 1: Creating BMP Header
A BMP file consists of:
- BMP File Header (14 bytes)
- BITMAPINFOHEADER (40 bytes)
- Pixel Data (raw data)
python read-only • copy allowed#!/usr/bin/env python3 import struct # Image parameters width = 5987 height = 3989 bits_per_pixel = 32 # RGBA pixel_data_size = width * height * 4 # BMP File Header (14 bytes) bmp_signature = b'BM' file_size = 14 + 40 + pixel_data_size # header + DIB header + pixels reserved = 0 pixel_data_offset = 14 + 40 # after both headers bmp_file_header = struct.pack('<2sIHHI', bmp_signature, # 'BM' file_size, # file size reserved, # reserved1 reserved, # reserved2 pixel_data_offset # offset to pixel data ) # BITMAPINFOHEADER (40 bytes) dib_header_size = 40 color_planes = 1 compression = 0 # BI_RGB (no compression) image_size = 0 # can be 0 for BI_RGB x_pixels_per_meter = 2835 # 72 DPI y_pixels_per_meter = 2835 colors_used = 0 important_colors = 0 bitmapinfoheader = struct.pack('<IiiHHIIiiII', dib_header_size, width, height, # positive = bottom-up color_planes, bits_per_pixel, compression, image_size, x_pixels_per_meter, y_pixels_per_meter, colors_used, important_colors ) # Read raw pixel data with open('task', 'rb') as f: pixel_data = f.read() # Assemble BMP file with open('restored.bmp', 'wb') as f: f.write(bmp_file_header) f.write(bitmapinfoheader) f.write(pixel_data) print(f"Created restored.bmp ({file_size} bytes)")
Step 2: Convert to PNG (optional)
bash read-only • copy allowedconvert restored.bmp restored.png
Step 3: View the Image
Open restored.bmp or restored.png — the flag is visible in the image.
Theory
BMP File Structure
+------------------+
| BMP File Header | 14 bytes
+------------------+
| DIB Header | 40+ bytes (BITMAPINFOHEADER)
+------------------+
| Color Table | optional (for indexed color)
+------------------+
| Pixel Data | raw pixels
+------------------+
BMP File Header (14 bytes)
| Offset | Size | Description |
|---|---|---|
| 0x00 | 2 | Signature ('BM') |
| 0x02 | 4 | File size |
| 0x06 | 2 | Reserved |
| 0x08 | 2 | Reserved |
| 0x0A | 4 | Pixel data offset |
BITMAPINFOHEADER (40 bytes)
| Offset | Size | Description |
|---|---|---|
| 0x0E | 4 | Header size (40) |
| 0x12 | 4 | Width (signed) |
| 0x16 | 4 | Height (signed, negative = top-down) |
| 0x1A | 2 | Color planes (1) |
| 0x1C | 2 | Bits per pixel (1/4/8/16/24/32) |
| 0x1E | 4 | Compression (0 = BI_RGB) |
| 0x22 | 4 | Image size |
| 0x26 | 4 | X pixels per meter |
| 0x2A | 4 | Y pixels per meter |
| 0x2E | 4 | Colors used |
| 0x32 | 4 | Important colors |
| |
Pixel Formats
| BPP | Format | Bytes/pixel |
|---|---|---|
| 24 | BGR | 3 |
| 32 | BGRA | 4 |
Important: BMP stores pixels in BGR(A) order, not RGB(A)!
Calculating Image Dimensions
With known file size and pixel format:
python read-only • copy allowedtotal_pixels = file_size // bytes_per_pixel # Factorization to find width × height
For CTF tasks, "nice" dimensions or simple factors are typically used.
Full Solution Script
python read-only • copy allowed#!/usr/bin/env python3 """ Dead Image - BMP Header Reconstruction """ import struct import sympy def factorize_pixels(n): """Find image dimensions from pixel count""" factors = sympy.factorint(n) # For two prime factors, they are width and height primes = list(factors.keys()) if len(primes) == 2: return max(primes), min(primes) # width, height return None, None def create_bmp_header(width, height, bpp=32): """Create BMP file header + BITMAPINFOHEADER""" pixel_data_size = width * height * (bpp // 8) file_size = 14 + 40 + pixel_data_size # BMP File Header bmp_header = struct.pack('<2sIHHI', b'BM', file_size, 0, 0, 54) # BITMAPINFOHEADER dib_header = struct.pack('<IiiHHIIiiII', 40, width, height, 1, bpp, 0, 0, 2835, 2835, 0, 0) return bmp_header + dib_header def main(): # Read raw pixel data with open('task', 'rb') as f: raw_data = f.read() # Calculate dimensions bytes_per_pixel = 4 # RGBA total_pixels = len(raw_data) // bytes_per_pixel width, height = factorize_pixels(total_pixels) print(f"File size: {len(raw_data)} bytes") print(f"Total pixels: {total_pixels}") print(f"Dimensions: {width} x {height}") # Create BMP header = create_bmp_header(width, height) with open('restored.bmp', 'wb') as f: f.write(header) f.write(raw_data) print("Created restored.bmp") if __name__ == '__main__': main()
Alternative Approaches
Using PIL/Pillow
python read-only • copy allowedfrom PIL import Image width, height = 5987, 3989 with open('task', 'rb') as f: raw_data = f.read() # Create image from raw RGBA data img = Image.frombytes('RGBA', (width, height), raw_data) img.save('restored.png')
Using ImageMagick
bash read-only • copy allowed# If dimensions are known convert -size 5987x3989 -depth 8 rgba:task restored.png
Related Techniques
- PNG header reconstruction — similar task for PNG
- JPEG header recovery — recovering JPEG from raw data
- File carving — extracting files from memory/disk dumps
- Steganography — hidden data in images