API Reverse Engineering
Decompiling Mobile Binaries to Extract Hidden API Endpoints
Discover how to use JADX and Apktool to reverse Android and iOS binaries, revealing hardcoded URLs, API keys, and internal routing logic hidden in the source code.
In this article
The Android Deconstruction Pipeline
Android applications are distributed as APK or AAB files, which are essentially specialized ZIP archives. These archives contain the compiled bytecode in DEX files, along with resources and a manifest. To understand the API interaction, we must decompress these assets and convert the bytecode into a navigable format.
Apktool is the industry standard for unpacking the manifest and resource files. It decodes the binary XML files into a readable format and converts DEX files into Smali code. Smali is an intermediate representation that is useful for patching but difficult for high-level logic analysis.
For a higher-level view, JADX is the preferred tool for converting DEX files back into Java source code. It provides a searchable interface that allows developers to jump between class definitions and method calls. This makes it significantly easier to trace the flow of data from a user interface component to a network request.
- Apktool: Best for resource analysis and binary patching to bypass security controls.
- JADX: Best for understanding business logic and mapping internal API routing.
- Ghidra: Used for analyzing native libraries written in C or C++ via the Java Native Interface.
Reconstructing the Networking Layer
Most modern Android applications use the Retrofit library for managing API interactions. Retrofit uses Java interfaces to define endpoints, which makes them very easy to identify during static analysis. These interfaces typically contain annotations that reveal the HTTP method, the path, and the required parameters.
When searching the decompiled source, looking for the @GET or @POST annotations is the fastest way to map the API surface. Even if the code is obfuscated, these annotations often remain intact to allow the library to function. This provides a clear list of all server-side functions the app is capable of calling.
1// This is an example of what a reversed Retrofit interface looks like in JADX
2public interface UserApiService {
3 @POST("api/v2/auth/login")
4 @Headers({"X-Platform: Android", "Content-Type: application/json"})
5 Call<LoginResponse> authenticateUser(@Body LoginRequest request);
6
7 @GET("api/v2/user/profile/{uid}")
8 Call<UserProfile> getUserData(@Path("uid") String userId, @Header("Authorization") String token);
9
10 @DELETE("api/v2/internal/debug_cleanup")
11 Call<Void> triggerInternalCleanup(); // This endpoint might not be visible in a proxy
12}The iOS Perspective and Mach-O Analysis
Reversing iOS applications presents different challenges because they are compiled to native machine code for ARM64 architectures. Unlike the intermediate bytecode of Android, iOS binaries must be analyzed using disassemblers. This requires a deeper understanding of assembly language and the Objective-C or Swift runtime.
The first step is often decrypting the application binary, as apps downloaded from the App Store are encrypted with FairPlay DRM. Tools like Clutch or Frida-ios-dump are used on jailbroken devices to dump the decrypted Mach-O file. Once decrypted, the binary can be loaded into tools like Hopper Disassembler or Ghidra.
iOS apps written in Objective-C are particularly verbose in their metadata. Even in compiled form, the binary contains the names of classes and methods. This allows researchers to search for terms like API, Client, or Network to find the logic responsible for communicating with the backend.
Analyzing Swift Binaries
Swift is more difficult to reverse than Objective-C because it does not rely as heavily on dynamic message passing. However, Swift still leaves clues in the form of name mangling and metadata. Dedicated Swift demanglers can help turn complex symbols back into readable function signatures.
Focusing on the imports is a highly effective strategy for iOS. If an application imports the Network or Foundation frameworks, you can set breakpoints on common networking functions like URLSession. This allows you to inspect the arguments and discover the endpoints being targeted during execution.
