Appdome's Anti-Apktool, a bigger threat to modding than PairIP.

Trusted by over 1.6 million members since 2014 — why not join them?
Log in or Register to join us!

Snailsoft

∞ and beyond!
Staff Member
Moderator
SB Mod Squad ⭐
✔ Approved Releaser
Active User
Member for 2 years
I figured I would copy this information to a thread here as the posts in the general chat are likely to get lost.
This is interesting. I was going to mod a game when it suddenly shut down and popped up this message: APP APKTOOL-M DETECTED. I'm working on locating the code and possibly creating a RegEx for it.
I suspect it is a similar process that led Game Guardian and Lucky Patcher to use random names.
Not the run of the mill app killer. RegEx+ located hundreds of matches. None of them (nor all of them) being changed has worked.
ApkID just about had a spasm. Anti VM, compiler unknown, anti debugger, anti hook. This game has some serious protection.
And here we have it, the culprit is called: Appdome's Anti-Apktool. Features includes over 300 layers of security to protect all aspects of an app, from data encryption to defense against malware.
It is frightening to know that ApkTool/M, MT, NP, and even ApkID can not identify Appdome. Most did not identify ANY protection. Only ApkID identified numerous types of protection yet it couldn't identify the protection used.

Even more irritating, one of those 300+ security features is the ability to fake another form of apk protection!
(It turn out that a very troublesome PairIP protected game that nobody has been able to RE is in fact NOT PairIP, rather, it is Appdome faking PairIP protection.)

After hours of testing, installing, uninstalling, revising, I finally came up with a script to build an Appdome identifier.

I've been dreading installing Frida as it doesn't play well with Termux. However, it is needed to identify Appdome.
It took several attempts and modifying of older scripts, but I finally beat the penguin into place.
This script will install the tools required to identify this new protection.

Code:
#Numpy
pkg install python-numpy

#APKleaks
cd $HOME
yes | pkg update && pkg upgrade
pip3 install apkleaks

#JaDx
cd $HOME
yes | pkg update && pkg upgrade
git clone https://github.com/Lexiie/Termux-Jadx 
cd Termux-Jadx
dpkg -i ./jadx-0.6.1_all.deb

#Frida for Termux
cd $HOME
yes | pkg update && pkg upgrade
pkg install build-essential python python-pip git wget binutils openssl

pkg i -y python git

pip install setuptools

  DARCH="arm64"

wget -o frida-core-devkit-17.0.5-android-arm64.tar.xz https://github.com/frida/frida/releases/download/17.0.5/frida-core-devkit-17.0.5-android-arm64.tar.xz

if [ -d "$HOME/devkit" ]; then
  rm -rf "$HOME/devkit"
fi

mkdir -p "$HOME/devkit"

tar -xvf frida-core-devkit-17.0.5-android-arm64.tar.xz -C $HOME/devkit

  rm -f frida-core-devkit-17.0.5-android-arm64.tar.xz

git clone https://github.com/AbhiTheModder/frida-python frida-python-android

cd frida-python-android

FRIDA_VERSION="17.0.5" FRIDA_CORE_DEVKIT="$HOME/devkit" pip install --force-reinstall .

if [ -d "$HOME/frida-python-android" ]; then
  rm -rf "$HOME/frida-python-android"
  cd $HOME
fi

pip install --upgrade frida-tools

#Tools needed to identify if Appdome's Anti-Apktool is protecting an APK.
#Appdome's Anti-Apktool features includes over 300 layers of security to protect all aspects of an app from data encryption to defense against malware.

cd $HOME
yes | pkg update && pkg upgrade
yes | apt update && apt upgrade

 git clone https://github.com/notdeanos/appdome_app_checker
 cd appdome_app_checker
 
pip install asn1crypto bcrypt cffi colorama cryptography enum34 frida-tools idna ipaddress paramiko prompt-toolkit pyasn1 pycparser Pygments PyNaCl scp six tqdm wcwidth androguard

echo USAGE:  cd appdome_app_checker
echo         python appdome_app_checker.py /sdcard/Download/FILENAME.APK
That will install and set up the tools needed to run the Appdome python script.
However, the script is worthless as is because it was made in 2023 primarily for iOS.
Open (with nano/vim/etc) appdome_app_checker.py and replace the entire content with my updated Android code:
Code:
#!/usr/bin/env python3

