Description
A challenge about Windows internals. Analyze the Windows executable to find the flag.
Setup
Download the executable.
wget <url>/B1ll_Gat35.exeSolution
Want to try it yourself first?
The guided walkthrough reveals hints one step at a time.
Step 1
Examine the PE binaryObservationI noticed the challenge provided a Windows .exe file, which suggested starting with 'file' and 'strings' to cheaply identify the binary type and check for any plaintext secrets before spending time on a full decompiler.Run file and strings on the .exe file. Identify the executable type (32-bit vs 64-bit, .NET or native). Native PE files can be analyzed in Ghidra or Radare2.bashfile B1ll_Gat35.exebashstrings B1ll_Gat35.exe | head -50bashstrings B1ll_Gat35.exe | grep -i picoExpected output
PICOCTF{These are the access codes to the vault: 1063340}What didn't work first
Tried: Run 'strings B1ll_Gat35.exe | grep -i flag' expecting a flag-shaped line and find nothing.
The flag format here is PICOCTF{...} (all caps, non-standard) rather than picoCTF{...}, so a case-insensitive grep for 'flag' misses it entirely. Grep for 'pico' (case-insensitive) or look at the raw strings output and scan for curly braces to catch non-standard capitalization.
Tried: Open the .exe in a hex editor and search for 'MZ' expecting to find .NET metadata tables like #Strings or #US, then pull the flag from there.
If file reports a native PE (not a .NET assembly), there are no managed metadata streams - only raw section data. The readable flag string lives in the .rdata section and is found by strings, not by hunting IL metadata tables. Reserve the IL/dnSpy path for binaries that file reports as CIL/.NET.
Learn more
PE (Portable Executable) is Windows' binary format, analogous to ELF on Linux. A PE file has a DOS header (starts with MZ), a PE header, section headers, and sections including .text (code), .data (initialized data), .rdata (read-only data, where strings live), and .rsrc (resources).
If it is a .NET assembly, use dnSpy or dotPeek for decompilation. If it is a native binary, use Ghidra, x64dbg, or IDA.
Step 2
Load in Ghidra or run in WineObservationI noticed that 'strings' revealed a flag-shaped string but did not expose the password comparison logic, which suggested loading the binary in Ghidra for static analysis to find the expected key value and understand the check function.On Linux, use Ghidra for static analysis. If you need dynamic analysis, run the executable under Wine. On Windows, use x64dbg for dynamic debugging.bashghidra B1ll_Gat35.exe &bash# Or with Wine:bashwine B1ll_Gat35.exeWhat didn't work first
Tried: Use objdump -d B1ll_Gat35.exe on Linux to disassemble and look for the password comparison.
objdump can disassemble PE files but it does not resolve Windows API symbols or imports, so calls to lstrcmpA or lstrcmpW appear as unresolved indirect calls with no argument labels. Ghidra's auto-analysis resolves the IAT and annotates both pointer arguments to the comparison, making the expected string immediately visible.
Tried: Run Wine immediately without Ghidra to observe runtime behavior and enter guesses interactively.
Wine on Linux may not have all required Windows runtime DLLs, causing the program to crash before the password prompt appears. Static analysis in Ghidra finds the expected string without needing a working Wine environment, and is faster than trial-and-error guessing at a runtime prompt.
Learn more
Ghidra handles PE files natively on any OS. After auto-analysis, navigate to the main function (search for the entry point in the Symbol Tree). The Windows API calls (MessageBox, CreateFile, etc.) provide context for understanding what the program does.
Step 3
Find the password check and extract the flagObservationI noticed Ghidra's decompilation showed an 'lstrcmpA' call with one argument pointing to a literal string beginning with 'The key is:', which suggested the program expected that full string verbatim rather than a bare number, and that patching the conditional jump would bypass the check entirely.The program prompts for a number and then a key, and a check function compares your input against an expected string. The catch: the expected input is the full string 'The key is: 4253360', not the bare number 4253360, so entering just the number fails the comparison. Enter the complete string to pass the check (or patch the conditional jump so the success branch prints the flag regardless).bash# Enter exactly: The key is: 4253360bash# Alternatively, in x64dbg/Ghidra patch the JNZ after the strcmp to fall through to the success branch.What didn't work first
Tried: Enter just the number 4253360 at the key prompt after seeing it in Ghidra's decompilation.
The lstrcmpA call compares your input against the full string 'The key is: 4253360', not the bare number. Entering only 4253360 makes the comparison return non-zero, so the conditional jump takes the failure branch. The entire prefix including 'The key is: ' must be typed verbatim.
Tried: Patch the JE instruction after the comparison to JMP (unconditional jump) so the success branch always runs.
A JE (jump-if-equal) patch to JMP would skip the success branch rather than force it - you want to patch the JNZ (jump-if-not-zero, i.e. jump on mismatch) to a NOP or short JMP into the success block. Confusing the direction of the conditional means the patch has the opposite effect and the failure path always runs instead.
Learn more
Windows API functions for string comparison:
lstrcmpA(ANSI),lstrcmpW(Unicode/Wide). In Ghidra these appear as calls with two pointer arguments; one is your input and one is the expected value. Here the expected value is the literalThe key is: 4253360, which is easy to misread as just the number.
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{These are the access codes to the vault: 1063340}
The check compares your key input against the full string 'The key is: 4253360' (not the bare number). Enter that exact string, or patch the conditional jump, to reach the success branch. Note the flag uses the non-standard PICOCTF{...} format.