# Getting Started

```java
 <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
```

This activity defines Mainactivity class to launch on startup, `.`here signifies it is in same package.

It is considered main entry point with class launcher(launch this mainactivity class on startup).

Exported activities are activities that can be started by an app or adb.

### ADB (Android Debug Bridge)

1. `adb shell` drops us on android linux environment.
2. `adb push your_path android_path`to transfer files from your machine to android.
3. `adb pull android_path your_path`to fetch files from android machine to yours.
4. `adb install path_of_apk`to install apk on android machine.
5. `adb shell pm list packages`to list all installed packages on device.
   1. Note: `pm`is package manager, `shell`allows us to run commands as if we were inside device shell.
6. `adb shell pm list packages -3`to list only 3rd part apps.
7. `adb shell pm clear package_name`to clear all data of an app in phone but not uninstall it.
8. `adb shell dumpsys package package_name`to dump information about a package like permissions, activities exported, apk path etc.
9. `adb shell am start package_name/.activityname`to start an activity.
   1. Note: `am` is activity manager.
10. `adb uninstall package_name`to uninstall the app.
11. `adb shell pm path package_name`to get full path of apk on android file system.
12. `adb shell pm list packages -3 -f` to get path of apk in same command.

#### If multiple emulators are running

`adb devices -l`shows list&#x20;

`adb -s device_name subcommand`  to insteract with that emulator.

or

`adb subcommand -t transport_id` to interact with emulator transport id instead of big name.

### ADB Logcat

1. `adb logcat`shows logs from all the things in device.
2. `adb logcat -v brief`to see brief messgaes. It has multiple tags

   ```
   V    Verbose (default for <tag>)
   D    Debug (default for '*')
   I    Info
   W    Warn
   E    Error
   F    Fatal
   S    Silent (suppress all output)
   ```
3. `adb logcat -v brief "MainActivity:V *:S"`we can filter logs specifically. this will only log verbose log of type mainactivity.

### Android compilation process:

Java/Kotlin ------> .class files(using javac/kotlic)-------->classes.dex (using d8 compiler) it is android byte code.------------>machine code(using Dalvik virtual machine(older android)/ ART android run time)

### Smali

Smali means assembly closely . Assembly language of dalvic bytecode.

`baksmali` : disassembler for dalvik bytecode. thats's what apktool tool does, disassembled `.dex` files to `smali` files.

### Apktool

Installation: <https://apktool.org/docs/install/>

`apktool d path_tok_apk`to decompile an apk.

#### Recompiler and sign APK

1. create the apk

From inside the folder where all files and folders are created after decompilation run:

`apktool b` this will drop a new apk file in `dist`directory.

We can't install this file yet. Let's sign it first

2. **Create a keystore:**

`keytool -genkey -v -keystore research.keystore -alias research_key -keyalg RSA -keysize 2048 -validity 10000`

keystore `research.keystore` file will contain our signing key `research_key`.

Confirm with `yes`on last question.

3. **Sign the APK:**

`jarsigner  -verbose -keystore research.keystore app_name.apk research_key`

4. **Troubleshooting Install Errors**

**INSTALL\_PARSE\_FAILED\_NO\_CERTIFICATES:** There is still something wrong with the signature. Maybe you tried to install an unsigned apk or the chosen algorithm (eg. SHA1) gets rejected.

**INSTALL\_FAILED\_INVALID\_APK:** Failed to extract native libraries

This error occurs with some versions of apktool if the app contains native-libraries. To fix it, edit the AndroidManifest.xml so that `extractNativeLibs` is set to `true`. Afterwards you need to repackage and re-sign your APK.

**INSTALL\_FAILED\_UPDATE\_INCOMPATIBLE:** Package `package_name` signatures do not match previously installed version; ignoring!

You will get this message if a version of the app signed with a different key is installed on the device. The simple solution is to delete the existing app.

**Failed parse during installPackageLI:**

> Targeting R+ (version 30 and above) requires the resources.arsc of installed APKs to be stored uncompressed and aligned on a 4-byte boundary'

This happens on newer apps, try this alternative method using `zipalign` and `apksigner` coming with the specific version build tools:

