Read the success condition first
This lab uses examples/ctf_gate.c from the downloadable source ZIP. The goal is to locate an integer and a byte array in a running process, change them, and verify the resulting branch. The global gate starts at zero and the four bytes of tag spell NOPE. The checkpoint function returns true only when gate equals 0x1337 and the first four bytes of tag equal PASS. main prints CTF demo solved or CTF demo locked according to that result. The small condition makes mistakes easy to isolate. You are changing process memory rather than patching the executable file. Starting a new process restores the original global values.
volatile unsigned int gate = 0;
volatile unsigned char tag[4] = {'N', 'O', 'P', 'E'};
__attribute__((noinline)) int checkpoint(void) {
return gate == 0x1337 && memcmp((const void *)tag, "PASS", 4) == 0;
}Build for the target OS and stop at checkpoint
Use GCC and GDB inside Linux or WSL for an ELF target. For a Windows PE target, build an EXE with MSYS2 UCRT64 and use native Windows GDB. DBG Desk does not include a compatibility layer for running Windows processes under Linux GDB or running either format directly on macOS. Run the build command from the extracted ZIP folder. On Windows, change the output name to examples/ctf_gate.exe. The -g option preserves symbols and -O0 avoids optimization that can obscure this exercise. Start the local server and select the compiled file in the debugger. Add a breakpoint at checkpoint, then choose Run. Loading a file is different from stopping a running process; memory edits are available only after the process has stopped.
gcc -g -O0 examples/ctf_gate.c -o examples/ctf_gateWrite bytes at the runtime addresses of gate and tag
In CTF tools, enter gate in Resolve symbol address and click Resolve address. The current runtime address is also placed in the memory-write address field. Do not reuse an address from a different computer or an earlier process. PIE and ASLR can change the layout each time the executable starts. Enter 37 13 00 00 as the bytes to write. On the x86 and x86-64 systems used in this lab, little-endian order places 0x37 at the lowest address and 0x13 at the next address, producing the integer 0x1337. Writing 13 37 would produce a different value. The debugger reads the same area immediately after writing and checks that the resulting bytes match.
Resolve tag next and write 50 41 53 53, the ASCII bytes for PASS. This is a four-byte array and the comparison reads exactly four bytes, so do not append a zero byte here. Automatically adding a string terminator could overwrite the following byte. Changing gate alone will not pass the condition because tag would still contain NOPE. Check both regions before continuing. You can put the resolved address into the memory panel to inspect the bytes directly. A string's file offset and the entry address recorded in a binary header are different from a resolved runtime address.
gate: 00 00 00 00 -> 37 13 00 00
tag: 4e 4f 50 45 -> 50 41 53 53Check the return value before continuing
Choose Step out while stopped in checkpoint. Execution returns to main. In this x86/x86-64 example, inspect eax for the integer return value. A value of one means both comparisons passed. Continuing then follows the branch that prints the success message. If the return value is zero, check the current address, the integer byte order, and the exact four bytes written to tag. Also check whether you stopped before the comparison rather than after it. To practice register editing separately, select stack frame zero, write 0x2a to rax, and inspect its current value. A later instruction may overwrite that register, so successfully writing a register is different from satisfying the program's final condition.
CTF demo solvedWhat was actually tested
On October 18, 2026, Native debugger checks run 39103791405 exercised this flow using real GDB processes on Windows MSYS2 UCRT64 and Ubuntu. The automated test stops at checkpoint, resolves gate and tag, writes their bytes, changes and reads back a register, and restores the original register value. It then checks that the function returned one and continues until the process exits. The test names below come from that run. The earlier output block describes the output expected from the source code; it is not a captured terminal screenshot.
Linux passed all 54 Python checks in that run. Windows passed its checks with two POSIX-only tests skipped. Separate tests verified a first-instruction stop in a stripped binary and transmission of 41 00 42 0a through a Linux PTY, including a NUL byte and newline. This evidence covers the included small examples. It does not establish compatibility with every competition binary, architecture, protected process, or GDB version. For a real challenge, fix the input and stop location first, then change one value at a time and compare it with the original execution. Process-memory changes are not saved into the executable; resolve addresses and repeat the writes after restarting.
test_native_pe_or_elf_memory_register_edits_solve_gate ... ok
test_stripped_binary_stops_without_main_symbol ... ok
Linux: Ran 54 tests / OK
Windows: Ran 54 tests / OK (skipped=2)