├── .gitignore ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── assets │ └── privacy_methods.json │ ├── java │ └── com │ │ └── lanshifu │ │ └── privacymethodhooker │ │ ├── MainActivity.kt │ │ ├── test │ │ └── JaveTest.java │ │ ├── testcase │ │ └── TopLevelUtil.kt │ │ └── utils │ │ ├── PrivacyUtil.kt │ │ ├── ReflectUtils.kt │ │ └── Test.java │ └── res │ ├── drawable-v24 │ └── ic_launcher_foreground.xml │ ├── drawable │ └── ic_launcher_background.xml │ ├── layout │ ├── activity_main.xml │ ├── content_main.xml │ ├── fragment_first.xml │ └── fragment_second.xml │ ├── menu │ └── menu_main.xml │ ├── mipmap-anydpi-v26 │ ├── ic_launcher.xml │ └── ic_launcher_round.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 │ ├── navigation │ └── nav_graph.xml │ ├── values-land │ └── dimens.xml │ ├── values-night │ └── themes.xml │ ├── values-w1240dp │ └── dimens.xml │ ├── values-w600dp │ └── dimens.xml │ └── values │ ├── colors.xml │ ├── dimens.xml │ ├── strings.xml │ └── themes.xml ├── asm_annotation ├── .gitignore ├── build.gradle ├── consumer-rules.pro ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ └── java │ └── com │ └── lanshifu │ └── asm_annotation │ ├── AsmMethodOpcodes.kt │ └── AsmMethodReplace.java ├── build.gradle ├── buildSrc ├── build.gradle └── src │ └── main │ ├── kotlin │ └── com │ │ └── lanshifu │ │ └── plugin │ │ ├── DelegateTransformInvocation.kt │ │ ├── Plugin.kt │ │ ├── asmtransformer │ │ └── BaseAsmTransformer.kt │ │ ├── classtransformer │ │ ├── AbsClassTransformer.kt │ │ ├── AnnotationParserClassTransform.kt │ │ └── PrivacyMethodReplaceTransform.kt │ │ ├── extension │ │ └── CommonExt.kt │ │ ├── privacymethod │ │ └── AsmItem.kt │ │ └── transform │ │ ├── BaseTransform.kt │ │ ├── CommonTransform.kt │ │ └── PrivacyMethodHookTransform.kt │ └── resources │ └── META-INF │ └── gradle-plugins │ └── com.lanshifu.privacy_method.properties ├── config.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .idea/ 3 | .gradle 4 | /local.properties 5 | /.idea/caches 6 | /.idea/libraries 7 | /.idea/modules.xml 8 | /.idea/workspace.xml 9 | /.idea/navEditor.xml 10 | /.idea/assetWizardSettings.xml 11 | .DS_Store 12 | /build 13 | /captures 14 | .externalNativeBuild 15 | .cxx 16 | local.properties 17 | 18 | # Project exclude paths 19 | /buildSrc/build/ 20 | /buildSrc/build/classes/java/main/ 21 | /buildSrc/build/classes/java/test/ 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PrivacyMethodHooker 2 | 关联文章: 3 | [ASM hook隐私方法调用,防止App被下架](https://juejin.cn/post/7043399520486424612#heading-0) 4 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | apply plugin: 'kotlin-android' 3 | 4 | apply plugin: 'kotlin-android-extensions' 5 | apply plugin: 'kotlin-kapt' 6 | 7 | apply plugin: 'com.lanshifu.privacy_method' 8 | 9 | android { 10 | compileSdkVersion rootProject.ext.version["compileSdkVersion"] 11 | defaultConfig { 12 | applicationId "com.lanshifu.privacymethodhooker" 13 | minSdkVersion rootProject.ext.version["minSdkVersion"] 14 | targetSdkVersion rootProject.ext.version["targetSdkVersion"] 15 | versionCode rootProject.ext.version["versionCode"] 16 | versionName rootProject.ext.version["versionName"] 17 | multiDexEnabled true 18 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 19 | } 20 | 21 | sourceSets { 22 | main { 23 | jniLibs.srcDirs = ['libs'] 24 | } 25 | } 26 | 27 | buildTypes { 28 | release { 29 | minifyEnabled false 30 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 31 | } 32 | } 33 | 34 | buildFeatures { 35 | dataBinding = true 36 | } 37 | 38 | compileOptions { 39 | sourceCompatibility JavaVersion.VERSION_11 40 | targetCompatibility JavaVersion.VERSION_11 41 | } 42 | 43 | kotlinOptions { 44 | jvmTarget = '11' 45 | } 46 | } 47 | 48 | dependencies { 49 | implementation 'androidx.core:core-ktx:1.8.0' 50 | implementation 'androidx.appcompat:appcompat:1.5.0' 51 | implementation 'com.google.android.material:material:1.6.1' 52 | implementation 'androidx.constraintlayout:constraintlayout:2.1.4' 53 | implementation 'androidx.navigation:navigation-fragment-ktx:2.5.1' 54 | implementation 'androidx.navigation:navigation-ui-ktx:2.5.1' 55 | testImplementation 'junit:junit:4.13.2' 56 | androidTestImplementation 'androidx.test.ext:junit:1.1.3' 57 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' 58 | implementation project(path: ':asm_annotation') 59 | } 60 | -------------------------------------------------------------------------------- /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 -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 20 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /app/src/main/assets/privacy_methods.json: -------------------------------------------------------------------------------- 1 | { 2 | "methods": [ 3 | { 4 | "name_regex": "android.app.ActivityManager.getRunningAppProcesses", 5 | "message": "读取当前运行应用进程" 6 | }, 7 | { 8 | "name_regex": "android.telephony.TelephonyManager.listen", 9 | "message": "监听呼入电话信息" 10 | }, 11 | { 12 | "name_regex": "android.app.ActivityManager.getRecentTasks", 13 | "message": "读取最近运行任务" 14 | }, 15 | { 16 | "name_regex": "android.app.ActivityManager.getRunningTasks", 17 | "message": "读取当前运行任务" 18 | }, 19 | { 20 | "name_regex": "android.telephony.TelephonyManager.getAllCellInfo", 21 | "message": "读取基站信息" 22 | }, 23 | { 24 | "name_regex": "android.location.LocationManager.getLastKnownLocation", 25 | "message": "读取精细位置信息" 26 | }, 27 | { 28 | "name_regex": "android.location.LocationManager.getLastLocation", 29 | "message": "读取粗略位置信息" 30 | }, 31 | { 32 | "name_regex": "android.location.LocationManager.requestLocationUpdates", 33 | "message": "监视精细行动轨迹" 34 | }, 35 | { 36 | "name_regex": "android.media.AudioRecord.native_start", 37 | "message": "音频录制" 38 | }, 39 | { 40 | "name_regex": "android.telephony.TelephonyManager.getDeviceId", 41 | "message": "读取DEVICEID" 42 | }, 43 | { 44 | "name_regex": "android.net.wifi.WifiInfo.getSSID", 45 | "message": "读取WIFI的SSID" 46 | }, 47 | { 48 | "name_regex": "android.net.wifi.WifiInfo.getBSSID", 49 | "message": "读取WIFI的BSSID" 50 | }, 51 | { 52 | "name_regex": "android.hardware.SensorManager.getSensorList", 53 | "message": "读取传感器列表" 54 | }, 55 | { 56 | "name_regex": "android.hardware.SensorManager.registerListenerImpl", 57 | "message": "监视传感器" 58 | }, 59 | { 60 | "name_regex": "android.net.wifi.WifiManager.getConfiguredNetworks", 61 | "message": "读取已配置WIFI信息" 62 | }, 63 | { 64 | "name_regex": "android.telephony.TelephonyManager.getSubscriberId", 65 | "message": "读取IMSI" 66 | }, 67 | { 68 | "name_regex": "android.telephony.TelephonyManager.getSimSerialNumber", 69 | "message": "读取ICCID" 70 | }, 71 | { 72 | "name_regex": "android.net.wifi.WifiInfo.getMacAddress", 73 | "message": "读取MAC地址" 74 | }, 75 | { 76 | "name_regex": "android.telephony.TelephonyManager.getImei", 77 | "message": "读取IMEI" 78 | }, 79 | { 80 | "name_regex": "android.net.wifi.WifiManager.getScanResults", 81 | "message": "读取WIFI扫描结果" 82 | }, 83 | { 84 | "name_regex": "android.net.wifi.WifiManager.getDhcpInfo", 85 | "message": "读取DHCP信息" 86 | }, 87 | { 88 | "name_regex": "android.app.AlarmManager.setImpl", 89 | "message": "定时启动->AlarmManager.setImpl" 90 | }, 91 | { 92 | "name_regex": "android.provider.Settings$Secure.getStringForUser", 93 | "message": "读取AndroidID" 94 | }, 95 | { 96 | "name_regex": "android.content.ContentResolver.registerContentObserver", 97 | "message": "registerContentObserver ->监视多媒体文件" 98 | } 99 | ], 100 | "constructions": [ 101 | { 102 | "name_regex": "", 103 | "message": "" 104 | } 105 | ] 106 | } 107 | -------------------------------------------------------------------------------- /app/src/main/java/com/lanshifu/privacymethodhooker/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.lanshifu.privacymethodhooker 2 | 3 | import android.os.Build 4 | import android.os.Bundle 5 | import android.provider.Settings 6 | import androidx.annotation.RequiresApi 7 | import androidx.appcompat.app.AppCompatActivity 8 | import com.lanshifu.privacymethodhooker.testcase.* 9 | import com.lanshifu.privacymethodhooker.utils.PrivacyUtil 10 | import kotlinx.android.synthetic.main.activity_main.* 11 | 12 | class MainActivity : AppCompatActivity() { 13 | 14 | @RequiresApi(Build.VERSION_CODES.M) 15 | override fun onCreate(savedInstanceState: Bundle?) { 16 | super.onCreate(savedInstanceState) 17 | 18 | setContentView(R.layout.activity_main) 19 | 20 | cbAgree.setOnCheckedChangeListener { compoundButton, b -> 21 | PrivacyUtil.isAgreePrivacy = b 22 | updateData() 23 | } 24 | 25 | cbUseCache.setOnCheckedChangeListener { compoundButton, b -> 26 | PrivacyUtil.isUseCache = b 27 | updateData() 28 | } 29 | 30 | updateData() 31 | 32 | } 33 | 34 | @RequiresApi(Build.VERSION_CODES.M) 35 | private fun updateData() { 36 | val getRunningAppProcesses = getRunningAppProcesses(this) 37 | btnGetRunningAppProcesses.text = 38 | "getRunningAppProcesses size=${getRunningAppProcesses?.size}" 39 | 40 | val getRecentTasks = getRecentTasks(this) 41 | btnGetRecentTasks.text = ("getRecentTasks size=${getRecentTasks?.size}") 42 | 43 | val getRunningTasks = getRunningTasks(this) 44 | btnGetRunningTasks.text = ("getRunningTasks size=${getRunningTasks?.size}") 45 | 46 | val getAllCellInfo = getAllCellInfo(this) 47 | btnGetAllCellInfo.text = ("getAllCellInfo size=${getAllCellInfo?.size}") 48 | 49 | val getDeviceId = getDeviceId(this) 50 | btnGetDeviceId.text = ("getDeviceId=$getDeviceId") 51 | 52 | getSimSerialNumber.text = ("getSimSerialNumber=${getSimSerialNumber(this)}") 53 | 54 | val androidId = Settings.System.getString(this.contentResolver, Settings.Secure.ANDROID_ID) 55 | btnGetIdAndroid.text = ("androidId=$androidId") 56 | 57 | getSSID.text = ("getSSID=${getSSID(this)}") 58 | getBSSID.text = ("getBSSID=${getBSSID(this)}") 59 | getMacAddress.text = ("getMacAddress=${getMacAddress(this)}") 60 | getConfiguredNetworks.text = ("getConfiguredNetworks,size=${getConfiguredNetworks(this)?.size}") 61 | 62 | getSensorList.text = ("getSensorList size=${getSensorList(this)?.size}") 63 | getImei.text = ("getImei=${getImei(this)}") 64 | 65 | getScanResults.text = "getScanResults size=${getScanResults(this)?.size}" 66 | getDhcpInfo.text = "getDhcpInfo=${getDhcpInfo(this)}" 67 | 68 | getLastKnownLocation.text = "getLastKnownLocation=${getLastKnownLocation(this)}" 69 | 70 | requestLocationUpdates(this) 71 | requestLocationUpdates.text = "requestLocationUpdates" 72 | } 73 | 74 | } -------------------------------------------------------------------------------- /app/src/main/java/com/lanshifu/privacymethodhooker/test/JaveTest.java: -------------------------------------------------------------------------------- 1 | package com.lanshifu.privacymethodhooker.test; 2 | 3 | import android.content.ContentResolver; 4 | import android.content.Context; 5 | import android.hardware.Sensor; 6 | import android.hardware.SensorManager; 7 | import android.os.Build; 8 | import android.provider.Settings; 9 | 10 | import androidx.annotation.RequiresApi; 11 | 12 | import java.util.List; 13 | 14 | /** 15 | * @author lanxiaobin 16 | * @date 2021/10/10 17 | */ 18 | class JaveTest { 19 | 20 | String getDeviceId(Context context) { 21 | ContentResolver cr = context.getContentResolver(); 22 | String androidId = Settings.System.getString(cr, Settings.Secure.ANDROID_ID); 23 | return androidId; 24 | } 25 | 26 | @RequiresApi(api = Build.VERSION_CODES.M) 27 | List test(Context context) { 28 | { 29 | SensorManager manager = 30 | (SensorManager) context.getSystemService(SensorManager.class); 31 | 32 | return manager.getSensorList(Sensor.TYPE_ALL); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /app/src/main/java/com/lanshifu/privacymethodhooker/testcase/TopLevelUtil.kt: -------------------------------------------------------------------------------- 1 | package com.lanshifu.privacymethodhooker.testcase 2 | 3 | import android.Manifest 4 | import android.annotation.SuppressLint 5 | import android.app.Activity 6 | import android.app.ActivityManager 7 | import android.app.ActivityManager.RunningAppProcessInfo 8 | import android.content.Context 9 | import android.content.pm.PackageManager 10 | import android.hardware.Sensor 11 | import android.hardware.SensorManager 12 | import android.location.Location 13 | import android.location.LocationListener 14 | import android.location.LocationManager 15 | import android.net.ConnectivityManager 16 | import android.net.DhcpInfo 17 | import android.net.wifi.ScanResult 18 | import android.net.wifi.WifiConfiguration 19 | import android.net.wifi.WifiInfo 20 | import android.net.wifi.WifiManager 21 | import android.os.Build 22 | import android.os.Bundle 23 | import android.os.Process 24 | import android.telephony.CellInfo 25 | import android.telephony.TelephonyManager 26 | import android.util.Log 27 | import androidx.annotation.RequiresApi 28 | import androidx.core.app.ActivityCompat 29 | 30 | /** 31 | * @author lanxiaobin 32 | * @date 2020-04-25 33 | */ 34 | 35 | fun Context.getCurrentProcessName(): String? { 36 | val pid = Process.myPid() 37 | var processName = "" 38 | val manager: ActivityManager = 39 | applicationContext.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager 40 | val runningAppProcesses = manager.runningAppProcesses 41 | for (process in runningAppProcesses) { 42 | 43 | if (process.pid == pid) { 44 | processName = process.processName 45 | } 46 | } 47 | return processName 48 | } 49 | 50 | fun getRunningAppProcesses(context: Context): MutableList? { 51 | val manager: ActivityManager = 52 | context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager 53 | return manager.runningAppProcesses 54 | } 55 | 56 | fun getRecentTasks(context: Context): MutableList? { 57 | val manager: ActivityManager = 58 | context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager 59 | return manager.getRecentTasks(100, ActivityManager.RECENT_WITH_EXCLUDED) 60 | } 61 | 62 | fun getRunningTasks(context: Context): MutableList? { 63 | val manager: ActivityManager = 64 | context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager 65 | return manager.getRunningTasks(100) 66 | } 67 | 68 | @RequiresApi(Build.VERSION_CODES.M) 69 | fun getAllCellInfo(context: Activity): MutableList? { 70 | val manager: TelephonyManager = 71 | context.getSystemService(TelephonyManager::class.java) as TelephonyManager 72 | if (ActivityCompat.checkSelfPermission( 73 | context, 74 | Manifest.permission.ACCESS_FINE_LOCATION 75 | ) != PackageManager.PERMISSION_GRANTED 76 | ) { 77 | 78 | context.requestPermissions(arrayOf(Manifest.permission.ACCESS_FINE_LOCATION), 0) 79 | return null 80 | } 81 | return manager.getAllCellInfo() 82 | } 83 | 84 | 85 | @SuppressLint("HardwareIds") 86 | @RequiresApi(Build.VERSION_CODES.M) 87 | fun getDeviceId(context: Activity): String? { 88 | val manager: TelephonyManager = 89 | context.getSystemService(TelephonyManager::class.java) as TelephonyManager 90 | if (ActivityCompat.checkSelfPermission( 91 | context, 92 | Manifest.permission.READ_PHONE_STATE 93 | ) != PackageManager.PERMISSION_GRANTED 94 | ) { 95 | 96 | context.requestPermissions(arrayOf(Manifest.permission.READ_PHONE_STATE), 0) 97 | return null 98 | } 99 | return manager.getDeviceId() 100 | } 101 | 102 | @RequiresApi(Build.VERSION_CODES.M) 103 | @SuppressLint("HardwareIds") 104 | fun getImei(context: Activity): String? { 105 | val manager: TelephonyManager = 106 | context.getSystemService(TelephonyManager::class.java) as TelephonyManager 107 | 108 | return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { 109 | manager.getImei() 110 | } else { 111 | "VERSION.SDK_INT < O" 112 | } 113 | } 114 | 115 | @RequiresApi(Build.VERSION_CODES.M) 116 | fun getSimSerialNumber(context: Activity): String? { 117 | val manager: TelephonyManager = 118 | context.getSystemService(TelephonyManager::class.java) as TelephonyManager 119 | return manager.getSimSerialNumber() 120 | } 121 | 122 | 123 | @RequiresApi(Build.VERSION_CODES.M) 124 | fun getSensorList(context: Activity): MutableList? { 125 | val manager: SensorManager = 126 | context.getSystemService(SensorManager::class.java) as SensorManager 127 | 128 | return manager.getSensorList(Sensor.TYPE_ALL) 129 | } 130 | 131 | private fun getWifiInfo(context: Activity): WifiInfo? { 132 | 133 | if (ActivityCompat.checkSelfPermission( 134 | context, 135 | Manifest.permission.ACCESS_WIFI_STATE 136 | ) != PackageManager.PERMISSION_GRANTED 137 | ) { 138 | 139 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { 140 | context.requestPermissions(arrayOf(Manifest.permission.ACCESS_FINE_LOCATION), 0) 141 | return null 142 | } 143 | } 144 | 145 | val connManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager 146 | val networkInfo = connManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI) 147 | if (networkInfo != null && networkInfo.isConnected) { 148 | val wifiManager = 149 | context.applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager 150 | return wifiManager.connectionInfo 151 | } 152 | return null 153 | } 154 | 155 | fun getSSID(context: Activity): String? { 156 | return getWifiInfo(context)?.bssid 157 | } 158 | 159 | fun getBSSID(context: Activity): String? { 160 | return getWifiInfo(context)?.bssid 161 | } 162 | 163 | fun getMacAddress(context: Activity): String? { 164 | return getWifiInfo(context)?.macAddress 165 | } 166 | 167 | @RequiresApi(Build.VERSION_CODES.M) 168 | fun getScanResults(context: Activity): MutableList? { 169 | val wifiManager = context.getSystemService(WifiManager::class.java) as WifiManager 170 | if (ActivityCompat.checkSelfPermission( 171 | context, 172 | Manifest.permission.ACCESS_FINE_LOCATION 173 | ) != PackageManager.PERMISSION_GRANTED 174 | ) { 175 | // TODO: Consider calling 176 | // ActivityCompat#requestPermissions 177 | // here to request the missing permissions, and then overriding 178 | // public void onRequestPermissionsResult(int requestCode, String[] permissions, 179 | // int[] grantResults) 180 | // to handle the case where the user grants the permission. See the documentation 181 | // for ActivityCompat#requestPermissions for more details. 182 | return null 183 | } 184 | return wifiManager.scanResults 185 | } 186 | 187 | @RequiresApi(Build.VERSION_CODES.M) 188 | fun getDhcpInfo(context: Activity): DhcpInfo? { 189 | val wifiManager = context.getSystemService(WifiManager::class.java) as WifiManager 190 | return wifiManager.dhcpInfo 191 | } 192 | 193 | @RequiresApi(Build.VERSION_CODES.M) 194 | fun getConfiguredNetworks(context: Activity): MutableList? { 195 | val wifiManager = context.getSystemService(WifiManager::class.java) as WifiManager 196 | if (ActivityCompat.checkSelfPermission( 197 | context, 198 | Manifest.permission.ACCESS_FINE_LOCATION 199 | ) != PackageManager.PERMISSION_GRANTED 200 | ) { 201 | context.requestPermissions(arrayOf(Manifest.permission.ACCESS_FINE_LOCATION), 0) 202 | return null 203 | } 204 | return wifiManager.configuredNetworks 205 | } 206 | 207 | @RequiresApi(Build.VERSION_CODES.M) 208 | fun requestLocationUpdates(context: Activity): Boolean { 209 | 210 | val listener: LocationListener = object : LocationListener { 211 | override fun onLocationChanged(location: Location) { 212 | } 213 | 214 | override fun onProviderDisabled(provider: String) {} 215 | override fun onProviderEnabled(provider: String) {} 216 | override fun onStatusChanged(provider: String, status: Int, extras: Bundle) {} 217 | } 218 | 219 | val manager = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager 220 | val provider = manager.getProviders(true) 221 | 222 | if (ActivityCompat.checkSelfPermission( 223 | context, 224 | Manifest.permission.ACCESS_FINE_LOCATION 225 | ) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission( 226 | context, 227 | Manifest.permission.ACCESS_COARSE_LOCATION 228 | ) != PackageManager.PERMISSION_GRANTED 229 | ) { 230 | context.requestPermissions( 231 | arrayOf( 232 | Manifest.permission.ACCESS_FINE_LOCATION, 233 | Manifest.permission.ACCESS_COARSE_LOCATION 234 | ), 0 235 | ) 236 | return true 237 | } 238 | 239 | if (provider.size > 0) { 240 | } 241 | manager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0L, 0f, listener) 242 | return true 243 | } 244 | 245 | @RequiresApi(Build.VERSION_CODES.M) 246 | fun getLastKnownLocation(context: Activity): Location? { 247 | 248 | val manager = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager 249 | val provider = manager.getProviders(true) 250 | if (ActivityCompat.checkSelfPermission( 251 | context, 252 | Manifest.permission.ACCESS_FINE_LOCATION 253 | ) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission( 254 | context, 255 | Manifest.permission.ACCESS_COARSE_LOCATION 256 | ) != PackageManager.PERMISSION_GRANTED 257 | ) { 258 | context.requestPermissions( 259 | arrayOf( 260 | Manifest.permission.ACCESS_FINE_LOCATION, 261 | Manifest.permission.ACCESS_COARSE_LOCATION 262 | ), 0 263 | ) 264 | return null 265 | } 266 | Log.i("TAG", "getLastKnownLocation: provider.size=${provider.size}") 267 | if (provider.size > 0) { 268 | } 269 | return manager.getLastKnownLocation(LocationManager.GPS_PROVIDER) 270 | 271 | } 272 | //@RequiresApi(Build.VERSION_CODES.M) 273 | //fun getLastLocation(context: Activity):Location? { 274 | // 275 | // val manager = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager 276 | // val provider = manager.getProviders(true) 277 | // if (ActivityCompat.checkSelfPermission( 278 | // context, 279 | // Manifest.permission.ACCESS_FINE_LOCATION 280 | // ) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission( 281 | // context, 282 | // Manifest.permission.ACCESS_COARSE_LOCATION 283 | // ) != PackageManager.PERMISSION_GRANTED 284 | // ) { 285 | // context.requestPermissions(arrayOf(Manifest.permission.ACCESS_FINE_LOCATION,Manifest.permission.ACCESS_COARSE_LOCATION),0) 286 | // return null 287 | // } 288 | // Log.i("TAG", "getLastKnownLocation: provider.size=${provider.size}") 289 | // return manager.getLastLocation(LocationManager.GPS_PROVIDER) 290 | // 291 | //} 292 | -------------------------------------------------------------------------------- /app/src/main/java/com/lanshifu/privacymethodhooker/utils/PrivacyUtil.kt: -------------------------------------------------------------------------------- 1 | package com.lanshifu.privacymethodhooker.utils 2 | 3 | import android.annotation.SuppressLint 4 | import android.app.ActivityManager 5 | import android.app.ActivityManager.RunningAppProcessInfo 6 | import android.content.ContentResolver 7 | import android.hardware.Sensor 8 | import android.hardware.SensorManager 9 | import android.location.Location 10 | import android.location.LocationListener 11 | import android.location.LocationManager 12 | import android.net.DhcpInfo 13 | import android.net.wifi.ScanResult 14 | import android.net.wifi.WifiConfiguration 15 | import android.net.wifi.WifiInfo 16 | import android.net.wifi.WifiManager 17 | import android.provider.Settings 18 | import android.telephony.CellInfo 19 | import android.telephony.TelephonyManager 20 | import android.util.Log 21 | import androidx.annotation.Keep 22 | import com.lanshifu.asm_annotation.AsmMethodReplace 23 | import com.lanshifu.asm_annotation.AsmMethodOpcodes 24 | import com.lanshifu.privacymethodhooker.BuildConfig 25 | import java.util.* 26 | import kotlin.collections.HashMap 27 | 28 | /** 29 | * @author lanxiaobin 30 | * @date 2021/10/9 31 | * 32 | * 1、不要被混淆 33 | * 34 | * 2、Kotlin 的方法必须要使用JvmStatic注解,否则Java调用会报错 35 | * 36 | * java.lang.IncompatibleClassChangeError: The method 37 | * 'java.lang.String com.lanshifu.privacymethodhooker.utils.PrivacyUtil.getString(android.content.ContentResolver, java.lang.String)' 38 | * was expected to be of type static but instead was found to be of type virtual 39 | * (declaration of 'com.lanshifu.privacymethodhooker.MainActivity' appears in /data/app/com.lanshifu.privacymethodhooker-2/base.apk) 40 | */ 41 | @Keep 42 | object PrivacyUtil { 43 | 44 | private const val TAG = "PrivacyUtil" 45 | 46 | var isAgreePrivacy: Boolean = false 47 | var isUseCache: Boolean = true 48 | 49 | private var anyCache = HashMap() 50 | 51 | private fun checkAgreePrivacy(name: String): Boolean { 52 | if (!isAgreePrivacy) { 53 | logW("$name: isAgreePrivacy=false") 54 | //没有同意隐私权限,打印堆栈,toast 55 | if (BuildConfig.DEBUG) { 56 | // Toast.makeText( 57 | // ApplicationContext.getContext(), 58 | // "隐私同意前禁止调用$name,现在返回默认值,log过滤PrivacyUtil", 59 | // Toast.LENGTH_LONG 60 | // ).show() 61 | Log.d(TAG, "$name: stack= " + Log.getStackTraceString(Throwable())) 62 | } 63 | return false 64 | } 65 | 66 | return true 67 | } 68 | 69 | private fun getListCache(key: String): List? { 70 | if (!isUseCache){ 71 | return null 72 | } 73 | val cache = anyCache[key] 74 | if (cache != null && cache is List<*>) { 75 | try { 76 | return cache as List 77 | } catch (e: Exception) { 78 | Log.w(TAG, "getListCache: key=$key,e=${e.message}") 79 | } 80 | } 81 | logD("getListCache key=$key,return null") 82 | return null 83 | } 84 | 85 | private fun getCache(key: String): T? { 86 | if (!isUseCache){ 87 | return null 88 | } 89 | val cache = anyCache[key] 90 | if (cache != null) { 91 | try { 92 | Log.d(TAG, "getCache: key=$key,value=$cache") 93 | return cache as T 94 | } catch (e: Exception) { 95 | Log.w(TAG, "getListCache: key=$key,e=${e.message}") 96 | } 97 | } 98 | logD("getCache key=$key,return null") 99 | return null 100 | } 101 | 102 | 103 | private fun putCache(key: String, value: T): T { 104 | logI("putCache key=$key,value=$value") 105 | value?.let { 106 | anyCache[key] = value 107 | } 108 | return value 109 | } 110 | 111 | 112 | @JvmStatic 113 | @AsmMethodReplace(oriClass = ActivityManager::class, oriAccess = AsmMethodOpcodes.INVOKEVIRTUAL) 114 | fun getRunningAppProcesses(manager: ActivityManager): List { 115 | val key = "getRunningAppProcesses" 116 | val cache = getListCache(key) 117 | if (cache != null) { 118 | return cache 119 | } 120 | if (!checkAgreePrivacy(key)) { 121 | return emptyList() 122 | } 123 | val value = manager.runningAppProcesses 124 | return putCache(key, value) 125 | } 126 | 127 | @JvmStatic 128 | @AsmMethodReplace(oriClass = ActivityManager::class, oriAccess = AsmMethodOpcodes.INVOKEVIRTUAL) 129 | fun getRecentTasks( 130 | manager: ActivityManager, 131 | maxNum: Int, 132 | flags: Int 133 | ): List? { 134 | val key = "getRecentTasks" 135 | val cache = getListCache(key) 136 | if (cache != null) { 137 | return cache 138 | } 139 | if (!checkAgreePrivacy(key)) { 140 | return emptyList() 141 | } 142 | val value = manager.getRecentTasks(maxNum, flags) 143 | return putCache(key, value) 144 | 145 | } 146 | 147 | @JvmStatic 148 | @AsmMethodReplace(oriClass = ActivityManager::class, oriAccess = AsmMethodOpcodes.INVOKEVIRTUAL) 149 | fun getRunningTasks( 150 | manager: ActivityManager, 151 | maxNum: Int 152 | ): List? { 153 | val key = "getRunningTasks" 154 | val cache = getListCache(key) 155 | if (cache != null) { 156 | return cache 157 | } 158 | if (!checkAgreePrivacy(key)) { 159 | return emptyList() 160 | } 161 | val value = manager.getRunningTasks(maxNum) 162 | return putCache(key, value) 163 | 164 | } 165 | 166 | /** 167 | * 读取基站信息,需要开启定位 168 | */ 169 | @JvmStatic 170 | @SuppressLint("MissingPermission") 171 | @AsmMethodReplace(oriClass = TelephonyManager::class, oriAccess = AsmMethodOpcodes.INVOKEVIRTUAL) 172 | fun getAllCellInfo(manager: TelephonyManager): List? { 173 | val key = "getAllCellInfo" 174 | val cache = getListCache(key) 175 | if (cache != null) { 176 | return cache 177 | } 178 | if (!checkAgreePrivacy(key)) { 179 | return emptyList() 180 | } 181 | val value = manager.getAllCellInfo() 182 | return putCache(key, value) 183 | } 184 | 185 | /** 186 | * 读取基站信息 187 | */ 188 | @SuppressLint("HardwareIds") 189 | @JvmStatic 190 | @AsmMethodReplace(oriClass = TelephonyManager::class, oriAccess = AsmMethodOpcodes.INVOKEVIRTUAL) 191 | fun getDeviceId(manager: TelephonyManager): String? { 192 | val key = "getDeviceId" 193 | val cache = getCache(key) 194 | if (cache != null) { 195 | return cache 196 | } 197 | if (!checkAgreePrivacy(key)) { 198 | return null 199 | } 200 | //READ_PHONE_STATE 已经整改去掉,返回null 201 | // return manager.deviceId 202 | return putCache(key, null) 203 | 204 | } 205 | 206 | /** 207 | * 读取基站信息 208 | */ 209 | @SuppressLint("HardwareIds") 210 | @JvmStatic 211 | @AsmMethodReplace(oriClass = TelephonyManager::class, oriAccess = AsmMethodOpcodes.INVOKEVIRTUAL) 212 | fun getImei(manager: TelephonyManager): String? { 213 | val key = "getImei" 214 | val cache = getCache(key) 215 | if (cache != null) { 216 | return cache 217 | } 218 | if (!checkAgreePrivacy(key)) { 219 | return null 220 | } 221 | //READ_PHONE_STATE 已经整改去掉,返回null 222 | return putCache(key, null) 223 | } 224 | 225 | /** 226 | * 读取ICCID 227 | */ 228 | @SuppressLint("HardwareIds") 229 | @JvmStatic 230 | @AsmMethodReplace(oriClass = TelephonyManager::class, oriAccess = AsmMethodOpcodes.INVOKEVIRTUAL) 231 | fun getSimSerialNumber(manager: TelephonyManager): String? { 232 | val key = "getSimSerialNumber" 233 | val cache = getCache(key) 234 | if (cache != null) { 235 | return cache 236 | } 237 | if (!checkAgreePrivacy(key)) { 238 | return "" 239 | } 240 | //android.Manifest.permission.READ_PHONE_STATE,不允许App读取,拦截调用 241 | return putCache(key, null) 242 | } 243 | 244 | /** 245 | * 读取WIFI的SSID 246 | */ 247 | @JvmStatic 248 | @AsmMethodReplace(oriClass = WifiInfo::class, oriAccess = AsmMethodOpcodes.INVOKEVIRTUAL) 249 | fun getSSID(manager: WifiInfo): String? { 250 | val key = "getSSID" 251 | val cache = getCache(key) 252 | if (cache != null) { 253 | return cache 254 | } 255 | 256 | if (!checkAgreePrivacy(key)) { 257 | return "" 258 | } 259 | 260 | val value = manager.ssid 261 | return putCache(key, value) 262 | } 263 | 264 | /** 265 | * 读取WIFI的SSID 266 | */ 267 | @JvmStatic 268 | @AsmMethodReplace(oriClass = WifiInfo::class, oriAccess = AsmMethodOpcodes.INVOKEVIRTUAL) 269 | fun getBSSID(manager: WifiInfo): String? { 270 | val key = "getBSSID" 271 | val cache = getCache(key) 272 | if (cache != null) { 273 | return cache 274 | } 275 | if (!checkAgreePrivacy(key)) { 276 | return "" 277 | } 278 | val value = manager.bssid 279 | return putCache(key, value) 280 | } 281 | 282 | /** 283 | * 读取WIFI的SSID 284 | */ 285 | @SuppressLint("HardwareIds") 286 | @JvmStatic 287 | @AsmMethodReplace(oriClass = WifiInfo::class, oriAccess = AsmMethodOpcodes.INVOKEVIRTUAL) 288 | fun getMacAddress(manager: WifiInfo): String? { 289 | val key = "getMacAddress" 290 | val cache = getCache(key) 291 | if (cache != null) { 292 | return cache 293 | } 294 | if (!checkAgreePrivacy(key)) { 295 | return "" 296 | } 297 | val value = manager.macAddress 298 | return putCache(key, value) 299 | } 300 | 301 | /** 302 | * 读取AndroidId 303 | */ 304 | @JvmStatic 305 | @AsmMethodReplace(oriClass = Settings.System::class, oriAccess = AsmMethodOpcodes.INVOKESTATIC) 306 | fun getString(resolver: ContentResolver, name: String): String? { 307 | //处理AndroidId 308 | if (Settings.Secure.ANDROID_ID == name) { 309 | 310 | val key = "ANDROID_ID" 311 | val cache = getCache(key) 312 | if (cache != null) { 313 | return cache 314 | } 315 | if (!checkAgreePrivacy(key)) { 316 | return "" 317 | } 318 | val value = Settings.System.getString(resolver, name) 319 | return putCache(key, value) 320 | } 321 | 322 | return Settings.System.getString(resolver, name) 323 | } 324 | 325 | /** 326 | * getSensorList 327 | */ 328 | @JvmStatic 329 | @AsmMethodReplace(oriClass = SensorManager::class, oriAccess = AsmMethodOpcodes.INVOKEVIRTUAL) 330 | fun getSensorList(manager: SensorManager, type: Int): List? { 331 | val key = "getSensorList" 332 | val cache = getListCache(key) 333 | if (cache != null) { 334 | return cache 335 | } 336 | if (!checkAgreePrivacy(key)) { 337 | return mutableListOf() 338 | } 339 | val value = manager.getSensorList(type) 340 | return putCache(key, value) 341 | 342 | } 343 | 344 | /** 345 | * 读取WIFI扫描结果 346 | */ 347 | @JvmStatic 348 | @AsmMethodReplace(oriClass = WifiManager::class, oriAccess = AsmMethodOpcodes.INVOKEVIRTUAL) 349 | fun getScanResults(manager: WifiManager): List? { 350 | val key = "getScanResults" 351 | val cache = getListCache(key) 352 | if (cache != null) { 353 | return cache 354 | } 355 | if (!checkAgreePrivacy("getScanResults")) { 356 | return mutableListOf() 357 | } 358 | val value = manager.getScanResults() 359 | return putCache(key, value) 360 | } 361 | 362 | /** 363 | * 读取DHCP信息 364 | */ 365 | @JvmStatic 366 | @AsmMethodReplace(oriClass = WifiManager::class, oriAccess = AsmMethodOpcodes.INVOKEVIRTUAL) 367 | fun getDhcpInfo(manager: WifiManager): DhcpInfo? { 368 | val key = "getDhcpInfo" 369 | val cache = getCache(key) 370 | if (cache != null) { 371 | return cache 372 | } 373 | if (!checkAgreePrivacy(key)) { 374 | return null 375 | } 376 | val value = manager.getDhcpInfo() 377 | return putCache(key, value) 378 | 379 | } 380 | 381 | 382 | /** 383 | * 读取DHCP信息 384 | */ 385 | @SuppressLint("MissingPermission") 386 | @JvmStatic 387 | @AsmMethodReplace(oriClass = WifiManager::class, oriAccess = AsmMethodOpcodes.INVOKEVIRTUAL) 388 | fun getConfiguredNetworks(manager: WifiManager): List? { 389 | val key = "getConfiguredNetworks" 390 | val cache = getListCache(key) 391 | if (cache != null) { 392 | return cache 393 | } 394 | if (!checkAgreePrivacy(key)) { 395 | return mutableListOf() 396 | } 397 | val value = manager.getConfiguredNetworks() 398 | return putCache(key, value) 399 | 400 | } 401 | 402 | 403 | /** 404 | * 读取位置信息 405 | */ 406 | @JvmStatic 407 | @SuppressLint("MissingPermission") 408 | @AsmMethodReplace(oriClass = LocationManager::class, oriAccess = AsmMethodOpcodes.INVOKEVIRTUAL) 409 | fun getLastKnownLocation( 410 | manager: LocationManager, provider: String 411 | ): Location? { 412 | if (!checkAgreePrivacy("getLastKnownLocation")) { 413 | return null 414 | } 415 | return manager.getLastKnownLocation(provider) 416 | } 417 | 418 | 419 | /** 420 | * 读取位置信息 421 | */ 422 | // @JvmStatic 423 | // @AsmField(oriClass = LocationManager::class, oriAccess = AsmMethodOpcodes.INVOKEVIRTUAL) 424 | // fun getLastLocation( 425 | // manager: LocationManager, provider: String 426 | // ): Location? { 427 | // log("getLastKnownLocation: isAgreePrivacy=$isAgreePrivacy") 428 | // if (isAgreePrivacy) { 429 | // return manager.getLastLocation(provider) 430 | // } 431 | // return null 432 | // } 433 | 434 | 435 | /** 436 | * 监视精细行动轨迹 437 | */ 438 | @SuppressLint("MissingPermission") 439 | @JvmStatic 440 | @AsmMethodReplace(oriClass = LocationManager::class, oriAccess = AsmMethodOpcodes.INVOKEVIRTUAL) 441 | fun requestLocationUpdates( 442 | manager: LocationManager, provider: String, minTime: Long, minDistance: Float, 443 | listener: LocationListener 444 | ) { 445 | if (!checkAgreePrivacy("requestLocationUpdates")) { 446 | return 447 | } 448 | manager.requestLocationUpdates(provider, minTime, minDistance, listener) 449 | 450 | } 451 | 452 | 453 | private fun logI(log: String) { 454 | Log.i(TAG, log) 455 | } 456 | private fun logD(log: String) { 457 | if (BuildConfig.DEBUG){ 458 | Log.d(TAG, log) 459 | } 460 | } 461 | 462 | private fun logW(log: String) { 463 | Log.w(TAG, log) 464 | } 465 | 466 | } -------------------------------------------------------------------------------- /app/src/main/java/com/lanshifu/privacymethodhooker/utils/ReflectUtils.kt: -------------------------------------------------------------------------------- 1 | package com.lanshifu.privacymethodhooker.utils 2 | 3 | import java.lang.reflect.Method 4 | 5 | /** 6 | * @author lanxiaobin 7 | * @date 2021/10/5 8 | */ 9 | object ReflectUtils { 10 | 11 | // fun invokeSuperMethod( 12 | // obj: Any, 13 | // name: String, 14 | // types: Array>, 15 | // args: Array? 16 | // ): Any? { 17 | // try { 18 | // val method: Method? = getMethod(obj.javaClass.superclass, name, types) 19 | // if (null != method) { 20 | // method.isAccessible = true 21 | // return method.invoke(obj, args) 22 | // } 23 | // } catch (t: Throwable) { 24 | // t.printStackTrace() 25 | // } 26 | // return null 27 | // } 28 | 29 | private fun getMethod(klass: Class<*>, name: String, types: Array>): Method? { 30 | return try { 31 | klass.getDeclaredMethod(name, *types) 32 | } catch (e: NoSuchMethodException) { 33 | val parent = klass.superclass ?: return null 34 | getMethod(parent, name, types) 35 | } 36 | } 37 | 38 | @JvmStatic 39 | fun invokeMethod(obj: Any, name: String, types: Array>, args: Array): Any? { 40 | try { 41 | val method = getMethod(obj.javaClass, name, types) 42 | if (null != method) { 43 | method.isAccessible = true 44 | return method.invoke(obj, *args) 45 | } 46 | } catch (t: Throwable) { 47 | t.printStackTrace() 48 | } 49 | return null 50 | } 51 | } -------------------------------------------------------------------------------- /app/src/main/java/com/lanshifu/privacymethodhooker/utils/Test.java: -------------------------------------------------------------------------------- 1 | package com.lanshifu.privacymethodhooker.utils; 2 | 3 | import android.app.ActivityManager; 4 | import android.content.Context; 5 | 6 | /** 7 | * @author lanxiaobin 8 | * @date 2021/10/9 9 | */ 10 | class Test { 11 | 12 | void test0(Context context) { 13 | ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); 14 | //INVOKEVIRTUAL android/app/ActivityManager.getRunningAppProcesses ()Ljava/util/List; 15 | for (ActivityManager.RunningAppProcessInfo process : manager.getRunningAppProcesses()) { 16 | 17 | } 18 | } 19 | 20 | void test() { 21 | ActivityManager activityManager = null; 22 | 23 | //INVOKESTATIC com/lanshifu/privacymethodhooker/utils/PrivacyUtilJava.getRunningAppProcesses (Landroid/app/ActivityManager;)Ljava/util/List; 24 | 25 | for (ActivityManager.RunningAppProcessInfo runningAppProcessInfo : PrivacyUtil.getRunningAppProcesses(activityManager)) { 26 | 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 14 | 15 | 19 | 20 | 25 | 26 | 31 | 32 | 36 | 37 | 38 |