```bash
$ apktool b
$ [...]/build-tools/34.0.0/zipalign -p -f -v 4 ./dist/<apktool_build>.apk aligned.apk
$ [...]/build-tools/34.0.0/apksigner sign --ks ./research.keystore ./aligned.apk
```

apksigner, zipalign are installed with Androistudio but you need to add `build-tools/34.0.0`to SYSTEM PATH.

jarsigner and keytool are installed when you install JDK from internet.

## Decompiling

Reflection feature in java/kotlin allows decompilation easy and in very high level. Unless app is obfuscated.

Reflection enables code to kind of "self introspect" at run time to resolve symbols \[variable, class names etc]

### Start Emulator without Android studio

If you just want run emulator and nit android studio. Use `emulator`tool android studio. you will have to add it to your path.

`emulator -list-avdsto list your mobile devices.`

`emulator -avd device_name` to start your device.

`emulator -tcpdump file.pcap -avd device_name` to capture network dump of entire phone.

Note: you may need to disable wifi interface, as tcpdump might not be able to capture http packets on wifi interface.

### Proxy

After configuring proxy in device, we need to install burp CA cert in android trusted certificated list to be able to perorm mitm with ssl traffic.

In latest android you can just browse to `http://burpsuite`and download ca cert and install it by searching certificate in settings. This will install certificate in user trusted list which is not supported/trusted by most of the apps.

In older android device do not support der certificate. You will need to convert it to pem format first then install.

`/system/etc/security/cacerts` directory has all system certificates.&#x20;

`/data/misc/user/0/cacerts-added/`directory has all user installed certificates.

In order to install our certificate as system cert we need root access.

1. root android studio emulator. (search rootavd)
2. have a rooted physical device
3. use genymotion, it ocmes with rooted emulators.
4. or use a non-google play image in android studio which allows you to be root.

Then

### install certificate. <a href="#install-system-certificate" id="install-system-certificate"></a>

If you have a device with root access follow the following steps:

1. Install the proxy certificate as a regular user certificate
2. Ensure you are root (`adb root`), and execute the following commands in `adb shell`:

```bash
# Backup the existing system certificates to the user certs folder
cp /system/etc/security/cacerts/* /data/misc/user/0/cacerts-added/

# Create the in-memory mount on top of the system certs folder
mount -t tmpfs tmpfs /system/etc/security/cacerts

# copy all system certs and our user cert into the tmpfs system certs folder
cp /data/misc/user/0/cacerts-added/* /system/etc/security/cacerts/

# Fix any permissions & selinux context labels
chown root:root /system/etc/security/cacerts/*
chmod 644 /system/etc/security/cacerts/*
chcon u:object_r:system_file:s0 /system/etc/security/cacerts/*
```

&#x20;

This trick of adding your certificate to /system/etc/security/cacerts work till android 13 only after that android has made changes that it doesn't handle system certificate this way anymore.

Read this for android 14 fix and problem:

{% embed url="<https://httptoolkit.com/blog/android-14-install-system-ca-certificate/>" %}

Here is script to automate this, once you have installed you certificate on Android 14 as user. And you have root access, run this bash script inside emulator:

```bash
# Create a separate temp directory, to hold the current certificates
# Otherwise, when we add the mount we can't read the current certs anymore.
mkdir -p -m 700 /data/local/tmp/tmp-ca-copy

# Copy out the existing certificates
cp /apex/com.android.conscrypt/cacerts/* /data/local/tmp/tmp-ca-copy/

# Create the in-memory mount on top of the system certs folder
mount -t tmpfs tmpfs /system/etc/security/cacerts

# Copy the existing certs back into the tmpfs, so we keep trusting them
mv /data/local/tmp/tmp-ca-copy/* /system/etc/security/cacerts/

# Copy our new cert in, so we trust that too
cp /data/misc/user/0/cacerts-added/* /system/etc/security/cacerts/

# Update the perms & selinux context labels
chown root:root /system/etc/security/cacerts/*
chmod 644 /system/etc/security/cacerts/*
chcon u:object_r:system_file:s0 /system/etc/security/cacerts/*

# Deal with the APEX overrides, which need injecting into each namespace:

# First we get the Zygote process(es), which launch each app
ZYGOTE_PID=$(pidof zygote || true)
ZYGOTE64_PID=$(pidof zygote64 || true)
# N.b. some devices appear to have both!

# Apps inherit the Zygote's mounts at startup, so we inject here to ensure
# all newly started apps will see these certs straight away:
for Z_PID in "$ZYGOTE_PID" "$ZYGOTE64_PID"; do
    if [ -n "$Z_PID" ]; then
        nsenter --mount=/proc/$Z_PID/ns/mnt -- \
            /bin/mount --bind /system/etc/security/cacerts /apex/com.android.conscrypt/cacerts
    fi
done

# Then we inject the mount into all already running apps, so they
# too see these CA certs immediately:

# Get the PID of every process whose parent is one of the Zygotes:
APP_PIDS=$(
    echo "$ZYGOTE_PID $ZYGOTE64_PID" | \
    xargs -n1 ps -o 'PID' -P | \
    grep -v PID
)

# Inject into the mount namespace of each of those apps:
for PID in $APP_PIDS; do
    nsenter --mount=/proc/$PID/ns/mnt -- \
        /bin/mount --bind /system/etc/security/cacerts /apex/com.android.conscrypt/cacerts &
done
wait # Launched in parallel - wait for completion here

echo "System certificate injected"
```

