WinAntiDbg0x100 picoCTF 2024 Solution

Published: April 3, 2024

Description

A Windows console binary detects debuggers via IsDebuggerPresent and refuses to show the flag if a debugger is attached. By intercepting the anti-debug check in x64dbg and skipping the alert path, you can read the embedded flag string.

This is a gentle intro to anti-debug bypass: find the check, break on it, and set EIP past the failure branch so execution continues as if no debugger was detected.

Windows & x64dbg

Download WinAntiDbg0x100.zip (password: picoctf) and extract the executable.

Open WinAntiDbg0x100.exe inside x64dbg (32-bit).

bash
wget https://artifacts.picoctf.net/c_titan/84/WinAntiDbg0x100.zip && \
unzip WinAntiDbg0x100.zip
First challenge in the WinAntiDbg series; WinAntiDbg0x200 and 0x300 introduce progressively more advanced anti-debug techniques. If x64dbg is new to you, the Ghidra reverse engineering guide gives a useful static-analysis companion view.
  1. Step 1Load in Ghidra to understand the check
    Open the binary in Ghidra (NSA's free reverse engineering suite). Ghidra shows that the program calls IsDebuggerPresent, then tests the return value: if it is zero (no debugger), execution continues to the flag. The TEST instruction address in the canonical build ends in digits like 1602 or 1604; use those last four digits to locate the equivalent instruction in x32dbg.
    Learn more

    IsDebuggerPresent is a Windows API function (in kernel32.dll) that returns non-zero if the calling process has a debugger attached. It reads the BeingDebugged byte in the Process Environment Block (PEB). The result lands in EAX; if EAX is non-zero, a conditional jump sends execution to the failure message. If EAX is zero, execution falls through to the flag display code.

    ASLR means absolute addresses differ between runs, but the last four hex digits of a code address are consistent in this build because the binary is not compiled as PIE. Compare the four-digit suffix you see in Ghidra to the addresses shown in x32dbg to find the corresponding instruction.

    • x64dbg / x32dbg is a free, open-source Windows debugger. Use x32dbg for 32-bit binaries (registers starting with E indicate 32-bit).
    • Ghidra's decompiler view shows the overall logic; x32dbg is where you actually manipulate register values at runtime.
  2. Step 2Set a breakpoint at the TEST instruction and zero EAX
    Run the binary in x32dbg until user code. Locate the TEST instruction (the one immediately after the call to IsDebuggerPresent) using the last-four-digit address match from Ghidra. Set a breakpoint there. Run the binary; it pauses at TEST with EAX = 1 (debugger detected). In the Registers pane, double-click EAX and change it to 0. Then run to completion.

    Important: set the breakpoint on the TEST instruction itself, not on the instruction before it. If you break before TEST runs and try to change EAX, the flag still does not appear because the TEST has not yet set the condition flags. Break on TEST, change EAX to 0 there, then continue.

    Learn more

    EAX holds the return value of the most recently called function. After call IsDebuggerPresent, EAX is 1 if a debugger is attached, 0 otherwise. The conditional jump (JZ / JNZ) that follows TEST EAX, EAX decides which path to take. Changing EAX to 0 before the TEST runs tells the CPU there is no debugger, so the jump follows the success path.

    This technique is called a register patch: instead of permanently modifying the binary, you intercept execution at the right moment and change a register value in memory. It is non-destructive (the file on disk is unchanged) and does not require knowing assembly syntax beyond the breakpoint location.

    The WinAntiDbg series progressively introduces more sophisticated anti-debug techniques. Level 0x100 uses this single API check; 0x200 adds multiple checks including one that requires admin privileges; 0x300 uses an infinite loop and requires a patched binary exported via Ghidra.

  3. Step 3Read the flag from the log
    After changing EAX to 0 and continuing, the program follows the no-debugger code path and prints the flag to the x32dbg log window. Look in the log pane for the line starting with picoCTF.
    Learn more

    Once the anti-debug check is bypassed, the binary executes the code path that displays the flag. The flag string is stored in the binary's data segment and printed to the debugger's log output rather than a console window. Check the x32dbg log tab to find the output.

    Embedded strings in executables are stored in the .data or .rdata section. Running strings on the executable sometimes reveals flag-like content, though challenges that decrypt the flag at runtime require dynamic analysis (running with a debugger) as the only viable path.

Flag

picoCTF{d3bug_f0r_th3_Win_0x100_e7...}

Skipping the failure branch reveals the stored flag string immediately.

Want more picoCTF 2024 writeups?

Useful tools for Reverse Engineering

Related reading

What to try next