"""
Appdome App Checker revised by Snailsoft 2025 for Android Termux
iOS testing and tools replaced with Android.
Python packages updated.

Dean Mcdonald <dean@appdome.com> (c) Appdome, 2023.

This script analyzes Android APK files to detect various security-related properties and anti-tampering measures.
It performs static analysis on the provided app file to identify potential security risks and protections implemented.

The script supports Android (APK and ABB) app files and checks for the following:

- App permissions
- Debuggable flag
- Root detection
- Frida detection
- SSL/TLS pinning
- Anti-tampering protection
- Magisk detection
- Zygisk detection

Usage: python appdome_app_checker.py [APP_FILE]

Note: Ensure that the required dependencies (jadx, unzip, aapt, strings) are installed.

"""
# Send results to results.txt since lengthy results get lost on the screen.
# f = open("results.txt", "w")


import subprocess
import json
import sys
import os
from androguard.core.apk import APK
# from androguard.core.bytecodes.dvm import DalvikVMFormat
from androguard.core.analysis.analysis import Analysis

# Set the ANDROID_HOME environment variable
android_sdk_path = os.path.expanduser('~/Library/Android/sdk')  # Path to Android SDK directory
os.environ['ANDROID_HOME'] = android_sdk_path

# Check if a binary is available in the system PATH
def is_binary_available(binary):
    try:
        subprocess.check_output(['which', binary])
        return True
    except subprocess.CalledProcessError:
        return False

# Check for obfuscation and perform necessary checks based on the file extension
def check_for_obfuscation(file_path, file_extension):
    # Check if all required binaries are available
    #sys.stderr = open(os.devnull, 'w')
    required_binaries = ['jadx', 'unzip', 'strings']
    for binary in required_binaries:
        if not is_binary_available(binary):
            print(f"Error: {binary} not found. Use pip3 install {binary}")
            return

    # Create a temporary directory for extraction
    temp_dir = 'temp'
    os.makedirs(temp_dir, exist_ok=True)

    # Assign app_binary based on file extension
    app_binary = file_path if file_extension in ['.apk', '.aab'] else None

    # Extract the app
    if file_extension == '.apk' or file_extension == '.aab':  # Android app or app bundle
        check_android_permissions(app_binary)
        check_debuggable_android(app_binary)
        check_root_detection_android(app_binary)
        check_frida_detection_android(app_binary)
        check_ssl_pinning_android(app_binary)
        check_anti_tampering_protection_android(app_binary)
        check_magisk_detection(app_binary)
        check_zygisk_detection_android(app_binary)
    else:
        print(f"Unsupported file format. File extension received: {file_extension}")
        return

    # Cleanup temporary files
    subprocess.call(['rm', '-rf', temp_dir])
    sys.stderr = sys.__stderr__


# Check app permissions
f = open("permissions.txt", "w")
def check_android_permissions(app_binary):
    apk = APK(app_binary)
    permissions = apk.get_permissions()
    if permissions:
        print("I found these permissions:", file=f)
        for permission in permissions:
            print(" -", permission, file=f)

# Check if the app is debuggable
f = open("debug.txt", "w")
def check_debuggable_android(app_binary):
    apk = APK(app_binary)
    debuggable = apk.get_element('application', '{http://schemas.android.com/apk/res/android}debuggable')
    if debuggable == 'true':
        print("I found these permissions:", file=f)
        for permission in permissions:
            print("App is debuggable.", file=f)
    elif debuggable == 'false':
            print("App is not debuggable.", file=f)
    else:
            print("Unable to check debuggable status.", file=f)

# Check for root detection
f = open("root.txt", "w")
def check_root_detection_android(app_binary):
    # India
    """
    Check for root detection in the Android app.
    """
    apk = APK(app_binary)
    arsc_parser = apk.get_android_resources()
    if arsc_parser is not None:
        strings_set = set()
        for resource_key in arsc_parser.resource_keys:
            if resource_key.package_name == 'android' and resource_key.type_name == 'string':
                resource_value = arsc_parser.get_string(resource_key)
                if resource_value is not None:
                    strings_set.add(resource_value)
        if any('root' in string.lower() or 'su' in string.lower() for string in strings_set):
            print("The Android app has signs of root detection.", file=f)
        else:
            print("The Android app does not have signs of root detection.", file=f)
    else:
        print("Failed to retrieve Android resources.", file=f)

# Check for Frida detection
f = open("frida.txt", "w")
def check_frida_detection_android(app_binary):
    result = subprocess.check_output(['strings', app_binary])
    result_str = result.decode().lower()
    if 'frida' in result_str or "Frida" in result_str:
        print("The Android app has signs of Frida detection.", file=f)
    else:
        print("The Android app does not have signs of Frida detection.", file=f)

