├── packages.txt ├── helper ├── app │ ├── .gitignore │ ├── src │ │ ├── main │ │ │ ├── res │ │ │ │ ├── values │ │ │ │ │ ├── strings.xml │ │ │ │ │ ├── colors.xml │ │ │ │ │ └── themes.xml │ │ │ │ ├── mipmap-hdpi │ │ │ │ │ ├── ic_launcher.webp │ │ │ │ │ └── ic_launcher_round.webp │ │ │ │ ├── mipmap-mdpi │ │ │ │ │ ├── ic_launcher.webp │ │ │ │ │ └── ic_launcher_round.webp │ │ │ │ ├── mipmap-xhdpi │ │ │ │ │ ├── ic_launcher.webp │ │ │ │ │ └── ic_launcher_round.webp │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ │ ├── ic_launcher.webp │ │ │ │ │ └── ic_launcher_round.webp │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ │ ├── ic_launcher.webp │ │ │ │ │ └── ic_launcher_round.webp │ │ │ │ ├── mipmap-anydpi-v26 │ │ │ │ │ ├── ic_launcher.xml │ │ │ │ │ └── ic_launcher_round.xml │ │ │ │ ├── layout │ │ │ │ │ └── activity_main.xml │ │ │ │ ├── values-night │ │ │ │ │ └── themes.xml │ │ │ │ ├── drawable-v24 │ │ │ │ │ └── ic_launcher_foreground.xml │ │ │ │ └── drawable │ │ │ │ │ └── ic_launcher_background.xml │ │ │ ├── java │ │ │ │ └── com │ │ │ │ │ └── research │ │ │ │ │ └── helper │ │ │ │ │ ├── MainActivity.kt │ │ │ │ │ ├── AutoStart.kt │ │ │ │ │ └── SendGPS.kt │ │ │ └── AndroidManifest.xml │ │ ├── test │ │ │ └── java │ │ │ │ └── com │ │ │ │ └── research │ │ │ │ └── helper │ │ │ │ └── ExampleUnitTest.kt │ │ └── androidTest │ │ │ └── java │ │ │ └── com │ │ │ └── research │ │ │ └── helper │ │ │ └── ExampleInstrumentedTest.kt │ ├── proguard-rules.pro │ └── build.gradle ├── .idea │ ├── .gitignore │ ├── compiler.xml │ ├── misc.xml │ └── gradle.xml ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── .gitignore ├── settings.gradle ├── build.gradle ├── gradle.properties ├── gradlew.bat └── gradlew ├── requirements.txt ├── exceptions.py ├── timeout.py ├── Readme.md ├── interactor_parameters.json ├── js ├── debug.js ├── fs.js ├── media.js ├── bypass_root_detection.js ├── general.js ├── native.js ├── built_in_crypto.js ├── java.js └── pinning.js ├── amplification.py ├── static.py ├── main.py ├── dynamic.py ├── device.py └── interactor.py /packages.txt: -------------------------------------------------------------------------------- 1 | pkg.name -------------------------------------------------------------------------------- /helper/app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /helper/.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /helper/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | helper 3 | -------------------------------------------------------------------------------- /helper/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Madiba-Research/ThirdEye/HEAD/helper/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /helper/app/src/main/res/mipmap-hdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Madiba-Research/ThirdEye/HEAD/helper/app/src/main/res/mipmap-hdpi/ic_launcher.webp -------------------------------------------------------------------------------- /helper/app/src/main/res/mipmap-mdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Madiba-Research/ThirdEye/HEAD/helper/app/src/main/res/mipmap-mdpi/ic_launcher.webp -------------------------------------------------------------------------------- /helper/app/src/main/res/mipmap-xhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Madiba-Research/ThirdEye/HEAD/helper/app/src/main/res/mipmap-xhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /helper/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Madiba-Research/ThirdEye/HEAD/helper/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /helper/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Madiba-Research/ThirdEye/HEAD/helper/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /helper/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Madiba-Research/ThirdEye/HEAD/helper/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /helper/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Madiba-Research/ThirdEye/HEAD/helper/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /helper/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Madiba-Research/ThirdEye/HEAD/helper/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | androidviewclient 2 | uiautomator 3 | translators==5.4.2 4 | androguard==3.4.0a1 5 | frida 6 | frida-tools 7 | mitmproxy 8 | xmltodict 9 | dpkt 10 | -------------------------------------------------------------------------------- /helper/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Madiba-Research/ThirdEye/HEAD/helper/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /helper/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Madiba-Research/ThirdEye/HEAD/helper/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /helper/.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /helper/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sat Oct 16 01:45:11 EDT 2021 2 | distributionBase=GRADLE_USER_HOME 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip 4 | distributionPath=wrapper/dists 5 | zipStorePath=wrapper/dists 6 | zipStoreBase=GRADLE_USER_HOME 7 | -------------------------------------------------------------------------------- /helper/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | .cxx 15 | local.properties 16 | -------------------------------------------------------------------------------- /helper/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /exceptions.py: -------------------------------------------------------------------------------- 1 | class DeviceNotFound(Exception): 2 | # def __init__(self, salary=12, message="Salary is not in (5000, 15000) range"): 3 | # self.salary = salary 4 | # self.message = message 5 | # super().__init__(self.message) 6 | 7 | def __str__(self): 8 | return 'no devices/emulators found' 9 | 10 | -------------------------------------------------------------------------------- /helper/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /helper/settings.gradle: -------------------------------------------------------------------------------- 1 | dependencyResolutionManagement { 2 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) 3 | repositories { 4 | google() 5 | mavenCentral() 6 | jcenter() // Warning: this repository is going to shut down soon 7 | } 8 | } 9 | rootProject.name = "helper" 10 | include ':app' 11 | -------------------------------------------------------------------------------- /helper/.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9 | -------------------------------------------------------------------------------- /helper/app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FFBB86FC 4 | #FF6200EE 5 | #FF3700B3 6 | #FF03DAC5 7 | #FF018786 8 | #FF000000 9 | #FFFFFFFF 10 | -------------------------------------------------------------------------------- /helper/app/src/test/java/com/research/helper/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package com.research.helper 2 | 3 | import org.junit.Test 4 | 5 | import org.junit.Assert.* 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * See [testing documentation](http://d.android.com/tools/testing). 11 | */ 12 | class ExampleUnitTest { 13 | @Test 14 | fun addition_isCorrect() { 15 | assertEquals(4, 2 + 2) 16 | } 17 | } -------------------------------------------------------------------------------- /helper/app/src/main/java/com/research/helper/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.research.helper 2 | 3 | import android.content.Intent 4 | import androidx.appcompat.app.AppCompatActivity 5 | import android.os.Bundle 6 | 7 | class MainActivity : AppCompatActivity() { 8 | override fun onCreate(savedInstanceState: Bundle?) { 9 | super.onCreate(savedInstanceState) 10 | // val number5 = Intent(baseContext, HelperService::class.java) 11 | // number5.putExtra("times", 5) 12 | // startForegroundService(number5) 13 | finish() 14 | } 15 | } -------------------------------------------------------------------------------- /timeout.py: -------------------------------------------------------------------------------- 1 | import signal 2 | import traceback 3 | 4 | class timeout: 5 | def __init__(self, seconds=1, error_message="Timeout"): 6 | self.seconds = seconds 7 | self.error_message = error_message 8 | 9 | def handle_timeout(self, signum, frame): 10 | traceback.print_exc() 11 | raise TimeoutError(self.error_message) 12 | 13 | def __enter__(self): 14 | signal.signal(signal.SIGALRM, self.handle_timeout) 15 | signal.alarm(self.seconds) 16 | 17 | def __exit__(self, type, value, traceback): 18 | signal.alarm(0) 19 | -------------------------------------------------------------------------------- /helper/build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | buildscript { 3 | repositories { 4 | google() 5 | mavenCentral() 6 | } 7 | dependencies { 8 | classpath "com.android.tools.build:gradle:7.0.2" 9 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.31" 10 | 11 | // NOTE: Do not place your application dependencies here; they belong 12 | // in the individual module build.gradle files 13 | } 14 | } 15 | 16 | task clean(type: Delete) { 17 | delete rootProject.buildDir 18 | } -------------------------------------------------------------------------------- /helper/app/src/androidTest/java/com/research/helper/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.research.helper 2 | 3 | import androidx.test.platform.app.InstrumentationRegistry 4 | import androidx.test.ext.junit.runners.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 22 | assertEquals("com.research.helper", appContext.packageName) 23 | } 24 | } -------------------------------------------------------------------------------- /helper/.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 19 | 20 | -------------------------------------------------------------------------------- /helper/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /helper/app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 17 | 18 | -------------------------------------------------------------------------------- /helper/app/src/main/res/values/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16 | -------------------------------------------------------------------------------- /helper/app/src/main/res/values-night/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | **Implementation of CCS'2022 paper "Hidden in Plain Sight: Exploring Encrypted Channels in Android Apps"** 2 | 3 | - For inspiration only: This tool is not ready to use out of the box and requires some modification. 4 | - It mainly requires modification of the device.py file. Primarily, focus on the window and activity. 5 | - There are no plans to maintain, develop, or provide support for this tool. 6 | ----- 7 | ``` 8 | flame:/ # mkdir /data/crontab 9 | flame:/ # echo '* * * * * svc wifi enable' >> /data/crontab/root 10 | flame:/ # echo '* * * * * settings put global airplane_mode_on 0' >> /data/crontab/root 11 | flame:/ # echo 'crond -b -c /data/crontab' > /data/adb/service.d/crond.sh 12 | flame:/ # chmod +x /data/adb/service.d/crond.sh 13 | settings put global captive_portal_mode 0 14 | 15 | adb shell content insert --uri content://settings/system --bind name:s:accelerometer_rotation --bind value:i:0 16 | ``` 17 | -------------------------------------------------------------------------------- /helper/gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | # AndroidX package structure to make it clearer which packages are bundled with the 15 | # Android operating system, and which are packaged with your app"s APK 16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 17 | android.useAndroidX=true 18 | # Automatically convert third-party libraries to use AndroidX 19 | android.enableJetifier=true 20 | # Kotlin code style for this project: "official" or "obsolete": 21 | kotlin.code.style=official -------------------------------------------------------------------------------- /helper/app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.application' 3 | id 'kotlin-android' 4 | } 5 | 6 | android { 7 | compileSdk 31 8 | 9 | defaultConfig { 10 | applicationId "com.research.helper" 11 | minSdk 29 12 | targetSdk 31 13 | versionCode 1 14 | versionName "1.0" 15 | 16 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 17 | } 18 | 19 | buildTypes { 20 | release { 21 | minifyEnabled false 22 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 23 | } 24 | } 25 | compileOptions { 26 | sourceCompatibility JavaVersion.VERSION_1_8 27 | targetCompatibility JavaVersion.VERSION_1_8 28 | } 29 | kotlinOptions { 30 | jvmTarget = '1.8' 31 | } 32 | } 33 | 34 | dependencies { 35 | 36 | implementation 'androidx.core:core-ktx:1.3.2' 37 | implementation 'androidx.appcompat:appcompat:1.2.0' 38 | implementation 'com.google.android.material:material:1.3.0' 39 | implementation 'androidx.constraintlayout:constraintlayout:2.0.4' 40 | testImplementation 'junit:junit:4.+' 41 | androidTestImplementation 'androidx.test.ext:junit:1.1.2' 42 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' 43 | } -------------------------------------------------------------------------------- /helper/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 17 | 20 | 21 | 22 | 23 | 24 | 28 | 29 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /helper/app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /helper/app/src/main/java/com/research/helper/AutoStart.kt: -------------------------------------------------------------------------------- 1 | package com.research.helper 2 | 3 | 4 | import android.content.Intent 5 | 6 | import android.content.BroadcastReceiver 7 | import android.content.Context 8 | import android.util.Log 9 | import java.lang.String 10 | import android.location.LocationManager 11 | import android.location.Location 12 | import android.os.SystemClock 13 | 14 | 15 | class AutoStart : BroadcastReceiver() { 16 | override fun onReceive(context: Context, arg1: Intent) { 17 | var lat = 45.4950378 18 | var lon = -73.5779508 19 | var alt = 5.0 20 | var accurate = 0.5f 21 | Log.i( 22 | "logTag", 23 | String.format( 24 | "setting mock to Latitude=%f, Longitude=%f Altitude=%f Accuracy=%f", 25 | lat, 26 | lon, 27 | alt, 28 | accurate 29 | ) 30 | ) 31 | 32 | setLocation(LocationManager.GPS_PROVIDER, context, lat, lon, alt, accurate) 33 | setLocation(LocationManager.NETWORK_PROVIDER, context, lat, lon, alt, accurate) 34 | 35 | 36 | } 37 | 38 | fun setLocation( 39 | provider: kotlin.String, 40 | context: Context, 41 | lat: Double, 42 | lon: Double, 43 | alt: Double, 44 | accuracy: Float 45 | ) { 46 | var lm = context.getSystemService( 47 | Context.LOCATION_SERVICE 48 | ) as LocationManager 49 | lm.addTestProvider( 50 | provider, false, false, false, false, false, 51 | true, true, 1, 1 52 | ) 53 | val mockLocation = Location(provider) 54 | mockLocation.latitude = lat 55 | mockLocation.longitude = lon 56 | mockLocation.altitude = alt 57 | mockLocation.time = System.currentTimeMillis() 58 | mockLocation.accuracy = accuracy 59 | mockLocation.elapsedRealtimeNanos = SystemClock.elapsedRealtimeNanos() 60 | lm.setTestProviderEnabled(provider, true) 61 | lm.setTestProviderLocation(provider, mockLocation) 62 | } 63 | } 64 | 65 | -------------------------------------------------------------------------------- /interactor_parameters.json: -------------------------------------------------------------------------------- 1 | { 2 | "keywords": { 3 | "none of the above": [], 4 | "not now": [], 5 | "get code": [], 6 | "approv": [], 7 | "509-6730": [], 8 | "read": ["already"], 9 | "scan": [], 10 | "resend": [], 11 | "woman": [], 12 | "women": [], 13 | "female": [], 14 | "global": [], 15 | "sajjad": [], 16 | "country": [], 17 | "canada": [], 18 | "quebec": [], 19 | "18": [], 20 | "skip": [], 21 | "gmail.com": [], 22 | "start": [ 23 | "startup" 24 | ], 25 | "agree": [ 26 | "disagree","agreement" 27 | ], 28 | "next": [], 29 | "allow": [ 30 | "disallow" 31 | ], 32 | "continue": [ 33 | "continue with" 34 | ], 35 | "confirm": [], 36 | "accept": [ 37 | "not accept" 38 | ], 39 | "checkbox": [], 40 | "without": [], 41 | "google": ["share"], 42 | "register": [], 43 | "sign": [ 44 | "help", 45 | "can" 46 | ], 47 | "login": [ 48 | "help", 49 | "can", 50 | "facebook", 51 | "google" 52 | ], 53 | "log in": [ 54 | "help", 55 | "can", 56 | "facebook", 57 | "google" 58 | ], 59 | "sign in": [ 60 | "help", 61 | "can", 62 | "facebook", 63 | "google" 64 | ], 65 | "enable": [], 66 | "submit": [], 67 | "retry": [], 68 | "again": [], 69 | "current": [], 70 | "i don": [], 71 | "find": [], 72 | "guest": [], 73 | "ok": [ 74 | "facebook", 75 | "book" 76 | ], 77 | "test": [], 78 | "done": [], 79 | "use": [ 80 | "user" 81 | ], 82 | "go": [ 83 | "forgot", 84 | "good", 85 | "google" 86 | ], 87 | "an account": [], 88 | "later": [], 89 | "comment": [], 90 | "quick": [], 91 | "close": [], 92 | "send": [], 93 | "english": [], 94 | "dismiss": [], 95 | "cancel": [], 96 | "great": [], 97 | "navigation": [], 98 | "settings": [], 99 | "free": [], 100 | "limit": [], 101 | "tap": [], 102 | "click": [], 103 | "icon": [] 104 | }, 105 | "input": { 106 | "username": "admin88888888", 107 | "email": "ev010.173@gmail.com", 108 | "e-mail": "ev010.173@gmail.com", 109 | "name": "admin88888888", 110 | "mobile number": "+888888888888", 111 | "phone number": "9158888888", 112 | "phone": "9158888888", 113 | "password": "P4ss@88888888", 114 | "code": "9188", 115 | "pin": "1370", 116 | "city": "Montreal", 117 | "id": "9188888888", 118 | "How old": "45", 119 | "YYYY": "1991", 120 | "search": "search88888888" 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /js/debug.js: -------------------------------------------------------------------------------- 1 | setTimeout(function () { 2 | Java.perform(function () { 3 | var androidSettings = ['adb_enabled']; 4 | var settingGlobal = Java.use('android.provider.Settings$Global'); 5 | 6 | settingGlobal.getInt.overload('android.content.ContentResolver', 'java.lang.String').implementation = function (cr, name) { 7 | if (name == androidSettings[0]) { 8 | return 0; 9 | } 10 | var ret = this.getInt(cr, name); 11 | return ret; 12 | } 13 | 14 | settingGlobal.getInt.overload('android.content.ContentResolver', 'java.lang.String', 'int').implementation = function (cr, name, def) { 15 | if (name == (androidSettings[0])) { 16 | return 0; 17 | } 18 | var ret = this.getInt(cr, name, def); 19 | return ret; 20 | } 21 | 22 | settingGlobal.getFloat.overload('android.content.ContentResolver', 'java.lang.String').implementation = function (cr, name) { 23 | if (name == androidSettings[0]) { 24 | return 0; 25 | } 26 | var ret = this.getFloat(cr, name); 27 | return ret; 28 | } 29 | 30 | settingGlobal.getFloat.overload('android.content.ContentResolver', 'java.lang.String', 'float').implementation = function (cr, name, def) { 31 | if (name == androidSettings[0]) { 32 | return 0; 33 | } 34 | var ret = this.getFloat(cr, name, def); 35 | return ret; 36 | } 37 | 38 | settingGlobal.getLong.overload('android.content.ContentResolver', 'java.lang.String').implementation = function (cr, name) { 39 | if (name == androidSettings[0]) { 40 | return 0; 41 | } 42 | var ret = this.getLong(cr, name); 43 | return ret; 44 | } 45 | 46 | settingGlobal.getLong.overload('android.content.ContentResolver', 'java.lang.String', 'long').implementation = function (cr, name, def) { 47 | if (name == androidSettings[0]) { 48 | return 0; 49 | } 50 | var ret = this.getLong(cr, name, def); 51 | return ret; 52 | } 53 | 54 | settingGlobal.getString.overload('android.content.ContentResolver', 'java.lang.String').implementation = function (cr, name) { 55 | if (name == androidSettings[0]) { 56 | var stringClass = Java.use("java.lang.String"); 57 | var stringInstance = stringClass.$new("0"); 58 | 59 | console.log('[+]Global.getString(cr, name) Bypassed'); 60 | return stringInstance; 61 | } 62 | var ret = this.getString(cr, name); 63 | return ret; 64 | } 65 | 66 | }); 67 | }, 0); 68 | -------------------------------------------------------------------------------- /helper/app/src/main/java/com/research/helper/SendGPS.kt: -------------------------------------------------------------------------------- 1 | package com.research.helper 2 | 3 | import android.content.Intent 4 | 5 | import android.content.BroadcastReceiver 6 | import android.content.Context 7 | import android.util.Log 8 | import java.lang.String 9 | import android.location.LocationManager 10 | import android.location.Location 11 | import android.os.Build 12 | import android.os.SystemClock 13 | 14 | 15 | class SendGPS : BroadcastReceiver() { 16 | override fun onReceive(context: Context, intent: Intent) { 17 | 18 | Log.i("research", intent.action.toString()) 19 | 20 | var lat = 21 | if (intent.getStringExtra("lat") != null) intent.getStringExtra("lat")!! 22 | .toDouble() else "0".toDouble() 23 | var lon = 24 | if (intent.getStringExtra("lon") != null) intent.getStringExtra("lon")!! 25 | .toDouble() else "0".toDouble() 26 | var alt = 27 | if (intent.getStringExtra("alt") != null) intent.getStringExtra("alt")!! 28 | .toDouble() else "0".toDouble() 29 | var accurate = 30 | if (intent.getStringExtra("accurate") != null) intent.getStringExtra("accurate")!! 31 | .toFloat() else "0".toFloat() 32 | Log.i( 33 | "logTag", 34 | String.format( 35 | "setting mock to Latitude=%f, Longitude=%f Altitude=%f Accuracy=%f", 36 | lat, 37 | lon, 38 | alt, 39 | accurate 40 | ) 41 | ) 42 | 43 | setLocation(LocationManager.GPS_PROVIDER, context, lat, lon, alt, accurate) 44 | setLocation(LocationManager.NETWORK_PROVIDER, context, lat, lon, alt, accurate) 45 | // val intent = Intent(context, HelperService::class.java) 46 | // context.startForegroundService(intent) 47 | 48 | } 49 | 50 | 51 | fun setLocation( 52 | provider: kotlin.String, 53 | context: Context, 54 | lat: Double, 55 | lon: Double, 56 | alt: Double, 57 | accuracy: Float 58 | ) { 59 | var lm = context.getSystemService( 60 | Context.LOCATION_SERVICE 61 | ) as LocationManager 62 | lm.addTestProvider( 63 | provider, false, false, false, false, false, 64 | true, true, 1, 1 65 | ) 66 | val mockLocation = Location(provider) 67 | mockLocation.latitude = lat 68 | mockLocation.longitude = lon 69 | mockLocation.altitude = alt 70 | mockLocation.time = System.currentTimeMillis() 71 | mockLocation.accuracy = accuracy 72 | mockLocation.elapsedRealtimeNanos = SystemClock.elapsedRealtimeNanos() 73 | lm.setTestProviderEnabled(provider, true) 74 | lm.setTestProviderLocation(provider, mockLocation) 75 | } 76 | } -------------------------------------------------------------------------------- /helper/gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /js/fs.js: -------------------------------------------------------------------------------- 1 | 2 | Interceptor.attach(Module.findExportByName("libc.so", "open"), { 3 | onEnter: function (args) { 4 | //console.log("Hi:"+args[0].readUtf8String()) 5 | this.name = args[0].readUtf8String(); 6 | this.flag = parseInt(args[1]); 7 | if (this.name.startsWith("/storage") && !this.name.startsWith("/storage/emulated/0/Android/obb") && !this.name.startsWith("/storage/emulated/0/Android/data")) { 8 | 9 | console.log("--->" + this.name); 10 | } 11 | }, 12 | onLeave(retval) { 13 | if (this.name.startsWith("/storage") && !this.name.startsWith("/storage/emulated/0/Android/obb") && !this.name.startsWith("/storage/emulated/0/Android/data")) { 14 | Files.set(parseInt(retval), this.name); 15 | send('{"fs":{"function":"open","fd":' + parseInt(retval) + ',"path":"' + this.name + '","flag":"' + this.flag + '"}}'); 16 | } 17 | } 18 | }); 19 | 20 | Interceptor.attach(Module.findExportByName("libc.so", "remove"), { 21 | onEnter: function (args) { 22 | this.name = args[0].readUtf8String(); 23 | }, 24 | onLeave(retval) { 25 | if (this.name.startsWith("/storage")) { 26 | send('{"fs":{"function":"remove","status":' + parseInt(retval) + ',"path":"' + this.name + '"}}'); 27 | } 28 | } 29 | }); 30 | 31 | Interceptor.attach(Module.findExportByName("libc.so", "rename"), { 32 | onEnter: function (args) { 33 | this.src = args[0].readUtf8String(); 34 | this.dst = args[1].readUtf8String(); 35 | }, 36 | onLeave(retval) { 37 | if (((this.src.startsWith("/storage") && !this.src.startsWith("/storage/emulated/0/Android/obb") && !this.src.startsWith("/storage/emulated/0/Android/data")) || (this.dst.startsWith("/storage") && !this.dst.startsWith("/storage/emulated/0/Android/obb") && !this.dst.startsWith("/storage/emulated/0/Android/data"))) && parseInt(retval) == 0) { 38 | send('{"fs":{"function":"rename","source":"' + this.src + '","destination":"' + this.dst + '"}}'); 39 | } 40 | } 41 | }); 42 | 43 | 44 | Interceptor.attach(Module.findExportByName("libc.so", "read"), { 45 | onEnter: function (args) { 46 | this.fd = parseInt(args[0]) 47 | this.addr = args[1] 48 | }, 49 | onLeave(retval) { 50 | var len = parseInt(retval); 51 | if (len != 0xFFFFFFFF && len != 0x0 && Files.has(this.fd)) { 52 | send('{"fs":{"function":"read","fd":' + this.fd + ',"path":"' + Files.get(this.fd) + '","data":"' + arrayBufferToBase64(Memory.readByteArray(this.addr, len)) + '"}}'); 53 | } 54 | } 55 | }); 56 | 57 | Interceptor.attach(Module.findExportByName("libc.so", "close"), { 58 | onEnter: function (args) { 59 | if (Files.has(this.fd)){ 60 | send('{"fs":{"function":"close","fd":' + parseInt(retval) + '"}}'); 61 | Files.delete(parseInt(args[0])); 62 | } 63 | } 64 | }); 65 | 66 | Interceptor.attach(Module.findExportByName("libc.so", "write"), { 67 | onEnter: function (args) { 68 | this.fd = parseInt(args[0]) 69 | this.addr = args[1] 70 | }, 71 | onLeave(retval) { 72 | var len = parseInt(retval); 73 | if (len != 0xFFFFFFFF && Files.has(this.fd)) { 74 | send('{"fs":{"function":"write","fd":' + this.fd + ',"path":"' + Files.get(this.fd) + '","data":"' + arrayBufferToBase64(Memory.readByteArray(this.addr, len)) + '"}}'); 75 | } 76 | } 77 | }); 78 | -------------------------------------------------------------------------------- /amplification.py: -------------------------------------------------------------------------------- 1 | import os 2 | import json 3 | import dpkt 4 | import socket 5 | import pickle 6 | from multiprocessing.pool import ThreadPool 7 | 8 | def amplification_ratio(info): 9 | pkg, addr, port, data = info 10 | res_len = 0 11 | _d = bytes() 12 | try: 13 | sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 14 | d_len = len(data) 15 | _sent = sock.sendto(data, (addr, port)) 16 | sock.settimeout(10) 17 | while True: 18 | r_data, server = sock.recvfrom(10*10000) 19 | _d += r_data 20 | except: 21 | if len(_d)/d_len > 1: 22 | return (pkg, addr, port, data, _d) 23 | 24 | leaks = [] 25 | hardcoded_keys = [] 26 | all_packets = set() 27 | words_path = [] 28 | all_words = [] 29 | c = 0 30 | for root, dirnames, filenames in os.walk("./"): 31 | for filename in filenames: 32 | if filename.endswith(".pcap"): 33 | conns_files = [] 34 | local_addrs = set() 35 | if os.path.exists(root+"/conn-1.txt"): 36 | conns_file_1 = open(os.path.join(root, "conn-1.txt"), "rb") 37 | conns_files += conns_file_1.readlines() 38 | conns_file_1.close() 39 | if os.path.exists(root+"/conn-2.txt"): 40 | conns_file_2 = open(os.path.join(root, "conn-2.txt"), "rb") 41 | conns_files += conns_file_2.readlines() 42 | conns_file_2.close() 43 | 44 | for conn in conns_files: 45 | if (b"'udp'" in conn or b"'udp6'" in conn) and (not (b":53'," in conn or b":1900'," in conn or b":443'," in conn or b":123'," in conn or b":0'," in conn or b"'null" in conn)) and (b"local_address': '10.42.0" in conn or b"local_address': '/10.42.0" in conn or b"local_address': '::ffff:10.42.0" in conn): 46 | cj = json.loads( 47 | conn.decode().strip().replace("'", '"')) 48 | local_addr = cj["java"]["local_address"] if "java" in cj else cj["native"]["local_address"] 49 | local_addrs.add(local_addr.replace( 50 | "::ffff:", "").replace("/", "")) 51 | if len(local_addrs) == 0: 52 | continue 53 | _pcap = open(root+"/"+filename, "rb") 54 | for _, pkt in dpkt.pcap.Reader(_pcap): 55 | packet = dpkt.ethernet.Ethernet(pkt) 56 | if type(packet.data) == dpkt.ip.IP and type(packet.data.data) == dpkt.udp.UDP and len(packet.data.src) == 4: 57 | if socket.inet_ntoa(packet.data.src)+":"+str(packet.data.data.sport) in local_addrs and int(packet.data.data.dport) not in (53, 0) and not (packet.data.dst[0] == 255 or packet.data.dst[0] in range(224, 240)): 58 | all_packets.add((root, socket.inet_ntoa(packet.data.dst), int( 59 | packet.data.data.dport), packet.data.data.data)) 60 | 61 | 62 | 63 | 64 | amplification_rates = dict() 65 | _ar = list() 66 | with ThreadPool(100) as p: 67 | _ar += [data for data in (p.map(amplification_ratio, all_packets)) if data] 68 | with ThreadPool(500) as p: 69 | for (pkg, addr, port, data, rdata) in [data for data in (p.map(amplification_ratio, all_packets)) if data]+_ar: 70 | pkg_name = pkg.split("/")[-1] 71 | rate = len(rdata)/len(data) 72 | if pkg_name in amplification_rates: 73 | if rate > amplification_rates[pkg_name]: 74 | amplification_rates[pkg_name] = rate 75 | else: 76 | amplification_rates[pkg_name] = rate 77 | 78 | with open('amplification_ratio.json', 'w') as outfile: 79 | json.dump(amplification_rates, outfile, indent=4) 80 | -------------------------------------------------------------------------------- /static.py: -------------------------------------------------------------------------------- 1 | import os 2 | import re 3 | from zipfile import BadZipFile 4 | 5 | from androguard.core.bytecodes.apk import APK 6 | from androguard.core.bytecodes.dvm import DalvikVMFormat 7 | 8 | 9 | class Package: 10 | def __init__(self, package, output="out/"): 11 | self.dexes = set() 12 | for apk_path in [output+package+'/'+f for f in os.listdir( 13 | output+package+'/') if re.match(r'.+\.apk', f)]: 14 | try: 15 | for dex in APK(apk_path).get_all_dex(): 16 | try: 17 | self.dexes.add(DalvikVMFormat(dex)) 18 | except: 19 | pass 20 | # break 21 | except BadZipFile: 22 | (os.remove(apk_path) for apk_path in [ 23 | output+package+'/'+f for f in os.listdir(output+package+'/') if re.match(r'.+\.apk', f)]) 24 | os.system('kill -9 {pid}'.format(pid=os.getpid())) 25 | 26 | def get_methods(self, filter): 27 | methods = set() 28 | for dex in self.dexes: 29 | for method in dex.get_methods(): 30 | if method.get_name().endswith(b"-impl"): 31 | continue 32 | new_method = Method() 33 | new_method.into(method) 34 | if method.get_name().decode("utf-8").find(' ') == -1 and method.get_name().decode("utf-8").find('-') == -1 and method.class_name.decode("utf-8").find('$') == -1 and method.get_descriptor().decode("utf-8").find('$') == -1 and not method.class_name.decode("utf-8").endswith("Impl;") and not (method.get_access_flags() & 0x100) == 0x100: 35 | if filter(new_method): 36 | print(method.class_name) 37 | print(method.get_name()) 38 | methods.add(new_method) 39 | return methods 40 | 41 | 42 | class Method: 43 | def into(self, method): 44 | self.class_name = (method.get_class_name( 45 | )[1:].decode("utf-8").rstrip(';').replace('/', '.')) 46 | self.name = ("$init" if method.get_name().decode("utf-8") == 47 | "" else method.get_name().decode("utf-8")) 48 | self.params, self.return_type = description_mapper( 49 | method.get_descriptor()) 50 | 51 | def to_jni(self): 52 | return "Java_"+jni_translation(self.class_name).replace('.', "_")+"_"+jni_translation(self.name) 53 | 54 | def to_frida(self, body=lambda method, body: "", ret=""): 55 | params = {} 56 | for i, param in enumerate(self.params): 57 | params["p"+str(i)] = param 58 | if ret == "": 59 | ret = "return this."+self.name + \ 60 | "("+(", ".join([key for key, value in params.items()]))+")" 61 | return "try{Java.use('"+self.class_name+"')."+self.name+".overload("+("" if len(params) == 0 else "'"+"', '".join([value for key, value in params.items()])+"'")+").implementation = function ("+", ".join([key for key, value in params.items()])+") {"+body(self, params)+";"+ret+"};} catch(_e) {}" 62 | 63 | 64 | def jni_translation(_str): 65 | return _str.replace('_', '_1').replace(';', '_2').replace('[', '_3') 66 | 67 | 68 | def description_mapper(description): 69 | types = { 70 | "V": "void", 71 | "Z": "boolean", 72 | "B": "byte", 73 | "S": "short", 74 | "C": "char", 75 | "I": "int", 76 | "J": "long", 77 | "F": "float", 78 | "D": "double", 79 | } 80 | description, return_type = description.split(")", 1) 81 | param_list = list() 82 | return_type = types[return_type] if return_type in types else "[" + \ 83 | types[return_type[1:]] if return_type[1:] in types else return_type.replace( 84 | "/", ".") 85 | ret = return_type if type(return_type) == str else return_type.decode() 86 | 87 | for params in description[1:].split(" "): 88 | if params in types and params[0] != "[": 89 | param_list.append((types[params] if type( 90 | types[params]) == str else types[params].decode())) 91 | elif len(params) != 0: 92 | if len(params) != 0 and params[0] == "[": 93 | param_list.append("["+(params[1:] if type(params[1:]) 94 | == str else params[1:].decode()).replace("/", ".")) 95 | else: 96 | param_list.append((params[1:len(params)-1] if type(params[1:len( 97 | params)-1]) == str else params[1:len(params)-1].decode()).replace("/", ".")) 98 | 99 | return param_list, ret 100 | 101 | # def description_mapper(description): 102 | # types = { 103 | # "V": "void", 104 | # "Z": "boolean", 105 | # "B": "byte", 106 | # "S": "short", 107 | # "C": "char", 108 | # "I": "int", 109 | # "J": "long", 110 | # "F": "float", 111 | # "D": "double", 112 | # } 113 | # description, return_type = description.split(")", 1) 114 | # param_list = list() 115 | # return_type = types[return_type] if return_type in types else "[" + \ 116 | # types[return_type[1:]] if return_type[1:] in types else return_type.replace( 117 | # "/", ".") 118 | 119 | # for params in description[1:].split(" "): 120 | # if params in types and params[0] != "[": 121 | # param_list.append(bytes(types[params], encoding='utf8')) 122 | # elif len(params) != 0: 123 | # if len(params) != 0 and params[0] == "[": 124 | # param_list.append(b"["+params[1:].replace("/", ".")) 125 | # else: 126 | # param_list.append(params[1:len(params)-1].replace("/", ".")) 127 | 128 | # return param_list, return_type 129 | -------------------------------------------------------------------------------- /js/media.js: -------------------------------------------------------------------------------- 1 | setTimeout(function() { 2 | Java.perform(function() { 3 | var MediaMuxer = Java.use("android.media.MediaMuxer"); 4 | var AudioRecord = Java.use("android.media.AudioRecord"); 5 | var MediaProjection = Java.use("android.media.projection.MediaProjection"); 6 | var MediaProjectionManager = Java.use("android.media.projection.MediaProjectionManager"); 7 | var Camera = Java.use("android.hardware.Camera"); 8 | var CameraDevice = Java.use("android.hardware.camera2.CameraDevice"); 9 | 10 | 11 | Camera.open.overload('int').implementation = function(p0) { 12 | var ret = this.open(p0); 13 | send('{"media":"{\\"class_name\\":\\"android.hardware.Camera\\",\\"method_name\\":\\"open\\",\\"args\\":[' + p0 + '],\\"ret\\":\\"\\",\\"stackTrace\\":\\"' + btoa(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new())) + '\\"}"}'); 14 | return ret; 15 | } 16 | 17 | Camera.open.overload().implementation = function() { 18 | var ret = this.open(); 19 | send('{"media":"{\\"class_name\\":\\"android.hardware.Camera\\",\\"method_name\\":\\"open\\",\\"args\\":[],\\"ret\\":\\"\\",\\"stackTrace\\":\\"' + btoa(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new())) + '\\"}"}'); 20 | return ret; 21 | } 22 | Camera.release.overload().implementation = function() { 23 | var ret = this.release(); 24 | send('{"media":"{\\"class_name\\":\\"android.hardware.Camera\\",\\"method_name\\":\\"release\\",\\"args\\":[],\\"ret\\":\\"\\",\\"stackTrace\\":\\"' + btoa(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new())) + '\\"}"}'); 25 | return ret; 26 | } 27 | 28 | CameraDevice.createCaptureRequest.overload('int').implementation = function(p0) { 29 | var ret = this.createCaptureRequest(p0); 30 | send('{"media":"{\\"class_name\\":\\"android.hardware.camera2.CameraDevice\\",\\"method_name\\":\\"createCaptureRequest\\",\\"args\\":[' + p0 + '],\\"ret\\":\\"\\",\\"stackTrace\\":\\"' + btoa(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new())) + '\\"}"}'); 31 | return ret; 32 | } 33 | 34 | CameraDevice.close.overload().implementation = function() { 35 | var ret = this.close(); 36 | send('{"media":"{\\"class_name\\":\\"android.hardware.camera2.CameraDevice\\",\\"method_name\\":\\"close\\",\\"args\\":[],\\"ret\\":\\"\\",\\"stackTrace\\":\\"' + btoa(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new())) + '\\"}"}'); 37 | return ret; 38 | } 39 | 40 | CameraDevice.createCaptureRequest.overload('int').implementation = function(p0) { 41 | var ret = this.createCaptureRequest(p0); 42 | send('{"media":"{\\"class_name\\":\\"android.hardware.camera2.CameraDevice\\",\\"method_name\\":\\"createCaptureRequest\\",\\"args\\":[' + p0 + '],\\"ret\\":\\"\\",\\"stackTrace\\":\\"' + btoa(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new())) + '\\"}"}'); 43 | return ret; 44 | } 45 | 46 | MediaMuxer.start.implementation = function() { 47 | var ret = this.start(); 48 | send('{"media":"{\\"class_name\\":\\"android.media.MediaMuxer\\",\\"method_name\\":\\"start\\",\\"args\\":[],\\"ret\\":\\"\\",\\"stackTrace\\":\\"' + btoa(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new())) + '\\"}"}'); 49 | return ret; 50 | } 51 | 52 | MediaMuxer.stop.implementation = function() { 53 | var ret = this.stop(); 54 | send('{"media":"{\\"class_name\\":\\"android.media.MediaMuxer\\",\\"method_name\\":\\"stop\\",\\"args\\":[],\\"ret\\":\\"\\",\\"stackTrace\\":\\"' + btoa(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new())) + '\\"}"}'); 55 | return ret; 56 | } 57 | 58 | MediaProjection.stop.implementation = function() { 59 | var ret = this.stop(); 60 | send('{"media":"{\\"class_name\\":\\"android.media.projection.MediaProjection\\",\\"method_name\\":\\"stop\\",\\"args\\":[],\\"ret\\":\\"\\",\\"stackTrace\\":\\"' + btoa(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new())) + '\\"}"}'); 61 | return ret; 62 | } 63 | 64 | MediaProjectionManager.createScreenCaptureIntent.implementation = function() { 65 | var ret = this.createScreenCaptureIntent(); 66 | send('{"media":"{\\"class_name\\":\\"android.media.projection.MediaProjectionManager\\",\\"method_name\\":\\"createScreenCaptureIntent\\",\\"args\\":[],\\"ret\\":\\"\\",\\"stackTrace\\":\\"' + btoa(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new())) + '\\"}"}'); 67 | return ret; 68 | } 69 | AudioRecord.startRecording.overload().implementation = function() { 70 | var ret = this.startRecording(); 71 | send('{"media":"{\\"class_name\\":\\"android.media.AudioRecord\\",\\"method_name\\":\\"startRecording\\",\\"args\\":[],\\"ret\\":\\"\\",\\"stackTrace\\":\\"' + btoa(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new())) + '\\"}"}'); 72 | return ret; 73 | } 74 | AudioRecord.stop.overload().implementation = function() { 75 | var ret = this.stop(); 76 | send('{"media":"{\\"class_name\\":\\"android.media.AudioRecord\\",\\"method_name\\":\\"stop\\",\\"args\\":[],\\"ret\\":\\"\\",\\"stackTrace\\":\\"' + btoa(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new())) + '\\"}"}'); 77 | return ret; 78 | } 79 | }); 80 | }, 0); -------------------------------------------------------------------------------- /js/bypass_root_detection.js: -------------------------------------------------------------------------------- 1 | setTimeout(function () { 2 | Java.perform(function () { 3 | var RootPackages = ["com.topjohnwu.magisk"]; 4 | var RootBins = ["su", "busybox"]; 5 | 6 | Java.use("android.app.ApplicationPackageManager").getPackageInfoAsUser.implementation = function (packageName, flags, userId) { 7 | if (RootPackages.indexOf(packageName) > -1) { 8 | packageName = "fake.fake.fake.fake.fake.package"; 9 | } 10 | return this.getPackageInfoAsUser(packageName, flags, userId); 11 | } //Android 11 12 | Java.use("android.app.ApplicationPackageManager").getPackageInfo.overload('java.lang.String', 'int').implementation = function (packageName, flags) { 13 | if (RootPackages.indexOf(packageName) > -1) { 14 | packageName = "fake.fake.fake.fake.fake.package"; 15 | } 16 | return this.getPackageInfo(packageName, flags); 17 | } 18 | // Java.use('android.content.pm.PackageManager').getPackageInfo.overload('java.lang.String', 'int').implementation = function (packageName, flags) { 19 | // console.log("-------------------------") 20 | // console.log(packageName); 21 | // if (RootPackages.indexOf(packageName) > -1) { 22 | // packageName = "fake.fake.fake.fake.fake.package"; 23 | // } 24 | // return this.getPackageInfo(packageName, flags); 25 | // } 26 | Java.use('java.lang.Runtime').exec.overload('[Ljava.lang.String;').implementation = function (cmd) { 27 | console.log(cmd) 28 | if (cmd[0] == "which" ||cmd[0] == "su" || cmd[0] == "mount" || RootBins.indexOf(cmd[0]) || RootBins.indexOf(cmd[1])) { 29 | return this.exec([]); 30 | } 31 | return this.exec(cmd); 32 | } 33 | Java.use('java.lang.String').contains.overload('java.lang.CharSequence').implementation = function (charSequence) { 34 | if (charSequence.toString() == "test-keys") { 35 | return false; 36 | } 37 | return this.contains(charSequence); 38 | } 39 | Java.use('java.lang.Runtime').exec.overload('java.lang.String').implementation = function (command) { 40 | // console.log(command) 41 | 42 | if (command.match(/.*which\s.*su/) || command.match(/mount/)) { 43 | console.log(command) 44 | return this.exec(Java.use("java.lang.String").$new("which D4my").toString()); 45 | } 46 | 47 | if (command.match(/su/)) { 48 | return this.exec(Java.use("java.lang.String").$new("D4my").toString()); 49 | } 50 | // if (command.match(/getprop/)) { 51 | // return this.exec(command + " | grep -v ro.debuggable"); 52 | // } 53 | return this.exec(command); 54 | } 55 | Java.use('java.io.File').$init.overload('java.lang.String', 'java.lang.String').implementation = function (path, file) { 56 | if (["su", "busybox", "supersu", "Superuser.apk", "KingoUser.apk", "SuperSu.apk", "magisk"].includes(file)) { 57 | return this.$init(path, Java.use("java.lang.String").$new("D4my").toString()); 58 | } else { 59 | return this.$init(path, file); 60 | }; 61 | } 62 | Java.use('java.io.File').$init.overload('java.io.File', 'java.lang.String').implementation = function (path, file) { 63 | if (["su", "busybox", "supersu", "Superuser.apk", "KingoUser.apk", "SuperSu.apk", "magisk"].includes(file)) { 64 | return this.$init(path, Java.use("java.lang.String").$new("D4my").toString()); 65 | } else { 66 | return this.$init(path, file); 67 | }; 68 | } 69 | Java.use('java.io.File').$init.overload('java.lang.String').implementation = function (file) { 70 | if (["/data/local/su", 71 | "/data/local/bin/su", 72 | "/data/local/xbin/su", 73 | "/sbin/su", 74 | "/su/bin/su", 75 | "/system/bin/su", 76 | "/system/bin/.ext/su", 77 | "/system/bin/failsafe/su", 78 | "/system/sd/xbin/su", 79 | "/system/usr/we-need-root/su", 80 | "/system/xbin/su/su", 81 | "/system/app/Superuser.apk", 82 | "/cache/su", 83 | "/system/xbin/busybox", 84 | "/system/bin/magisk", 85 | "/data/su", 86 | "/dev/su" 87 | ].includes(file)) { 88 | return this.$init(Java.use("java.lang.String").$new("D4my").toString());; 89 | } else { 90 | return this.$init(file) 91 | }; 92 | } 93 | try { 94 | Java.use('android.location.Location').isMock.implementation = function () { 95 | return false; 96 | } 97 | Java.use('android.location.Location').isFromMockProvider.implementation = function () { 98 | return false; 99 | } 100 | } catch (_) { } 101 | }); 102 | 103 | Interceptor.attach(Module.findExportByName("libc.so", "fopen"), { 104 | onEnter: function (args) { 105 | this.path = Memory.readCString(args[0]).split("/"); 106 | }, 107 | onLeave: function (retval) { 108 | var path = this.path; 109 | if ((["su", "busybox", "supersu", "Superuser.apk", "KingoUser.apk", "SuperSu.apk", "magisk"].indexOf(path[path.length - 1]) > -1)) { 110 | retval.replace(0x0); 111 | } 112 | } 113 | }); 114 | 115 | Interceptor.attach(Module.findExportByName("libc.so", "system"), { 116 | onEnter: function (args) { 117 | this.cmd = Memory.readCString(args[0]); 118 | }, 119 | onLeave: function (retval) { 120 | var cmd = this.cmd; 121 | if (cmd == "su" || cmd.indexOf("getprop") != -1 || cmd == "mount" || cmd.indexOf("build.prop") != -1) { 122 | retval.replace(0x0); 123 | } 124 | } 125 | }); 126 | }, 1); -------------------------------------------------------------------------------- /helper/app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | import device 2 | import functools 3 | import sys 4 | import exceptions 5 | import hooker 6 | import time 7 | import static 8 | import dynamic 9 | import logging 10 | import os 11 | import shutil 12 | 13 | # logging.debug(list(substring in "aaass" for substring in ['java/security', 'javax/crypto/spec'])) 14 | 15 | root = logging.getLogger() 16 | root.setLevel(logging.DEBUG) 17 | 18 | ch = logging.StreamHandler(sys.stdout) 19 | ch.setLevel(logging.DEBUG) 20 | FORMAT = "[%(asctime)s - %(filename)s:%(lineno)s - %(funcName)s() ] %(message)s" 21 | formatter = logging.Formatter(FORMAT) 22 | ch.setFormatter(formatter) 23 | root.addHandler(ch) 24 | 25 | 26 | def main(): 27 | logging.debug("value") 28 | try: 29 | devices = device.get_active_devices() 30 | except: 31 | _, value, _ = sys.exc_info() 32 | logging.debug(value) 33 | sys.exit(1) 34 | else: 35 | cdevice = None 36 | # print(devices) 37 | for d in devices: 38 | if d.d.serialno == "10.42.0.27:5555": 39 | # continue 40 | cdevice = d 41 | break 42 | cdevice = d 43 | print(cdevice.d.serialno) 44 | cdevice.close_all_apps() 45 | # cdevice = devices[0] 46 | 47 | with open("packages.txt", "r") as packages: 48 | for _i, pkg_name in enumerate(packages.read().splitlines()): 49 | # if i < 20: 50 | # continue 51 | # pkg_name = "com.trtf.blue" 52 | 53 | if (not os.path.exists("out/" + pkg_name+"/"+pkg_name + ".pcap") or not os.path.exists("out/" + pkg_name + "/mitmdump")): 54 | if os.path.exists("out/" + pkg_name+"/"): 55 | for f in os.listdir("out/" + pkg_name+"/"): 56 | if f.endswith('.png') or f.endswith('.txt') or f.endswith('.lock'): 57 | os.remove("out/" + pkg_name+"/"+f) 58 | if os.path.exists("out/" + pkg_name + "/mitmdump"): 59 | os.remove("out/" + pkg_name + "/mitmdump") 60 | if os.path.exists("out/" + pkg_name + "/files-1"): 61 | shutil.rmtree("out/" + pkg_name + "/files-1") 62 | if os.path.exists("out/" + pkg_name + "/files-2"): 63 | shutil.rmtree("out/" + pkg_name + "/files-2") 64 | if os.path.exists("out/" + pkg_name+"/"+pkg_name + ".pcap"): 65 | os.remove("out/" + pkg_name+"/"+pkg_name + ".pcap") 66 | elif os.path.exists("out/" + pkg_name+".fail"): 67 | continue 68 | else: 69 | continue 70 | cdevice.uninstall_3rd_party_apps() 71 | #pkg_name = "com.apple.movetoios" 72 | logging.debug(pkg_name) 73 | if cdevice.install_app(pkg_name, reinstall=False) == False: 74 | continue 75 | cdevice.store_app(pkg_name) 76 | # static_analysis = None 77 | static_analysis = static.Package(pkg_name) 78 | perms, service_name = cdevice.grant_app_permissions(pkg_name) 79 | (p, mitm) = cdevice.start_capture(pkg_name) 80 | h = dynamic.Dynamic(cdevice, pkg_name, static_analysis) 81 | h.run() 82 | analysis_time = int(time.time()) 83 | with open("out/"+pkg_name+"/time.txt", "a") as f: 84 | f.write("s1+:"+str(analysis_time)+"\n") 85 | cdevice.run_app(pkg_name) 86 | cdevice.start_interaction(pkg_name, 1, analysis_time) 87 | cdevice.close_app(pkg_name) 88 | with open("out/"+pkg_name+"/time.txt", "a") as f: 89 | f.write("s1-:"+str(int(time.time()))+"\n") 90 | cdevice.store_files(pkg_name, 1) 91 | if os.path.exists("out/"+pkg_name+"/crypt-1.txt") and 1==2: 92 | logging.debug("======> "+pkg_name+" 2nd") 93 | open("out/"+pkg_name+"/2nd.lock", 'a').close() 94 | if cdevice.install_app(pkg_name, reinstall=True) == False: 95 | raise "Unable to install" 96 | cdevice.grant_app_permissions( 97 | pkg_name, perms=perms, service_name=service_name) 98 | analysis_time = int(time.time()) 99 | with open("out/"+pkg_name+"/time.txt", "a") as f: 100 | f.write("s2+:"+str(analysis_time)+"\n") 101 | cdevice.run_app(pkg_name) 102 | cdevice.start_interaction(pkg_name, 2, analysis_time) 103 | cdevice.close_app(pkg_name) 104 | with open("out/"+pkg_name+"/time.txt", "a") as f: 105 | f.write("s2-:"+str(int(time.time()))+"\n") 106 | cdevice.store_files(pkg_name, 2) 107 | os.remove("out/"+pkg_name+"/2nd.lock") 108 | h.stop() 109 | cdevice.stop_capture(p, mitm, pkg_name) 110 | cdevice.uninstall_app(pkg_name) 111 | # break 112 | # package = static.Package(pkg_name) 113 | # # native_methods = package.get_methods(lambda m: ( 114 | # # m.get_access_flags() & 0x100) == 0x100) # 0x100 means native 115 | # # native_methods = package.get_methods( 116 | # # lambda m: (m.get_name() in ("encrypt", "decrypt") or any(substring in m.get_descriptor().decode("utf-8") for substring in ['java/security', 'javax/crypto/spec']))) 117 | # native_methods = package.get_methods( 118 | # lambda m: (m.get_name() in ("encrypt", "decrypt") or m.get_name().decode("utf-8").startswith("hash"))) 119 | # jscode = "Java.perform(function () {"+("".join((m.to_frida(p).decode("utf-8") 120 | # for m in native_methods)))+"});" 121 | # logging.debug(jscode) 122 | # with open("js/bypass_root_detection.js") as f: 123 | # jscode += f.read() 124 | # # logging.debug(cdevice.is_alive()) 125 | # cdevice.frida.on("spawn-added", functools.partial(hooker.spawn_added, 126 | # package=pkg_name, jscode=jscode, frida_device=cdevice.frida, processes=dict())) 127 | # cdevice.frida.enable_spawn_gating() 128 | # logging.debug("x") 129 | # sys.stdin.read() 130 | 131 | 132 | if __name__ == '__main__': 133 | main() 134 | 135 | # for i in `find . -name "*.pcap" -type f`; do 136 | # python ../pcap-full.py "$i" 137 | # done 138 | -------------------------------------------------------------------------------- /helper/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | 86 | # Determine the Java command to use to start the JVM. 87 | if [ -n "$JAVA_HOME" ] ; then 88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 89 | # IBM's JDK on AIX uses strange locations for the executables 90 | JAVACMD="$JAVA_HOME/jre/sh/java" 91 | else 92 | JAVACMD="$JAVA_HOME/bin/java" 93 | fi 94 | if [ ! -x "$JAVACMD" ] ; then 95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 96 | 97 | Please set the JAVA_HOME variable in your environment to match the 98 | location of your Java installation." 99 | fi 100 | else 101 | JAVACMD="java" 102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 103 | 104 | Please set the JAVA_HOME variable in your environment to match the 105 | location of your Java installation." 106 | fi 107 | 108 | # Increase the maximum file descriptors if we can. 109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 110 | MAX_FD_LIMIT=`ulimit -H -n` 111 | if [ $? -eq 0 ] ; then 112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 113 | MAX_FD="$MAX_FD_LIMIT" 114 | fi 115 | ulimit -n $MAX_FD 116 | if [ $? -ne 0 ] ; then 117 | warn "Could not set maximum file descriptor limit: $MAX_FD" 118 | fi 119 | else 120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 121 | fi 122 | fi 123 | 124 | # For Darwin, add options to specify how the application appears in the dock 125 | if $darwin; then 126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 127 | fi 128 | 129 | # For Cygwin or MSYS, switch paths to Windows format before running java 130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 133 | 134 | JAVACMD=`cygpath --unix "$JAVACMD"` 135 | 136 | # We build the pattern for arguments to be converted via cygpath 137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 138 | SEP="" 139 | for dir in $ROOTDIRSRAW ; do 140 | ROOTDIRS="$ROOTDIRS$SEP$dir" 141 | SEP="|" 142 | done 143 | OURCYGPATTERN="(^($ROOTDIRS))" 144 | # Add a user-defined pattern to the cygpath arguments 145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 147 | fi 148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 149 | i=0 150 | for arg in "$@" ; do 151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 153 | 154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 156 | else 157 | eval `echo args$i`="\"$arg\"" 158 | fi 159 | i=`expr $i + 1` 160 | done 161 | case $i in 162 | 0) set -- ;; 163 | 1) set -- "$args0" ;; 164 | 2) set -- "$args0" "$args1" ;; 165 | 3) set -- "$args0" "$args1" "$args2" ;; 166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 172 | esac 173 | fi 174 | 175 | # Escape application args 176 | save () { 177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 178 | echo " " 179 | } 180 | APP_ARGS=`save "$@"` 181 | 182 | # Collect all arguments for the java command, following the shell quoting and substitution rules 183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 184 | 185 | exec "$JAVACMD" "$@" 186 | -------------------------------------------------------------------------------- /js/general.js: -------------------------------------------------------------------------------- 1 | var JavaConnectionPool = {}; 2 | var Modules = {}; 3 | const Files = new Map(); 4 | 5 | 6 | function base64(input) { 7 | var _keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=" 8 | var output = ""; 9 | var chr1, chr2, chr3, enc1, enc2, enc3, enc4; 10 | var i = 0; 11 | while (i < input.length) { 12 | chr1 = input.charCodeAt(i++); 13 | chr2 = input.charCodeAt(i++); 14 | chr3 = input.charCodeAt(i++); 15 | enc1 = chr1 >> 2; 16 | enc2 = ((chr1 & 3) << 4) | (chr2 >> 4); 17 | enc3 = ((chr2 & 15) << 2) | (chr3 >> 6); 18 | enc4 = chr3 & 63; 19 | if (isNaN(chr2)) { 20 | enc3 = enc4 = 64; 21 | } else if (isNaN(chr3)) { 22 | enc4 = 64; 23 | } 24 | 25 | output = output + _keyStr.charAt(enc1) + _keyStr.charAt(enc2) + _keyStr.charAt(enc3) + _keyStr.charAt(enc4); 26 | } 27 | return output; 28 | } 29 | 30 | 31 | function btoa(p) { 32 | if (p == null) { 33 | return '' 34 | } else { 35 | if (typeof (p) === 'string') { 36 | var p = Java.use('java.lang.String').$new(p).getBytes(); 37 | } 38 | try { 39 | if (p.$className == "java.nio.HeapByteBuffer") { 40 | p = p.array(); 41 | } 42 | var Base64 = Java.use('android.util.Base64'); 43 | var base64encode = Base64.encodeToString.overload('[B', 'int'); 44 | return base64encode.call(Base64, p, 2); 45 | } catch (_e) { 46 | return arrayBufferToBase64(p); 47 | } 48 | } 49 | } 50 | 51 | function arrayBufferToBase64(buffer) { 52 | var binary = ''; 53 | var bytes = new Uint8Array(buffer); 54 | var len = bytes.byteLength; 55 | for (var i = 0; i < len; i++) { 56 | binary += String.fromCharCode(bytes[i]); 57 | } 58 | 59 | return base64(binary); 60 | 61 | } 62 | function ntohs(val) { 63 | return ((val & 0xFF) << 8) | ((val >> 8) & 0xFF); 64 | } 65 | 66 | function inet_pton(a) { 67 | let m 68 | let i 69 | let j 70 | const f = String.fromCharCode 71 | 72 | m = a.match(/^(?:\d{1,3}(?:\.|$)){4}/) 73 | if (m) { 74 | m = m[0].split('.') 75 | m = f(m[0], m[1], m[2], m[3]) 76 | // Return if 4 bytes, otherwise false. 77 | if (m.length === 4) { 78 | var ret = new Uint8Array(4); 79 | for (var ii = 0; ii < m.length; ii++) { 80 | ret[ii] = m.charCodeAt(ii); 81 | } 82 | return ret; 83 | } else { 84 | return false; 85 | } 86 | // return m.length === 4 ? new Uint8Array(m.split("") 87 | // .map(c => c.charCodeAt(0).toString(16).padStart(2, "0"))) : false 88 | } 89 | 90 | // IPv6 91 | if (a.length > 39) { 92 | return false 93 | } 94 | 95 | m = a.split('::') 96 | 97 | if (m.length > 2) { 98 | return false 99 | } // :: can't be used more than once in IPv6. 100 | 101 | const reHexDigits = /^[\da-f]{1,4}$/i 102 | 103 | for (j = 0; j < m.length; j++) { 104 | if (m[j].length === 0) { // Skip if empty. 105 | continue 106 | } 107 | m[j] = m[j].split(':') 108 | for (i = 0; i < m[j].length; i++) { 109 | let hextet = m[j][i] 110 | if (!reHexDigits.test(hextet)) { 111 | return false 112 | } 113 | 114 | hextet = parseInt(hextet, 16) 115 | if (isNaN(hextet)) { 116 | return false 117 | } 118 | m[j][i] = f(hextet >> 8, hextet & 0xFF) 119 | } 120 | m[j] = m[j].join('') 121 | } 122 | var final = m.join('\x00'.repeat(16 - m.reduce((tl, m) => tl + m.length, 0))); 123 | 124 | var ret = new Uint8Array(16); 125 | for (var ii = 0; ii < final.length; ii++) { 126 | ret[ii] = final.charCodeAt(ii); 127 | } 128 | return ret; 129 | 130 | } 131 | function arrayBuffer(buffer) { 132 | var binary = ''; 133 | var bytes = new Uint8Array(buffer); 134 | var len = bytes.byteLength; 135 | for (var i = 0; i < len; i++) { 136 | binary += String.fromCharCode(bytes[i]); 137 | } 138 | return (binary); 139 | 140 | } 141 | function inet_ntop(v) { //https://github.com/locutusjs/locutus/blob/master/src/php/network/inet_ntop.js 142 | var a = arrayBuffer(v); 143 | let i = 0 144 | let m = '' 145 | const c = [] 146 | a += '' 147 | if (a.length === 4) { 148 | return [a.charCodeAt(0), a.charCodeAt(1), a.charCodeAt(2), a.charCodeAt(3)].join('.') 149 | } else if (a.length === 16) { 150 | for (i = 0; i < 16; i++) { 151 | c.push(((a.charCodeAt(i++) << 8) + a.charCodeAt(i)).toString(16)) 152 | } 153 | return c.join(':').replace(/((^|:)0(?=:|$))+:?/g, function (t) { 154 | m = (t.length > m.length) ? t : m 155 | return t 156 | }).replace(m || ' ', '::') 157 | } else { 158 | return null 159 | } 160 | } 161 | function ipv4t6(a) { 162 | var z = inet_pton(a) 163 | if (z == false) { 164 | return false 165 | } 166 | var ar = new Uint8Array(16); 167 | for (var i = 0; i < 4; i++) { 168 | ar[12 + i] = z[i]; 169 | } 170 | return inet_ntop(ar); 171 | } 172 | 173 | var proxy_addr4 = "10.42.0.1" 174 | var proxy_addr6 = ipv4t6(proxy_addr4) 175 | 176 | Java.perform(function () { 177 | try { 178 | Java.use('java.lang.System').exit.implementation = function (v) { 179 | // console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new())); 180 | return this.exit(v); 181 | } 182 | } catch (_) { } 183 | try { 184 | Java.use('android.app.KeyguardManager').isDeviceSecure.overload().implementation = function (v) { 185 | 186 | // console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new())); 187 | return true; 188 | } 189 | } catch (_) { } 190 | try { 191 | Java.use("android.app.ApplicationPackageManager").getInstallerPackageName.implementation = function (_packageName) { 192 | // console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new())); 193 | return Java.use("java.lang.String").$new("com.android.vending").toString();; 194 | } 195 | } catch (_) { } 196 | try { 197 | Java.use('android.content.pm.InstallSourceInfo').getInstallingPackageName.implementation = function () { 198 | return Java.use("java.lang.String").$new("com.android.vending").toString(); 199 | } 200 | } catch (_) { } 201 | 202 | }); 203 | 204 | // Process.enumerateModules() 205 | // .forEach(function (m) { 206 | // if (m.path.startsWith("/apex/") || m.path.startsWith("/data/app/")) { 207 | // Memory.protect(m.base, m.size, 'rwx'); 208 | // } 209 | // }); 210 | 211 | -------------------------------------------------------------------------------- /js/native.js: -------------------------------------------------------------------------------- 1 | 2 | // function inet4_ntoa(int) { 3 | // return ((int >> 24) & 255) + "." + ((int >> 16) & 255) + "." + ((int >> 8) & 255) + "." + (int & 255); 4 | // } 5 | 6 | // Interceptor.attach(Module.findExportByName("libc.so", "pthread_create"), { 7 | // onEnter: function (args) { 8 | // this.id = null 9 | // this.m = null; 10 | // // console.log('Pthreaaaaad from:\n' + Thread.backtrace(this.context, Backtracer.ACCURATE).map(DebugSymbol.fromAddress).join('\n') + '\n'); 11 | // // console.log(JSON.stringify(this.context)); 12 | // // console.log("Pthreaaaaad from"+DebugSymbol.fromAddress(this.context.lr).moduleName) 13 | // if (Process.enumerateModules() 14 | // .filter(function (m) { 15 | // // return true; 16 | // return m.path.startsWith("/data/app/") && m.path.endsWith(".so"); 17 | // }).map(function (m) { 18 | // return m.name; 19 | // }).includes(DebugSymbol.fromAddress(this.context.lr).moduleName)) { 20 | // this.id = args[0] 21 | // this.m = DebugSymbol.fromAddress(this.context.lr).moduleName 22 | 23 | // console.log(DebugSymbol.fromAddress(this.context.lr)); 24 | // console.log("----"); 25 | // console.log(DebugSymbol.fromAddress(args[2])); 26 | // console.log("----"); 27 | 28 | // console.log(JSON.stringify(this.context, null, 2)); 29 | 30 | // console.log('pthread_create called from:\n' + 31 | // Thread.backtrace(this.context, Backtracer.ACCURATE) // FUZZY ACCURATE 32 | // .map(DebugSymbol.fromAddress).join('\n') + '\n'); 33 | 34 | 35 | // } 36 | 37 | // }, 38 | // onLeave: function (retval) { 39 | // if (this.id) { 40 | // console.log(this.m + "===>" + ptr(this.id).readUInt()) 41 | // } 42 | // // if (this.flag) // passed from onEnter 43 | // // console.warn("\nretval: " + retval); 44 | // } 45 | // }); 46 | 47 | 48 | Process 49 | .getModuleByName({ linux: 'libc.so', darwin: 'libSystem.B.dylib', windows: 'ws2_32.dll' }[Process.platform]) 50 | .enumerateExports().filter(ex => ex.type === 'function' && ['connect', 'recv', 'send', 'sendto', 'recvfrom', 'accept', 'listen', 'bind'].some(prefix => ex.name.indexOf(prefix) === 0)) 51 | .forEach(ex => { 52 | Interceptor.attach(ex.address, { 53 | onEnter: function (args) { 54 | this.fd = args[0].toInt32(); 55 | this.socketType = Socket.type(this.fd); 56 | if (this.socketType == 'tcp' || this.socketType == 'udp' || this.socketType == 'tcp6' || this.socketType == 'udp6') { 57 | this.ipAddress = null; 58 | this.portAddress = null; 59 | this.localIpAddress = null; 60 | this.localPortAddress = null; 61 | 62 | 63 | var address = Socket.peerAddress(this.fd); 64 | var local_address = Socket.localAddress(this.fd); 65 | if (address != null) { 66 | this.ipAddress = address.ip; 67 | this.portAddress = address.port; 68 | } 69 | if (local_address != null) { 70 | this.localIpAddress = local_address.ip; 71 | this.localPortAddress = local_address.port; 72 | } 73 | if (ex.name == "connect" || ex.name == "bind" || ex.name == "accept") { 74 | this.portAddress = ntohs(Memory.readU16(ptr(args[1]).add(2))); 75 | this.ipAddress = inet_ntop(Memory.readByteArray(ptr(args[1]).add(this.socketType.slice(-1) == '6' ? 8 : 4), this.socketType.slice(-1) == '6' ? 16 : 4)); 76 | if (ex.name == "connect" && this.portAddress == 443 && this.socketType.startsWith('tcp')) { 77 | var socketType = this.socketType; 78 | Process.enumerateRanges('rw-') 79 | .forEach(function (m) { 80 | if (m.base.toInt32() < args[1].toInt32() && args[1].toInt32() - m.base.toInt32() < m.size) { 81 | // console.log(this.portAddress) 82 | ptr(args[1]).add(2).writeU16(36895); 83 | if (socketType.slice(-1) == '6') { 84 | ptr(args[1]).add(8).writeByteArray(inet_pton(proxy_addr6)); 85 | } else { 86 | ptr(args[1]).add(4).writeByteArray(inet_pton(proxy_addr4)); 87 | } 88 | } 89 | }); 90 | //00 00 00 00 00 00 00 00 00 00 ff ff 0a 2a 00 01 91 | //36895 92 | //::ffff:a2a:1 93 | //0a 2a 00 01 94 | //10.42.0.1 95 | } 96 | if (ex.name == "bind") { 97 | this.localPortAddress = this.portAddress; 98 | this.localIpAddress = this.ipAddress; 99 | } 100 | } else if ((ex.name == "sendto" || ex.name == "recvfrom") && args[4] != 0x0) { 101 | this.portAddress = ntohs(Memory.readU16(ptr(args[4]).add(2))); 102 | this.ipAddress = inet_ntop(Memory.readByteArray(ptr(args[4]).add(this.socketType.slice(-1) == '6' ? 8 : 4), this.socketType.slice(-1) == '6' ? 16 : 4)); 103 | } 104 | if (ex.name == "listen" || ex.name == "bind") { 105 | this.portAddress = null; 106 | this.ipAddress = null; 107 | } 108 | // if (this.portAddress == null || this.ipAddress == null || this.localPortAddress == null || this.localIpAddress == null) { 109 | // console.log(ex.name); 110 | // return 111 | // } 112 | } 113 | 114 | }, 115 | onLeave: function (retval) { 116 | if (this.socketType == 'tcp' || this.socketType == 'udp' || this.socketType == 'tcp6' || this.socketType == 'udp6') { 117 | if (this.localPortAddress == 0 && ex.name == "connect") { 118 | var local_address = Socket.localAddress(this.fd); 119 | if (local_address != null) { 120 | this.localIpAddress = local_address.ip; 121 | this.localPortAddress = local_address.port; 122 | } 123 | } 124 | if (JavaConnectionPool[(this.ipAddress + ':' + this.portAddress).replace(/^::ffff:/, '')] !== undefined && JavaConnectionPool[(this.ipAddress + ':' + this.portAddress).replace(/^::ffff:/, '')].includes((this.localIpAddress + ':' + this.localPortAddress).replace(/^::ffff:/, ''))) { 125 | send('{"java":{"protocol":"' + this.socketType + '","fd":"' + this.fd + '","function":"' + ex.name + '","pid":' + Process.id + ',"address":"' + this.ipAddress + ':' + this.portAddress + '","local_address":"' + this.localIpAddress + ':' + this.localPortAddress + '"}}'); 126 | 127 | } else { 128 | send('{"native":{"protocol":"' + this.socketType + '","fd":"' + this.fd + '","function":"' + ex.name + '","pid":' + Process.id + ',"address":"' + this.ipAddress + ':' + this.portAddress + '","local_address":"' + this.localIpAddress + ':' + this.localPortAddress + '"}}'); 129 | } 130 | if (ex.name == "connect" && (this.ipAddress == "127.0.0.1" || this.ipAddress == "::ffff:7f00:1" || this.ipAddress == "::1") && this.portAddress == 27042 && this.socketType.startsWith('tcp')) { 131 | retval.replace(-1); 132 | } 133 | } 134 | } 135 | 136 | }) 137 | }) 138 | 139 | -------------------------------------------------------------------------------- /dynamic.py: -------------------------------------------------------------------------------- 1 | import time 2 | import json 3 | import functools 4 | import logging 5 | import os 6 | import sys 7 | import subprocess 8 | 9 | 10 | def fh(method, body): 11 | params = "" 12 | for i, (key, _) in enumerate(body.items()): 13 | if i > 0: 14 | params += "+'\\\\\", \\\\\"'+" 15 | # params += "+'\\\\\"], [\\\\\"'+" 16 | params += "btoa(" + key+")" 17 | #Java.use('android.provider.Settings$Secure').getString(Java.use('android.app.ActivityThread').currentApplication().getApplicationContext().getContentResolver(), 'android_id') 18 | exec = "var ret = this." + \ 19 | method.name+"("+(", ".join([key for key, value in body.items()]))+");" 20 | return exec+"send('{\"crypto\":\"{\\\\\"class_name\\\\\":\\\\\""+method.class_name+"\\\\\",\\\\\"method_name\\\\\":\\\\\""+method.name+"\\\\\",\\\\\"args\\\\\":["+("\\\\\"'+"+params+"+'\\\\\"" if params else "")+"],\\\\\"ret\\\\\":\\\\\"'+btoa(ret)+'\\\\\",\\\\\"stackTrace\\\\\":\\\\\"'+btoa(Java.use(\"android.util.Log\").getStackTraceString(Java.use(\"java.lang.Exception\").$new()))+'\\\\\"}\"}');return ret;" 21 | #console.log('"+method.class_name.decode("utf-8")+":"+method.name.decode("utf-8")+"("+("'+"+params+"+'" if params else "")+")->'+ret); 22 | 23 | #Java.use('android.provider.Settings$Secure').getString(Java.use('android.app.ActivityThread').currentApplication().getApplicationContext().getContentResolver(), 'android_id'); 24 | 25 | 26 | class Dynamic: 27 | def __init__(self, device, package, static): 28 | self.frida = device.frida 29 | self.device = device 30 | self.package = package 31 | self.sessions = set() 32 | self.jscode = str() 33 | 34 | def candid_methods( 35 | m): return "java.lang.String" in m or "java.lang.Byte" in m or "byte" == m 36 | # args , ret = description_mapper(method.get_descriptor()) 37 | # self.params, self.return_type 38 | rooted_methods = static.get_methods( 39 | lambda m: (("rooted" in m.name.lower()) and "path" not in m.class_name and ".db." not in m.class_name and "google" not in m.class_name and "kotlin" not in m.class_name and m.return_type == "boolean")) 40 | self.jscode = "Java.perform(function () {"+( 41 | "".join((m.to_frida((lambda _m, _b: ""), "return false;") for m in rooted_methods)))+( 42 | "".join((m.to_frida((lambda _m, _b: "p0 = false;")) for m in static.get_methods( 43 | lambda m: (("root" in m.name.lower() and "detect" in m.name.lower()) and ".db." not in m.class_name and "path" not in m.class_name and "google" not in m.class_name and m.return_type == "void" and len(m.params) == 1 and m.params[0] == "boolean")))))+"});" 44 | 45 | # fake_verifiers = static.get_methods( 46 | # lambda m: (("verify" == m.name.lower()) and "google" not in m.class_name and "java" not in m.class_name and "kotlin" not in m.class_name and m.return_type == "boolean" and len(m.params) == 2)) 47 | 48 | # self.jscode = "Java.perform(function () {"+( 49 | # "".join((m.to_frida((lambda _m, _b: ""), "return true;") for m in fake_verifiers)))+"});" 50 | 51 | # self.jscode += "Java.perform(function () {"+( 52 | # "".join((m.to_frida((lambda _m,_b: "p0 = false;")) for m in static.get_methods( 53 | # lambda m: (("root" in m.name.lower() and "detect" in m.name.lower()) and ".db." not in m.class_name and "path" not in m.class_name and "google" not in m.class_name and m.return_type == "void" and len(m.params) == 1 and m.params[0] == "boolean")))))+"});" 54 | # crypto_methods = static.get_methods( 55 | # lambda m: (((("encrypt" in m.name.lower() or "decrypt" in m.name.lower()) and ("aes" in m.name.lower() or "rsa" in m.name.lower() or "byte" in m.name.lower() and "compress" in m.name.lower() and "string" in m.name.lower())) or ("encrypt" == m.name.lower() or "decrypt" == m.name.lower())) and ".db." not in m.class_name and 1 <= len(m.params) >= 3 and all([candid_methods(param) for param in m.params]) and candid_methods(m.return_type))) 56 | 57 | crypto_methods = static.get_methods( 58 | lambda m: (("encrypt" == m.name.lower() or "decrypt" in m.name.lower()) and ".db." not in m.class_name and 1 <= len(m.params) <= 4 and all([candid_methods(param) for param in m.params]) and candid_methods(m.return_type))) 59 | self.jscode += "Java.perform(function () {"+( 60 | "".join((m.to_frida(fh) for m in crypto_methods)))+"});" 61 | 62 | print(self.jscode) 63 | for file in ("general.js", "debug.js", "bypass_root_detection.js", "native.js", "java.js", "media.js", "fs.js", "pinning.js", "built_in_crypto.js"): 64 | with open("js/"+file) as f: 65 | self.jscode += f.read() 66 | # "java.lang.Object" in m or "JSONObject" in m or 67 | 68 | self.spawn = functools.partial(spawn_added, frida_device=self.frida, device=self.device, 69 | package=self.package, jscode=self.jscode, processes=dict(), fs=dict(), sessions=self.sessions) 70 | self.frida.on("spawn-added", self.spawn) 71 | self.frida.on("child-added", self.spawn) 72 | 73 | def run(self): 74 | self.frida.enable_spawn_gating() 75 | 76 | def stop(self): 77 | # self.frida.close() 78 | try: 79 | self.frida.disable_spawn_gating() 80 | self.frida.off("spawn-added", self.spawn) 81 | self.frida.off("child-added", self.spawn) 82 | for session in self.sessions: 83 | session.detach() 84 | except: 85 | pass 86 | 87 | 88 | def spawn_added(spawn, frida_device, device, package, jscode, processes, fs, sessions): # identifier pid 89 | # print("-------->"+spawn.identifier) 90 | print(spawn.identifier+"::::::"+package) 91 | if spawn.identifier.startswith(package) or spawn.identifier.startswith("com.android") or spawn.identifier.startswith("com.google.process") or spawn.identifier.startswith("com.google.android") or spawn.identifier in ("com.research.helper", "com.topjohnwu.magisk"): 92 | logging.debug("Process:"+str(spawn)) 93 | processes[spawn.pid] = spawn.identifier 94 | if spawn.identifier.startswith(package+":") or spawn.identifier == package: 95 | try: 96 | session = frida_device.attach(spawn.pid) 97 | sessions.add(session) 98 | script = session.create_script(jscode) 99 | script.on("message", functools.partial( 100 | on_message, processes=processes, fs=fs, package=package)) 101 | script.load() 102 | except: 103 | session.detach() 104 | # pass 105 | # sys.exit() 106 | # logging.debug(spawn.pid) 107 | else: 108 | pass 109 | # session.detach() 110 | try: 111 | frida_device.resume(spawn.pid) 112 | except: 113 | logging.debug("Errrr:"+str(spawn)) 114 | pass 115 | else: 116 | for p in frida_device.enumerate_processes(): 117 | if p.pid == spawn.pid: 118 | print("killed" + str(spawn.pid) + ":" + spawn.identifier) 119 | frida_device.resume(spawn.pid) 120 | frida_device.kill(spawn.pid) 121 | device.close_app(spawn.identifier) 122 | 123 | 124 | def on_message(message, data, package, processes, fs): 125 | stage = "-2" if os.path.exists("out/"+package+"/2nd.lock") else "-1" 126 | try: 127 | # print(message["payload"]) 128 | # print(message) 129 | conn = json.loads(message["payload"]) 130 | if "crypto" in conn: 131 | with open("out/"+package+"/crypt"+stage+".txt", "a") as f: 132 | c = json.loads(conn["crypto"]) 133 | c["ts"] = time.time() 134 | f.write(json.dumps(c) + '\n') 135 | elif "key-iv" in conn: 136 | with open("out/"+package+"/key-iv"+stage+".txt", "a") as f: 137 | k = json.loads(conn["key-iv"]) 138 | k["ts"] = time.time() 139 | f.write(json.dumps(k) + '\n') 140 | elif "deviceid" in conn: 141 | with open("out/"+package+"/id"+stage+".txt", "w") as f: 142 | f.write(str(conn["deviceid"]) + '\n') 143 | elif "fs" in conn: 144 | with open("out/"+package+"/fs"+stage+".txt", "a") as f: 145 | conn["fs"]["ts"] = time.time() 146 | f.write(json.dumps(conn["fs"]) + '\n') 147 | elif "media" in conn: 148 | with open("out/"+package+"/media"+stage+".txt", "a") as f: 149 | c = json.loads(conn["media"]) 150 | ts = time.time() 151 | c["ts"] = ts 152 | print("adb exec-out screencap -p > out/" + 153 | package+"/media-"+str(ts)+".png") 154 | pp = subprocess.Popen( 155 | ["adb", "exec-out", "screencap", "-p"], stdout=subprocess.PIPE,) 156 | stdoutdata, stderrdata = pp.communicate() 157 | with open("out/"+package+"/media"+stage+"-"+str(ts)+".png", "wb") as fm: 158 | fm.write(stdoutdata) 159 | pp.wait() 160 | # c["ts"] = time.time() 161 | f.write(json.dumps(c) + '\n') 162 | else: 163 | t = "java" if "java" in conn else "native" 164 | conn[t]["pid"] = str(conn[t]["pid"])+"-"+processes[conn[t]["pid"]] 165 | conn[t]["ts"] = time.time() 166 | with open("out/"+package+"/conn"+stage+".txt", "a") as f: 167 | f.write(str(conn) + '\n') 168 | except: 169 | logging.debug("mm") 170 | logging.debug(message) 171 | -------------------------------------------------------------------------------- /js/built_in_crypto.js: -------------------------------------------------------------------------------- 1 | setTimeout(function () { 2 | 3 | Java.perform(function () { 4 | // try { 5 | var cipher = Java.use('javax.crypto.Cipher'); 6 | cipher.doFinal.overload().implementation = function () { 7 | var ret = this.doFinal(); 8 | send('{"crypto":"{\\"class_name\\":\\"javax.crypto.Cipher\\",\\"method_name\\":\\"doFinal\\",\\"hashcode\\":\\"' + this.hashCode() + '\\",\\"args\\":[],\\"ret\\":\\"' + btoa(ret) + '\\",\\"stackTrace\\":\\"' + btoa(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new())) + '\\"}"}'); 9 | return ret; 10 | }; 11 | 12 | cipher.doFinal.overload('[B').implementation = function (p0) { 13 | var ret = this.doFinal(p0); 14 | send('{"crypto":"{\\"class_name\\":\\"javax.crypto.Cipher\\",\\"method_name\\":\\"doFinal\\",\\"hashcode\\":\\"' + this.hashCode() + '\\",\\"args\\":[\\"' + btoa(p0) + '\\"],\\"ret\\":\\"' + btoa(ret) + '\\",\\"stackTrace\\":\\"' + btoa(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new())) + '\\"}"}'); 15 | return ret; 16 | }; 17 | 18 | cipher.doFinal.overload('java.nio.ByteBuffer', 'java.nio.ByteBuffer').implementation = function (p0, p1) { 19 | var ret = this.doFinal(p0, p1); 20 | send('{"crypto":"{\\"class_name\\":\\"javax.crypto.Cipher\\",\\"method_name\\":\\"doFinal\\",\\"hashcode\\":\\"' + this.hashCode() + '\\",\\"args\\":[\\"' + btoa(p0) + '\\",\\"' + btoa(p1) + '\\"],\\"ret\\":\\"' + (ret) + '\\",\\"stackTrace\\":\\"' + btoa(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new())) + '\\"}"}'); 21 | return ret; 22 | }; 23 | 24 | cipher.doFinal.overload('[B', 'int').implementation = function (p0, p1) { 25 | var ret = this.doFinal(p0, p1); 26 | send('{"crypto":"{\\"class_name\\":\\"javax.crypto.Cipher\\",\\"method_name\\":\\"doFinal\\",\\"hashcode\\":\\"' + this.hashCode() + '\\",\\"args\\":[\\"' + btoa(p0) + '\\",\\"' + (p1) + '\\"],\\"ret\\":\\"' + (ret) + '\\",\\"stackTrace\\":\\"' + btoa(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new())) + '\\"}"}'); 27 | return ret; 28 | }; 29 | 30 | cipher.doFinal.overload('[B', 'int', 'int').implementation = function (p0, p1, p2) { 31 | var ret = this.doFinal(p0, p1, p2); 32 | send('{"crypto":"{\\"class_name\\":\\"javax.crypto.Cipher\\",\\"method_name\\":\\"doFinal\\",\\"hashcode\\":\\"' + this.hashCode() + '\\",\\"args\\":[\\"' + btoa(p0) + '\\",\\"' + (p1) + '\\",\\"' + (p2) + '\\"],\\"ret\\":\\"' + btoa(ret) + '\\",\\"stackTrace\\":\\"' + btoa(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new())) + '\\"}"}'); 33 | return ret; 34 | }; 35 | 36 | cipher.doFinal.overload('[B', 'int', 'int', '[B').implementation = function (p0, p1, p2, p3) { 37 | var ret = this.doFinal(p0, p1, p2, p3); 38 | send('{"crypto":"{\\"class_name\\":\\"javax.crypto.Cipher\\",\\"method_name\\":\\"doFinal\\",\\"hashcode\\":\\"' + this.hashCode() + '\\",\\"args\\":[\\"' + btoa(p0) + '\\",\\"' + (p1) + '\\",\\"' + (p2) + '\\",\\"' + btoa(p3) + '\\"],\\"ret\\":\\"' + (ret) + '\\",\\"stackTrace\\":\\"' + btoa(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new())) + '\\"}"}'); 39 | return ret; 40 | }; 41 | 42 | cipher.doFinal.overload('[B', 'int', 'int', '[B', 'int').implementation = function (p0, p1, p2, p3, p4) { 43 | var ret = this.doFinal(p0, p1, p2, p3, p4); 44 | send('{"crypto":"{\\"class_name\\":\\"javax.crypto.Cipher\\",\\"method_name\\":\\"doFinal\\",\\"hashcode\\":\\"' + this.hashCode() + '\\",\\"args\\":[\\"' + btoa(p0) + '\\",\\"' + (p1) + '\\",\\"' + (p2) + '\\",\\"' + btoa(p3) + '\\",\\"' + (p4) + '\\"],\\"ret\\":\\"' + (ret) + '\\",\\"stackTrace\\":\\"' + btoa(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new())) + '\\"}"}'); 45 | return ret; 46 | }; 47 | 48 | 49 | cipher.update.overload('[B').implementation = function (p0) { 50 | var ret = this.update(p0); 51 | send('{"crypto":"{\\"class_name\\":\\"javax.crypto.Cipher\\",\\"method_name\\":\\"update\\",\\"hashcode\\":\\"' + this.hashCode() + '\\",\\"args\\":[\\"' + btoa(p0) + '\\"],\\"ret\\":\\"' + btoa(ret) + '\\",\\"stackTrace\\":\\"' + btoa(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new())) + '\\"}"}'); 52 | return ret; 53 | }; 54 | 55 | cipher.update.overload('[B', 'int', 'int').implementation = function (p0, p1, p2) { 56 | var ret = this.update(p0, p1, p2); 57 | send('{"crypto":"{\\"class_name\\":\\"javax.crypto.Cipher\\",\\"method_name\\":\\"update\\",\\"hashcode\\":\\"' + this.hashCode() + '\\",\\"args\\":[\\"' + btoa(p0) + '\\",\\"' + (p1) + '\\",\\"' + (p2) + '\\"],\\"ret\\":\\"' + btoa(ret) + '\\",\\"stackTrace\\":\\"' + btoa(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new())) + '\\"}"}'); 58 | return ret; 59 | }; 60 | 61 | cipher.update.overload('[B', 'int', 'int', '[B').implementation = function (p0, p1, p2, p3) { 62 | var ret = this.update(p0, p1, p2, p3); 63 | send('{"crypto":"{\\"class_name\\":\\"javax.crypto.Cipher\\",\\"method_name\\":\\"update\\",\\"hashcode\\":\\"' + this.hashCode() + '\\",\\"args\\":[\\"' + btoa(p0) + '\\",\\"' + (p1) + '\\",\\"' + (p2) + '\\",\\"' + btoa(p3) + '\\"],\\"ret\\":\\"' + (ret) + '\\",\\"stackTrace\\":\\"' + btoa(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new())) + '\\"}"}'); 64 | return ret; 65 | }; 66 | 67 | cipher.update.overload('[B', 'int', 'int', '[B', 'int').implementation = function (p0, p1, p2, p3, p4) { 68 | var ret = this.update(p0, p1, p2, p3, p4); 69 | send('{"crypto":"{\\"class_name\\":\\"javax.crypto.Cipher\\",\\"method_name\\":\\"update\\",\\"hashcode\\":\\"' + this.hashCode() + '\\",\\"args\\":[\\"' + btoa(p0) + '\\",\\"' + (p1) + '\\",\\"' + (p2) + '\\",\\"' + btoa(p3) + '\\",\\"' + (p4) + '\\"],\\"ret\\":\\"' + (ret) + '\\",\\"stackTrace\\":\\"' + btoa(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new())) + '\\"}"}'); 70 | return ret; 71 | }; 72 | 73 | 74 | cipher.update.overload('java.nio.ByteBuffer', 'java.nio.ByteBuffer').implementation = function (p0, p1) { 75 | var ret = this.update(p0, p1); 76 | send('{"crypto":"{\\"class_name\\":\\"javax.crypto.Cipher\\",\\"method_name\\":\\"update\\",\\"hashcode\\":\\"' + this.hashCode() + '\\",\\"args\\":[\\"' + btoa(p0) + '\\",\\"' + btoa(p1) + '\\"],\\"ret\\":\\"' + (ret) + '\\",\\"stackTrace\\":\\"' + btoa(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new())) + '\\"}"}'); 77 | return ret; 78 | }; 79 | 80 | 81 | ///////// 82 | 83 | 84 | cipher.init.overload('int', 'java.security.Key').implementation = function (p0, p1) { 85 | this.init(p0, p1); 86 | send('{"crypto":"{\\"class_name\\":\\"javax.crypto.Cipher\\",\\"method_name\\":\\"init-key\\",\\"hashcode\\":\\"' + this.hashCode() + '\\",\\"algorithm\\":\\"' + this.getAlgorithm() + '\\",\\"IV\\":\\"' + btoa(this.getIV()) + '\\",\\"args\\":[\\"' + p0 + '\\",\\"' + btoa(p1.getEncoded()) + '\\"],\\"ret\\":\\"\\"}"}'); 87 | }; 88 | 89 | cipher.init.overload('int', 'java.security.cert.Certificate').implementation = function (p0, p1) { 90 | this.init(p0, p1); 91 | send('{"crypto":"{\\"class_name\\":\\"javax.crypto.Cipher\\",\\"method_name\\":\\"init-cert\\",\\"hashcode\\":\\"' + this.hashCode() + '\\",\\"algorithm\\":\\"' + this.getAlgorithm() + '\\",\\"IV\\":\\"' + btoa(this.getIV()) + '\\",\\"args\\":[\\"' + p0 + '\\",\\"' + btoa(p1.getEncoded()) + '\\"],\\"ret\\":\\"\\"}"}'); 92 | }; 93 | cipher.init.overload('int', 'java.security.Key', 'java.security.AlgorithmParameters').implementation = function (p0, p1, p2) { 94 | this.init(p0, p1, p2); 95 | send('{"crypto":"{\\"class_name\\":\\"javax.crypto.Cipher\\",\\"method_name\\":\\"init-key\\",\\"hashcode\\":\\"' + this.hashCode() + '\\",\\"algorithm\\":\\"' + this.getAlgorithm() + '\\",\\"IV\\":\\"' + btoa(this.getIV()) + '\\",\\"args\\":[\\"' + p0 + '\\",\\"' + btoa(p1.getEncoded()) + '\\",\\"' + p2.getEncoded().getAlgorithm() + '\\"],\\"ret\\":\\"\\"}"}'); 96 | }; 97 | cipher.init.overload('int', 'java.security.Key', 'java.security.SecureRandom').implementation = function (p0, p1, p2) { 98 | this.init(p0, p1, p2); 99 | send('{"crypto":"{\\"class_name\\":\\"javax.crypto.Cipher\\",\\"method_name\\":\\"init-key\\",\\"hashcode\\":\\"' + this.hashCode() + '\\",\\"algorithm\\":\\"' + this.getAlgorithm() + '\\",\\"IV\\":\\"' + btoa(this.getIV()) + '\\",\\"args\\":[\\"' + p0 + '\\",\\"' + btoa(p1.getEncoded()) + '\\",\\"' + "java.security.SecureRandom" + '\\"],\\"ret\\":\\"\\"}"}'); 100 | }; 101 | 102 | cipher.init.overload('int', 'java.security.Key', 'java.security.spec.AlgorithmParameterSpec').implementation = function (p0, p1, p2) { 103 | this.init(p0, p1, p2); 104 | send('{"crypto":"{\\"class_name\\":\\"javax.crypto.Cipher\\",\\"method_name\\":\\"init-key\\",\\"hashcode\\":\\"' + this.hashCode() + '\\",\\"algorithm\\":\\"' + this.getAlgorithm() + '\\",\\"IV\\":\\"' + btoa(this.getIV()) + '\\",\\"args\\":[\\"' + p0 + '\\",\\"' + btoa(p1.getEncoded()) + '\\",\\"' + "java.security.spec.AlgorithmParameterSpec" + '\\"],\\"ret\\":\\"\\"}"}'); 105 | }; 106 | 107 | cipher.init.overload('int', 'java.security.cert.Certificate', 'java.security.SecureRandom').implementation = function (p0, p1, p2) { 108 | this.init(p0, p1, p2); 109 | send('{"crypto":"{\\"class_name\\":\\"javax.crypto.Cipher\\",\\"method_name\\":\\"init-cert\\",\\"hashcode\\":\\"' + this.hashCode() + '\\",\\"algorithm\\":\\"' + this.getAlgorithm() + '\\",\\"IV\\":\\"' + btoa(this.getIV()) + '\\",\\"args\\":[\\"' + p0 + '\\",\\"' + btoa(p1.getEncoded()) + '\\",\\"' + "java.security.SecureRandom" + '\\"],\\"ret\\":\\"\\"}"}'); 110 | }; 111 | 112 | cipher.init.overload('int', 'java.security.Key', 'java.security.AlgorithmParameters', 'java.security.SecureRandom').implementation = function (p0, p1, p2, p3) { 113 | this.init(p0, p1, p2, p3); 114 | send('{"crypto":"{\\"class_name\\":\\"javax.crypto.Cipher\\",\\"method_name\\":\\"init-key\\",\\"hashcode\\":\\"' + this.hashCode() + '\\",\\"algorithm\\":\\"' + this.getAlgorithm() + '\\",\\"IV\\":\\"' + btoa(this.getIV()) + '\\",\\"args\\":[\\"' + p0 + '\\",\\"' + btoa(p1.getEncoded()) + '\\",\\"' + "java.security.AlgorithmParameters" + '\\",\\"' + "java.security.SecureRandom" + '\\"],\\"ret\\":\\"\\"}"}'); 115 | }; 116 | 117 | cipher.init.overload('int', 'java.security.Key', 'java.security.spec.AlgorithmParameterSpec', 'java.security.SecureRandom').implementation = function (p0, p1, p2, p3) { 118 | this.init(p0, p1, p2, p3); 119 | send('{"crypto":"{\\"class_name\\":\\"javax.crypto.Cipher\\",\\"method_name\\":\\"init-key\\",\\"hashcode\\":\\"' + this.hashCode() + '\\",\\"algorithm\\":\\"' + this.getAlgorithm() + '\\",\\"IV\\":\\"' + btoa(this.getIV()) + '\\",\\"args\\":[\\"' + p0 + '\\",\\"' + btoa(p1.getEncoded()) + '\\",\\"' + "java.security.spec.AlgorithmParameterSpec" + '\\",\\"' + "java.security.SecureRandom" + '\\"],\\"ret\\":\\"\\"}"}'); 120 | }; 121 | 122 | ///// 123 | var IvParameterSpec = Java.use('javax.crypto.spec.IvParameterSpec'); 124 | IvParameterSpec.$init.overload('[B').implementation = function (p0) { 125 | var ret = this.$init(p0); 126 | send('{"key-iv":"{\\"class_name\\":\\"javax.crypto.spec.IvParameterSpec\\",\\"method_name\\":\\"$new\\",\\"args\\":[\\"' + btoa(p0) + '\\"],\\"ret\\":\\"\\"}"}'); 127 | return ret; 128 | }; 129 | var IvParameterSpec = Java.use('javax.crypto.spec.IvParameterSpec'); 130 | IvParameterSpec.$init.overload('[B', 'int', 'int').implementation = function (p0, p1, p2) { 131 | var ret = this.$init(p0, p1, p2); 132 | send('{"key-iv":"{\\"class_name\\":\\"javax.crypto.spec.IvParameterSpec\\",\\"method_name\\":\\"$new\\",\\"args\\":[\\"' + btoa(p0) + '\\",\\"' + (p1) + '\\",\\"' + (p2) + '\\"],\\"ret\\":\\"\\"}"}'); 133 | return ret; 134 | }; 135 | var SecretKeySpec = Java.use('javax.crypto.spec.SecretKeySpec'); 136 | SecretKeySpec.$init.overload('[B', 'java.lang.String').implementation = function (p0, p1) { 137 | var ret = this.$init(p0, p1); 138 | send('{"key-iv":"{\\"class_name\\":\\"javax.crypto.spec.SecretKeySpec\\",\\"method_name\\":\\"$new\\",\\"args\\":[\\"' + btoa(p0) + '\\",\\"' + (p1) + '\\"],\\"ret\\":\\"\\"}"}'); 139 | return ret; 140 | }; 141 | var SecretKeySpec = Java.use('javax.crypto.spec.SecretKeySpec'); 142 | SecretKeySpec.$init.overload('[B', 'int', 'int', 'java.lang.String').implementation = function (p0, p1, p2, p3) { 143 | var ret = this.$init(p0, p1, p2, p3); 144 | send('{"key-iv":"{\\"class_name\\":\\"javax.crypto.spec.SecretKeySpec\\",\\"method_name\\":\\"$new\\",\\"args\\":[\\"' + btoa(p0) + '\\",\\"' + (p1) + '\\",\\"' + (p2) + '\\",\\"' + p3 + '\\"],\\"ret\\":\\"\\"}"}'); 145 | return ret; 146 | }; 147 | }); 148 | 149 | }, 0); -------------------------------------------------------------------------------- /js/java.js: -------------------------------------------------------------------------------- 1 | setTimeout(function () { 2 | Java.perform(function () { 3 | var socket = Java.use('java.net.Socket'); 4 | var datagramSocket = Java.use('java.net.DatagramSocket'); 5 | var socketChannel = Java.use('java.nio.channels.SocketChannel'); 6 | var datagramChannel = Java.use('java.nio.channels.DatagramChannel'); 7 | var SettingsSecure = Java.use('android.provider.Settings$Secure'); 8 | // var InetSocketAddress = Java.use('java.net.InetSocketAddress'); 9 | // var sock = Java.use("java.net.Socket"); 10 | 11 | // // socket constructors 12 | 13 | // //new Socket() 14 | // sock.$init.overload().implementation = function(){ 15 | // console.log("new Socket() called"); 16 | // // return this.$init.overload().call(this); 17 | // } 18 | 19 | // // new Socket(inetAddress, port) 20 | // sock.$init.overload("java.net.InetAddress", "int").implementation = function(inetAddress, port){ 21 | // console.log("new Socket('"+inetAddress.toString()+"', "+port+") called"); 22 | // // return this.$init.overload("java.net.InetAddress", "int").call(this, inetAddress, port); 23 | // } 24 | 25 | // // // new Socket(inetAddress address, port, localInetAddress, localPort) 26 | // // sock.$init.overload("java.net.InetAddress", "int","java.net.InetAddress", "int").implementation = function(inetAddress, port, localInet, localPort){ 27 | // // console.log("new Socket(RemoteInet: '"+inetAddress.toString()+"', RemotePort"+port+", LocalInet: '"+localInet+"', LocalPort: "+localPort+") called"); 28 | // // this.$init.overload("java.net.InetAddress", "int","java.net.InetAddress", "int").call(this, inetAddress, port); 29 | // // } 30 | 31 | // // // new Socket(Proxy) 32 | // // sock.$init.overload("java.net.Proxy").implementation = function(proxy){ 33 | // // console.log("new Socket(Proxy: '"+proxy.toString()+"') called"); 34 | // // this.$init.overload("java.net.Proxy").call(this, proxy); 35 | // // } 36 | 37 | // // // new Socket(SocketImp) 38 | // // sock.$init.overload("java.net.SocketImpl").implementation = function(si){ 39 | // // console.log("new Socket(SocketImpl: '"+si.toString()+"') called"); 40 | // // this.$init.overload("java.net.SocketImpl").call(this, si); 41 | // // } 42 | 43 | // // // new Socket(host, port, localInetAddr, localPort) 44 | // // sock.$init.overload("java.lang.String", "int", "java.net.InetAddress", "int").implementation = function(host,port, localInetAddress, localPort){ 45 | // // console.log("new Socket(Host: '"+host+"', RemPort: "+port+", LocalInet: '"+localInetAddress+"', localPort: "+localPort+") called"); 46 | // // this.$init.overload("java.lang.String", "int", "java.net.InetAddress", "int").call(this, si); 47 | // // } 48 | 49 | var inetSockAddrWrap = Java.use("java.net.InetSocketAddress"); 50 | try { 51 | var sfs2x = Java.use('sfs2x.client.SmartFox'); 52 | sfs2x.connect.overload('java.lang.String', 'int').implementation = function (addr, port) { 53 | if (addr != null && port != -1) { 54 | send('{"java":{"protocol":"tcp","function":"SmartFox::connect(String, int)","pid":' + Process.id + ',"address":"' + addr + ":" + port.toString() + '"}}'); 55 | //send(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new())) 56 | } 57 | return this.connect(addr, port) 58 | }; 59 | } catch (error) { } 60 | 61 | socket.connect.overload('java.net.SocketAddress', 'int').implementation = function (addr, timeout) { 62 | // var ret = this.connect(addr, timeout); 63 | if (addr.toString().endsWith(":443")) { 64 | console.log(addr.toString()) 65 | var inetSockAddrImpl = inetSockAddrWrap.$new(proxy_addr4, 8080) 66 | var ret = this.connect(inetSockAddrImpl, timeout); 67 | console.log(addr.toString()); 68 | } else if (addr.toString().endsWith("127.0.0.1:27042")){ 69 | var inetSockAddrImpl = inetSockAddrWrap.$new("127.0.0.1", 1) 70 | send('{"java":{"protocol":"tcp","function":"Socket::connect(SocketAddress, int)","pid":' + Process.id + ',"address":"' + addr.toString() + '","local_address":"' + this.getLocalSocketAddress() + '"}}'); 71 | var ret = this.connect(inetSockAddrImpl, timeout); 72 | } else { 73 | var ret = this.connect(addr, timeout); 74 | } 75 | 76 | // console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new())); 77 | 78 | if (JavaConnectionPool[addr.toString().replace(/^.*\//, '')] == undefined) { 79 | JavaConnectionPool[addr.toString().replace(/^.*\//, '')] = [String(this.getLocalSocketAddress()).replace(/^.*\//, '')] 80 | } else { 81 | JavaConnectionPool[addr.toString().replace(/^.*\//, '')].push(String(this.getLocalSocketAddress()).replace(/^.*\//, '')); 82 | } 83 | 84 | // JavaConnectionPool[addr.toString().replace(/^.*\//, '')] = String(this.getLocalSocketAddress()).replace(/^.*\//, ''); 85 | send('{"java":{"protocol":"tcp","function":"Socket::connect(SocketAddress, int)","pid":' + Process.id + ',"address":"' + addr.toString() + '","local_address":"' + this.getLocalSocketAddress() + '"}}'); 86 | send('{"deviceid":"' + SettingsSecure.getString(Java.use('android.app.ActivityThread').currentApplication().getApplicationContext().getContentResolver(), 'android_id') + '"}'); 87 | return ret; 88 | }; 89 | 90 | socket.connect.overload('java.net.SocketAddress').implementation = function (addr) { 91 | // var ret = this.connect(addr); 92 | if (addr.toString().endsWith(":443")) { 93 | var inetSockAddrImpl = inetSockAddrWrap.$new(proxy_addr4, 8080) 94 | var ret = this.connect(inetSockAddrImpl); 95 | console.log(addr.toString()); 96 | // } else if (addr.toString().endsWith("127.0.0.1:27042")){ 97 | // var inetSockAddrImpl = inetSockAddrWrap.$new("127.0.0.1", 1) 98 | // send('{"java":{"protocol":"tcp","function":"Socket::connect(SocketAddress, int)","pid":' + Process.id + ',"address":"' + addr.toString() + '","local_address":"' + this.getLocalSocketAddress() + '"}}'); 99 | // var ret = this.connect(inetSockAddrImpl); 100 | } else { 101 | var ret = this.connect(addr); 102 | } 103 | 104 | if (JavaConnectionPool[addr.toString().replace(/^.*\//, '')] == undefined) { 105 | JavaConnectionPool[addr.toString().replace(/^.*\//, '')] = [String(this.getLocalSocketAddress()).replace(/^.*\//, '')] 106 | } else { 107 | JavaConnectionPool[addr.toString().replace(/^.*\//, '')].push(String(this.getLocalSocketAddress()).replace(/^.*\//, '')); 108 | } 109 | // JavaConnectionPool[addr.toString().replace(/^.*\//, '')] = String(this.getLocalSocketAddress()).replace(/^.*\//, ''); 110 | send('{"java":{"protocol":"tcp","function":"Socket::connect(SocketAddress)","pid":' + Process.id + ',"address":"' + addr.toString() + '","local_address":"' + this.getLocalSocketAddress() + '"}}'); 111 | send('{"deviceid":"' + SettingsSecure.getString(Java.use('android.app.ActivityThread').currentApplication().getApplicationContext().getContentResolver(), 'android_id') + '"}'); 112 | return ret; 113 | }; 114 | datagramSocket.connect.overload('java.net.InetAddress', 'int').implementation = function (addr, timeout) { 115 | var ret = this.connect(addr, timeout); 116 | if (JavaConnectionPool[addr.toString().replace(/^.*\//, '')] == undefined) { 117 | JavaConnectionPool[addr.toString().replace(/^.*\//, '')] = [String(this.getLocalSocketAddress()).replace(/^.*\//, '')] 118 | } else { 119 | JavaConnectionPool[addr.toString().replace(/^.*\//, '')].push(String(this.getLocalSocketAddress()).replace(/^.*\//, '')); 120 | } 121 | // JavaConnectionPool[addr.toString().replace(/^.*\//, '')] = String(this.getLocalSocketAddress()).replace(/^.*\//, ''); 122 | send('{"java":{"protocol":"tcp","function":"DatagramSocket::connect(SocketAddress, int)","pid":' + Process.id + ',"address":"' + addr.toString() + '","local_address":"' + this.getLocalSocketAddress() + '"}}'); 123 | send('{"deviceid":"' + SettingsSecure.getString(Java.use('android.app.ActivityThread').currentApplication().getApplicationContext().getContentResolver(), 'android_id') + '"}'); 124 | return ret; 125 | }; 126 | datagramSocket.connect.overload('java.net.SocketAddress').implementation = function (addr) { 127 | var ret = this.connect(addr); 128 | if (JavaConnectionPool[addr.toString().replace(/^.*\//, '')] == undefined) { 129 | JavaConnectionPool[addr.toString().replace(/^.*\//, '')] = [String(this.getLocalSocketAddress()).replace(/^.*\//, '')] 130 | } else { 131 | JavaConnectionPool[addr.toString().replace(/^.*\//, '')].push(String(this.getLocalSocketAddress()).replace(/^.*\//, '')); 132 | } 133 | // JavaConnectionPool[addr.toString().replace(/^.*\//, '')] = String(this.getLocalSocketAddress()).replace(/^.*\//, ''); 134 | send('{"java":{"protocol":"tcp","function":"DatagramSocket::connect(SocketAddress)","pid":' + Process.id + ',"address":"' + addr.toString() + '","local_address":"' + this.getLocalSocketAddress() + '"}}'); 135 | send('{"deviceid":"' + SettingsSecure.getString(Java.use('android.app.ActivityThread').currentApplication().getApplicationContext().getContentResolver(), 'android_id') + '"}'); 136 | return ret; 137 | }; 138 | datagramSocket.send.implementation = function (dp) { 139 | var ret = this.send(dp); 140 | if (JavaConnectionPool[dp.getSocketAddress().toString().replace(/^.*\//, '')] == undefined) { 141 | JavaConnectionPool[dp.getSocketAddress().toString().replace(/^.*\//, '')] = [String(this.getLocalSocketAddress()).replace(/^.*\//, '')] 142 | } else { 143 | JavaConnectionPool[dp.getSocketAddress().toString().replace(/^.*\//, '')].push(String(this.getLocalSocketAddress()).replace(/^.*\//, '')); 144 | } 145 | // JavaConnectionPool[dp.getSocketAddress().toString().replace(/^.*\//, '')] = String(this.getLocalSocketAddress()).replace(/^.*\//, ''); 146 | send('{"java":{"protocol":"udp","function":"DatagramSocket::send","pid":' + Process.id + ',"address":"' + dp.getSocketAddress().toString() + '","local_address":"' + this.getLocalSocketAddress() + '"}}'); 147 | send('{"deviceid":"' + SettingsSecure.getString(Java.use('android.app.ActivityThread').currentApplication().getApplicationContext().getContentResolver(), 'android_id') + '"}'); 148 | return ret; 149 | }; 150 | datagramSocket.receive.implementation = function (dp) { 151 | var ret = this.receive(dp); 152 | if (JavaConnectionPool[dp.getSocketAddress().toString().replace(/^.*\//, '')] == undefined) { 153 | JavaConnectionPool[dp.getSocketAddress().toString().replace(/^.*\//, '')] = [String(this.getLocalSocketAddress()).replace(/^.*\//, '')] 154 | } else { 155 | JavaConnectionPool[dp.getSocketAddress().toString().replace(/^.*\//, '')].push(String(this.getLocalSocketAddress()).replace(/^.*\//, '')); 156 | } 157 | // JavaConnectionPool[dp.getSocketAddress().toString().replace(/^.*\//, '')] = String(this.getLocalSocketAddress()).replace(/^.*\//, ''); 158 | send('{"java":{"protocol":"udp","function":"DatagramSocket::receive","pid":' + Process.id + ',"address":"' + dp.getSocketAddress().toString() + '","local_address":"' + this.getLocalSocketAddress() + '"}}'); 159 | send('{"deviceid":"' + SettingsSecure.getString(Java.use('android.app.ActivityThread').currentApplication().getApplicationContext().getContentResolver(), 'android_id') + '"}'); 160 | return ret; 161 | }; 162 | socketChannel.connect.implementation = function (addr) { 163 | var ret = this.connect(addr); 164 | if (JavaConnectionPool[addr.toString().replace(/^.*\//, '')] == undefined) { 165 | JavaConnectionPool[addr.toString().replace(/^.*\//, '')] = [String(this.socket().getLocalSocketAddress()).replace(/^.*\//, '')] 166 | } else { 167 | JavaConnectionPool[addr.toString().replace(/^.*\//, '')].push(String(this.socket().getLocalSocketAddress()).replace(/^.*\//, '')); 168 | } 169 | 170 | // JavaConnectionPool[addr.toString().replace(/^.*\//, '')] = String(this.getLocalSocketAddress()).replace(/^.*\//, ''); 171 | send('{"java":{"protocol":"tcp","function":"SocketChannel::connect(SocketAddress)","pid":' + Process.id + ',"address":"' + addr.toString() + '","local_address":"' + this.socket().getLocalSocketAddress() + '"}}'); 172 | send('{"deviceid":"' + SettingsSecure.getString(Java.use('android.app.ActivityThread').currentApplication().getApplicationContext().getContentResolver(), 'android_id') + '"}'); 173 | return ret; 174 | }; 175 | socketChannel.open.overload('java.net.SocketAddress').implementation = function (addr) { 176 | var ret = this.open(addr); 177 | if (JavaConnectionPool[addr.toString().replace(/^.*\//, '')] == undefined) { 178 | JavaConnectionPool[addr.toString().replace(/^.*\//, '')] = [String(this.socket().getLocalSocketAddress()).replace(/^.*\//, '')] 179 | } else { 180 | JavaConnectionPool[addr.toString().replace(/^.*\//, '')].push(String(this.socket().getLocalSocketAddress()).replace(/^.*\//, '')); 181 | } 182 | // JavaConnectionPool[addr.toString().replace(/^.*\//, '')] = String(this.socket().getLocalSocketAddress()).replace(/^.*\//, ''); 183 | send('{"java":{"protocol":"tcp","function":"SocketChannel::open(SocketAddress)","pid":' + Process.id + ',"address":"' + addr.toString() + '","local_address":"' + this.socket().getLocalSocketAddress() + '"}}'); 184 | send('{"deviceid":"' + SettingsSecure.getString(Java.use('android.app.ActivityThread').currentApplication().getApplicationContext().getContentResolver(), 'android_id') + '"}'); 185 | return ret; 186 | }; 187 | datagramChannel.connect.implementation = function (addr) { 188 | var ret = this.connect(addr); 189 | if (JavaConnectionPool[addr.toString().replace(/^.*\//, '')] == undefined) { 190 | JavaConnectionPool[addr.toString().replace(/^.*\//, '')] = [String(this.socket().getLocalSocketAddress()).replace(/^.*\//, '')] 191 | } else { 192 | JavaConnectionPool[addr.toString().replace(/^.*\//, '')].push(String(this.socket().getLocalSocketAddress()).replace(/^.*\//, '')); 193 | } 194 | // JavaConnectionPool[addr.toString().replace(/^.*\//, '')] = String(this.socket().getLocalSocketAddress()).replace(/^.*\//, ''); 195 | send('{"java":{"protocol":"tcp","function":"DatagramChannel::connect(SocketAddress)","pid":' + Process.id + ',"address":"' + addr.toString() + '","local_address":"' + this.socket().getLocalSocketAddress() + '"}}'); 196 | send('{"deviceid":"' + SettingsSecure.getString(Java.use('android.app.ActivityThread').currentApplication().getApplicationContext().getContentResolver(), 'android_id') + '"}'); 197 | return ret; 198 | }; 199 | }); 200 | }, 0); -------------------------------------------------------------------------------- /device.py: -------------------------------------------------------------------------------- 1 | import hashlib 2 | import json 3 | import logging 4 | import math 5 | import os 6 | import subprocess 7 | import sys 8 | import time 9 | 10 | import frida 11 | from com.dtmilano.android.adb.adbclient import AdbClient 12 | from com.dtmilano.android.viewclient import ViewClient 13 | from uiautomator import device 14 | 15 | import exceptions 16 | import interactor 17 | import timeout 18 | 19 | # adb shell ime list -s -a 20 | # adb shell ime set com.apedroid.hwkeyboardhelperfree/.HWKeyboardHelperIME 21 | 22 | 23 | def get_active_devices(): 24 | devices = list() 25 | for d in AdbClient().getDevices(): 26 | transport_id = [i for i in d.qualifiers if i.startswith( 27 | 'transport_id:')][0][13:] 28 | devices.append(Device(AdbClient(serialno=d.serialno), transport_id)) 29 | logging.debug(d.serialno) 30 | if devices == []: 31 | raise exceptions.DeviceNotFound 32 | return devices 33 | 34 | 35 | class Device: 36 | def __init__(self, d, transport_id): 37 | 38 | self.d = d 39 | self.transport_id = transport_id 40 | self.shell = lambda c: self.d.shell(c).strip() 41 | self.alive = True 42 | self.info = {} 43 | self.update_info() 44 | self.frida = list(d for d in frida.get_device_manager( 45 | ).enumerate_devices() if d.id in (self.d.serialno, self.info["ro.serialno"]))[0] 46 | self.close_all_apps() 47 | self.vc = ViewClient(*(self.d, self.d.serialno)) 48 | self.display = Display(self) 49 | self.permissions = self.get_device_permissions() 50 | 51 | def second(self): 52 | self.frida = list(d for d in frida.get_device_manager().enumerate_devices( 53 | ) if d.id in (self.d.serialno, self.info["ro.serialno"]))[0] 54 | 55 | def get_device_permissions(self): 56 | return set(p[11:] for p in self.shell("pm list permissions").splitlines() if p.startswith("permission:")) 57 | 58 | def get_app_permissions(self, package): 59 | dumpsys = self.shell("dumpsys package "+package).splitlines() 60 | l = {"android.permission.SYSTEM_ALERT_WINDOW"} 61 | a = str() 62 | f = False 63 | 64 | for i, r in enumerate(dumpsys): 65 | if r.startswith((" "*6)+"android.service.notification.NotificationListenerService") and dumpsys[i+1].startswith(" "*8) and dumpsys[i+1].endswith("BIND_NOTIFICATION_LISTENER_SERVICE"): 66 | a += "cmd notification allow_listener {};".format( 67 | dumpsys[i+1][8:].split(' ')[1]) 68 | break 69 | for i, r in enumerate(dumpsys): 70 | if r.startswith((" "*6)+"android.accessibilityservice.AccessibilityService") and dumpsys[i+1].startswith(" "*8) and dumpsys[i+1].endswith("BIND_ACCESSIBILITY_SERVICE"): 71 | a += "settings put secure enabled_accessibility_services {};".format( 72 | dumpsys[i+1][8:].split(' ')[1]) 73 | break 74 | for i in dumpsys: 75 | if i.startswith((" "*6)+"runtime permissions"): 76 | f = True 77 | continue 78 | if f and i.startswith(" "*8): 79 | if ' ' in i[8:]: 80 | l.add(i[8:].split(': ')[0]) 81 | else: 82 | l.add(i[8:]) 83 | else: 84 | f = False 85 | # print(l) 86 | return l, a 87 | # for i in self.shell("dumpsys package "+package).splitlines(): 88 | # if i.startswith((" "*4)+"requested permissions"): 89 | # f = True 90 | # continue 91 | # if f and i.startswith(" "*6): 92 | # if ' ' in i[6:]: 93 | # l.add(i[6:].split(': ')[0]) 94 | # else: 95 | # l.add(i[6:]) 96 | # else: 97 | # f = False 98 | # return l 99 | 100 | def grant_app_permissions(self, package, perms=set(), service_name=str(), service=True): 101 | _perms = set() 102 | if len(perms) == 0: 103 | _perms, _service_name = self.get_app_permissions(package) 104 | else: 105 | _perms = perms 106 | _service_name = service_name 107 | for perm in _perms: 108 | self.shell("pm grant "+package+" "+perm) 109 | if _service_name: 110 | self.shell(_service_name) 111 | return _perms, _service_name 112 | 113 | def update_info(self): 114 | serialno = self.shell("getprop ro.serialno") 115 | if len(serialno) != 0: 116 | self.info["ro.serialno"] = serialno 117 | 118 | def is_alive(self): 119 | try: 120 | with timeout.timeout(seconds=20): 121 | if self.shell("echo alive") == "alive": 122 | self.alive = True 123 | return True 124 | else: 125 | self.alive = False 126 | return False 127 | except: 128 | self.alive = False 129 | return False 130 | 131 | def close_app(self, package): 132 | try: 133 | if self.shell("pm clear "+package) == "Success": 134 | return True 135 | else: 136 | return False 137 | except: 138 | return False 139 | 140 | def close_all_apps(self): 141 | packages = self.get_paused_activites() 142 | print(packages) 143 | if self.get_current_activity() != None: 144 | packages.add(self.get_current_activity()) 145 | # packages.discard('com.android.launcher3/.lineage.LineageLauncher') 146 | packages.discard( 147 | 'com.google.android.apps.nexuslauncher/.NexusLauncherActivity') 148 | packages.discard('com.google.android.apps.nexuslauncher/com.android.launcher3.settings.SettingsActivity') 149 | # packages.add('org.lineageos.jelly') 150 | # packages.add('com.android.chrome') 151 | print(packages) 152 | for package in packages: 153 | p = package.split("/")[0] 154 | if p == "com.google.android.apps.nexuslauncher": 155 | self.shell("am force-stop "+p) 156 | elif self.shell("pm clear "+p) != "Success": 157 | return False 158 | return True 159 | 160 | def close_paused_apps(self): 161 | for package in self.get_paused_activites(): 162 | p = package.split("/")[0] 163 | if p == "com.google.android.apps.nexuslauncher": 164 | self.shell("am force-stop "+p) 165 | elif self.shell("pm clear "+p) != "Success": 166 | return False 167 | return True 168 | 169 | def uninstall_3rd_party_apps(self): 170 | self.shell("su -c killall tcpdump") 171 | packages = self.shell('pm list packages -3 | cut -c9- | grep -Ev "(com.apedroid.hwkeyboardhelperfree|com.github.shadowsocks|com.research.helper|org.proxydroid|com.fakemygps.android|org.meowcat.edxposed.manager|edu.berkeley.icsi.haystack|com.topjohnwu.magisk|app.greyshirts.sslcapture|tw.fatminmin.xposed.minminguard|com.cofface.ivader)"') 172 | for package in packages.splitlines(): 173 | self.uninstall_app(package) 174 | 175 | def is_internet_available(self): 176 | # if "success" in self.shell( 177 | # "echo \"GET /success.txt\" | nc detectportal.firefox.com 80"): 178 | # if "success" in self.shell("curl --connect-timeout 2 detectportal.firefox.com/success.txt"): 179 | if "ttl=" in self.shell("ping -c 1 1.1.1.1"): 180 | return True 181 | return False 182 | 183 | def wait_if_internet_isnt_available(self): 184 | while self.is_internet_available() == False: 185 | logging.warning('Internet is not available, please wait') 186 | time.sleep(2) 187 | 188 | def is_app_crashed(self, app): 189 | current_focus = self.shell( 190 | "dumpsys activity activities | grep -E \"mCurrentFocus.+Application Error:.+"+app+"\"").split() 191 | if len(current_focus) > 0: 192 | return True 193 | else: 194 | return False 195 | 196 | def is_app_hangs(self, app): 197 | current_focus = self.shell( 198 | "dumpsys activity activities | grep -E \"mCurrentFocus.+Application Not Responding:.+"+app+"\"").split() 199 | if len(current_focus) > 0: 200 | return True 201 | else: 202 | return False 203 | 204 | def get_current_activity(self): 205 | # time.sleep(0.5) 206 | m_resumed_activity = self.shell( 207 | "dumpsys activity activities | grep mResumedActivity").split() 208 | print("00s00") 209 | print(m_resumed_activity) 210 | print("00s01") 211 | i = 0 212 | if len(m_resumed_activity) > 0: 213 | print("00s01-1") 214 | if m_resumed_activity in (["Can't", 'find', 'service:', 'activity']) or m_resumed_activity[0] == "Can't": 215 | print("00s01-2") 216 | os.system('kill -9 {pid}'.format(pid=os.getpid())) 217 | 218 | while m_resumed_activity in ([], ['mHoldScreenWindow=null']): 219 | if i > 8: 220 | return None 221 | break 222 | i += 1 223 | print(m_resumed_activity) 224 | print("00s11") 225 | # self.shell('input keyevent KEYCODE_POWER') 226 | self.shell('input keyevent KEYCODE_HOME') 227 | time.sleep(3) 228 | self.d.wake() 229 | m_resumed_activity = self.shell( 230 | "dumpsys activity activities | grep mResumedActivity").split() 231 | if m_resumed_activity == []: 232 | m_resumed_activity = self.shell( 233 | "dumpsys window windows | grep mHoldScreenWindow").split() 234 | if m_resumed_activity in ([], ['mHoldScreenWindow=null']): 235 | m_resumed_activity = self.shell( 236 | "dumpsys window windows | grep mActivityRecord | grep -v com.android.launcher3").split() 237 | print(m_resumed_activity) 238 | if len(m_resumed_activity) > 0: 239 | if m_resumed_activity in (["Can't", 'find', 'service:', 'activity']) or m_resumed_activity[0] == "Can't": 240 | os.system('kill -9 {pid}'.format(pid=os.getpid())) 241 | # logging.debug("m_resumed_activity") 242 | # logging.debug(m_resumed_activity) 243 | 244 | # while m_resumed_activity == []: 245 | # logging.debug(m_resumed_activity) 246 | # time.sleep(0.5) 247 | # m_resumed_activity = self.shell( 248 | # "dumpsys activity activities | grep mResumedActivity").split() 249 | # logging.debug(m_resumed_activity) 250 | print(len(m_resumed_activity)) 251 | if len(m_resumed_activity) > 2 and m_resumed_activity[0].startswith("mActivityRecord"): 252 | print(m_resumed_activity[2]) 253 | return m_resumed_activity[2] 254 | if len(m_resumed_activity) > 4: 255 | print(m_resumed_activity[3]) 256 | return m_resumed_activity[3] 257 | 258 | def get_paused_activites(self): 259 | return set(line.split()[3] for line in self.shell("dumpsys activity activities | grep mLastPausedActivity").splitlines()) 260 | 261 | def get_package_window_hash(self, pkg): 262 | packages = self.get_paused_activites() 263 | if self.get_current_activity() != None: 264 | packages.add(self.get_current_activity()) 265 | for package in packages: 266 | if package.startswith(pkg): 267 | return hashlib.sha256(self.shell("dumpsys window | grep "+pkg).encode("utf-8")).hexdigest() 268 | 269 | def pull(self, src, dst="./"): 270 | # logging.debug(src) 271 | final_path = dst+"/"+src.split("/").pop() 272 | try: 273 | apk_device_hash = self.shell("sha256sum"+" "+src).split()[0] 274 | logging.debug(apk_device_hash) 275 | logging.debug(src) 276 | except: 277 | return False 278 | 279 | if os.path.exists(final_path): 280 | with open(final_path, "rb") as f: 281 | if hashlib.sha256(f.read()).hexdigest() == apk_device_hash: 282 | return True 283 | p = subprocess.Popen( 284 | ["adb", "-t", self.transport_id, "pull", src, dst],) 285 | # stdout=subprocess.PIPE,) 286 | p.wait() 287 | with open(final_path, "rb") as f: 288 | if hashlib.sha256(f.read()).hexdigest() != apk_device_hash: 289 | return False 290 | return True 291 | 292 | def push(self, src, dst): 293 | # src_name = src.split("/").pop() 294 | with open(src, "rb") as f: 295 | fhash = hashlib.sha256(f.read()).hexdigest() 296 | p = subprocess.Popen( 297 | ["adb", "-t", self.transport_id, "push", src, dst],) 298 | # stdout=subprocess.PIPE,) 299 | p.wait() 300 | try: 301 | apk_device_hash = self.shell( 302 | "sha256sum"+" "+dst+src.split("/").pop()).split()[0] 303 | except: 304 | return False 305 | if fhash != apk_device_hash: 306 | return False 307 | return True 308 | 309 | def start_capture(self, package): 310 | self.shell("rm -f /sdcard/*") 311 | self.shell("rm -f /data/local/tmp/*.pcap") 312 | return (subprocess.Popen( 313 | [ 314 | "adb", 315 | "shell", 316 | "su", 317 | "-c", 318 | "tcpdump", 319 | "port not 5555", 320 | "-i", 321 | "wlan0", 322 | "-w", 323 | "/data/local/tmp/" + package + ".pcap", 324 | ], 325 | stdout=subprocess.DEVNULL, 326 | stderr=subprocess.DEVNULL, 327 | ), subprocess.Popen( 328 | [ 329 | "mitmdump", 330 | "-w", 331 | "out/"+package+"/mitmdump", 332 | "--anticomp", 333 | "--listen-port", 334 | "8080", 335 | ], 336 | stdout=subprocess.DEVNULL, 337 | stderr=subprocess.DEVNULL, 338 | )) 339 | 340 | def file_readable(self, path): 341 | if (self.shell("if [ -r \""+path+"\" ] && [ -f \""+path+"\" ]; then echo True;fi") == "True"): 342 | return True 343 | else: 344 | return False 345 | 346 | def store_files(self, package, stage): 347 | outpath = "out/"+package+"/" 348 | filelines = [] 349 | for f in os.listdir(outpath): 350 | if f.startswith('fs-') and f.endswith('.txt'): 351 | with open(outpath+f, 'r') as _f: 352 | filelines += _f.readlines() 353 | recordes = [json.loads(i) for i in filelines if json.loads(i)[ 354 | "function"] in ("open", "rename")] 355 | paths = ((i["path"] if i[ 356 | "function"] == "open" else i["destination"]) for i in recordes) 357 | 358 | for p in paths: 359 | 360 | if self.file_readable(p): 361 | os.makedirs(outpath+"/files-"+str(stage) + "/" + 362 | os.path.dirname(p), exist_ok=True) 363 | self.pull(p, outpath+"/files-"+str(stage) + 364 | "/"+os.path.dirname(p)+"/") 365 | 366 | def stop_capture(self, p, mitm, package): 367 | p.kill() 368 | mitm.kill() 369 | # parents_of_dead_kids=$(ps -ef | grep [d]efunct | awk '{print $3}' | sort | uniq | egrep -v '^1$'); echo "$parents_of_dead_kids" | xargs kill 370 | self.shell("su -c killall tcpdump") 371 | # logging.debug("x") 372 | self.pull("/data/local/tmp/" + package+".pcap", "out/"+package) 373 | subprocess.Popen( 374 | [ 375 | "pcapfix", 376 | "out/" + package + "/"+package + ".pcap", 377 | "-o", 378 | "out/" + package + "/clean.pcap", 379 | ], 380 | stdout=subprocess.DEVNULL, 381 | stderr=subprocess.DEVNULL, 382 | ).wait() 383 | # tmp 384 | os.system( 385 | "echo $(ps -ef | grep [d]efunct | awk '{print $3}' | sort | uniq | grep mitmdump| egrep -v '^1$') | xargs kill -9") 386 | 387 | def install_app(self, package, output="out/", reinstall=True): # multiple try 388 | if os.path.exists(output + package+"/"+package + ".pcap") or os.path.exists("out/" + package + ".fail"): 389 | return False 390 | self.shell("su -c 'pm disable com.android.chrome;pm disable com.google.android.youtube;pm disable com.google.android.calendar;pm disable com.google.android.apps.docs;pm disable com.google.android.apps.customization.pixel;pm disable com.google.android.gm;pm disable com.google.android.apps.tycho;pm disable com.google.android.calculator;pm disable com.google.android.markup;pm disable com.android.safetyregulatoryinfo;pm disable com.google.android.apps.wallpaper.pixel;pm disable com.google.android.videos;pm disable com.google.android.apps.youtube.music;pm disable com.google.pixel.dynamicwallpapers;pm disable com.google.ar.core;pm disable com.google.android.projection.gearhead;pm disable com.google.android.apps.tips;pm disable com.google.android.googlequicksearchbox;pm disable com.google.android.apps.safetyhub'") 391 | if self.is_app_exist(package) and reinstall: 392 | self.uninstall_app(package) 393 | elif self.is_app_exist(package) and reinstall == False: 394 | self.shell("su -c pm disable {package};".format(package=package)) 395 | return True 396 | self.shell( 397 | "content insert --uri content://settings/system --bind name:s:accelerometer_rotation --bind value:i:0") 398 | if os.path.exists(output + package + "/base.apk"): # debug 399 | apks = list(output + package + "/"+f for f in os.listdir( 400 | output + package) if f.endswith('.apk')) 401 | obbs = list(output + package + "/"+f for f in os.listdir( 402 | output + package) if f.endswith('.obb')) 403 | if apks: 404 | p = subprocess.Popen( 405 | ["adb", "-t", self.transport_id,"install-multiple" , "-g"]+apks, stdout=subprocess.PIPE,) 406 | p.wait() 407 | try: 408 | if "Success" in str(p.communicate()[0]): 409 | if obbs: 410 | self.shell("mkdir -p /sdcard/obb/"+package) 411 | print(obbs) 412 | if all([self.push(obb, "/sdcard/obb/"+package+"/") for obb in obbs]): 413 | self.shell("mv /sdcard/obb/{} /sdcard/Android/obb/".format(package)) 414 | self.shell( 415 | "su -c pm disable {package};".format(package=package)) 416 | return True 417 | else: 418 | return False 419 | self.shell( 420 | "su -c pm disable {package};".format(package=package)) 421 | return True 422 | except: 423 | return False 424 | else: 425 | if os.path.exists(output + package + ".fail"): 426 | return False 427 | gp = interactor.GooglePlay(self) 428 | for _ in range(0, 2): 429 | gpi = gp.install(package) 430 | if gpi == None or gpi == False: 431 | with open(output + package + ".fail", 'w') as fp: 432 | pass 433 | return False 434 | elif gpi == True: 435 | self.shell( 436 | "su -c pm disable {package};".format(package=package)) 437 | return True 438 | pass 439 | pass 440 | 441 | def uninstall_app(self, package): 442 | self.shell( 443 | 'for file in $(find /sdcard/ -maxdepth 1 ); do if [ $file != "/sdcard/DCIM" ] && [ $file != "/sdcard/" ]; then rm -rf "$file" ;fi;done;rm -rf /sdcard/*\ *') 444 | if self.is_app_exist(package) and self.shell("pm uninstall {package}".format(package=package)) == "Success": 445 | return True 446 | return False 447 | 448 | def is_app_open(self, package): 449 | try: 450 | current_activity = self.get_current_activity() 451 | print(current_activity) 452 | if self.get_current_activity() and (current_activity.startswith(package) or current_activity.startswith("com.google.android.gms/.common") or current_activity in ["com.google.android.gms/.signin.activity.ConsentActivity", "com.google.android.gms/.auth.uiflows.consent.BrowserConsentActivity", "com.google.android.gms/.auth.uiflows.addaccount.AccountIntroActivity", "com.android.permissioncontroller/.permission.ui.ReviewPermissionsActivity"]): 453 | return True 454 | return False 455 | except: 456 | return False 457 | 458 | def run_app(self, package, close=True): 459 | if close: 460 | self.close_all_apps() 461 | self.shell( 462 | "su -c pm enable {package};monkey -p {package} --pct-touch 100 1".format(package=package)) 463 | # print("----------") 464 | time.sleep(1) 465 | if not self.is_app_open(package): 466 | return True 467 | else: 468 | return False 469 | 470 | def is_app_exist(self, package): 471 | for exsited_package in self.shell("cmd package list packages "+package).splitlines(): 472 | if exsited_package.split(":", 1)[1] == package: 473 | return True 474 | return False 475 | 476 | def store_app(self, package, output="out/"): 477 | if os.path.exists(output + package+"/base.apk"): 478 | return 479 | if not os.path.exists(output + package): 480 | os.makedirs(output + package) 481 | obbs = list(map(lambda x: "/sdcard/Android/obb/"+package+"/"+x, 482 | self.shell("ls -1 /sdcard/Android/obb/"+package+"/").split("\n"))) 483 | if "No such" in obbs[0]: 484 | obbs.clear() 485 | apks = list(map(lambda x: x[8:], self.shell( 486 | "pm path "+package).split("\n"))) 487 | for i in obbs+apks: 488 | while True: 489 | try: 490 | with timeout.timeout(seconds=120): 491 | logging.debug("t2") 492 | print(i, output + package) 493 | if self.pull(i, output + package) == True: 494 | break 495 | except: 496 | subprocess.Popen(["killall", "adb"], 497 | stdout=subprocess.PIPE,).wait() 498 | subprocess.Popen( 499 | ["rm", "-rf", output + package], stdout=subprocess.PIPE,).wait() 500 | # sys.exit() 501 | os.system('kill -9 {pid}'.format(pid=os.getpid())) 502 | 503 | def start_interaction(self, package, stage, analysis_time): 504 | # try: 505 | if not self.is_app_open(package): 506 | self.run_app(package) 507 | interaction = interactor.App(self, package, stage, analysis_time) 508 | interaction.smart() 509 | # except: 510 | # with open("out/"+package+"/animat.txt", "a") as f: 511 | # f.write(str(stage)+"-"+str(int(time.time()))+"\n") 512 | # return False 513 | # # The views are being refreshed too frequently to dump. 514 | # logging.debug("xx") 515 | 516 | 517 | class Display: 518 | def __init__(self, device): 519 | self.density = int(device.shell("wm density").split(" ")[-1]) 520 | (x, y) = device.shell("wm size").split(" ")[-1].split("x") 521 | self.x = int(x) 522 | self.y = int(y) 523 | self.statusbar = math.ceil(self.density/160)*24 524 | 525 | 526 | # adb shell content query --uri content://com.android.contacts/data --projection display_name:data1:data4:contact_id 527 | # adb shell "su -c 'sqlite3 /data/data/com.android.providers.contacts/databases/calllog.db \"select * from calls\"'" 528 | -------------------------------------------------------------------------------- /js/pinning.js: -------------------------------------------------------------------------------- 1 | setTimeout(function () { 2 | Java.perform(function () { 3 | //console.log("---"); 4 | //console.log("Unpinning Android app..."); 5 | 6 | // HttpsURLConnection 7 | try { 8 | const HttpsURLConnection = Java.use("javax.net.ssl.HttpsURLConnection"); 9 | HttpsURLConnection.setDefaultHostnameVerifier.implementation = function (hostnameVerifier) { 10 | //console.log(' --> Bypassing HttpsURLConnection (setDefaultHostnameVerifier)'); 11 | return; // Do nothing, i.e. don't change the hostname verifier 12 | }; 13 | //console.log('[+] HttpsURLConnection (setDefaultHostnameVerifier)'); 14 | } catch (err) { 15 | //console.log('[ ] HttpsURLConnection (setDefaultHostnameVerifier)'); 16 | } 17 | try { 18 | const HttpsURLConnection = Java.use("javax.net.ssl.HttpsURLConnection"); 19 | HttpsURLConnection.setSSLSocketFactory.implementation = function (SSLSocketFactory) { 20 | //console.log(' --> Bypassing HttpsURLConnection (setSSLSocketFactory)'); 21 | return; // Do nothing, i.e. don't change the SSL socket factory 22 | }; 23 | //console.log('[+] HttpsURLConnection (setSSLSocketFactory)'); 24 | } catch (err) { 25 | //console.log('[ ] HttpsURLConnection (setSSLSocketFactory)'); 26 | } 27 | try { 28 | const HttpsURLConnection = Java.use("javax.net.ssl.HttpsURLConnection"); 29 | HttpsURLConnection.setHostnameVerifier.implementation = function (hostnameVerifier) { 30 | //console.log(' --> Bypassing HttpsURLConnection (setHostnameVerifier)'); 31 | return; // Do nothing, i.e. don't change the hostname verifier 32 | }; 33 | //console.log('[+] HttpsURLConnection (setHostnameVerifier)'); 34 | } catch (err) { 35 | //console.log('[ ] HttpsURLConnection (setHostnameVerifier)'); 36 | } 37 | 38 | // SSLContext 39 | try { 40 | const X509TrustManager = Java.use('javax.net.ssl.X509TrustManager'); 41 | const SSLContext = Java.use('javax.net.ssl.SSLContext'); 42 | 43 | const TrustManager = Java.registerClass({ 44 | // Implement a custom TrustManager 45 | name: 'dev.asd.test.TrustManager', 46 | implements: [X509TrustManager], 47 | methods: { 48 | checkClientTrusted: function (chain, authType) { }, 49 | checkServerTrusted: function (chain, authType) { }, 50 | getAcceptedIssuers: function () { return []; } 51 | } 52 | }); 53 | 54 | // Prepare the TrustManager array to pass to SSLContext.init() 55 | const TrustManagers = [TrustManager.$new()]; 56 | 57 | // Get a handle on the init() on the SSLContext class 58 | const SSLContext_init = SSLContext.init.overload( 59 | '[Ljavax.net.ssl.KeyManager;', '[Ljavax.net.ssl.TrustManager;', 'java.security.SecureRandom' 60 | ); 61 | 62 | // Override the init method, specifying the custom TrustManager 63 | SSLContext_init.implementation = function (keyManager, trustManager, secureRandom) { 64 | //console.log(' --> Bypassing Trustmanager (Android < 7) request'); 65 | SSLContext_init.call(this, keyManager, TrustManagers, secureRandom); 66 | }; 67 | //console.log('[+] SSLContext'); 68 | } catch (err) { 69 | //console.log('[ ] SSLContext'); 70 | } 71 | 72 | // TrustManagerImpl (Android > 7) 73 | try { 74 | const array_list = Java.use("java.util.ArrayList"); 75 | const TrustManagerImpl = Java.use('com.android.org.conscrypt.TrustManagerImpl'); 76 | 77 | // This step is notably what defeats the most common case: network security config 78 | TrustManagerImpl.checkTrustedRecursive.implementation = function(a1, a2, a3, a4, a5, a6) { 79 | //console.log(' --> Bypassing TrustManagerImpl checkTrusted '); 80 | return array_list.$new(); 81 | } 82 | 83 | TrustManagerImpl.verifyChain.implementation = function (untrustedChain, trustAnchorChain, host, clientAuth, ocspData, tlsSctData) { 84 | //console.log(' --> Bypassing TrustManagerImpl verifyChain: ' + host); 85 | return untrustedChain; 86 | }; 87 | //console.log('[+] TrustManagerImpl'); 88 | } catch (err) { 89 | //console.log('[ ] TrustManagerImpl'); 90 | } 91 | 92 | // OkHTTPv3 (quadruple bypass) 93 | try { 94 | // Bypass OkHTTPv3 {1} 95 | const okhttp3_Activity_1 = Java.use('okhttp3.CertificatePinner'); 96 | okhttp3_Activity_1.check.overload('java.lang.String', 'java.util.List').implementation = function (a, b) { 97 | //console.log(' --> Bypassing OkHTTPv3 (list): ' + a); 98 | return; 99 | }; 100 | //console.log('[+] OkHTTPv3 (list)'); 101 | } catch (err) { 102 | //console.log('[ ] OkHTTPv3 (list)'); 103 | } 104 | try { 105 | // Bypass OkHTTPv3 {2} 106 | // This method of CertificatePinner.check could be found in some old Android app 107 | const okhttp3_Activity_2 = Java.use('okhttp3.CertificatePinner'); 108 | okhttp3_Activity_2.check.overload('java.lang.String', 'java.security.cert.Certificate').implementation = function (a, b) { 109 | //console.log(' --> Bypassing OkHTTPv3 (cert): ' + a); 110 | return; 111 | }; 112 | //console.log('[+] OkHTTPv3 (cert)'); 113 | } catch (err) { 114 | //console.log('[ ] OkHTTPv3 (cert)'); 115 | } 116 | try { 117 | // Bypass OkHTTPv3 {3} 118 | const okhttp3_Activity_3 = Java.use('okhttp3.CertificatePinner'); 119 | okhttp3_Activity_3.check.overload('java.lang.String', '[Ljava.security.cert.Certificate;').implementation = function (a, b) { 120 | //console.log(' --> Bypassing OkHTTPv3 (cert array): ' + a); 121 | return; 122 | }; 123 | //console.log('[+] OkHTTPv3 (cert array)'); 124 | } catch (err) { 125 | //console.log('[ ] OkHTTPv3 (cert array)'); 126 | } 127 | try { 128 | // Bypass OkHTTPv3 {4} 129 | const okhttp3_Activity_4 = Java.use('okhttp3.CertificatePinner'); 130 | okhttp3_Activity_4['check$okhttp'].implementation = function (a, b) { 131 | //console.log(' --> Bypassing OkHTTPv3 ($okhttp): ' + a); 132 | return; 133 | }; 134 | //console.log('[+] OkHTTPv3 ($okhttp)'); 135 | } catch (err) { 136 | //console.log('[ ] OkHTTPv3 ($okhttp)'); 137 | } 138 | 139 | // Trustkit (triple bypass) 140 | try { 141 | // Bypass Trustkit {1} 142 | const trustkit_Activity_1 = Java.use('com.datatheorem.android.trustkit.pinning.OkHostnameVerifier'); 143 | trustkit_Activity_1.verify.overload('java.lang.String', 'javax.net.ssl.SSLSession').implementation = function (a, b) { 144 | //console.log(' --> Bypassing Trustkit OkHostnameVerifier(SSLSession): ' + a); 145 | return true; 146 | }; 147 | //console.log('[+] Trustkit OkHostnameVerifier(SSLSession)'); 148 | } catch (err) { 149 | //console.log('[ ] Trustkit OkHostnameVerifier(SSLSession)'); 150 | } 151 | try { 152 | // Bypass Trustkit {2} 153 | const trustkit_Activity_2 = Java.use('com.datatheorem.android.trustkit.pinning.OkHostnameVerifier'); 154 | trustkit_Activity_2.verify.overload('java.lang.String', 'java.security.cert.X509Certificate').implementation = function (a, b) { 155 | //console.log(' --> Bypassing Trustkit OkHostnameVerifier(cert): ' + a); 156 | return true; 157 | }; 158 | //console.log('[+] Trustkit OkHostnameVerifier(cert)'); 159 | } catch (err) { 160 | //console.log('[ ] Trustkit OkHostnameVerifier(cert)'); 161 | } 162 | try { 163 | // Bypass Trustkit {3} 164 | const trustkit_PinningTrustManager = Java.use('com.datatheorem.android.trustkit.pinning.PinningTrustManager'); 165 | trustkit_PinningTrustManager.checkServerTrusted.implementation = function () { 166 | //console.log(' --> Bypassing Trustkit PinningTrustManager'); 167 | }; 168 | //console.log('[+] Trustkit PinningTrustManager'); 169 | } catch (err) { 170 | //console.log('[ ] Trustkit PinningTrustManager'); 171 | } 172 | 173 | // Appcelerator Titanium 174 | try { 175 | const appcelerator_PinningTrustManager = Java.use('appcelerator.https.PinningTrustManager'); 176 | appcelerator_PinningTrustManager.checkServerTrusted.implementation = function () { 177 | //console.log(' --> Bypassing Appcelerator PinningTrustManager'); 178 | }; 179 | //console.log('[+] Appcelerator PinningTrustManager'); 180 | } catch (err) { 181 | //console.log('[ ] Appcelerator PinningTrustManager'); 182 | } 183 | 184 | // OpenSSLSocketImpl Conscrypt 185 | try { 186 | const OpenSSLSocketImpl = Java.use('com.android.org.conscrypt.OpenSSLSocketImpl'); 187 | OpenSSLSocketImpl.verifyCertificateChain.implementation = function (certRefs, JavaObject, authMethod) { 188 | //console.log(' --> Bypassing OpenSSLSocketImpl Conscrypt'); 189 | }; 190 | //console.log('[+] OpenSSLSocketImpl Conscrypt'); 191 | } catch (err) { 192 | //console.log('[ ] OpenSSLSocketImpl Conscrypt'); 193 | } 194 | 195 | // OpenSSLEngineSocketImpl Conscrypt 196 | try { 197 | const OpenSSLEngineSocketImpl_Activity = Java.use('com.android.org.conscrypt.OpenSSLEngineSocketImpl'); 198 | OpenSSLEngineSocketImpl_Activity.verifyCertificateChain.overload('[Ljava.lang.Long;', 'java.lang.String').implementation = function (a, b) { 199 | //console.log(' --> Bypassing OpenSSLEngineSocketImpl Conscrypt: ' + b); 200 | }; 201 | //console.log('[+] OpenSSLEngineSocketImpl Conscrypt'); 202 | } catch (err) { 203 | //console.log('[ ] OpenSSLEngineSocketImpl Conscrypt'); 204 | } 205 | 206 | // OpenSSLSocketImpl Apache Harmony 207 | try { 208 | const OpenSSLSocketImpl_Harmony = Java.use('org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl'); 209 | OpenSSLSocketImpl_Harmony.verifyCertificateChain.implementation = function (asn1DerEncodedCertificateChain, authMethod) { 210 | //console.log(' --> Bypassing OpenSSLSocketImpl Apache Harmony'); 211 | }; 212 | //console.log('[+] OpenSSLSocketImpl Apache Harmony'); 213 | } catch (err) { 214 | //console.log('[ ] OpenSSLSocketImpl Apache Harmony'); 215 | } 216 | 217 | // PhoneGap sslCertificateChecker (https://github.com/EddyVerbruggen/SSLCertificateChecker-PhoneGap-Plugin) 218 | try { 219 | const phonegap_Activity = Java.use('nl.xservices.plugins.sslCertificateChecker'); 220 | phonegap_Activity.execute.overload('java.lang.String', 'org.json.JSONArray', 'org.apache.cordova.CallbackContext').implementation = function (a, b, c) { 221 | //console.log(' --> Bypassing PhoneGap sslCertificateChecker: ' + a); 222 | return true; 223 | }; 224 | //console.log('[+] PhoneGap sslCertificateChecker'); 225 | } catch (err) { 226 | //console.log('[ ] PhoneGap sslCertificateChecker'); 227 | } 228 | 229 | // IBM MobileFirst pinTrustedCertificatePublicKey (double bypass) 230 | try { 231 | // Bypass IBM MobileFirst {1} 232 | const WLClient_Activity_1 = Java.use('com.worklight.wlclient.api.WLClient'); 233 | WLClient_Activity_1.getInstance().pinTrustedCertificatePublicKey.overload('java.lang.String').implementation = function (cert) { 234 | //console.log(' --> Bypassing IBM MobileFirst pinTrustedCertificatePublicKey (string): ' + cert); 235 | return; 236 | }; 237 | //console.log('[+] IBM MobileFirst pinTrustedCertificatePublicKey (string)'); 238 | } catch (err) { 239 | //console.log('[ ] IBM MobileFirst pinTrustedCertificatePublicKey (string)'); 240 | } 241 | try { 242 | // Bypass IBM MobileFirst {2} 243 | const WLClient_Activity_2 = Java.use('com.worklight.wlclient.api.WLClient'); 244 | WLClient_Activity_2.getInstance().pinTrustedCertificatePublicKey.overload('[Ljava.lang.String;').implementation = function (cert) { 245 | //console.log(' --> Bypassing IBM MobileFirst pinTrustedCertificatePublicKey (string array): ' + cert); 246 | return; 247 | }; 248 | //console.log('[+] IBM MobileFirst pinTrustedCertificatePublicKey (string array)'); 249 | } catch (err) { 250 | //console.log('[ ] IBM MobileFirst pinTrustedCertificatePublicKey (string array)'); 251 | } 252 | 253 | // IBM WorkLight (ancestor of MobileFirst) HostNameVerifierWithCertificatePinning (quadruple bypass) 254 | try { 255 | // Bypass IBM WorkLight {1} 256 | const worklight_Activity_1 = Java.use('com.worklight.wlclient.certificatepinning.HostNameVerifierWithCertificatePinning'); 257 | worklight_Activity_1.verify.overload('java.lang.String', 'javax.net.ssl.SSLSocket').implementation = function (a, b) { 258 | //console.log(' --> Bypassing IBM WorkLight HostNameVerifierWithCertificatePinning (SSLSocket): ' + a); 259 | return; 260 | }; 261 | //console.log('[+] IBM WorkLight HostNameVerifierWithCertificatePinning (SSLSocket)'); 262 | } catch (err) { 263 | //console.log('[ ] IBM WorkLight HostNameVerifierWithCertificatePinning (SSLSocket)'); 264 | } 265 | try { 266 | // Bypass IBM WorkLight {2} 267 | const worklight_Activity_2 = Java.use('com.worklight.wlclient.certificatepinning.HostNameVerifierWithCertificatePinning'); 268 | worklight_Activity_2.verify.overload('java.lang.String', 'java.security.cert.X509Certificate').implementation = function (a, b) { 269 | //console.log(' --> Bypassing IBM WorkLight HostNameVerifierWithCertificatePinning (cert): ' + a); 270 | return; 271 | }; 272 | //console.log('[+] IBM WorkLight HostNameVerifierWithCertificatePinning (cert)'); 273 | } catch (err) { 274 | //console.log('[ ] IBM WorkLight HostNameVerifierWithCertificatePinning (cert)'); 275 | } 276 | try { 277 | // Bypass IBM WorkLight {3} 278 | const worklight_Activity_3 = Java.use('com.worklight.wlclient.certificatepinning.HostNameVerifierWithCertificatePinning'); 279 | worklight_Activity_3.verify.overload('java.lang.String', '[Ljava.lang.String;', '[Ljava.lang.String;').implementation = function (a, b) { 280 | //console.log(' --> Bypassing IBM WorkLight HostNameVerifierWithCertificatePinning (string string): ' + a); 281 | return; 282 | }; 283 | //console.log('[+] IBM WorkLight HostNameVerifierWithCertificatePinning (string string)'); 284 | } catch (err) { 285 | //console.log('[ ] IBM WorkLight HostNameVerifierWithCertificatePinning (string string)'); 286 | } 287 | try { 288 | // Bypass IBM WorkLight {4} 289 | const worklight_Activity_4 = Java.use('com.worklight.wlclient.certificatepinning.HostNameVerifierWithCertificatePinning'); 290 | worklight_Activity_4.verify.overload('java.lang.String', 'javax.net.ssl.SSLSession').implementation = function (a, b) { 291 | //console.log(' --> Bypassing IBM WorkLight HostNameVerifierWithCertificatePinning (SSLSession): ' + a); 292 | return true; 293 | }; 294 | //console.log('[+] IBM WorkLight HostNameVerifierWithCertificatePinning (SSLSession)'); 295 | } catch (err) { 296 | //console.log('[ ] IBM WorkLight HostNameVerifierWithCertificatePinning (SSLSession)'); 297 | } 298 | 299 | // Conscrypt CertPinManager 300 | try { 301 | const conscrypt_CertPinManager_Activity = Java.use('com.android.org.conscrypt.CertPinManager'); 302 | conscrypt_CertPinManager_Activity.isChainValid.overload('java.lang.String', 'java.util.List').implementation = function (a, b) { 303 | //console.log(' --> Bypassing Conscrypt CertPinManager: ' + a); 304 | return true; 305 | }; 306 | //console.log('[+] Conscrypt CertPinManager'); 307 | } catch (err) { 308 | //console.log('[ ] Conscrypt CertPinManager'); 309 | } 310 | 311 | // CWAC-Netsecurity (unofficial back-port pinner for Android<4.2) CertPinManager 312 | try { 313 | const cwac_CertPinManager_Activity = Java.use('com.commonsware.cwac.netsecurity.conscrypt.CertPinManager'); 314 | cwac_CertPinManager_Activity.isChainValid.overload('java.lang.String', 'java.util.List').implementation = function (a, b) { 315 | //console.log(' --> Bypassing CWAC-Netsecurity CertPinManager: ' + a); 316 | return true; 317 | }; 318 | //console.log('[+] CWAC-Netsecurity CertPinManager'); 319 | } catch (err) { 320 | //console.log('[ ] CWAC-Netsecurity CertPinManager'); 321 | } 322 | 323 | // Worklight Androidgap WLCertificatePinningPlugin 324 | try { 325 | const androidgap_WLCertificatePinningPlugin_Activity = Java.use('com.worklight.androidgap.plugin.WLCertificatePinningPlugin'); 326 | androidgap_WLCertificatePinningPlugin_Activity.execute.overload('java.lang.String', 'org.json.JSONArray', 'org.apache.cordova.CallbackContext').implementation = function (a, b, c) { 327 | //console.log(' --> Bypassing Worklight Androidgap WLCertificatePinningPlugin: ' + a); 328 | return true; 329 | }; 330 | //console.log('[+] Worklight Androidgap WLCertificatePinningPlugin'); 331 | } catch (err) { 332 | //console.log('[ ] Worklight Androidgap WLCertificatePinningPlugin'); 333 | } 334 | 335 | // Netty FingerprintTrustManagerFactory 336 | try { 337 | const netty_FingerprintTrustManagerFactory = Java.use('io.netty.handler.ssl.util.FingerprintTrustManagerFactory'); 338 | netty_FingerprintTrustManagerFactory.checkTrusted.implementation = function (type, chain) { 339 | //console.log(' --> Bypassing Netty FingerprintTrustManagerFactory'); 340 | }; 341 | //console.log('[+] Netty FingerprintTrustManagerFactory'); 342 | } catch (err) { 343 | //console.log('[ ] Netty FingerprintTrustManagerFactory'); 344 | } 345 | 346 | // Squareup CertificatePinner [OkHTTP Bypassing Squareup CertificatePinner (cert): ' + a); 352 | return; 353 | }; 354 | //console.log('[+] Squareup CertificatePinner (cert)'); 355 | } catch (err) { 356 | //console.log('[ ] Squareup CertificatePinner (cert)'); 357 | } 358 | try { 359 | // Bypass Squareup CertificatePinner {2} 360 | const Squareup_CertificatePinner_Activity_2 = Java.use('com.squareup.okhttp.CertificatePinner'); 361 | Squareup_CertificatePinner_Activity_2.check.overload('java.lang.String', 'java.util.List').implementation = function (a, b) { 362 | //console.log(' --> Bypassing Squareup CertificatePinner (list): ' + a); 363 | return; 364 | }; 365 | //console.log('[+] Squareup CertificatePinner (list)'); 366 | } catch (err) { 367 | //console.log('[ ] Squareup CertificatePinner (list)'); 368 | } 369 | 370 | // Squareup OkHostnameVerifier [OkHTTP v3] (double bypass) 371 | try { 372 | // Bypass Squareup OkHostnameVerifier {1} 373 | const Squareup_OkHostnameVerifier_Activity_1 = Java.use('com.squareup.okhttp.internal.tls.OkHostnameVerifier'); 374 | Squareup_OkHostnameVerifier_Activity_1.verify.overload('java.lang.String', 'java.security.cert.X509Certificate').implementation = function (a, b) { 375 | //console.log(' --> Bypassing Squareup OkHostnameVerifier (cert): ' + a); 376 | return true; 377 | }; 378 | //console.log('[+] Squareup OkHostnameVerifier (cert)'); 379 | } catch (err) { 380 | //console.log('[ ] Squareup OkHostnameVerifier (cert)'); 381 | } 382 | try { 383 | // Bypass Squareup OkHostnameVerifier {2} 384 | const Squareup_OkHostnameVerifier_Activity_2 = Java.use('com.squareup.okhttp.internal.tls.OkHostnameVerifier'); 385 | Squareup_OkHostnameVerifier_Activity_2.verify.overload('java.lang.String', 'javax.net.ssl.SSLSession').implementation = function (a, b) { 386 | //console.log(' --> Bypassing Squareup OkHostnameVerifier (SSLSession): ' + a); 387 | return true; 388 | }; 389 | //console.log('[+] Squareup OkHostnameVerifier (SSLSession)'); 390 | } catch (err) { 391 | //console.log('[ ] Squareup OkHostnameVerifier (SSLSession)'); 392 | } 393 | 394 | // Android WebViewClient (double bypass) 395 | try { 396 | // Bypass WebViewClient {1} (deprecated from Android 6) 397 | const AndroidWebViewClient_Activity_1 = Java.use('android.webkit.WebViewClient'); 398 | AndroidWebViewClient_Activity_1.onReceivedSslError.overload('android.webkit.WebView', 'android.webkit.SslErrorHandler', 'android.net.http.SslError').implementation = function (obj1, obj2, obj3) { 399 | //console.log(' --> Bypassing Android WebViewClient (SslErrorHandler)'); 400 | }; 401 | //console.log('[+] Android WebViewClient (SslErrorHandler)'); 402 | } catch (err) { 403 | //console.log('[ ] Android WebViewClient (SslErrorHandler)'); 404 | } 405 | try { 406 | // Bypass WebViewClient {2} 407 | const AndroidWebViewClient_Activity_2 = Java.use('android.webkit.WebViewClient'); 408 | AndroidWebViewClient_Activity_2.onReceivedSslError.overload('android.webkit.WebView', 'android.webkit.WebResourceRequest', 'android.webkit.WebResourceError').implementation = function (obj1, obj2, obj3) { 409 | //console.log(' --> Bypassing Android WebViewClient (WebResourceError)'); 410 | }; 411 | //console.log('[+] Android WebViewClient (WebResourceError)'); 412 | } catch (err) { 413 | //console.log('[ ] Android WebViewClient (WebResourceError)'); 414 | } 415 | 416 | // Apache Cordova WebViewClient 417 | try { 418 | const CordovaWebViewClient_Activity = Java.use('org.apache.cordova.CordovaWebViewClient'); 419 | CordovaWebViewClient_Activity.onReceivedSslError.overload('android.webkit.WebView', 'android.webkit.SslErrorHandler', 'android.net.http.SslError').implementation = function (obj1, obj2, obj3) { 420 | //console.log(' --> Bypassing Apache Cordova WebViewClient'); 421 | obj3.proceed(); 422 | }; 423 | } catch (err) { 424 | //console.log('[ ] Apache Cordova WebViewClient'); 425 | } 426 | 427 | // Boye AbstractVerifier 428 | try { 429 | const boye_AbstractVerifier = Java.use('ch.boye.httpclientandroidlib.conn.ssl.AbstractVerifier'); 430 | boye_AbstractVerifier.verify.implementation = function (host, ssl) { 431 | //console.log(' --> Bypassing Boye AbstractVerifier: ' + host); 432 | }; 433 | } catch (err) { 434 | //console.log('[ ] Boye AbstractVerifier'); 435 | } 436 | }); 437 | 438 | //console.log("Unpinning setup cmopleted"); 439 | //console.log("---"); 440 | 441 | }, 0); -------------------------------------------------------------------------------- /interactor.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | import hashlib 3 | import itertools 4 | import json 5 | import logging 6 | import os 7 | import random 8 | import sqlite3 9 | import sys 10 | import time 11 | from functools import partial 12 | 13 | import translators 14 | 15 | import timeout 16 | 17 | # pm list packages -3 | cut -d':' -f2 | tr '\r' ' ' | grep -v com.github.shadowsocks 18 | # 1- adb reboot recovery 19 | # 2- twrp wipe system ; twrp wipe dalvik ; twrp wipe data ; twrp wipe cache ; rm -rf /sdcard/* 20 | # 3- adb push TWRP /sdcard/ 21 | # 4- twrp restore clean 22 | # 5- rm -rf /sdcard/TWRP 23 | # adb shell dumpsys window | grep com.android.vending 24 | # adb shell dumpsys activity activities | grep mResumedActivity 25 | 26 | # pm list packages -3 | cut -c9- | grep -Ev "(com.github.shadowsocks|org.proxydroid|com.fakemygps.android|org.meowcat.edxposed.manager|edu.berkeley.icsi.haystack|com.topjohnwu.magisk|app.greyshirts.sslcapture|tw.fatminmin.xposed.minminguard|com.cofface.ivader)" | xargs pm uninstall 27 | 28 | 29 | def dictionary(text): 30 | en = text 31 | if len(text) > 2: 32 | con = sqlite3.connect('dict.db') 33 | cur = con.cursor() 34 | cur.execute( 35 | '''CREATE TABLE IF NOT EXISTS words (ID INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, original text, en text)''') 36 | cur.execute("select en from words where original = ?", (text,)) 37 | row = cur.fetchone() 38 | 39 | if row == None: 40 | i = 0 41 | while i < 10: 42 | # print("zzz") 43 | # print(i) 44 | try: 45 | with timeout.timeout(seconds=10): 46 | en = translators.google(text) 47 | break 48 | except: 49 | i += 1 50 | time.sleep(2) 51 | continue 52 | logging.debug("dict") 53 | cur.execute("insert into words values (?,?,?)", 54 | (None, text, en)) 55 | con.commit() 56 | else: 57 | en = row[0] 58 | con.close() 59 | return en 60 | 61 | 62 | def find_clickable_enable(view, text="", click=False, translate=True, exclude=["None"], obj_class=["android.widget.checkedtextview", "android.view.view", "android.widget.button", "android.widget.textview","android.widget.LinearLayout"], attr="text"): 63 | if (view.isClickable() or view.__getattr__('isEnabled')() or view.__getattr__('checkable')()) and view.getClass().lower() in obj_class: 64 | if attr.lower() == "text": 65 | # obj_text = (view.getText()).lower() 66 | obj_text = dictionary(view.getText()).lower( 67 | ) if translate else view.getText().lower() 68 | elif attr.lower() == "id": 69 | obj_text = view.getId().lower() 70 | if "/" in obj_text: 71 | obj_text = obj_text.split("/")[1] 72 | elif attr.lower() in ("cd","content-desc"): 73 | # obj_text = (view.getContentDescription()).lower() 74 | obj_text = dictionary(view.getContentDescription()).lower( 75 | ) if translate else view.getContentDescription().lower() 76 | else: 77 | return None 78 | text = text.lower() 79 | if (text == obj_text and exclude == ['None']) or (text in obj_text and len(obj_text) < 64 and (not any(substring in obj_text for substring in exclude) and exclude != ['None'])): 80 | if click: 81 | logging.debug("::"+text) 82 | view.touch() 83 | return True 84 | 85 | 86 | def window_hash(root): 87 | id_list = [v.getId() 88 | for v in finder(root, lambda v: True if v.getId() else False)] 89 | id_list.sort() 90 | return hashlib.sha256(json.dumps(id_list).encode('utf-8')).hexdigest() 91 | 92 | 93 | def problem(view): 94 | if view.getText().startswith("Can't download"): 95 | # "You're offline" 96 | return True 97 | 98 | 99 | def finder(root, transform=str, nlist=None, count=[-1], window_hash="", memory=None): 100 | if memory is None: 101 | memory = dict() 102 | if nlist is None: 103 | nlist = [] 104 | if not root or count[0] == 0 or (memory.get(window_hash) != None and (root.getUniqueId()+root.getId()+str(root.isClickable())+str(root.__getattr__('isEnabled')())) in memory.get(window_hash)): 105 | return 106 | if transform(root) and (window_hash == "" or memory.get(window_hash) == None or (root.getUniqueId()+root.getId()+str(root.isClickable())+str(root.__getattr__('isEnabled')())) not in memory.get(window_hash)): 107 | if count[0] != -1: 108 | count[0] -= 1 109 | nlist.append(root) 110 | if window_hash != "": 111 | if window_hash in memory: 112 | memory.get(window_hash).add(root.getUniqueId( 113 | )+root.getId()+str(root.isClickable())+str(root.__getattr__('isEnabled')())) 114 | # memory.update( 115 | # {window_hash: {root.getUniqueId()+root.getId()}}) 116 | else: 117 | memory.update( 118 | {window_hash: {root.getUniqueId()+root.getId()+str(root.isClickable())+str(root.__getattr__('isEnabled')())}}) 119 | 120 | for ch in root.children: 121 | finder(ch, transform=transform, nlist=nlist, count=count, 122 | window_hash=window_hash, memory=memory) 123 | return nlist 124 | 125 | 126 | def get_root(device, window=-1, sleep=0.5): 127 | for chanse in range(0, 10): 128 | try: 129 | for n in device.vc.dump(window=window, sleep=sleep): 130 | if n.getParent() == None: 131 | return n 132 | except Exception as e: 133 | if chanse < 3: 134 | time.sleep(2) 135 | continue 136 | raise e 137 | 138 | 139 | class GooglePlay(): 140 | def __init__(self, device): 141 | self.device = device 142 | 143 | def install(self, package): 144 | self.device.close_app("com.android.vending") 145 | self.device.wait_if_internet_isnt_available() 146 | self.open_package_page(package) 147 | # time.sleep(3) 148 | name = self.get_package_name() 149 | root = get_root(self.device) 150 | if None in (self.is_package_installable(package), self.get_package_name()) and finder(root, transform=partial( 151 | find_clickable_enable, text="Understood", click=True, translate=False)) == []: 152 | logging.debug(str(name) + " " + package) 153 | return None 154 | try: 155 | with timeout.timeout(seconds=200): 156 | uninstallable = 0 157 | while True: 158 | if os.path.exists("./skip"): 159 | os.remove("./skip") 160 | return None 161 | # self.device.close_app("com.android.chrome") 162 | # self.device.close_app("org.lineageos.jelly") 163 | logging.debug("4444") 164 | self.device.wait_if_internet_isnt_available() 165 | self.device.d.wake() 166 | # time.sleep(2) 167 | root = get_root(self.device) 168 | if self.is_gp_open(root=root) == False or name != self.get_package_name(): 169 | self.open_package_page(package) 170 | 171 | if finder(root, transform=partial( 172 | find_clickable_enable, text="Open", translate=False)) or finder(root, transform=partial( 173 | find_clickable_enable, text="Enable", translate=False)): 174 | return True 175 | 176 | if finder(root, transform=partial( 177 | find_clickable_enable, text="Play", translate=False)): 178 | return True 179 | 180 | if finder(root, transform=partial( 181 | find_clickable_enable, text="Uninstall", translate=False)): 182 | if uninstallable >= 2: 183 | return None 184 | uninstallable += 1 185 | continue 186 | 187 | if finder(root, transform=partial( 188 | find_clickable_enable, text="Install", click=True, translate=False)) or finder(root, transform=partial( 189 | find_clickable_enable, text="Try again", click=True, translate=False)) or finder(root, transform=partial( 190 | find_clickable_enable, text="Retry", click=True, translate=False)) or finder(root, transform=partial( 191 | find_clickable_enable, text="Accept", click=True, translate=False)) or finder(root, transform=partial( 192 | find_clickable_enable, text="Update", click=True, translate=False)) or finder(root, transform=partial( 193 | find_clickable_enable, text="Skip", click=True, translate=False)) or finder(root, transform=partial( 194 | find_clickable_enable, text="Accept", click=True, translate=False)) or finder(root, transform=partial( 195 | find_clickable_enable, text="No thanks", click=True, translate=False)) or finder(root, transform=partial( 196 | find_clickable_enable, text="Continue", click=True, translate=False)) or finder(root, transform=partial( 197 | find_clickable_enable, text="Ok", click=True, translate=False)): 198 | logging.debug("5555") 199 | continue 200 | if finder(root, transform=partial( 201 | find_clickable_enable, text="Got it", click=True, translate=False)) or finder(root, transform=partial( 202 | find_clickable_enable, text="Understood", click=True, translate=False)): 203 | return None 204 | except: 205 | # return None 206 | self.device.d.wake() 207 | root = get_root(self.device) 208 | if self.is_gp_open(root=root) == False or name != self.get_package_name(): 209 | self.open_package_page(package) 210 | if finder(root, transform=partial(find_clickable_enable, text="Cancel", click=True, translate=False)) or finder(root, transform=partial(find_clickable_enable, text="Uninstall", click=True, translate=False)): 211 | return None 212 | else: 213 | time.sleep(2) 214 | self.device.uninstall_app(package) 215 | return None 216 | 217 | def is_package_installable(self, package): 218 | if self.is_gp_open(): 219 | items = [c.getText() for i in self.device.vc.dump() if i.getClass() == "android.view.ViewGroup" and i.getParent( 220 | ).getClass() != "android.widget.LinearLayout" for c in i.getChildren()] 221 | if "Install" in items or "Update" in items or "Open" in items: 222 | return True 223 | 224 | def is_package_installed(self, package, name): 225 | if self.is_gp_open() or name == self.get_package_name(): 226 | if self.device.uiautomator( 227 | text="Open", 228 | className="android.widget.Button", 229 | clickable="true", 230 | enabled="true", 231 | ).exists: 232 | return True 233 | return False 234 | 235 | def is_gp_open(self, root='root'): 236 | if root == 'root': 237 | root = get_root(self.device) 238 | # google loading page hash 239 | while window_hash(root) == "bcdec05b796550fb1c36544f80af3d15dec9c4d4bcede57fe9187ab65ce632be": 240 | root = get_root(self.device) 241 | if self.device.get_current_activity() and self.device.get_current_activity().startswith("com.android.vending") and (not finder(root, transform=problem)): 242 | return True 243 | return False 244 | 245 | def get_package_name(self): 246 | if self.is_gp_open(): 247 | _try = 0 248 | while _try < 2: 249 | while True: 250 | root = get_root(self.device) 251 | if (finder(root, transform=partial( 252 | find_clickable_enable, text="Retry", click=True, translate=False)) or finder(root, transform=partial( 253 | find_clickable_enable, text="Understood", click=True, translate=False)) or finder(root, transform=partial( 254 | find_clickable_enable, text="Skip", click=True, translate=False)) or finder(root, transform=partial( 255 | find_clickable_enable, text="Accept", click=True, translate=False)) or finder(root, transform=partial( 256 | find_clickable_enable, text="No, thanks", click=True, translate=False)) or finder(root, transform=partial( 257 | find_clickable_enable, text="Continue", click=True, translate=False)) or finder(root, transform=partial( 258 | find_clickable_enable, text="Got it", click=True, translate=False))) == []: 259 | logging.debug("7777") 260 | break 261 | try: 262 | return [i.getText() for i in self.device.vc.dump() if i.getClass() == "android.widget.TextView" and i.getParent().getClass() == "android.widget.LinearLayout"][0] 263 | except: 264 | _try += 1 265 | self.device.d.shell('input keyevent KEYCODE_BACK') 266 | # if not re.match(r'^(2[0-3]|[01]?[0-9]):([0-5]?[0-9])$', name): 267 | 268 | def open_package_page(self, package): 269 | self.device.d.wake() 270 | self.device.shell( 271 | "am start -a android.intent.action.VIEW -d 'market://details?id=" + package + "'") 272 | for _ in range(0, 5): 273 | time.sleep(0.5) 274 | if self.is_gp_open() == True: 275 | return True 276 | return False 277 | 278 | 279 | # def remove_space(words): 280 | # nd = dict() 281 | # for key, value in words.items(): 282 | # if ' ' in key: 283 | # nd[key.replace(' ', '')] = list(w.replace(' ', '') 284 | # if ' ' in w else w for w in value) 285 | # nd[key.replace(' ', '-')] = list(w.replace(' ', '-') 286 | # if ' ' in w else w for w in value) 287 | # nd[key.replace(' ', '_')] = list(w.replace(' ', '_') 288 | # if ' ' in w else w for w in value) 289 | # else: 290 | # nd[key] = list(itertools.chain.from_iterable([w.replace(' ', ''), w.replace( 291 | # ' ', '_'), w.replace(' ', '-')] if ' ' in w else [w] for w in value)) 292 | # return nd 293 | 294 | 295 | class App: 296 | def __init__(self, device, package, stage, analysis_time, param_path="./interactor_parameters.json"): 297 | self.d = device 298 | self.p = package 299 | self.t = analysis_time 300 | self.stage = str(stage) 301 | self.memory = dict() 302 | with open(param_path, 'r') as file: 303 | self.params = json.loads(file.read()) 304 | 305 | def get_params(self, text=True): 306 | if text: 307 | return self.params 308 | else: 309 | res = dict() 310 | for cat, val in self.params.items(): 311 | nd = dict() 312 | for key, value in val.items(): 313 | 314 | if ' ' in key: 315 | nd[key.replace(' ', '')] = value if type(value) is not list else list(w.replace(' ', '') 316 | if ' ' in w else w for w in value) 317 | nd[key.replace(' ', '-')] = value if type(value) is not list else list(w.replace(' ', '-') 318 | if ' ' in w else w for w in value) 319 | nd[key.replace(' ', '_')] = value if type(value) is not list else list(w.replace(' ', '_') 320 | if ' ' in w else w for w in value) 321 | else: 322 | nd[key] = value if type(value) is not list else list(itertools.chain.from_iterable([w.replace(' ', ''), w.replace( 323 | ' ', '_'), w.replace(' ', '-')] if ' ' in w else [w] for w in value)) 324 | res[cat] = nd 325 | return res 326 | 327 | def find_and_click_by_text(self, root, obj_class): 328 | w_hash = window_hash(root) 329 | # print("----------") 330 | current_activity = self.d.get_current_activity() 331 | if current_activity in ["com.google.android.gms/.signin.activity.ConsentActivity", "com.google.android.gms/.auth.uiflows.consent.BrowserConsentActivity"]: 332 | if len(finder(root, transform=partial(find_clickable_enable, text="allow", click=True)) + finder(root, transform=partial(find_clickable_enable, text="continue", click=True))) == 0: 333 | w = self.d.d.display['width'] 334 | h = self.d.d.display['height'] 335 | s = (w / 2, (h / 3) * 2) 336 | e = (w / 2, (h / 3)) 337 | self.d.d.drag(s, e, 500, 20, -1) 338 | self.d.d.drag(s, e, 500, 20, -1) 339 | # google found 340 | return True 341 | # time.sleep(1) 342 | elif current_activity in ["com.android.vending/com.google.android.finsky.activities.MarketDeepLinkHandlerActivity", "com.android.vending/com.google.android.finsky.billing.acquire.LockToPortraitUiBuilderHostActivity", "com.android.vending/com.google.android.finsky.billing.acquire.SheetUiBuilderHostActivity"] or current_activity.endswith("/com.google.android.gms.ads.AdActivity") or current_activity.endswith("/com.unity3d.services.ads.adunit.AdUnitActivity") or current_activity.endswith("/com.unity3d.ads.adunit.AdUnitActivity"): 343 | time.sleep(1.5) 344 | # com.android.vending/com.google.android.finsky.billing.acquire.SheetUiBuilderHostActivity 345 | print("////////////////") 346 | self.d.shell('input keyevent KEYCODE_BACK') 347 | return True 348 | for include, exclude in self.get_params()["keywords"].items(): 349 | for node in finder(root, transform=partial( 350 | find_clickable_enable, text=include, exclude=exclude, obj_class=obj_class, attr="CD"), count=[1], window_hash=w_hash, memory=self.memory): 351 | logging.debug("id12:"+include) 352 | node.touch() 353 | return True 354 | for include, exclude in self.get_params(text=False)["keywords"].items(): 355 | for node in finder(root, transform=partial( 356 | find_clickable_enable, text=include, exclude=exclude, obj_class=obj_class, attr="Id"), count=[1], window_hash=w_hash, memory=self.memory): 357 | logging.debug("id13:"+include) 358 | node.touch() 359 | return True 360 | for include, exclude in self.get_params()["keywords"].items(): 361 | for node in finder(root, transform=partial( 362 | find_clickable_enable, text=include, exclude=exclude, obj_class=obj_class), count=[1], window_hash=w_hash, memory=self.memory): 363 | node.touch() 364 | time.sleep(0.5) 365 | return True 366 | # print("----------1-2") 367 | # for include, exclude in self.get_params()["avoid"].items(): 368 | # for node in finder(root, transform=partial( 369 | # find_clickable_enable, text=include, exclude=exclude, obj_class=obj_class), count=[1], window_hash=w_hash, memory=self.memory): 370 | # # com.android.vending/com.google.android.finsky.activities.MarketDeepLinkHandlerActivity install 371 | # # */com.google.android.gms.ads.AdActivity 372 | # # com.android.vending/com.google.android.finsky.billing.acquire.LockToPortraitUiBuilderHostActivity 373 | # logging.debug("id-----------------------------:"+node.getText()) 374 | # logging.debug("id11:"+include) 375 | # print("--+++++++++++--"+self.d.get_current_activity()) 376 | # node.touch() 377 | # return True 378 | time.sleep(1) 379 | return False 380 | 381 | def find_and_scroll(self, root): 382 | pass 383 | 384 | def find_input_and_fill(self, root, obj_class): 385 | w_hash = window_hash(root) 386 | for key, value in self.get_params()["input"].items(): 387 | for node in finder(root, transform=partial( 388 | find_clickable_enable, text=key, exclude=[], obj_class=obj_class), count=[1], window_hash=w_hash, memory=self.memory): 389 | logging.debug("id1:"+node.getId()) 390 | node.setText(value) 391 | return True 392 | for key, value in self.get_params(text=False)["input"].items(): 393 | for node in finder(root, transform=partial( 394 | find_clickable_enable, text=key, exclude=[], obj_class=obj_class, attr="Id"), count=[1], window_hash=w_hash, memory=self.memory): 395 | logging.debug("id2:"+node.getId()) 396 | logging.debug("v1:"+value) 397 | node.setText(value) 398 | return True 399 | # for node in finder(root, transform=partial( 400 | # find_clickable_enable, text=key, obj_class=obj_class), count=[1], window_hash=w_hash, memory=self.memory): 401 | # node.setText(value) 402 | # return True 403 | return False 404 | 405 | def scroll_down(self, w, h): 406 | w = self.d.d.display['width'] 407 | h = self.d.d.display['height'] 408 | self.d.shell("input swipe {} {} {} {} 100".format( 409 | w / 2, (h / 3) * 2, w / 2, h / 3)) 410 | 411 | def scroll_up(self, w, h): 412 | w = self.d.d.display['width'] 413 | h = self.d.d.display['height'] 414 | self.d.shell("input swipe {} {} {} {} 100".format( 415 | w / 2, h / 3, w / 2, (h / 3) * 2)) 416 | 417 | def scroll_right(self, w, h): 418 | self.d.shell("input swipe {} {} {} {} 100".format( 419 | w / 5, h / 2, (w / 5 * 4), h / 2, 500, 20)) 420 | 421 | def dumb_interaction(self, deep=False): 422 | click_command = "" 423 | w = int(self.d.display.y) 424 | h = int(self.d.display.x - self.d.display.statusbar) 425 | if bool(random.getrandbits(1)): 426 | self.scroll_up(w, h) 427 | else: 428 | self.scroll_down(w, h) 429 | self.scroll_right(w, h) 430 | if deep: 431 | base = (10, 5) 432 | else: 433 | base = (6, 3) 434 | for y in reversed(range(int(h/base[0]), h, int(h/base[0]))): 435 | for x in range(int(w/base[1]), w, int(w/base[1])): 436 | click_command += "input tap {} {};".format(x, y) 437 | self.d.shell(click_command) 438 | 439 | def smart(self): 440 | futile = -1 441 | back_key = 2 442 | app_closed = 0 443 | time.sleep(8) 444 | start = int(time.time()) 445 | for _ in range(0, 100): 446 | if os.path.exists("./skip"): 447 | os.remove("./skip") 448 | break 449 | if int(time.time()) - start > 300: # 300 450 | break 451 | if self.d.is_app_crashed(self.p): 452 | with open("out/"+self.p+"/crash-"+self.stage+"-"+str(int(time.time()))+".txt", "a") as f: 453 | f.write(self.d.shell('logcat -d *:E -t \''+datetime.datetime.fromtimestamp( 454 | self.t).strftime('%m-%d %H:%M:%S.0')+'\'|base64')+"\n") 455 | self.d.shell('input keyevent KEYCODE_BACK') 456 | if self.d.is_app_hangs(self.p): 457 | with open("out/"+self.p+"/hang-"+self.stage+"-"+str(int(time.time()))+".txt", "a") as f: 458 | f.write(str(int(time.time()))+"\n") 459 | self.d.shell('input keyevent KEYCODE_BACK') 460 | 461 | if self.d.frida.is_lost != 0: 462 | os.system('kill -9 {pid}'.format(pid=os.getpid())) 463 | 464 | self.d.d.shell( 465 | "am broadcast -n com.research.helper/.SendGPS -e lat 45.4950378 -e lon -73.5779508 -e accurate 0.5 -e alt 5") 466 | 467 | try: 468 | # print("sleep 10") 469 | # time.sleep(10) 470 | 471 | if not self.d.is_app_open(self.p): 472 | if app_closed >= 3: 473 | break 474 | app_closed += 1 475 | self.d.run_app(self.p, close=False) 476 | # print("sleep 10.1") 477 | # time.sleep(10) 478 | 479 | memory_snapshot = len(str(self.memory)) 480 | self.d.wait_if_internet_isnt_available() 481 | self.d.d.wake() 482 | 483 | root = get_root(self.d) 484 | print("**---++----") 485 | for _ in range(0, 10): 486 | self.find_input_and_fill( 487 | root, obj_class=["android.widget.edittext"]) 488 | 489 | for _ in range(0, 10): 490 | if finder(root, transform=partial( 491 | find_clickable_enable, text="9", obj_class=["android.widget.button"], click=True), count=[1], window_hash=window_hash(root)): 492 | root = get_root(self.d) 493 | else: 494 | break 495 | windowhash = window_hash(root) 496 | 497 | if not self.d.is_app_open(self.p): 498 | if app_closed >= 3: 499 | break 500 | app_closed += 1 501 | self.d.run_app(self.p, close=False) 502 | continue 503 | 504 | fct = self.find_and_click_by_text( 505 | root, obj_class=["android.widget.checkedtextview", "android.view.view", "android.widget.button", "android.widget.textview", "android.widget.imageview", "android.widget.imagebutton","android.widget.LinearLayout"]) 506 | logging.debug("mem:"+str(self.memory)) 507 | if fct == False: 508 | if windowhash == get_root(self.d): 509 | self.d.shell('input keyevent KEYCODE_BACK') 510 | if memory_snapshot == len(str(self.memory)): 511 | logging.debug("futile:"+str(futile)) 512 | if futile >= 4: 513 | if back_key <= 0: 514 | break 515 | futile -= 1 516 | self.d.shell('input keyevent KEYCODE_BACK') 517 | time.sleep(1) 518 | if not self.d.is_app_open(self.p): 519 | break 520 | back_key -= 1 521 | elif futile == 3: 522 | self.dumb_interaction() 523 | futile += 1 524 | time.sleep(abs(futile)/2) 525 | logging.debug("xxxxxx") 526 | else: 527 | logging.debug("aaaaaa") 528 | back_key = 3 529 | futile = 1 530 | except ValueError as ee: 531 | logging.debug("xsxs") 532 | logging.debug(ee) 533 | self.dumb_interaction() 534 | if back_key <= 0: 535 | break 536 | 537 | self.d.shell('input keyevent KEYCODE_BACK') 538 | back_key -= 1 539 | except RuntimeError: 540 | self.dumb_interaction(deep=True) 541 | # self.d.shell('input keyevent KEYCODE_BACK') 542 | # logging.debug(self.memory) 543 | # time.sleep(20) 544 | # finder(root, transform=partial(find_button_click, text="Update")) 545 | # logging.debug(self.d.vc.dump()) 546 | 547 | def find_similar_button(self): 548 | pass 549 | 550 | def dump(self): 551 | pass 552 | --------------------------------------------------------------------------------