├── .gitignore ├── .idea ├── gradle.xml └── runConfigurations.xml ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── greens1995 │ │ └── myapplication │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── greens1995 │ │ │ └── myapplication │ │ │ ├── CheckHook.java │ │ │ ├── CheckRoot.java │ │ │ ├── CheckVirtual.java │ │ │ ├── EmulatorDetector.java │ │ │ └── MainActivity.java │ └── res │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ └── ic_launcher_background.xml │ │ ├── layout │ │ └── activity_main.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ └── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── greens1995 │ └── myapplication │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | # Built application files 2 | *.apk 3 | *.ap_ 4 | 5 | # Files for the ART/Dalvik VM 6 | *.dex 7 | 8 | # Java class files 9 | *.class 10 | 11 | # Generated files 12 | bin/ 13 | gen/ 14 | out/ 15 | 16 | # Gradle files 17 | .gradle/ 18 | build/ 19 | 20 | # Local configuration file (sdk path, etc) 21 | local.properties 22 | 23 | # Proguard folder generated by Eclipse 24 | proguard/ 25 | 26 | # Log Files 27 | *.log 28 | 29 | # Android Studio Navigation editor temp files 30 | .navigation/ 31 | 32 | # Android Studio captures folder 33 | captures/ 34 | 35 | # Intellij 36 | *.iml 37 | .idea 38 | 39 | # Keystore files 40 | *.jks 41 | 42 | # External native build folder generated in Android Studio 2.2 and later 43 | .externalNativeBuild 44 | 45 | # Google Services (e.g. APIs or Firebase) 46 | google-services.json 47 | 48 | # Freeline 49 | freeline.py 50 | freeline/ 51 | freeline_project_description.json 52 | *.iml 53 | .gradle 54 | /local.properties 55 | /.idea/workspace.xml 56 | /.idea/libraries 57 | .DS_Store 58 | /build 59 | /captures 60 | .externalNativeBuild 61 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 18 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # anti-counterfeit-android 2 | 模拟器检测,多开检测,Hook检测,Root检测。 3 | 4 | 5 | 4个Java类,直接拷贝使用即可。 6 | ## 模拟器检测 7 | boolean isEmulator = EmulatorDetector.isEmulator(); 8 | ## 多开检测 9 | boolean isVirtual = CheckVirtual.isRunInVirtual(context); 10 | ## Hook检测 11 | boolean isHook = CheckHook.isHook(context); 12 | ## Root检测 13 | boolean isRoot = CheckRoot.isDeviceRooted(); 14 | 15 | ## TODO... 16 | 1. AccessibilityService检测 17 | 2. 模拟器检测增加 Cache 特征识别 18 | 19 | 参考:https://github.com/happylishang/CacheEmulatorChecker 20 | 21 | 3. Hook检测增加 Native层检测 22 | 23 | 参考:https://tech.meituan.com/android_anti_hooking.html 24 | ------------------------------ 25 | [恶意用户识别?——Java 层反模拟器、反Hook、反多开技巧](http://greens1995.com/2018/01/30/anti-counterfeit) 26 | 27 | 近两年,Android端的虚拟化技术和群控技术发展急速,带来很多好玩产品和便利工具。但是作为App开发者就头疼了,恶意用户(比如不文明用户、比如刷单)利用这些技术,作恶门槛低得不知道哪里去。我们需要思考怎么识别和防御了。 28 | 下文介绍一些简单但是有效的恶意用户识别(方便后续封号)方案。 29 | # Anti 模拟器 30 | 这个很容易理解,模拟出来的机器,每次模拟的时候生成的设备ID,只存在模拟器使用的生命周期里。可能下一次模拟时又不一样了。 31 | 应对方法:主要是检测运行模拟器的一些特征,比如驱动文件,Build类内的硬件讯息等。 32 | 比如Build类内有模拟器的字串,明显就是模拟器: 33 | ```java 34 | public static boolean isEmulatorAbsoluly() { 35 | if (Build.PRODUCT.contains("sdk") || 36 | Build.PRODUCT.contains("sdk_x86") || 37 | Build.PRODUCT.contains("sdk_google") || 38 | Build.PRODUCT.contains("Andy") || 39 | Build.PRODUCT.contains("Droid4X") || 40 | Build.PRODUCT.contains("nox") || 41 | Build.PRODUCT.contains("vbox86p")) { 42 | return true; 43 | } 44 | if (Build.MANUFACTURER.equals("Genymotion") || 45 | Build.MANUFACTURER.contains("Andy") || 46 | Build.MANUFACTURER.contains("nox") || 47 | Build.MANUFACTURER.contains("TiantianVM")) { 48 | return true; 49 | } 50 | if (Build.BRAND.contains("Andy")) { 51 | return true; 52 | } 53 | if (Build.DEVICE.contains("Andy") || 54 | Build.DEVICE.contains("Droid4X") || 55 | Build.DEVICE.contains("nox") || 56 | Build.DEVICE.contains("vbox86p")) { 57 | return true; 58 | } 59 | if (Build.MODEL.contains("Emulator") || 60 | Build.MODEL.equals("google_sdk") || 61 | Build.MODEL.contains("Droid4X") || 62 | Build.MODEL.contains("TiantianVM") || 63 | Build.MODEL.contains("Andy") || 64 | Build.MODEL.equals("Android SDK built for x86_64") || 65 | Build.MODEL.equals("Android SDK built for x86")) { 66 | return true; 67 | } 68 | if (Build.HARDWARE.equals("vbox86") || 69 | Build.HARDWARE.contains("nox") || 70 | Build.HARDWARE.contains("ttVM_x86")) { 71 | return true; 72 | } 73 | if (Build.FINGERPRINT.contains("generic/sdk/generic") || 74 | Build.FINGERPRINT.contains("generic_x86/sdk_x86/generic_x86") || 75 | Build.FINGERPRINT.contains("Andy") || 76 | Build.FINGERPRINT.contains("ttVM_Hdragon") || 77 | Build.FINGERPRINT.contains("generic/google_sdk/generic") || 78 | Build.FINGERPRINT.contains("vbox86p") || 79 | Build.FINGERPRINT.contains("generic/vbox86p/vbox86p")) { 80 | return true; 81 | } 82 | return false; 83 | } 84 | ``` 85 | 还有的特征只是疑似,但不确定,对于这些特征,可以集合起来做一个疑似度评分,评分达到一定程度就标记为模拟器: 86 | ```java 87 | int newRating = 0; 88 | if (rating < 0) { 89 | if (Build.PRODUCT.contains("sdk") || 90 | Build.PRODUCT.contains("Andy") || 91 | Build.PRODUCT.contains("ttVM_Hdragon") || 92 | Build.PRODUCT.contains("google_sdk") || 93 | Build.PRODUCT.contains("Droid4X") || 94 | Build.PRODUCT.contains("nox") || 95 | Build.PRODUCT.contains("sdk_x86") || 96 | Build.PRODUCT.contains("sdk_google") || 97 | Build.PRODUCT.contains("vbox86p")) { 98 | newRating++; 99 | } 100 | 101 | if (Build.MANUFACTURER.equals("unknown") || 102 | Build.MANUFACTURER.equals("Genymotion") || 103 | Build.MANUFACTURER.contains("Andy") || 104 | Build.MANUFACTURER.contains("MIT") || 105 | Build.MANUFACTURER.contains("nox") || 106 | Build.MANUFACTURER.contains("TiantianVM")) { 107 | newRating++; 108 | } 109 | 110 | if (Build.BRAND.equals("generic") || 111 | Build.BRAND.equals("generic_x86") || 112 | Build.BRAND.equals("TTVM") || 113 | Build.BRAND.contains("Andy")) { 114 | newRating++; 115 | } 116 | 117 | if (Build.DEVICE.contains("generic") || 118 | Build.DEVICE.contains("generic_x86") || 119 | Build.DEVICE.contains("Andy") || 120 | Build.DEVICE.contains("ttVM_Hdragon") || 121 | Build.DEVICE.contains("Droid4X") || 122 | Build.DEVICE.contains("nox") || 123 | Build.DEVICE.contains("generic_x86_64") || 124 | Build.DEVICE.contains("vbox86p")) { 125 | newRating++; 126 | } 127 | 128 | if (Build.MODEL.equals("sdk") || 129 | Build.MODEL.contains("Emulator") || 130 | Build.MODEL.equals("google_sdk") || 131 | Build.MODEL.contains("Droid4X") || 132 | Build.MODEL.contains("TiantianVM") || 133 | Build.MODEL.contains("Andy") || 134 | Build.MODEL.equals("Android SDK built for x86_64") || 135 | Build.MODEL.equals("Android SDK built for x86")) { 136 | newRating++; 137 | } 138 | 139 | if (Build.HARDWARE.equals("goldfish") || 140 | Build.HARDWARE.equals("vbox86") || 141 | Build.HARDWARE.contains("nox") || 142 | Build.HARDWARE.contains("ttVM_x86")) { 143 | newRating++; 144 | } 145 | 146 | if (Build.FINGERPRINT.contains("generic/sdk/generic") || 147 | Build.FINGERPRINT.contains("generic_x86/sdk_x86/generic_x86") || 148 | Build.FINGERPRINT.contains("Andy") || 149 | Build.FINGERPRINT.contains("ttVM_Hdragon") || 150 | Build.FINGERPRINT.contains("generic_x86_64") || 151 | Build.FINGERPRINT.contains("generic/google_sdk/generic") || 152 | Build.FINGERPRINT.contains("vbox86p") || 153 | Build.FINGERPRINT.contains("generic/vbox86p/vbox86p")) { 154 | newRating++; 155 | } 156 | 157 | try { 158 | String opengl = android.opengl.GLES20.glGetString(android.opengl.GLES20.GL_RENDERER); 159 | if (opengl != null) { 160 | if (opengl.contains("Bluestacks") || 161 | opengl.contains("Translator") 162 | ) 163 | newRating += 10; 164 | } 165 | } catch (Exception e) { 166 | e.printStackTrace(); 167 | } 168 | 169 | try { 170 | File sharedFolder = new File(Environment 171 | .getExternalStorageDirectory().toString() 172 | + File.separatorChar 173 | + "windows" 174 | + File.separatorChar 175 | + "BstSharedFolder"); 176 | 177 | if (sharedFolder.exists()) { 178 | newRating += 10; 179 | } 180 | } catch (Exception e) { 181 | e.printStackTrace(); 182 | } 183 | rating = newRating; 184 | } 185 | return rating > 3;//不能再少了,否则有可能误判,若增减了新的嫌疑度判定属性,要重新评估该值 186 | ``` 187 | 188 | # Anti 多开 189 | 多开麻烦在于真机多开,具备真机特征,模拟器的检测就失效了,因为它就是真机。 190 | 应对方法:普通的软多开,一般绕不过uid,还是用宿主的。因此,如果满足同一uid下的两个进程对应的包名,在"/data/data"下有两个私有目录,则违背了系统 "只为一个应用创建唯一一个私有目录"的设定,则该应用被多开了。 191 | ```java 192 | public static boolean isRunInVirtual() { 193 | 194 | String filter = getUidStrFormat(); 195 | 196 | String result = exec("ps"); 197 | if (result == null || result.isEmpty()) { 198 | return false; 199 | } 200 | 201 | String[] lines = result.split("\n"); 202 | if (lines == null || lines.length <= 0) { 203 | return false; 204 | } 205 | 206 | int exitDirCount = 0; 207 | 208 | for (int i = 0; i < lines.length; i++) { 209 | if (lines[i].contains(filter)) { 210 | int pkgStartIndex = lines[i].lastIndexOf(" "); 211 | String processName = lines[i].substring(pkgStartIndex <= 0 212 | ? 0 : pkgStartIndex + 1, lines[i].length()); 213 | File dataFile = new File(String.format("/data/data/%s", 214 | processName, Locale.CHINA)); 215 | if (dataFile.exists()) { 216 | exitDirCount++; 217 | } 218 | } 219 | } 220 | 221 | return exitDirCount > 1; 222 | } 223 | ``` 224 | 这个方法是在简书 JZaratustra 大佬的文章里学到的:[Android虚拟机多开检测](https://www.jianshu.com/p/216d65d9971e)。 225 | 但是有一些多开,比如小米自带的多开这种,进程好像都是隔离的独立uid的,暂时没有好办法识别。 226 | 227 | # Anti Hook 228 | 不多说了,方法都被你Hook了,你就是大爷,你说啥就是啥。 229 | 应对方法:检测是否安装了xposed相关应用,检测调用栈道的可疑方法,检测并不应该native的native方法,通过/proc/[pid]/maps检测可疑的共享对象或者JAR。 230 | ### 检测是否安装了xposed相关应用 231 | ```java 232 | PackageManager packageManager = context.getPackageManager(); 233 | List applicationInfoList = packageManager.getInstalledApplications(PackageManager.GET_META_DATA); 234 | 235 | for(ApplicationInfo applicationInfo : applicationInfoList) { 236 | if(applicationInfo.packageName.equals("de.robv.android.xposed.installer")) { 237 | Log.wtf("HookDetection", "Xposed found on the system."); 238 | } 239 | if(applicationInfo.packageName.equals("com.saurik.substrate")) { 240 | Log.wtf("HookDetection", "Substrate found on the system."); 241 | } 242 | } 243 | ``` 244 | ### 检测调用栈道的可疑方法 245 | ``` 246 | try { 247 | throw new Exception("blah"); 248 | } catch (Exception e) { 249 | int zygoteInitCallCount = 0; 250 | for (StackTraceElement stackTraceElement : e.getStackTrace()) { 251 | if (stackTraceElement.getClassName().equals("com.android.internal.os.ZygoteInit")) { 252 | zygoteInitCallCount++; 253 | if (zygoteInitCallCount == 2) { 254 | Log.wtf("HookDetection", "Substrate is active on the device."); 255 | isHook = true; 256 | } 257 | } 258 | if (stackTraceElement.getClassName().equals("com.saurik.substrate.MS$2") && 259 | stackTraceElement.getMethodName().equals("invoked")) { 260 | Log.wtf("HookDetection", "A method on the stack trace has been hooked using Substrate."); 261 | isHook = true; 262 | } 263 | if (stackTraceElement.getClassName().equals("de.robv.android.xposed.XposedBridge") && 264 | stackTraceElement.getMethodName().equals("main")) { 265 | Log.wtf("HookDetection", "Xposed is active on the device."); 266 | isHook = true; 267 | } 268 | if (stackTraceElement.getClassName().equals("de.robv.android.xposed.XposedBridge") && 269 | stackTraceElement.getMethodName().equals("handleHookedMethod")) { 270 | Log.wtf("HookDetection", "A method on the stack trace has been hooked using Xposed."); 271 | isHook = true; 272 | } 273 | 274 | } 275 | } 276 | ``` 277 | ### 通过/proc/[pid]/maps检测可疑的共享对象或者JAR: 278 | ```java 279 | try { 280 | Set libraries = new HashSet(); 281 | String mapsFilename = "/proc/" + android.os.Process.myPid() + "/maps"; 282 | BufferedReader reader = new BufferedReader(new FileReader(mapsFilename)); 283 | String line; 284 | while ((line = reader.readLine()) != null) { 285 | if (line.endsWith(".so") || line.endsWith(".jar")) { 286 | int n = line.lastIndexOf(" "); 287 | libraries.add(line.substring(n + 1)); 288 | } 289 | } 290 | for (String library : libraries) { 291 | if (library.contains("com.saurik.substrate")) { 292 | Log.wtf("HookDetection", "Substrate shared object found: " + library); 293 | isHook = true; 294 | } 295 | if (library.contains("XposedBridge.jar")) { 296 | Log.wtf("HookDetection", "Xposed JAR found: " + library); 297 | isHook = true; 298 | } 299 | } 300 | reader.close(); 301 | } catch (Exception e) { 302 | Log.wtf("HookDetection", e.toString()); 303 | } 304 | ``` 305 | 注意,只要针对这几个检测相关函数Hook,就反反Hook了。很容易绕过。 306 | 307 | # 服务器分析数据相似性 308 | 可用于识别设备的标识有很多,除了Android ID,还有imei、mac、pseduo_id,aaid,gsf_id等。由于谷歌是反对唯一绝对追踪用户的,所以这些id或难或简单都是可能被修改的。比如,通过adb命令就可以无root直接修改Android ID。但是,这些标识全部都修改的话还是优点麻烦的。客户端可以把这些id都上报给服务器,服务器再结合地理位置、ip等其他信息做一个相似度判定,可以找出一些疑似同一恶意用户的账号。 309 | # SD卡存储自制ID 310 | 如果你有SD卡写权限的话,按自己的规则生成id并加密,在自己应用私有目录之外的隐蔽地方偷偷写成一个隐藏文件(只要在文件名或者文件夹名字前加一个点号即可)。只要生成过一次,就以这个为准,无论用户修改设备信息注册多少个马甲,都能识别为同一设备用户。 311 | # 手机号短信认证 312 | 所有登录用户都必须绑定手机号。从产品流程上提高了马甲成本,但是也提高了用户注册门槛。 313 | 314 | 当然了,以上方法只能防小白不防大师,这些方法很容易就可以被有经验的逆向人员绕过。 315 | 写出来,是希望能集思广益,获得更多的反制思路,提高恶意分子伪造设备的成本。(其实是希望碰到大佬指点,提高下本不成器菜鸟的知识水平😄)有更深入实践的同学,求评论,求私信。 316 | 317 | 参考文章: 318 | 319 | [Android反调试之 AntiEmulator 检测安卓模拟器](http://blog.csdn.net/earbao/article/details/51455564) 320 | 321 | [基于文件特征的Android模拟器检测](https://mp.weixin.qq.com/s/sl33d2pnyLMJ-fUY_DfBDw) 322 | 323 | [Android Java层的anti-hooking技巧](http://www.droidsec.cn/android-java%E5%B1%82%E7%9A%84anti-hooking%E6%8A%80%E5%B7%A7/) 324 | 325 | [Android虚拟机多开检测](https://www.jianshu.com/p/216d65d9971e) 326 | 327 | 参考Demo: 328 | 329 | [anti-counterfeit-android](https://github.com/Labmem003/anti-counterfeit-android)。 330 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 26 5 | defaultConfig { 6 | applicationId "com.greens1995.myapplication" 7 | minSdkVersion 14 8 | targetSdkVersion 26 9 | versionCode 1 10 | versionName "1.0" 11 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 12 | } 13 | buildTypes { 14 | release { 15 | minifyEnabled false 16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 17 | } 18 | } 19 | } 20 | 21 | dependencies { 22 | implementation fileTree(dir: 'libs', include: ['*.jar']) 23 | implementation 'com.android.support:appcompat-v7:26.1.0' 24 | implementation 'com.android.support.constraint:constraint-layout:1.0.2' 25 | testImplementation 'junit:junit:4.12' 26 | androidTestImplementation 'com.android.support.test:runner:1.0.1' 27 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1' 28 | } 29 | -------------------------------------------------------------------------------- /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 22 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/greens1995/myapplication/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.greens1995.myapplication; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumented test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.greens1995.myapplication", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /app/src/main/java/com/greens1995/myapplication/CheckHook.java: -------------------------------------------------------------------------------- 1 | package com.greens1995.myapplication; 2 | 3 | import android.content.Context; 4 | import android.content.pm.ApplicationInfo; 5 | import android.content.pm.PackageManager; 6 | import android.util.Log; 7 | 8 | import java.io.BufferedReader; 9 | import java.io.FileReader; 10 | import java.util.HashSet; 11 | import java.util.List; 12 | import java.util.Set; 13 | 14 | /** 15 | * Created by Ben on 2018/1/29. 16 | */ 17 | 18 | public class CheckHook { 19 | 20 | public static boolean isHook(Context context) { 21 | return isHookByPackageName(context) || isHookByStack(context) || isHookByJar(); 22 | } 23 | 24 | /** 25 | * 包名检测 26 | * 27 | * @param context 28 | * @return 29 | */ 30 | public static boolean isHookByPackageName(Context context) { 31 | boolean isHook = false; 32 | PackageManager packageManager = context.getPackageManager(); 33 | List applicationInfoList = packageManager.getInstalledApplications(PackageManager.GET_META_DATA); 34 | if (null == applicationInfoList) { 35 | return isHook; 36 | } 37 | for (ApplicationInfo applicationInfo : applicationInfoList) { 38 | if (applicationInfo.packageName.equals("de.robv.android.xposed.installer")) { 39 | Log.wtf("HookDetection", "Xposed found on the system."); 40 | isHook = true; 41 | } 42 | if (applicationInfo.packageName.equals("com.saurik.substrate")) { 43 | isHook = true; 44 | Log.wtf("HookDetection", "Substrate found on the system."); 45 | } 46 | } 47 | return isHook; 48 | } 49 | 50 | public static boolean isHookByStack(Context context) { 51 | boolean isHook = false; 52 | try { 53 | throw new Exception("blah"); 54 | } catch (Exception e) { 55 | int zygoteInitCallCount = 0; 56 | for (StackTraceElement stackTraceElement : e.getStackTrace()) { 57 | if (stackTraceElement.getClassName().equals("com.android.internal.os.ZygoteInit")) { 58 | zygoteInitCallCount++; 59 | if (zygoteInitCallCount == 2) { 60 | Log.wtf("HookDetection", "Substrate is active on the device."); 61 | isHook = true; 62 | } 63 | } 64 | if (stackTraceElement.getClassName().equals("com.saurik.substrate.MS$2") && 65 | stackTraceElement.getMethodName().equals("invoked")) { 66 | Log.wtf("HookDetection", "A method on the stack trace has been hooked using Substrate."); 67 | isHook = true; 68 | } 69 | if (stackTraceElement.getClassName().equals("de.robv.android.xposed.XposedBridge") && 70 | stackTraceElement.getMethodName().equals("main")) { 71 | Log.wtf("HookDetection", "Xposed is active on the device."); 72 | isHook = true; 73 | } 74 | if (stackTraceElement.getClassName().equals("de.robv.android.xposed.XposedBridge") && 75 | stackTraceElement.getMethodName().equals("handleHookedMethod")) { 76 | Log.wtf("HookDetection", "A method on the stack trace has been hooked using Xposed."); 77 | isHook = true; 78 | } 79 | 80 | } 81 | } 82 | return isHook; 83 | } 84 | 85 | public static boolean isHookByJar() { 86 | boolean isHook = false; 87 | try { 88 | Set libraries = new HashSet(); 89 | String mapsFilename = "/proc/" + android.os.Process.myPid() + "/maps"; 90 | BufferedReader reader = new BufferedReader(new FileReader(mapsFilename)); 91 | String line; 92 | while ((line = reader.readLine()) != null) { 93 | if (line.endsWith(".so") || line.endsWith(".jar")) { 94 | int n = line.lastIndexOf(" "); 95 | libraries.add(line.substring(n + 1)); 96 | } 97 | } 98 | for (String library : libraries) { 99 | if (library.contains("com.saurik.substrate")) { 100 | Log.wtf("HookDetection", "Substrate shared object found: " + library); 101 | isHook = true; 102 | } 103 | if (library.contains("XposedBridge.jar")) { 104 | Log.wtf("HookDetection", "Xposed JAR found: " + library); 105 | isHook = true; 106 | } 107 | } 108 | reader.close(); 109 | } catch (Exception e) { 110 | Log.wtf("HookDetection", e.toString()); 111 | } 112 | return isHook; 113 | } 114 | } -------------------------------------------------------------------------------- /app/src/main/java/com/greens1995/myapplication/CheckRoot.java: -------------------------------------------------------------------------------- 1 | package com.greens1995.myapplication; 2 | 3 | import android.util.Log; 4 | 5 | import java.io.BufferedReader; 6 | import java.io.BufferedWriter; 7 | import java.io.ByteArrayOutputStream; 8 | import java.io.DataOutputStream; 9 | import java.io.File; 10 | import java.io.FileInputStream; 11 | import java.io.FileOutputStream; 12 | import java.io.InputStreamReader; 13 | import java.io.OutputStreamWriter; 14 | import java.util.ArrayList; 15 | 16 | /** 17 | * Created by Ben on 2018/1/29. 18 | */ 19 | 20 | public class CheckRoot { 21 | private static String LOG_TAG = CheckRoot.class.getName(); 22 | 23 | public static boolean isDeviceRooted() { 24 | if (checkDeviceDebuggable()) { 25 | return true; 26 | }//check buildTags 27 | if (checkSuperuserApk()) { 28 | return true; 29 | }//Superuser.apk 30 | if (checkRootPathSU()) { 31 | return true; 32 | }//find su in some path 33 | if (checkRootWhichSU()){return true;}//find su use 'which' 34 | // if (checkBusybox()) { 35 | // return true; 36 | // }//find su use 'which' 37 | if (checkAccessRootData()) { 38 | return true; 39 | }//find su use 'which' 40 | // if (checkGetRootAuth()) { 41 | // return true; 42 | // }//exec su 43 | 44 | return false; 45 | } 46 | 47 | public static boolean checkDeviceDebuggable() { 48 | String buildTags = android.os.Build.TAGS; 49 | if (buildTags != null && buildTags.contains("test-keys")) { 50 | Log.i(LOG_TAG, "buildTags=" + buildTags); 51 | return true; 52 | } 53 | return false; 54 | } 55 | 56 | public static boolean checkSuperuserApk() { 57 | try { 58 | File file = new File("/system/app/Superuser.apk"); 59 | if (file.exists()) { 60 | Log.i(LOG_TAG, "/system/app/Superuser.apk exist"); 61 | return true; 62 | } 63 | } catch (Exception e) { 64 | } 65 | return false; 66 | } 67 | 68 | public static boolean checkRootPathSU() { 69 | File f = null; 70 | final String kSuSearchPaths[] = {"/system/bin/", "/system/xbin/", "/system/sbin/", "/sbin/", "/vendor/bin/"}; 71 | try { 72 | for (int i = 0; i < kSuSearchPaths.length; i++) { 73 | f = new File(kSuSearchPaths[i] + "su"); 74 | if (f != null && f.exists()) { 75 | Log.i(LOG_TAG, "find su in : " + kSuSearchPaths[i]); 76 | return true; 77 | } 78 | } 79 | } catch (Exception e) { 80 | e.printStackTrace(); 81 | } 82 | return false; 83 | } 84 | 85 | public static boolean checkRootWhichSU() { 86 | String[] strCmd = new String[]{"/system/xbin/which", "su"}; 87 | ArrayList execResult = executeCommand(strCmd); 88 | if (execResult != null) { 89 | Log.i(LOG_TAG, "execResult=" + execResult.toString()); 90 | return true; 91 | } else { 92 | Log.i(LOG_TAG, "execResult=null"); 93 | return false; 94 | } 95 | } 96 | 97 | public static ArrayList executeCommand(String[] shellCmd) { 98 | String line = null; 99 | ArrayList fullResponse = new ArrayList(); 100 | Process localProcess = null; 101 | try { 102 | Log.i(LOG_TAG, "to shell exec which for find su :"); 103 | localProcess = Runtime.getRuntime().exec(shellCmd); 104 | } catch (Exception e) { 105 | return null; 106 | } 107 | BufferedWriter out = new BufferedWriter(new OutputStreamWriter(localProcess.getOutputStream())); 108 | BufferedReader in = new BufferedReader(new InputStreamReader(localProcess.getInputStream())); 109 | try { 110 | while ((line = in.readLine()) != null) { 111 | Log.i(LOG_TAG, "–> Line received: " + line); 112 | fullResponse.add(line); 113 | } 114 | } catch (Exception e) { 115 | e.printStackTrace(); 116 | } 117 | Log.i(LOG_TAG, "–> Full response was: " + fullResponse); 118 | return fullResponse; 119 | } 120 | 121 | public static synchronized boolean checkGetRootAuth() { 122 | Process process = null; 123 | DataOutputStream os = null; 124 | try { 125 | Log.i(LOG_TAG, "to exec su"); 126 | process = Runtime.getRuntime().exec("su"); 127 | os = new DataOutputStream(process.getOutputStream()); 128 | os.writeBytes("exit\n"); 129 | os.flush(); 130 | int exitValue = process.waitFor(); 131 | Log.i(LOG_TAG, "exitValue=" + exitValue); 132 | if (exitValue == 0) { 133 | return true; 134 | } else { 135 | return false; 136 | } 137 | } catch (Exception e) { 138 | Log.i(LOG_TAG, "Unexpected error - Here is what I know: " 139 | + e.getMessage()); 140 | return false; 141 | } finally { 142 | try { 143 | if (os != null) { 144 | os.close(); 145 | } 146 | process.destroy(); 147 | } catch (Exception e) { 148 | e.printStackTrace(); 149 | } 150 | } 151 | } 152 | 153 | public static synchronized boolean checkBusybox() { 154 | try { 155 | Log.i(LOG_TAG, "to exec busybox df"); 156 | String[] strCmd = new String[]{"busybox", "df"}; 157 | ArrayList execResult = executeCommand(strCmd); 158 | if (execResult != null) { 159 | Log.i(LOG_TAG, "execResult=" + execResult.toString()); 160 | return true; 161 | } else { 162 | Log.i(LOG_TAG, "execResult=null"); 163 | return false; 164 | } 165 | } catch (Exception e) { 166 | Log.i(LOG_TAG, "Unexpected error - Here is what I know: " 167 | + e.getMessage()); 168 | return false; 169 | } 170 | } 171 | 172 | public static synchronized boolean checkAccessRootData() { 173 | try { 174 | Log.i(LOG_TAG, "to write /data"); 175 | String fileContent = "test_ok"; 176 | Boolean writeFlag = writeFile("/data/su_test", fileContent); 177 | if (writeFlag) { 178 | Log.i(LOG_TAG, "write ok"); 179 | } else { 180 | Log.i(LOG_TAG, "write failed"); 181 | } 182 | 183 | Log.i(LOG_TAG, "to read /data"); 184 | String strRead = readFile("/data/su_test"); 185 | Log.i(LOG_TAG, "strRead=" + strRead); 186 | if (fileContent.equals(strRead)) { 187 | return true; 188 | } else { 189 | return false; 190 | } 191 | } catch (Exception e) { 192 | Log.i(LOG_TAG, "Unexpected error - Here is what I know: " 193 | + e.getMessage()); 194 | return false; 195 | } 196 | } 197 | 198 | //写文件 199 | public static Boolean writeFile(String fileName, String message) { 200 | try { 201 | FileOutputStream fout = new FileOutputStream(fileName); 202 | byte[] bytes = message.getBytes(); 203 | fout.write(bytes); 204 | fout.close(); 205 | return true; 206 | } catch (Exception e) { 207 | e.printStackTrace(); 208 | return false; 209 | } 210 | } 211 | 212 | //读文件 213 | public static String readFile(String fileName) { 214 | File file = new File(fileName); 215 | try { 216 | FileInputStream fis = new FileInputStream(file); 217 | byte[] bytes = new byte[1024]; 218 | ByteArrayOutputStream bos = new ByteArrayOutputStream(); 219 | int len; 220 | while ((len = fis.read(bytes)) > 0) { 221 | bos.write(bytes, 0, len); 222 | } 223 | String result = new String(bos.toByteArray()); 224 | Log.i(LOG_TAG, result); 225 | return result; 226 | } catch (Exception e) { 227 | e.printStackTrace(); 228 | return null; 229 | } 230 | } 231 | } 232 | -------------------------------------------------------------------------------- /app/src/main/java/com/greens1995/myapplication/CheckVirtual.java: -------------------------------------------------------------------------------- 1 | package com.greens1995.myapplication; 2 | 3 | import java.io.BufferedInputStream; 4 | import java.io.BufferedOutputStream; 5 | import java.io.File; 6 | import java.io.IOException; 7 | import java.util.Locale; 8 | 9 | /** 10 | * Created by zaratustra on 2017/9/14. 11 | *原repo地址:https://github.com/ZaratustraN/Check_VirtualAPK 12 | */ 13 | public class CheckVirtual { 14 | 15 | private static final String TAG = "CheckVirtual"; 16 | 17 | public static boolean isRunInVirtual() { 18 | 19 | String filter = getUidStrFormat(); 20 | if (filter == null || filter.length() == 0){ 21 | return false; 22 | } 23 | 24 | String result = exec("ps"); 25 | if (result == null || result.isEmpty()) { 26 | return false; 27 | } 28 | 29 | String[] lines = result.split("\n"); 30 | if (lines == null || lines.length <= 0) { 31 | return false; 32 | } 33 | 34 | int exitDirCount = 0; 35 | 36 | for (int i = 0; i < lines.length; i++) { 37 | if (lines[i].contains(filter)) { 38 | int pkgStartIndex = lines[i].lastIndexOf(" "); 39 | String processName = lines[i].substring(pkgStartIndex <= 0 40 | ? 0 : pkgStartIndex + 1, lines[i].length()); 41 | File dataFile = new File(String.format("/data/data/%s", 42 | processName, Locale.CHINA)); 43 | if (dataFile.exists()) { 44 | exitDirCount++; 45 | } 46 | } 47 | } 48 | 49 | return exitDirCount > 1; 50 | } 51 | 52 | 53 | private static String exec(String command) { 54 | BufferedOutputStream bufferedOutputStream = null; 55 | BufferedInputStream bufferedInputStream = null; 56 | Process process = null; 57 | try { 58 | process = Runtime.getRuntime().exec("sh"); 59 | bufferedOutputStream = new BufferedOutputStream(process.getOutputStream()); 60 | 61 | bufferedInputStream = new BufferedInputStream(process.getInputStream()); 62 | bufferedOutputStream.write(command.getBytes()); 63 | bufferedOutputStream.write('\n'); 64 | bufferedOutputStream.flush(); 65 | bufferedOutputStream.close(); 66 | 67 | process.waitFor(); 68 | 69 | String outputStr = getStrFromBufferInputSteam(bufferedInputStream); 70 | return outputStr; 71 | } catch (Exception e) { 72 | return null; 73 | } finally { 74 | if (bufferedOutputStream != null) { 75 | try { 76 | bufferedOutputStream.close(); 77 | } catch (IOException e) { 78 | e.printStackTrace(); 79 | } 80 | } 81 | if (bufferedInputStream != null) { 82 | try { 83 | bufferedInputStream.close(); 84 | } catch (IOException e) { 85 | e.printStackTrace(); 86 | } 87 | } 88 | if (process != null) { 89 | process.destroy(); 90 | } 91 | } 92 | } 93 | 94 | private static String getStrFromBufferInputSteam(BufferedInputStream bufferedInputStream) { 95 | if (null == bufferedInputStream) { 96 | return ""; 97 | } 98 | int BUFFER_SIZE = 512; 99 | byte[] buffer = new byte[BUFFER_SIZE]; 100 | StringBuilder result = new StringBuilder(); 101 | try { 102 | while (true) { 103 | int read = bufferedInputStream.read(buffer); 104 | if (read > 0) { 105 | result.append(new String(buffer, 0, read)); 106 | } 107 | if (read < BUFFER_SIZE) { 108 | break; 109 | } 110 | } 111 | } catch (Exception e) { 112 | e.printStackTrace(); 113 | } 114 | return result.toString(); 115 | } 116 | 117 | public static String getUidStrFormat() { 118 | String filter = exec("cat /proc/self/cgroup"); 119 | if (filter == null || filter.length() == 0){ 120 | return null; 121 | } 122 | 123 | int uidStartIndex = filter.lastIndexOf("uid"); 124 | int uidEndIndex = filter.lastIndexOf("/pid"); 125 | if (uidStartIndex < 0) { 126 | return null; 127 | } 128 | if (uidEndIndex<=0){ 129 | uidEndIndex = filter.length(); 130 | } 131 | 132 | filter = filter.substring(uidStartIndex + 4, uidEndIndex); 133 | try { 134 | String strUid = filter.replaceAll("\n", ""); 135 | if (isNumericZidai(strUid)){ 136 | int uid = Integer.valueOf(strUid); 137 | filter = String.format("u0_a%d", uid - 10000); 138 | return filter; 139 | } 140 | return null; 141 | } catch (Exception e) { 142 | e.printStackTrace(); 143 | return null; 144 | } 145 | } 146 | 147 | public static boolean isNumericZidai(String str) { 148 | if (str == null || str.length() == 0){ 149 | return false; 150 | } 151 | for (int i = 0; i < str.length(); i++) { 152 | if (!Character.isDigit(str.charAt(i))) { 153 | return false; 154 | } 155 | } 156 | return true; 157 | } 158 | } -------------------------------------------------------------------------------- /app/src/main/java/com/greens1995/myapplication/EmulatorDetector.java: -------------------------------------------------------------------------------- 1 | package com.greens1995.myapplication; 2 | 3 | import android.content.Context; 4 | import android.os.Build; 5 | import android.os.Environment; 6 | import android.util.Log; 7 | 8 | import java.io.File; 9 | import java.lang.reflect.Method; 10 | 11 | /** 12 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 13 | * the License. You may obtain a copy of the License at 14 | *

