← Back to Writeups
CUSTOMEasyReversing

Deadbeef

XESXOR9/10/20263 min read
#reversing#custom#easy#exhaustive_32bit_bruteforce#iat_thunk_following#little_endian_dword_to_ascii#magic_constant_compare#win32_io_pattern_recognition#linux

Deadbeef

Platform: rev_kids20 | Category: Reversing | Type: Challenge | Difficulty: Easy | OS: Linux | Author: XESXOR | Date: 2026-06-15 | Status: Solved Techniques: exhaustive_32bit_bruteforce, iat_thunk_following, little_endian_dword_to_ascii, magic_constant_compare, win32_io_pattern_recognition

Summary

Task: tiny hand-written MASM PE32 console binary reads input and prints OK/NO based on a ror/rol/xor transform of the first dword compared to a magic constant. Solution: follow IAT thunks to recover the Win32 I/O pattern, extract the transform and compare constant 0x395547a2, then brute-force all 2^32 inputs in C since X is used on both sides; printable solution 0x49684179 = little-endian 'REDACTED'.

Deadbeef — rev_kids20

Description

This small but ugly binary ... Flag is the input that makes app say "OK".

A tiny PE32 console executable deadbeef.exe (Intel 80386, hand-written MASM, no symbols). It reads input from stdin and prints either OK or NO. The flag is the exact input that makes the program print OK. The flag is submitted via a POST flag parameter to the platform (rev-kids20.forkbomb.ru).

Analysis

The binary is hand-written MASM with no symbol info, so the disassembly is read by following the IAT thunks. Image base is 0x401000. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Imports (via IAT thunks)

Three kernel32 functions are reached through jump thunks:

ThunkFunction
jmp [0x402008]GetStdHandle
jmp [0x402000]ReadFile
jmp [0x402004]WriteFile

Data

At 0x401020 there are the output strings and the magic constant:

0x401020: "OK"          ; [0x401020] = 'O','K'
0x401022: "NO"          ; [0x401022] = 'N','O'
0x401024: dd 0x395547a2 ; compare constant (bytes: a2 47 55 39)

‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

main

GetStdHandle(-0xb)  -> stdout, saved @ 0x403000   ; STD_OUTPUT_HANDLE
GetStdHandle(-0xa)  -> stdin,  saved @ 0x403004   ; STD_INPUT_HANDLE
ReadFile(stdin, buf=[ebp-0x64], 0x64, ...)        ; read user input into stack buffer
call transform @ 0x401029                         ; operates on FIRST dword of buf
cmp [ebp-0x64], [0x401024]                         ; transformed first dword == 0x395547a2 ?
  equal -> WriteFile("OK")
  else  -> WriteFile("NO")

transform @ 0x401029

Operates on the first DWORD X of the input buffer: ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

eax = 0xcafebabe
eax ^= X
eax = ror(eax, 7)
eax ^= 0xdeadbeef
eax = rol(eax, 0x15)
eax ^= 0x13373389
X   = X ^ eax            ; result written back to buf[0]

So the program prints OK iff a 4-byte input X satisfies:

X ^ f(X) == 0x395547a2
where f(X) = ((ror(0xcafebabe ^ X, 7) ^ 0xdeadbeef) rol 0x15) ^ 0x13373389

Because X appears on both sides of the equation, the relation is not trivially invertible. With only a 32-bit search space, exhaustive brute force in C finishes in a few seconds. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Solution

Brute-force every 32-bit X:

c
#include <stdint.h>
#include <stdio.h>

int main(void) {
    uint32_t T = 0x395547a2;
    for (uint64_t x = 0; x < 0x100000000ULL; x++) {
        uint32_t X = (uint32_t)x;
        uint32_t e = 0xcafebabe ^ X;
        e = (e >> 7) | (e << 25);    // ror 7
        e ^= 0xdeadbeef;
        e = (e << 0x15) | (e >> 11); // rol 0x15
        e ^= 0x13373389;
        if ((X ^ e) == T)
            printf("%08x\n", X);
    }
    return 0;
}

Compile and run:

bash
gcc -O2 db.c -o db && ./db

‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Four solutions:

0x1c3d142c
0x49684179   <- printable
0xb697be86
0xe3c2ebd3

The printable one is X = 0x49684179. As a little-endian dword the bytes are 0x79 0x41 0x68 0x49 decodes to REDACTED.

Verification in Python:

python
def f(X):
    e = (0xcafebabe ^ X) & 0xffffffff
    e = ((e >> 7) | (e << 25)) & 0xffffffff     # ror 7
    e ^= 0xdeadbeef
    e = ((e << 0x15) | (e >> 11)) & 0xffffffff  # rol 0x15
    e ^= 0x13373389
    return (X ^ e) & 0xffffffff

X = int.from_bytes(bytes.fromhex("79 41 68 49"), "little")  # 0x49684179
assert f(X) == 0x395547a2

‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Submitting REDACTED as the input makes the binary print OK. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