### Install split apks

In modern devices and latest playstore versions, I noticed that playstore install apps in split apks instead of one single base.apk file.

And for analysis or you want all install this same app on a device with no access to playstor. You will have to pull all apks one by one and install them at all.

`adb shell pm path package_name`

e.g.

```
adb -t 1 shell pm path com.umdeos.bdwjdx 

package:/data/app/~~xZdyqSPoq06amWY0XJLMfg==/com.umdeos.bdwjdx-A-4Jk-fhIxiUYahBsLFERA==/base.apk
package:/data/app/~~xZdyqSPoq06amWY0XJLMfg==/com.umdeos.bdwjdx-A-4Jk-fhIxiUYahBsLFERA==/split_config.en.apk
package:/data/app/~~xZdyqSPoq06amWY0XJLMfg==/com.umdeos.bdwjdx-A-4Jk-fhIxiUYahBsLFERA==/split_config.x86_64.apk
package:/data/app/~~xZdyqSPoq06amWY0XJLMfg==/com.umdeos.bdwjdx-A-4Jk-fhIxiUYahBsLFERA==/split_config.xxhdpi.apk

```

You can now pull each apk and install them together using this command.

```
adb -t 2 install-multiple .\base.apk .\split_config.en.apk .\split_config.x86_64.apk .\split_config.xxhdpi.apk
```

This will install the original APK on new device.

If some patching is done in APKs you can use objection to sign them

`objection signapk *.apk`&#x20;

There is another option called `SAI split APKs` installer app from playstore which is paid now. But you can use its older version for free from its github: <https://github.com/Aefyr/SAI>

1. Using this app you create a backup of your target app then export the backup in apks format.&#x20;
2. Transfer this apks backup to second device and using the same SAI application import the backup and install the app.

### Advanced Proxy:

We have seen that if we got root access on a device, we can install our ca cert in system trusted CA certificate list.

But, What if we can't get root on our device ? ? ?

In that case we can patch our target APK to trust user certificate too.

<figure><img src="/files/mRfQgC3f4tgnnY13jNiJ" alt=""><figcaption></figcaption></figure>

By default android 9 and above if application doesn't define CA trust configuration, ANdroid defaults to system certificate only.

But we can update this value to trust user certificate too.&#x20;

In androidmanifest.xml check `<application>  tag.`

```
<application android:allowBackup="false" android:appComponentFactory="androidx.core.app.CoreComponentFactory" android:extractNativeLibs="false" android:hardwareAccelerated="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:largeHeap="true" android:name="com.appx.core.Appx" android:networkSecurityConfig="@xml/network_security_config" android:preserveLegacyExternalStorage="true" android:requestLegacyExternalStorage="true" android:roundIcon="@mipmap/ic_launcher" android:supportsRtl="true" android:theme="@style/AppThemeOwn">
```

If `android:networkSecurityConfig="@xml/network_security_config"` is present in it which means application do already implement some custom config, we just have to update `res/xml/network_security_config.xml`file.

or if not add \``android:networkSecurityConfig="@xml/network_security_config"`  to \<application> tag among other entries and create \``res/xml/network_security_config.xml file.`

```
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <base-config cleartextTrafficPermitted="false">
        <trust-anchors>
            <certificates src="user" />
            <certificates src="system" />
        </trust-anchors>
    </base-config>
