Every day we use software we didn't write.
Sometimes we have the source code. Sometimes we don't. In both cases, the first step is the same.
Observe before changing.
Somewhere inside the Instrumentation Journey app there's a hidden unlock code. The application never displays it. The logs don't reveal it. There's no hint inside the UI.
The goal isn't to guess the code. The goal is to investigate the running process and understand how to find data that's present in memory but invisible on screen.
This chapter introduces the tools and concepts you need to conduct that investigation. By the end, you'll have found the code and, more importantly, understood why the approach works.
Concept
What is a process?
When you tap an application icon, the operating system doesn't just open a file. It creates a process.
A process is the application in motion. It has an identity (a process ID assigned by the OS), a region of memory allocated for its data, code that is actively being executed, and state that changes as the application runs.
A process isn't a file on disk. A file is static. A process is alive.
This distinction matters. Reading the source code or inspecting the compiled binary won't tell you what the application is doing right now. To see that, you need to observe the process while it's running.
Concept
What is a debugger?
A debugger is a tool that attaches to a running process and lets you inspect its state.
With a debugger you can pause execution at any point. You can read the current value of any variable. You can examine the contents of memory. You can follow the call stack and understand what code ran before arriving at the current point.
Debuggers are commonly associated with fixing bugs. But their purpose is broader. A debugger is an observation instrument. It makes the invisible visible.
You don't need to modify the application to use a debugger. You don't need to recompile it. You simply attach to the process that's already running and start asking questions.
Tool
What is LLDB?
LLDB is Apple's debugger. It ships with Xcode and is part of the LLVM compiler toolchain.
If you've ever set a breakpoint in Xcode, stepped through code, or hovered over a variable to see its value, you've used LLDB. Xcode's debugging interface is a graphical layer on top of it.
LLDB also has a direct command-line interface. Through it, you can attach to any running process and inspect its memory, call stack, and register values without ever opening Xcode.
In this chapter, we'll use LLDB to attach to the Instrumentation Journey app and investigate its runtime memory.
Core Concept
Where does the code live?
The unlock code is never displayed in the UI. The logs don't print it. So a reasonable question is: does it even exist anywhere accessible?
Think about what has to happen for the application to verify your input.
At some point, the app must compare what you typed against the expected value. For that comparison to happen, the expected value must exist in memory at the moment the comparison runs. There's no other way for the program to do its job.
It doesn't matter that it's hidden from the interface. It doesn't matter that it's not printed in a log. The application needs that value to function, so it must be in memory when it's needed.
This is the central insight of runtime observation. Data that exists inside a running process can be found, regardless of whether the user interface exposes it. The interface is just one view into the process. A debugger gives you a different view, one that is much more complete.
Challenge
Your task
Open the Instrumentation Journey app and navigate to Chapter 1: The Observer.
The app shows a text field and asks for a hidden unlock code. The code is never displayed in the UI. It exists in memory while the app is running.
Your task is to find it by observing the running process. You don't need to modify the app. You don't need to change any code. Attach a debugger and investigate.
- ›The app IPA: download InstrumentationJourney.ipa (see the v1.0.0 release for changelog and assets).
- ›Xcode: installed on your Mac. It provides the
lldbbinary with device support. - ›An Apple ID: a free account is enough. It's used to generate a personal development certificate for signing the IPA.
- ›Sideloadly: sideloadly.io signs the IPA with your Apple ID certificate and installs it on the device. No paid developer account required. After installation, the device may prompt you to trust the developer certificate before launching the app: go to Settings, General, VPN & Device Management, select your Apple ID, and tap Trust.
- ›Developer Mode: enabled on the device under Settings, Privacy & Security, Developer Mode.
Free accounts generate certificates that expire after 7 days. After that, re-sign the IPA using Sideloadly. If you have a paid Apple Developer account ($99/year), certificates last a full year. The app ships with the get-task-allow entitlement. Without it, LLDB cannot attach to a non-jailbroken device.
Hints
Need a direction?
Each hint narrows the search space without revealing the answer. Use them only when you're genuinely stuck.
memory find command that searches a memory range for a specific byte sequence. If you know even the beginning of the string you're looking for, you can search for it directly.
Investigated and still need the full walkthrough?
The solution page explains the reasoning process step by step before presenting any commands.
View Solution →Reveal all hints first to unlock this link.
Reflection
What just happened?
Something worth pausing on.
We found a hidden value without modifying the application. We didn't bypass any protection. We didn't change source code. We didn't patch anything. We attached a debugger, observed the running process, and the value was there.
This is what runtime observation means.
The application contained data that was invisible to the user but accessible to the process. The user interface simply wasn't displaying it. A debugger let us look past the interface and see the memory directly.
Today the hidden value was an unlock code. In real engineering work, it might be a session token, a feature flag, a cryptographic key, or a piece of business logic that only activates in specific conditions. The technique stays the same.
Runtime observation is often the first step in reverse engineering and instrumentation. Before you can change a system, you need to understand it. Before you can understand it, you need to see what it's actually doing.
That's what a debugger gives you. Not a way to break things. A way to see them.
Think further
Questions without answers.
This chapter gave you one tool and one technique. Before moving on, sit with these questions.
- › What if this app were installed from the App Store instead of sideloaded? Would the same steps still work?
-
›
What if the binary had no debug symbols (no
SecretAssembler, nounlockCodein the symbol table)? How would your approach change? - › LLDB is Apple's debugger. It's not the only tool that can attach to a running process. What alternatives exist, and what trade-offs would each bring?
- › You intercepted the value before the comparison ran. Could you have found it after? Does the order of operations matter here?
You don't need answers right now. The point is to recognize the edges of what you know, and to understand that those gaps are exactly worth exploring.