Stuck on a mobile target that won’t proxy, crashes in emulators and shouts "device compromised"?
This guide takes you from installation to your first decrypted HTTPS request fast - then shows how to keep control when apps enforce SSL pinning, root checks and emulator detection. You’ll choose the right setup (Android Emulator or Magisk-rooted device) and follow a tested path to interception, hooking and persistence.
You will achieve in your first session:
- A repeatable emulator and/or real device with snapshots ready
- Burp interception on Android (system CA and hook when needed)
- Frida hooks for pinning, root and emulator checks (with Zygisk/LSPosed hiding knowhow)
- A fallback path when the app blocks emulators or tools
Outline
- Why set up an Android lab for Bug Bounty?
- Emulator vs Real Device: pros & cons
- Genymotion
- Setup
- Strengths & limitations (ARM issues + ARM translation)
- Android Studio Emulator
- Installing CLI tools
- Creating a rooted x86 AVD
- Snapshot mode for persistence
- GUI setup via Device Manager
- Real Device with Magisk
- Why Magisk (systemless root, modules, Zygisk, root hiding)
- Rooting workflow (bootloader unlock, patch boot.img, flash)
- Useful community modules
- Setting up Burp Suite interception
- Global proxy config with ADB
- Installing Burp’s CA cert
- Pinning limitations → need for Frida/Magisk bypass
- Hooking with Frida
- What is hooking?
- Frida server vs Frida gadget
- SSL pinning bypass
- Root & emulator detection bypass
- Medusa framework
- Prebuilt modules (ssl_unpinning, root detection, crypto hooks)
- Conclusion & Next Steps
- Emulator vs device recap
- What you can now test (traffic, pinning, detections)
- Teaser: next article on mobile reconnaissance (APK analysis, endpoint mapping)
Step 1 - understanding the importance and qualities of a well-configured, controlled mobile lab (SSL pinning, root & emulator checks)
On YesWeHack, a significant number of Bug Bounty scopes include mobile applications. If you want to be an effective hunter on mobile scopes, you need a solid Android lab to analyse these apps thoroughly.
A properly configured Android testing lab gives you two essential qualities:
- Repeatability: Emulators are fast to reset, easy to break and safe to experiment with. If something goes wrong, you simply wipe and restart quickly.
- Isolation: Physical devices provide realistic testing conditions through biometric sensors and cameras used for KYC processes, hardware-backed keystores and the quirks of real-world usage that emulators struggle to replicate. They also allow you to avoid triggering anti-VM protections, which many modern apps use to block execution inside emulators.
A rooted device with Magisk installed also unlocks advanced capabilities. By hiding root from apps directly through your phone, bypassing SSL pinning and spoofing device properties using Zygisk, which allows code to be executed within each application’s process, you can achieve deeper levels of analysis.
Before diving into commands, decide whether to start with an emulator or a Magisk/Zygisk device:
- Use an emulator if you need speed, snapshots and rootable images
- Use a real device if the target enforces emulator detection, uses ARM-only libs or if Play Store/DRM is required.
Always keep both options ready - it will save you hours if your primary choice fails
Step 2 - Genymotion emulator (x86 Only): pros, limitations and ARM translation workaround
Genymotion is the Android emulator of choice for many hunters because it’s lightweight, quick to configure, and gives you root access from the start. It runs on VirtualBox for Windows and QEMU/KVM for Linux.
Benefits of using Genymotion
- Really quick to install
- Installing APKs is frictionless (adb install or drag-and-drop)
- Root access makes deploying tools like Frida trivial
- Proxy configuration takes minutes
- Google Play services can be added with GApps if the target requires them
Imagine you’re testing a news app that relies on HTTP APIs. With Genymotion, you can drop the APK into the emulator, configure Burp Suite as a proxy, and start observing traffic.
In rare cases, the app won’t enforce SSL pinning or anti-root/anti-VM checks, and cleartext requests will appear right away. However, these situations are becoming rarer still - which is why the next step is to learn how to systematically bypass such protections.
Genymotion limitations
Genymotion only supports x86 system images and doesn’t provide built-in ARM → x86_64 translation. Since most modern apps are compiled for ARM/ARM64, this often leads to compatibility issue
Genymotion Desktop devices architecture on PC and old Mac (Intel) is x86 or x86_64: it is impossible to install or run applications and games that are only available for arm or arm64
Genymotion_ARM_Translation, a community repository, can overcome this limitation by injecting ARM translation libraries into the emulator, allowing many ARM-only apps to run.
However, results are mixed. Some apps will work; others will still crash or refuse to run - making it less reliable than a physical ARM device or the official Android Studio emulator with ARM images.
Another Genymotion drawback is the prevalence of emulator detection, which means some apps simply won’t run.
A third limitation is that, although you can choose Android versions (between 5.1-13), you cannot specify the Android API level you want.
Genymotion installation and creating a virtual device
Find out how to install Genymotion on Linux here. To create a virtual device:
- Launch Genymotion Desktop
- Log in with your account
- Click Add → select a device template (Pixel, Nexus, etc)
- Choose your Android version (x86 images only)
Step 3 - Android Emulator (AVD): rootable images, ARM translation, snapshots
The official Android Emulator (shipped with Android Studio) has become the industry standard for testing Android applications. It is tightly integrated with the SDK and provides accurate device emulation, making it more reliable than third-party solutions.
The Android Emulator is a virtualized Android device running on top of QEMU. It replicates real hardware (CPU, GPU, sensors, storage etc) and allows you to:
- Run almost any Android version (from API 16 upwards)
- Simulate different device profiles (Pixel, Nexus, tablets, foldables…)
- Debug apps with ADB as if you were connected to a physical device
- Test configurations like network changes, battery drain, screen rotation and more.
Benefits of using Android Emulator
- Accuracy: Closer to real devices than Genymotion (since it’s maintained by Google)
- Compatibility: Direct support for Google APIs, Play Store images and developer/debug build
- Flexibility: Lets you create multiple AVDs (Android virtual devices) for different scenarios (rooted, non-root, Play Store enabled, etc)
- Free: Unlike Genymotion, the Android Emulator is entirely free, with no licensing costs or usage restrictions
What Are SDK, AVD and system images?
- Android SDK (software development kit): Tools provided by Google for building and testing Android apps
- Platform tools: Including adb (Android Debug Bridge) and fastboot, these tools are essential for interacting with your emulator or device
- AVD (Android virtual device): Virtual phone configuration - think of it as a template describing the model, Android version and image
- System image: Emulator 'firmware'. Choose API level (Android version), architecture (x86 or ARM) and whether you want Play Store or debug features
A debug build gives you root access. An engineering (eng
) build also comes with default root access, while a user-debug
build does not provide root by default but can still be rooted. The user-debug
build also includes additional logging.
1. Download command-line tools
Head to the official Android developer download page to grab the Linux command-line tools.
There’s no need to install the full Android Studio IDE. Instead of pulling gigabytes of extra packages you may never use, you just get the essentials:
- sdkmanager to install SDK components and system images
- avdmanager to create and configure emulators
- emulator to actually launch the AVDs
This approach is much cleaner if you only care about security testing, since you avoid the heavy IDE while retaining full control over your Android environment:
# Create a directory for the SDK with proper layout
mkdir -p ~/Android/cmdline-tools/latest
# Download archive
wget https://dl.google.com/android/repository/commandlinetools-linux-13114758_latest.zip -O /tmp/cmdline-tools.zip
# Extract directly into 'latest'
unzip /tmp/cmdline-tools.zip -d ~/Android/cmdline-tools/latest
# Clean up
rm /tmp/cmdline-tools.zip
2. Configure environment variables
Add the following to your ~/.bashrc
or ~/.zshrc
:
export ANDROID_HOME=$HOME/Android
export PATH=$ANDROID_HOME/cmdline-tools/latest/bin:$ANDROID_HOME/platform-tools:$ANDROID_HOME/emulator:$PATH
Reload your shell:
source ~/.zshrc
3. Install core SDK components
Install the essentials : adb
, emulator
and tools
:
sdkmanager --install "platform-tools""emulator"
4. Install a rooted x86 system image
In terms of achieving a more realistic testing environment and broader compatibility with modern tooling, a rooted x86 image is usually the most practical choice on Intel/AMD hosts. Google provides debug-capable builds under Google APIs, such as Android 11 (API 30). That said, in some cases it can actually be more useful to run a non-rooted image – for example with Flutter applications, where traffic interception often works more reliably on a standard build.
We’ll dive into these scenarios later on! ;)
You can list all available images with sdkmanager --list
, then install an x86 build.
From there, you’ll notice that images come in different 'flavours':
- google_apis: Debuggable build, allows adb root. Great for hunting since you can remount /system and inject tools
- google_apis_playstore: Same, but bundled with the Play Store. This one is not rootable (adb root will fail)
- default / aosp: Pure AOSP builds. Lightweight, but usually no Play Services and sometimes limited debugging
This command tells the Android SDK to download and install a system image:
sdkmanager --install "system-images;android-30;google_apis;x86_64"
android-30
: Android 11 (API level 30)google_apis
: Google APIs–enabled image (debuggable, allows adb root)x86_64
: Emulator architecture (runs fast on Intel/AMD hosts)
In short: it installs an Android 11 x86_64 image with Google API support, which you can then use to create an emulator (AVD).
Disclaimer: If your machine runs on x86_64 (Intel/AMD), then trying to emulate ARM64 images won’t be supported by QEMU beyond a certain point.
Here’s the relevant snippet from QEMU itself:
ARM64 emulation on an x86_64 host is only possible up to API level 27 (Oreo):
#ifdef __x86_64__
if (sarch == "arm64" && apiLevel >=28) {
APANIC("Avd's CPU Architecture '%s' is not supported by the QEMU2 emulator on x86_64 host.\n", avdarch);
}
#endif
To run something like Android 33 in ARM64, you’d need an ARM64 CPU host:
#if defined(__aarch64__)
if (sarch != "arm64") {
APANIC("Avd's CPU Architecture '%s' is not supported by the QEMU2 emulator on aarch64 host.\n", avdarch);
}
#endif
ARM translation in Android Emulator (Android 11+)
With Android 11, Google introduced a performance-focused enhancement to the emulator: the ability to translate ARM binaries to x86 on the fly, only for the app process – instead of running full ARM emulation.
As the Android developers explain:
"The new Android 11 system images are capable of translating ARM instructions to x86 without impacting the entire system. This allows execution of ARM binaries for testing without the performance overhead of full ARM emulation."
What this means:
- Instead of having slow, system-wide ARM emulation, only the app process (and its linked native libraries) get the translation treatment
- The rest of the emulator - ART, graphics, system libraries - run natively as x86, leveraging your CPU’s virtualization capabilities
- Result: fast emulator performance, even when testing ARM-only apps, because most of the system still benefits from hardware acceleration
How to use it:
- Choose an Android 11+ x86 or x86_64 system image in your AVD (must be Google APIs or Play Store variant, not AOSP)
- You can then install and fully test ARM/ARM64 apps (even those with native C++ code) without performance issues
- This translation layer doesn’t always work – some apps will still fail. There are third-party repos and hacks to translate ARM to x86, but in practice the most reliable and efficient option remains a physical ARM device
6. Create and run a rooted x86 AVD (Persistent Snapshot Mode)
Create the emulator:
avdmanager create avd -n PixelRootX86 -k "system-images;android-30;google_apis;x86_64" -d "pixel"
Start it with a writable system and snapshot support:
emulator -avd PixelRootX86 -writable-system -snapshot PixelRootX86_snap
-avd PixelRootX86
: picks the virtual phone you just created.-writable-system
: makes /system writable so you can push Frida, install certs or patch system files.-snapshot PixelRootX86_snap
: tells the emulator to save and restore a named snapshot.- At the first launch, if no snapshot exists, it boots normally and creates one when you quit.
- On the next launch, it restores that state – so all your changes (Burp cert, Frida server, Magisk modules, etc) persist across reboots!
Save a snapshot manually
Sometimes you want to ‘freeze’ your emulator at a specific point (eg after configuring Burp, pushing Frida and disabling security checks). You can do this via the emulator console:
- Connect to the emulator’s console (it listens on port
5554
by default):
adb -s emulator-5554 emu avd snapshot save my_clean_setup
- Save the snapshot:
avd snapshot save my_clean_setup
- Next time, you can boot directly into that state:
emulator -avd PixelRootX86 -writable-system -snapshot my_clean_setup
Verify root access with ADB
ADB (Android Debug Bridge) is a command-line tool included in the SDK’s platform-tools. It allows you to communicate with your emulator (or a real device) - think of it as your remote control for Android.
adb root
adb shell whoami
adb root
: restarts the ADB daemon with root privileges (only works onuserdebug
/eng
builds, which is why we installed the right system image)adb shell whoami
: drops you into the device shell and asks: "who am I?" If everything worked, you’ll see root
That confirms your emulator is running with root access and can be modified freely - exactly what you need for your Bug Bounty workflows (Frida instrumentation, bypassing SSL pinning, etc).
7. Create and run a rooted x86 AVD with Android Studio (GUI method)
Now, if you want to emulate x86 smoothly, the easiest path is to use the full Android Studio client. Its built-in AVD Manager automatically handles system images and device configs, so you don’t need to fight with command-line quirks.
From Android Studio, you can simply:
- Install the Android Studio client (Linux). Download it from the official page and extract it:
tar -xvf android-studio-*.tar.gz -C /opt/
/opt/android-studio/bin/studio.sh
This gives you the full IDE with the built-in AVD Manager, which makes creating and running emulators much easier than using only the CLI tools.
- Open Device Manager from the 'Tools' menu.
- Create a new virtual device (a Pixel model is a safe default). Images with Play Store are production builds and cannot be rooted. If you want root access, pick a Google APIs image (without Play Store). These are debug-friendly, let you enable root with adb root, and can be started with -writable-system for deeper testing.
- When prompted, select a Google APIs x86_64 image (for example, Android 12 / API 31).
- Hit download and let Android Studio take care of the install.
This method uses more disk space, but it’s far more reliable because you don’t need to manually install system images or troubleshoot QEMU flags. Android Studio’s AVD Manager downloads the right image for your host, configures the device profil, and ensures it boots correctly.
Step 4 – Magisk/Zygisk rooted device for Bug Bounty: root hiding, Frida hooks, system CA
Using a real device is often the most practical setup for mobile testing. Rooting it with Magisk makes things even easier, since it gives you full control over the system, the ability to load modules, and the flexibility to bypass protections or customise the environment to your needs.
Most importantly, a real device runs natively on ARM/ARM64, which is what most production apps are compiled for. Unlike emulators on x86 hosts (which rely on translation layers that don’t always work reliably), a rooted physical phone guarantees full ARM compatibility and avoids the performance or stability issues you may face in virtual environments.
Why Magisk?
Magisk goes way beyond just giving you root access - it’s basically a Swiss army knife for Android hacking, offering:
- Systemless root: Magisk patches the boot image instead of directly modifying
/system
. This preserves system files and makes root safer, reversible and easier to hide from apps that perform root detection (banking apps, corporate apps etc) - Modules ecosystem: Plug-and-play modules let you extend functionality with SSL pinning bypass, SafetyNet evasion, Frida injection, custom DNS and more.
- Zygisk integration: Hooks directly into Android’s Zygote process, which is the parent of all apps. This allows runtime injection into every application, enabling you to globally hook app functions or patch security checks.
- Root hiding: With modules like Shamiko, you can cloak your root status from apps that refuse to run on rooted devices.
Together, Magisk and Zygisk form one of the most flexible setups for real-device testing. You get the stability of a physical ARM environment with the stealth + control needed to bypass modern app defenses.
Example: Root your Pixel phone with Magisk
Google Pixel offers the smoothest testing experience. How?
- Factory images are always available directly from Google
- Bootloader unlocking is simple and official
- Excellent Magisk support with an active community
In short: fewer headaches, more time to focus on hacking.
#1 – Unlock the Bootloader
This will wipe your device, so back up your data first:
- Enable Developer Options: go to Settings > About Phone > tap Build Number 7 times
- In Developer Options, enable OEM Unlocking and USB Debugging
- Plug in your phone and reboot to bootloader:
adb reboot bootloader
- Unlock:
fastboot flashing unlock
Confirm on the device with volume + power buttons.
#2 - Get the factory image
- Go to Google’s official factory images
- Download the image for your Pixel and Android version
- Extract it and locate the
boot.img
file
#3 - Patch with Magisk
- Transfer
boot.img
to your Pixel - Install the latest Magisk app from GitHub releases
- Open Magisk > Install > Select and Patch a File > choose boot.img
- Magisk creates magisk_patched-XXXXX.img
- Transfer it back to your computer
#4 - Flash the patched boot
- Reboot to bootloader:
adb reboot bootloader
- Flash the patched image:
fastboot flash boot magisk_patched-XXXXX.img
- Reboot your phone
#5 - verify root
- Open Magisk and check that it shows Installed
- Test from adb:
adb shell
su
whoami # should return root
Tips: Mirror and control your device with scrcpy
You can use scrcpy to mirror the screen of your physical device directly onto your computer. This offers the following benefits:
- Control the phone with mouse and keyboard
- Copy/paste text between your host and the device
- Record demo videos or screenshots for reports
sudo pacman -S scrcpy # On Arch based
scrcpy # Launch the mirror
It’s lightweight, fast, and doesn’t require root - perfect for managing your test device while running tools like Frida or Burp on the side!
Things to keep in mind
- OTA updates will break root - so you’ll need to re-flash a patched boot image
- Keep the original
boot.img
handy in case of bootloops - SafetyNet failures can be fixed with the Magisk deny list + Props modules
You now have a rooted physical or emulated phone, fully set up for mobile bug hunting.
At this stage, the device itself is ready - but the real work begins here:
- Learning how to bypass SSL pinning to intercept encrypted traffic
- Getting comfortable with Frida hooking to patch logic on the fly, bypass anti-debugging or extract secrets directly from memory
- Explore anti-root/anti-emulator bypass techniques, since many apps actively try to block analysis
What makes the difference here is your ability to adapt to app defenses and use the right tooling to keep control.
Emulators vs real devices: what to expect
The benefits of both emulators and real devices come with trade-offs and the appropriate choice depends on the target app and the testing stage you’ve reached.
The best emulators, such as Genymotion, are fast and disposable.Genymotion is useful for quick APK installs or basic network interception, but the lack of ARM support and its ‘sandbox-y’ feel make it far from realistic.
The Android Studio Emulator is the most reliable virtual option. It’s maintained directly by Google, free to use and supports the latest Android versions, integrates Google APIs and Play Store builds, and runs on x86 with built-in ARM translation for most apps. With debug system images, you can even enable root (adb root) and make /system writable for low-level testing. For many workflows, this is more than enough without reaching for a physical device.
However, a Magisk-rooted real device is still the most powerful setup. It gives you native ARM/ARM64 execution, full hardware features and the ability to bypass emulator/VM detection. It’s harder to set up and slower to reset, but essential for high-value targets where realism and stealth are critical.
Magisk modules: extending your device capabilities
Magisk by itself provides root management, but its true power comes from modules. For mobile security testing, a few stand out:
- DenyList: selectively hides root from sensitive apps. Without this, many apps refuse to even launch on rooted devices
- Zygisk + LSPosed: hook into the Android runtime to modify app behavior dynamically. You can load custom scripts or force methods to behave differently without repackaging the APK
- Frida Gadget/Frida Server integration: run Frida persistently as part of your environment, making dynamic instrumentation easier without manually starting up each time
Magisk and the LSPosed ecosystem offer plenty of community-driven modules, giving you readymade tools for bypassing protections, customising environments and experimenting without reinventing the wheel – all without modifying the target app. These modules can turn a rooted device into a flexible and stealthy research platform.
At this point, your lab (whether emulator or device) is rooted and under control. Next, you’ll route traffic into Burp Suite so you can actually observe requests and responses.
Step 5 – Burp Suite on Android: proxy, system CA on Android and HTTPS interception
When analysing a mobile app, the best first step is often to observe its network traffic. By default, the emulator sends requests directly to the internet, which means you’ll miss the opportunity to see what’s happening under the hood.
Setting a global proxy ensures that all HTTP/HTTPS traffic is routed through Burp Suite, giving you full visibility and control.
Configure proxy for traffic interception
Configure the proxy with ADB:
adb shell settings put global http_proxy 192.168.1.2:8080
Here, 192.168.1.2
is your host machine’s IP (as seen from the emulator), and 8080
is the default listening port for Burp.
To quickly identify your host IP, you can run:
ip a | grep '192' # or '10'
Installing Burp’s certificate
To decrypt HTTPS requests, the emulator needs to trust Burp’s CA:
- On your host, open https://127.0.0.1:8080 (Burp’s proxy listener). Then export Burp’s CA certificate (Proxy > Options > Export CA certificate) and save it in DER format (.cer).
- Push it to the emulator:
adb push cacert.cer /data/media/0/Download/
- On the device: Settings > Security > Install from storage > Select the certificate.
- Don’t forget to configure Burp to listen on all interfaces, not just
127.0.0.1
.
Go to Proxy > Options > Proxy Listeners, edit the listener and bind it to all interfaces (0.0.0.0).
At this point, apps with SSL pinning will trust Burp’s CA and you’ll be able to see HTTPS traffic in cleartext directly inside Burp Suite.
However, keep in mind that many modern apps implement certificate pinning. In that case, system-wide certificates aren’t enough. You’ll need to go further - typically with Frida scripts or Magisk modules - to bypass the pinning mechanism and fully intercept traffic.
Step 6 – Bypass SSL pinning, root detection and emulator checks with Frida/Zygisk (OkHttp, Conscrypt, Native)
Once your device is rooted and ready, you’ll quickly hit a wall: most modern apps block debugging, proxies and modified environments. This is why you need to understand hooking.
What Is hooking?
Hooking means intercepting function calls inside an app and altering their behaviour at runtime. Instead of patching the APK and resigning it, you attach to the process dynamically with tools like Frida.
Think of it as saying: “Whenever this app calls function X, I’ll decide what happens instead.”
For mobile hacking, this means you can disable root checks, override SSL validation or monitor cryptographic functions.
This is the foundation of bypassing SSL pinning and anti-root or anti-emulator protections.
What Is Frida?
Frida is a dynamic instrumentation toolkit – basically a Swiss army knife for hackers and reverse engineers.
It lets you inject JavaScript code into a running process for Android, iOS, Windows, Linux, etc.On Android, this means you can attach to any app and hook its Java/native functions in real time.
You don’t need to recompile or resign the APK – Frida operates from the outside, directly in memory.
Some typical use cases in mobile hacking:
- Bypass SSL pinning: hook certificate validation functions so Burp’s CA is always accepted
- Bypass root detection: intercept isDeviceRooted() or similar checks and force them to return false
- Crypto analysis: log arguments/results of encryption functions to recover secrets like API keys or tokens
- Debugging hidden logic: dump runtime variables, inspect obfuscated methods or monitor network calls before they’re encrypted
Frida comes in two flavours:
Frida server
Frida server is a binary you push to your rooted device/emulator.
You need to download the right binary for your device’s architecture (e.g. arm64 for modern phones, x86_64 for emulators).
Each time you reboot, you’ll need to push it again (or keep it on the device) and run it manually with adb shell ./frida-server &.
Only once the server is running can your host machine’s frida-tools connect to the device.
This is the most straightforward setup and the one we usually rely on, since it doesn’t require repackaging the target APK.
Frida gadget
Frida gadget is a shared library you bundle into an APK. This works without root but the target APK must be repackaged.
You drop the gadget .so file in the APK’s lib/ folder, configure frida-gadget.config then reinstall the modified APK.
As with Frida server, the gadget must be loaded at runtime (by the app itself) before you can attach your scripts.
In both cases, you can’t just ‘run’ Frida directly – you always need to start the server or load the gadget first, then attach with frida -U -n com.target.app from your host.
With just a few lines of JavaScript, you can completely alter how an app behaves – and since it runs live, you can experiment fast/real time without breaking the app!
SSL pinning on Android: OkHttp, TrustManager, Conscrypt hooks with Frida
Normally, Android trusts your Burp CA once installed. But apps that use certificate pinning reject all certificates except the ones they ship with. That means your proxy will break HTTPS.
How To bypass certificate pinning
With Frida, you can for example hook into the system’s TrustManager or OkHttp functions and force them to accept your certificate.
Example: Disable SSL checks via Frida
Inject this code via frida -U -f com.target.app -s ssl-bypass.js --no-pause
and your proxy will work even against pinned apps:
Java.perform(function () {
var X509TrustManager = Java.use('javax.net.ssl.X509TrustManager');
var SSLContext = Java.use('javax.net.ssl.SSLContext');
// Override checkClientTrusted & checkServerTrusted
X509TrustManager.checkClientTrusted.implementation = function () {};
X509TrustManager.checkServerTrusted.implementation = function () {};
// Return a new TrustManager that accepts everything
var TrustManagers = [X509TrustManager.$new()];
var SSLContextInit = SSLContext.init.overload('[Ljavax.net.ssl.KeyManager;', '[Ljavax.net.ssl.TrustManager;', 'java.security.SecureRandom');
SSLContextInit.implementation = function (km, tm, sr) {
SSLContextInit.call(this, km, TrustManagers, sr);
};
});
Frida CodeShare
You don’t always need to reinvent the wheel. Frida CodeShare hosts community scripts like universal-android-ssl-pinning-bypass
!
Load them directly:
frida -U -f com.target.app -l frida-ssl-unpinning.js --no-pause
Root detection evasion: Zygisk/LSPosed DenyList and Frida overrides
Root detection identifies whether a device has been modified to grant the user elevated privileges. Developers often implement multiple overlapping checks to make bypassing harder.
Here are some common detection vectors (non-exhaustive):
- Binary checks
- Search for su, busybox, or other binaries in standard paths (/system/xbin/, /system/bin/, /sbin/ etc)
- Look for Magisk’s hidden binaries or daemon
- Package checks
- Detect if well-known root apps are installed (eg Magisk Manager, SuperSU)
- Query installed packages against a blacklist
- System property checks
- Read values like ro.build.tags, ro.debuggable, ro.secure
- If ro.build.tags contains test-keys, it usually signifies a custom build
- File system modifications
- Check if system partition is writable
- Look for suspicious mount points (eg, /system mounted as rw)
How to bypass root detection
Hook these checks to return safe values.
Example: Bypass 'isDeviceRooted'
Java.perform(function () {
var RootChecker = Java.use("com.target.security.RootCheck");
RootChecker.isDeviceRooted.implementation = function () {
console.log("Bypassing root check");
return false;
};
});
Inject this code and the app will deem your rooted device as 'clean'.
Emulator/VM Detection Bypass: Build Props, Sensors, Filesystem Artifacts
Some apps actively detect whether they are running inside an emulator or virtual machine. This is a common anti-tampering measure because emulators are widely used for reverse engineering, automated testing, or fraud. If the app suspects an emulator, it may refuse to run or cripple some features.
How Detection Works (non-exhaustive)
- Build properties
- Values such as
Build.FINGERPRINT
,Build.MODEL
,Build.MANUFACTURER
,Build.HARDWARE
,Build.BOARD
, andBuild.BRAND
often reveal “goldfish
”, “ranchu
”, “generic
”, “sdk
” or other emulator strings. - Example:
ro.product.model
=sdk_gphone_x86
is a dead giveaway.
- Values such as
- Lack of real hardware components
- Missing or empty IMEI, serial number, or baseband version.
- No SIM card, Bluetooth, or camera detected.
- No real battery stats (some emulators fake values like 100% / charging all the time).
- Sensor checks
- Real phones have accelerometers, gyroscopes, magnetometers, etc. Emulators often lack these or report constant zero values.
- Filesystem artifacts
- Presence of known emulator files like:
/dev/qemu_pipe
/dev/socket/qemud
/system/lib/libc_malloc_debug_qemu.so
/init.goldfish.rc
- These files are unique to QEMU-based emulators.
- Presence of known emulator files like:
- Network configuration
- Default emulator MAC addresses (e.g.,
02:00:00:00:00:00
). - Known IP ranges (
10.0.2.x
) used by the Android emulator’s NAT.
- Default emulator MAC addresses (e.g.,
There’s no single "emulator check" - apps combine dozens of indicators. The more of them that are detected, the higher the chance the app will block execution.
That’s why bypassing emulator detection usually requires hooking suspicious API calls (e.g., Build.MODEL
, TelephonyManager.getDeviceId()
) and returning spoofed values with Frida or Medusa.
How to bypass it
Hook into the Android Build class to spoof real device values.
Example: Spoof Build Properties
Java.perform(function () {
var Build = Java.use("android.os.Build");
Build.MODEL.value = "Pixel 7 Pro";
Build.MANUFACTURER.value = "Google";
Build.BRAND.value = "google";
});
Now the app can’t distinguish your emulator from a real device.
Frida automation with Medusa: SSL unpinning, root hiding, and crypto hooks
Frida is extremely powerful, but writing your own hooks for every app can get repetitive. That’s where Medusa comes in.
Medusa is a Frida-powered framework that provides more than 90 prebuilt modules for common tasks like SSL pinning bypass, root detection evasion, traffic logging and crypto analysis.
Instead of reinventing the wheel, you just load the module you need and run it against the target app.
Installation
git clone https://github.com/Ch0pin/medusa
cd medusa
pip install -r requirements.txt
python medusa.py
What You Can Do With Medusa
Medusa lets you combine modules interactively. For example:
ssl_unpinning
: bypass certificate pinning instantlyroot_detection_bypass
: hide root from stubborn appshttp_communications
: log and analyze cleartext network trafficcrypto_hooks
: intercept and dump cryptographic keys in real time
In short: Medusa saves you time!
Instead of writing and maintaining dozens of custom Frida scripts, you can quickly load proven modules from a large community-driven library, and still extend it with your own code when needed.
Example workflow
Inside the Medusa console:
show categories # List all available hook categories
use http_communications/multiple_unpinner # Select the SSL bypass module
use root_detection/universal_root_detection_bypass # Select the root detection bypass module
run com.target.app # Attach to your target app with hooks enabled
This workflow allows you to quickly bypass SSL pinning and root detection in the target application. With these hooks in place, you can reliably intercept, analyse, and manipulate requests.
Step 7 - wrap-up and next steps
So that's your lab set up and ready to go! It's time to take stock.
You now have:
- A repeatable lab (emulator +/or real device)
- Burp interception working on Android N
- Frida/Zygisk hooks bypassing SSL pinning, root, and emulator checks
Your next step is using this lab to test real bug bounty targets, such as by:
- Looking for IDORs by replaying requests
- Analysing auth flows for bypass
- Observing crypto/storage flaws with full control
Conclusion – trade-offs, adaptability and the next phase in your mobile-hacking journey
Botch the setup of your Android lab and you’ll undermine every subsequent step of your Bug Bounty hunt. Without a controlled and flexible environment, you’ll be constantly slowed down by incompatibilities, detection mechanisms or missing features.
That’s why it’s worth taking the time to understand the trade-offs:
- Genymotion gives you speed and disposability – great when you want to quickly spin up a test device and inspect network flows. But its lack of ARM support and realism limits its utility for modern apps.
- The Android Studio emulator represents a balanced middle ground. It can be ‘heavier’ than Genymotion, but its behaviour is much closer to how apps behave in the real world. With the right x86 or ARM images, plus root access and writable system partitions, it becomes a serious tool for debugging, SSL bypass and traffic interception.
- Finally, a Magisk-rooted physical device is the gold standard. It runs natively on ARM/ARM64, avoids translation quirks and gives you access to community-driven modules (Zygisk, LSPosed, Frida integration, root hiding, SSL bypass, etc) that let you replicate realistic attack scenarios. It’s slower to reset, but indispensable for high-value targets or apps that aggressively detect emulators.
Across all these setups, the real challenge isn’t just running an APK – it’s breaking through its defenses. Root detection, SSL pinning, emulator checks, integrity verifications… these are the hurdles you’ll face on a daily basis.
Tools like Frida (with custom scripts or frameworks like Medusa) allow you to hook into an app’s runtime, disable protections and monitor its most sensitive functions. Combined with Magisk modules, they transform your lab into a playground where almost any restriction can be bypassed.
In other words: your lab is not just a testing ground; it’s your battlefield. The more control and flexibility you build into it, the easier it becomes to adapt to each new app you target. And in Bug Bounty, adaptability is often what separates a failed attempt from a valid finding.
So now you know how to build the right environment for testing mobile scopes. In the next article, we’ll dive into mobile reconnaissance – including how to dissect an APK, extract endpoints, map attack surfaces, and prepare your exploitation workflow before even touching runtime hooks.
Stay tuned: with a solid lab in place, the next step is learning how to spot opportunities in mobile apps that others might miss.
Happy Android hunting! 🤘