</network-security-config>
```

See we explicitly told system that this app trust "user" certificate.

Now Rebuild apk and sign the apk and install it which will alow you to intercept raffic without root access.

Note: In modern devices you will face problem of split apks and in that case

1. Fetch all split files as discussed earlier.
2. Most probably Androidmanifest.xml and rest of the config files will be base.apk file. So decompile the base.apk
3. Make required changes.
4. rebuild the base apk.
5. Now you have rebuilt base.apk and split apks. Use `Objection` tool to sign all apks

```
objection signapk *.apk
```

6. or you can maually sign and align 4 apks using zipalign and apksigner tool as discussed earlier. (You don't have to align other split apks as you didn't change them )
7. When APKs are ready you can perform

```
adb install-multiple base.objection.apk split.sobjection.apk ........
```

You should be go to got from here. But take it with some grain of salt.

This way of patching might break some things functionality wise or you might not be able to install at all and get some errors. As apktool decompilation and rebulding process is not flawless. It's your skill to find the solution of your problem.

### Use VpnService API

Sometime you might not be able to intercept traffic from your application even if certificates and proxy are configured propelry. Because application might be trying to bypass system proxy settings. e.g

**Direct Sockets**: Some apps use low-level socket APIs (e.g., `Socket` or `OkHttp`) to establish direct connections, bypassing the HTTP proxy set at the system level.

Then we can use android vpnservice API&#x20;

Android's VPNService API allows apps to intercept and redirect all network traffic at network level.

{% embed url="<https://github.com/celzero/rethink-app/releases>" %}

We can install this app and set proxy to our burp suite and set DNS to "system" setting. Using this we can capture traffic as it forces application to reroute traffic through our tunnel as this app registers itself as vpnservice which tells android device to send all raw tcp packets through this gateway and get rewritten by rethink app.

{% embed url="<https://developer.android.com/reference/android/net/VpnService>" %}

## Dynamic Instrumentation

Install frida: <https://frida.re/>

```
pip3 install frida-tools
```

Install Objection: <https://github.com/sensepost/objection>

```
pip3 install objection
```

If you are doing it in virtual environment and you are getting this error&#x20;

```
ModuleNotFoundError: No module named 'pkg_resources'
```

Here is fix: `pip install --upgrade setuptools`

### Hooking app with frida

There are two ways to got from here

1. Patching the apk with frida
2. Installing frida server on android and dynamically hooking app at runtime.

<figure><img src="/files/NKksGn3PXX2RzQ5McUWe" alt=""><figcaption></figcaption></figure>

#### Method 1:

1. Go to the official Frida releases page: <https://github.com/frida/frida/releases>.
2. Download same architecture(android devicee) and version as frida-cli on your machine.
3. You can check your device architecture using:

   ```bash
   adb shell getprop ro.product.cpu.ab
   ```
4. Push frida server to android device

```
adb push frida-server /data/local/tmp/
```

5. Give execute permission&#x20;

```bash
adb shell chmod +x /data/local/tmp/frida-server
```

6. strat the server

```
adb shell /data/local/tmp/frida-server &
```

7. Use frida cli to interact with app.&#x20;
8. List installed packages.

```
frida-ps -Uai
```

9. attach to a running app

```
frida -U Process_Name
```

#### Method 2:

To inject Frida into an APK we can use objection:

```
objection patchapk -s apk_name.apk
```

Objection will extract, patch, re-pack, align and sign the application, and so it's a very fast and easy way to get Frida running.

Note that the application will wait on launch for Frida to connect to it, so to start the application we have to run:

```
frida -U process_name
```

### Frida REPL (Read-Eval-Print Loop)

<figure><img src="/files/Ncxo7VHGJ8Fkadje4hCm" alt=""><figcaption></figcaption></figure>

This terminal you see is just JS interpreter which means it can run JS code.

Run a js script&#x20;

```
frida -U -l script.js process_name
```

anychanges to your script are auto reoloaded in memory. Which can be configured

```
%autoreload on/off
```

#### Hooking Java Classes implementation

Jadx-gui provides option to copy class as frida snippet.

In Frida, the `Java.perform` function is a wrapper that ensures your script is executed within the context of a valid Java Virtual Machine (VM) thread. This is essential because certain operations, such as interacting with Java classes or calling methods, require access to the Java VM, which might not be immediately available when your script is loaded.

When you call `Java.perform`, Frida:

1. **Schedules your callback** to run in a valid Java thread.
2. **Ensures the current thread is attached** to the Java VM if it isn't already.
3. Executes your code within the context of the valid thread.

#### Example Usage

**Without `Java.perform`**

If you attempt to run Java API code directly without `Java.perform` in REPL or using a script file:

```javascript
let ExampleClass = Java.use("io.hextree.fridatarget.ExampleClass");
console.log(ExampleClass.$new().returnDecryptedString());
```

This will likely throw an error because the current thread might not be attached to the Java VM:

```arduino
arduinoCopyEditjava.lang.RuntimeException: Unable to get current thread
```

***

**With `Java.perform`**

Using `Java.perform`, the script will ensure the thread is correctly attached:

```javascript
javascriptCopyEditJava.perform(() => {
    let ExampleClass = Java.use("io.hextree.fridatarget.ExampleClass");
    console.log(ExampleClass.$new().returnDecryptedString());
});
```

Frida Script to hook `Activity` ->`onResume`function to track which Activity class we are in:

```javascript
Java.perform(() => {
	console.log("Frida script started.");

    	let ExampleClass = Java.use("android.app.Activity");

	ExampleClass.onResume.implementation = function() {

	console.log("Called Activity: ", this.getClass().getName());

	this.onResume(); 
	//calling original onResume func after our custom 
	//instructions are executed. otherwise app will just crash as we dont have 
	//all the logic of onResume with us here. after this execution flow will come back
	//to our interception here. and execute next instruction.
	
	console.log("done.");

	};
	
});

