đ¯ Introduction
This educational guide demonstrates how to analyze iOS applications using developer tools like Frida and Objection to understand their architecture and implementation. Using Voice Memo as an example, we'll explore how iOS apps implement audio recording using public APIs and frameworks. The knowledge gained can help developers better understand iOS development patterns and best practices while staying within Apple's developer guidelines.
đ ī¸ Required Tools
- âĸJailbroken iOS Device
- âĸFrida & Objection
đą Installation Guide
1. Installing Frida on iOS đ˛
Add the following source to your package manager (Cydia, Sileo, etc.):
https://build.frida.re
2. Installing Frida on Your Computer đģ
If you're using Windows, you can use WSL2 (Windows Subsystem for Linux). After setting up your Linux environment, create a Python virtual environment and install Frida tools:
pip install frida-tools
đ Remote Connection Setup
In the following sections, we'll cover how to set up and use Frida remotely between your iOS device and computer without requiring a USB connection - a setup that's often overlooked in most guides.
Frida automatically enables frida-server but it's set on localhost:27042. We need to change this to enable remote access from our computer.
frida-server -l <IP-Address>:27042
<IP-Address>
with your iOS device's actual IP address. Make sure both devices are on the same network.Then on your computer, test the connection with:
frida-ps -H <IP-Address>
đ¯ Target Application

Voice Memo
Bundle ID: com.apple.VoiceMemos
Voice Memo is Apple's built-in audio recording application. For educational purposes only, we'll examine how this app implements standard iOS audio recording APIs and frameworks. This analysis focuses on understanding public APIs and documented frameworks, helping developers learn about proper audio implementation patterns on iOS. All exploration is done within the bounds of Apple's developer guidelines and terms of service.
1. Connecting with Objection đ
Use the following command to connect to Voice Memo:
objection --network --host <IP-Address> --gadget "com.apple.VoiceMemos" explore
com.apple.VoiceMemos
exactly as shown.2. Identify Audio Recording Classes đ¤
First, we need to find the classes responsible for audio recording:
ios hooking search classes audio
AVAudio
or recorder
.đ Search Results in Action

Demo: Objection searching for audio-related classes in the Voice Memo app
3. Analyzing State Transitions with Frida đ
The most interesting aspect of Voice Memo is how it manages recording states. Let's create a custom Frida script to track these state transitions in real-time:
// voice_memo_state_tracker.js
// Target the _VMAudioRecorder class
var VMAudioRecorder = ObjC.classes._VMAudioRecorder;
// Hook the setCurrentState: method
Interceptor.attach(VMAudioRecorder["- setCurrentState:"].implementation, {
onEnter: function(args) {
// args[0] is 'self', args[1] is 'SEL', args[2] is the new state value
var newState = args[2].toInt32();
console.log('[*] State Transition: ' + getStateName(newState));
}
});
// Hook recording control methods
['startRecording', 'pauseRecording', 'stopRecording'].forEach(function(methodName) {
Interceptor.attach(VMAudioRecorder["- " + methodName].implementation, {
onEnter: function() {
console.log('[*] Method called: ' + methodName);
}
});
});
// Helper function to convert state integers to readable names
function getStateName(stateInt) {
var states = {
0: 'Idle',
1: 'Preparing',
2: 'Recording',
3: 'Paused',
4: 'Stopping',
5: 'Stopped'
};
return states[stateInt] || 'Unknown(' + stateInt + ')';
}
To run this script with Frida, save it as voice_memo_state_tracker.js
and execute:
frida -H <IP-Address> -l voice_memo_state_tracker.js -f com.apple.VoiceMemos
4. Real-World State Transitions đ
When running our Frida script on an actual device, we can observe the precise state transitions during a recording session:

Real-time output showing state transitions during Voice Memo recording
Let's break down what's happening in this recording session:
Starting a Recording
startRecording
method is called- State changes to "Preparing"
- Audio session and hardware are initialized
- State changes to "Recording"
- Recording confirmation occurs
Ending a Recording
stopRecording
method is called- State changes to "Stopping"
- Recording is finalized and saved
- Resources are released
- State returns to "Idle"
5. Tracking State Variables with Frida đ
Let's go deeper by examining the internal variables that change during state transitions:
// voice_memo_variable_tracker.js
var VMAudioRecorder = ObjC.classes._VMAudioRecorder;
// Map of variables to track with their corresponding ivar names
var stateVars = {
'currentState': '_currentState',
'targetState': '_targetState',
'currentTime': '_currentTime',
'currentDuration': '_currentDuration',
'recording': '_recording',
'paused': '_paused',
'editing': '_editing'
};
// State name mapping
var stateNames = {
0: 'Idle',
1: 'Preparing',
2: 'Recording',
3: 'Paused',
4: 'Stopping',
5: 'Stopped'
};
// Hook the state change method
Interceptor.attach(VMAudioRecorder["- setCurrentState:"].implementation, {
onEnter: function(args) {
var newState = args[2].toInt32();
var instance = new ObjC.Object(args[0]);
console.log('
[*] State Change: ' + newState + ' (' + (stateNames[newState] || 'Unknown') + ')');
logStateVariables(instance);
}
});
// Log the current values of all tracked state variables
function logStateVariables(instance) {
if (!instance || !instance.$ivars) return;
for (var varName in stateVars) {
try {
var ivarName = stateVars[varName];
var value = instance.$ivars[ivarName];
// Format the value appropriately based on variable type
var formattedValue;
if (varName === 'currentState' || varName === 'targetState') {
formattedValue = value + ' (' + (stateNames[value] || 'Unknown') + ')';
} else if (varName === 'currentTime' || varName === 'currentDuration') {
formattedValue = value.toFixed(2) + ' sec';
} else if (typeof value === 'boolean') {
formattedValue = value ? 'YES' : 'NO';
} else {
formattedValue = value;
}
console.log(' - ' + varName + ': ' + formattedValue);
} catch (e) {
console.log(' - ' + varName + ': <error>');
}
}
}
When we run this script on a device while interacting with Voice Memo, we can see the internal state variables updating in real-time. The visualization below demonstrates how these variables change during a complete recording cycle:

Console output showing variable changes during state transitions in Voice Memo
currentState
and targetState
values change during transitions, and how the boolean flags (recording
, paused
) reflect the user-facing state.6. Conclusion: Putting It All Together đ§Š
Our analysis has revealed Voice Memo's elegant state machine implementation. Let's examine the core state transitions that occur during a recording session:
Voice Memo State Transition Flow
Idle â Preparing
When recording begins, the app first transitions to a "Preparing" state to initialize audio hardware and set up buffers. At this point, boolean flags remain unchanged (recording = NO).
Preparing â Recording
Once hardware setup is complete, the app transitions to "Recording" state. This change updates the boolean flag (recording = YES) and starts capturing audio data.
Recording â Paused (Optional)
When paused, the app maintains its resources but stops writing to the audio buffer. The flags update to reflect this state (recording = YES, paused = YES).
Recording â Stopping
When recording ends, the app enters a "Stopping" state to finalize the audio file. Resources remain allocated (recording = YES) while writing completes.
Stopping â Idle
Finally, the app returns to "Idle" state after finalizing the recording, updating timing information (currentDuration) and releasing hardware resources.
7. Resources for Further Learning đ
To learn more about iOS audio development, check out these resources: