Description
A roguelike dungeon-crawler binary hides the flag in a room blocked by invisible walls. You can see the flag characters on the map but cannot reach them under normal collision rules.
Patch the wall-collision check in Ghidra, or use GDB to skip the check at runtime, allowing free movement through walls.
Download the binary and make it executable. Get the files from the challenge page on CyLab Security Academy (formerly play.picoctf.org).
Run it to understand the game mechanics (WASD or arrow keys to move).
Use Ghidra to find and patch the wall-collision check.
chmod +x wizardlike./wizardlikeSolution
Want to try it yourself first?
The guided walkthrough reveals hints one step at a time.
set $eflags trick used here, and Ghidra Reverse Engineering covers the on-disk Patch Instruction workflow if you want a permanent fix.Step 1Explore the dungeon and identify blocked areas
ObservationThe binary is a roguelike where flag characters are visible on the map but sit in rooms you cannot enter. Map what is visible against what is reachable first, so you know which tiles to head for once the collision check is bypassed.Run the binary and navigate the dungeon with WASD/arrow keys. You will see flag characters spelled out across rooms separated by#walls and locked corridors. Sketch the layout so you know where you need to walk after the patch.Learn more
A typical screen looks like this (your character is
@, walls are#, doors are+, flag characters are scattered glyphs visible through gaps):#################### #......##....p..i..# #..@...##..........# #......++....c..o..# ########++###..C..T# #......##....F..{..# #..h...##..........# #......##..!..}..._# #################### ^ flag chars visible across the wall @ = player # = wall + = door ! = key h = monsterThe binary renders the map in the terminal (ncurses or direct escape codes). The flag is spelled out in a room or corridor that your character cannot enter under normal rules because the wall-collision function blocks attempts to walk onto
#tiles. Before modifying anything, note which tiles you can see but cannot reach: this gives you the target coordinates.Step 2Find the collision check in Ghidra
ObservationThe game blocks movement onto specific tiles rather than everywhere, which means one conditional in the movement function compares the next tile against the wall character and returns early. Ghidra will locate that exact instruction to patch.In Ghidra, find the movement function. It contains a check like 'if (tile[y][x] == WALL) return;'. Patch this check to always allow movement.Learn more
Import the binary into Ghidra and run auto-analysis. Search for string references related to wall tiles (often
'#'in ASCII dungeons) or look for the movement handling function in main().The collision check in decompiled C looks like:
if (map[player_y + dy][player_x + dx] == '#') { return; }In the disassembly, the pattern looks like:
mov rax, QWORD PTR [rip+0xNNNN] ; load map base pointer movzx eax, BYTE PTR [rax+rcx] ; load tile at (player_y * width + dx) cmp al, 0x23 ; 0x23 is ASCII '#' -> compare to wall char jne <update_position> ; skip the early return when not a wall ret ; bail without moving update_position: mov ... ; write new player_x / player_yLook for a
cmpagainst0x23(the ASCII for#) followed by a conditional jump. Which patch you want depends on which way that jump points. In the shape above the jump is the good path (jne update_positionskips the bail-out), so replace it with an unconditionaljmp update_position; NOP-ing it there would fall straight into theretand block every move, not just the ones into walls. When the compiler emits the mirror image instead (je bail_out, then fall through to the move), the NOP (no-operation,0x90) is the right patch. Read the branch target before choosing.In Ghidra: right-click the conditional jump instruction, select "Patch Instruction", and type the replacement (
JMPat the branch target for the shape above, orNOPif the jump is the one heading for the bail-out). Then export the patched binary (File > Export Program > ELF).Step 3Run the patched binary and navigate through walls
ObservationThe patch removes the tile comparison guarding the return-without-moving path. Run the patched binary and you can walk straight through the previously blocked tiles into the room holding the scattered flag characters.Execute the patched binary. Your character can now move onto any tile. Navigate to the flag room and read the flag characters displayed on the map.bashchmod +x wizardlike-patched && ./wizardlike-patchedbash# Navigate with WASD/arrow keys through the previously blocked wallsWhat didn't work first
Tried: Trying to use GDB's 'set $eflags ^= 0x40' trick but applying it before reaching the collision branch, so the flag flip lands on an unrelated instruction and the wall still blocks movement.
The zero flag only carries the meaning you want at the instant the collision check's conditional jump is about to execute. Flip it an instruction earlier or later and you are toggling a bit governing some other branch, so the game crashes, skips an unrelated update, or still blocks you. Break exactly on the jump address from Ghidra, flip the flag, then continue so that jump is the very next instruction.
Tried: Exporting the patched binary from Ghidra and running it, but getting 'Exec format error' or a segfault immediately on launch because Ghidra's default export options changed the ELF header or stripped the interpreter path.
Ghidra's Export Program defaults to the original format with only the patched bytes changed, but some configurations rewrite the ELF program headers or drop the interpreter segment. If that happens, the file command reports an executable with no dynamic linker path. Use Save As with the ELF format instead, and check with readelf that the interpreter segment survives and points at a valid loader before running it.
Learn more
An alternative to patching is using GDB at runtime: set a breakpoint on the conditional jump instruction address, and when it triggers run
set $eflags ^= 0x40. The0x40bit is the zero flag; XORing flips it, which means ajnetaken-because-not-a-wall becomes ajnenot-taken (or vice versa). The single line turns "step into wall > bail" into "step into wall > proceed." You only have to do this once per movement attempt;continueuntil you reach the next blocked tile.Another approach is to directly modify the player's coordinates in memory while the program is paused in GDB:
set *(int*)&player_x = TARGET_X. The cast tells GDB to write a 32-bit integer at the address ofplayer_x, overwriting the current value withTARGET_X. Pair withset *(int*)&player_y = TARGET_Yandcontinue: the next render places you at the target tile, no walking required. (Ifplayer_xis not a symbol in a stripped build, replace&player_xwith the absolute address you found in Ghidra.)Reading the flag off the rendered map. Once you can move freely, walk every accessible cell and watch the rendered output. The flag glyphs are written into the tile map at fixed coordinates, so you may need to step adjacent to each character (the renderer often only reveals a tile when the player is within line-of-sight). The map spells out the whole flag, wrapper included, so record the visible glyphs in order from
pthrough the closing brace and submit that. If line-of-sight is blocking the read, set the player tile to each flag coordinate in turn with the GDBset *(int*)trick.This class of challenge - where the flag is hidden in the game world behind an artificial barrier - teaches binary patching, a fundamental technique in game modding, license bypass analysis, and malware modification.
Interactive tools
- Strings ExtractorPull printable text from any binary, library, or image. ASCII and UTF-16 detection, configurable minimum length, flag-like highlight, no command line needed.
- Hex ViewerView text or raw hex bytes as a xxd-style hex dump with byte offset, hex columns, and ASCII sidebar. Highlights printable characters and null bytes.
Flag
Reveal flag
picoCTF{ur_4_w1z4rd_...}
This challenge was not solved during the competition. Patch the wall-collision branch in Ghidra so the move always goes through, run the patched binary, and navigate to the hidden flag room.