Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
One of the fanciest options offered by the IDA 4.8 debugger is the debugging of Windows executables from a Linux machine. The 64 bits remote debugging server is started on the Windows64 machine.
and IDA for Linux is started with the following command line
idat -rwin32@192.168.1.56+
the command line switch specifies the debugger type (windows in this case), the machine name/IP (192.168.1.56) and the last + specifies that a list of running processes will be requested from the target machine. IDA will then display that list and you'll be able to connect to processes on the Windows64 machine.
and here is the 64 bit program, ready to be debugged under Linux.
The IDA Debugger allows you to either start a new process (Run) or attach itself to an existing process (Attach)
Let's select "Local Windows debugger". What we get is a list of currently running processes to which we can attach with a simple click.
and here is the result once we are attached to the program.
Connecting a Windows Debuging session to a Linux machine is essentially similar to a Windows to Windows connection. The Linux remote debugger server is started on the command line.
then connect to the Linux Machine by selecting the attach to remote Linux command. We can then select the process we want to debug and connect to it with a simple click.
we are now connected to the remote process running on the Linux machine.
Last updated on March 6, 2021 — v2.0
IDA Pro fully supports debugging native macOS applications.
Intel x86/64 debugging has been supported since IDA 5.6 (during OSX 10.5 Leopard), but due to IDA’s use of libc++ we can only officially support debugging on OSX 10.9 Mavericks and later. Apple Silicon arm64 debugging for macOS11 is also supported since IDA 7.6.
Note that this task is riddled with gotchas, and often times it demands precise workarounds that are not required for other platforms. In this tutorial we will purposefully throw ourselves into the various pitfalls of debugging on a Mac, in the hopes that learning things the hard way will ultimately lead to a smoother experience overall.
Begin by downloading samples:
which contains the sample applications used in this writeup.
It is important to note that a debugger running on macOS requires special permissions in order to function properly. This means that the debugger itself must be codesigned in such a way that MacOS allows it to inspect other processes.
The main IDA Pro application is not codesigned in this way. Later on we’ll discuss why.
To quickly demonstrate this, let’s open a binary in IDA Pro and try to debug it. In this example we’ll be debugging the helloworld app from samples.zip:
on MacOSX 10.15 Catalina using IDA 7.5. Begin by loading the file in IDA:
Now go to menu Debugger>Select debugger and select Local Mac OS X Debugger:
Immediately IDA should print a warning message to the Output window:
This is because IDA is aware that it is not codesigned, and is warning you that attempting to debug the target application will likely fail. Try launching the application with shortcut F9. You will likely get this error message:
Codesigning IDA Pro might resolve this issue, but we have purposefully decided not to do this. Doing so would require refactoring IDA’s internal plugin directory structure so that it abides by Apple’s bundle structure guidelines. This would potentially break existing plugins as well as third-party plugins written by users. We have no plans to inconvenience our users in such a way.
Also note that running IDA as root will allow you to use the Local Mac OS X Debugger without issue, but this is not advisable.
A much better option is to use IDA’s mac debug server - discussed in detail in the next section.
A good workaround for the debugging restrictions on macOS is to use IDA’s debug server - even when debugging local apps on your mac machine. The mac debug server is a standalone application that communicates with IDA Pro via IPC, so we can ship it pre-codesigned and ready for debugging right out of the box:
Let’s try launching the server:
Now go back to IDA and use menu Debugger>Switch debugger to switch to remote debugging:
Now use Debugger>Process options to set the Hostname and Port fields to localhost and 23946.
(Note that the port number was printed by mac_server64 after launching it):
Also be sure to check the option Save network settings as default so IDA will remember this configuration.
Now go to _main in the helloworld disassembly, press F2 to set a breakpoint, then F9 to launch the process. Upon launching the debugger you might receive this prompt from the OS:
macOS is picky about debugging permissions, and despite the fact that mac_server is properly codesigned you still must explicitly grant it permission to take control of another process. Thankfully this only needs to be done once per login session, so macOS should shut up until the next time you log out (we discuss how to disable this prompt entirely in the Debugging Over SSH section below).
After providing your credentials the debugger should start up without issue:
To simplify using the mac server, save the following XML as com.hexrays.mac_server64.plist in ~/Library/LaunchAgents/:
Now mac_server64 will be launched in the background whenever you log in. You can connect to it from IDA at any time using the Remote Mac OS X Debugger option. Hopefully this will make local debugging on macOS almost as easy as other platforms.
There are some applications that macOS will refuse to allow IDA to debug.
For example, load /System/Applications/Calculator.app/Contents/MacOS/Calculator in IDA and try launching the debugger. You will likely get this error message:
Despite the fact that mac_server64 is codesigned, it still failed to retrieve permission from the OS to debug the target app. This is because Calculator.app and all other apps in /System/Applications/ are protected by System Integrity Protection and they cannot be debugged until SIP is disabled. Note that the error message is a bit misleading because it implies that running mac_server64 as root will resolve the issue - it will not. Not even root can debug apps protected by SIP.
Disabling SIP allows IDA to debug applications like Calculator without issue:
The effects of SIP are also apparent when attaching to an existing process. Try using menu Debugger>Attach to process, with SIP enabled there will likely only be a handful of apps that IDA can debug:
Disabling SIP makes all system apps available for attach:
It is unfortunate that such drastic measures are required to inspect system processes running on your own machine, but this is the reality of MacOS. We advise that you only disable System Integrity Protection when absolutely necessary, or use a virtual machine that can be compromised with impunity.
With IDA you can debug any system library in /usr/lib/ or any framework in /System/Library/.
This functionality is fully supported, but surprisingly it is one of the hardest problems the mac debugger must handle. To demonstrate this, let’s try debugging the _getaddrinfo function in libsystem_info.dylib.
Consider the getaddrinfo application from samples.zip:
Try testing it out with a few hostnames:
Now load libsystem_info.dylib in IDA and set a breakpoint at _getaddrinfo:
Choose Remote Mac OS X Debugger from the Debugger menu and under Debugger>Process options be sure to provide a hostname in the Parameters field. IDA will pass this argument to the executable when launching it:
Before launching the process, use Ctrl+S to pull up the segment list for libsystem_info.dylib. Pay special attention to the __eh_frame and __nl_symbol_ptr segments. Note that they appear to be next to each other in memory:
This will be important later.
Finally, use F9 to launch the debugger and wait for our breakpoint at _getaddrinfo to be hit. We can now start stepping through the logic:
Everything appears to be working normally, but use Ctrl+S to pull up the segment information again. We can still see __eh_frame, but it looks like __nl_symbol_ptr has gone missing:
It is actually still present, but we find it at a much higher address:
Recall that we opened the file directly from the filesystem (/usr/lib/system/libsystem_info.dylib). However this is actually not the file that macOS loaded into memory. The libsystem_info image in process memory was mapped in from the dyld_shared_cache, and the library’s segment mappings were modified before it was inserted into the cache.
IDA was able to detect this situation and adjust the database so that it matches the layout in process memory. This functionality is fully supported, but it is not trivial. Essentially the debugger must split your database in half, rebase all code segments to one address, then rebase all data segments to a completely different address.
It is worth noting there is another approach that achieves the same result, but without so much complexity.
As an alternative for the above example, note that you can load any module directly from a dyld_shared_cache file and debug it. For example, open the shared cache in IDA:
When prompted, select the "single module" option:
Then choose the libsystem_info module:
Select the Remote Mac OS X Debugger and for Debugger>Process options use the exact same options as before:
Now set a breakpoint at _getaddrinfo and launch the process with F9.
After launching the debugger you might see this warning:
This is normal. Modules from the dyld_shared_cache will contain tagged pointers, and IDA patched the pointers when loading the file so that analysis would not be hindered by the tags. IDA is warning us that the patches might cause a discrepancy between the database and the process, but in this case we know it’s ok. Check Don’t display this message again and don’t worry about it.
Launching the process should work just like before, and we can start stepping through the function in the shared cache:
This time there was no special logic to map the database into process memory. Since we loaded the module directly from the cache, segment mappings already match what’s expected in the process. Thus only one rebasing operation was required (as apposed to the segment scattering discussed in the previous example).
Both techniques are perfectly viable and IDA goes out of its way to fully support both of them. In the end having multiple solutions to a complex problem is a good thing.
When debugging macOS applications it is easy to get lost in some obscure Objective-C framework. IDA’s mac debugger provides tools to make debugging Objective-C code a bit less painful.
Consider the bluetooth application from samples.zip:
The app will print all devices that have been paired with your host via Bluetooth. Try running it:
Let’s try debugging this app. First consider the call to method +[IOBluetoothDevice pairedDevices]:
If we execute a regular instruction step with F7, IDA will step into the _objc_msgSend function in libobjc.A.dylib, which is probably not what we want here. Instead use shortcut Shift+O. IDA will automatically detect the address of the Objective-C method that is being invoked and break at it:
This module appears to be Objective-C heavy, so it might be a good idea to extract Objective-C type info from the module using right click -> Load debug symbols in the Modules window:
This operation will extract any Objective-C types encoded in the module, which should give us some nice prototypes for the methods we’re stepping in:
Let’s continue to another method call - but this time the code invokes a stub for _objc_msgSend that IDA has not analyzed yet, so its name has not been properly resolved:
In this case Shift+O should still work:
Shift+O is purposefully flexible so that it can be invoked at any point before a direct or indirect call to _objc_msgSend. It will simply intercept execution at the function in libobjc.A.dylib and use the arguments to calculate the target method address.
However, you must be careful. If you use this action in a process that does not call _objc_msgSend, you will lose control of the process. It is best to only use it when you’re certain the code is compiled from Objective-C and an _objc_msgSend call is imminent.
The Objective-C runtime analysis performed by Load debug symbols will also improve decompilation.
Consider the method -[IOBluetoothDevice isConnected]:
Before we start stepping through this method we might want to peek at the pseudocode to get a sense of how it works. Note that the Objective-C analysis created local types for the IOBluetoothDevice class, as well as many other classes:
This type info results in some sensible pseudocode:
We knew nothing about this method going in - but it’s immediately clear that device connectivity is determined by the state of an io_service_t handle in the IOBluetoothObject superclass, and we’re well on our way.
In this section we will discuss how to remotely debug an app on a mac machine using only an SSH connection. Naturally, this task introduces some unique complications.
To start, copy the mac_server binaries and the bluetooth app from samples.zip:
to the target machine:
Now ssh to the target machine and launch the mac_server:
Now open the bluetooth binary on the machine with your IDA installation, select Remote Mac OS X Debugger from the debugger menu, and for Debugger>Process options set the debugging parameters. Be sure to replace <remote user> and <remote ip> with the username and ip address of the target machine:
Try launching the debugger with F9. You might get the following error message:
This happened because debugging requires manual authentication from the user for every login session (via the Take Control prompt discussed under Using the Mac Debug Server, above).
But since we’re logged into the mac via SSH, the OS has no way of prompting you with the authentication window and thus debugging permissions are refused.
Note that mac_server64 might have printed this workaround:
But this is an extreme measure. As an absolute last resort you can launch the mac_server with your credentials in the environment variables, which should take care of authentication without requiring any interaction with the OS. However there is a more secure workaround.
In your SSH session, terminate the mac_server process and run the following command:
Edit taskport.plist and change the authenticate-user option to false:
Then apply the changes:
This will completely disable the debugging authentication prompt (even across reboots), which should allow you to use the debug server over SSH without macOS bothering you about permissions.
When debugging over SSH you might experience some slowdowns. For example you might see this dialog appear for several seconds when starting the debugger:
During this operation IDA is fetching function names from the symbol tables for all dylibs that have been loaded in the target process. It is a critical task (after all we want our stack traces to look nice), but it is made complicated by the sheer volume of dylibs loaded in a typical macOS process due to the dyld_shared_cache. This results in several megabytes of raw symbol names that mac_server must transmit over the wire every time the debugger is launched.
We can fix this by using the same trick that IDA’s Remote iOS Debugger uses to speed up debugging - by extracting symbol files from the dyld cache and parsing them locally. Start by downloading the ios_deploy utility from our downloads page, and copy it to the remote mac:
Then SSH to the remote mac and run it:
Copy mac_symbols.zip from the remote machine to your host machine and unzip it. Then open Debugger>Debugger options>Set specific options and set the Symbol path field:
Now try launching the debugger again, it should start up much faster.
Also keep the following in mind:
Use /var/db/dyld/dyld_shared_cache_i386 if debugging 32-bit apps
You must perform this operation after every macOS update. Updating the OS will update the dyld_shared_cache, which invalidates the extracted symbol files.
The ios_deploy utility simply invokes dyld_shared_cache_extract_dylibs_progress from the dsc_extractor.bundle library in Xcode. If you don’t want to use ios_deploy there are likely other third-party tools that do something similar.
IDA 7.6 introduced the ARM Mac Debugger, which can debug any application that runs natively on Apple Silicon.
On Apple Silicon, the same rules apply (see Codesigning & Permissions above). The Local ARM Mac Debugger can only be used when run as root, so it is better to use the Remote ARM Mac Debugger with the debug server (mac_server_arm64), which can debug any arm64 app out of the box (see Using the Mac Debug Server).
We have included arm64 versions of the sample binaries used in the previous examples. We encourage you to go back and try them. They should work just as well on Apple Silicon.
Similar to Intel Macs, IDA cannot debug system apps on Apple Silicon until System Integrity Protection is disabled.
But here macOS introduces another complication. All system apps shipped with macOS are built for arm64e - and thus have pointer authentication enabled. This is interesting because ptruath-enabled processes are treated much differently within the XNU kernel. All register values that typically contain pointers (PC, LR, SP, and FP) will be signed and authenticated by PAC.
Thus, if a debugger wants to modify the register state of an arm64e process, it must know how to properly sign the register values. Only arm64e applications are allowed to do this (canonically, at least).
You may have noticed that IDA 7.6 ships with two versions of the arm64 debug server:
mac_server_arm64e is built specifically for the arm64e architecture, and thus will be able to properly inspect other arm64e processes. We might want to try running this version right away, but by default macOS will refuse to run any third-party software built for arm64e:
According to Apple, this is because the arm64e ABI is not stable enough to be used generically. In order to run third-party arm64e binaries you must enable the following boot arg:
After rebooting you can finally run mac_server_arm64e:
This allows you to debug any system application (e.g. /System/Applications/Calculator.app) without issue:
Also note that the arm64e ABI limitation means you cannot use the Local ARM Mac Debugger to debug system arm64e apps, since IDA itself is not built for arm64e. It is likely that Apple will break the arm64e ABI in the future and IDA might cease to work. We want to avoid this scenario entirely.
Using the Remote ARM Mac Debugger with mac_server_arm64e is a nice workaround. It guarantees ida.app will continue to work normally regardless of any breakages in the arm64e ABI, and we can easily ship new arm64e builds of the server to anybody who needs it.
To summarize:
Use mac_server_arm64 if you’re debugging third-party arm64 apps that aren’t protected by SIP
Use mac_server_arm64e if you’re feeling frisky and want to debug macOS system internals. You must disable SIP and enable nvram boot-args=-arm64e_preview_abi, then you can debug any app you want (arm64/arm64e apps, system/non-system apps, shouldn’t matter).
If you have any questions about this writeup or encounter any issues with the debugger itself in your environment, don’t hesitate to contact us at support@hex-rays.com.
Our Mac support team has years of experience keeping the debugger functional through rapid changes in the Apple developer ecosystem. It is likely that we can resolve your issue quickly.
Here you can find a comprehensive set of step-by-step tutorials categorized by different debugging types and platforms.
Overview of Linux debugging with IDA
Overview of Windows debugging with IDA
IDA scriptable debugger: Overview and scriptability
Windows local debugging:
Linux local debugging:
PIN Tracer:
Android/Dalvik debugging:
XNU debugging:
QEMU debugging:
Trace and replay debugger features:
Appcall mechanism:
Outdated tutorials that no longer apply have been moved to the archive.
Last updated on July 29, 2020 — v0.1
You may already know that IDA lets you debug an application from an already existing IDB, by selecting the debugger using the drop-down debugger list.
However, it is also possible to start IDA in a way that it will initially create an empty IDB, and then either:
start a new process under its control
attach to an existing process
To do so, you will have to launch IDA from the command line, like so:
IDA will then launch the /bin/ls program, and break at its entrypoint
For this example, we’ll launch, from a shell, a /usr/bin/yes process, and attach to.
Now, we’ll launch IDA so it offers a selection of processes to (and use quick filtering (Ctrl+F) to quickly find our process):
IDA will then attach to the selected process, and leave it suspended at the place it was when it was attached to:
You may either start a local debugging session on a new process or start a local debugging session and attach it to an existing process. Both options are accessible through the command line.
idat -rlinux MY_PROGRAM
will start the program, create a temporary database that allows the user to work with the target at once.
The command
idat -rlinux+
will offer you a choice of running processes to connect to.
and we can proceed with our local Linux debugging session.
This guide shows a walkthrough of how one can use IDA Pro to debug an app installed using Xcode on a device running iOS 17 or iOS 18. The pre-debugging setup is specific to non-jailbroken devices running iOS 18 with a macOS host but the steps in IDA can be reused for most iOS/iPadOS targets (jailbroken, Corellium) with a different platform as the host. The iOS debugger is available with all platforms IDA supports though due to Apple's iOS tools being available on macs only, the workflow is easier on macOS.
Tested on macOS 15.0 (24A335) using Xcode 16.0 (16A242) with an iPhone 11 running iOS 18.0 (22A3354).
Since it's the simplest way to install an app onto an iOS device, we'll be using Xcode to quickly build and install a sample app onto our target device.
Create an iOS Game app by choosing the appropriate template:
Enter a Product name, make note of the resulting Bundle Identifier. For the scope of this guide, the rest of the project options aren't relevant.
In the main Xcode window, ensure that the appropriate target device (Run Destinations) is selected.
If the device hasn’t been paired, a warning may appear requesting you to accept the “Trust” prompt on the device to pair with the host. If the device hasn’t been used for development recently, Xcode will install a Developer Disk Image to enable development services. It will also copy and extract the shared cache symbols to the host (in ~/Library/Developer/Xcode/iOS DeviceSupport/<device_and_os_version>/
).
If you try to Run (clicking on the “play” icon or using CMD-R) the app, an error may appear prompting you to select a development team. Xcode requires that a developer certificate is selected for the signing of the application.
The developer team can be selected via the Signing & Capabilities editor.
Now that a developer certificate was selected for the app, it must also be trusted on the device. If you attempt to Run the app, another warning should appear prompting you to trust the certificate on the device. (Please follow those instructions to trust that certificate)
Finally, Run the app and ensure that it starts properly on the target device.
Debugging an application on an iPhone is enabled by the debugserver
that will attach to the application's process. The debugserver
communicates with clients using (an extended version of) the GDB protocol. IDA comes with an iOS debugger that can "talk" that same protocol. The techniques necessary to prepare the debugging environment on an iPhone have evolved over time (and will likely keep evolving) but fundamentally the goal is always to establish a connection with a debugserver
.
The device communication stack was revamped with iOS 17. Devices expose a series of remote services (through remoted
which is visible via Bonjour). One of those services is a debugproxy
which is... a proxy to the debugserver
. debugproxy
is a secure service, it is not available to any client. To gain access to secure services, a trusted tunnel must be setup (requires the device to be paired with the host), between the host and the device (the service to set up the tunnel is itself available via remoted
).
One of the primary frameworks used for these communications is CoreDevice, with it Apple also provides devicectl
which is a very convenient utility that can be used to automate certain tasks to control devices. We will use devicectl
to perform certain tasks such as launching an application on the device. Unfortunately devicectl
doesn't provide a direct interface to setup a trusted tunnel and retrieve ports of services exposed through it. It is however possible to reuse some commands of devicectl
to create a tunnel and keep it open. In addition, the ports of services are written to system logs after the tunnel is set up so we can recover them with a few tricks.
To make commands provided below immediately usable, we'll define two helper environment variables. The device name is the same as the one that was used for the target device in Xcode, it can also be found using xcrun devicectl list devices
. The bundle identifier is the identifier of the application we’d like to debug
Trigger the creation of a trusted tunnel
To request the creation of the trusted tunnel, the devicectl device notification observe
command was selected because it can keep the tunnel open for an arbitrary amount of time (controlled using the timeout). Here the name for the Darwin notification ('foobar') to observe is one that will presumably never be posted. The timeout (3000[s]) was chosen to be long enough for a debugging session.
Retrieve the details of the debugproxy
service (provided through the trusted tunnel) from the system logs. The command below will filter for the specific log messages we’re looking for (ipv6 address of device through tunnel and port of debugproxy
service). In short log show
(see log(1)
) will show messages from the system logs; --last 5m
will limit the search to the past 5 minutes (the tunnel should have been created when the previous command was started so 5 minutes ought to be enough); The messages we're looking for are "Info" messages so --info
is necessary; --predicate
is used for message filters.
NOTE: if no log messages match the filter, it is recommended to force the recreation of the tunnel
kill the
remotepairingd
daemon:sudo pkill -9 remotepairingd
retry previous two steps
Some applications can keep the tunnel open, examples: Xcode, Console and Safari. It is preferable to close them before creating the tunnel. If multiple sets of messages match the filter (with different connection details), only the last one should be considered (previous connections would likely be stale).
The relevant pieces of information in the messages are:
remote fd57:8329:afda::1
in the first message and Port => 49350
in the second message.
TIP: There are some really good third party tools out there such as DoronZ's pymobiledevice3 that reimplement the necessary machinery to create a trusted tunnel and make services available.
Launch the app (--start-stopped
will make it wait at the process entry point)
Retrieve the PID of the process.
We have now created a trusted tunnel, found the connection details necessary for the debugproxy
, launched an app and fetched its PID.
Open the application executable (typically located in ~/Library/Developer/Xcode/DerivedData/<project_id>/Build/Products/Debug-iphoneos/<appname>.app/<appname>
, the path to the build folder can also be retrieved via Xcode Product>Copy Build Folder Path) in IDA.
Open the Debugger>Process options.. dialog.
Fill-in the Hostname (address) and Port of the debugproxy
. Please use the ones retrieved from the system logs earlier.
The Application and Parameters fields can safely be ignored since we’ll be attaching to a process. For this example the input file is the main executable for the application, as such the Input file field doesn't need to be modified.
Open the Debugger specific options.
IDA can speed up the loading of shared cache symbols if they have been copied and extracted to the host machine, it is highly recommended to provide the Symbol path (normally ~/Library/Developer/Xcode/iOS DeviceSupport/<device_and_os_version>/Symbols
). Launch debugserver automatically should be disabled as it is used to communicate with devices using the MobileDevice framework (no longer a viable option as of iOS 17). Accept this dialog.
Optionally, open the Debugger options and enable Suspend on debugging start. The Debugger setup dialog can then be closed as well as the Debug application setup dialog.
Now that the necessary connection details have been given to IDA, we can attach to the target process.
Open Debugger>Attach to process... We will provide the PID manually so accept this dialog.
Enter the PID of the target process retrieved earlier using devicectl
and accept this dialog.
Profit! The Debugging session should start.
Last updated on September 27, 2023 — v0.3
Starting with version 6.6, IDA Pro can debug Android applications written for the Dalvik Virtual Machine. This includes source level debugging too. This tutorial explains how to set up and start a Dalvik debugging session.
First of all we have to install the Android SDK from the official site Android Studio.
IDA needs to know where the adb
utility resides, and tries various methods to locate it automatically. Usually IDA finds the path to adb
, but if it fails then we can define the ANDROID_SDK_HOME
or the ANDROID_HOME
environment variable to point to the directory where the Android SDK is installed to.
Start the Android Emulator or connect to the Android device.
Information about preparing a physical device for development can be found at Using Hardware Devices.
Check that the device can be correctly detected by adb
:
IDA assumes that the debugged application is already installed on the Android emulator/device.
Please download:
and
from our site. We will use this application in the tutorial.
We will use adb
to install the application:
IDA can handle both .apk
app bundles, or just the contained .dex
files storing the app’s bytecode. If we specify an .apk
file, IDA can either extract one of the contained .dex
files by loading it with the ZIP
load option, or load all classes*.dex
files when using the APK
loader.
The main configuration of the dalvik debugger happens resides in "Debugger > Debugger Options > Set specific options":
As mentioned above IDA tries to locate the adb
utility. If IDA failed to find it then we can set the path to adb
here.
Specifies the argument to the adb connect
command. It is either empty (to let adb
figure out a meaningful target) or a <host>[:<port>]
combination to connect to a remote device somewhere on the network.
Serial number of an emulator or a device. Passed to adb``'s -s
option. This option is useful if there are multiple potential target devices running. For the official Android emulator, it is typically emulator-5554
.
Press button and point IDA to either the APK or the AndroidManifest.xml
file of the mobile app. IDA then automatically fetches the package name and application start activity, as well as the debuggable
flag from the specified file.
Package name containing the activity to be launched by the debugger.
Start activity to be launched by the debugger.
Usually IDA builds the start command from the package and activity name and launches the APK from the command line as follows:
If that does not match your desired debugging setup, you can enter an alternative start command here. Note that you have to provide package and activity as part of the startup command.
The value of the debuggable flag, as extracted from the AndroidManifest.xml
or the APK. APKs that do not have the debuggable flag set (most do not) cannot be started on unpatched phones. Hence, while this value is false, IDA will display a (silencable) warning when starting a debugging session. To produce a debuggable APK that has the flag set to true, please revert to third-party tooling.
This controls the behavior of IDA’s type guessing engine. "Always" and "Never" are pretty self-explanatory: The options force-enable or force-disable type guessing. "Auto" means that type guessing is disabled for Android APIs < 28 and enabled on APIs >= 28. If you work with very old (i.e. API 23 and lower) Android devices and experience crashes during debugging, set this option to "Never". Note that when type guessing is disabled, IDA automatically assumes int
for unknown variable types, which causes warnings on API 30 and above.
Local Variables with Type Guessing Deactivated
Local Variables with Type Guessing Activated
If active, IDA shows the object ID assigned by the Java VM for composite (non-trivial) types in the local variables window.
If active, IDA sets breakpoints at the beginning of all (non-synthetic, non-empty) methods of the start activity class specified in the Activity field above.
To use source-level debugging we have to set paths to the application source files. We can do it using the "Options > Sources path" menu item.
Our Dalvik debugger presumes that the application sources reside in the current (".") directory. If this is not the case, we can map current directory (".") to the directory where the source files are located.
Let us place the source files DisplayMessageActivity.java
and MainActivity.java
in the same directory as the MyFirstApp.apk
package. This way we do not need any mapping.
Before launching the application it is reasonable to set a few breakpoints. We can rely on the decision made by IDA (see above the presetBPTs
option) or set breakpoints ourselves. A good candidate is the onCreate
method of the application’s main activity.
We can use the activity name and the method name onCreate
to set a breakpoint:
Naturally, we can set any other breakpoints any time. For example, we can do it later, when we suspend the application.
At last we can start the debugger. Check that the Dalvik debugger backend is selected. Usually it should be done automatically by IDA:
If the debugger backend is correct, we are ready to start a debugger session. There are two ways to do it:
Launch a new copy of the application (Start process)
Attach to a running process (Attach to process)
To start a new copy of the application just press <F9> or use the "Debugger > Start process" menu item. The Dalvik debugger will launch the application, wait until application is ready and open a debugger session to it.
We may wait for the execution to reach a breakpoint or press the “Cancel” button to suspend the application.
In our case let us wait until execution reach of onCreate
method breakpoint.
Instead of launching a new process we could attach to a running process and debug it. For that we could have selected the "Debugger > Attach to process…" menu item. IDA will display a list of active processes.
We just select the process we want to attach to.
All traditional debug actions like Step into
, Step over
, Run until return
and others can be used. If the application sources are accessible then IDA will automatically switch to the source-level debugging.
Below is the list of special things about our Dalvik debugger:
In Dalvik there is no stack and there is no SP
register. The only available register is IP
.
The method frame registers and slots (v0
, v1
, …) are represented as local variables in IDA. We can see them in the "Debugger > Debugger Windows > Locals" window (see below)
The stack trace is available from "Debugger > Debugger windows > Stack trace" (the hot key is <Ctrl-Alt-S>).
When the application is running, it may execute some system code. If we break the execution by clicking on the “Cancel” button, quite often we may find ourselves outside of the application, in the system code. The value of the IP
register is 0xFFFFFFFF
in this case, and stack trace shows only system calls and a lot of 0xFFFFFFFF
. It means that IDA could not locate the current execution position inside the application. We recommend to set more breakpoints inside the application, resume the execution and interact with application by clicking on its windows, selecting menu items, etc. The same thing can occur when we step out the application.
Use “Run until return” command to return to the source-level debugging if you occasionally step into a method and the value of the IP
register becomes 0xFFFFFFFF
.
IDA considers the method frame registers, slots, and variables (v0
, v1
, …) as local variables. To see their values we have to open the "Locals" window from the "Debugger > Debugger windows > Locals" menu item.
At the moment the debugger stopped the execution at the breakpoint which we set on onCreate
method.
Perform “Step over” action (the hot key is <F8>) two times and open the "Locals" window, we will see something like the following:
If information about the frame is available (the symbol table is intact) or type guessing is enabled then IDA shows the method arguments, the method local variables with names and other non-named variables. Otherwise some variable values will not be displayed because IDA does not know their types.
Variables without type information are marked with "Bad type" in the "Locals" window. To see the variable value in this case please use the "Watch view" window and query them with an explicit type (see below).
To open the "Watch view" window select the "Debugger > Debugger windows > Watch view" menu item. In this window we can add any variable to watch its value.
note that we have to specify type of variable if it is not known. Use C-style casts:
(Object*)v0
(String)v6
(char*)v17
(int)v7
We do not need to specify the real type of an object variable, the “(Object*)” cast is enough. IDA can derive the real object type itself.
Attention! On Android API versions 23 and below an incorrect type may cause the Dalvik VM to crash. There is not much we can do about it. Our recommendation is to never cast an integer variable to an object type, the Dalvik VM usually crashes if we do that. But the integer cast “(int)” is safe in practice.
Keeping the above in the mind, do not leave the cast entries in the "Watch view" window for a long time. Delete them before any executing instruction that may change the type of the watched variable.
Overall we recommend to debug on a device that runs at least Android API 24.
Check the path to adb
in the "Debugger specific options"
Check the package and activity names
Check that the emulator is working and was registered as an adb
device. Try to restart the adb
daemon.
Check that the application was successfully installed on the emulator/device
Check the output window of IDA for any errors or warnings
Turn on more debug print in IDA with the -z50000
command line switch.
Android APIs 24 and 25 are known to return wrong instruction sizes during single stepping. Try migrating to a different Android API if you have trouble with single steps.
IDA exposes a subset of the JDWP specification as IDC commands. (Usually the name from the specification prefixed with JDWP_
).
Android APIs 23 and below crash if type guessing is enabled. Remedy this by setting the Detect Local Variable Types
option to Never
or migrate to a newer Android API.
Copyright 2020 Hex-Rays SA
This tutorial discusses optimal strategies for debugging native iOS applications with IDA Pro.
IDA Pro supports remote debugging on any iOS version since iOS 9 (including iPadOS). Debugging is generally device agnostic so it shouldn't matter which hardware you're using as long as it's running iOS. The debugger itself can be used on any desktop platform that IDA supports (Mac/Windows/Linux), although using the debugger on Mac makes more features available.
Note that IDA supports debugging on both jailbroken and non-jailbroken devices. Each environment provides its own unique challenges and advantages, and we will discuss both in detail in this writeup.
The quickest way to get started with iOS debugging is to use Xcode to install a sample app on your device, then switch to IDA to debug it.
In this example we'll be using an iPhone SE 2 with iOS 13.4 (non-jailbroken) while using IDA 7.5 SP1 on OSX 10.15 Catalina. Start by launching Xcode and use menu File>New>Project... to create a new project from one of the iOS templates, any of them will work:
After selecting a template, set the following project options:
Note the bundle identifier primer.idatest, it will be important later. For the Team option choose the team associated with your iOS Developer account, and click OK. Before building be sure to set the target device in the top left of the Xcode window:
Now launch the build in Xcode. If it succeeds then Xcode will install the app on your device automatically.
Now that we have a test app installed on our device, let's prepare to debug it. First we must ensure that the iOS debugserver is installed on the device. Since our device is not jailbroken, this is not such a trivial task. By default iOS restricts all remote access to the device, and such operations are managed by special MacOS Frameworks.
Fortunately Hex-Rays provides a solution. Download the ios_deploy utility from our download center. This is a command-line support utility that can perform critical tasks on iOS devices without requiring a jailbreak. Try running it with the listen phase. If ios_deploy can detect your device it will print a message:
Use the mount phase to install DeveloperDiskImage.dmg, which contains the debugserver:
The device itself is now ready for debugging. Now let's switch to IDA and start configuring the debugger. Load the idatest binary in IDA, Xcode likely put it somewhere in its DerivedData directory:
Then go to menu Debugger>Select debugger... and select Remote iOS Debugger:
When debugging a binary remotely, IDA must know the full path to the executable on the target device. This is another task that iOS makes surprisingly difficult. Details of the filesystem are not advertised, so we must use ios_deploy to retrieve the executable path. Use the path phase with the app's bundle ID:
NOTE: the path contains a hex string representing the application's 16-byte UUID. This id is regenerated every time you reinstall the app, so you must update the path in IDA whenever the app is updated on the device.
Now go to Debugger>Debugger options>Set specific options... and ensure the following fields are set:
Make special note of the Symbol path option. This directory contains symbol files extracted from your device. Both IDA and Xcode use these files to load symbol tables for system libraries during debugging (instead of reading the tables in process memory), which will dramatically speed up debugging.
Xcode likely already created this directory when it first connected to your device, but if not you can always use ios_deploy to create it yourself:
Also ensure that the Launch debugserver automatically option is checked. This is required for non-jailbroken devices since we have no way to launch the server manually. This option instructs IDA to establish a connection to the debugserver itself via the MacOS Frameworks, which will happen automatically at debugging start.
Lastly, Xcode might have launched the test application after installing it. Use the proclist phase to retreive the app's pid and terminate it with the kill phase:
Finally we are ready to launch the debugger. Go to main in IDA's disassembly view, use F2 to set a breakpoint, then F9 to launch the process, and wait for the process to hit our breakpoint:
You are free to single step, inspect registers, and read/write memory just like any other IDA debugger.
You can also use IDA to debug the source code of your iOS application. Let's rebuild the idatest application with the DWARF with dSYM File build setting:
Since the app is reinstalled, the executable path will change. We'll need to update the remote path in IDA:
Be sure to enable Debugger>Use source-level debugging, then launch the process. At runtime IDA will be able to load the DWARF source information:
Note that the debugserver does not provide DWARF information to IDA - instead IDA looks for dSYM bundles in the vicinity of the idb on your local filesystem. Thus if you want IDA to load DWARF info for a given module, both the module binary and its matching dSYM must be in the same directory as the idb, or in the idb's parent directory.
For example, in the case of the idatest build:
IDA was able to find the idatest binary next to idatest.i64, as well as the dSYM bundle next to the parent app directory.
If IDA can't find DWARF info on your filesystem for whatever reason, try launching IDA with the command-line option -z440010, which will enable much more verbose logging related to source-level debugging:
IDA can also be used to debug binaries that are not user applications. For example, dyld.
The ability to debug dyld is a nice advantage because it allows us to observe critical changes in the latest versions of iOS (especially regarding the shared cache) before a jailbreak is even available. We document this functionality here in the hopes it will be useful to others as well.
In this example we'll be using IDA to discover how dyld uses ARMv8.3 Pointer Authentication to perform secure symbol bindings. Start by loading the dyld binary in IDA. It is usually found here:
The target application will be a trivial helloworld program:
Compile and install this app on your device, then set the following fields in Debugger>Process options...
Under Debugger>Debugger options, enable Suspend on debugging start. This will instruct IDA to suspend the process at dyld's entry point, before it has begun binding symbols. Now launch the process with F9 - immediately the process will be suspended at __dyld_start:
Double-click on the helloworld module to bring up its symbol list and go to the _main function:
Note that function sub_1009CBF98 is the stub for puts:
The stub reads a value from off_109CC000, then performs a branch with pointer authentication. We can assume that at some point, dyld will fill off_109CC000 with an authenticated pointer to puts. Let's use IDA to quickly track down this logic in dyld.
The iOS debugger supports watchpoints. Now would be a good time to use one:
Resume the process and wait for dyld to trigger our watchpoint:
The instruction STR X21 [X19] triggered the watchpoint, and note the value in X21 (BB457A81BA95ADD8) which is the authenticated pointer to puts. Where did this value come from? We can see that X21 was previously set with MOV X21, X0 after a call to this function:
It seems like we're on the right track. Also note that IDA was able to extract a nice stack trace despite dyld's heavy use of PAC instructions to authenticate return addresses on the stack:
This leads us to the following logic in the dyld-733.6 source:
Here, fixupLoc (off_109CC00) and newValue (address of puts) are passed as the loc and target arguments for Arm64e::signPointer:
Thus, the pointer to puts is signed using its destination address in helloworld:__auth_got as salt for the signing operation. This is quite clever because the salt value is subject to ASLR and therefore cannot be guessed, but at this point the executable has already been loaded into memory – so it won’t change by the time the pointer is verified in the stub.
To see this in action, use F4 to run to the BRAA instruction in the stub and note the values of the operands:
The branch will use the operands to verify that the target address has not been modified after it was originally calculated by dyld. Since we haven't done anything malicious, one more single step should take us right to puts:
Just for fun, let's rewind the process back to the start of the stub:
Then overwrite the authenticated pointer to puts with a raw pointer to printf:
Now when we step through the stub, the BRAA instruction should detect that the authenticated pointer has been modified, and it will purposefully crash the application by setting PC to an invalid address:
Any attempt to resume execution will inevitably fail:
It seems we now have an understanding of secure symbol bindings in dyld. Fascinating!
This section discusses how to optimally debug system libraries in a dyld_shared_cache.
NOTE: full support for dyld_shared_cache debugging requires IDA 7.5 SP1
Debugging iOS system libraries is a challenge because the code is only available in the dyld cache. IDA allows you to load a library directly from the cache, but this has its own complications. A single module typically requires loading several other modules before the analysis becomes useful. Fortunately IDA is aware of these annoyances and allows you to debug such code with minimal effort.
To start, consider the following sample application that uses the CryptoTokenKit framework:
Assume this program has been compiled and installed on the device as ctk.app.
Instead of debugging the test application, let's try debugging the CryptoTokenKit framework itself - focusing specifically on the -[TKTokenWatcher init] method.
First we'll need access to the dyldcache that contains the CryptoTokenKit framework. The best way to obtain the cache is to extract it from the ipsw package for your device/iOS version. This ensures that you are working with the original untouched cache that was installed on your device.
When opening the cache in IDA, choose the load option Apple DYLD cache for arm64e (single module) and select the CryptoTokenKit module:
Wait for IDA to finish the initial analysis of CryptoTokenKit. Immediately we might notice that the analysis suffers because of references to unloaded code. Most notably many Objective-C methods are missing a prototype, which is unusual:
However this is expected. Modern dyld caches store all Objective-C class names and method selectors inside the libobjc module. Objective-C analysis is practically useless without these strings, so we must load the libobjc module to access them. Since a vast majority of modules depend on libobjc in such a way, it is a good idea to automate this in a script.
For a quick fix, save the following idapython code as init.py:
Then reopen the cache with:
This will tell IDA to load libobjc immediately after the database is created, then perform the Objective-C analysis once all critical info is in the database. This should make the initial analysis acceptable in most cases. In the case of CryptoTokenKit, we see that the Objective-C prototypes are now correct:
Now let's go to the -[TKTokenWatcher init] method invoked by the ctk application:
If we right-click on the unmapped address 0x1B271C01C, IDA provides two options in the context menu:
In this case the better option is Load ProVideo:__auth_stubs, which loads only the stubs from the module and properly resolves the names:
This is a common pattern in the latest arm64e dyldcaches, and it is quite convenient for us. Loading a handful of __auth_stubs sections is enough to resolve most of the calls in CryptoTokenKit, which gives us some nice analysis for -[TKTokenWatcher init] and its helper method:
Now that the static analysis is on par with a typical iOS binary, let's combine it with dynamic analysis. We can debug this database by setting the following options in Debugger>Process options:
Here we set the Input file field to the full path of the CryptoTokenKit module. This allows IDA to easily detect the dyldcache slide at runtime. When CryptoTokenKit is loaded into the process, IDA will compare its runtime load address to the imagebase in the current idb, then rebase the database accordingly.
By default the imagebase in the idb corresponds to the first module that was loaded:
Thus, it is easiest to set Input file to the module corresponding to the default imagebase.
Note however that we could also use this configuration:
Provided that we update the imagebase in the idb to the base of the libobjc module:
This will result in the same dyld slide and should work just as well, because the the imagebase and the Input file field both correspond to the same module. This is something to keep in mind when debugging dyldcache idbs that contain multiple libraries.
Now let's try launching the debugger. Set a breakpoint at -[TKTokenWatcher initWithClient:], use F9 to launch the process, then wait for our breakpoint to be hit:
IDA was able to map our database (including CryptoTokenKit, libobjc, and the satellite __auth_stubs sections) into process memory. We can single step, resume, inspect registers, and perform any other operation that is typical of an IDA debugging session.
Note that after terminating the debugging session you can continue to load new modules from the cache. If a dyld slide has been applied to the database, new modules will be correctly loaded into the rebased address space. This did not work in previous versions of IDA.
For example, after a debugging session we might notice some more unresolved calls:
IDA is aware that the address space has shifted, and it will load the new code at the correct address:
You are free to load new modules and relaunch debugging sessions indefinitely.
The previous examples used custom applications to demonstrate IDA's debugging capabilities. In this case IDA can utilize the debugserver included in Apple's iOS developer tools, but there are situations in which this server is not sufficient for our needs.
The debugserver will refuse to debug any application that we didn't build ourselves. To demonstrate this, try launching IDA with an empty database and use Debugger>Attach>Remote iOS Debugger to attach to one of the system daemons:
You will likely get this error message:
It is possible to install a custom version of the debugserver that can debug system processes, but this requires a jailbroken device. We document the necessary steps and IDA configuration here. The device used in this example is an iPhone 8 with iOS 13.2.2, jailbroken with checkra1n 0.10.1.
First we must obtain a copy of the debugserver binary from the DeveloperDiskImage.dmg:
Now save the following xml as entitlements.plist:
Then use ldid to codesign the server:
This will grant the debugserver permission to debug any application, including system apps. Now we can copy the server to the device and run it:
Note that we specified 192.168.1.7 which is the IP of the host machine used in this example. Be sure to replace this with the IP of your host so that the server will accept incoming connections from IDA.
To enable debugging with the patched debugserver, set the following options in dbg_ios.cfg:
We're now ready to open a binary in IDA and debug it. Copy the itunesstored binary from your device, it is typically found here:
After loading the binary use Debugger>Select debugger and choose Remote iOS Debugger, then under Debugger>Process options set the following fields:
Since we set AUTOLAUNCH = NO, IDA now provides the Hostname and Port fields so we can specify how to connect to our patched debugserver instance.
Now use Debugger>Attach to process and choose itunesstored from the process list. Since we have modified the debugserver it should agree to debug the target process, allowing IDA to create a typically robust debugging environment:
Note that although we're not using the debugserver from DeveloperDiskImage.dmg, IDA still depends on other developer tools to query the process list. We discuss how to install the DeveloperDiskImage in the Getting Started section above, but for a quick workaround you can always just specify the PID manually:
Now that we've successfully attached to a system process, let's do something interesting with it. Consider the method -[PurchaseOperation initWithPurchase:]. This logic seems to be invoked when a transaction is performed in the AppStore. Set a breakpoint at this method, then open the AppStore on your device and try downloading an app (it can be any app, even a free one).
Immediately our breakpoint is hit, and we can start unwinding the logic that brought us here:
Stepping through this function, we see many Objective-C method call sites:
Instead of using F7 to step into the _objc_msgSend function, we can use shortcut Shift-O to take us directly to the Objective-C method that is being invoked:
We discuss the Shift-O action in detail in our mac debugger primer, but it is worth demonstrating that this action works just as well in arm64/iOS environments.
It seems that we're well on our way to reverse-engineering transactions in the AppStore. The remaining work is left as an exercise for the reader :)
Hopefully by now we've shown that IDA's iOS Debugger is quite versatile. It can play by Apple's rules when debugging on a non-jailbroken device, and it can also be configured to use an enhanced debugserver when a jailbreak is available.
Also keep in mind that all previous examples in this writeup should work equally well with the patched debugserver. We encourage you to go back and try them.
\
IDA uses the Remote GDB Protocol to communicate with the iOS debugserver. Thus, the best way to diagnose possible issues is to log the packets transmitted between IDA and the server. You can do this by running IDA with the -z10000 command-line option:
Often times these packets contain messages or error codes that provide clues to the issue.
For more enhanced troubleshooting, you can also enable logging on the server side. Go to Debugger>Debugger options>Set specific options and set the Syslog flags field:
This will instruct the debugserver to log details about the debugging session to the iOS system log (all valid flags are documented under the SYSLOG_FLAGS option in dbg_ios.cfg).
Start collecting the iOS system log with:
Then launch the debugger. Now both the client (/tmp/ida.log) and the server (/tmp/sys.log) will log important events in the debugger session, which will often times reveal the issue.`
This tutorial replaces the old iOS debugging tutorial, which is available here.
We will now demonstrate how to debug the kernel through a virtual machine.
In this example we will be using VMware Workstation 15 Player and Windows 7.
Run the VM and use the bcedit
to configure the boot menu as stated in the article.
Edit the VM hardware settings and add a new serial port with option use named pipe
:
Restart the VM to debug. At the boot prompt, select the menu item containing [debugger enabled]
from the boot menu.
The connection string com:port=\\.\pipe\com_2,baud=115200,pipe,reconnect
for Windbg plugin should refer to the named pipe we set up in the previous steps.
Start IDA Pro with an empty database:
Select the Windbg debugger using "Debugger > Select debugger":
Then configure it to use “Kernel mode debugging” debugging in the “Debugger specific options” dialog:
After the debugger is properly configured, edit the process options and set the connection string:
Finally, start debugging using "Debugger > Attach to process":
IDA Pro may display a wait box "Refreshing module list" for some time. Then it will display something like this:
The simplest way to start WinDbg Plugin is to run IDA Pro with the following option:
{MODE=1}
means "Kernel mode"
+0
means the "<Kernel>" process
In kernel mode IDA Pro will display one entry in the threads window for each processor.
For example a two processor yields:
This screenshot shows how we are debugging the kernel and changing the disassembly listing (renaming stack variables, or using structure offsets):
At the end you can detach from the kernel and resume it or detach from the kernel and keep it suspended.
To detach and resume, simply select the “Debugger > Detach from process”, however to detach and keep the kernel suspended select “Debugger > Terminate Process”.
In some cases, when debugging a 64bit kernel using a 1394 cable then 64bit drivers are needed, thus dbgeng (32bits) will not work. To workaround this problem we need to run the kernel debugger server from the x64 debugging tools folder and connect to it:
Go to “Debugging Tools (x64)” installation
Run kdsrv.exe (change the port number/transport appropriately):
kdsrv -t tcp:port=6000
Now run ida64 and specify the following connection string (change the transport value appropriately):
kdsrv:server=@{tcp:port=6000,server=127.0.0.1},trans=@{com:port=\\.\pipe\com_3,baud=115200,pipe}
Check the tutorial about debuggind Windows apps with IDA Bochs:
This guide illustrates how to configure the Bochs debugger plugin under Linux/MacOS. Downloading and compiling Bochs Please download the Bochs source code tarball and extract it.
Run the 'configure' script (it is possible to pass other switches) and make sure that the switches marked in bold are present:
Note: under MacOS Lion 10.7.3 use the following switches:
For a complete installation guide please check: http://bochs.sourceforge.net/doc/docbook/user/compiling.html. Now run "make" and "make install". Then type "whereis bochs" to get something like:
After installing Bochs, run IDA Pro and open a Windows PE file and select 'Debugger -> switch debugger' and select "Local Bochs Debugger": If a PE file was loaded, then the Bochs debugger plugin will operate in "PE mode":
In case the other two modes (IDB or Disk Image mode) are used then there is no need to specify any additional configurations options, otherwise please continue reading this guide. Before launching the debugger with F9, the Bochs debugger plugin needs to know where to find the MS Windows DLLs and which environment variables to use. Attempting to run the debugger without configuring it may result in errors like this:
Here is a basic list of DLLs that are needed by most programs: • advapi32.dll • comctl32.dll • comdlg32.dll • gdi32.dll • kernel32.dll • msvcrt.dll • mswsock.dll • ntdll.dll • ntoskrnl.exe • shell32.dll • shlwapi.dll • urlmon.dll • user32.dll • wininet.dll • ws2_32.dll • wsock32.dll Let us create a directory in $HOME/bochs_windir/ and place those DLLs there. Specifying the Windows DLL path and environment variables using the startup file The startup file is a script file found in idadir\plugins\bochs directory. If IDC was the currently active language then startup.idc is used, otherwise startup.ext (where ext is the extension used by the currently selected extlang). In this tutorial we will be working with IDC, so we will edit the startup.idc file. (Please note that changes to this file will affect all databases. For local changes (database specific configuration) take a copy of the startup script file and place it in the same directory as the database then modify it). It is possible to specify a path map for a complete directory, for example:
This line means that /home/lallous/bochs_windir/* will be visible to the debugged program as c:\windows\system32* (for example /home/lallous/bochs_windir/ntdll.dll will be visible as c:\windows\system32\ntdll.dll)
If all DLLs referenced by the program are in the bochs_windir directory, then running the process again should work: (Bochs has already started and IDA switched to debugging mode.) There are two things that should be configured. Press “.” to switch to the output window (or use the Debugger / Modules list window to inspect the modules list):
Now, after we run the program again we should get a more correct module list:
It is equally important to specify some environment variables. We will use the env keyword to define all the environment variables:
Specifying the Windows DLL path and environment variables using environment variables An alternative way of configuring the DLLs path and environment variables is to use the IDABXPATHMAP and the IDABXENVMAP environment variables. To specify the path map, export the following environment variable:
(Please note that the forward slash (/) will be replaced with a backslash automatically by the plugin) Similarly, specify the environment variables with the IDABXENVMAP environment variable:
(Please note that we used the ++ to separate between multiple variables)
In case you require to do specific changes (per database) to the startup file then please take a copy of it and place it in the same directory as the database. Refer to the help IDA Pro help file for more information.
The PIN tracer is a remote debugger plugin used to record execution traces. It allows to record traces on Linux and Windows (x86 and x86_64) from any of the supported IDA platforms (Windows, Linux and MacOSX). Support for MacOSX targets is not yet available.
Recording traces on MacOSX target is not supported yet.
However, it’s possible to record traces from a Linux or Windows target using the MacOSX version of IDA.
Before using the PIN tracer the PIN tool module (distributed only in source code form) must be built as the Intel PIN license disallows redistributing PIN tools in binary form.
the PIN tools are a little sensitive to spaces in paths. Therefore, we recommend unpacking in a no-space path. E.g., "C:\pin", but not "C:\Program Files (x86)\.
The building process of the PIN tool is different for Windows and Linux.
Install Visual Studio. It is possible to build the PIN tool with the Express version of Visual Studio for C++.
pintool 6.9 and higher should be built with PIN version 3.0 and higher, for earlier versions of pintool you should use PIN build 65163.
Unpack the .zip file into /path/to/pin/source/tools/
Open /path/to/pin/source/tools/idapin/IDADBG.sln in Visual Studio, select the correct build configuration (either Win32 or x64) and build the solution.
Alternatively you can use GNU make:
Install GNU make as a part of cygwin or MinGW package
Unpack the .zip file into /path/to/pin/source/tools/
Prepare Visual Studio environment (e.g. %VCINSTALLDIR%\Auxiliary\Build\vcvars32.bat for 32-bit pintool or %VCINSTALLDIR%\Auxiliary\Build\vcvars64.bat for 64-bit one)
cd /path/to/pin/source/tools/idapin
make
Install GCC 3.4 or later
Unpack the .zip file into /path/to/pin/source/tools/
Open a console, and do the following (only for versions of PIN prior to 3.0):
cd /path/to/pin/ia32/runtime
ln -s libelf.so.0.8.13 libelf.so
cd /path/to/pin/intel64/runtime
ln -s libelf.so.0.8.13 libelf.so
cd /path/to/pin/source/tools/Utils
ls testGccVersion 2>/dev/null || ln -s ../testGccVersion testGccVersion
cd /path/to/pin/source/tools/idapin
for building the x86 version, or
for the x64 version.
Pintool 6.9 and higher are compatible with versions 6.5-6.8 of IDA so currently you can use them.
Once the PIN tool module is built we can use it in IDA. Open a binary in IDA and wait for the initial analysis to finish. When it’s done select the PIN tracer module from the debuggers drop down list or via Debugger > Select debugger:
After selecting the PIN tracer module select the menu Debugger > Debugger options > Set specific options. The following new dialog will be displayed:
In this dialog at least the following options are mandatory:
PIN executable: This is the full path to the PIN binary (including the “pin.exe” or “pin” file name). In some versions “pin.sh” may exist – in this case you should use it.
Directory with idadbg: This is the directory where the idadbg.so or idadbg.dll PIN tool resides. Please note that only the directory must be specified.
Fill the form with the correct paths and press OK in this dialog and enable option Autolaunch PIN for localhost.
We can interact with the PIN tracer like with any other debugger module: add breakpoints and step into or step over functions by pressing F7 or F8 alternatively.
Now we put a breakpoint in the very first instruction of function main
and launch the debugger by pressing the F9 key or by clicking the Start button in the debugger toolbar.
Make several steps by pressing F8. We can see all the instructions that were executed changed their color:
Now let the application run and finish by pressing F9 again. After a while the process will terminate and IDA will display a dialog telling us that is reading the recorded trace. Once IDA reads the trace the debugger will stop and the instructions executed will be highlighted (like with the built-in tracing engine) as in the following picture:
We can see in the graph view mode the complete path the application took in some specific function by switching to the graph view, pressing space bar and then pressing “w” to zoom out:
Instead of launching a new process we could attach to a running process and debug it. For that we could have selected the "Debugger > Attach to process…" menu item. IDA will display a list of active processes.
We just select the process we want to attach to. IDA will then attach to the selected process, and leave it suspended at the place it was when it was attached to:
In case of remote debugging you can run IDA and PIN backend on different platforms.
The first thing to do, is to start the PIN debugging backend on the target machine. Command line depends of bitness of the target application.
For example, a 64-bit application ls would be started for debugging by the following comand:
whereas a 32-bit one hello32 as follows:
there is a more complicated way to start an application regardless bitness:
Also you can attach to already running programs:
For example:
The next step is to select PIN tracer module in IDA via Debugger > Select debugger and switch IDA to remote PIN backend. For this you should disable option Autolaunch PIN for localhost in the PIN options dialod (Debugger > Debugger options > Set specific options):
and then tell IDA about the backend endpoint, through the menu action Debugger > Process options…
Once IDA knows what host to contact (and on what port), debugging an application remotely behaves exactly the same way as if you were debugging it locally.
Quick overview:
The Windbg debugger plugin is an IDA Pro debugger plugin that uses Microsoft's debugging engine (dbgeng) that is used by Windbg, Cdb or Kd.
or from the Windows SDK / DDK package.
Please make sure you should install the x86 version of the debugging tools which is used by both IDA Pro and IDA Pro 64. The x64 version will NOT work.
After installing the debugging tools, make sure you select « Debugger / Switch Debugger » and select the WinDbg debugger.
Also make sure you specify the correct settings in the “Debugger specific options” dialog:
User mode: Select this mode for user mode application debugging (default mode)
Kernel mode: Select this mode to attach to a live kernel.
Output flags: These flags tell the debugging engine which kind of output messages to display and which to omit
Kernel mode debugging with reconnect and initial break: Select this option when debugging a kernel and when the connection string contains 'reconnect'. This option will assure that the debugger breaks as soon as possible after a reconnect.
To make these settings permanent, please edit the IDA\cfg\dbg_windbg.cfg file.
** To specify the debugging tools folde**r you may add to the PATH environment variable the location of Windbg.exe or edit %IDA%\cfg\ida.cfg and change the value of the DBGTOOLS key.
After the debugger is properly configured, edit the process options and leave the connection string value empty because we intend to debug a local user-mode application.
Now hit F9 to start debugging:
The Windbg plugin is very similar to IDA Pro's Win32 debugger plugin, nonetheless by using the former, one can benefit from the command line facilities and the extensions that ship with the debugging tools.
For example, one can type “!chain” to see the registered Windbg extensions:
“!gle” is another command to get the last error value of a given Win32 API call.
Another benefit of using the Windbg debugger plugin is the use of symbolic information.
Normally, if the debugging symbols path is not set, then the module window will only show the exported names. For example kernel32.dll displays 1359 names:
Let us configure a symbol source by adding this environment variable before running IDA:
set _NT_SYMBOL_PATH=srv*C:\Temp\pdb*http://msdl.microsoft.com/download/symbols
It is also possible to set the symbol path directly while debugging:
and then typing “.reload /f” to reload the symbols.
Now we try again and notice that more symbol names are retrieved from kernel32.dll:
Now we have 5818 symbols instead!
It is also possible to use the “x” command to quickly search for symbols:
(Looking for any symbol in any module that contains the word “continue”)
We have seen how to debug a local user mode program, now let us see how to debug a remote process.
First let us assume that “pcA” is the target machine (where we will run the debugger server and the debugged program) and “pcB” is the machine where IDA Pro and the debugging tools are installed.
To start a remote process:
On “pcA”, type:
dbgsrv -t tcp:port=5000
(change the port number as needed)
On “pcB”, setup IDA Pro and Windbg debugger plugin:
“Application/Input file”: these should contain a path to the debuggee residing in “pcA”
Connection string: tcp:port=5000,server=pcA
Now run the program and debug it remotely.
To attach to a remote process, use the same steps to setup “pcA” and use the same connection string when attaching to the process.
More about connection strings and different protocols (other than TCP/IP) can be found in “debugger.chm” in the debugging tools folder.
We will now demonstrate how to debug the kernel through a virtual machine.
In this example we will be using VMWare 6.5 and Windows XP SP3.
Configuring the virtual machine:
Run the VM and then edit “c:\boot.ini” file and add one more entry (see in bold):
[operating systems]
multi(0)disk(0)rdisk(0)partition(1)\WINDOWS="Microsoft Windows XP Professional" /noexecute=optin /fastdetect multi(0)disk(0)rdisk(0)partition(1)\WINDOWS="Local debug" /noexecute=optin /fastdetect /debug /debugport=com1 /baudrate=115200
Actually the last line is just a copy of the first line but we added the “/debug” switch and some configuration values.
Now shutdown the virtual machine and edit its hardware settings and add a new serial port with option “use named pipes”:
Press “Finish” and start the VM. At the boot prompt, select “Local debug” from the boot menu:
Configuring Windbg debugger plugin:
Now run IDA Pro and select Debugger / Attach / Windbg
Then configure it to use “Kernel mode” debugging and use the following connection string:
com:port=\.\pipe\com_1,baud=115200,pipe
It is possible to use the 'reconnect' keyword in the connection string:
com:port=\.\pipe\com_1,baud=115200,pipe,reconnect
Also make sure the appropriate option is selected from the debugger specific options.
Please note that the connection string (in step 1) refers to the named pipe we set up in the previous steps.
Finally, press OK to attach and start debugging.
When IDA attaches successfully, it will display something like this:
If you do not see named labels then try checking your debugging symbols settings.
Note: In kernel mode IDA Pro will display one entry in the threads window for each processor.
For example a two processor configuration yields:
This screenshot shows how we are debugging the kernel and changing the disassembly listing (renaming stack variables, or using structure offsets):
At the end you can detach from the kernel and resume it or detach from the kernel and keep it suspended.
To detach and resume, simply select the “Debugger / Detach”, however to detach and keep the kernel suspended select “Debugger / Terminate Process”.
Debugging the kernel through kdsrv.exe
In some cases, when debugging a 64bit kernel using a 1394 cable then 64bit drivers are needed, thus dbgeng (32bits) will not work. To workaround this problem we need to run the kernel debugger server from the x64 debugging tools folder and connect to it:
Go to “Debugging Tools (x64)” installation
Run kdsrv.exe (change the port number/transport appropriately):
kdsrv -t tcp:port=6000
Now run ida64 and specify the following connection string (change the transport value appropriately):
kdsrv:server=@{tcp:port=6000,server=127.0.0.1},trans=@{com:port=\.\pipe\com_3,baud=115200,pipe}
Current versions of VMWare Workstation include a GDB stub for remote debugging of the virtual machines running inside it. In version 5.4, IDA includes a debugger module which supports the remote GDB protocol. This document describes how to use it with VMWare. As an example, we'll debug a Linux kernel.
Let's assume that you already have a VM with Linux installed. Before starting the debugging, we will copy symbols for the kernel for easier navigation later. Copy either /proc/kallsyms or /boot/Sytem.map* file from the VM to host.
Now edit the VM's .vmx file to enable GDB debugger stub:
debugStub.listen.guest32 = "TRUE"
debugStub.hideBreakpoints= "TRUE"
monitor.debugOnStartGuest32 = "TRUE"
Save the file.
In VMWare, click "Power on this virtual machine" or click the green Play button on the toolbar.
A black screen is displayed since VMWare is waiting for a debugger to connect.
Start IDA.
If you get the welcome dialog, choose "Go".
Choose Debugger | Attach | Remote GDB debugger.
Enter "localhost" for hostname and 8832 for the port number.
Choose <attach to the process started on target> and click OK.
We land in the BIOS, but since we're not interested in debugging it, we can skip directly to the kernel. Inspect the kallsyms or System.map file you downloaded from the guest and search for the start_kernel symbol:
Press F2 or choose "Add breakpoint" from the context menu.
Press F9. You will see loading messages and then the execution will stop at the entrypoint.
Symbols are very useful during debugging, and we can use the kallsyms or System.map file to add them to IDA. Go to File | Python command... and paste the following short script (don't forget to edit the file path):
Click OK and wait a bit until it finishes. After that you should see the symbols in the disassembly and name list:
Happy debugging!
Copyright 2009 Hex-Rays SA
Use this path for the fields in Debugger>Process options...
\
It is highly recommended to read the article
First of all download PIN from , and unpack it on your hard drive.
Download the IDA pintool sources from: (*)
Download the IDA pintool sources from: (*)
(*) Where '$(IDAMAJMIN)' is the IDA version major/minor. E.g., for IDA 7.6, the final URL would be:
To get started, you need to install the latest Debugging Tools from Microsoft website:
Non Invasive debugging: Select this mode to attach to a process
For MS Windows Vista please see:
Add these lines to the file:
Copy the address, and navigate to it in IDA (Jump | Jump to addres... or just "g").
Check "Hardware breakpoint" and select "Execute" in "Modes". Click OK.
ksyms = open(r"") # path to the kallsyms/map file for line in ksyms: if line[9]=='A': continue # skip absolute symbols addr = int(line[:8], 16) name = line[11:-1] if name[-1]==']': continue # skip module symbols idaapi.set_debug_name(addr, name) MakeNameEx(addr, name, SN_NOWARN) Message("%08X: %s\n"%(addr, name))