Skip to main content

Binary Instrumentation 1 picoCTF 2025 Solution

Inspect a Windows executable to find a hidden encoded string and recover the flag from it.

Published: April 2, 2025Updated: August 25, 2026

Description

A Windows executable announces the flag but first takes a very long sleep. Use Frida to hook and bypass the Sleep call so the binary wakes up immediately and prints the flag.

Unzip with the password picoctf to obtain bininst1.exe. The challenge needs a Windows machine (or VM) with Python and Frida installed.

Install Frida tools: pip install frida-tools. You may also need the Visual C++ Redistributable from Microsoft.

Run the binary once to see what it says. It will announce the flag but then sleep for a very long time.

Create a JavaScript Frida script that intercepts the Sleep function in kernel32.dll and returns immediately instead of waiting.

bash
pip install frida-tools
bash
# Run the binary to see the sleep behavior:
bash
bininst1.exe
bash
# The binary says it will print the flag but needs a nap first

Solution

Want to try it yourself first?

The guided walkthrough reveals hints one step at a time.

Walk me through it
  1. Step 1Hook Sleep with a Frida script
    Observation
    The binary announces it will print the flag and then sleeps for a very long time. Replace the Sleep call in kernel32.dll with Frida and execution reaches the output immediately.
    Write a Frida JavaScript file that locates Sleep in kernel32.dll and replaces it with a function that just returns immediately. Run the binary under frida to apply the hook.
    js
    // kill_sleep.js
    var sleep = Module.getExportByName("kernel32.dll", "Sleep");
    Interceptor.replace(sleep, new NativeCallback(function(ms) {
      return;
    }, "void", ["uint32"]));
    bash
    frida bininst1.exe -l kill_sleep.js
    What didn't work first

    Tried: Using Interceptor.attach instead of Interceptor.replace to hook Sleep.

    Interceptor.attach lets you run code on entry and exit but does not prevent the original function from executing, so the binary still sleeps. Interceptor.replace swaps the entire function pointer with your NativeCallback, ensuring Sleep never actually waits.

    Tried: Attaching Frida to the already-running bininst1.exe process with frida -n bininst1.exe -l kill_sleep.js after the binary has started.

    Sleep is called very early. Attach by name and the thread is already deep inside the wait, so the hook lands too late. Spawn the binary through Frida instead and the hook is in place before any of its code runs.

    Learn more

    Frida is a dynamic binary instrumentation toolkit that lets you inject JavaScript into running processes. It works by attaching to a process (or spawning one) and loading a JavaScript engine into it, giving you full access to the process memory, function hooks, and API interception. Frida is widely used for security research, reverse engineering, and CTF challenges on Windows, macOS, Linux, iOS, and Android.

    Module.getExportByName looks up a named export from a loaded DLL by name. Interceptor.replace replaces the function at that address with a custom NativeCallback written in JavaScript. The callback here accepts the millisecond argument Sleep normally takes and simply returns without doing anything - effectively making Sleep a no-op and letting the binary continue immediately.

    The Windows Sleep function lives in kernel32.dll and pauses the current thread for the specified number of milliseconds. Malware and anti-analysis binaries commonly call Sleep with enormous values (hours or even days) to frustrate dynamic analysis tools that time out after a few seconds. Hooking Sleep is one of the first things analysts do when running a suspicious sample in a sandbox.

  2. Step 2Decode the Base64 output
    Observation
    The output starts with cGljb0NURn, the Base64 encoding of the literal picoCTF{ prefix. One decode gives the plaintext.
    With Sleep bypassed, the binary wakes up and prints the flag as a Base64-encoded string. Decode it to recover the picoCTF flag.
    bash
    # The binary prints something like:
    bash
    # Ok, I'm Up! The flag is: cGljb0NURnt3NGtlX20zX3VwX3cxdGhfZnIxZGFfZjI3YS4uLn0=
    bash
    # Decode with:
    bash
    echo 'cGljb0NURnt3NGtlX20zX3VwX3cxdGhfZnIxZGFfZjI3YS4uLn0=' | base64 -d

    Expected output

    picoCTF{w4ke_m3_up_w1th_fr1da_f27a...}
    What didn't work first

    Tried: Running strings on bininst1.exe to find the flag without using Frida.

    The flag is Base64-encoded in the program's logic rather than sitting as a literal in read-only data. strings would only show the encoded form if it were a literal, and even then Base64 text is easy to skim past. The binary has to run past the hook to emit it.

    Tried: Piping the binary's output through CyberChef using 'From Base64' with the URL-safe alphabet.

    The output uses the standard Base64 alphabet, which is also CyberChef's default, so it works. Pick the URL-safe alphabet by mistake and the bytes come out corrupted. The command-line decoder always uses the standard one.

    Learn more

    Base64 is a reversible encoding that represents binary data using 64 printable ASCII characters. Because it encodes three input bytes at a time, a fixed prefix produces a fixed output prefix: picoCTF{ always encodes to cGljb0NURn, so any encoded picoCTF flag starts with those characters. Spotting this pattern is the fastest way to identify encoded flags in binary output or network captures.

    The binary encodes its output as Base64 to avoid the flag appearing as a plain string in the executable's read-only data section. A naive strings scan of the binary would not immediately reveal the flag, making the Frida instrumentation approach necessary rather than optional.

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{w4ke_m3_up_w1th_fr1da_...}

Install Frida, write a one-function kill_sleep.js that replaces Sleep with a no-op, and run the binary under frida to get the Base64 flag output.

Key takeaway

Dynamic instrumentation intercepts and rewrites a running process at the API level, with no source and no recompilation. Frida lets you swap any exported function for JavaScript, which turns artificial delays, anti-debug checks, and broken paths into no-ops. Malware analysis, mobile app reversing, and anti-cheat research all lean on it.

Related reading

Useful tools for Reverse Engineering

Where to go next