```

Note: frida doesn't replace original function, it just intercepts function calls and allows you to modifiy behviour of this call. You can always access original function with `this`keyword.

#### Why You Can Still Access the Original Function:

* **Overriding vs. Replacing:** You're overriding the function's implementation, not replacing it entirely. The original method still exists, and you're allowed to call it within your custom hook.
* **`this` keyword:** The `this` keyword refers to the current instance of the `InterceptionFragment` class, which means you're invoking the original function (with the altered argument) through the same object. This keeps the rest of the object context intact.

#### Frida-Trace

#### a tool from frida toolkit to trace function calls from of loaded classes in memory and its return values.

```javascript
frida-trace.exe -U -j 'io.hextree.*!*'  -J "*Annoying*!*" process_name
```

Search for java class included by `-j` and ignore java classes included by `-J` .

(Format: classname!methodname)

using frida we can trace function calls in native libraries too (JNI)

```javascript
frida-trace.exe -U -I 'libhextree.so' -j 'io.hextree.*!*'  -J "*Annoying*!*" process_name
```

Note: If in frida-trace you dont' see any output after clicking button. RUn frida-trace again as it loads new things in memory which was not loaded previously.

example time:

using frida-trace or jadx we can identify functions being called for a challenge.

```java
public class DiceGameFragment extends Fragment {
    private String[] diceMapping = {"⚀", "⚁", "⚂", "⚃", "⚄", "⚅"};
    private int[] diceViewMapping = {R.id.dice_1, R.id.dice_2, R.id.dice_3, R.id.dice_4, R.id.dice_5};

    public int randomDice() {
        Random random = new Random();
        int randomNumber = random.nextInt(6);
        return randomNumber;
    }

