← Back to Writeups
CUSTOMEasyReversing

patch2

XESXOR9/10/20263 min read
#reversing#custom#easy#little_endian_dword_reconstruc#peb_beingdebugged_antidebug#rol_inverse#ror_xor_transform_reversal#static_disassembly#windows

patch2

Platform: sbpctf | Category: Reversing | Type: Challenge | Difficulty: Easy | OS: Windows | Author: XESXOR | Date: 2026-06-25 | Status: Solved Techniques: little_endian_dword_reconstruction, peb_beingdebugged_antidebug, rol_inverse, ror_xor_transform_reversal, static_disassembly

Summary

Task: tiny ~3KB PE32 console crackme that reads 4 bytes from stdin, applies ror+xor and compares to a constant, with a PEB.BeingDebugged anti-debug tweak to the rotate amount. Solution: reverse the transform statically (cl=14 when not debugged), invert ror with rol and xor to recover the little-endian dword input 'REDACTED'.

patch2 — sbpctf

Description

rev-kids20.forkbomb.ru — task2.exe (PE32 console crackme). Find the input that makes the program print "OK".

task2.exe is a ~3KB PE32 (Intel 80386, MS Windows) console crackme written in C. It imports only GetStdHandle, ReadFile, and WriteFile from kernel32.dll. It reads bytes from stdin, transforms the first 4 bytes, and prints "OK" or "NO". The goal is to find the input that yields "OK".

Analysis

The .rdata segment holds the adjacent 2-byte messages "OK" (at 0x401020) and "NO" (at 0x401022), forming the string OKNO. ‍​‌‌​​​​‌​‌‌​​‌‌​​‌‌​​‌‌​​‌‌​​‌​​​​‌‌​​​​​‌‌​​‌‌​​‌‌​​‌​‌​‌‌​​‌​‌‍

Transform function at 0x401028

asm
401028: push ebp
401029: mov ebp, esp
40102b: push ecx
40102c: mov eax, fs:[0x18]      ; TEB
401032: mov eax, [eax+0x30]     ; PEB
401035: movzx ecx, byte [eax+2] ; PEB.BeingDebugged
401039: add ecx, 0x0e           ; cl = BeingDebugged + 14
40103c: mov eax, [ebp+8]        ; input dword (first 4 bytes)
40103f: ror eax, cl
401041: xor eax, 0x64198234
401046: pop ecx
401047: pop ebp
401048: ret

Main routine at 0x401049

- GetStdHandle(-11) -> STD_OUTPUT_HANDLE @ 0x403000
- GetStdHandle(-10) -> STD_INPUT_HANDLE  @ 0x403004
- ReadFile(hStdIn, buf=ebp-0x64, 0x64, &read, 0)
- eax = *(dword*)buf        ; first 4 input bytes
- call 0x401028            ; transform
- cmp eax, [0x401024]      ; [0x401024] = 0xf1d833fd
- jne fail
    success: WriteFile(hStdOut, 0x401020 "OK", 2, ...)
    fail:    WriteFile(hStdOut, 0x401022 "NO", 2, ...)

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

The compared constant 0xf1d833fd is read from 0x401024, which overlaps the byte stream fd 33 d8 f1 lying inside the code region just before the function — a reuse-of-bytes trick typical of tiny crackmes.

Anti-debug twist (the "patch" theme)

The rotate count is cl = PEB.BeingDebugged + 14. When not under a debugger, BeingDebugged = 0, so cl = 14. Under a debugger the rotation amount changes (cl = 15), so the required input would differ — a small anti-debug detail that breaks naive dynamic solving. Solving statically with cl = 14 gives the correct answer. ‍​‌‌​​​​‌​‌‌​​‌‌​​‌‌​​‌‌​​‌‌​​‌​​​​‌‌​​​​​‌‌​​‌‌​​‌‌​​‌​‌​‌‌​​‌​‌‍

Solution

We need: ror(input, 14) ^ 0x64198234 == 0xf1d833fd

ror(input, 14) = 0xf1d833fd ^ 0x64198234 = 0x95c1b1c9
input = rol(0x95c1b1c9, 14) = 0x6c726570

Little-endian dword 0x6c726570 = bytes 70 65 72 6c = b'REDACTED' (p=0x70, e=0x65, r=0x72, l=0x6c).

python
#!/usr/bin/env python3
import struct

def ror(x, n): n %= 32; return ((x >> n) | (x << (32 - n))) & 0xffffffff
def rol(x, n): n %= 32; return ((x << n) | (x >> (32 - n))) & 0xffffffff

TARGET = 0xf1d833fd
XORK   = 0x64198234

# Reverse: undo xor, then undo ror (cl = BeingDebugged(0) + 14 = 14)
rotated = TARGET ^ XORK          # = 0x95c1b1c9
inp_dword = rol(rotated, 14)     # = 0x6c726570
answer = struct.pack('<I', inp_dword)
print(answer)                    # b'REDACTED'
‍​‌‌​​​​‌​‌‌​​‌‌​​‌‌​​‌‌​​‌‌​​‌​​​​‌‌​​​​​‌‌​​‌‌​​‌‌​​‌​‌​‌‌​​‌​‌‍

# Verify forward
assert (ror(struct.unpack('<I', answer)[0], 14) ^ XORK) == TARGET

Feeding the 4-byte input REDACTED to the program on stdin makes it print "OK". This is a kids-series crackme with no FLAG{...} wrapper — the accepted answer string is REDACTED. ‍​‌‌​​​​‌​‌‌​​‌‌​​‌‌​​‌‌​​‌‌​​‌​​​​‌‌​​​​​‌‌​​‌‌​​‌‌​​‌​‌​‌‌​​‌​‌‍