Repository: daizhongyin/SecuritySDK Branch: master Commit: cbfd1337777b Files: 383 Total size: 3.1 MB Directory structure: gitextract_eqr7fb5n/ ├── .gradle/ │ └── buildOutputCleanup/ │ └── cache.properties ├── .idea/ │ ├── SecuritySDK.iml │ ├── gradle.xml │ ├── libraries/ │ │ ├── android_arch_core_common_1_0_0_jar.xml │ │ ├── android_arch_lifecycle_common_1_0_0_jar.xml │ │ ├── android_arch_lifecycle_runtime_1_0_0.xml │ │ ├── com_alibaba_fastjson_1_2_42_jar.xml │ │ ├── com_android_support_animated_vector_drawable_26_1_0.xml │ │ ├── com_android_support_appcompat_v7_26_1_0.xml │ │ ├── com_android_support_constraint_constraint_layout_1_0_2.xml │ │ ├── com_android_support_constraint_constraint_layout_solver_1_0_2_jar.xml │ │ ├── com_android_support_support_annotations_26_1_0_jar.xml │ │ ├── com_android_support_support_compat_26_1_0.xml │ │ ├── com_android_support_support_core_ui_26_1_0.xml │ │ ├── com_android_support_support_core_utils_26_1_0.xml │ │ ├── com_android_support_support_fragment_26_1_0.xml │ │ ├── com_android_support_support_media_compat_26_1_0.xml │ │ ├── com_android_support_support_v4_26_1_0.xml │ │ ├── com_android_support_support_vector_drawable_26_1_0.xml │ │ ├── com_android_support_test_espresso_espresso_core_2_2_2.xml │ │ ├── com_android_support_test_espresso_espresso_idling_resource_2_2_2.xml │ │ ├── com_android_support_test_exposed_instrumentation_api_publish_0_5.xml │ │ ├── com_android_support_test_rules_0_5.xml │ │ ├── com_android_support_test_runner_0_5.xml │ │ ├── com_github_lzyzsd_jsbridge_1_0_4.xml │ │ ├── com_google_code_findbugs_jsr305_2_0_1_jar.xml │ │ ├── com_google_code_gson_gson_2_3_1_jar.xml │ │ ├── com_squareup_javawriter_2_1_1_jar.xml │ │ ├── javax_annotation_javax_annotation_api_1_2_jar.xml │ │ ├── javax_inject_javax_inject_1_jar.xml │ │ ├── junit_junit_4_12_jar.xml │ │ ├── org_hamcrest_hamcrest_core_1_3_jar.xml │ │ ├── org_hamcrest_hamcrest_integration_1_3_jar.xml │ │ └── org_hamcrest_hamcrest_library_1_3_jar.xml │ ├── misc.xml │ ├── modules.xml │ ├── runConfigurations.xml │ ├── vcs.xml │ └── workspace.xml ├── README.md ├── SecuritySDK-master.iml ├── SecuritySDK.iml ├── app/ │ ├── .gitignore │ ├── CMakeLists.txt │ ├── app.iml │ ├── build.gradle │ ├── config.json │ ├── proguard-rules.pro │ └── src/ │ ├── androidTest/ │ │ └── java/ │ │ └── com/ │ │ └── nstl/ │ │ └── securitysdk/ │ │ └── ExampleInstrumentedTest.java │ ├── main/ │ │ ├── AndroidManifest.xml │ │ ├── aidl/ │ │ │ └── com/ │ │ │ └── nstl/ │ │ │ └── securitysdk/ │ │ │ └── IMyAidlInterface.aidl │ │ ├── cpp/ │ │ │ └── native-lib.cpp │ │ ├── java/ │ │ │ └── com/ │ │ │ └── nstl/ │ │ │ └── securitysdk/ │ │ │ ├── MainActivity.java │ │ │ └── MyService.java │ │ └── res/ │ │ ├── layout/ │ │ │ └── activity_main.xml │ │ └── values/ │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test/ │ └── java/ │ └── com/ │ └── nstl/ │ └── securitysdk/ │ └── ExampleUnitTest.java ├── build/ │ └── android-profile/ │ ├── profile-2018-02-05-23-35-46-154.json │ ├── profile-2018-02-05-23-35-46-154.rawproto │ ├── profile-2018-02-05-23-36-23-899.json │ ├── profile-2018-02-05-23-36-23-899.rawproto │ ├── profile-2018-02-05-23-37-09-255.json │ ├── profile-2018-02-05-23-37-09-255.rawproto │ ├── profile-2018-02-05-23-37-44-297.json │ ├── profile-2018-02-05-23-37-44-297.rawproto │ ├── profile-2018-02-05-23-38-38-486.json │ ├── profile-2018-02-05-23-38-38-486.rawproto │ ├── profile-2018-02-05-23-39-52-843.json │ ├── profile-2018-02-05-23-39-52-843.rawproto │ ├── profile-2018-02-05-23-42-15-823.json │ ├── profile-2018-02-05-23-42-15-823.rawproto │ ├── profile-2018-02-05-23-42-59-896.json │ ├── profile-2018-02-05-23-42-59-896.rawproto │ ├── profile-2018-04-20-22-26-30-303.json │ ├── profile-2018-04-20-22-26-30-303.rawproto │ ├── profile-2018-04-20-22-26-52-129.json │ ├── profile-2018-04-20-22-26-52-129.rawproto │ ├── profile-2018-04-20-22-28-07-451.json │ ├── profile-2018-04-20-22-28-07-451.rawproto │ ├── profile-2018-04-20-22-29-40-882.json │ ├── profile-2018-04-20-22-29-40-882.rawproto │ ├── profile-2018-04-20-22-30-01-056.json │ ├── profile-2018-04-20-22-30-01-056.rawproto │ ├── profile-2018-04-20-22-30-41-698.json │ ├── profile-2018-04-20-22-30-41-698.rawproto │ ├── profile-2018-04-20-22-31-58-460.json │ ├── profile-2018-04-20-22-31-58-460.rawproto │ ├── profile-2018-04-20-22-59-44-709.json │ ├── profile-2018-04-20-22-59-44-709.rawproto │ ├── profile-2018-04-20-23-01-41-661.json │ └── profile-2018-04-20-23-01-41-661.rawproto ├── build.gradle ├── docs/ │ ├── BinderSecurityUtil.md │ ├── IntentUriSchemeFilter.md │ ├── PluginInvokeValidate.md │ ├── SafeZipFile.md │ ├── jarSignatureVerify.md │ ├── safeupgrade.md │ └── safewebview.md ├── gradle/ │ └── wrapper/ │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradle.properties ├── gradlew ├── gradlew.bat ├── local.properties ├── securitysdkcore/ │ ├── .externalNativeBuild/ │ │ └── cmake/ │ │ ├── debug/ │ │ │ ├── arm64-v8a/ │ │ │ │ ├── CMakeCache.txt │ │ │ │ ├── CMakeFiles/ │ │ │ │ │ ├── 3.6.0-rc2/ │ │ │ │ │ │ ├── CMakeCCompiler.cmake │ │ │ │ │ │ ├── CMakeCXXCompiler.cmake │ │ │ │ │ │ └── CMakeSystem.cmake │ │ │ │ │ ├── CMakeOutput.log │ │ │ │ │ ├── TargetDirectories.txt │ │ │ │ │ ├── cmake.check_cache │ │ │ │ │ ├── feature_tests.c │ │ │ │ │ └── feature_tests.cxx │ │ │ │ ├── android_gradle_build.json │ │ │ │ ├── build.ninja │ │ │ │ ├── cmake_build_command.txt │ │ │ │ ├── cmake_build_output.txt │ │ │ │ ├── cmake_install.cmake │ │ │ │ └── rules.ninja │ │ │ ├── armeabi/ │ │ │ │ ├── CMakeCache.txt │ │ │ │ ├── CMakeFiles/ │ │ │ │ │ ├── 3.6.0-rc2/ │ │ │ │ │ │ ├── CMakeCCompiler.cmake │ │ │ │ │ │ ├── CMakeCXXCompiler.cmake │ │ │ │ │ │ └── CMakeSystem.cmake │ │ │ │ │ ├── CMakeOutput.log │ │ │ │ │ ├── TargetDirectories.txt │ │ │ │ │ ├── cmake.check_cache │ │ │ │ │ ├── feature_tests.c │ │ │ │ │ └── feature_tests.cxx │ │ │ │ ├── android_gradle_build.json │ │ │ │ ├── build.ninja │ │ │ │ ├── cmake_build_command.txt │ │ │ │ ├── cmake_build_output.txt │ │ │ │ ├── cmake_install.cmake │ │ │ │ └── rules.ninja │ │ │ ├── armeabi-v7a/ │ │ │ │ ├── CMakeCache.txt │ │ │ │ ├── CMakeFiles/ │ │ │ │ │ ├── 3.6.0-rc2/ │ │ │ │ │ │ ├── CMakeCCompiler.cmake │ │ │ │ │ │ ├── CMakeCXXCompiler.cmake │ │ │ │ │ │ └── CMakeSystem.cmake │ │ │ │ │ ├── CMakeOutput.log │ │ │ │ │ ├── TargetDirectories.txt │ │ │ │ │ ├── cmake.check_cache │ │ │ │ │ ├── feature_tests.c │ │ │ │ │ └── feature_tests.cxx │ │ │ │ ├── android_gradle_build.json │ │ │ │ ├── build.ninja │ │ │ │ ├── cmake_build_command.txt │ │ │ │ ├── cmake_build_output.txt │ │ │ │ ├── cmake_install.cmake │ │ │ │ └── rules.ninja │ │ │ ├── mips/ │ │ │ │ ├── CMakeCache.txt │ │ │ │ ├── CMakeFiles/ │ │ │ │ │ ├── 3.6.0-rc2/ │ │ │ │ │ │ ├── CMakeCCompiler.cmake │ │ │ │ │ │ ├── CMakeCXXCompiler.cmake │ │ │ │ │ │ └── CMakeSystem.cmake │ │ │ │ │ ├── CMakeOutput.log │ │ │ │ │ ├── TargetDirectories.txt │ │ │ │ │ ├── cmake.check_cache │ │ │ │ │ ├── feature_tests.c │ │ │ │ │ └── feature_tests.cxx │ │ │ │ ├── android_gradle_build.json │ │ │ │ ├── build.ninja │ │ │ │ ├── cmake_build_command.txt │ │ │ │ ├── cmake_build_output.txt │ │ │ │ ├── cmake_install.cmake │ │ │ │ └── rules.ninja │ │ │ ├── mips64/ │ │ │ │ ├── CMakeCache.txt │ │ │ │ ├── CMakeFiles/ │ │ │ │ │ ├── 3.6.0-rc2/ │ │ │ │ │ │ ├── CMakeCCompiler.cmake │ │ │ │ │ │ ├── CMakeCXXCompiler.cmake │ │ │ │ │ │ └── CMakeSystem.cmake │ │ │ │ │ ├── CMakeOutput.log │ │ │ │ │ ├── TargetDirectories.txt │ │ │ │ │ ├── cmake.check_cache │ │ │ │ │ ├── feature_tests.c │ │ │ │ │ └── feature_tests.cxx │ │ │ │ ├── android_gradle_build.json │ │ │ │ ├── build.ninja │ │ │ │ ├── cmake_build_command.txt │ │ │ │ ├── cmake_build_output.txt │ │ │ │ ├── cmake_install.cmake │ │ │ │ └── rules.ninja │ │ │ ├── x86/ │ │ │ │ ├── CMakeCache.txt │ │ │ │ ├── CMakeFiles/ │ │ │ │ │ ├── 3.6.0-rc2/ │ │ │ │ │ │ ├── CMakeCCompiler.cmake │ │ │ │ │ │ ├── CMakeCXXCompiler.cmake │ │ │ │ │ │ └── CMakeSystem.cmake │ │ │ │ │ ├── CMakeOutput.log │ │ │ │ │ ├── TargetDirectories.txt │ │ │ │ │ ├── cmake.check_cache │ │ │ │ │ ├── feature_tests.c │ │ │ │ │ └── feature_tests.cxx │ │ │ │ ├── android_gradle_build.json │ │ │ │ ├── build.ninja │ │ │ │ ├── cmake_build_command.txt │ │ │ │ ├── cmake_build_output.txt │ │ │ │ ├── cmake_install.cmake │ │ │ │ └── rules.ninja │ │ │ └── x86_64/ │ │ │ ├── CMakeCache.txt │ │ │ ├── CMakeFiles/ │ │ │ │ ├── 3.6.0-rc2/ │ │ │ │ │ ├── CMakeCCompiler.cmake │ │ │ │ │ ├── CMakeCXXCompiler.cmake │ │ │ │ │ └── CMakeSystem.cmake │ │ │ │ ├── CMakeOutput.log │ │ │ │ ├── TargetDirectories.txt │ │ │ │ ├── cmake.check_cache │ │ │ │ ├── feature_tests.c │ │ │ │ └── feature_tests.cxx │ │ │ ├── android_gradle_build.json │ │ │ ├── build.ninja │ │ │ ├── cmake_build_command.txt │ │ │ ├── cmake_build_output.txt │ │ │ ├── cmake_install.cmake │ │ │ └── rules.ninja │ │ └── release/ │ │ ├── arm64-v8a/ │ │ │ ├── CMakeCache.txt │ │ │ ├── CMakeFiles/ │ │ │ │ ├── 3.6.0-rc2/ │ │ │ │ │ ├── CMakeCCompiler.cmake │ │ │ │ │ ├── CMakeCXXCompiler.cmake │ │ │ │ │ └── CMakeSystem.cmake │ │ │ │ ├── CMakeOutput.log │ │ │ │ ├── TargetDirectories.txt │ │ │ │ ├── cmake.check_cache │ │ │ │ ├── feature_tests.c │ │ │ │ └── feature_tests.cxx │ │ │ ├── android_gradle_build.json │ │ │ ├── build.ninja │ │ │ ├── cmake_build_command.txt │ │ │ ├── cmake_build_output.txt │ │ │ ├── cmake_install.cmake │ │ │ └── rules.ninja │ │ ├── armeabi/ │ │ │ ├── CMakeCache.txt │ │ │ ├── CMakeFiles/ │ │ │ │ ├── 3.6.0-rc2/ │ │ │ │ │ ├── CMakeCCompiler.cmake │ │ │ │ │ ├── CMakeCXXCompiler.cmake │ │ │ │ │ └── CMakeSystem.cmake │ │ │ │ ├── CMakeOutput.log │ │ │ │ ├── TargetDirectories.txt │ │ │ │ ├── cmake.check_cache │ │ │ │ ├── feature_tests.c │ │ │ │ └── feature_tests.cxx │ │ │ ├── android_gradle_build.json │ │ │ ├── build.ninja │ │ │ ├── cmake_build_command.txt │ │ │ ├── cmake_build_output.txt │ │ │ ├── cmake_install.cmake │ │ │ └── rules.ninja │ │ ├── armeabi-v7a/ │ │ │ ├── CMakeCache.txt │ │ │ ├── CMakeFiles/ │ │ │ │ ├── 3.6.0-rc2/ │ │ │ │ │ ├── CMakeCCompiler.cmake │ │ │ │ │ ├── CMakeCXXCompiler.cmake │ │ │ │ │ └── CMakeSystem.cmake │ │ │ │ ├── CMakeOutput.log │ │ │ │ ├── TargetDirectories.txt │ │ │ │ ├── cmake.check_cache │ │ │ │ ├── feature_tests.c │ │ │ │ └── feature_tests.cxx │ │ │ ├── android_gradle_build.json │ │ │ ├── build.ninja │ │ │ ├── cmake_build_command.txt │ │ │ ├── cmake_build_output.txt │ │ │ ├── cmake_install.cmake │ │ │ └── rules.ninja │ │ ├── mips/ │ │ │ ├── CMakeCache.txt │ │ │ ├── CMakeFiles/ │ │ │ │ ├── 3.6.0-rc2/ │ │ │ │ │ ├── CMakeCCompiler.cmake │ │ │ │ │ ├── CMakeCXXCompiler.cmake │ │ │ │ │ └── CMakeSystem.cmake │ │ │ │ ├── CMakeOutput.log │ │ │ │ ├── TargetDirectories.txt │ │ │ │ ├── cmake.check_cache │ │ │ │ ├── feature_tests.c │ │ │ │ └── feature_tests.cxx │ │ │ ├── android_gradle_build.json │ │ │ ├── build.ninja │ │ │ ├── cmake_build_command.txt │ │ │ ├── cmake_build_output.txt │ │ │ ├── cmake_install.cmake │ │ │ └── rules.ninja │ │ ├── mips64/ │ │ │ ├── CMakeCache.txt │ │ │ ├── CMakeFiles/ │ │ │ │ ├── 3.6.0-rc2/ │ │ │ │ │ ├── CMakeCCompiler.cmake │ │ │ │ │ ├── CMakeCXXCompiler.cmake │ │ │ │ │ └── CMakeSystem.cmake │ │ │ │ ├── CMakeOutput.log │ │ │ │ ├── TargetDirectories.txt │ │ │ │ ├── cmake.check_cache │ │ │ │ ├── feature_tests.c │ │ │ │ └── feature_tests.cxx │ │ │ ├── android_gradle_build.json │ │ │ ├── build.ninja │ │ │ ├── cmake_build_command.txt │ │ │ ├── cmake_build_output.txt │ │ │ ├── cmake_install.cmake │ │ │ └── rules.ninja │ │ ├── x86/ │ │ │ ├── CMakeCache.txt │ │ │ ├── CMakeFiles/ │ │ │ │ ├── 3.6.0-rc2/ │ │ │ │ │ ├── CMakeCCompiler.cmake │ │ │ │ │ ├── CMakeCXXCompiler.cmake │ │ │ │ │ └── CMakeSystem.cmake │ │ │ │ ├── CMakeOutput.log │ │ │ │ ├── TargetDirectories.txt │ │ │ │ ├── cmake.check_cache │ │ │ │ ├── feature_tests.c │ │ │ │ └── feature_tests.cxx │ │ │ ├── android_gradle_build.json │ │ │ ├── build.ninja │ │ │ ├── cmake_build_command.txt │ │ │ ├── cmake_build_output.txt │ │ │ ├── cmake_install.cmake │ │ │ └── rules.ninja │ │ └── x86_64/ │ │ ├── CMakeCache.txt │ │ ├── CMakeFiles/ │ │ │ ├── 3.6.0-rc2/ │ │ │ │ ├── CMakeCCompiler.cmake │ │ │ │ ├── CMakeCXXCompiler.cmake │ │ │ │ └── CMakeSystem.cmake │ │ │ ├── CMakeOutput.log │ │ │ ├── TargetDirectories.txt │ │ │ ├── cmake.check_cache │ │ │ ├── feature_tests.c │ │ │ └── feature_tests.cxx │ │ ├── android_gradle_build.json │ │ ├── build.ninja │ │ ├── cmake_build_command.txt │ │ ├── cmake_build_output.txt │ │ ├── cmake_install.cmake │ │ └── rules.ninja │ ├── .gitignore │ ├── CMakeLists.txt │ ├── build.gradle │ ├── config.json │ ├── proguard-rules.pro │ ├── securitysdkcore.iml │ └── src/ │ ├── androidTest/ │ │ └── java/ │ │ └── com/ │ │ └── nstl/ │ │ └── securitysdkcore/ │ │ └── ExampleInstrumentedTest.java │ ├── main/ │ │ ├── AndroidManifest.xml │ │ ├── assets/ │ │ │ ├── config.json │ │ │ ├── javascript.html │ │ │ └── zhihu.cer │ │ ├── cpp/ │ │ │ ├── BasicInfor.cpp │ │ │ ├── BasicInfor.h │ │ │ ├── InjectDetected.cpp │ │ │ ├── InjectDetected.h │ │ │ ├── SimulatorDetected.cpp │ │ │ ├── SimulatorDetected.h │ │ │ ├── SoDecode/ │ │ │ │ ├── CMakeLists.txt │ │ │ │ ├── Rc4Util.cpp │ │ │ │ ├── Rc4Util.h │ │ │ │ ├── build.gradle │ │ │ │ └── native-lib.cpp │ │ │ ├── Util.cpp │ │ │ ├── Util.h │ │ │ ├── getSign.cpp │ │ │ ├── getSign.h │ │ │ └── native-lib.cpp │ │ ├── java/ │ │ │ └── com/ │ │ │ └── nstl/ │ │ │ └── securitysdkcore/ │ │ │ ├── APPThreatIntelligence.java │ │ │ ├── BinderSecurityUtil.java │ │ │ ├── HelpUtil.java │ │ │ ├── HttpUtil.java │ │ │ ├── Logger/ │ │ │ │ ├── LogLevel.java │ │ │ │ ├── Logger.java │ │ │ │ ├── SecuritySdkError.java │ │ │ │ ├── SecuritySdkTrace.java │ │ │ │ └── Track.java │ │ │ ├── NativeCoreUtil.java │ │ │ ├── PluginInvokeValidate.java │ │ │ ├── SecuritySDKInit.java │ │ │ ├── UpgradeTool/ │ │ │ │ ├── DataBean.java │ │ │ │ ├── ISafeInstall.java │ │ │ │ ├── UpgradeModel.java │ │ │ │ ├── UpgradeTool.java │ │ │ │ ├── UpgradeToolTest/ │ │ │ │ │ └── SafeInstall.java │ │ │ │ └── UserConfig.java │ │ │ ├── Util/ │ │ │ │ └── VerifyUtil.java │ │ │ ├── config/ │ │ │ │ ├── ConfigFileToObject.java │ │ │ │ ├── IntentUriScheme.java │ │ │ │ ├── InterceptMethod.java │ │ │ │ ├── InterceptPluginInvoke.java │ │ │ │ ├── SecuritySDKConfig.java │ │ │ │ └── WebviewConfig.java │ │ │ ├── crypt/ │ │ │ │ ├── CryptAndHttps.java │ │ │ │ └── bean/ │ │ │ │ └── EncryptData.java │ │ │ ├── reinforce/ │ │ │ │ ├── DetectRootUtil.java │ │ │ │ ├── IVerifyListener.java │ │ │ │ ├── JarSignatureVerifier.java │ │ │ │ ├── SafeZipFile.java │ │ │ │ └── bean/ │ │ │ │ └── InstallPackageInfo.java │ │ │ ├── urischeme/ │ │ │ │ ├── IValidateIntentUriScheme.java │ │ │ │ └── IntentUriSchemeFilter.java │ │ │ └── webview/ │ │ │ ├── IMethodInvokeInterface.java │ │ │ └── SafeWebView.java │ │ └── res/ │ │ └── values/ │ │ └── strings.xml │ └── test/ │ └── java/ │ └── com/ │ └── nstl/ │ └── securitysdkcore/ │ └── ExampleUnitTest.java └── settings.gradle ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gradle/buildOutputCleanup/cache.properties ================================================ #Fri Apr 20 22:24:02 CST 2018 gradle.version=4.1 ================================================ FILE: .idea/SecuritySDK.iml ================================================ ================================================ FILE: .idea/gradle.xml ================================================ ================================================ FILE: .idea/libraries/android_arch_core_common_1_0_0_jar.xml ================================================ ================================================ FILE: .idea/libraries/android_arch_lifecycle_common_1_0_0_jar.xml ================================================ ================================================ FILE: .idea/libraries/android_arch_lifecycle_runtime_1_0_0.xml ================================================ ================================================ FILE: .idea/libraries/com_alibaba_fastjson_1_2_42_jar.xml ================================================ ================================================ FILE: .idea/libraries/com_android_support_animated_vector_drawable_26_1_0.xml ================================================ ================================================ FILE: .idea/libraries/com_android_support_appcompat_v7_26_1_0.xml ================================================ ================================================ FILE: .idea/libraries/com_android_support_constraint_constraint_layout_1_0_2.xml ================================================ ================================================ FILE: .idea/libraries/com_android_support_constraint_constraint_layout_solver_1_0_2_jar.xml ================================================ ================================================ FILE: .idea/libraries/com_android_support_support_annotations_26_1_0_jar.xml ================================================ ================================================ FILE: .idea/libraries/com_android_support_support_compat_26_1_0.xml ================================================ ================================================ FILE: .idea/libraries/com_android_support_support_core_ui_26_1_0.xml ================================================ ================================================ FILE: .idea/libraries/com_android_support_support_core_utils_26_1_0.xml ================================================ ================================================ FILE: .idea/libraries/com_android_support_support_fragment_26_1_0.xml ================================================ ================================================ FILE: .idea/libraries/com_android_support_support_media_compat_26_1_0.xml ================================================ ================================================ FILE: .idea/libraries/com_android_support_support_v4_26_1_0.xml ================================================ ================================================ FILE: .idea/libraries/com_android_support_support_vector_drawable_26_1_0.xml ================================================ ================================================ FILE: .idea/libraries/com_android_support_test_espresso_espresso_core_2_2_2.xml ================================================ ================================================ FILE: .idea/libraries/com_android_support_test_espresso_espresso_idling_resource_2_2_2.xml ================================================ ================================================ FILE: .idea/libraries/com_android_support_test_exposed_instrumentation_api_publish_0_5.xml ================================================ ================================================ FILE: .idea/libraries/com_android_support_test_rules_0_5.xml ================================================ ================================================ FILE: .idea/libraries/com_android_support_test_runner_0_5.xml ================================================ ================================================ FILE: .idea/libraries/com_github_lzyzsd_jsbridge_1_0_4.xml ================================================ ================================================ FILE: .idea/libraries/com_google_code_findbugs_jsr305_2_0_1_jar.xml ================================================ ================================================ FILE: .idea/libraries/com_google_code_gson_gson_2_3_1_jar.xml ================================================ ================================================ FILE: .idea/libraries/com_squareup_javawriter_2_1_1_jar.xml ================================================ ================================================ FILE: .idea/libraries/javax_annotation_javax_annotation_api_1_2_jar.xml ================================================ ================================================ FILE: .idea/libraries/javax_inject_javax_inject_1_jar.xml ================================================ ================================================ FILE: .idea/libraries/junit_junit_4_12_jar.xml ================================================ ================================================ FILE: .idea/libraries/org_hamcrest_hamcrest_core_1_3_jar.xml ================================================ ================================================ FILE: .idea/libraries/org_hamcrest_hamcrest_integration_1_3_jar.xml ================================================ ================================================ FILE: .idea/libraries/org_hamcrest_hamcrest_library_1_3_jar.xml ================================================ ================================================ FILE: .idea/misc.xml ================================================ ================================================ FILE: .idea/modules.xml ================================================ ================================================ FILE: .idea/runConfigurations.xml ================================================ ================================================ FILE: .idea/vcs.xml ================================================ ================================================ FILE: .idea/workspace.xml ================================================ 1524234027296 1525450436774 Android API 26 Platform ================================================ FILE: README.md ================================================ # SecuritySDK ---------- **SecuritySDK**是为Android APP提供一系列安全防护功能,包括但不限于:基础加固对抗防护、典型漏洞防护方案和代码、威胁情报收集等功能。其中securitysdkcore是项目核心功能代码,app只是securitysdkcore的测试demo apk。项目成员:daizy(daizhongyin@126.com)、张林(97615274@qq.com)、唐海洋(ffthy@qq.com)。如有任何问题,欢迎随时联系我们。 **典型漏洞防护方案和代码**包括:安全webview、应用IPC通信安全、应用和插件更新安全、插件调用安全、ZIPFile读取压缩文件的安全性、Intent Uri Scheme安全、插件plugin调用安全、Jar签名效验验证。 **基础加固对抗防护**包括:反调试、模拟器检测、重打包检测、进程注入检测、HOOK框架检测。 **威胁情报收集**包括:当前设备上安装的所有包名+签名、连接WIFI、是否root、HOOK框架检测、设备位置信息、基站信息。 ## SecuritySDK的配置文件设置与使用 由于漏洞防护中的很多能力依赖于运行时拦截,所以**配置文件**的更新和获取是SecuritySDK的使用前提,配置文件格式和内容,可以参考[示例](./securitysdkcore/config.json),配置文件由server端生成,SecuritySDK获取到本地后,反序列化后使用。 1)配置文件config.json(通过preferences方式存储),是基于json格式的内容,它提供了4类拦截规则:webview的拦截规则、intent uri拦截规则、应用插件调用的过滤拦截规则。 2)通过SecuritySDKInit.syncConfig(url, params)方法进行配置文件的更新,新版本的更新取决于设置的time_out和version,当更新间隔时间到了,就回去服务器请求新的版本号,如果版本号大于本地版本号,就会进行规则更新; 3)获得拦截规则:先通过SecuritySDKInit.getConfigStringValueByKey(key)获得本地存储的配置文件json内容,然后自己通过fastjson讲字符串在转换成SecuritySDKConfig中的相关拦截规则对象。示例代码如下: ```java String str = SecuritySDKInit.getConfigStringValueByKey(SecuritySDKInit.WEBVIEWCONFIG); WebviewConfig webviewConfig = JSON.parseObject(str, WebviewConfig.class); ``` ## 典型漏洞防护方案、代码和使用说明 * [安全webview](./docs/safewebview.md)——[代码](./securitysdkcore/src/main/java/com/nstl/securitysdkcore/webview/SafeWebView.java) 安全webview是针对webview常见的远程命令执行漏洞,file域攻击、高危接口对外和中间人劫持,这几个高危漏洞设计的安全webview,可以有效的解决上述几个高危漏洞。同时参考了微信、支付宝和github上的开源代码(JSBridge),做到了有效性、兼容性和安全性并存。 * [应用IPC通信安全](./docs/BinderSecurityUtil.md)——[代码](./securitysdkcore/src/main/java/com/nstl/securitysdkcore/BinderSecurityUtil.java) IPC通信安全是针对应用进程间,进行数据通信时设计的方案。应用A和应用B进行数据通信时,业务方需要使用Binder异步接口进行,不要使用开放端口,然后再每个开放的Binder通信接口中,调用BinderSecurityUtil. checkClientSig()进行调用方的签名认证,签名认证可以在本地,也可以在云端进行。 * [应用和插件安全更新](./docs/safeupgrade.md)——[代码](./securitysdkcore/src/main/java/com/nstl/securitysdkcore/UpgradeTool/UpgradeTool.java) 应用和插件安全更新功能提供:应用和插件安全更新的能力,防止应用或插件更新升级时,被劫持或者下载的apk/zip被串改,从而导致任意代码执行的漏洞。 * [ZIPFile读取APK文件安全](./docs/SafeZipFile.md)——[代码](./securitysdkcore/src/main/java/com/nstl/securitysdkcore/reinforce/SafeZipFile.java) 为了保证ZIPFile读取APK文件的安全性,确保ZipFile文件内不包含../,并且只有一个Dex(避免恶意代码加载执行漏洞),此外还需要确保APK中的签名和业务方提供的签名信息一致,防止签名绕过漏洞。 * [Intent Uri Scheme安全](./docs/IntentUriSchemeFilter.md)——[代码](./securitysdkcore/src/main/java/com/nstl/securitysdkcore/urischeme/IntentUriSchemeFilter.java) Uri scheme类似 bainuo://web?url=http://aaa.bbb.com/xxx,其中参数容易被攻击者篡改以达到某种目的,为此,安全sdk定义IntentUriSchemeFilter类来提供Uri Scheme安全拦截的功能,作为临时补丁和恶意行为的拦截,类似web安全中的WAF。 * [插件plugin调用安全](./docs/PluginInvokeValidate.md)——[代码](./securitysdkcore/src/main/java/com/nstl/securitysdkcore/PluginInvokeValidate.java) 插件调用安全是通过增加拦截类,根据配置文件(config)中的拦截规则,对恶意插件调用进行拦截;拦截规则提供了插件包名拦截、插件方法名拦截、调用的类名、方法参数和Intent数据的拦截. * [jar签名效验](./docs/jarSignatureVerify.md)——[代码](./securitysdkcore/src/main/java/com/nstl/securitysdkcore/reinforce/JarSignatureVerifier.java) JarSignatureVerifier类是为了保证JAR签名和内容一致性,防止攻击者合法签名文件,但是.class文件和签名信息不一致,从而绕过签名校验,导致任意代码执行。 ## 基础加固对抗防护 ### NativeCoreUtil 提供debugPresent(检测应用是否处于调试中)、runInEmulator(检测应用是否处于模拟器)、rePackage(重打包检查)、detectInject(检测应用是否被xposed,substrate等框架注入)、isExisSUAndExecute(是否被root)、getRemoteAppSign(获取远程应用的签名)6个函数 ### AndroidSo 加解密 核心加解密过程如下: 1、首先获取.so 在内存中加载的基址 2、因为android 系统对于.so的加载是基于段的,dynamic段中包含了多个节区,有hash,dynstr(函数的名字在该节区中),符号表 3、由于hash表和dynstr表 节区 的类型不唯一,有可能是别的,所以在dynamic段中寻找 4、hash表中存储了符号表中对应的索引,通过函数名进行哈希运算得到索引,然后去符号表中,符号表的结构是1、dynstr中的索引 2、地址 3、大小 5、通过符号表中的第一个字节获取dynstr中对应的字符串 ,然后比对,比对成功则进行加密解密 使用过程 (1)业务方将解密的核心代码加入到自己的native代码中,指定SO_NAME(生成的SO名称),FUNC_NAME(加密函数名),RCE_KEY(加密密钥),RC4_KEY_LEN(加密密钥的长度),生成so 注:密钥分发和保存 (2)将so进行加密,指定加密的函数名、加密的密钥(此时加密SO的RC4密钥需要与解密的密钥一致),加密的so文件路径(EncodeSo类中RC4_KEY,funcName,so_path) (3)业务方在Build.gradle中指定加载so的方式,如Jnilibs、libs等等,将加密后的so放到指定的路径下即可 (4)在需要调用jni的地方, static { System.loadLibrary("加密后的so库名称"); } 参考: http://blog.csdn.net/jiangwei0910410003/article/details/49966719 http://bbs.pediy.com/thread-191649.htm ### sdk各文件及其相关API说明 1、Rc4Util.h Rc4Util.cpp(RC4加密算法的实现文件) 注: 因为涉及到修改内存操作,目前暂考虑同位加密,为此选取了RC4加密算法,RC4加密算法原理这里暂不介绍,请自行google。 2、 getSign.cpp getSign.h(获取签名) 通过NativeCoreUtil类中的getRemoteAppSign函数进行调用。传入上下文和包名,返回远程调用的签名。本地签名哈希暂时写在getSign.cpp文件中。 3、插件调用拦截。使用PluginInvokeValidate 类中的validate函数进行拦截,初始化安全策略文件写在键intercept_plugin_invoke中。 4、安全通信 调用接口为CryptAndHttps类中的getHttpsUrlConnection函数。使用前 必须先配置服务器证书,证书文件放在Assets目录。具体使用时见MainActivity中的Https登录按钮。 ## 威胁情报收集 todo ================================================ FILE: SecuritySDK-master.iml ================================================ ================================================ FILE: SecuritySDK.iml ================================================ ================================================ FILE: app/.gitignore ================================================ /build ================================================ FILE: app/CMakeLists.txt ================================================ # For more information about using CMake with Android Studio, read the # documentation: https://d.android.com/studio/projects/add-native-code.html # Sets the minimum version of CMake required to build the native library. cmake_minimum_required(VERSION 3.4.1) # Creates and names a library, sets it as either STATIC # or SHARED, and provides the relative paths to its source code. # You can define multiple libraries, and CMake builds them for you. # Gradle automatically packages shared libraries with your APK. add_library( # Sets the name of the library. native-lib # Sets the library as a shared library. SHARED # Provides a relative path to your source file(s). src/main/cpp/native-lib.cpp ) # Searches for a specified prebuilt library and stores the path as a # variable. Because CMake includes system libraries in the search path by # default, you only need to specify the name of the public NDK library # you want to add. CMake verifies that the library exists before # completing its build. find_library( # Sets the name of the path variable. log-lib # Specifies the name of the NDK library that # you want CMake to locate. log ) # Specifies libraries CMake should link to your target library. You # can link multiple libraries, such as libraries you define in this # build script, prebuilt third-party libraries, or system libraries. target_link_libraries( # Specifies the target library. native-lib # Links the target library to the log library # included in the NDK. ${log-lib} ) ================================================ FILE: app/app.iml ================================================ ================================================ FILE: app/build.gradle ================================================ apply plugin: 'com.android.application' android { compileSdkVersion 26 buildToolsVersion "26.0.2" defaultConfig { applicationId "com.nstl.securitysdk" minSdkVersion 18 targetSdkVersion 26 versionCode 1 versionName "1.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } } repositories { maven { url "https://jitpack.io" } } dependencies { compile fileTree(include: ['*.jar'], dir: 'libs') androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { exclude group: 'com.android.support', module: 'support-annotations' }) compile 'com.android.support:appcompat-v7:26.+' compile 'com.android.support.constraint:constraint-layout:1.0.2' testCompile 'junit:junit:4.12' compile 'com.github.lzyzsd:jsbridge:1.0.4' compile 'com.google.code.gson:gson:2.3.1' compile project(':securitysdkcore') } ================================================ FILE: app/config.json ================================================ { "intentUriList": [ { "actionStringList": [ "com.action.view", "com.action.browser" ], "type": 1, "uriHostList": [ "baidu.com" ], "uriParaMaps": { "keywords": "file" } } ], "interceptPluginInvokeList": [ //需要禁止的插件调用(plugin_name "插件名, method_name "方法名",method_arg “方法参数”,targetclass “目标类名”, intent_data "Intent中需要过滤的内容") { "method": { "methodNmae": "sendsmsbydefault", "type": 0 }, "pluginName": "com.sendsms.plugin", "type": 0, "uriScheme": { "$ref": "$.intentUriList[0]" } } ], "timeout": 100000, // 更新间隔,SDK提供修改接口,业务方根据策略,分批设置更新时间,来缓解服务器压力.默认是单位是秒. "updateAndDownloadUrlWhiteList": [ "safedownload.com" ], "version": "1.0", //当前配置信息的版本 "webviewConfig": { "methodList": [ { "methodArgMap": { "1": "baidu.com" }, "methodNmae": "openurl", "type": 1 } ], "urlBlackList": [ "test.com", "attack.com" ] } } ================================================ FILE: app/proguard-rules.pro ================================================ # Add project specific ProGuard rules here. # By default, the flags in this file are appended to flags specified # in /home/plldzy/soft/android-sdk/tools/proguard/proguard-android.txt # You can edit the include path and order by changing the proguardFiles # directive in build.gradle. # # For more details, see # http://developer.android.com/guide/developing/tools/proguard.html # Add any project specific keep options here: # If your project uses WebView with JS, uncomment the following # and specify the fully qualified class name to the JavaScript interface # class: #-keepclassmembers class fqcn.of.javascript.interface.for.webview { # public *; #} # Uncomment this to preserve the line number information for # debugging stack traces. #-keepattributes SourceFile,LineNumberTable # If you keep the line number information, uncomment this to # hide the original source file name. #-renamesourcefileattribute SourceFile ================================================ FILE: app/src/androidTest/java/com/nstl/securitysdk/ExampleInstrumentedTest.java ================================================ package com.nstl.securitysdk; import android.content.Context; import android.support.test.InstrumentationRegistry; import android.support.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; import static org.junit.Assert.*; /** * Instrumentation test, which will execute on an Android device. * * @see Testing documentation */ @RunWith(AndroidJUnit4.class) public class ExampleInstrumentedTest { @Test public void useAppContext() throws Exception { // Context of the app under test. Context appContext = InstrumentationRegistry.getTargetContext(); assertEquals("com.nstl.securitysdk", appContext.getPackageName()); } } ================================================ FILE: app/src/main/AndroidManifest.xml ================================================ ================================================ FILE: app/src/main/aidl/com/nstl/securitysdk/IMyAidlInterface.aidl ================================================ // IMyAidlInterface.aidl package com.nstl.securitysdk; // Declare any non-default types here with import statements interface IMyAidlInterface { /** * Demonstrates some basic types that you can use as parameters * and return values in AIDL. */ String getInfoFromCli(String s); } ================================================ FILE: app/src/main/cpp/native-lib.cpp ================================================ #include #include extern "C" JNIEXPORT jstring JNICALL Java_com_nstl_securitysdk_MainActivity_stringFromJNI( JNIEnv* env, jobject /* this */) { std::string hello = "Hello from C++"; return env->NewStringUTF(hello.c_str()); } ================================================ FILE: app/src/main/java/com/nstl/securitysdk/MainActivity.java ================================================ package com.nstl.securitysdk; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.os.Environment; import android.os.IBinder; import android.os.RemoteException; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.util.Log; import android.view.View; import android.widget.Button; import android.widget.TextView; import android.widget.Toast; import com.alibaba.fastjson.JSON; import com.nstl.securitysdkcore.HelpUtil; import com.nstl.securitysdkcore.NativeCoreUtil; import com.nstl.securitysdkcore.UpgradeTool.DataBean; import com.nstl.securitysdkcore.reinforce.SafeZipFile; import com.nstl.securitysdkcore.UpgradeTool.ISafeInstall; import com.nstl.securitysdkcore.UpgradeTool.UpgradeModel; import com.nstl.securitysdkcore.UpgradeTool.UpgradeTool; import com.nstl.securitysdkcore.config.IntentUriScheme; import com.nstl.securitysdkcore.config.InterceptMethod; import com.nstl.securitysdkcore.config.InterceptPluginInvoke; import com.nstl.securitysdkcore.config.SecuritySDKConfig; import com.nstl.securitysdkcore.config.WebviewConfig; import com.nstl.securitysdkcore.reinforce.DetectRootUtil; import com.nstl.securitysdkcore.reinforce.IVerifyListener; import com.nstl.securitysdkcore.webview.IMethodInvokeInterface; import com.nstl.securitysdkcore.webview.SafeWebView; import java.io.File; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; public class MainActivity extends AppCompatActivity { private String TAG = "SecuritySdk Tets"; // Used to load the 'native-lib' library on application startup. static { System.loadLibrary("native-lib"); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // Example of a call to a native method /*HelpUtil helpUtil = new HelpUtil(); List installPackageInfoList = helpUtil.getInstallPackageAndSig(this); StringBuilder builder = new StringBuilder(); for(InstallPackageInfo pkg : installPackageInfoList){ builder.append(pkg.getPkgName()); }*/ // //****************基础功能测试**************** // //查看设备是否root // DetectRootUtil detectRootUtil = DetectRootUtil.getInstance(this); // Toast.makeText(this, "设备是否root:" + detectRootUtil.isRoot(), Toast.LENGTH_SHORT).show(); // // //查看是否在调试应用 // NativeCoreUtil nativeCoreUtil = new NativeCoreUtil(); // Toast.makeText(this, "设备是否在调试阶段:" + nativeCoreUtil.debugPresent(), Toast.LENGTH_SHORT).show(); // // //查看设备是否是模拟器 // Toast.makeText(this, "设备是否是模拟器:" + nativeCoreUtil.runInEmulator(this), Toast.LENGTH_SHORT).show(); // // //查看应用是否被重打包 // IVerifyListener iVerifyListener = new IVerifyListener() { // @Override // public void onVerifySuccess() { // Toast.makeText(getApplicationContext(), "success", Toast.LENGTH_SHORT).show(); // } // // @Override // public void onVerifyFail() { // Toast.makeText(getApplicationContext(), "failed", Toast.LENGTH_SHORT).show(); // } // }; // nativeCoreUtil.rePackage(this, iVerifyListener); // // //是否注入 // Toast.makeText(this, "应用是否被注入:" + nativeCoreUtil.detectInject(this), Toast.LENGTH_SHORT).show(); // // //****************安全检测zip文件,提供路径**************** String zipFilePath = getSDPath() + File.separator + ""; File zipFile = new File(""); String md5Str = ""; SafeZipFile safeZipFile = new SafeZipFile(this, zipFile, md5Str); Toast.makeText(this, "文件:" + zipFile.getName() + "是否是安全的: " + safeZipFile.isLegalZipFile(), Toast.LENGTH_SHORT).show(); //****************upgradetool 升级测试**************** // DataBean dataBean = new DataBean(); // dataBean.setVercode(1); // dataBean.setDescription("this is for upgrade test"); // dataBean.setDownUrl("http://192.168.1.15/test.apk"); // dataBean.setIsForce("true"); // dataBean.setSingnedVerifyCode("xxxxxxxxx"); // dataBean.setVerifyCode("8321cfd49ab78e58b0bea51877b4e617"); // dataBean.setVersion("1.0.1"); // // UpgradeModel upgradeModel_for_json = new UpgradeModel(); // upgradeModel_for_json.setCode(0); // upgradeModel_for_json.setDataBean(dataBean); // upgradeModel_for_json.setMsg("success"); // // String public_key = ""; // String upgrade_model_json = JSON.toJSONString(upgradeModel_for_json); //此处应该是从服务器获取json串,然后反json为Upgrade // UpgradeModel upgradeModel = JSON.parseObject(upgrade_model_json, UpgradeModel.class); // Toast.makeText(this, "文件:" + upgradeModel.getMsg(), Toast.LENGTH_SHORT).show(); // String sava_path = "/download"; // String file_nme = "xxxxxxxxxxxxxxx.apk"; // // ISafeInstall iSafeInstall = new ISafeInstall() { // @Override // public int getVerCode() { // return 0; // } // // @Override // public void install(Context context, String savePath, String fileName, String isForce) { // Toast.makeText(getApplicationContext(), "success install ", Toast.LENGTH_SHORT).show(); // } // @Override // public String getErrMsg(String errMsg) { // Toast.makeText(getApplicationContext(), "failed install ", Toast.LENGTH_SHORT).show(); // return errMsg; // } // }; // // UpgradeTool upgradeTool = new UpgradeTool(this, public_key, upgradeModel, sava_path, file_nme, iSafeInstall); // upgradeTool.upgrade(); // // // //****************webview 安全测试**************** // SafeWebView safeWebView = (SafeWebView) this.findViewById(R.id.my_webview); // IMethodInvokeInterface iMethodInvokeInterface = new IMethodInvokeInterface() { // @Override // public String dispatch(String data) { // //业务方在此处处理JS回调 // return null; // } // }; // safeWebView.init(this, iMethodInvokeInterface); // //向js注册一个hander,js调用 // safeWebView.registerHandler("safeWebViewInterface"); // safeWebView.loadUrl("http://www.test.com"); // // //todo****************插件调用安全测试**************** // // //todo****************binder机制通信安全测试**************** // // // //todo****************配置文件读取测试**************** // SecuritySDKConfig sdkConfig = new SecuritySDKConfig(); // // //todo****************https安全通信测试**************** // // //todo****************加密、解密组建测试**************** // // //todo****************移动威胁情报分析测试**************** // //查看 // //webview的过滤配置规则 // WebviewConfig webviewConfig = new WebviewConfig(); // List urlWhiteList = new LinkedList(); // urlWhiteList.add("baidu.com"); // urlWhiteList.add("google.com"); // List urlBlackList = new LinkedList(); // urlBlackList.add("test.com"); // urlBlackList.add("attack.com"); // List methodList = new LinkedList(); // // InterceptMethod method = new InterceptMethod(); // Map methodArgMap = new HashMap(); // methodArgMap.put(1, "baidu.com"); // method.setType(1); //白名单方式过滤openurl函数的第一个参数,只允许host是baidu.com的url // method.setMethodNmae("openurl"); // method.setMethodArgMap(methodArgMap); // methodList.add(method); // // webviewConfig.setUrlBlackList(urlWhiteList); // webviewConfig.setUrlBlackList(urlBlackList); // webviewConfig.setMethodList(methodList); // // //intent uri调用拦截 // List uriSchemeList = new LinkedList(); // IntentUriScheme uriScheme1 = new IntentUriScheme(); // uriScheme1.setType(1); //此uri是白名单,只允许http开头host是baidu.com下的两个相关action // List actionList = new LinkedList(); // actionList.add("com.action.view"); // actionList.add("com.action.browser"); // uriScheme1.setActionStringList(actionList); // List hostList = new LinkedList(); // hostList.add("baidu.com"); // uriScheme1.setUriHostList(hostList); // List schemeList = new LinkedList(); // schemeList.add("http"); // uriSchemeList.add(uriScheme1); // // //应用和插件更新的url白名单 // List updateAndDownloadUrlWhiteList = new LinkedList(); // updateAndDownloadUrlWhiteList.add("safedownload.com"); // // //插件调用配置规则 // List interceptPluginInvokeList = new LinkedList(); // InterceptPluginInvoke pluginInvoke = new InterceptPluginInvoke(); // pluginInvoke.setType(0); //黑名单,拦截查件中的短信发送方法 // pluginInvoke.setPluginName("com.sendsms.plugin"); // InterceptMethod pluginMethod = new InterceptMethod(); // pluginMethod.setType(0); //黑名单方式拦截函数sendsmsbydefault,不用过滤参数,直接拦截 // pluginMethod.setMethodNmae("sendsmsbydefault"); // pluginInvoke.setMethod(pluginMethod); // //插件中=====对intent 中uri增加params过滤,比如禁止某个关键字的http和file字符 // Map uriParaMaps = new HashMap(); // uriParaMaps.put("keywords", "http"); // uriParaMaps.put("keywords", "file"); // uriScheme1.setUriParaMaps(uriParaMaps); // pluginInvoke.setUriScheme(uriScheme1); // interceptPluginInvokeList.add(pluginInvoke); // // sdkConfig.setVersion("1.0"); // sdkConfig.setTimeout(100000); // sdkConfig.setWebviewConfig(webviewConfig); // sdkConfig.setIntentUriList(uriSchemeList); // sdkConfig.setUpdateAndDownloadUrlWhiteList(updateAndDownloadUrlWhiteList); // sdkConfig.setInterceptPluginInvokeList(interceptPluginInvokeList); // // String jsonStr = JSON.toJSONString(sdkConfig); // Log.i(TAG, jsonStr); // sdkConfig = JSON.parseObject(jsonStr, SecuritySDKConfig.class); // //tv.setText(sdkConfig.getIntentUriList().get(0).getUriHostList().get(0)); // // //配置文件的初始化 // /* Map params = new HashMap(); // params.put("username", "test"); // params.put("token", "123456"); // SecuritySDKInit.getInstance(this).syncConfig("http://192.168.199.164:8080/mytest/test.txt", params);*/ // SafeWebView webView = (SafeWebView) this.findViewById(R.id.my_webview); // IMethodInvokeInterface methodInvokeInterface = new IMethodInvokeInterface() { // @Override // public String dispatch(String data) { // //业务方在此处处理JS回调 // return null; // } // }; // webView.init(this, methodInvokeInterface); // webView.loadUrl("http://www.test.com"); // //远程调用 // final TextView tv3 = (TextView) findViewById(R.id.IPC_check); // Intent service = new Intent(MainActivity.this, MyService.class); // ServiceConnection serviceConnection = new ServiceConnection() { // @Override // public void onServiceConnected(ComponentName name, IBinder service) { // if ((service instanceof IMyAidlInterface.Stub) == false) { // Log.d(TAG, "service " + (service instanceof IMyAidlInterface.Stub)); // } // IMyAidlInterface myAidlInterface = IMyAidlInterface.Stub.asInterface(service); // try { // String s = myAidlInterface.getInfoFromCli("hello from Cli"); // tv3.setText(s); // } catch (RemoteException e) { // e.printStackTrace(); // } // } // // @Override // public void onServiceDisconnected(ComponentName name) { // // } // }; // bindService(service, serviceConnection, Context.BIND_AUTO_CREATE); // // // //远程调用 包名查看 // String referrerStr = HelpUtil.reflectGetReferrer(this); // final TextView tv4 = (TextView) findViewById(R.id.caller_name); // tv4.setText("caller namer:" + referrerStr); // // //https访问 // Button button = (Button) findViewById(R.id.myButton); // button.setOnClickListener(new View.OnClickListener() { // @Override // public void onClick(View v) { // new Thread(new Runnable() { // // @Override // public void run() { // try { // HelpUtil.initSSLWithHttpClient(MainActivity.this); // } catch (Exception e) { // Log.e("HTTPS TEST", e.getMessage()); // } // } // }).start(); // } // }); /** * A native method that is implemented by the 'native-lib' native library, * which is packaged with this application. */ } public String getSDPath(){ File sdDir = null; boolean sdCardExist = Environment.getExternalStorageState() .equals(android.os.Environment.MEDIA_MOUNTED);//判断sd卡是否存在 if(sdCardExist) { sdDir = Environment.getExternalStorageDirectory();//获取跟目录 } return sdDir.toString(); } } ================================================ FILE: app/src/main/java/com/nstl/securitysdk/MyService.java ================================================ package com.nstl.securitysdk; import android.app.ActivityManager; import android.app.Service; import android.content.Context; import android.content.Intent; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; import android.util.Log; import com.nstl.securitysdkcore.BinderSecurityUtil; public class MyService extends Service { public String TAG="MyService"; public MyService() { } private IBinder binder = new IMyAidlInterface.Stub(){ /** * 利用flag进行调用者是否合法的判定. */ @Override public String getInfoFromCli(String s) throws RemoteException { Boolean flag= BinderSecurityUtil.checkClientSig(getApplicationContext()); int pid =Binder.getCallingPid(); // String ss=getAppNameByPID(getApplicationContext(),pid); // Log.d(TAG, "onBind+pid: "+ss); return "hello from service"; } }; @Override public IBinder onBind(Intent intent) { // TODO: Return the communication channel to the service. String callingApp = getApplicationContext().getPackageManager().getNameForUid(Binder.getCallingUid()); Log.d(TAG, "onBind: "+callingApp); // if(callingApp.equals("com.nstl.securitysdk")){ // Log.d(TAG, "包名正确 "); // return binder; // } // Log.d(TAG, "包名不在白名单内"); // return null; // int pid =Binder.getCallingPid(); // String ss=getAppNameByPID(getApplicationContext(),pid); // Log.d(TAG, "onBind+pid: "+ss); return binder; } // public static String getAppNameByPID(Context context, int pid){ // ActivityManager manager; // manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); // // for(ActivityManager.RunningAppProcessInfo processInfo : manager.getRunningAppProcesses()){ // if(processInfo.pid == pid){ // return processInfo.processName; // } // } // return ""; // } } ================================================ FILE: app/src/main/res/layout/activity_main.xml ================================================ ================================================ FILE: securitysdkcore/src/main/cpp/BasicInfor.cpp ================================================ // // Created by Lin on 2017/11/13. // #include "BasicInfor.h" #include #include #include #include #include "Util.h" /* * 获取TelephoneManager * @param env * @param mContext 传入的Context信息 * @return 失败返回NULL */ jobject getTelephonyManager(JNIEnv *env,jobject mContext) { //获取系统的context jclass system_context = env->FindClass("android/content/Context"); if(env->ExceptionCheck()){ env->ExceptionDescribe(); env->ExceptionClear(); return NULL; } if(system_context == 0){ return NULL; } jmethodID METHOD_getSystemService = env->GetMethodID(system_context,"getSystemService","(Ljava/lang/String;)Ljava/lang/Object;"); if(env->ExceptionCheck()){ env->ExceptionDescribe(); env->ExceptionClear(); return NULL; } if(METHOD_getSystemService == 0){ return NULL; } jfieldID FIELD_TELEPHONY_SERVICE = env->GetStaticFieldID(system_context,"TELEPHONY_SERVICE","Ljava/lang/String;"); if(env->ExceptionCheck()){ env->ExceptionDescribe(); env->ExceptionClear(); return NULL; } if(FIELD_TELEPHONY_SERVICE == 0){ return NULL; } jstring Value_TELEPHONY_SERVICE = (jstring) env->GetStaticObjectField(system_context, FIELD_TELEPHONY_SERVICE ); if(env->ExceptionCheck()){ env->ExceptionDescribe(); env->ExceptionClear(); return NULL; } if(Value_TELEPHONY_SERVICE == 0 ){ return NULL; } jobject Object_telephone_manager = env->CallObjectMethod(mContext, METHOD_getSystemService ,Value_TELEPHONY_SERVICE); if(env->ExceptionCheck()){ env->ExceptionDescribe(); env->ExceptionClear(); return NULL; } if(Object_telephone_manager == 0 ){ return NULL; } return Object_telephone_manager; } /* * 获取获取IMEI 模拟器为000000000000000 * @param env * @param mContext 传入的Context信息 * @return 失败返回error信息 */ jstring getIMEI(JNIEnv *env,jobject mContext) { jobject OBJECT_telephone_manager = getTelephonyManager(env,mContext); if(OBJECT_telephone_manager == NULL){ return getErrorInfo(env,__FILE__, __FUNCTION__, __LINE__,"OBJECT_telephone_manager get failed"); } jclass CLASS_TelephonyManager = env->FindClass("android/telephony/TelephonyManager"); if(env->ExceptionCheck()){ env->ExceptionClear(); return getErrorInfo(env,__FILE__, __FUNCTION__, __LINE__,"CLASS_Telephonymanager get failed Excption"); } if(CLASS_TelephonyManager == 0){ return getErrorInfo(env,__FILE__, __FUNCTION__, __LINE__,"CLASS_Telephonymanager get failed"); } jmethodID METHOD_getDeviceId = env->GetMethodID(CLASS_TelephonyManager,"getDeviceId","()Ljava/lang/String;"); if(env->ExceptionCheck()){ env->ExceptionClear(); return getErrorInfo(env,__FILE__, __FUNCTION__, __LINE__,"METHOD_getDeviceId get failed Excption"); } if(METHOD_getDeviceId == 0){ return getErrorInfo(env,__FILE__, __FUNCTION__, __LINE__,"METHOD_getDeviceId get failed"); } jstring retStr = (jstring) env->CallObjectMethod(OBJECT_telephone_manager, METHOD_getDeviceId); if(env->ExceptionCheck()){ env->ExceptionClear(); return getErrorInfo(env,__FILE__, __FUNCTION__, __LINE__,"IMEI get failed Excption"); } return retStr; } /* * 获取IMSI 模拟器为:310260000000000 * @param env * @param mContext 传入的Context信息 * @return 失败返回error信息 */ jstring getIMSI(JNIEnv *env,jobject mContext) { jobject OBJECT_telephone_manager = getTelephonyManager(env,mContext); if(OBJECT_telephone_manager == NULL ){ return getErrorInfo(env,__FILE__, __FUNCTION__, __LINE__,"OBJECT_telephone_manager get failed"); } jclass CLASS_TelephonyManager = env->FindClass("android/telephony/TelephonyManager"); if(env->ExceptionCheck()){ env->ExceptionClear(); return getErrorInfo(env,__FILE__, __FUNCTION__, __LINE__,"CLASS_Telephonymanager get failed Excption"); } if(CLASS_TelephonyManager == 0 ){ return getErrorInfo(env,__FILE__, __FUNCTION__, __LINE__,"CLASS_Telephonymanager get failed "); } jmethodID METHOD_getSubscriberId = env->GetMethodID(CLASS_TelephonyManager,"getSubscriberId","()Ljava/lang/String;"); if(env->ExceptionCheck()){ env->ExceptionClear(); return getErrorInfo(env,__FILE__, __FUNCTION__, __LINE__,"METHOD_getSubscriberId get failed Excption"); } if(METHOD_getSubscriberId == 0 ){ return getErrorInfo(env,__FILE__, __FUNCTION__, __LINE__,"METHOD_getSubscriberId get failed"); } jstring retStr = (jstring) env->CallObjectMethod(OBJECT_telephone_manager, METHOD_getSubscriberId); if(env->ExceptionCheck()){ env->ExceptionClear(); return getErrorInfo(env,__FILE__, __FUNCTION__, __LINE__,"IMEI Value get failed Excption"); } return retStr; } /* * 获取本机号码 * @param env * @param mContext 传入的Context信息 * @return 失败返回errror信息 */ jstring getTelephoneNum(JNIEnv *env,jobject mContext) { jobject OBJECT_telephone_manager = getTelephonyManager(env,mContext); if(OBJECT_telephone_manager == NULL){ return getErrorInfo(env,__FILE__, __FUNCTION__, __LINE__,"OBJECT_telephone_manager get failed"); } jclass CLASS_TelephonyManager = env->FindClass("android/telephony/TelephonyManager"); if(env->ExceptionCheck()){ env->ExceptionClear(); return getErrorInfo(env,__FILE__, __FUNCTION__, __LINE__,"CLASS_Telephonymanager get failed Excption"); } if(CLASS_TelephonyManager == 0 ){ return getErrorInfo(env,__FILE__, __FUNCTION__, __LINE__,"CLASS_Telephonymanager get failed"); } jmethodID METHOD_getLine1Number = env->GetMethodID(CLASS_TelephonyManager,"getLine1Number","()Ljava/lang/String;"); if(env->ExceptionCheck()){ env->ExceptionClear(); return getErrorInfo(env,__FILE__, __FUNCTION__, __LINE__,"METHOD_getLine1Number get failed Excption"); } if(METHOD_getLine1Number == 0 ){ return getErrorInfo(env,__FILE__, __FUNCTION__, __LINE__,"METHOD_getLine1Number get failed"); } jstring retStr = (jstring)env->CallObjectMethod(OBJECT_telephone_manager,METHOD_getLine1Number); if(env->ExceptionCheck()){ env->ExceptionClear(); return getErrorInfo(env,__FILE__, __FUNCTION__, __LINE__,"LineNumber value get failed Excption"); } return retStr; } /* * 获取基本信息所需的Build * @param env * @param value 传入的属性值 * @return 失败返回error信息 */ jstring getBuildBasicInfo(JNIEnv *env,const char *value) { jclass CLASS_ANDROID_OS_BUILD = env->FindClass("android/os/Build"); if(env->ExceptionCheck()){ env->ExceptionClear(); return getErrorInfo(env,__FILE__, __FUNCTION__, __LINE__,"android/os/Build get failed Excption"); } if(CLASS_ANDROID_OS_BUILD == 0){ return getErrorInfo(env,__FILE__, __FUNCTION__, __LINE__,"android/os/Build get failed"); } jfieldID FIELD_VALUE = env->GetStaticFieldID(CLASS_ANDROID_OS_BUILD,value,"Ljava/lang/String;"); if(env->ExceptionCheck()){ env->ExceptionClear(); return getErrorInfo(env,__FILE__, __FUNCTION__, __LINE__,"FIELD_VALUE get failed Excption"); } if(FIELD_VALUE == 0 ){ char msg[100] = "\0"; sprintf(msg,"android/os/Build key = %s get Field failed",value); jstring msgRetStr = getErrorInfo(env,__FILE__, __FUNCTION__, __LINE__,msg); return msgRetStr; } jstring retStr = (jstring) env->GetStaticObjectField(CLASS_ANDROID_OS_BUILD, FIELD_VALUE); if(env->ExceptionCheck()){ env->ExceptionClear(); char msg[100] = "\0"; sprintf(msg,"android/os/Build key = %s get value failed",value); jstring msgRetStr = getErrorInfo(env,__FILE__, __FUNCTION__, __LINE__,msg); return msgRetStr; } return retStr; } /* * 获取Serial * @param env * @return 失败返回error信息 */ jstring getSerial(JNIEnv *env) { return getBuildBasicInfo(env,"SERIAL"); } /* * 获取Board * @param env * @return 失败返回error信息 */ jstring getBoard(JNIEnv *env) { return getBuildBasicInfo(env,"BOARD"); } /* * 获取BootLoader * @param env * @return 失败返回error信息 */ jstring getBootLoader(JNIEnv *env) { return getBuildBasicInfo(env,"BOOTLOADER"); } /* * 获取Brand * @param env * @return 失败返回error信息 */ jstring getBrand(JNIEnv *env) { return getBuildBasicInfo(env,"BRAND"); } /* * 获取Device * @param env * @return 失败返回error信息 */ jstring getDevice(JNIEnv *env) { return getBuildBasicInfo(env,"DEVICE"); } /* * 获取HardWare * @param env * @return 失败返回error信息 */ jstring getHardware(JNIEnv *env) { return getBuildBasicInfo(env,"HARDWARE"); } /* * 获取Model * @param env * @return 失败返回error信息 */ jstring getModel(JNIEnv *env) { return getBuildBasicInfo(env,"MODEL"); } /* * 获取Product * @param env * @return 失败返回error信息 */ jstring getProduct(JNIEnv *env) { return getBuildBasicInfo(env,"PRODUCT"); } /* * 获取Board * @param env * @return 失败返回error信息 */ jstring getCpuInfo(JNIEnv *env) { FILE *fp = fopen(CPU_FILE_PATH,"r"); if(fp == NULL){ return getErrorInfo(env,__FILE__, __FUNCTION__, __LINE__,"CPU_FILE open failed"); } char ret[1025] = "\0"; int size = fread(ret,1024,1,fp); if(size == -1){ fclose(fp); return getErrorInfo(env,__FILE__, __FUNCTION__, __LINE__,"CPU_FILE fread failed"); } jstring retStr = charsTojstring(env,ret); fclose(fp); return retStr; } /* * 获取wlan的MAC地址 * @param env * @return 失败返回error信息 */ jstring getWlan0Mac(JNIEnv *env,jobject mContext) { jclass system_context = env->FindClass("android/content/Context"); if(env->ExceptionCheck()){ env->ExceptionClear(); return getErrorInfo(env,__FILE__, __FUNCTION__, __LINE__,"Context get failed Excption"); } if(system_context == 0){ return getErrorInfo(env,__FILE__, __FUNCTION__, __LINE__,"Context get failed"); } jfieldID FIELD_WIFI_SERVICE = env->GetStaticFieldID(system_context,"WIFI_SERVICE","Ljava/lang/String;"); if(env->ExceptionCheck()){ env->ExceptionClear(); return getErrorInfo(env,__FILE__, __FUNCTION__, __LINE__,"FIELD_WIFI_SERVICE get failed Excption"); } if(FIELD_WIFI_SERVICE == 0){ return getErrorInfo(env,__FILE__, __FUNCTION__, __LINE__,"FIELD_WIFI_SERVICE get failed"); } jstring VALUE_WIFI_SERVICE = (jstring) env->GetStaticObjectField(system_context, FIELD_WIFI_SERVICE); if(env->ExceptionCheck()){ env->ExceptionClear(); return getErrorInfo(env,__FILE__, __FUNCTION__, __LINE__,"VALUE_WIFI_SERVER get failed Excption"); } if(VALUE_WIFI_SERVICE == 0){ return getErrorInfo(env,__FILE__, __FUNCTION__, __LINE__,"VALUE_WIFI_SERVICE get failed "); } jmethodID METHOD_getSystemService = env->GetMethodID(system_context,"getSystemService","(Ljava/lang/String;)Ljava/lang/Object;"); if(env->ExceptionCheck()){ env->ExceptionClear(); return getErrorInfo(env,__FILE__, __FUNCTION__, __LINE__,"METHOD_getSystemService get failed Excption"); } if(METHOD_getSystemService == 0){ return getErrorInfo(env,__FILE__, __FUNCTION__, __LINE__,"METHOD_getSystemService get failed" ); } jobject OBJECT_WIFI_MANAGER = env->CallObjectMethod(mContext,METHOD_getSystemService,VALUE_WIFI_SERVICE); if(env->ExceptionCheck()){ env->ExceptionClear(); return getErrorInfo(env,__FILE__, __FUNCTION__, __LINE__,"OBJECT_WIFI_MANAGER get failed Excption"); } if(OBJECT_WIFI_MANAGER == 0){ return getErrorInfo(env,__FILE__, __FUNCTION__, __LINE__,"OBJECT_WIFI_MANAGER get failed "); } jclass CLASS_WIFI_MANAGER = env->GetObjectClass(OBJECT_WIFI_MANAGER); if(env->ExceptionCheck()){ env->ExceptionClear(); return getErrorInfo(env,__FILE__, __FUNCTION__, __LINE__,"CLASS_WIFI_MANAGER get failed Excption"); } if(CLASS_WIFI_MANAGER == 0){ return getErrorInfo(env,__FILE__, __FUNCTION__, __LINE__,"CLASS_WIFI_MANAGER get failed"); } jmethodID METHOD_WIFI_INFO = env->GetMethodID(CLASS_WIFI_MANAGER,"getConnectionInfo","()Landroid/net/wifi/WifiInfo;"); if(env->ExceptionCheck()){ env->ExceptionClear(); return getErrorInfo(env,__FILE__, __FUNCTION__, __LINE__,"METHOD_WIFI_INFO get failed Excption"); } if(METHOD_WIFI_INFO == 0){ return getErrorInfo(env,__FILE__, __FUNCTION__, __LINE__,"METHOD_WIFI_INFO get failed "); } jobject OBJECT_WIFI_INFO = env->CallObjectMethod(OBJECT_WIFI_MANAGER,METHOD_WIFI_INFO); if(env->ExceptionCheck()){ env->ExceptionClear(); return getErrorInfo(env,__FILE__, __FUNCTION__, __LINE__,"OBJECT_WIFI_INFO get failed Excption"); } if(OBJECT_WIFI_INFO == 0){ return getErrorInfo(env,__FILE__, __FUNCTION__, __LINE__,"OBJECT_WIFI_INFO get failed "); } jclass CLASS_WIFI_INFO = env->GetObjectClass(OBJECT_WIFI_INFO ); if(env->ExceptionCheck()){ env->ExceptionClear(); return getErrorInfo(env,__FILE__, __FUNCTION__, __LINE__,"CLASS_WIFI_INFO get failed Excption"); } if(CLASS_WIFI_INFO == 0){ return getErrorInfo(env,__FILE__, __FUNCTION__, __LINE__,"CLASS_WIFI_INFO get failed "); } jmethodID METHOD_MAC_getMacAddress = env->GetMethodID(CLASS_WIFI_INFO , "getMacAddress", "()Ljava/lang/String;"); if(env->ExceptionCheck()){ env->ExceptionClear(); return getErrorInfo(env,__FILE__, __FUNCTION__, __LINE__,"METHOD_MAC_getMacAddress get failed Excption"); } if(METHOD_MAC_getMacAddress == 0){ return getErrorInfo(env,__FILE__, __FUNCTION__, __LINE__,"METHOD_MAC_getMacAddress get failed "); } jstring mac_str = (jstring) env->CallObjectMethod(OBJECT_WIFI_INFO , METHOD_MAC_getMacAddress); if(env->ExceptionCheck()){ env->ExceptionClear(); return getErrorInfo(env,__FILE__, __FUNCTION__, __LINE__,"mac_str get failed Excption"); } if(mac_str == NULL){ return charsTojstring(env,"no_data"); } return mac_str; } ================================================ FILE: securitysdkcore/src/main/cpp/BasicInfor.h ================================================ // // Created by Lin on 2017/11/13. // #include #ifndef SECURITYSDK_BASICINFO_H #define SECURITYSDK_BASICINFO_H #define CPU_FILE_PATH "/proc/cpuinfo" jstring getIMEI(JNIEnv *env,jobject mContext); jstring getIMSI(JNIEnv *env,jobject mContext); jstring getTelephoneNum(JNIEnv *env,jobject mContext); jstring getSerial(JNIEnv *env); jstring getBoard(JNIEnv *env); jstring getBootLoader(JNIEnv *env); jstring getBrand(JNIEnv *env); jstring getDevice(JNIEnv *env); jstring getHardware(JNIEnv *env); jstring getModel(JNIEnv *env); jstring getProduct(JNIEnv *env); jstring getCpuInfo(JNIEnv *env); jstring getWlan0Mac(JNIEnv *env,jobject mContext); #endif //SECURITYSDK_BASICINFO_H ================================================ FILE: securitysdkcore/src/main/cpp/InjectDetected.cpp ================================================ // // Created by ffthy on 21/11/2017. // #include #include #include #include #include "Util.h" #include "InjectDetected.h" using namespace std; /** * 存在就是1 不存在就是-1 * @return */ int getimagebase() { pid_t pid = getpid(); char fileName[256] = {0}; sprintf(fileName, "/proc/%d/maps", pid); ifstream in(fileName); string line; char* charline; if(in) // 有该文件 { while (getline (in, line)) // line中不包括每行的换行符 { //LOGD(line.data()); int length=strlen(line.data()); charline=new char(length+1); strcpy(charline,line.data()); strtok(charline, ""); LOGD("ddd%s",charline); delete[] charline; if(line.find("com.saurik.substrate",0)!=-1||line.find("XposedBridge.jar",0)!=-1){ return 1; } } return -1; } } ================================================ FILE: securitysdkcore/src/main/cpp/InjectDetected.h ================================================ // // Created by ffthy on 21/11/2017. // #ifndef SECURITYSDK_INJECTDETECTED_H #define SECURITYSDK_INJECTDETECTED_H int getimagebase(); #endif //SECURITYSDK_INJECTDETECTED_H ================================================ FILE: securitysdkcore/src/main/cpp/SimulatorDetected.cpp ================================================ // // Created by Lin on 2017/11/13. // #include #include #include #include #include "BasicInfor.h" #include "SimulatorDetected.h" #include "Util.h" /* * 获取模拟器特殊文件检测的权值 * @return 失败返回0 */ int getWeightByCpu(){ FILE *fp = fopen(CPU_FILE_PATH,"r"); if( fp == NULL ){ return 0; } char line[1024] = "\0"; ///文件包含函数 while (fgets(line, 1024, fp)) { if (strstr(line,"Intel") || strstr(line,"ranchud") || strstr(line,"amd")) { //转化为数值型 fclose(fp); return 15; } } fclose(fp); return 0; } /* * 获取模拟器特殊文件检测的权值 * @return 失败返回0 */ int getWeightByUniqueFile() { //检测特殊文件 char *qemu_path[3]={ "/system/lib/libc_malloc_debug_qemu.so", "/sys/qemu_trace", "/system/bin/qemu-props" }; for(int i = 0; i < 3; i ++){ FILE *fp = fopen((const char *) qemu_path[i], "r"); if(fp != NULL){ fclose(fp); return 15; } } return 0; } /* * 获取模拟器驱动文件检测的权值 * @return 失败返回0 */ int getWeightByQeumDreiver() { //模拟器驱动文件 FILE *fp = fopen("/proc/tty/drivers","r"); if( fp == NULL ){ return 0; } char line[1024] = "\0"; ///文件包含函数 while (fgets(line, 1024, fp)) { if (strstr(line,"goldfish")) { //转化为数值型 fclose(fp); return 15; } } fclose(fp); return 0; } /* * 获取pipe文件检测的权值 * @return 失败返回0 */ int getWeightByPipeFile() { //检测pipe char *pipe[2] = { "/dev/socket/qemud","/dev/qemu_pipe" }; for(int i = 0; i < 2; i ++){ FILE *fp = fopen((const char *) pipe[i], "r"); if(fp != NULL){ fclose(fp); return 15; } } free(pipe); return 0; } /* * 获取蓝牙文件检测的权值 * @return 失败返回0 */ int getWeightByBlueStack() { char *bluestacks_path[22]={ "/data/app/com.bluestacks.appmart-1.apk", "/data/app/com.bluestacks.BstCommandProcessor-1.apk", "/data/app/com.bluestacks.help-1.apk", "/data/app/com.bluestacks.home-1.apk", "/data/app/com.bluestacks.s2p-1.apk", "/data/app/com.bluestacks.searchapp-1.apk", "/data/bluestacks.prop", "/data/data/com.androVM.vmconfig", "/data/data/com.bluestacks.accelerometerui", "/data/data/com.bluestacks.appfinder", "/data/data/com.bluestacks.appmart", "/data/data/com.bluestacks.appsettings", "/data/data/com.bluestacks.BstCommandProcessor", "/data/data/com.bluestacks.bstfolder", "/data/data/com.bluestacks.help", "/data/data/com.bluestacks.home", "/data/data/com.bluestacks.s2p", "/data/data/com.bluestacks.searchapp", "/data/data/com.bluestacks.settings", "/data/data/com.bluestacks.setup", "/data/data/com.bluestacks.spotlight", "/mnt/prebundledapps/bluestacks.prop.orig" }; for(int i = 0; i < 22; i ++){ FILE *fp = fopen((const char *) bluestacks_path[i], "r"); if(fp != NULL){ fclose(fp); return 0; } } return 15; } /* * 获取模拟器IMEI检测的权值 * @param env * @param mContext * @return 失败返回0 */ int getWeightByIMEI(JNIEnv *env,jobject mContext){ jstring ret_str = getIMEI(env,mContext); if(strcmp(jstringToChar(env,ret_str),"000000000000000")){ return -1; }else{ return 5; } } /* * 获取模拟器IMSI检测的权值 * @param env * @param mContext * @return 失败返回0 */ int getWeightByIMSI(JNIEnv *env,jobject mContext){ jstring ret_str = getIMSI(env,mContext); if(strcmp(jstringToChar(env,ret_str),"310260000000000")){ return -1; }else{ return 5; } } /* * 获取模拟器TelephoneNum检测的权值 * * @param env * @param mContext * @return 失败返回0 */ int getWeightByTelephoneNum(JNIEnv *env,jobject mContext){ jstring ret_str = getTelephoneNum(env,mContext); char *simulatorTelephoneNum[16] = { "15555215554","15555215556","15555215558","15555215560", "15555215562","15555215564", "15555215566","15555215568", "15555215570","15555215572", "15555215574","15555215576", "15555215578","15555215580","15555215582","15555215584" }; for(int i = 0; i < 16;i ++){ if(strcmp(simulatorTelephoneNum[i],jstringToChar(env,ret_str))){ return 5; } } return 0; } /* * 获取模拟器Serial检测的权值 * @param env * @return 失败返回0 */ int getWeightBySerial(JNIEnv *env){ jstring ret_str = getSerial(env); if(strcmp(jstringToChar(env,ret_str),"unknown") == 0){ return 5; }else{ return 0; } } /* * 获取模拟器Board检测的权值 * @param env * @return 失败返回0 */ int getWeightByBoard(JNIEnv *env){ jstring ret_str = getBoard(env); if(strcmp(jstringToChar(env,ret_str),"unknown") == 0){ return 2; }else{ return 0; } } /* * 获取模拟器Bootloader检测的权值 * @param env * @return 失败返回0 */ int getWeightByBootLoader(JNIEnv *env){ jstring ret_str = getBootLoader(env); if(strcmp(jstringToChar(env,ret_str),"unknown") == 0){ return 2; }else{ return 0; } } /* * 获取模拟器Hardware检测的权值 * @param env * @return 失败返回0 */ int getWeightByHardware(JNIEnv *env){ jstring ret_str = getHardware(env); if(strcmp(jstringToChar(env,ret_str),"goldfish") || strcmp(jstringToChar(env,ret_str),"ranchu")){ return 2; }else{ return 0; } } /* * 获取模拟器Hardware检测的权值 * @param env * @return 失败返回0 */ int getWeightByDevice(JNIEnv *env){ jstring ret_str = getDevice(env); if(strstr(jstringToChar(env,ret_str),"generic")){ return 2; }else{ return 0; } } /* * 获取模拟器Product检测的权值 * @param env * @return 失败返回0 */ int getWeightByProduct(JNIEnv *env){ jstring ret_str = getProduct(env); if(strstr(jstringToChar(env,ret_str),"sdk")){ return 2; }else{ return 0; } } /* * 获取模拟器Model检测的权值 * @param env * @return 失败返回0 */ int getWeightByModel(JNIEnv *env){ jstring ret_str = getModel(env); if(strstr(jstringToChar(env,ret_str),"sdk") || strstr(jstringToChar(env,ret_str),"SDK")){ return 2; }else{ return 0; } } /* * 获取模拟器Brand检测的权值 * @param env * @return 失败返回0 */ int getWeightByBrand(JNIEnv *env){ jstring ret_str = getBrand(env); if(strstr(jstringToChar(env,ret_str),"generic") || strstr(jstringToChar(env,ret_str),"Android")){ return 2; }else{ return 0; } } /* * 获取模拟器Brand检测的权值 * @param env * @param mContext * @return 失败返回0 */ int getWeightByWlan0Mac(JNIEnv *env,jobject mContext){ jstring retStr = getWlan0Mac(env,mContext); if(strcmp(jstringToChar(env,retStr),"no data")){ return 10; } return 0; } /* * 判断是否为模拟器 * @param env * @param mContext * @param 模拟器的阈值, >30 为模拟器,30>x>10 为疑似模拟器,<10为不是模拟器 * @return 1 为 模拟器 * -1 为不是模拟器 * 0 为疑似模拟器 */ int simulatorDetected(JNIEnv *env,jobject mContext,const int threshold) { //先判断蓝牙文件 int weight_total = 0; weight_total += getWeightByBlueStack(); if( weight_total >= threshold){ return 1; } //pipe文件 weight_total += getWeightByPipeFile(); if(weight_total >= threshold){ return 1; } //特殊文件 weight_total += getWeightByUniqueFile(); if(weight_total >= threshold){ return 1; } //驱动文件 weight_total += getWeightByQeumDreiver(); if(weight_total >= threshold){ return 1; } //Cpu信息 weight_total += getWeightByCpu(); if(weight_total >= threshold){ return 1; } //waln0mac weight_total += getWeightByWlan0Mac(env,mContext); if(weight_total >= threshold){ return 1; } //IMEI weight_total += getWeightByIMEI(env,mContext); if(weight_total >= threshold){ return 1; } //IMSI weight_total += getWeightByIMSI(env,mContext); if(weight_total >= threshold){ return 1; } //TELEPHONENUM weight_total += getWeightByTelephoneNum(env,mContext); if(weight_total >= threshold){ return 1; } //SERIAL weight_total += getWeightBySerial(env); if(weight_total >= threshold){ return 1; } //BOARD weight_total += getWeightByBoard(env); if(weight_total >= threshold){ return 1; } //MODEL weight_total += getWeightByModel(env); if(weight_total >= threshold){ return 1; } //product weight_total += getWeightByProduct(env); if(weight_total >= threshold){ return 1; } //HARDWARE weight_total += getWeightByHardware(env); if(weight_total >= threshold){ return 1; } //Brand weight_total += getWeightByBrand(env); if(weight_total >= threshold){ return 1; } //Device weight_total += getWeightByDevice(env); if(weight_total >= threshold){ return 1; } //Brand weight_total += getWeightByBootLoader(env); if(weight_total >= threshold){ return 1; } if(threshold > weight_total > 10){ return 0; } return -1; } ================================================ FILE: securitysdkcore/src/main/cpp/SimulatorDetected.h ================================================ // // Created by Lin on 2017/11/13. // #include #ifndef SECURITYSDK_SIMULATORDETECTED_H #define SECURITYSDK_SIMULATORDETECTED_H int getWeightByPipeFile(); int getWeightByUniqueFile(); int getWeightByQeumDreiver(); int getWeightByBlueStack(); int getWeightByWlan0Mac(JNIEnv *env,jobject mContext); int getWeightByCpu(); int getWeightByIMEI(JNIEnv *env,jobject mContext); int getWeightByIMSI(JNIEnv *env,jobject mContext); int getWeightByTelephoneNum(JNIEnv *env,jobject mContext); int getWeightBySerial(JNIEnv *env); int getWeightByBoard(JNIEnv *env); int getWeightByBootLoader(JNIEnv *env); int getWeightByHardware(JNIEnv *env); int getWeightByProduct(JNIEnv *env); int getWeightByModel(JNIEnv *env); int getWeightByBrand(JNIEnv *env); int getWeightByDevice(JNIEnv *env); int simulatorDetected(JNIEnv *env,jobject mContext,const int threshold); #endif //SECURITYSDK_SIMULATORDETECTED_H ================================================ FILE: securitysdkcore/src/main/cpp/SoDecode/CMakeLists.txt ================================================ # For more information about using CMake with Android Studio, read the # documentation: https://d.android.com/studio/projects/add-native-code.html # Sets the minimum version of CMake required to build the native library. cmake_minimum_required(VERSION 3.4.1) set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/../jniLibs/${ANDROID_ABI}) # Creates and names a library, sets it as either STATIC # or SHARED, and provides the relative paths to its source code. # You can define multiple libraries, and CMake builds them for you. # Gradle automatically packages shared libraries with your APK. add_library( # Sets the name of the library. androidso # Sets the library as a shared library. SHARED # Provides a relative path to your source file(s). src/main/cpp/native-lib.cpp src/main/cpp/Rc4Util.cpp src/main/cpp/Rc4Util.h ) # Searches for a specified prebuilt library and stores the path as a # variable. Because CMake includes system libraries in the search path by # default, you only need to specify the name of the public NDK library # you want to add. CMake verifies that the library exists before # completing its build. find_library( # Sets the name of the path variable. log-lib # Specifies the name of the NDK library that # you want CMake to locate. log ) # Specifies libraries CMake should link to your target library. You # can link multiple libraries, such as libraries you define in this # build script, prebuilt third-party libraries, or system libraries. target_link_libraries( # Specifies the target library. androidso # Links the target library to the log library # included in the NDK. ${log-lib} ) ================================================ FILE: securitysdkcore/src/main/cpp/SoDecode/Rc4Util.cpp ================================================ // // Created by Lin on 2018/1/20. // #include "Rc4Util.h" #include void swap_bytes(u_char *a, u_char *b) { u_char temp; temp = *a; *a = *b; *b = temp; } /* * Initialize an RC4 state buffer using the supplied key, * which can have arbitrary length. */ void rc4_init(struct rc4_state *const state, const u_char *key, int keylen) { u_char j; int i; /* Initialize state with identity permutation */ for (i = 0; i < 256; i++) state->perm[i] = (u_char)i; state->index1 = 0; state->index2 = 0; /* Randomize the permutation using key data */ for (j = i = 0; i < 256; i++) { j += state->perm[i] + key[i % keylen]; swap_bytes(&state->perm[i], &state->perm[j]); } } /* * Encrypt some data using the supplied RC4 state buffer. * The input and output buffers may be the same buffer. * Since RC4 is a stream cypher, this function is used * for both encryption and decryption. */ void rc4_crypt(struct rc4_state *const state, u_char *inbuf, int buflen) { int i; u_char j; for (i = 0; i < buflen; i++) { /* Update modification indicies */ state->index1++; state->index2 += state->perm[state->index1]; /* Modify permutation */ swap_bytes(&state->perm[state->index1], &state->perm[state->index2]); /* Encrypt/decrypt next byte */ j = state->perm[state->index1] + state->perm[state->index2]; inbuf[i] = inbuf[i] ^ state->perm[j]; } } /** * * @param inbuf * @param buflen * @param key * @param keylen */ void rc4_util(u_char *inbuf, int buflen,const u_char *key, int keylen){ struct rc4_state * state = (struct rc4_state *)malloc(258); rc4_init(state,key, keylen); rc4_crypt(state, inbuf, 184); } ================================================ FILE: securitysdkcore/src/main/cpp/SoDecode/Rc4Util.h ================================================ // // Created by Lin on 2018/1/20. // #include #ifndef ANDROIDSOENHANCE2_RC4UTIL_H #define ANDROIDSOENHANCE2_RC4UTIL_H struct rc4_state { u_char perm[256]; u_char index1; u_char index2; }; extern void rc4_init(struct rc4_state *state, const u_char *key, int keylen); extern void rc4_crypt(struct rc4_state *state, const u_char *inbuf, u_char *outbuf, int buflen); void rc4_util(u_char *inbuf, int buflen,const u_char *key, int keylen); #endif //ANDROIDSOENHANCE2_RC4UTIL_H ================================================ FILE: securitysdkcore/src/main/cpp/SoDecode/build.gradle ================================================ package SoDecode apply plugin: 'com.android.application' android { compileSdkVersion 26 defaultConfig { applicationId "com.example.lin.androidsoaddrc4" minSdkVersion 22 targetSdkVersion 26 versionCode 1 versionName "1.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" // externalNativeBuild { // cmake { // cppFlags "" // abiFilters 'armeabi',"armeabi-v7a" // } // } } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } sourceSets { main { jniLibs.srcDirs = ['libs'] } } // externalNativeBuild { // cmake { // path "CMakeLists.txt" // } // } } dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation 'com.android.support:appcompat-v7:26.1.0' implementation 'com.android.support.constraint:constraint-layout:1.0.2' testImplementation 'junit:junit:4.12' androidTestImplementation 'com.android.support.test:runner:1.0.1' androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1' } ================================================ FILE: securitysdkcore/src/main/cpp/SoDecode/native-lib.cpp ================================================ #include #include #include #include #include #include #include #include #include #include #include "Rc4Util.h" #define FUN_NAME "Java_com_example_lin_androidsoaddrc4_MainActivity_stringFromJNI" #define RC4_KEY "123456" #define RC4_KEY_LEN 6 #define SO_NAME "libandroidso.so" #define DEBUG typedef struct _funcInfo{ Elf32_Addr st_value; Elf32_Word st_size; }funcInfo; void init_getString() __attribute__((constructor)); /** * 打印日志 * @param msg */ static void print_debug(const char *msg){ #ifdef DEBUG __android_log_print(ANDROID_LOG_INFO, "JNITag", "%s", msg); #endif } /** *hash运算 * @param _name * @return */ static unsigned elfhash(const char *_name) { const unsigned char *name = (const unsigned char *) _name; unsigned h = 0, g; while(*name) { h = (h << 4) + *name++; g = h & 0xf0000000; h ^= g; h ^= g >> 24; } return h; } /** * 获取当前链接库的地址 * @return */ static unsigned int getLibAddr(){ unsigned int ret = 0; char name[] = SO_NAME; char buf[4096], *temp; int pid; FILE *fp; pid = getpid(); sprintf(buf, "/proc/%d/maps", pid); fp = fopen(buf, "r"); if(fp == NULL) { puts("open failed"); goto _error; } while(fgets(buf, sizeof(buf), fp)){ if(strstr(buf, name)){ temp = strtok(buf, "-"); ret = strtoul(temp, NULL, 16); break; } } _error: fclose(fp); return ret; } /** *获取待解密的函数的地址信息 * @param base * @param funcName * @param info * @return */ static char getTargetFuncInfo(unsigned long base, const char *funcName, funcInfo *info){ char flag = -1, *dynstr; int i; Elf32_Ehdr *ehdr; Elf32_Phdr *phdr; Elf32_Off dyn_vaddr; Elf32_Word dyn_size, dyn_strsz; Elf32_Dyn *dyn; Elf32_Addr dyn_symtab, dyn_strtab, dyn_hash; Elf32_Sym *funSym; unsigned funHash, nbucket; unsigned *bucket, *chain; ehdr = (Elf32_Ehdr *)base; phdr = (Elf32_Phdr *)(base + ehdr->e_phoff); for (i = 0; i < ehdr->e_phnum; ++i) { if(phdr->p_type == PT_DYNAMIC){ flag = 0; print_debug("Find .dynamic segment"); break; } phdr ++; } if(flag) return -1; dyn_vaddr = phdr->p_vaddr + base; dyn_size = phdr->p_filesz; __android_log_print(ANDROID_LOG_INFO, "JNITag", "dyn_vadd = 0x%x, dyn_size = 0x%x", dyn_vaddr, dyn_size); flag = 0; for (i = 0; i < dyn_size / sizeof(Elf32_Dyn); ++i) { dyn = (Elf32_Dyn *)(dyn_vaddr + i * sizeof(Elf32_Dyn)); if(dyn->d_tag == DT_SYMTAB){ dyn_symtab = (dyn->d_un).d_ptr; flag += 1; __android_log_print(ANDROID_LOG_INFO, "JNITag", "Find .dynsym section, addr = 0x%x\n", dyn_symtab); } if(dyn->d_tag == DT_HASH){ dyn_hash = (dyn->d_un).d_ptr; flag += 2; __android_log_print(ANDROID_LOG_INFO, "JNITag", "Find .hash section, addr = 0x%x\n", dyn_hash); } if(dyn->d_tag == DT_STRTAB){ dyn_strtab = (dyn->d_un).d_ptr; flag += 4; __android_log_print(ANDROID_LOG_INFO, "JNITag", "Find .dynstr section, addr = 0x%x\n", dyn_strtab); } if(dyn->d_tag == DT_STRSZ){ dyn_strsz = (dyn->d_un).d_val; flag += 8; __android_log_print(ANDROID_LOG_INFO, "JNITag", "Find strsz size = 0x%x\n", dyn_strsz); } } if((flag & 0x0f) != 0x0f){ print_debug("Find needed .section failed\n"); return -1; } dyn_symtab += base; dyn_hash += base; dyn_strtab += base; dyn_strsz += base; funHash = elfhash(funcName); funSym = (Elf32_Sym *) dyn_symtab; dynstr = (char*) dyn_strtab; nbucket = *((int *) dyn_hash); bucket = (unsigned int *)(dyn_hash + 8); chain = (unsigned int *)(dyn_hash + 4 * (2 + nbucket)); flag = -1; __android_log_print(ANDROID_LOG_INFO, "JNITag", "hash = 0x%x, nbucket = 0x%x\n", funHash, nbucket); int mod = (funHash % nbucket); __android_log_print(ANDROID_LOG_INFO, "JNITag", "mod = %d\n", mod); __android_log_print(ANDROID_LOG_INFO, "JNITag", "i = 0x%d\n", bucket[mod]); for(i = bucket[mod]; i != 0; i = chain[i]){ __android_log_print(ANDROID_LOG_INFO, "JNITag", "Find index = %d\n", i); if(strcmp(dynstr + (funSym + i)->st_name, funcName) == 0){ flag = 0; __android_log_print(ANDROID_LOG_INFO, "JNITag", "Find %s\n", funcName); break; } } if(flag) return -1; info->st_value = (funSym + i)->st_value; info->st_size = (funSym + i)->st_size; __android_log_print(ANDROID_LOG_INFO, "JNITag", "st_value = %d, st_size = %d", info->st_value, info->st_size); return 0; // _error: // return -1; } /** * 解密目标函数 */ void init_getString(){ const char target_fun[] = FUN_NAME; funcInfo info; int i; unsigned int npage, base = getLibAddr(); __android_log_print(ANDROID_LOG_INFO, "JNITag", "base addr = 0x%x", base); if(getTargetFuncInfo(base, target_fun, &info) == -1){ print_debug("Find Java_com_example_shelldemo2_MainActivity_getString failed"); return ; } npage = info.st_size / PAGE_SIZE + ((info.st_size % PAGE_SIZE == 0) ? 0 : 1); __android_log_print(ANDROID_LOG_INFO, "JNITag", "npage = 0x%d", npage); __android_log_print(ANDROID_LOG_INFO, "JNITag", "npage = 0x%d", PAGE_SIZE); if(mprotect((void *) ((base + info.st_value) / PAGE_SIZE * PAGE_SIZE), 4096*npage, PROT_READ | PROT_EXEC | PROT_WRITE) != 0){ print_debug("mem privilege change failed"); } char *addr = (char*)(base + info.st_value -1); rc4_util((u_char *)addr,info.st_size,(u_char *)RC4_KEY,RC4_KEY_LEN); // for( i = 0;i < info.st_size; i++ ){ // __android_log_print(ANDROID_LOG_INFO, "JNITag", "解密后的字节 = 0x%02x",addr[i] ); // } if(mprotect((void *) ((base + info.st_value) / PAGE_SIZE * PAGE_SIZE), 4096*npage, PROT_READ | PROT_EXEC) != 0){ print_debug("mem privilege change failed"); } } extern "C" JNIEXPORT jstring JNICALL Java_com_example_lin_androidsoaddrc4_MainActivity_stringFromJNI( JNIEnv *env, jobject /* this */) { std::string hello = "Hello from C++"; return env->NewStringUTF(hello.c_str()); } ================================================ FILE: securitysdkcore/src/main/cpp/Util.cpp ================================================ // // Created by Lin on 2017/11/15. // #include #include #include "Util.h" /** * jni中string转化成char[] * 由于jvm和c++对中文的编码不一样,暂不支持 * @param env * @param jstr * @return 失败返回NULL */ char* jstringToChar(JNIEnv* env, jstring jstr) { const char *c_str = NULL; jboolean isCopy = NULL; // 返回JNI_TRUE表示原字符串的拷贝,返回JNI_FALSE表示返回原字符串的指针 c_str = env->GetStringUTFChars(jstr, &isCopy); printf("isCopy:%d\n",isCopy); if(c_str == NULL) return NULL; int len = strlen(c_str); char* rtn = new char[len + 1]; memset(rtn,0,len + 1); memcpy(rtn, c_str, len); rtn[len] = 0; env->ReleaseStringUTFChars(jstr, c_str); return rtn; } /** * //把char数组转化成jstring对象,失败返回NULL * @param env * @param str * @return */ jstring charsTojstring(JNIEnv* env, char* str){ jstring rtn = NULL; int slen = strlen(str); if (slen != 0) rtn = env->NewStringUTF(str); return rtn; } /** * 给java层返回native层的错误信息 * @param env * @param file 失败时的类名 * @param func 失败时的函数 * @param line 失败时的代码行数 * @param msg 函数执行失败的原因 * @return 返回失败原因的string对象,执行出错时返回NULL */ jstring getErrorInfo(JNIEnv* env,char *file, const char *func, int line, char *msg) { jstring errinfoStr = NULL; int errinfo_len = strlen(file) + strlen(func) + sizeof(line) + strlen(msg); char *errinfo = (char *) malloc(errinfo_len + 1); if(errinfo == NULL) return NULL; sprintf(errinfo,"error: %s,%s,%d,%s",file,func,line, msg); errinfoStr = charsTojstring(env, errinfo); free(errinfo); return errinfoStr; } /** * //byte数组转化成char数组 * @param env * @param bytearray * @return */ char* ConvertJByteaArrayToChars(JNIEnv *env, jbyteArray bytearray) { char *chars = NULL; jbyte *bytes; bytes = env->GetByteArrayElements(bytearray, 0); int chars_len = env->GetArrayLength(bytearray); chars = new char[chars_len + 1]; memset(chars, 0, chars_len + 1); memcpy(chars, bytes, chars_len); chars[chars_len] = 0; env->ReleaseByteArrayElements(bytearray, bytes, 0); return chars; } ================================================ FILE: securitysdkcore/src/main/cpp/Util.h ================================================ /** * Created by plldzy on 17-11-15. 基础工具类: 1)字符串jstring处理; 2)生成错误信息; 3)byte数组和char数组转换; 4)log信息输出 */ #include #include #ifndef SECURITYSDK_BASICUTIL_H #define SECURITYSDK_BASICUTIL_H #define LOG_TAG "security-sdk-core" // 这个是自定义的LOG的标识 #define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG, __VA_ARGS__) #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG, __VA_ARGS__) #define LOGW(...) __android_log_print(ANDROID_LOG_WARN,LOG_TAG, __VA_ARGS__) #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG, __VA_ARGS__) #define LOGF(...) __android_log_print(ANDROID_LOG_FATAL,LOG_TAG, __VA_ARGS__) char* jstringToChar(JNIEnv* env, jstring jstr); jstring charsTojstring(JNIEnv* env, char* str); jstring getErrorInfo(JNIEnv* env,char *file, const char *func, int line, char *msg); char* ConvertJByteaArrayToChars(JNIEnv *env, jbyteArray bytearray); #endif //SECURITYSDK_BASICUTIL_H ================================================ FILE: securitysdkcore/src/main/cpp/getSign.cpp ================================================ #include "getSign.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "Util.h" using namespace std; //签名信息 const char *UserSign_sha1="E83868175D274D31A62D3BC667D1289748E3779D"; const char hexcode[] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; extern "C" char* getAppSignSha1(JNIEnv *env, jobject context_object){ //上下文对象 jclass context_class = env->GetObjectClass(context_object); //反射获取PackageManager //context.getPackageManager() jmethodID methodId = env->GetMethodID(context_class, "getPackageManager", "()Landroid/content/pm/PackageManager;"); jobject package_manager = env->CallObjectMethod(context_object, methodId); if (package_manager == NULL) { LOGD("package_manager is NULL!!!"); return NULL; } //反射获取包名 //context.getPackageName() methodId = env->GetMethodID(context_class, "getPackageName", "()Ljava/lang/String;"); jstring package_name = (jstring)env->CallObjectMethod(context_object, methodId); if (package_name == NULL) { LOGD("package_name is NULL!!!"); return NULL; } env->DeleteLocalRef(context_class); //获取PackageInfo对象 //PackageManager.getPackageInfo(Sting, int) jclass pack_manager_class = env->GetObjectClass(package_manager); methodId = env->GetMethodID(pack_manager_class, "getPackageInfo", "(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;"); env->DeleteLocalRef(pack_manager_class); jobject package_info = env->CallObjectMethod(package_manager, methodId, package_name, 0x40); if (package_info == NULL) { LOGD("getPackageInfo() is NULL!!!"); return NULL; } env->DeleteLocalRef(package_manager); //获取签名信息 //PackageInfo.signatures[0] jclass package_info_class = env->GetObjectClass(package_info); jfieldID fieldId = env->GetFieldID(package_info_class, "signatures", "[Landroid/content/pm/Signature;"); env->DeleteLocalRef(package_info_class); jobjectArray signature_object_array = (jobjectArray)env->GetObjectField(package_info, fieldId); if (signature_object_array == NULL) { LOGD("signature is NULL!!!"); return NULL; } jobject signature_object = env->GetObjectArrayElement(signature_object_array, 0); env->DeleteLocalRef(package_info); //签名信息转换成sha1值 //Signature.toByteArray() jclass signature_class = env->GetObjectClass(signature_object); methodId = env->GetMethodID(signature_class, "toByteArray", "()[B"); env->DeleteLocalRef(signature_class); jbyteArray signature_byte = (jbyteArray) env->CallObjectMethod(signature_object, methodId); jclass message_digest_class=env->FindClass("java/security/MessageDigest"); methodId=env->GetStaticMethodID(message_digest_class,"getInstance","(Ljava/lang/String;)Ljava/security/MessageDigest;"); jstring sha1_jstring=env->NewStringUTF("SHA1"); jobject sha1_digest=env->CallStaticObjectMethod(message_digest_class,methodId,sha1_jstring); //sha1.digest (signature_byte) methodId=env->GetMethodID(message_digest_class,"digest","([B)[B"); jbyteArray sha1_byte=(jbyteArray)env->CallObjectMethod(sha1_digest,methodId,signature_byte); env->DeleteLocalRef(message_digest_class); //转换成char jsize array_size=env->GetArrayLength(sha1_byte); jbyte* sha1 =env->GetByteArrayElements(sha1_byte,NULL); char *hex_sha=new char[array_size*2+1]; for (int i = 0; i GetObjectClass(context_object); //反射获取PackageManager //context.getPackageManager() jmethodID methodId = env->GetMethodID(context_class, "getPackageManager", "()Landroid/content/pm/PackageManager;"); jobject package_manager = env->CallObjectMethod(context_object, methodId); if (package_manager == NULL) { LOGD("package_manager is NULL!!!"); return NULL; } jstring package_name=pkgname; //获取PackageInfo对象 //PackageManager.getPackageInfo(Sting, int) jclass pack_manager_class = env->GetObjectClass(package_manager); methodId = env->GetMethodID(pack_manager_class, "getPackageInfo", "(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;"); env->DeleteLocalRef(pack_manager_class); jobject package_info = env->CallObjectMethod(package_manager, methodId, package_name, 0x40); if (package_info == NULL) { LOGD("getPackageInfo() is NULL!!!"); return NULL; } env->DeleteLocalRef(package_manager); //获取签名信息 //PackageInfo.signatures[0] jclass package_info_class = env->GetObjectClass(package_info); jfieldID fieldId = env->GetFieldID(package_info_class, "signatures", "[Landroid/content/pm/Signature;"); env->DeleteLocalRef(package_info_class); jobjectArray signature_object_array = (jobjectArray)env->GetObjectField(package_info, fieldId); if (signature_object_array == NULL) { LOGD("signature is NULL!!!"); return NULL; } jobject signature_object = env->GetObjectArrayElement(signature_object_array, 0); env->DeleteLocalRef(package_info); //签名信息转换成sha1值 //Signature.toByteArray() jclass signature_class = env->GetObjectClass(signature_object); methodId = env->GetMethodID(signature_class, "toByteArray", "()[B"); env->DeleteLocalRef(signature_class); jbyteArray signature_byte = (jbyteArray) env->CallObjectMethod(signature_object, methodId); //MessageDigest.getInstance("SHA1") jclass message_digest_class=env->FindClass("java/security/MessageDigest"); methodId=env->GetStaticMethodID(message_digest_class,"getInstance","(Ljava/lang/String;)Ljava/security/MessageDigest;"); jstring sha1_jstring=env->NewStringUTF("SHA1"); jobject sha1_digest=env->CallStaticObjectMethod(message_digest_class,methodId,sha1_jstring); //sha1.digest (signature_byte) methodId=env->GetMethodID(message_digest_class,"digest","([B)[B"); jbyteArray sha1_byte=(jbyteArray)env->CallObjectMethod(sha1_digest,methodId,signature_byte); env->DeleteLocalRef(message_digest_class); //转换成char jsize array_size=env->GetArrayLength(sha1_byte); jbyte* sha1 =env->GetByteArrayElements(sha1_byte,NULL); char *hex_sha=new char[array_size*2+1]; for (int i = 0; i #ifndef SECURITYSDK_GETSIGN_H #define SECURITYSDK_GETSIGN_H extern "C" char* getAppSignSha1(JNIEnv *env, jobject context_object); char* getAppSignSha1(JNIEnv *env, jobject context_object,jstring pkgname); extern "C" jboolean checkValidity(JNIEnv *env, char *Appsha1); #endif //SECURITYSDK_GETSIGN_H ================================================ FILE: securitysdkcore/src/main/cpp/native-lib.cpp ================================================ #include #include #include #include #include #include "Util.h" #include "SimulatorDetected.h" #include "getSign.h" #include "InjectDetected.h" /** *判断是否处于调试模式 */ extern "C" JNIEXPORT jint JNICALL Java_com_nstl_securitysdkcore_NativeCoreUtil_debugPresent(JNIEnv *env, jobject instance) { const int BUF_SIZE = 1024; char file_path[128] = "\0"; char buffer[BUF_SIZE] = "\0"; //获取当前进程的pid int pid = getpid(); sprintf(file_path, "/proc/%d/status", pid); FILE *fp = fopen(file_path, "r"); if (fp == NULL) { return -2; } else { while (fgets(buffer, BUF_SIZE, fp)) { if (strncmp(buffer, "TracerPid", 9) == 0) { int statue = atoi(&buffer[10]); if (statue != 0) { fclose(fp); return -1; } break; } } fclose(fp); } return 0; } /** * 判断是否在模拟器中 */ extern "C" JNIEXPORT jint JNICALL Java_com_nstl_securitysdkcore_NativeCoreUtil_runInEmulator(JNIEnv *env, jobject instance, jobject mContext) { return simulatorDetected(env, mContext, 30); } /** * 重打包检测 */ extern "C" JNIEXPORT void JNICALL Java_com_nstl_securitysdkcore_NativeCoreUtil_rePackage(JNIEnv *env, jobject instance, jobject mContext, jobject verifyListener) { // TODO char *sha1 = getAppSignSha1(env, mContext); jboolean result = checkValidity(env, sha1); jclass jhandlerClass = env->GetObjectClass(verifyListener); jmethodID SuccessMethodId = env->GetMethodID(jhandlerClass, "onVerifySuccess", "()V"); jmethodID FailMethodId = env->GetMethodID(jhandlerClass, "onVerifyFail", "()V"); if (result) { env->CallVoidMethod(verifyListener, SuccessMethodId); } else { env->CallVoidMethod(verifyListener, FailMethodId); } } /** * 注入检测 */ extern "C" JNIEXPORT jint JNICALL Java_com_nstl_securitysdkcore_NativeCoreUtil_detectInject(JNIEnv *env, jobject instance, jobject mContext) { // TODO int i = getimagebase(); return i; } /** * su文件检测 */ extern "C" JNIEXPORT jint JNICALL Java_com_nstl_securitysdkcore_NativeCoreUtil_isExisSUAndExecute(JNIEnv *env, jobject instance) { int result = 0; char *path[5] = {"/system/bin/su", "/system/xbin/su", "/system/xbin/busybox", "/system/bin/busybox", "/data/local/tmp/busybox"}; for (int i = 0; i < 5; ++i) { if (access(path[i], F_OK | X_OK) == 0) //判断上述文件是否存在并且拥有可执行的权限 result++; } return result; } /** * 获取app的签名 */ extern "C" JNIEXPORT jstring JNICALL Java_com_nstl_securitysdkcore_NativeCoreUtil_getRemoteAppSign(JNIEnv *env, jobject instance, jobject context, jstring pkgname_) { char *sha1 = getAppSignSha1(env, context, pkgname_); // TODO return charsTojstring(env, sha1); } ================================================ FILE: securitysdkcore/src/main/java/com/nstl/securitysdkcore/APPThreatIntelligence.java ================================================ package com.nstl.securitysdkcore; /** * Created by plldzy on 17-12-6. * 移动端威胁情报信息收集 */ public class APPThreatIntelligence { } ================================================ FILE: securitysdkcore/src/main/java/com/nstl/securitysdkcore/BinderSecurityUtil.java ================================================ package com.nstl.securitysdkcore; import android.content.Context; import android.os.Binder; import android.util.Log; /** * Created by ffthy on 13/12/2017. */ public class BinderSecurityUtil { /** * callingAPP, RemoteAppSign两个变量为用户提前配置,callingAPP 为调用者的包名,RemoteAppSign为包签名 */ public static Boolean checkClientSig(Context context){ String TAG="BinderSecurityUtil"; String callingApp = context.getPackageManager().getNameForUid(Binder.getCallingUid()); Log.d(TAG, "checkClientSig: "+callingApp); NativeCoreUtil nativeCoreUtil=new NativeCoreUtil(); String remoteAppSign=nativeCoreUtil.getRemoteAppSign(context,callingApp); Log.d(TAG, "checkClientSig: "+remoteAppSign); if("*************************".equals(remoteAppSign) && "com.text.package".equals(callingApp)){ return true; } return false; } } ================================================ FILE: securitysdkcore/src/main/java/com/nstl/securitysdkcore/HelpUtil.java ================================================ package com.nstl.securitysdkcore; import android.content.Context; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.Signature; import android.os.Environment; import android.util.Log; import com.nstl.securitysdkcore.crypt.CryptAndHttps; import com.nstl.securitysdkcore.reinforce.bean.InstallPackageInfo; import java.io.BufferedInputStream; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.lang.reflect.Field; import java.math.BigInteger; import java.security.MessageDigest; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import javax.net.ssl.HttpsURLConnection; /** * Created by plldzy on 17-11-17. * java层基础帮助工具类 */ public class HelpUtil { //获得手机上安装的应用及其相关信息 public static List getInstallPackageAndSig(Context context){ List installPkgList = new LinkedList(); PackageManager packageManager = context.getPackageManager(); List packageList = packageManager.getInstalledPackages(PackageManager.GET_SIGNATURES); for (PackageInfo p : packageList) { InstallPackageInfo installPkg = new InstallPackageInfo(); installPkg.setPkgName(p.packageName); installPkg.setVersionCode(p.versionCode); installPkg.setVersionName(p.versionName); installPkg.setActivityInfos(p.activities); installPkg.setFirstInstallTime(p.firstInstallTime); installPkg.setLastUpdateTime(p.lastUpdateTime); installPkg.setPermissionInfos(p.permissions); installPkg.setReceivers(p.receivers); installPkg.setServiceInfos(p.services); //获得签名 installPkg.setPkgSig(p.signatures[0].toCharsString()); Signature[] arrSignatures = p.signatures; installPkgList.add(installPkg); } return installPkgList; } /** * 获取文件的MD5的值 * @param file * @return */ public static String getFileMD5(File file) { if (!file.isFile()) { return null; } MessageDigest digest = null; FileInputStream in = null; byte buffer[] = new byte[1024]; int len; try { digest = MessageDigest.getInstance("MD5"); in = new FileInputStream(file); while ((len = in.read(buffer, 0, 1024)) != -1) { digest.update(buffer, 0, len); } in.close(); } catch (Exception e) { e.printStackTrace(); return null; } BigInteger bigInt = new BigInteger(1, digest.digest()); return bigInt.toString(16); } /** * 获取sd卡的路径信息 * @return */ public static String getSDPath(){ File sdDir = null; boolean sdCardExist = Environment.getExternalStorageState() .equals(Environment.MEDIA_MOUNTED);//判断sd卡是否存在 if(sdCardExist) { sdDir = Environment.getExternalStorageDirectory();//获取跟目录 } return sdDir.toString(); } /** * 通过反射的方法获得refererField * @return */ public static String reflectGetReferrer(Context context) { Class activityClass = null; try { activityClass = Class.forName("android.app.Activity"); Field refererField = activityClass.getDeclaredField("mReferrer"); refererField.setAccessible(true); String referrer = (String) refererField.get(context); return referrer; } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (NoSuchFieldException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } return null; } /** * 测试https链接 assets文件夹中必须提前配有服务器对应的cer证书 * @return */ public static void initSSLWithHttpClient(Context context) { try { String CertName="zhihu.cer"; InputStream cerInput=new BufferedInputStream(context.getAssets().open(CertName)); List IpAndHosts= new ArrayList(); IpAndHosts.add("192.168.0.32"); HttpsURLConnection conn= CryptAndHttps.getHttpsUrlConnection("https://zhuanlan.zhihu.com/p/22816331",IpAndHosts,cerInput); conn.connect(); InputStream in = conn.getInputStream(); BufferedReader reader = new BufferedReader(new InputStreamReader(in)); String line = ""; StringBuffer result = new StringBuffer(); while ((line = reader.readLine()) != null) { result.append(line); } Log.d("TTTT", result.toString()); } catch (IOException e) { e.printStackTrace(); } } /** * 字节转String * @param src * @return */ public static String bytesToHexString(byte[] src){ StringBuilder stringBuilder = new StringBuilder(""); if (src == null || src.length <= 0) { return null; } for (int i = 0; i < src.length; i++) { int v = src[i] & 0xFF; String hv = Integer.toHexString(v); if (hv.length() < 2) { stringBuilder.append(0); } stringBuilder.append(hv); } return stringBuilder.toString(); } } ================================================ FILE: securitysdkcore/src/main/java/com/nstl/securitysdkcore/HttpUtil.java ================================================ package com.nstl.securitysdkcore; import java.io.BufferedOutputStream; import java.io.BufferedReader; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.URL; import java.net.URLConnection; import java.net.URLEncoder; import java.util.Map; import java.util.concurrent.Callable; import java.util.concurrent.FutureTask; /** * Created by plldzy on 17-11-15. * 进行Http请求:get和post * 推荐使用https,如果是自定义证书,可以调用CryptAndHttps中的api进行通信 */ public class HttpUtil { public static String doGet(final String url){ final StringBuilder sb = new StringBuilder(); FutureTask task = new FutureTask(new Callable() { @Override public String call() throws Exception { BufferedReader br = null; InputStreamReader isr = null; URLConnection urlConnection = null; try{ URL url1 = new URL(url); urlConnection = url1.openConnection(); urlConnection.connect(); isr = new InputStreamReader(urlConnection.getInputStream()); //输入流 br = new BufferedReader(isr); String line = null; while( (line = br.readLine()) != null){ sb.append(line); } }catch(Exception e){ e.printStackTrace(); }finally { try { if(br != null){ br.close(); } if(isr != null){ isr.close(); } }catch (Exception e){ e.printStackTrace(); } } return sb.toString(); } }); new Thread(task).start(); String s = null; try{ s = task.get(); }catch(Exception e){ e.printStackTrace(); } return s; } /** * //post会提交相关参数,如cookie等账号信息,所以需要params参数 * @param url * @param params * @return */ public static String doPost(final String url, final Map params){ final StringBuilder sb = new StringBuilder(); FutureTask task = new FutureTask(new Callable() { @Override public String call() throws Exception { HttpURLConnection connection = null; BufferedReader reader = null; try{ URL postUrl = new URL(url); connection = (HttpURLConnection) postUrl.openConnection(); connection.setRequestMethod("POST"); connection.setConnectTimeout(8000); //连接超时时间 connection.setReadTimeout(8000); //读取超时时间 //发送post请求必须设置 connection.setDoOutput(true); connection.setDoInput(true); connection.setUseCaches(false); connection.setInstanceFollowRedirects(true); connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); OutputStream outputStream = new BufferedOutputStream(connection.getOutputStream()); StringBuilder request = new StringBuilder(); for(String key: params.keySet()){ request.append(key + "=" + URLEncoder.encode(params.get(key), "UTF-8") + "&"); } outputStream.write(request.toString().getBytes()); //写入请求参数 outputStream.flush(); outputStream.close(); if(connection.getResponseCode() == 200){ InputStream inputStream = connection.getInputStream(); reader = new BufferedReader(new InputStreamReader(inputStream)); String line = null; while( (line = reader.readLine()) != null ){ sb.append(line); } //System.out.println(sb); } }catch (Exception e){ e.printStackTrace(); }finally { try{ if(reader != null){ reader.close(); } if(connection != null){ connection.disconnect(); //断开链接,释放资源 } }catch(Exception e){ e.printStackTrace(); } } return sb.toString(); } }); new Thread(task).start(); String s = null; try{ s = task.get(); }catch (Exception e){ e.printStackTrace(); } return s; } } ================================================ FILE: securitysdkcore/src/main/java/com/nstl/securitysdkcore/Logger/LogLevel.java ================================================ package com.nstl.securitysdkcore.Logger; public class LogLevel { public static final String DEBUG_ERROR = "error"; public static final String DEBUG_WARNING = "warning"; public static final String DEBUG_TRACE = "trace"; public static final String DEBUG_INFO = "info"; } ================================================ FILE: securitysdkcore/src/main/java/com/nstl/securitysdkcore/Logger/Logger.java ================================================ package com.nstl.securitysdkcore.Logger; import java.io.*; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date; public class Logger { private static DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS"); private static final String FILE_PATH = "./securitySdk.log"; /** * 向日志文件写入 * @param err_msg * @param err_no * @param level */ public static void writeToFile(String err_msg ,int err_no, String level){ String time = "[ " +dateFormat.format(new Date()) + " ] : "; String basicInfo = level + " error_no = " + err_no + " error_msg = "; String fileName = "in FileName = " + getFileName() + " "; String className = " ClassName = " + getClassName() + " "; String methodName = " MethodName = " + getMethodName() + " "; String lineNumber = " LineNumber = " + getLineNumber() + " "; StringBuilder log = new StringBuilder(); log.append(time); log.append(basicInfo); log.append(fileName); log.append(className); log.append(methodName); log.append(lineNumber); log.append("\r\n"); try { // 打开一个写文件器,构造函数中的第二个参数true表示以追加形式写文件 FileWriter writer = new FileWriter(FILE_PATH, true); writer.write(log.toString()); writer.close(); } catch (IOException e) { e.printStackTrace(); } } /** * * @param str * @param errno */ public static void warning(String str, int errno){ Logger.writeToFile(str, errno, LogLevel.DEBUG_WARNING); } /** * * @param str * @param errno */ public static void error(String str, int errno){ Logger.writeToFile(str, errno, LogLevel.DEBUG_ERROR); } /** * * @param str * @param errno */ public static void trace(String str, int errno){ Logger.writeToFile(str, errno, LogLevel.DEBUG_TRACE); } /** * * @param str * @param errno */ public static void info(String str, int errno){ Logger.writeToFile(str, errno, LogLevel.DEBUG_INFO); } private static int originStackIndex = 3; /** * 文件名 * @return */ public static String getFileName() { return Thread.currentThread().getStackTrace()[originStackIndex].getFileName(); } /** * 类名 * @return */ public static String getClassName() { return Thread.currentThread().getStackTrace()[originStackIndex].getClassName(); } /** * 方法名 * @return */ public static String getMethodName() { return Thread.currentThread().getStackTrace()[originStackIndex].getMethodName(); } /** * 行号 * @return */ public static int getLineNumber() { return Thread.currentThread().getStackTrace()[originStackIndex].getLineNumber(); } } ================================================ FILE: securitysdkcore/src/main/java/com/nstl/securitysdkcore/Logger/SecuritySdkError.java ================================================ package com.nstl.securitysdkcore.Logger; import android.app.admin.*; /** * Created by Lin on 2018/5/7. */ public enum SecuritySdkError{ FILE_OPEN_ERROR(10000, "文件打开错误"), NETWORK_ERROR(10001,"网络异常错误"), //签名算法相关 NO_SUCCH_ALGORITHM(10002, "没有此类算法"), SIGNATURE_ERROR(10002, "计算签名错误"), INVALID_KEY(10003, "无效的密钥"); private String msg; private int code; private SecuritySdkError(int code,String msg) { this.code=code; this.msg=msg; } public String getMsg() { return this.msg; } public int getCode() { return this.code; } } ================================================ FILE: securitysdkcore/src/main/java/com/nstl/securitysdkcore/Logger/SecuritySdkTrace.java ================================================ package com.nstl.securitysdkcore.Logger; /** * Created by Lin on 2018/5/8. */ public enum SecuritySdkTrace { } ================================================ FILE: securitysdkcore/src/main/java/com/nstl/securitysdkcore/Logger/Track.java ================================================ package com.nstl.securitysdkcore.Logger; import java.util.HashMap; import java.util.Map; public class Track { public static final String METHOD_SAFEZIP = "safezip"; public static final String METHOD_SAFEWEBVIEW = "safeWebView"; public static final String METHOD_SAFEUPDATE = "update"; public static final String METHOD_NATIVECODE_DEBUGPRESENT = "debugPresent"; public static final String METHOD_NATIVECODE_RUNINEMU = "runInEmulator"; public static final String METHOD_NATIVECODE_REPACKAGE = "repackage"; public static final String METHOD_NATIVECODE_DETECTINJECT = "detectInject"; public static final String METHOD_NATIVECODE_ISROOT = "isRoot"; public static int METHOD_SAFE_ZIP = 0; public static Map trace(){ Map map = new HashMap<>(); map.put(METHOD_SAFEZIP,METHOD_SAFE_ZIP); return map; } } ================================================ FILE: securitysdkcore/src/main/java/com/nstl/securitysdkcore/NativeCoreUtil.java ================================================ package com.nstl.securitysdkcore; import com.nstl.securitysdkcore.reinforce.IVerifyListener; /** * Created by plldzy on 17-11-15. */ /* 核心能力的本地代码实现 */ public class NativeCoreUtil { static { System.loadLibrary("native-lib"); } //public native String stringFromJNI(); public native int debugPresent(); // 调试 public native int runInEmulator(Object mContext); // 模拟器 public native void rePackage(Object mContext,IVerifyListener verifyListener); // 重打包 public native int detectInject(Object mcontext); //注入检测 public native int isExisSUAndExecute(); //是否存在su文件并且是可执行的 public native String getRemoteAppSign(Object context,String pkgname); //得到远程调用APP的签名 } ================================================ FILE: securitysdkcore/src/main/java/com/nstl/securitysdkcore/PluginInvokeValidate.java ================================================ package com.nstl.securitysdkcore; import android.content.Context; import android.content.Intent; import com.alibaba.fastjson.JSON; import com.nstl.securitysdkcore.config.IntentUriScheme; import com.nstl.securitysdkcore.config.InterceptPluginInvoke; /** * Created by ffthy on 2017/12/27. */ public class PluginInvokeValidate { private static Context context = null; public PluginInvokeValidate(Context context) { this.context = context; } /** * @param pluginName * @param className * @param methodSign * @param intent * @return */ public static boolean validate(String pluginName, String className, String methodSign, Intent intent) { // InterceptPluginInvoke interceptPluginInvoke=json.load() 根据配置生成实例类。 // InterceptPluginInvoke interceptPluginInvoke=new InterceptPluginInvoke(); SecuritySDKInit securitySDKInit = SecuritySDKInit.getInstance(context); String interceptString = securitySDKInit.getConfigStringValueByKey("intercept_plugin_invoke"); InterceptPluginInvoke interceptPluginInvoke = (InterceptPluginInvoke) JSON.parse(interceptString); return validatePlugin(pluginName, className, methodSign, intent, interceptPluginInvoke); } /** * @param pluginName * @param className * @param methodSign * @param intent * @param interceptPluginInvoke1 * @return */ public static boolean validatePlugin(String pluginName, String className, String methodSign, Intent intent, InterceptPluginInvoke interceptPluginInvoke1) { int type = interceptPluginInvoke1.getType(); if (pluginName == null) { return type == 0 ? false : true; } else { if (pluginName.equals(interceptPluginInvoke1.getPluginName())) { return validateclassName(className, methodSign, intent, interceptPluginInvoke1); } return type == 0 ? true : false; } } /** * @param className * @param methodSign * @param intent * @param interceptPluginInvoke1 * @return */ public static boolean validateclassName(String className, String methodSign, Intent intent, InterceptPluginInvoke interceptPluginInvoke1) { int type = interceptPluginInvoke1.getType(); if (className == null) { return type == 0 ? false : true; } else { if (className.equals(interceptPluginInvoke1.getTargetClassName())) { return validateMethod(methodSign, intent, interceptPluginInvoke1); } return type == 0 ? true : false; } } /** * * @param methodSign * @param intent * @param interceptPluginInvoke1 * @return */ private static boolean validateMethod(String methodSign, Intent intent, InterceptPluginInvoke interceptPluginInvoke1) { int type = interceptPluginInvoke1.getType(); if (methodSign == null) { return type == 0 ? false : true; } else { if (methodSign.equals(interceptPluginInvoke1.getMethod().getMethodSign())) { return validateIntent(intent, interceptPluginInvoke1); } return type == 0 ? true : false; } } /** * @param intent * @param interceptPluginInvoke1 * @return */ private static boolean validateIntent(Intent intent, InterceptPluginInvoke interceptPluginInvoke1) { IntentUriScheme intentUriScheme = interceptPluginInvoke1.getUriScheme(); int methodSignType = interceptPluginInvoke1.getMethod().getType(); if (intentUriScheme == null) { return methodSignType == 0 ? false : true; } for (String a : intentUriScheme.getActionStringList()) { if (intent.getAction().equals(a)) { return intentUriScheme.getType() == 0 ? false : true; } } //todo //这句逻辑需要重写 return true; } } ================================================ FILE: securitysdkcore/src/main/java/com/nstl/securitysdkcore/SecuritySDKInit.java ================================================ package com.nstl.securitysdkcore; /** * Created by plldzy on 17-11-15. */ import android.content.Context; import android.content.SharedPreferences; import android.os.Handler; import com.alibaba.fastjson.JSON; import com.nstl.securitysdkcore.config.SecuritySDKConfig; import java.util.Date; import java.util.Map; /** * SDK的配置文件更新和获取指定key的value */ public class SecuritySDKInit { private String configName = "securitysdk_config.json"; private Context context = null; private Handler updateHandler = new Handler(); private static SecuritySDKInit sdkInit = null; public final static String VERSION = "version"; public final static String UPDATETIME = "update_time"; public final static String TIMEOUT = "time_out"; public final static String WEBVIEWCONFIG = "Webview_config"; public final static String INTENTURISCHEMELIST = "intent_uri"; public final static String INTERCEPTPLUGININVOKE = "intercept_plugin_invoke"; private SharedPreferences preferences = null; private SecuritySDKInit(Context context){ this.context = context; preferences = context.getSharedPreferences(configName, Context.MODE_PRIVATE); } /** * A native method that is implemented by the 'native-lib' native library, * which is packaged with this application. */ public void syncConfig(final String updateUrl, final Map updateConfigJsonParams){ String jsonString = HttpUtil.doPost(updateUrl, updateConfigJsonParams); SecuritySDKConfig sdkConfig = JSON.parseObject(jsonString, SecuritySDKConfig.class); //根据根目录下的config.json,对配置文件进行进行key-value格式编写,后续直接根据key读出相关value,然后序列化成SecuritySDKConfig中相关属性 if((getConfigStringValueByKey(VERSION) != null && sdkConfig.getVersion().compareTo(getConfigStringValueByKey(VERSION))>0)){ //发现新版本的配置文件 preferences.edit().putString(VERSION, JSON.toJSONString(sdkConfig.getVersion())); preferences.edit().putString(UPDATETIME, new Date().toString()); preferences.edit().putLong(TIMEOUT, sdkConfig.getTimeout()); preferences.edit().putString(WEBVIEWCONFIG, JSON.toJSONString(sdkConfig.getWebviewConfig())); preferences.edit().putString(INTENTURISCHEMELIST, JSON.toJSONString(sdkConfig.getIntentUriList())); preferences.edit().putString(INTERCEPTPLUGININVOKE, JSON.toJSONString(sdkConfig.getInterceptPluginInvokeList())); } //执行定期更新任务查询 Runnable runnable = new Runnable() { @Override public void run() { syncConfig(updateUrl, updateConfigJsonParams); } }; updateHandler.postDelayed(runnable, sdkConfig.getTimeout()); } /** * 获得配置文件中指定key的值,如webview的参数 * @param keyName * @return */ public String getConfigStringValueByKey(String keyName){ return preferences.getString(keyName,null); } /** * 获得配置文件中指定key的值,如更新间隔时间 * @param keyName * @return */ public long getConfigLongValueByKey(String keyName){ return preferences.getLong(keyName,-1); } public static synchronized SecuritySDKInit getInstance(Context context){ if (sdkInit == null) sdkInit = new SecuritySDKInit(context); return sdkInit; } } ================================================ FILE: securitysdkcore/src/main/java/com/nstl/securitysdkcore/UpgradeTool/DataBean.java ================================================ package com.nstl.securitysdkcore.UpgradeTool; /** * Created by Lin on 2017/12/27. */ public class DataBean { private String description; private String downUrl; private String isForce; private String version; private int vercode; private String verifyCode; //下载内容的MD5等验证值 private String singnedVerifyCode; //RSA私钥签名后的验证值(需要业务方在应用中传递公钥验证改值是否合法) public void setDescription(String description){ this.description = description; } public String getDescription(){ return this.description; } public void setDownUrl(String downUrl){ this.downUrl = downUrl; } public String getDownUrl(){ return downUrl; } public void setIsForce(String isForce){ this.isForce = isForce; } public String getIsForce(){ return this.isForce; } public void setVersion(String version){ this.version = version; } public String getVersion(){ return this.version; } public int getVercode(){ return this.vercode; } public void setVercode(int vercode){ this.vercode = vercode; } public String getVerifyCode() { return verifyCode; } public void setVerifyCode(String verifyCode) { this.verifyCode = verifyCode; } public String getSingnedVerifyCode() { return singnedVerifyCode; } public void setSingnedVerifyCode(String singnedVerifyCode) { this.singnedVerifyCode = singnedVerifyCode; } } ================================================ FILE: securitysdkcore/src/main/java/com/nstl/securitysdkcore/UpgradeTool/ISafeInstall.java ================================================ package com.nstl.securitysdkcore.UpgradeTool; import android.content.Context; /** * Created by Lin on 2017/12/27. */ public interface ISafeInstall { public int getVerCode(); public void install(Context context, String savePath, String fileName, String isForce); public String getErrMsg(String errMsg); } ================================================ FILE: securitysdkcore/src/main/java/com/nstl/securitysdkcore/UpgradeTool/UpgradeModel.java ================================================ package com.nstl.securitysdkcore.UpgradeTool; /** * Created by LIN on 2017/12/27. */ public class UpgradeModel { private int code; // private String msg; //反序列化之后的信息 private DataBean dataBean; //apk的相关信息 public int getCode(){ return code; } public void setCode(int code){ this.code = code; } public String getMsg(){ return this.msg; } public void setMsg(String msg){ this.msg = msg; } public DataBean getDataBean(){ return this.dataBean; } public void setDataBean(DataBean dataBean){ this.dataBean = dataBean; } } ================================================ FILE: securitysdkcore/src/main/java/com/nstl/securitysdkcore/UpgradeTool/UpgradeTool.java ================================================ package com.nstl.securitysdkcore.UpgradeTool; /** * Created by Lin on 2017/12/27. */ import android.app.DownloadManager; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.Signature; import android.database.Cursor; import android.net.Uri; import android.os.Environment; import android.text.TextUtils; import android.webkit.MimeTypeMap; import com.alibaba.fastjson.JSON; import com.nstl.securitysdkcore.HelpUtil; import java.io.File; import java.security.KeyFactory; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.PublicKey; import java.security.spec.X509EncodedKeySpec; public class UpgradeTool { private UpgradeModel upgradeModel; private long mTaskId; private DownloadManager downloadManager; private String savePath; private String fileName; private ISafeInstall iSafeInstall; private Context mContext; private BroadcastReceiver receiver; private String publicKey = null; //RSA公钥,用来验证DataBean中的singnedVerifyCode是否和verifyCode一致,防止非法劫持 private String TAG = "TEST"; /** * * @param mContext 上下文 * @param publicKey RSA公钥,用来验证DataBean中的singnedVerifyCode是否和verifyCode一致,防止非法劫持 * @param savePath 保存路径 * @param fileName 保存的文件名 * @param iSafeInstall 业务实现的安装接口 */ public UpgradeTool( Context mContext,String publicKey, UpgradeModel upgradeModel, String savePath ,String fileName, ISafeInstall iSafeInstall ){ //请求json数据 //反序列化为UpgradeModel //这是测试的请求字符串,此处应为一个http请求 // TODO: 2017/12/31 下面需要实现和服务器的安全通信,获取apk下载需要的信息 String jsonStr = "{\n" + " \"code\": 100001,\n" + " \"dataBean\": {\n" + " \"description\": \"this is update\",\n" + " \"downUrl\": \"http://192.168.12.139/download.apk\",\n" + " \"isForce\": \"isForce\",\n" + " \"md5SignCode\": \"xxxxxx\",\n" + " \"vercode\": 7,\n" + " \"version\": \"1.0.1\"\n" + " },\n" + " \"msg\": \"download apk\"\n" + "}"; this.upgradeModel = upgradeModel; this.savePath = savePath; this.fileName = fileName; this.iSafeInstall = iSafeInstall; this.mContext = mContext; this.publicKey = publicKey; this.receiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { //执行下载完成后的操作 checkDownloadStatus(); } }; } /** * 检查下载的状态 */ private void checkDownloadStatus(){ DownloadManager.Query query = new DownloadManager.Query(); query.setFilterById(mTaskId);//筛选下载任务,传入任务ID,可变参数 Cursor c = downloadManager.query(query); if (c.moveToFirst()) { int status = c.getInt(c.getColumnIndex(DownloadManager.COLUMN_STATUS)); switch (status) { // TODO: 2017/12/31 不同的处理状态 case DownloadManager.STATUS_PAUSED: case DownloadManager.STATUS_PENDING: case DownloadManager.STATUS_RUNNING: break; case DownloadManager.STATUS_SUCCESSFUL: //下载完成,先校验下载的应用和签名是否合法,然后安装APK if(checkPkgSign(mContext, savePath, fileName)){ this.iSafeInstall.install(mContext,savePath,fileName,this.upgradeModel.getDataBean().getIsForce()); }else{ this.iSafeInstall.getErrMsg("download apk is success"); } break; case DownloadManager.STATUS_FAILED: this.iSafeInstall.getErrMsg("download apk is failed"); break; } } } /** * 下载的apk文件 * @param mContext 上下文 * @param url 下载文件的url * @param savePath 下载文件的路径 * @param fileName 下载后的文件名 */ private void downloadAPK(Context mContext,String url,String savePath, String fileName){ DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url)); //漫游网络是否可以下载,是否可以考虑业务来传递 request.setAllowedOverRoaming(false); MimeTypeMap mimeTypeMap = MimeTypeMap.getSingleton(); String mimeString = mimeTypeMap.getMimeTypeFromExtension(MimeTypeMap.getFileExtensionFromUrl(url)); request.setMimeType(mimeString); //在通知栏中显示,默认就是显示的 request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE); request.setVisibleInDownloadsUi(true); //sdcard的目录下的download文件夹,必须设置 //此时可以考虑 // TODO: 2017/12/31 判断是否放在sd上面去 request.setDestinationInExternalPublicDir(savePath,fileName); //request.setDestinationInExternalFilesDir(),也可以自己制定下载路径 //将下载请求加入下载队列 downloadManager = (DownloadManager) mContext.getSystemService(Context.DOWNLOAD_SERVICE); //加入下载队列后会给该任务返回一个long型的id, //通过该id可以取消任务,重启任务等等,看上面源码中框起来的方法 mTaskId = downloadManager.enqueue(request); //注册广播接收者,监听下载状态 mContext.registerReceiver(receiver, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE)); } /** * upgrade方法,对外提供调用的方法 */ public void upgrade(){ int verCode = this.iSafeInstall.getVerCode(); // TODO: 2017/12/31 if(this.upgradeModel.getDataBean().getVercode() <= verCode){ //不需要执行升级 this.iSafeInstall.getErrMsg("current version is latest"); return; } //downLoad apk this.downloadAPK(mContext,this.upgradeModel.getDataBean().getDownUrl(),this.savePath,this.fileName); } /** * * @param context * @param savePath * @param fileName * @return */ private boolean checkPkgSign(Context context,String savePath,String fileName){ String apkFilePath = savePath + File.separator + fileName; String signCode = this.upgradeModel.getDataBean().getSingnedVerifyCode(); //生成的签名 String code = this.upgradeModel.getDataBean().getVerifyCode(); //被签的原文 //验证签名信息 if(verifySignedVerifyCode(signCode, code)&& this.apkSignCheck(mContext,apkFilePath,code)){ return true; }else{ return false; } } //使用公钥用来验证DataBean中的singnedVerifyCode是否和verifyCode一致,防止下载被非法劫持 private boolean verifySignedVerifyCode(String signCode, String code){ boolean flag = false; try{ KeyFactory kf = KeyFactory.getInstance("RSA"); X509EncodedKeySpec keySpec = new X509EncodedKeySpec(this.publicKey.getBytes()); PublicKey pKey = kf.generatePublic(keySpec); java.security.Signature signature = java.security.Signature.getInstance("SHA1withRSA"); signature.initVerify(pKey); signature.update(code.getBytes()); flag = signature.verify(signCode.getBytes()); }catch (Exception e){ e.printStackTrace(); flag = false; } return flag; } /** * 检查下载的apk或者文件的MD5信息是否跟文件自身匹配 * @param ct * @param md5Code 下载文件的MD5值 * @return true,下载内容MD5验证合法,false MD5验证非法 */ private boolean apkSignCheck(Context ct, String apkFilePath, String md5Code){ //2.读取APK中的META-INF目录下的签名文件,验证一致性 boolean flag = false; if (TextUtils.isEmpty(apkFilePath)) return flag; File downloadFile = new File(apkFilePath); String downloadFileMD5 = HelpUtil.getFileMD5(downloadFile); flag = md5Code.equals(downloadFileMD5); return flag; } } ================================================ FILE: securitysdkcore/src/main/java/com/nstl/securitysdkcore/UpgradeTool/UpgradeToolTest/SafeInstall.java ================================================ package com.nstl.securitysdkcore.UpgradeTool.UpgradeToolTest; import android.content.Context; import android.util.Log; import com.nstl.securitysdkcore.UpgradeTool.ISafeInstall; /** * Created by LIN on 2017/12/31. */ public class SafeInstall implements ISafeInstall { @Override public int getVerCode() { return 7; } @Override public void install(Context context, String savePath, String fileName, String isForce) { Log.d("TEST","this is install apk interface"); } @Override public String getErrMsg(String errMsg) { return null; } } ================================================ FILE: securitysdkcore/src/main/java/com/nstl/securitysdkcore/UpgradeTool/UserConfig.java ================================================ package com.nstl.securitysdkcore.UpgradeTool; /** * Created by Lin on 2018/4/26. */ public class UserConfig { private String save_app_name; private String save_app_location; private boolean enableNetWrok; private boolean isShowInTitle; private String show_name; private String show_description; public String getSave_app_name(){ return save_app_name; } public String getSave_app_location(){ return save_app_location; } public boolean getEnableNetWork(){ return enableNetWrok; } public boolean getIsShowInTitle(){ return isShowInTitle; } } ================================================ FILE: securitysdkcore/src/main/java/com/nstl/securitysdkcore/Util/VerifyUtil.java ================================================ package com.nstl.securitysdkcore.Util; import android.content.Context; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.Signature; import android.text.TextUtils; import com.nstl.securitysdkcore.HelpUtil; import java.io.File; import java.security.KeyFactory; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.PublicKey; import java.security.spec.X509EncodedKeySpec; /** * Created by Lin on 2018/4/26. */ public class VerifyUtil { /** * 使用公钥用来验证DataBean中的singnedVerifyCode是否和verifyCode一致,防止下载被非法劫持 * @param signCode 签值 * @param code * @return */ public static boolean verifySignedVerifyCode(String publicKey,String signCode, String code) { if ( publicKey == null || publicKey.isEmpty()){ return false; } if( signCode == null || signCode.isEmpty()){ return false; } if( code == null || code.isEmpty()){ return false; } boolean flag = false; try { KeyFactory kf = KeyFactory.getInstance("RSA"); X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKey.getBytes()); PublicKey pKey = kf.generatePublic(keySpec); java.security.Signature signature = java.security.Signature.getInstance("SHA1withRSA"); signature.initVerify(pKey); signature.update(code.getBytes()); flag = signature.verify(signCode.getBytes()); } catch (Exception e) { //todo 打印日志 e.printStackTrace(); flag = false; } return flag; } /** * 检查下载的apk或者文件的MD5信息是否跟文件自身匹配 * @param md5Code 下载文件的MD5值 * @return true, 下载内容MD5验证合法,false MD5验证非法 */ public static boolean fileMd5Check(String apkFilePath, String md5Code) { boolean flag = false; if ( TextUtils.isEmpty(apkFilePath) ) return flag; if (md5Code == null || md5Code.isEmpty()){ return false; } try{ File downloadFile = new File(apkFilePath); String downloadFileMD5 = HelpUtil.getFileMD5(downloadFile); flag = md5Code.equals(downloadFileMD5); }catch (Exception e){ //todo 打印log flag = false; } return flag; } /** * 检查apk的签名 * @param ct * @param sig * @return */ public static boolean apkSignCheck(Context ct, String sig, String apkFilePath){ //2.读取APK中的META-INF目录下的签名文件,验证一致性 boolean flag = true; if (TextUtils.isEmpty(apkFilePath)) return true; PackageManager pm = ct.getPackageManager(); PackageInfo pi = pm.getPackageArchiveInfo(apkFilePath, PackageManager.GET_SIGNATURES); if(pi != null){ Signature[] signatures = pi.signatures; if(signatures != null && signatures.length > 0){ String signature = encryptionMD5(signatures[0].toByteArray()); if(signature.equals(sig)) flag = false; } } return flag; } /** * md5签名 * @param byteStr * @return */ private static String encryptionMD5(byte[] byteStr) { MessageDigest messageDigest = null; StringBuffer md5StrBuff = new StringBuffer(); try { messageDigest = MessageDigest.getInstance("MD5"); messageDigest.reset(); messageDigest.update(byteStr); byte[] byteArray = messageDigest.digest(); for (int i = 0; i < byteArray.length; i++) { if (Integer.toHexString(0xFF & byteArray[i]).length() == 1) { md5StrBuff.append("0").append(Integer.toHexString(0xFF & byteArray[i])); } else { md5StrBuff.append(Integer.toHexString(0xFF & byteArray[i])); } } } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } return md5StrBuff.toString(); } } ================================================ FILE: securitysdkcore/src/main/java/com/nstl/securitysdkcore/config/ConfigFileToObject.java ================================================ package com.nstl.securitysdkcore.config; import android.content.Context; import com.alibaba.fastjson.JSON; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; /** * Created by Lin on 2017/12/13. */ public class ConfigFileToObject { //配置文件的路径 private static String fileName = ""; /** * 获取反序列化后的对象 * @param context * @return */ public static SecuritySDKConfig getSecuritySDKConfig(Context context){ //从文件中读取json数据进行反序列化 InputStream abpath = context.getClass().getResourceAsStream("/assets/config.json"); String jsonStr = ""; try { jsonStr = new String(InputStreamToByte(abpath)); } catch (IOException e) { e.printStackTrace(); } SecuritySDKConfig securitySDKConfig = JSON.parseObject(jsonStr,SecuritySDKConfig.class); return securitySDKConfig; } /** * @param is * @return * @throws IOException */ private static byte[] InputStreamToByte(InputStream is) throws IOException { ByteArrayOutputStream bytestream = new ByteArrayOutputStream(); int ch; while ((ch = is.read()) != -1) { bytestream.write(ch); } byte imgdata[] = bytestream.toByteArray(); bytestream.close(); return imgdata; } } ================================================ FILE: securitysdkcore/src/main/java/com/nstl/securitysdkcore/config/IntentUriScheme.java ================================================ package com.nstl.securitysdkcore.config; import java.util.List; import java.util.Map; /** * Created by plldzy on 17-12-6. * Intent Uri Scheme的拦截规则:分别对intent中的action和type以及intent uri中的scheme、host、port、path以及uri中的参数进行过滤和拦截, */ public class IntentUriScheme { private int type = 0; //拦截调用的配置方式:黑白名单,0是黑名单,非0是白名单 private List actionStringList = null; //需要过滤的intent action private List intentTypeList = null; //需要过滤的intent中的type private List uriSchemeList = null; private List uriHostList = null; private List uriPathList = null; private Map uriParaMaps = null; public int getType() { return type; } public void setType(int type) { this.type = type; } public List getUriSchemeList() { return uriSchemeList; } public void setUriSchemeList(List uriSchemeList) { this.uriSchemeList = uriSchemeList; } public List getUriHostList() { return uriHostList; } public void setUriHostList(List uriHostList) { this.uriHostList = uriHostList; } public List getUriPathList() { return uriPathList; } public void setUriPathList(List uriPathList) { this.uriPathList = uriPathList; } public Map getUriParaMaps() { return uriParaMaps; } public void setUriParaMaps(Map uriParaMaps) { this.uriParaMaps = uriParaMaps; } public List getActionStringList() { return actionStringList; } public void setActionStringList(List actionStringList) { this.actionStringList = actionStringList; } public List getIntentTypeList() { return intentTypeList; } public void setIntentTypeList(List intentTypeList) { this.intentTypeList = intentTypeList; } } ================================================ FILE: securitysdkcore/src/main/java/com/nstl/securitysdkcore/config/InterceptMethod.java ================================================ package com.nstl.securitysdkcore.config; import java.lang.reflect.Method; import java.util.List; import java.util.Map; /** * Created by plldzy on 17-12-6. * webview中需要拦截的方法名和该方法的参数 */ public class InterceptMethod { private int type = 0; //拦截调用的配置方式:黑白名单,0是黑名单,-1拦截该方法,不用比较方法的参数内容,其他值都是白名单 private String methodNmae; //需要拦截的方法名 private Map methodArgMap = null; //需要拦截的方法的参数列表集合key是第几个参数,value是参数内容 private String MethodSign=null; //需要拦截的方法签名 public int getType() { return type; } public void setType(int type) { this.type = type; } public String getMethodNmae() { return methodNmae; } public void setMethodNmae(String methodNmae) { this.methodNmae = methodNmae; } public Map getMethodArgMap() { return methodArgMap; } public void setMethodArgMap(Map methodArgMap) { this.methodArgMap = methodArgMap; } public String getMethodSign(){ class ClassA{}; Method method=(new ClassA()).getClass().getEnclosingMethod(); String methodStr = method.toString(); String methodName = methodStr.substring(0, methodStr.lastIndexOf("(")); String methodNamePre = methodName.substring(0, methodName.lastIndexOf(".")); return methodStr.substring(methodNamePre.length() + 1); // return MethodSign; } public void setMethodSign(String methodSign){ this.MethodSign=methodSign; } } ================================================ FILE: securitysdkcore/src/main/java/com/nstl/securitysdkcore/config/InterceptPluginInvoke.java ================================================ package com.nstl.securitysdkcore.config; /** * Created by plldzy on 17-12-6. * 插件调用的拦截规则,属性type只能控制pluginname和targetClassName这两个属性 * InterceptMethod 和IntentUriScheme属性是白名单还是黑名单分别有各自属性控制; * InterceptPluginInvoke-->type和InterceptMethod、IntentUriScheme的type控制关系如下: * InterceptPluginInvoke-->type黑名单,那么后两者只能是黑名单;如果InterceptPluginInvoke-->type是白名单,后两者黑,白名单都可以 */ public class InterceptPluginInvoke { private int type = 0; //插件拦截调用的配置方式:黑白名单,0是黑名单,非0是白名单; private String pluginName; //需要拦截调用的插件名 private InterceptMethod method; //需要拦截的插件中的方法以及方法参数 private String targetClassName; //需要拦截调用的目标类名 private IntentUriScheme uriScheme; //需要拦截的intent uri的scheme规则 public int getType() { return type; } public void setType(int type) { this.type = type; } public String getPluginName() { return pluginName; } public void setPluginName(String pluginName) { this.pluginName = pluginName; } public InterceptMethod getMethod() { return method; } public void setMethod(InterceptMethod method) { this.method = method; } public String getTargetClassName() { return targetClassName; } public void setTargetClassName(String targetClassName) { this.targetClassName = targetClassName; } public IntentUriScheme getUriScheme() { return uriScheme; } public void setUriScheme(IntentUriScheme uriScheme) { this.uriScheme = uriScheme; } } ================================================ FILE: securitysdkcore/src/main/java/com/nstl/securitysdkcore/config/SecuritySDKConfig.java ================================================ package com.nstl.securitysdkcore.config; import android.content.SharedPreferences; import android.preference.Preference; import com.alibaba.fastjson.JSON; import com.nstl.securitysdkcore.SecuritySDKInit; import java.util.List; /** * Created by plldzy on 17-12-6. * 安全SDK的配置文件对webview、url scheme、应用下载和插件调用进行拦截的配置规则 */ public class SecuritySDKConfig { private String version = null; //版本号 private String updateTime = null; //本次更新时间 private long timeout = 3600; //下次更新间隔时间 private WebviewConfig webviewConfig = null; //webview的拦截规则 private List intentUriList = null; //intent uri拦截规则 private List interceptPluginInvokeList = null; //应用插件调用的过滤拦截规则 /*private SecuritySDKConfig(SharedPreferences p){ this.preferences = p; setVersion(p.getString(SecuritySDKInit.VERSION, null)); setUpdateTime(p.getString(SecuritySDKInit.UPDATETIME, null)); setTimeout(p.getLong(SecuritySDKInit.TIMEOUT, -1)); } public static SecuritySDKConfig getInstance(SharedPreferences p){ if(sdkConfig == null){ sdkConfig = new SecuritySDKConfig(p); } return sdkConfig; }*/ public String getVersion() { return version; } public void setVersion(String version) { this.version = version; } public String getUpdateTime() { return updateTime; } public void setUpdateTime(String updateTime) { this.updateTime = updateTime; } public long getTimeout() { return timeout; } public void setTimeout(long timeout) { this.timeout = timeout; } public WebviewConfig getWebviewConfig() { /*String str = preferences.getString(SecuritySDKInit.WEBVIEWCONFIG, null); if(str != null){ //fastjson webviewConfig = JSON.parseObject(str, WebviewConfig.class); }*/ return webviewConfig; } public List getIntentUriList() { /*String str = preferences.getString(SecuritySDKInit.INTENTURISCHEMELIST, null); if(str != null){ intentUriList = JSON.parseArray(str, IntentUriScheme.class); }*/ return intentUriList; } public List getInterceptPluginInvokeList() { /*String str = preferences.getString(SecuritySDKInit.INTERCEPTPLUGININVOKE, null); if(str != null){ interceptPluginInvokeList = JSON.parseArray(str, InterceptPluginInvoke.class); }*/ return interceptPluginInvokeList; } public void setWebviewConfig(WebviewConfig webviewConfig) { this.webviewConfig = webviewConfig; } public void setIntentUriList(List intentUriList) { this.intentUriList = intentUriList; } public void setInterceptPluginInvokeList(List interceptPluginInvokeList) { this.interceptPluginInvokeList = interceptPluginInvokeList; } } ================================================ FILE: securitysdkcore/src/main/java/com/nstl/securitysdkcore/config/WebviewConfig.java ================================================ package com.nstl.securitysdkcore.config; import java.util.List; /** * Created by plldzy on 17-12-6. * webview的拦截配置规则 */ public class WebviewConfig { private List urlWhiteList = null; //webview中url白名单列表 /*private List urlBlackList = null; //url黑名单列表 private List methodList = null; //需要拦截的方法的结合*/ public List getUrlWhiteList() { return urlWhiteList; } public void setUrlWhiteList(List urlWhiteList) { this.urlWhiteList = urlWhiteList; } /* public List getUrlBlackList() { return urlBlackList; } public void setUrlBlackList(List urlBlackList) { this.urlBlackList = urlBlackList; } public List getMethodList() { return methodList; } public void setMethodList(List methodList) { this.methodList = methodList; }*/ } ================================================ FILE: securitysdkcore/src/main/java/com/nstl/securitysdkcore/crypt/CryptAndHttps.java ================================================ package com.nstl.securitysdkcore.crypt; import android.util.Base64; import android.util.Log; import com.nstl.securitysdkcore.HelpUtil; import com.nstl.securitysdkcore.crypt.bean.EncryptData; import java.io.ByteArrayOutputStream; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.net.URL; import java.security.InvalidKeyException; import java.security.KeyFactory; import java.security.KeyManagementException; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.PublicKey; import java.security.SecureRandom; import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.security.interfaces.ECPublicKey; import java.security.interfaces.RSAPublicKey; import java.security.spec.ECPublicKeySpec; import java.security.spec.InvalidKeySpecException; import java.security.spec.X509EncodedKeySpec; import java.util.List; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; import javax.crypto.KeyGenerator; import javax.crypto.NoSuchPaddingException; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; import javax.net.SocketFactory; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSession; import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManagerFactory; /** * Created by plldzy on 17-11-15. */ public class CryptAndHttps { public static final String SIGN_ALGORITHMS = "SHA1WithRSA"; //签名算法 public static String TAG="CryptAndHttps"; /** * //初始化AES的key * @return */ private static byte[] getAESKey(){ try { KeyGenerator kg = KeyGenerator.getInstance("AES"); // kg.init(128, new SecureRandom(password.getBytes())); 这种方式按password指定的字符串生成AES密钥。 // SecureRandom是生成安全随机数序列,password.getBytes()是种子,只要种子相同,序列就一样,所以生成的秘钥就一样。 kg.init(128);//要生成多少位,只需要修改这里即可128, 192或256,第二个参数为空,密钥为随机生成 SecretKey sk = kg.generateKey(); byte[] b = sk.getEncoded(); return b; } catch (NoSuchAlgorithmException e) { e.printStackTrace(); System.out.println("没有此算法。"); } return null; } /** * AES加密 ,提供初始密钥种子 * @param data * @param keyByte * @return */ private static byte[] encryptDataByAES(String data, byte[] keyByte){ try { KeyGenerator kgen = KeyGenerator.getInstance("AES"); kgen.init(128, new SecureRandom(keyByte)); SecretKey secretKey = kgen.generateKey(); byte[] enCodeFormat = secretKey.getEncoded(); SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES"); Cipher cipher = Cipher.getInstance("AES");// 创建密码器 byte[] byteContent = data.getBytes("utf-8"); cipher.init(Cipher.ENCRYPT_MODE, key);// 初始化 byte[] result = cipher.doFinal(byteContent); return result; // 加密 } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (NoSuchPaddingException e) { e.printStackTrace(); } catch (InvalidKeyException e) { e.printStackTrace(); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } catch (IllegalBlockSizeException e) { e.printStackTrace(); } catch (BadPaddingException e) { e.printStackTrace(); } return null; } /** * //AES加密,不提供初始密钥 * @param data * @return */ private static byte[] encryptDataByAES(String data){ try { byte[] enCodeFormat =getAESKey(); SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES"); Cipher cipher = Cipher.getInstance("AES");// 创建密码器 byte[] byteContent = data.getBytes("utf-8"); cipher.init(Cipher.ENCRYPT_MODE, key);// 初始化 byte[] result = cipher.doFinal(byteContent); return result; // 加密 } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (NoSuchPaddingException e) { e.printStackTrace(); } catch (InvalidKeyException e) { e.printStackTrace(); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } catch (IllegalBlockSizeException e) { e.printStackTrace(); } catch (BadPaddingException e) { e.printStackTrace(); } return null; } /** * //用AES加密内容,然后非对称公钥加密AES的key * @param sourceData * @param publicKey * @param type * @return * @throws Exception */ public static EncryptData aesEocdeBodyAsymmetricEncodeKey(String sourceData, String publicKey, int type) throws Exception { byte aesKey[] = getAESKey(); EncryptData encryptData = new EncryptData(); encryptData.setEncryContent(encryptDataByAES(sourceData, aesKey)); if(type == 1){ //type=1,表示RSA加密key encryptData.setEncryKey(encryptByRSAPublicKey(aesKey, publicKey)); }else{ encryptData.setEncryKey(encryptByECCPublicKey(aesKey, publicKey)); } return encryptData; } /** * //RSA公钥加密 * @param data * @param rsaPublicKey * @return * @throws Exception */ public static byte[] encryptByRSAPublicKey(byte[] data, String rsaPublicKey) throws Exception { RSAPublicKey publicKey=loadPublicKeyByStr(rsaPublicKey); Cipher cipher = Cipher.getInstance(publicKey.getAlgorithm()); cipher.init(1, publicKey); int inputLen = data.length; ByteArrayOutputStream out = new ByteArrayOutputStream(); int offSet = 0; for(int i = 0; inputLen - offSet > 0; offSet = i * 117) { //RSA最大加密明文大小117 byte[] cache; if(inputLen - offSet > 117) { cache = cipher.doFinal(data, offSet, 117); } else { cache = cipher.doFinal(data, offSet, inputLen - offSet); } out.write(cache, 0, cache.length); ++i; } byte[] encryptedData = out.toByteArray(); out.close(); return encryptedData; } /** * //ECC公钥加密 * @param data * @param eccPublicKey * @return */ public static byte[] encryptByECCPublicKey(byte[] data, String eccPublicKey){ // byte[] keyBytes = Base64.decode(eccPublicKey,Base64.DEFAULT); // X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes); // KeyFactory keyFactory = ECKeyFactory.INSTANCE; // // ECPublicKey pubKey = (ECPublicKey) keyFactory // .generatePublic(x509KeySpec); // // ECPublicKeySpec ecPublicKeySpec = new ECPublicKeySpec(pubKey.getW(), // pubKey.getParams()); return null; } /** * //RSA签名校验 * @param data * @param rsaPublicKey * @param sign * @return */ public static boolean verifyByRSA(byte[] data, String rsaPublicKey, String sign){ //RSAPublicKey publicKey=loadPublicKeyByStr(rsaPublicKey); try { KeyFactory keyFactory = KeyFactory.getInstance("RSA"); byte[] encodedKey = Base64.decode(rsaPublicKey,Base64.DEFAULT); PublicKey pubKey = keyFactory.generatePublic(new X509EncodedKeySpec(encodedKey)); java.security.Signature signature = java.security.Signature .getInstance(SIGN_ALGORITHMS); signature.initVerify(pubKey); signature.update( data); boolean bverify = signature.verify( Base64.decode(sign,Base64.DEFAULT) ); return bverify; } catch (Exception e) { e.printStackTrace(); } return false; } /** * //ECC签名校验 * @param data * @param eccPublicKey * @param sign * @return */ public static boolean verifyByECC(byte[] data, String eccPublicKey, String sign){ return false; } /** * //生成信息摘要算法,type=1表示sha256,type=2表示sha512 * @param sourceStr * @param type * @return * @throws NoSuchAlgorithmException */ public static String getHashBySHA(String sourceStr, int type) throws NoSuchAlgorithmException { String algorithm = type == 1 ? "SHA-256" : "SHA-512"; MessageDigest md= MessageDigest.getInstance(algorithm); return HelpUtil.bytesToHexString(md.digest(sourceStr.getBytes())); } /** * 根据客户端自定义证书,进行Https安全通信 * @param urlString 需要通信的url * @param ipAndHosts 证书所签发的域名或者IP地址列表,进行hostnameverify的验证 * @param certInputstream 证书的输入流 */ public static HttpsURLConnection getHttpsUrlConnection(String urlString, List ipAndHosts, InputStream certInputstream) { InputStream is = null; SSLSocketFactory ssf=null; // if (!ipAndHosts.contains(urlString)){ // return null; // } // 创建HttpsURLConnection对象,并设置其SSLSocketFactory对象 HttpsURLConnection httpsConn = null; try { URL myURL = new URL(urlString); httpsConn = (HttpsURLConnection) myURL.openConnection(); } catch (IOException e) { e.printStackTrace(); } ssf=generateSSLSocketFactory(certInputstream); httpsConn.setHostnameVerifier(new HostnameVerifier() { @Override public boolean verify(String hostname, SSLSession session) { HostnameVerifier hv=HttpsURLConnection.getDefaultHostnameVerifier(); Boolean result=hv.verify(hostname,session); return result; } }); httpsConn.setSSLSocketFactory(ssf); return httpsConn; } /** * * @param publicKeyStr * @return * @throws Exception */ public static RSAPublicKey loadPublicKeyByStr(String publicKeyStr) throws Exception { try { byte[] buffer = Base64.decode(publicKeyStr,Base64.DEFAULT); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); X509EncodedKeySpec keySpec = new X509EncodedKeySpec(buffer); return (RSAPublicKey) keyFactory.generatePublic(keySpec); } catch (NoSuchAlgorithmException e) { throw new Exception("无此算法"); } catch (InvalidKeySpecException e) { throw new Exception("公钥非法"); } catch (NullPointerException e) { throw new Exception("公钥数据为空"); } } /** * * @param certInputstream * @return */ public static SSLSocketFactory generateSSLSocketFactory(InputStream certInputstream) { try { // is = new FileInputStream("anchor.crt"); CertificateFactory cf = CertificateFactory.getInstance("X.509"); //cert格式的文件需要再加个参数provider值为"BC" Certificate ca = cf.generateCertificate(certInputstream); //Log.d(TAG, ((X509Certificate)ca).getPublicKey().toString()); // 创建 Keystore 包含我们的证书 String keyStoreType = KeyStore.getDefaultType(); KeyStore keyStore = KeyStore.getInstance(keyStoreType); keyStore.load(null); keyStore.setCertificateEntry("anchor", ca); // 创建一个 TrustManager 仅把 Keystore 中的证书 作为信任的锚点 String algorithm = TrustManagerFactory.getDefaultAlgorithm(); TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(algorithm); trustManagerFactory.init(keyStore); TrustManager[] trustManagers = trustManagerFactory.getTrustManagers(); // 用 TrustManager 初始化一个 SSLContext SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(null, trustManagers, null); return sslContext.getSocketFactory(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (CertificateException e) { e.printStackTrace(); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (KeyStoreException e) { e.printStackTrace(); } catch (KeyManagementException e) { e.printStackTrace(); } return null; } } ================================================ FILE: securitysdkcore/src/main/java/com/nstl/securitysdkcore/crypt/bean/EncryptData.java ================================================ package com.nstl.securitysdkcore.crypt.bean; /** * Created by plldzy on 17-11-15. */ //加密后的key和内容 public class EncryptData { private byte[] encryKey; private byte[] encryContent; public byte[] getEncryKey() { return encryKey; } public void setEncryKey(byte[] encryKey) { this.encryKey = encryKey; } public byte[] getEncryContent() { return encryContent; } public void setEncryContent(byte[] encryContent) { this.encryContent = encryContent; } } ================================================ FILE: securitysdkcore/src/main/java/com/nstl/securitysdkcore/reinforce/DetectRootUtil.java ================================================ package com.nstl.securitysdkcore.reinforce; import android.content.Context; import android.os.Build; import com.nstl.securitysdkcore.HelpUtil; import com.nstl.securitysdkcore.NativeCoreUtil; import com.nstl.securitysdkcore.reinforce.bean.InstallPackageInfo; import java.io.File; import java.util.LinkedList; import java.util.List; /** * Created by plldzy on 17-11-15. * 检测用户手机是否root,单利模式 */ public class DetectRootUtil { private List rootAppNameList = new LinkedList(); private static Context context = null; private static DetectRootUtil instance = null; private DetectRootUtil(Context context){ this.context = context; //初始化常用的root应用和root管理应用的包名 rootAppNameList.add("com.mgyun.shua.su"); //root大师 rootAppNameList.add("com.qihoo.permmgr"); //360一键root rootAppNameList.add("eu.chainfire.supersu"); //supersu(root授权管理) rootAppNameList.add("com.shuame.rootgenius"); //root精灵 rootAppNameList.add("com.kingroot.kinguser"); //kingroot } public static synchronized DetectRootUtil getInstance(Context context){ if (instance == null){ instance = new DetectRootUtil(context); } return instance; } /** * 查看系统是正式版还是测试版,测试版可以运行root能力 * @return */ private static boolean checkDeviceDebuggable(){ String buildTags = Build.TAGS; if(buildTags != null && buildTags.contains("test-keys")){ return true; } return false; } /** * //检测root授权管理和进行root的应用APK * @return */ private static boolean checkRootApk(){ boolean flag = false; List installPackageInfoList = HelpUtil.getInstallPackageAndSig(context); //查找已知的广泛使用的root工具和root管理工具:1.kingroot,2.root精灵,3.supersu(对已经root进行授权管理的软件),4.360一键root,5.root大师 for(InstallPackageInfo pkg : installPackageInfoList){ if (instance.rootAppNameList.contains(pkg.getPkgName())){ flag = true; break; } } return flag; } /** * //检测是否存在su,并确定su是否是可执行的提权程序 * @return */ private static boolean checkExitSUAndIsExecute(){ boolean flag = false; NativeCoreUtil nativeCoreUtil = new NativeCoreUtil(); if(nativeCoreUtil.isExisSUAndExecute() > 0) flag = true; return flag; } /** * 检测设备是否被root * @return */ public boolean isRoot(){ boolean flag = false; flag = checkDeviceDebuggable(); if(!flag){ flag = checkRootApk(); } if(!flag){ flag = checkExitSUAndIsExecute(); } return flag; } //最后一种root检测方法:就是直接进行root授权请求,但是用户交互体验不好,另外用户拒绝后,无法判断用户是否root。 } ================================================ FILE: securitysdkcore/src/main/java/com/nstl/securitysdkcore/reinforce/IVerifyListener.java ================================================ package com.nstl.securitysdkcore.reinforce; /** * Created by plldzy on 17-11-15. */ /** * 签名效验时会调用,签名合法调用success,签名非法调用fail,用户可以在fail进行处理,比如退出程序,或者提示用户下载正版等等,具体业务逻辑业务方自己实现 */ public interface IVerifyListener { public void onVerifySuccess(); public void onVerifyFail(); } ================================================ FILE: securitysdkcore/src/main/java/com/nstl/securitysdkcore/reinforce/JarSignatureVerifier.java ================================================ package com.nstl.securitysdkcore.reinforce; import java.io.IOException; import java.io.InputStream; import java.util.Enumeration; import java.util.jar.JarEntry; import java.util.jar.JarFile; /** * Created by daizhongyin on 2018/5/7. */ /** 签名的jar的验证器 */ public class JarSignatureVerifier { public static void main(String[] args) throws IOException { String name1 = "D:\\Documents\\keystore\\commons-collections4-4.1_true.jar"; System.out.println(verifyJar(name1)); } public static boolean verifyJar(String jarPath) { boolean flag = false; try { flag = verify(jarPath); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } return flag; } private static boolean verify(String jarPath) throws IOException { boolean flag = true; JarFile jar = new JarFile(jarPath, true); Enumeration entries = jar.entries(); while (entries.hasMoreElements()) { JarEntry entry = entries.nextElement(); InputStream is = null; try { byte[] buffer = new byte[8192]; is = jar.getInputStream(entry); while ((is.read(buffer, 0, buffer.length)) != -1) { // We just read. This will throw a SecurityException // if a signature/digest check fails. } } catch (SecurityException se) { flag = false; } finally { if (is != null) { is.close(); } } } return flag; } } ================================================ FILE: securitysdkcore/src/main/java/com/nstl/securitysdkcore/reinforce/SafeZipFile.java ================================================ package com.nstl.securitysdkcore.reinforce; import android.content.Context; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.Signature; import android.text.TextUtils; import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.LinkedList; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; /** * 为了保证ZIPFile读取APK文件的安全性:防止zipfile的目录穿越漏洞,同时确保只有一个Dex,此外还需要确保APK中的签名和其自生代码的信息一致 */ public class SafeZipFile { private String apkFilePath = null; private boolean zipFileIsIllegal = false; private Context context; private String md5Sig; /** * @param file * @param context Context上下文 * @param md5Sig 要校验的签名证书的MD5值(可以通过keytool工具来查看) */ public SafeZipFile(File file, Context context, String md5Sig) { apkFilePath = file.getAbsolutePath(); context = context; md5Sig = md5Sig; } /** * true表示apk或者签名zip为合法的,false表示zip或者apk非法 * * @param validateSig 是否对zipfile文件进行签名校验,true表示进行,false表示不用进行zip文件的签名校验。 * @return true表示zipfile文件合法,false非法 */ public boolean isZipFileValid(boolean validateSig) { boolean flag = false; if (validateSig) { flag = this.dexCheck() && this.apkSignCheck(this.context, this.md5Sig); } else { flag = this.dexCheck(); } return flag; } /** * 判断zipfile的合法性,防止../ 及 重复的dex文件 * * @return true表示合法,false非法 * @throws IOException */ private boolean dexCheck() { // 检查zip压缩文件中,是否会包含../,以及zip中的所有dex文件,确保不存在相同的姓名,注意多个dex文件的情况-classes.dex,classes2.dex boolean flag = true; if (TextUtils.isEmpty(apkFilePath)) { return false; } File srcFile = new File(this.apkFilePath); FileInputStream fileInputStream = null; ZipEntry zipEntry = null; StringBuilder fileNameBuilder = new StringBuilder(); ZipInputStream zipInputStream = null; try { fileInputStream = new FileInputStream(srcFile); zipInputStream = new ZipInputStream(new BufferedInputStream(fileInputStream)); while ((zipEntry = zipInputStream.getNextEntry()) != null) { if (zipEntry.isDirectory()) { continue; } else { String entryName = zipEntry.getName(); if (entryName.contains("../")) { flag = false; return flag; } if (entryName.endsWith(".dex")) { if (fileNameBuilder.toString().contains(entryName)) { flag = false; return flag; } else { fileNameBuilder.append(entryName); } } } } } catch (Exception e) { e.printStackTrace(); flag = false; } finally { try { if (zipInputStream != null) { zipInputStream.close(); } if (fileInputStream != null) { fileInputStream.close(); } } catch (Exception e) { e.printStackTrace(); } } return flag; } /** * 检查apk的签名 * * @param ct * @param sig * @return true表示apk签名合法,false表示签名非法 */ private boolean apkSignCheck(Context ct, String sig) { //2.读取APK中的META-INF目录下的签名文件,验证一致性 boolean flag = false; PackageManager pm = ct.getPackageManager(); PackageInfo pi = pm.getPackageArchiveInfo(apkFilePath, PackageManager.GET_SIGNATURES); if (pi != null) { Signature[] signatures = pi.signatures; if (signatures != null && signatures.length > 0) { String signature = encryptionMD5(signatures[0].toByteArray()); if (signature.equals(sig)) flag = true; } } return flag; } /** * md5签名 * * @param byteStr * @return */ private String encryptionMD5(byte[] byteStr) { MessageDigest messageDigest = null; StringBuffer md5StrBuff = new StringBuffer(); try { messageDigest = MessageDigest.getInstance("MD5"); messageDigest.reset(); messageDigest.update(byteStr); byte[] byteArray = messageDigest.digest(); for (int i = 0; i < byteArray.length; i++) { if (Integer.toHexString(0xFF & byteArray[i]).length() == 1) { md5StrBuff.append("0").append(Integer.toHexString(0xFF & byteArray[i])); } else { md5StrBuff.append(Integer.toHexString(0xFF & byteArray[i])); } } } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } return md5StrBuff.toString(); } } ================================================ FILE: securitysdkcore/src/main/java/com/nstl/securitysdkcore/reinforce/bean/InstallPackageInfo.java ================================================ package com.nstl.securitysdkcore.reinforce.bean; import android.content.pm.ActivityInfo; import android.content.pm.PermissionInfo; import android.content.pm.ServiceInfo; /** * Created by plldzy on 17-11-17. */ public class InstallPackageInfo { private String pkgName; //应用包名 private String pkgSig; //应用签名 private int versionCode; //应用版本号 private String versionName; //应用版本名称 private PermissionInfo[] permissionInfos = null; //应用申请权限列表 private ActivityInfo[] activityInfos = null; //应用activity列表,下同 private ServiceInfo[] serviceInfos = null; private ActivityInfo[] receivers = null; private long firstInstallTime; private long lastUpdateTime; public String getPkgName() { return pkgName; } public void setPkgName(String pkgName) { this.pkgName = pkgName; } public String getPkgSig() { return pkgSig; } public void setPkgSig(String pkgSig) { this.pkgSig = pkgSig; } public int getVersionCode() { return versionCode; } public void setVersionCode(int version) { this.versionCode = version; } public String getVersionName() { return versionName; } public void setVersionName(String versionName) { this.versionName = versionName; } public PermissionInfo[] getPermissionInfos() { return permissionInfos; } public void setPermissionInfos(PermissionInfo[] permissionInfos) { this.permissionInfos = permissionInfos; } public ActivityInfo[] getActivityInfos() { return activityInfos; } public void setActivityInfos(ActivityInfo[] activityInfos) { this.activityInfos = activityInfos; } public ServiceInfo[] getServiceInfos() { return serviceInfos; } public void setServiceInfos(ServiceInfo[] serviceInfos) { this.serviceInfos = serviceInfos; } public ActivityInfo[] getReceivers() { return receivers; } public void setReceivers(ActivityInfo[] receivers) { this.receivers = receivers; } public long getFirstInstallTime() { return firstInstallTime; } public void setFirstInstallTime(long firstInstallTime) { this.firstInstallTime = firstInstallTime; } public long getLastUpdateTime() { return lastUpdateTime; } public void setLastUpdateTime(long lastUpdateTime) { this.lastUpdateTime = lastUpdateTime; } } ================================================ FILE: securitysdkcore/src/main/java/com/nstl/securitysdkcore/urischeme/IValidateIntentUriScheme.java ================================================ package com.nstl.securitysdkcore.urischeme; import android.net.Uri; import com.nstl.securitysdkcore.config.IntentUriScheme; /** * Created by daizhongyin on 2018/5/4. */ //Uri scheme类似 zhifubao://web?url=http://aaa.bbb.com/xxx public interface IValidateIntentUriScheme { /** * 业务方需要实现的Intent Uri Scheme的校验策略,其中根据IntentUriScheme.type字段来表示是黑名单策略,还是白名单策略,建议白名单策略 * 比如uri scheme可以调用内置浏览器,打开url,因此可以增加url白名单限制,如 zhifubao://web?url=http://aaa.bbb.com/xxx,我们对url的参数进行白名单校验,禁止打开非支付宝域名下的url。 * 也可以限制uri 可以打开的协议,比如禁止传入tel://10086这种拨打电话的uri scheme * @param uri * @param uriScheme * @return */ boolean validateUri(Uri uri, IntentUriScheme uriScheme); } ================================================ FILE: securitysdkcore/src/main/java/com/nstl/securitysdkcore/urischeme/IntentUriSchemeFilter.java ================================================ package com.nstl.securitysdkcore.urischeme; import android.content.Context; import android.content.Intent; import android.net.Uri; import com.alibaba.fastjson.JSON; import com.nstl.securitysdkcore.SecuritySDKInit; import com.nstl.securitysdkcore.config.IntentUriScheme; /** * Created by daizhongyin on 2018/5/4. * Uri scheme类似 zhifubao://web?url=http://aaa.bbb.com/xxx,其中参数容易被攻击者篡改以达到某种目的,为此,安全sdk定义IntentUriSchemeFilter类来提供Uri Scheme安全拦截的功能,作为临时补丁和恶意行为的拦截 */ public class IntentUriSchemeFilter { private Context context = null; public IntentUriSchemeFilter(Context context){ this.context = context; } /** * 对传入的IntentUri Scheme进行校验,true表示合法,false表示Uri参数非法 * @param uriScheme 外部传入的Uri字符串 * @param validateIntentUriScheme server端配置下发的IntentUriScheme拦截策略 * @return */ public boolean validate(String uriScheme, IValidateIntentUriScheme validateIntentUriScheme){ boolean flag = false; Uri uri = Uri.parse(uriScheme); String str = SecuritySDKInit.getInstance(context).getConfigStringValueByKey(SecuritySDKInit.INTENTURISCHEMELIST); IntentUriScheme intentUriScheme = JSON.parseObject(str, IntentUriScheme.class); flag = validateIntentUriScheme.validateUri(uri, intentUriScheme); return flag; } } ================================================ FILE: securitysdkcore/src/main/java/com/nstl/securitysdkcore/webview/IMethodInvokeInterface.java ================================================ package com.nstl.securitysdkcore.webview; /** * Created by plldzy on 2017/4/27. */ public interface IMethodInvokeInterface { /** * 处理js传递过来的参数,然后把本地函数的处理结果,返回给js页面 * @param data * @return */ String dispatch(String data); } ================================================ FILE: securitysdkcore/src/main/java/com/nstl/securitysdkcore/webview/SafeWebView.java ================================================ package com.nstl.securitysdkcore.webview; import android.content.Context; import android.net.http.SslError; import android.os.Build; import android.text.TextUtils; import android.util.AttributeSet; import android.webkit.SslErrorHandler; import android.webkit.WebSettings; import android.webkit.WebView; import android.webkit.WebViewClient; import com.alibaba.fastjson.JSON; import com.github.lzyzsd.jsbridge.BridgeHandler; import com.github.lzyzsd.jsbridge.BridgeWebView; import com.github.lzyzsd.jsbridge.BridgeWebViewClient; import com.github.lzyzsd.jsbridge.CallBackFunction; import com.nstl.securitysdkcore.SecuritySDKInit; import com.nstl.securitysdkcore.config.WebviewConfig; /** * Created by Lin on 2017/12/14. */ public class SafeWebView extends BridgeWebView implements BridgeHandler { private IMethodInvokeInterface invokeInterface = null; //业务方根据JS传递进来的参数,进行实际处理的类 private WebSettings settings = null; private WebViewClient client = null; private Context context = null; public SafeWebView(Context context, AttributeSet attrs) { super(context, attrs); } public SafeWebView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } public SafeWebView(Context context) { super(context); } /** * 客户端调用SafeWebView后,传递相关参数进行初始化 * 根据系统的版本关闭具有漏洞的接口 * * @param * @return void */ public void init(Context context, IMethodInvokeInterface miInterface) { this.context = context; settings = this.getSettings(); settings.setJavaScriptCanOpenWindowsAutomatically(true); this.invokeInterface = miInterface; client = new BridgeWebViewClient(this) { @Override public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) { super.onReceivedSslError(view, handler, error); //停止对错误的https证书的继续加载 handler.cancel(); } }; if (Build.VERSION.SDK_INT <= 16) { removeJavascriptInterface("searchBoxJavaBridge_"); removeJavascriptInterface("accessibility"); removeJavascriptInterface("accessibilityTraversal"); } this.setWebViewClient(client); } /** * 重写BridgeWebView 的 java向js注册对象的方法,里面主要是进行过滤 * @param handlerName */ public void registerHandler(String handlerName) { super.registerHandler(handlerName, this); } /** * 重写loadUrl,目的是为了判断url的准入 * @param url 加载的url */ @Override public void loadUrl(String url) { if (verifyUrl(url.trim()) < 0) { return; } super.loadUrl(url); } /** * 判断当前url是否合法,业务方可以根据业务场景,增加url的白名单匹配过程;如禁止webview加载非白名单中的url */ private int verifyUrl(String url) { //file协议只允许assert相关目录下的html文件 if (fileUrlISSafe(url)) { enableFileCrossAccess(); } else { disableFileCrossAccess(); } return 1; } /** * 关闭了file协议的跨域访问,防止webview加载外部file文件读取应用内的私有文件,造成信息泄露 */ private void disableFileCrossAccess() { settings.setAllowFileAccess(false); settings.setAllowFileAccessFromFileURLs(false); settings.setAllowUniversalAccessFromFileURLs(false); } /** * 开启file域访问能力,确保加载file协议文件,是在assert目录下,或者信任的私有目录下 */ private void enableFileCrossAccess() { settings.setAllowFileAccess(true); settings.setAllowFileAccessFromFileURLs(true); settings.setAllowUniversalAccessFromFileURLs(true); } /** * 返回到js的数据 * 双方约定一种数据交换格式:json, * @param data * @param function */ @Override public void handler(String data, CallBackFunction function) { String resultJson = "error"; //获得当前加载的url,如果url不是本地目录的文件,或者是百度域下的url,则不进行handle的处理【禁止JS和java本地代码进行交互,从而调用危险接口】 if (this.getUrl() != null && (urlISSafe(this.getUrl().trim()))) { //执行实现的接口,相当于js代码调用了java对象的方法,通过json来控制 resultJson = invokeInterface.dispatch(data); function.onCallBack(resultJson); } function.onCallBack(resultJson); } /** * 判断url是否安全:http开头的协议,只允许加载百度域下的url,file协议,只允许加载当前应用资源目录下的html文件 * @param urlStr * @return */ private boolean urlISSafe(String urlStr) { boolean isSafeFlag = false; String str = SecuritySDKInit.getInstance(context).getConfigStringValueByKey(SecuritySDKInit.WEBVIEWCONFIG); WebviewConfig webviewConfig = JSON.parseObject(str, WebviewConfig.class); if (urlStr.startsWith("http") || urlStr.startsWith("https")) { String host = urlStr.substring(0, urlStr.indexOf("/")); //匹配url是否是可信域 for(String s : webviewConfig.getUrlWhiteList()){ if(host.endsWith(s)){ isSafeFlag = true; break; } } }else{ //根据file协议类型来判断,url是否合法 isSafeFlag = fileUrlISSafe(urlStr); } return isSafeFlag; } /** * //file协议的url是合法的判断 * * @param fileUrl * @return */ private boolean fileUrlISSafe(String fileUrl) { boolean isSafeFlag = false; if (fileUrl.startsWith("file")) { //file路径截取 String urlPath = fileUrl.substring(fileUrl.indexOf("file:") + 7); //可信的file协议的目录文件 if (!TextUtils.isEmpty(((CharSequence) urlPath)) && !urlPath.contains("..") && !urlPath.contains("\\") && !urlPath.contains("%")) { if (urlPath.startsWith("/android_asset") || urlPath.startsWith("/android_res")) { isSafeFlag = true; } } } return isSafeFlag; } } ================================================ FILE: securitysdkcore/src/main/res/values/strings.xml ================================================ securitysdkcore ================================================ FILE: securitysdkcore/src/test/java/com/nstl/securitysdkcore/ExampleUnitTest.java ================================================ package com.nstl.securitysdkcore; import org.junit.Test; import static org.junit.Assert.*; /** * Example local unit test, which will execute on the development machine (host). * * @see Testing documentation */ public class ExampleUnitTest { @Test public void addition_isCorrect() throws Exception { assertEquals(4, 2 + 2); } } ================================================ FILE: settings.gradle ================================================ include ':app', ':securitysdkcore'