    public void rollDice() {
        boolean won = true;
        for (int i = 0; i < 5; i++) {
            TextView v = (TextView) getView().findViewById(this.diceViewMapping[i]);
            int dice = randomDice();
            if (dice != 5) {
                won = false;
            }
            v.setText(this.diceMapping[dice]);
        }
        if (won) {
        
        /// you win
        }
        
        else {
        
        //you lose
        
        }
        
        
```

You can see if dice roll is not equal to 5 then we will lose. let's hook `randomDice`funciton here to return always 5.

```javascript
Java.perform(() => {
	console.log("Frida script started.");

	let DiceGameFragment = Java.use("io.hextree.fridatarget.ui.DiceGameFragment");
	DiceGameFragment["randomDice"].implementation = function () {
	console.log(`DiceGameFragment.randomDice is called`);
	return 5;
	};
	
});
```

Which allows us to win.

#### Findings class names of our traced function.

If you want to find out name of class that implements your claled function. We can use frida REPL for  e.g.

If you are able to trace a function call&#x20;

```
Started tracing 9 functions. Web UI available at http://localhost:51749/
           /* TID 0x3371 */
  1684 ms  Platform.checkServerTrusted("<instance: javax.net.ssl.X509TrustManager, $className: com.android.org.conscrypt.ConscryptEngineSocket$2>", ["<instance: java.security.cert.X509Certificate, $className: com.android.org.conscrypt.OpenSSLX509Certificate>","<instance: java.security.cert.X509Certificate, $className: com.android.org.conscrypt.OpenSSLX509Certificate>","<instance: java.security.cert.X509Certificate, $className: com.android.org.conscrypt.OpenSSLX509Certificate>"], "GENERIC", "<instance: com.android.org.conscrypt.ConscryptEngine>")
```

Here you can see `Platform.checkServerTrusted` was called now we need to find out class name that implements it, so we can hook it.

we can use `Java.enumerateMethods()`in frida to that it accepts pattern smilar to frida-trace.

Note: Both are case sensitive.

```javascript

[Android Emulator 5554::Fridatarget ]-> Java.enumerateMethods("*Platform*!*checkServerTrusted*")
[
    {
        "classes": [
            {
                "methods": [
                    "checkServerTrusted"
                ],
                "name": "com.android.org.conscrypt.Platform"
            }
        ],
        "loader": null
    }
]
[Android Emulator 5554::Fridatarget ]->
```

here we have enumarted `com.android.org.conscrypt.Platform` implements our target function.

while hooking a function you may encounter a overloading issue.

#### Java Method overloading: *When a class have multiple methods by same name but different parameters*, i.e. known as method overloading

e.g see this frida error

```javascript
Error: checkServerTrusted(): has more than one overload, use .overload(<signature>) to choose from:
        .overload('javax.net.ssl.X509TrustManager', '[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'com.android.org.conscrypt.AbstractConscryptSocket')
        .overload('javax.net.ssl.X509TrustManager', '[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'com.android.org.conscrypt.ConscryptEngine')
```

Here you can see function `checkServerTrusted`has two definitions, two just just differs by what kind of argument they accept.

In frida we use `.overload()` to call the desired function. Let's do it.

```javascript
//hooking checkservertrusted function with method overloading
Java.perform(() => {
	console.log("Frida script started.");

	let er = Java.use("com.android.org.conscrypt.Platform");

	er.checkServerTrusted.overload('javax.net.ssl.X509TrustManager', '[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'com.android.org.conscrypt.ConscryptEngine').implementation = function () {
		console.log(`bypassing ssl`);
		
	};
	
});
```

apart from writing custom scripts we can sometimes use objection also bypass pinning.

#### Bypassing SSL pinning using objection

1. Attach the objection REPL to target process.

```
objection -g process_name explore
```

2. In REPL menu run following command

```
android sslpinning disable
```

It will automate the process of bypassing sslpinning of well known methods. You might get get lucky and might now have to work that hard.

Objection has many more features for android pentetsing&#x20;

e.g. `env`command output

```javascript
io.hextree.privacyfriendly2048 on (Android: 13) [usb] # env

Name                    Path
----------------------  -----------------------------------------------------------------------------------------------------
cacheDirectory          /data/user/0/io.hextree.privacyfriendly2048/cache
codeCacheDirectory      /data/user/0/io.hextree.privacyfriendly2048/code_cache
externalCacheDirectory  /storage/emulated/0/Android/data/io.hextree.privacyfriendly2048/cache
filesDirectory          /data/user/0/io.hextree.privacyfriendly2048/files
obbDir                  /storage/emulated/0/Android/obb/io.hextree.privacyfriendly2048
packageCodePath         /data/app/~~w5Ly0RaB6okl1UEXvcPYxg==/io.hextree.privacyfriendly2048-tPRirbby5vLxXMq73VBNhw==/base.apk
```

More on their wiki: <https://github.com/sensepost/objection/wiki/Using-objection>


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://newrouge.gitbook.io/roguebook1/group/android/getting-started.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