# Check for SSL/TLS pinning
f = open("ssl.txt", "w")
def check_ssl_pinning_android(app_binary):
    result = subprocess.check_output(['strings', app_binary])
    result_str = result.decode().lower()
    if 'sslpinning' in result_str or 'ssl pinning' in result_str:
        print("The Android app has signs of SSL/TLS pinning.", file=f)
    else:
        print("The Android app does not have signs of SSL/TLS pinning.", file=f)

# Check for anti-tampering protection
f = open("jadx.txt", "w")
def check_anti_tampering_protection_android(app_binary):
    result = subprocess.check_output(['jadx', app_binary])
    result_str = result.decode().lower()
    if 'j2waf' in result_str or 'tamper' in result_str:
        print("The Android app has signs of anti-tampering protection.", file=f)
    else:
        print("The Android app does not have signs of anti-tampering protection.", file=f)

# Check for Magisk detection
f = open("magisk.txt", "w")
def check_magisk_detection(app_binary):
    apk = APK(app_binary)
    manifest_data = apk.get_android_manifest_xml()
    magisk_detection = 'com.topjohnwu.magisk' in manifest_data
    if magisk_detection:
        print("The Android app has signs of Magisk Detection", file=f)
    else:
        print("The Android app does not have signs of Magisk Detection.", file=f)

# Check for Zygisk detection
f = open("zygisk.txt", "w")
def check_zygisk_detection_android(app_binary):
    result = subprocess.check_output(['strings', app_binary])
    result_str = result.decode().lower()
    if 'zygisk' in result_str or "Zygisk" in result_str:
        print("The Android app has signs of Zygisk detection.", file=f)
    else:
        print("The Android app does not have signs of Zygisk detection.", file=f)


# Main function
def main():
    if len(sys.argv) != 2:
        print("Usage: python appdome_app_checker.py [APP_FILE]")
        return

    app_file = sys.argv[1]
    file_name, file_extension = os.path.splitext(app_file)
    file_extension = file_extension.lower()

    if not os.path.isfile(app_file):
        print("Error: File not found.")
        return

    check_for_obfuscation(app_file, file_extension)


if __name__ == "__main__":
    main()

print("-=EOF=-", file=f)
f.close()
Save and run using,
Code:
python3 appdome_app_checker.py /sdcard/Download/FILENAME.APK
This will generate a lot of on screen code and numerous text files.
Examine the on screen code first.
Use
Code:
ls -lb *.txt
to see what outputs generated results.
Screenshot_20250526-172811.png

Note how a root test could not be performed?

There is at present, no known way to identify Appdome.
At best we can do all the usual tests using APKtool-M, MT, NP, ApkID to see if they even detect anything.
Keep in mind, it could easily be a false result as Appdome can do that.
If nothing shows up, try this method.

What are you looking for?
Inability to perform basic tests or tests with peculiar results.
If this happens, there is a good chance the apk is Appdome protected.

Install APKtool-M, and then install the apk in question and run it.
If the apk shuts down with a message that you have APKtool-M installed, you have an apk that is protected with government/commercial grade security.
The only good thing I can say is, because Appdome is a paid service, and the service must be maintained to work, not a whole lot of apk's use this uber security.

I, and others, will continue to endevour to find a means to thwart this security, however, the easier thing to do is not have APKtool-M on the same device.
 

Snailsoft

∞ and beyond!
Staff Member
Moderator
SB Mod Squad ⭐
✔ Approved Releaser
Active User
Member for 2 years
It should be noted that running an Appdome protected apk with APKtool/M installed is not a guarenteed method of testing as the app killing function based on the installation of APKtool/M is merely an option of Appdome.
 

Snailsoft

∞ and beyond!
Staff Member
Moderator
SB Mod Squad ⭐
✔ Approved Releaser
Active User
Member for 2 years
In reviewing some of the API injection code, one can see just an example of the protection scheme used by Appdome.

Code:
# This is a basic workflow for integrating appdome-api-python with your github project
# File location should be under .github/workflows/
name: Appdome Workflow

# Controls when the workflow will run
on:
  # Triggers the workflow on push or pull request events but only for the "main" branch
  push:
    branches: [ "main" ]
  pull_request:
    branches: [ "main" ]

  # Allows you to run this workflow manually from the Actions tab
  workflow_dispatch:

# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
  # A demo job that builds an application to use with appdome
  build_app:
    # The type of runner that the job will run on
    runs-on: ubuntu-latest

    # Steps represent a sequence of tasks that will be executed as part of the job
    steps:
      # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
      - uses: actions/checkout@v2
        with:
          path: main
          
     # Cloning appdome-api-python github repository
      - name: Cloning appdome-api-python github repository
        uses: actions/checkout@master
        with:
          repository: Appdome/appdome-api-python
          ref: refs/heads/main
          path: appdome

      # Runs a single command using the runners shell
      - name: Run a one-line script
        run: echo Building my app

      # Runs a set of commands using the runners shell
      - name: Run a multi-line script
        run: |
          echo Build app
          echo sign app
      
      # Run appdome api for Android apps
      - name: Appdome workflow for Android apps
        env: # creating needed environment variables using GitHub secrets
          APPDOME_API_KEY: ${{ secrets.APPDOME_API_KEY }}
          APPDOME_TEAM_ID: ${{ secrets.APPDOME_TEAM_ID }}
          APPDOME_ANDROID_FS_ID: ${{ secrets.APPDOME_ANDROID_FS_ID }}
        run: |
          mkdir outputs
          pip3 install -r appdome/requirements.txt
          python3 appdome/appdome-api-python/appdome_api.py --app <apk/aab file> --sign_on_appdome --keystore <keystore file> \
                  --keystore_pass <keystore password> --keystore_alias <key alias> --key_pass <key password> \
                  --output outputs/<output apk/aab> --certificate_output outputs/<output certificate pdf>
          ls outputs
          
      # Run appdome api for iOS apps
      - name: Appdome workflow for Android apps
        env: # creating needed environment variables using GitHub secrets
          APPDOME_API_KEY: ${{ secrets.APPDOME_API_KEY }}
          APPDOME_TEAM_ID: ${{ secrets.APPDOME_TEAM_ID }}
          APPDOME_IOS_FS_ID: ${{ secrets.APPDOME_IOS_FS_ID }}
        run: |
          mkdir outputs
          pip3 install -r appdome/requirements.txt
          python3 appdome/appdome-api-python/appdome_api.py --app <ipa file> --sign_on_appdome --keystore <p12 file> \
                  --keystore_pass <p12 password> --provisioning_profiles <provisioning profile file> <another provisioning profile file if needed> \
                  --entitlements <entitlements file> <another entitlements file if needed> --output outputs/<output ipa> 
                  --certificate_output outputs/<output certificate pdf>
          ls outputs
      
      # Upload fuse output as an artifacts
      - name: Upload artifacts
        uses: actions/upload-artifact@v3
        with:
          name: workflow-artifacts
          path: outputs # path to output folder
The apk is turned into a client to Appdome's server, not unlike PairIP, only the entire source code gets processed.
 

Snailsoft

∞ and beyond!
Staff Member
Moderator
SB Mod Squad ⭐
✔ Approved Releaser
Active User
Member for 2 years
I have partially recompiled a protected app.

I found this in the manifest
Code:
  <receiver
            android:name="androidx.work.impl.background.systemalarm.ConstraintProxyUpdateReceiver"
            android:enabled="@bool/enable_system_alarm_service_default"
            android:exported="false"
            android:directBootAware="false">
            <intent-filter>
                <action
                    android:name="androidx.work.impl.background.systemalarm.UpdateProxies" />
            </intent-filter>
        </receiver>
        <receiver
            android:name="androidx.work.impl.diagnostics.DiagnosticsReceiver"
            android:permission="android.permission.DUMP"
            android:enabled="true"
            android:exported="true"
            android:directBootAware="false">
            <intent-filter>
                <action
                    android:name="androidx.work.diagnostics.REQUEST_DIAGNOSTICS" />
            </intent-filter>
        </receiver>
Silently connecting to a server via proxy, acquires data, and does a diagnostic comparison for code alterations.
 

Snailsoft

∞ and beyond!
Staff Member
Moderator
SB Mod Squad ⭐
✔ Approved Releaser
Active User
Member for 2 years
Ugh. So, this 218MB game has 182MB of ad's code, mainly AppLovin but a wide assortment of others.
That leaves the game as actually being 36MB. Only I tally 32MB of code.
This means that 4MB of the games code is server side.
Even if I do manage to bypass this beast, I can tell the process will only work for this game, and not for the Appdome protection in general.
Enough whining. I'll post again if I get a major breakthrough.
 

Sbenny

A crazy scientist
Staff Member
Admin
SB Mod Squad ⭐
✔ Approved Releaser
Active User
Wow man, you did a very extensive research on this topic and I do greatly admire you for this. Hopefully Appdome won't be spread as PairIP or we'll seriously need to face yet another hard challenge!
 
The Cursed Castle - Online RPG on Google Play
Top