Quickstart
In its minimal form, APK Patcher can be used to extract and repackage an application with your own signature, without making any modifications. To do this, use the following command:
apkpatcher -a <apk> -s <sdktools> -b <version>
Where:
<apk> refers to the APK file of the application you want to patch.
<sdktools> is the directory containing the Android SDK tools. For detailed instructions on how to install the SDK tools, refer to this step-by-step tutorial (https://madsquirrels.gitlab.io/mobile/asthook/how.install.html#setup-sdktools).
<version> is the version of the Build Tools that was installed along with the Android SDK tools.
With this minimal version, you can patch an application without altering its behavior. If you’re interested in making further modifications, such as changing the app’s functionality, continue following the next steps in the tutorial.
Handle splits applications
Some applications are provisioned in multiple parts, known as splits. Each split is an APK file that is individually signed. These splits must be signed with the same key as the main application. To handle this, APK Patcher supports the -m parameter, allowing you to include one or more split APKs that will be signed simultaneously with the patched base application.
Here’s an example of how to use this feature:
apkpatcher -a base.apk -m split_config.arm64_v8a.apk split_config.en.apk split_config.xxhdpi.apk -b 30.0.3 -s /opt/android-sdk/ -o output.apk
In this example:
The base application (base.apk) will be patched and signed.
The splits (split_config.arm64_v8a.apk, split_config.en.apk, split_config.xxhdpi.apk) will also be signed with the same key.
The patched base APK will be saved as output.apk, while the split APKs will retain their original names but will have a postfix: _new_signed.apk.
To install the newly patched application and its corresponding splits, use the following adb command:
adb install-multiple base_patched.apk split_config.arm64_v8a_new_signed.apk split_config.en_new_signed.apk split_config.xxhdpi_new_signed.apk
This ensures that both the base APK and its associated splits are properly installed on the device.
Inject a library ie: frida-gadget
The -g option allows you to inject a library at the launch of an application. One of the most well-known examples of library injection is Frida, which enables powerful debugging and analysis without requiring privileged access (root) to the device. By simply injecting Frida into the application, it will run within the app’s context.
Frida is a dynamic instrumentation toolkit that is widely used to analyze Android applications for several reasons:
Frida allows you to hook into an application at runtime, which means you can inspect or modify the app’s behavior as it is running. This is extremely useful when you want to:
Bypass security mechanisms (e.g., root detection, SSL pinning).
Inspect function calls, methods, and parameters in real-time.
Modify the app’s internal logic dynamically without modifying its source code or binary.
To inject a library, you need to download the appropriate Frida Gadget file, which follows the naming convention:
frida-gadget-<version>-android-<arch>.so
You can then pass it as an argument to APK Patcher using the -g option. If the -a option for specifying architecture is not provided, the base name of the library must be common across all architectures and end with _<arch>.so, where <arch> denotes the architecture. For example, if you use -g gadget.so, the library paths must be:
gadget_x86_64.so
gadget_arm.so
gadget_arm64.so
gadget_x86.so
If you need to inject Frida or another gadget, it’s recommended to specify the architecture directly using -a <arch>, along with -g <gadget.so>, where gadget.so is the exact path to the library for the selected architecture.
Inject a Proxy certificate
Certificate injection simplifies the setup of a proxy for monitoring network connections between an application and its server, especially when the connections are encrypted. There are two methods to achieve this:
- Allow User Certificates:
You can configure the application to accept user-installed certificates. Once this is done, simply install the proxy certificate on the device as a user certificate. To use this method, apply the -e option alone. This method does not modify the APK’s internal certificate handling but allows for easier monitoring.
- Embed Certificate in the Application:
Alternatively, you can directly inject the certificate into the application itself. For this, use the -c option, specifying the certificate file in PEM or DER format. This method embeds the certificate within the APK, enabling network monitoring without the need to manually install certificates on the device.
Put a pause before continue the rebuid
Sometimes, manual actions may be required before repackaging the application. In such cases, you can use the -p option. This option pauses the process just before repackaging, allowing the user to perform any necessary manual operations.
Once the modifications are done, you can simply press Enter to continue with the packaging process.
This feature is particularly useful if you need to manually edit resources, Smali code, or any other files within the APK before completing the patching.
Use an external script before repackaging
- In a similar way, you can automate manual tasks by using a script with the –plugin option.
This allows you to execute predefined tasks automatically, such as modifying Smali code or editing AXML files.
For example, you can use a plugin to modify the AndroidManifest.xml file before repackaging. Here’s an example of how to modify the manifest using a plugin: Example of Command to Run the Plugin:
apkpatcher -a <base.apk> -s <sdktools> -b <android_version> --plugin pyaxml_script.py
Plugin Code Example:
#!/usr/bin/env python
import pyaxml
import click
@click.command()
@click.argument('input_dir')
def exploit_axmlfile(input_dir):
path_manifest = input_dir + "/AndroidManifest.xml"
axml_object, _ = pyaxml.axml.AXML.from_axml(open(path_manifest, "rb").read())
# Convert the AXML to regular XML
xml = axml_object.to_xml()
# Define the Android namespace for the "name" attribute
android_name = "{http://schemas.android.com/apk/res/android}name"
# Find the activity with name 'activity_name' and modify it
for activity in xml.findall(f"./application/activity/[@{android_name}='activity_name']"):
activity.attrib[android_name] = "new_activity_name"
# Re-encode the XML back to AXML
axml_object = pyaxml.axml.AXML()
axml_object.from_xml(xml)
# Write the modified AXML back to the manifest file
open(path_manifest, "wb").write(axml_object.pack())
Explanation:
The input_dir is the directory where the application is unpacked.
The script opens the AndroidManifest.xml file, reads it using pyaxml, and converts it into a standard XML format.
It then searches for an activity named “activity_name” and replaces it with “new_activity_name”.
Finally, the modified XML is re-encoded back into AXML format and written back to the manifest file.
By automating tasks like these with the –plugin option, you can streamline repetitive actions such as modifying Smali code or AXML files without the need for manual intervention.
Special case when apkpatcher don’t find ENTRYPOINT
In rare cases, APK Patcher may not be able to automatically identify the desired entry point for injecting a library. If you need to specify a different entry point for library injection, you can use the –entrypoint option to indicate the exact Smali function where you want the library to be injected.
Usage Example:
apkpatcher -a <base.apk> -g <gadget.so> --entrypoint <smali_function> -b <android_version> -s <sdktools> -o <output.apk>
Explanation:
<base.apk> is the APK file you want to patch.
<gadget.so> is the path to the library you want to inject.
<smali_function> is the name of the Smali function where you want the library to be injected.
<android_version> is the version of the Android Build Tools.
<sdktools> is the path to the Android SDK tools.
<output.apk> is the name of the resulting patched APK.
By specifying the –entrypoint option, you can precisely control where the library injection occurs, ensuring that it integrates seamlessly with your application’s execution flow.
Apkpatcher as library
All of the previously mentioned options can also be utilized programmatically by integrating APK Patcher as a Python library. You need to import apkpatcher and instantiate the Patcher object with the required parameters: the APK file, the path to SDK tools, and the version of the Android Build Tools. Each of the previously discussed options is available through corresponding API functions.
Once you’ve set up the required parameters, you can invoke the patching function to apply the modifications. Below is a minimalist example:
import apkpatcher
# Instantiate the Patcher object with the necessary parameters
patcher = apkpatcher.Patcher(apk, sdktools, version_android)
# Perform the patching process, specifying the output file
patcher.patching(None, output_file=output_file)
In this example:
apk is the APK file you wish to patch.
sdktools is the directory containing Android SDK tools.
version_android is the version of the Build Tools used during patching.
output_file is the name of the resulting patched APK.
Smali Code Patching
A new feature has been added to apkpatcher that allows users to modify Smali code by replacing specific methods within an APK. This can be particularly useful for scenarios such as bypassing SSL pinning or application signature verification during security audits. Instead of using tools like Frida scripts, you can directly patch the application’s behavior by modifying the Smali code.
Example: Replacing a Method
The following example shows how to replace a method in the Smali code of an APK: Define the method prototype and the new code to replace the original method:
from apkpatcher.smalipatching import Method, get_smali_file_from_class, replace_methods
prototype = ".method public setEndColor(I)V"
patch = "return-void"
m = Method(prototype, patch)
directory = "/tmp/app/apktools/"
classname = "com.github.mikephil.charting.model.GradientColor"
fname = get_smali_file_from_class(directory, classname)
with open(f"{directory}{fname}", "r") as f:
content = f.read()
with open(f"{directory}{fname}", "w") as f:
text = apkpatcher.smalipatching.replace_methods([m], content)
content = f.write(text)
In this example:
prototype specifies the method you want to replace.
The method signature includes its visibility (public), name (setEndColor), and its argument types ((I) for an integer argument), and return type (V for void).
patch contains the Smali code that will replace the method’s original implementation. In this case, the method will be replaced with a simple return-void, which effectively removes its functionality.
If you know the class but not the exact location of the Smali file, you can use the following helper function (get_smali_file_from_class) to find the file. - directory is the folder where the APK files were extracted using apktools. - classname is the full name of the class, for example: com.github.mikephil.charting.model.GradientColor.
Once you have the Smali file, read its contents.
Replace the method in the Smali file with the patch.
In this step, replace_methods will take a list of method replacements (in this case, just m) and apply them to the file content. The modified Smali code will be stored in the text variable, ready to be written back to the APK.
Use Cases
Bypassing SSL Pinning: Modify methods responsible for SSL verification and replace them with return-void to bypass SSL pinning mechanisms.
Disabling Signature Verification: Replace methods that enforce signature checks to disable signature verification during security audits.
Custom Behavioral Modifications: Adjust the behavior of an app by altering specific methods, enabling more flexibility in testing and debugging.
This functionality is designed to streamline the process of Smali code manipulation, providing an efficient alternative to runtime instrumentation techniques like Frida.