15 | * http://www.apache.org/licenses/LICENSE-2.0 16 | *

17 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 18 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 19 | * specific language governing permissions and limitations under the License. 20 | *

21 | * Copyright (C) 2013, Vladislav Gingo Skoumal (http://www.skoumal.net) 22 | * 在原Repo基础上增加了一些判定 23 | */ 24 | public class EmulatorDetector { 25 | 26 | private static final String TAG = "EmulatorDetector"; 27 | 28 | private static int rating = -1; 29 | 30 | public static boolean isEmulatorAbsoluly(Context context) { 31 | 32 | if (mayOnEmulatorViaQEMU(context)) { 33 | return true; 34 | } 35 | 36 | if (Build.PRODUCT.contains("sdk") || 37 | Build.PRODUCT.contains("sdk_x86") || 38 | Build.PRODUCT.contains("sdk_google") || 39 | Build.PRODUCT.contains("Andy") || 40 | Build.PRODUCT.contains("Droid4X") || 41 | Build.PRODUCT.contains("nox") || 42 | Build.PRODUCT.contains("vbox86p") || 43 | Build.PRODUCT.contains("aries")) { 44 | return true; 45 | } 46 | if (Build.MANUFACTURER.equals("Genymotion") || 47 | Build.MANUFACTURER.contains("Andy") || 48 | Build.MANUFACTURER.contains("nox") || 49 | Build.MANUFACTURER.contains("TiantianVM")) { 50 | return true; 51 | } 52 | if (Build.BRAND.contains("Andy")) { 53 | return true; 54 | } 55 | if (Build.DEVICE.contains("Andy") || 56 | Build.DEVICE.contains("Droid4X") || 57 | Build.DEVICE.contains("nox") || 58 | Build.DEVICE.contains("vbox86p") || 59 | Build.DEVICE.contains("aries")) { 60 | return true; 61 | } 62 | if (Build.MODEL.contains("Emulator") || 63 | Build.MODEL.equals("google_sdk") || 64 | Build.MODEL.contains("Droid4X") || 65 | Build.MODEL.contains("TiantianVM") || 66 | Build.MODEL.contains("Andy") || 67 | Build.MODEL.equals("Android SDK built for x86_64") || 68 | Build.MODEL.equals("Android SDK built for x86")) { 69 | return true; 70 | } 71 | if (Build.HARDWARE.equals("vbox86") || 72 | Build.HARDWARE.contains("nox") || 73 | Build.HARDWARE.contains("ttVM_x86")) { 74 | return true; 75 | } 76 | if (Build.FINGERPRINT.contains("generic/sdk/generic") || 77 | Build.FINGERPRINT.contains("generic_x86/sdk_x86/generic_x86") || 78 | Build.FINGERPRINT.contains("Andy") || 79 | Build.FINGERPRINT.contains("ttVM_Hdragon") || 80 | Build.FINGERPRINT.contains("generic/google_sdk/generic") || 81 | Build.FINGERPRINT.contains("vbox86p") || 82 | Build.FINGERPRINT.contains("generic/vbox86p/vbox86p")) { 83 | return true; 84 | } 85 | 86 | return false; 87 | } 88 | 89 | /** 90 | * Detects if app is currenly running on emulator, or real device. 91 | * 92 | * @return true for emulator, false for real devices 93 | */ 94 | public static boolean isEmulator(Context context) { 95 | if (isEmulatorAbsoluly(context)) { 96 | return true; 97 | } 98 | int newRating = 0; 99 | if (rating < 0) { 100 | if (Build.PRODUCT.contains("sdk") || 101 | Build.PRODUCT.contains("Andy") || 102 | Build.PRODUCT.contains("ttVM_Hdragon") || 103 | Build.PRODUCT.contains("google_sdk") || 104 | Build.PRODUCT.contains("Droid4X") || 105 | Build.PRODUCT.contains("nox") || 106 | Build.PRODUCT.contains("sdk_x86") || 107 | Build.PRODUCT.contains("sdk_google") || 108 | Build.PRODUCT.contains("vbox86p")|| 109 | Build.PRODUCT.contains("aries")) { 110 | newRating++; 111 | } 112 | 113 | if (Build.MANUFACTURER.equals("unknown") || 114 | Build.MANUFACTURER.equals("Genymotion") || 115 | Build.MANUFACTURER.contains("Andy") || 116 | Build.MANUFACTURER.contains("MIT") || 117 | Build.MANUFACTURER.contains("nox") || 118 | Build.MANUFACTURER.contains("TiantianVM")) { 119 | newRating++; 120 | } 121 | 122 | if (Build.BRAND.equals("generic") || 123 | Build.BRAND.equals("generic_x86") || 124 | Build.BRAND.equals("TTVM") || 125 | Build.BRAND.contains("Andy")) { 126 | newRating++; 127 | } 128 | 129 | if (Build.DEVICE.contains("generic") || 130 | Build.DEVICE.contains("generic_x86") || 131 | Build.DEVICE.contains("Andy") || 132 | Build.DEVICE.contains("ttVM_Hdragon") || 133 | Build.DEVICE.contains("Droid4X") || 134 | Build.DEVICE.contains("nox") || 135 | Build.DEVICE.contains("generic_x86_64") || 136 | Build.DEVICE.contains("vbox86p")|| 137 | Build.DEVICE.contains("aries")) { 138 | newRating++; 139 | } 140 | 141 | if (Build.MODEL.equals("sdk") || 142 | Build.MODEL.contains("Emulator") || 143 | Build.MODEL.equals("google_sdk") || 144 | Build.MODEL.contains("Droid4X") || 145 | Build.MODEL.contains("TiantianVM") || 146 | Build.MODEL.contains("Andy") || 147 | Build.MODEL.equals("Android SDK built for x86_64") || 148 | Build.MODEL.equals("Android SDK built for x86")) { 149 | newRating++; 150 | } 151 | 152 | if (Build.HARDWARE.equals("goldfish") || 153 | Build.HARDWARE.equals("vbox86") || 154 | Build.HARDWARE.contains("nox") || 155 | Build.HARDWARE.contains("ttVM_x86")) { 156 | newRating++; 157 | } 158 | 159 | if (Build.FINGERPRINT.contains("generic/sdk/generic") || 160 | Build.FINGERPRINT.contains("generic_x86/sdk_x86/generic_x86") || 161 | Build.FINGERPRINT.contains("Andy") || 162 | Build.FINGERPRINT.contains("ttVM_Hdragon") || 163 | Build.FINGERPRINT.contains("generic_x86_64") || 164 | Build.FINGERPRINT.contains("generic/google_sdk/generic") || 165 | Build.FINGERPRINT.contains("vbox86p") || 166 | Build.FINGERPRINT.contains("generic/vbox86p/vbox86p")) { 167 | newRating++; 168 | } 169 | 170 | try { 171 | String opengl = android.opengl.GLES20.glGetString(android.opengl.GLES20.GL_RENDERER); 172 | if (opengl != null) { 173 | if (opengl.contains("Bluestacks") || 174 | opengl.contains("Translator") 175 | ) { 176 | newRating += 10; 177 | } 178 | } 179 | } catch (Exception e) { 180 | e.printStackTrace(); 181 | } 182 | 183 | try { 184 | File sharedFolder = new File(Environment 185 | .getExternalStorageDirectory().toString() 186 | + File.separatorChar 187 | + "windows" 188 | + File.separatorChar 189 | + "BstSharedFolder"); 190 | 191 | if (sharedFolder.exists()) { 192 | newRating += 10; 193 | } 194 | } catch (Exception e) { 195 | e.printStackTrace(); 196 | } 197 | rating = newRating; 198 | } 199 | return rating > 3;//不能再少了,否则有可能误判,若增减了新的嫌疑度判定属性,要重新评估该值 200 | } 201 | 202 | 203 | private static final boolean mayOnEmulatorViaQEMU(Context context) { 204 | String qemu = getProp(context, "ro.kernel.qemu"); 205 | return "1".equals(qemu); 206 | } 207 | 208 | // /** 209 | // * 有权限可以打开这个判断 210 | // * @param context 211 | // * @return 212 | // */ 213 | // private static final boolean mayOnEmulatorViaTelephonyDeviceId(Context context) { 214 | // TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); 215 | // if (tm == null) { 216 | // return false; 217 | // } 218 | // 219 | // String deviceId = tm.getDeviceId(); 220 | // if (TextUtils.isEmpty(deviceId)) { 221 | // return false; 222 | // } 223 | // 224 | // /** 225 | // * device id of telephony likes '0*' 226 | // */ 227 | // for (int i = 0; i < deviceId.length(); i++) { 228 | // if (deviceId.charAt(i) != '0') { 229 | // return false; 230 | // } 231 | // } 232 | // 233 | // return true; 234 | // } 235 | 236 | /** 237 | * Returns string with human-readable listing of Build.* parameters used in {@link #isEmulator()} method. 238 | * 239 | * @return all involved Build.* parameters and its values 240 | */ 241 | public static String getDeviceListing() { 242 | return "Build.PRODUCT: " + Build.PRODUCT + "\n" + 243 | "Build.MANUFACTURER: " + Build.MANUFACTURER + "\n" + 244 | "Build.BRAND: " + Build.BRAND + "\n" + 245 | "Build.DEVICE: " + Build.DEVICE + "\n" + 246 | "Build.MODEL: " + Build.MODEL + "\n" + 247 | "Build.HARDWARE: " + Build.HARDWARE + "\n" + 248 | "Build.FINGERPRINT: " + Build.FINGERPRINT + "\n" + 249 | "Build.TAGS: " + Build.TAGS + "\n" + 250 | "GL_RENDERER: " + android.opengl.GLES20.glGetString(android.opengl.GLES20.GL_RENDERER) + "\n" + 251 | "GL_VENDOR: " + android.opengl.GLES20.glGetString(android.opengl.GLES20.GL_VENDOR) + "\n" + 252 | "GL_VERSION: " + android.opengl.GLES20.glGetString(android.opengl.GLES20.GL_VERSION) + "\n" + 253 | "GL_EXTENSIONS: " + android.opengl.GLES20.glGetString(android.opengl.GLES20.GL_EXTENSIONS) + "\n"; 254 | } 255 | 256 | 257 | private static final String getProp(Context context, String property) { 258 | try { 259 | ClassLoader cl = context.getClassLoader(); 260 | Class SystemProperties = cl.loadClass("android.os.SystemProperties"); 261 | Method method = SystemProperties.getMethod("get", String.class); 262 | Object[] params = new Object[1]; 263 | params[0] = property; 264 | return (String) method.invoke(SystemProperties, params); 265 | } catch (Exception e) { 266 | return null; 267 | } 268 | } 269 | 270 | /** 271 | * Prints all Build.* parameters used in {@link #isEmulator()} method to logcat. 272 | */ 273 | public static void logcat() { 274 | Log.d(TAG, getDeviceListing()); 275 | } 276 | 277 | } 278 | -------------------------------------------------------------------------------- /app/src/main/java/com/greens1995/myapplication/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.greens1995.myapplication; 2 | 3 | import android.os.Bundle; 4 | import android.support.v7.app.AppCompatActivity; 5 | import android.widget.TextView; 6 | 7 | public class MainActivity extends AppCompatActivity { 8 | 9 | @Override 10 | protected void onCreate(Bundle savedInstanceState) { 11 | super.onCreate(savedInstanceState); 12 | setContentView(R.layout.activity_main); 13 | boolean isHook = CheckHook.isHook(this); 14 | boolean isRoot = CheckRoot.isDeviceRooted(); 15 | boolean isVirtual = CheckVirtual.isRunInVirtual(); 16 | boolean isEmulator = EmulatorDetector.isEmulator(this); 17 | ((TextView) findViewById(R.id.text)).setText("is virtual " + isVirtual + ",isHook " + isHook + ",isRoot " + isRoot+ ",isEmulator " +isEmulator); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /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 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Labmem003/anti-counterfeit-android/3c4061f09a2b767690e748584fb7ac44c95d5c3c/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Labmem003/anti-counterfeit-android/3c4061f09a2b767690e748584fb7ac44c95d5c3c/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Labmem003/anti-counterfeit-android/3c4061f09a2b767690e748584fb7ac44c95d5c3c/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Labmem003/anti-counterfeit-android/3c4061f09a2b767690e748584fb7ac44c95d5c3c/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Labmem003/anti-counterfeit-android/3c4061f09a2b767690e748584fb7ac44c95d5c3c/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Labmem003/anti-counterfeit-android/3c4061f09a2b767690e748584fb7ac44c95d5c3c/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Labmem003/anti-counterfeit-android/3c4061f09a2b767690e748584fb7ac44c95d5c3c/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Labmem003/anti-counterfeit-android/3c4061f09a2b767690e748584fb7ac44c95d5c3c/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Labmem003/anti-counterfeit-android/3c4061f09a2b767690e748584fb7ac44c95d5c3c/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Labmem003/anti-counterfeit-android/3c4061f09a2b767690e748584fb7ac44c95d5c3c/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Detect Result 3 | 4 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/test/java/com/greens1995/myapplication/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.greens1995.myapplication; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() throws Exception { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | 5 | repositories { 6 | google() 7 | jcenter() 8 | } 9 | dependencies { 10 | classpath 'com.android.tools.build:gradle:3.0.0' 11 | 12 | 13 | // NOTE: Do not place your application dependencies here; they belong 14 | // in the individual module build.gradle files 15 | } 16 | } 17 | 18 | allprojects { 19 | repositories { 20 | google() 21 | jcenter() 22 | } 23 | } 24 | 25 | task clean(type: Delete) { 26 | delete rootProject.buildDir 27 | } 28 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | org.gradle.jvmargs=-Xmx1536m 13 | 14 | # When configured, Gradle will run in incubating parallel mode. 15 | # This option should only be used with decoupled projects. More details, visit 16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 17 | # org.gradle.parallel=true 18 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Labmem003/anti-counterfeit-android/3c4061f09a2b767690e748584fb7ac44c95d5c3c/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Tue Jan 30 17:28:58 CST 2018 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | --------------------------------------------------------------------------------