diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..dfe07704 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# Auto detect text files and perform LF normalization +* text=auto diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..a1c2a238 --- /dev/null +++ b/.gitignore @@ -0,0 +1,23 @@ +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* diff --git a/.gradle/6.7.1/executionHistory/executionHistory.bin b/.gradle/6.7.1/executionHistory/executionHistory.bin new file mode 100644 index 00000000..df13e64c Binary files /dev/null and b/.gradle/6.7.1/executionHistory/executionHistory.bin differ diff --git a/.gradle/6.7.1/executionHistory/executionHistory.lock b/.gradle/6.7.1/executionHistory/executionHistory.lock new file mode 100644 index 00000000..720c7fee Binary files /dev/null and b/.gradle/6.7.1/executionHistory/executionHistory.lock differ diff --git a/.gradle/6.7.1/fileChanges/last-build.bin b/.gradle/6.7.1/fileChanges/last-build.bin new file mode 100644 index 00000000..f76dd238 Binary files /dev/null and b/.gradle/6.7.1/fileChanges/last-build.bin differ diff --git a/.gradle/6.7.1/fileHashes/fileHashes.bin b/.gradle/6.7.1/fileHashes/fileHashes.bin new file mode 100644 index 00000000..ea483ea0 Binary files /dev/null and b/.gradle/6.7.1/fileHashes/fileHashes.bin differ diff --git a/.gradle/6.7.1/fileHashes/fileHashes.lock b/.gradle/6.7.1/fileHashes/fileHashes.lock new file mode 100644 index 00000000..c703349d Binary files /dev/null and b/.gradle/6.7.1/fileHashes/fileHashes.lock differ diff --git a/.gradle/6.7.1/fileHashes/resourceHashesCache.bin b/.gradle/6.7.1/fileHashes/resourceHashesCache.bin new file mode 100644 index 00000000..7282f4f3 Binary files /dev/null and b/.gradle/6.7.1/fileHashes/resourceHashesCache.bin differ diff --git a/.gradle/6.7.1/gc.properties b/.gradle/6.7.1/gc.properties new file mode 100644 index 00000000..e69de29b diff --git a/.gradle/6.7.1/javaCompile/classAnalysis.bin b/.gradle/6.7.1/javaCompile/classAnalysis.bin new file mode 100644 index 00000000..4858f70d Binary files /dev/null and b/.gradle/6.7.1/javaCompile/classAnalysis.bin differ diff --git a/.gradle/6.7.1/javaCompile/jarAnalysis.bin b/.gradle/6.7.1/javaCompile/jarAnalysis.bin new file mode 100644 index 00000000..a9187b3b Binary files /dev/null and b/.gradle/6.7.1/javaCompile/jarAnalysis.bin differ diff --git a/.gradle/6.7.1/javaCompile/javaCompile.lock b/.gradle/6.7.1/javaCompile/javaCompile.lock new file mode 100644 index 00000000..878bb43d Binary files /dev/null and b/.gradle/6.7.1/javaCompile/javaCompile.lock differ diff --git a/.gradle/6.7.1/javaCompile/taskHistory.bin b/.gradle/6.7.1/javaCompile/taskHistory.bin new file mode 100644 index 00000000..0c6562d5 Binary files /dev/null and b/.gradle/6.7.1/javaCompile/taskHistory.bin differ diff --git a/.gradle/buildOutputCleanup/buildOutputCleanup.lock b/.gradle/buildOutputCleanup/buildOutputCleanup.lock new file mode 100644 index 00000000..4570e97a Binary files /dev/null and b/.gradle/buildOutputCleanup/buildOutputCleanup.lock differ diff --git a/.gradle/buildOutputCleanup/cache.properties b/.gradle/buildOutputCleanup/cache.properties new file mode 100644 index 00000000..42441489 --- /dev/null +++ b/.gradle/buildOutputCleanup/cache.properties @@ -0,0 +1,2 @@ +#Wed May 19 16:54:23 CST 2021 +gradle.version=6.7.1 diff --git a/.gradle/buildOutputCleanup/outputFiles.bin b/.gradle/buildOutputCleanup/outputFiles.bin new file mode 100644 index 00000000..d8801815 Binary files /dev/null and b/.gradle/buildOutputCleanup/outputFiles.bin differ diff --git a/.gradle/checksums/checksums.lock b/.gradle/checksums/checksums.lock new file mode 100644 index 00000000..016cad5b Binary files /dev/null and b/.gradle/checksums/checksums.lock differ diff --git a/.gradle/checksums/md5-checksums.bin b/.gradle/checksums/md5-checksums.bin new file mode 100644 index 00000000..cc7e3f04 Binary files /dev/null and b/.gradle/checksums/md5-checksums.bin differ diff --git a/.gradle/checksums/sha1-checksums.bin b/.gradle/checksums/sha1-checksums.bin new file mode 100644 index 00000000..bc901261 Binary files /dev/null and b/.gradle/checksums/sha1-checksums.bin differ diff --git a/.gradle/configuration-cache/gc.properties b/.gradle/configuration-cache/gc.properties new file mode 100644 index 00000000..e69de29b diff --git a/.gradle/vcs-1/gc.properties b/.gradle/vcs-1/gc.properties new file mode 100644 index 00000000..e69de29b diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 00000000..26d33521 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 00000000..61a9130c --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml new file mode 100644 index 00000000..485b7145 --- /dev/null +++ b/.idea/gradle.xml @@ -0,0 +1,24 @@ + + + + + + + \ No newline at end of file diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml new file mode 100644 index 00000000..0380d8d3 --- /dev/null +++ b/.idea/jarRepositories.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__androidx_activity_activity_1_0_0_aar.xml b/.idea/libraries/Gradle__androidx_activity_activity_1_0_0_aar.xml new file mode 100644 index 00000000..01b1592e --- /dev/null +++ b/.idea/libraries/Gradle__androidx_activity_activity_1_0_0_aar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__androidx_annotation_annotation_1_1_0.xml b/.idea/libraries/Gradle__androidx_annotation_annotation_1_1_0.xml new file mode 100644 index 00000000..b2158ac9 --- /dev/null +++ b/.idea/libraries/Gradle__androidx_annotation_annotation_1_1_0.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__androidx_annotation_annotation_experimental_1_0_0_aar.xml b/.idea/libraries/Gradle__androidx_annotation_annotation_experimental_1_0_0_aar.xml new file mode 100644 index 00000000..23aa7a1c --- /dev/null +++ b/.idea/libraries/Gradle__androidx_annotation_annotation_experimental_1_0_0_aar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__androidx_appcompat_appcompat_1_2_0_aar.xml b/.idea/libraries/Gradle__androidx_appcompat_appcompat_1_2_0_aar.xml new file mode 100644 index 00000000..6cca000c --- /dev/null +++ b/.idea/libraries/Gradle__androidx_appcompat_appcompat_1_2_0_aar.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__androidx_appcompat_appcompat_resources_1_2_0_aar.xml b/.idea/libraries/Gradle__androidx_appcompat_appcompat_resources_1_2_0_aar.xml new file mode 100644 index 00000000..3eb1b7b5 --- /dev/null +++ b/.idea/libraries/Gradle__androidx_appcompat_appcompat_resources_1_2_0_aar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__androidx_arch_core_core_common_2_1_0.xml b/.idea/libraries/Gradle__androidx_arch_core_core_common_2_1_0.xml new file mode 100644 index 00000000..22084152 --- /dev/null +++ b/.idea/libraries/Gradle__androidx_arch_core_core_common_2_1_0.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__androidx_arch_core_core_runtime_2_0_0_aar.xml b/.idea/libraries/Gradle__androidx_arch_core_core_runtime_2_0_0_aar.xml new file mode 100644 index 00000000..c80b3e2b --- /dev/null +++ b/.idea/libraries/Gradle__androidx_arch_core_core_runtime_2_0_0_aar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__androidx_cardview_cardview_1_0_0_aar.xml b/.idea/libraries/Gradle__androidx_cardview_cardview_1_0_0_aar.xml new file mode 100644 index 00000000..4f0c1954 --- /dev/null +++ b/.idea/libraries/Gradle__androidx_cardview_cardview_1_0_0_aar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__androidx_collection_collection_1_1_0.xml b/.idea/libraries/Gradle__androidx_collection_collection_1_1_0.xml new file mode 100644 index 00000000..eafc05e9 --- /dev/null +++ b/.idea/libraries/Gradle__androidx_collection_collection_1_1_0.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__androidx_constraintlayout_constraintlayout_2_0_1_aar.xml b/.idea/libraries/Gradle__androidx_constraintlayout_constraintlayout_2_0_1_aar.xml new file mode 100644 index 00000000..7cf8b6f9 --- /dev/null +++ b/.idea/libraries/Gradle__androidx_constraintlayout_constraintlayout_2_0_1_aar.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__androidx_constraintlayout_constraintlayout_2_0_4_aar.xml b/.idea/libraries/Gradle__androidx_constraintlayout_constraintlayout_2_0_4_aar.xml new file mode 100644 index 00000000..0453b146 --- /dev/null +++ b/.idea/libraries/Gradle__androidx_constraintlayout_constraintlayout_2_0_4_aar.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__androidx_constraintlayout_constraintlayout_solver_2_0_1.xml b/.idea/libraries/Gradle__androidx_constraintlayout_constraintlayout_solver_2_0_1.xml new file mode 100644 index 00000000..24d38041 --- /dev/null +++ b/.idea/libraries/Gradle__androidx_constraintlayout_constraintlayout_solver_2_0_1.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__androidx_constraintlayout_constraintlayout_solver_2_0_4.xml b/.idea/libraries/Gradle__androidx_constraintlayout_constraintlayout_solver_2_0_4.xml new file mode 100644 index 00000000..cba1dae0 --- /dev/null +++ b/.idea/libraries/Gradle__androidx_constraintlayout_constraintlayout_solver_2_0_4.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__androidx_coordinatorlayout_coordinatorlayout_1_1_0_aar.xml b/.idea/libraries/Gradle__androidx_coordinatorlayout_coordinatorlayout_1_1_0_aar.xml new file mode 100644 index 00000000..9965072a --- /dev/null +++ b/.idea/libraries/Gradle__androidx_coordinatorlayout_coordinatorlayout_1_1_0_aar.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__androidx_core_core_1_3_1_aar.xml b/.idea/libraries/Gradle__androidx_core_core_1_3_1_aar.xml new file mode 100644 index 00000000..ad0c950b --- /dev/null +++ b/.idea/libraries/Gradle__androidx_core_core_1_3_1_aar.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__androidx_cursoradapter_cursoradapter_1_0_0_aar.xml b/.idea/libraries/Gradle__androidx_cursoradapter_cursoradapter_1_0_0_aar.xml new file mode 100644 index 00000000..d340865c --- /dev/null +++ b/.idea/libraries/Gradle__androidx_cursoradapter_cursoradapter_1_0_0_aar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__androidx_customview_customview_1_0_0_aar.xml b/.idea/libraries/Gradle__androidx_customview_customview_1_0_0_aar.xml new file mode 100644 index 00000000..f4199c6f --- /dev/null +++ b/.idea/libraries/Gradle__androidx_customview_customview_1_0_0_aar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__androidx_documentfile_documentfile_1_0_0_aar.xml b/.idea/libraries/Gradle__androidx_documentfile_documentfile_1_0_0_aar.xml new file mode 100644 index 00000000..7cd55b68 --- /dev/null +++ b/.idea/libraries/Gradle__androidx_documentfile_documentfile_1_0_0_aar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__androidx_drawerlayout_drawerlayout_1_0_0_aar.xml b/.idea/libraries/Gradle__androidx_drawerlayout_drawerlayout_1_0_0_aar.xml new file mode 100644 index 00000000..18a50584 --- /dev/null +++ b/.idea/libraries/Gradle__androidx_drawerlayout_drawerlayout_1_0_0_aar.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__androidx_dynamicanimation_dynamicanimation_1_0_0_aar.xml b/.idea/libraries/Gradle__androidx_dynamicanimation_dynamicanimation_1_0_0_aar.xml new file mode 100644 index 00000000..f55f1ea4 --- /dev/null +++ b/.idea/libraries/Gradle__androidx_dynamicanimation_dynamicanimation_1_0_0_aar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__androidx_fragment_fragment_1_1_0_aar.xml b/.idea/libraries/Gradle__androidx_fragment_fragment_1_1_0_aar.xml new file mode 100644 index 00000000..b49cca42 --- /dev/null +++ b/.idea/libraries/Gradle__androidx_fragment_fragment_1_1_0_aar.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__androidx_interpolator_interpolator_1_0_0_aar.xml b/.idea/libraries/Gradle__androidx_interpolator_interpolator_1_0_0_aar.xml new file mode 100644 index 00000000..c5daffc8 --- /dev/null +++ b/.idea/libraries/Gradle__androidx_interpolator_interpolator_1_0_0_aar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__androidx_legacy_legacy_support_core_utils_1_0_0_aar.xml b/.idea/libraries/Gradle__androidx_legacy_legacy_support_core_utils_1_0_0_aar.xml new file mode 100644 index 00000000..1b6e97e0 --- /dev/null +++ b/.idea/libraries/Gradle__androidx_legacy_legacy_support_core_utils_1_0_0_aar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_common_2_1_0.xml b/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_common_2_1_0.xml new file mode 100644 index 00000000..9354d446 --- /dev/null +++ b/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_common_2_1_0.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_livedata_2_0_0_aar.xml b/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_livedata_2_0_0_aar.xml new file mode 100644 index 00000000..ce5dd556 --- /dev/null +++ b/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_livedata_2_0_0_aar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_livedata_core_2_0_0_aar.xml b/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_livedata_core_2_0_0_aar.xml new file mode 100644 index 00000000..af653768 --- /dev/null +++ b/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_livedata_core_2_0_0_aar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_runtime_2_1_0_aar.xml b/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_runtime_2_1_0_aar.xml new file mode 100644 index 00000000..5346bfa0 --- /dev/null +++ b/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_runtime_2_1_0_aar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_viewmodel_2_1_0_aar.xml b/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_viewmodel_2_1_0_aar.xml new file mode 100644 index 00000000..2cea17fe --- /dev/null +++ b/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_viewmodel_2_1_0_aar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__androidx_loader_loader_1_0_0_aar.xml b/.idea/libraries/Gradle__androidx_loader_loader_1_0_0_aar.xml new file mode 100644 index 00000000..89715b1d --- /dev/null +++ b/.idea/libraries/Gradle__androidx_loader_loader_1_0_0_aar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__androidx_localbroadcastmanager_localbroadcastmanager_1_0_0_aar.xml b/.idea/libraries/Gradle__androidx_localbroadcastmanager_localbroadcastmanager_1_0_0_aar.xml new file mode 100644 index 00000000..706a617c --- /dev/null +++ b/.idea/libraries/Gradle__androidx_localbroadcastmanager_localbroadcastmanager_1_0_0_aar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__androidx_print_print_1_0_0_aar.xml b/.idea/libraries/Gradle__androidx_print_print_1_0_0_aar.xml new file mode 100644 index 00000000..dfad40b8 --- /dev/null +++ b/.idea/libraries/Gradle__androidx_print_print_1_0_0_aar.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__androidx_recyclerview_recyclerview_1_1_0_aar.xml b/.idea/libraries/Gradle__androidx_recyclerview_recyclerview_1_1_0_aar.xml new file mode 100644 index 00000000..ea102244 --- /dev/null +++ b/.idea/libraries/Gradle__androidx_recyclerview_recyclerview_1_1_0_aar.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__androidx_savedstate_savedstate_1_0_0_aar.xml b/.idea/libraries/Gradle__androidx_savedstate_savedstate_1_0_0_aar.xml new file mode 100644 index 00000000..61546812 --- /dev/null +++ b/.idea/libraries/Gradle__androidx_savedstate_savedstate_1_0_0_aar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__androidx_test_core_1_3_0_aar.xml b/.idea/libraries/Gradle__androidx_test_core_1_3_0_aar.xml new file mode 100644 index 00000000..2c70c02f --- /dev/null +++ b/.idea/libraries/Gradle__androidx_test_core_1_3_0_aar.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__androidx_test_espresso_espresso_core_3_3_0_aar.xml b/.idea/libraries/Gradle__androidx_test_espresso_espresso_core_3_3_0_aar.xml new file mode 100644 index 00000000..1c39e7e7 --- /dev/null +++ b/.idea/libraries/Gradle__androidx_test_espresso_espresso_core_3_3_0_aar.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__androidx_test_espresso_espresso_idling_resource_3_3_0_aar.xml b/.idea/libraries/Gradle__androidx_test_espresso_espresso_idling_resource_3_3_0_aar.xml new file mode 100644 index 00000000..ba11c807 --- /dev/null +++ b/.idea/libraries/Gradle__androidx_test_espresso_espresso_idling_resource_3_3_0_aar.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__androidx_test_ext_junit_1_1_2_aar.xml b/.idea/libraries/Gradle__androidx_test_ext_junit_1_1_2_aar.xml new file mode 100644 index 00000000..b7912691 --- /dev/null +++ b/.idea/libraries/Gradle__androidx_test_ext_junit_1_1_2_aar.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__androidx_test_monitor_1_3_0_aar.xml b/.idea/libraries/Gradle__androidx_test_monitor_1_3_0_aar.xml new file mode 100644 index 00000000..2d7f15ae --- /dev/null +++ b/.idea/libraries/Gradle__androidx_test_monitor_1_3_0_aar.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__androidx_test_runner_1_3_0_aar.xml b/.idea/libraries/Gradle__androidx_test_runner_1_3_0_aar.xml new file mode 100644 index 00000000..00692dc0 --- /dev/null +++ b/.idea/libraries/Gradle__androidx_test_runner_1_3_0_aar.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__androidx_transition_transition_1_2_0_aar.xml b/.idea/libraries/Gradle__androidx_transition_transition_1_2_0_aar.xml new file mode 100644 index 00000000..d73ab702 --- /dev/null +++ b/.idea/libraries/Gradle__androidx_transition_transition_1_2_0_aar.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__androidx_vectordrawable_vectordrawable_1_1_0_aar.xml b/.idea/libraries/Gradle__androidx_vectordrawable_vectordrawable_1_1_0_aar.xml new file mode 100644 index 00000000..8ef15ab4 --- /dev/null +++ b/.idea/libraries/Gradle__androidx_vectordrawable_vectordrawable_1_1_0_aar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__androidx_vectordrawable_vectordrawable_animated_1_1_0_aar.xml b/.idea/libraries/Gradle__androidx_vectordrawable_vectordrawable_animated_1_1_0_aar.xml new file mode 100644 index 00000000..7e174243 --- /dev/null +++ b/.idea/libraries/Gradle__androidx_vectordrawable_vectordrawable_animated_1_1_0_aar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__androidx_versionedparcelable_versionedparcelable_1_1_0_aar.xml b/.idea/libraries/Gradle__androidx_versionedparcelable_versionedparcelable_1_1_0_aar.xml new file mode 100644 index 00000000..4fb5aa60 --- /dev/null +++ b/.idea/libraries/Gradle__androidx_versionedparcelable_versionedparcelable_1_1_0_aar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__androidx_viewpager2_viewpager2_1_0_0_aar.xml b/.idea/libraries/Gradle__androidx_viewpager2_viewpager2_1_0_0_aar.xml new file mode 100644 index 00000000..148f0622 --- /dev/null +++ b/.idea/libraries/Gradle__androidx_viewpager2_viewpager2_1_0_0_aar.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__androidx_viewpager_viewpager_1_0_0_aar.xml b/.idea/libraries/Gradle__androidx_viewpager_viewpager_1_0_0_aar.xml new file mode 100644 index 00000000..e2c1928d --- /dev/null +++ b/.idea/libraries/Gradle__androidx_viewpager_viewpager_1_0_0_aar.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__com_google_android_material_material_1_3_0_aar.xml b/.idea/libraries/Gradle__com_google_android_material_material_1_3_0_aar.xml new file mode 100644 index 00000000..eadbae29 --- /dev/null +++ b/.idea/libraries/Gradle__com_google_android_material_material_1_3_0_aar.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__com_google_code_findbugs_jsr305_2_0_1.xml b/.idea/libraries/Gradle__com_google_code_findbugs_jsr305_2_0_1.xml new file mode 100644 index 00000000..2b834ea2 --- /dev/null +++ b/.idea/libraries/Gradle__com_google_code_findbugs_jsr305_2_0_1.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__com_squareup_javawriter_2_1_1.xml b/.idea/libraries/Gradle__com_squareup_javawriter_2_1_1.xml new file mode 100644 index 00000000..662b001e --- /dev/null +++ b/.idea/libraries/Gradle__com_squareup_javawriter_2_1_1.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__com_swift_sandhook_hookannotation_4_2_0.xml b/.idea/libraries/Gradle__com_swift_sandhook_hookannotation_4_2_0.xml new file mode 100644 index 00000000..759faabf --- /dev/null +++ b/.idea/libraries/Gradle__com_swift_sandhook_hookannotation_4_2_0.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__com_swift_sandhook_hooklib_4_2_0_aar.xml b/.idea/libraries/Gradle__com_swift_sandhook_hooklib_4_2_0_aar.xml new file mode 100644 index 00000000..e81bbec1 --- /dev/null +++ b/.idea/libraries/Gradle__com_swift_sandhook_hooklib_4_2_0_aar.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__javax_inject_javax_inject_1.xml b/.idea/libraries/Gradle__javax_inject_javax_inject_1.xml new file mode 100644 index 00000000..62012eaf --- /dev/null +++ b/.idea/libraries/Gradle__javax_inject_javax_inject_1.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__junit_junit_4_12.xml b/.idea/libraries/Gradle__junit_junit_4_12.xml new file mode 100644 index 00000000..6c078d62 --- /dev/null +++ b/.idea/libraries/Gradle__junit_junit_4_12.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__junit_junit_4_13_2.xml b/.idea/libraries/Gradle__junit_junit_4_13_2.xml new file mode 100644 index 00000000..c2cb45da --- /dev/null +++ b/.idea/libraries/Gradle__junit_junit_4_13_2.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__me_weishu_free_reflection_3_0_1_aar.xml b/.idea/libraries/Gradle__me_weishu_free_reflection_3_0_1_aar.xml new file mode 100644 index 00000000..dd5f7034 --- /dev/null +++ b/.idea/libraries/Gradle__me_weishu_free_reflection_3_0_1_aar.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__org_hamcrest_hamcrest_core_1_3.xml b/.idea/libraries/Gradle__org_hamcrest_hamcrest_core_1_3.xml new file mode 100644 index 00000000..09cf23d1 --- /dev/null +++ b/.idea/libraries/Gradle__org_hamcrest_hamcrest_core_1_3.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__org_hamcrest_hamcrest_integration_1_3.xml b/.idea/libraries/Gradle__org_hamcrest_hamcrest_integration_1_3.xml new file mode 100644 index 00000000..1a77dd83 --- /dev/null +++ b/.idea/libraries/Gradle__org_hamcrest_hamcrest_integration_1_3.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__org_hamcrest_hamcrest_library_1_3.xml b/.idea/libraries/Gradle__org_hamcrest_hamcrest_library_1_3.xml new file mode 100644 index 00000000..3d45e8e9 --- /dev/null +++ b/.idea/libraries/Gradle__org_hamcrest_hamcrest_library_1_3.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 00000000..58918f50 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 00000000..7a4c64a7 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules/Bcore/BlackDex.Bcore.iml b/.idea/modules/Bcore/BlackDex.Bcore.iml new file mode 100644 index 00000000..8c295779 --- /dev/null +++ b/.idea/modules/Bcore/BlackDex.Bcore.iml @@ -0,0 +1,89 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules/Bcore/black-fake/BlackDex.Bcore.black-fake.iml b/.idea/modules/Bcore/black-fake/BlackDex.Bcore.black-fake.iml new file mode 100644 index 00000000..cca49ae2 --- /dev/null +++ b/.idea/modules/Bcore/black-fake/BlackDex.Bcore.black-fake.iml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules/Bcore/black-hook/BlackDex.Bcore.black-hook.iml b/.idea/modules/Bcore/black-hook/BlackDex.Bcore.black-hook.iml new file mode 100644 index 00000000..b9241213 --- /dev/null +++ b/.idea/modules/Bcore/black-hook/BlackDex.Bcore.black-hook.iml @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules/BlackDex.iml b/.idea/modules/BlackDex.iml new file mode 100644 index 00000000..f19be494 --- /dev/null +++ b/.idea/modules/BlackDex.iml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules/app/BlackDex.app.iml b/.idea/modules/app/BlackDex.app.iml new file mode 100644 index 00000000..7e703090 --- /dev/null +++ b/.idea/modules/app/BlackDex.app.iml @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml new file mode 100644 index 00000000..797acea5 --- /dev/null +++ b/.idea/runConfigurations.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/Bcore/.gitignore b/Bcore/.gitignore new file mode 100644 index 00000000..a2c83022 --- /dev/null +++ b/Bcore/.gitignore @@ -0,0 +1,88 @@ +@@ -0,0 +1,84 @@ +# Built application files +*.apk +*.aar +*.ap_ +*.aab + +# Files for the ART/Dalvik VM +*.dex + +# Java class files +*.class + +# Generated files +bin/ +gen/ +out/ +# Uncomment the following line in case you need and you don't have the release build type files in your app +# release/ + +# Gradle files +# Gradle files +.gradle/ +build/ + +# Local configuration file (sdk path, etc) +local.properties + +# Proguard folder generated by Eclipse +proguard/ + +# Log Files +*.log + +# Android Studio Navigation editor temp files +.navigation/ + +# Android Studio captures folder +captures/ + +# IntelliJ +*.iml +.idea/workspace.xml +.idea/tasks.xml +.idea/gradle.xml +.idea/assetWizardSettings.xml +.idea/dictionaries +.idea/libraries +# Android Studio 3 in .gitignore file. +.idea/caches +.idea/ + +# Keystore files +# Uncomment the following lines if you do not want to check your keystore files in. +#*.jks +#*.keystore + +# External native build folder generated in Android Studio 2.2 and later +.externalNativeBuild +.cxx/ + +# Google Services (e.g. APIs or Firebase) +# google-services.json + +# Freeline +freeline.py +freeline/ +freeline_project_description.json + +# fastlane +fastlane/report.xml +fastlane/Preview.html +fastlane/screenshots +fastlane/test_output +fastlane/readme.md + +# Version control +vcs.xml + +# lint +lint/intermediates/ +lint/generated/ +lint/outputs/ +lint/tmp/ +# lint/reports/ + +# mac +*.DS_Store \ No newline at end of file diff --git a/Bcore/black-fake/.gitignore b/Bcore/black-fake/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/Bcore/black-fake/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/Bcore/black-fake/build.gradle b/Bcore/black-fake/build.gradle new file mode 100644 index 00000000..1d2c209c --- /dev/null +++ b/Bcore/black-fake/build.gradle @@ -0,0 +1,60 @@ +plugins { + id 'com.android.library' +} + +android { + compileSdkVersion 30 + buildToolsVersion "30.0.3" + + defaultConfig { + minSdkVersion 21 + targetSdkVersion 30 + versionCode 1 + versionName "1.0" + consumerProguardFiles "consumer-rules.pro" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + dexOptions { + preDexLibraries false + maxProcessCount 8 + javaMaxHeapSize "4g" + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + aaptOptions { + cruncherEnabled = false + useNewCruncher = false + } + testOptions { + unitTests.returnDefaultValues = true + } + lintOptions { + checkReleaseBuilds false + abortOnError false + warningsAsErrors false + disable "UnusedResources", 'RestrictedApi' + textOutput "stdout" + textReport false + check 'NewApi', 'InlinedApi' + } +} + + +tasks.withType(Javadoc) { + options.addStringOption('Xdoclint:none', '-quiet') + options.addStringOption('encoding', 'UTF-8') + options.addStringOption('charSet', 'UTF-8') +} + + +dependencies { + implementation project(':Bcore:black-hook') +} \ No newline at end of file diff --git a/Bcore/black-fake/consumer-rules.pro b/Bcore/black-fake/consumer-rules.pro new file mode 100644 index 00000000..e69de29b diff --git a/Bcore/black-fake/proguard-rules.pro b/Bcore/black-fake/proguard-rules.pro new file mode 100644 index 00000000..481bb434 --- /dev/null +++ b/Bcore/black-fake/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# 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 \ No newline at end of file diff --git a/Bcore/black-fake/src/main/AndroidManifest.xml b/Bcore/black-fake/src/main/AndroidManifest.xml new file mode 100644 index 00000000..15207d51 --- /dev/null +++ b/Bcore/black-fake/src/main/AndroidManifest.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/Bcore/black-fake/src/main/java/android/app/ActivityThread.java b/Bcore/black-fake/src/main/java/android/app/ActivityThread.java new file mode 100644 index 00000000..825fd0b7 --- /dev/null +++ b/Bcore/black-fake/src/main/java/android/app/ActivityThread.java @@ -0,0 +1,86 @@ +package android.app; + +import android.content.Context; +import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.content.pm.ProviderInfo; +import android.os.Handler; +import android.os.IBinder; +import android.util.ArrayMap; + +import java.lang.ref.WeakReference; +import java.util.Map; +import java.util.Objects; + +/** + * Created by Milk on 2021/5/7. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + */ +public class ActivityThread { + public H mH = null; + public AppBindData mBoundApplication; + public Application mInitialApplication; + public Instrumentation mInstrumentation; + public Map> mPackages; + public Map mActivities; + public ArrayMap mProviderMap; + + static class H extends Handler { + } + + public static ActivityThread currentActivityThread() { + throw new RuntimeException(); + } + + public String getProcessName() { + throw new RuntimeException(); + } + + public Handler getHandler() { + throw new RuntimeException(); + } + + public ContentProviderHolder installProvider(Context context, + ContentProviderHolder holder, ProviderInfo info, + boolean noisy, boolean noReleaseNeeded, boolean stable) { + throw new RuntimeException(); + } + + static final class AppBindData { + } + + public static final class ActivityClientRecord { + public Activity activity; + public IBinder token; + public ActivityInfo activityInfo; + public Intent intent; + } + + public static final class ProviderKey { + public final String authority; + public final int userId; + + public ProviderKey(String authority, int userId) { + this.authority = authority; + this.userId = userId; + } + + @Override + public boolean equals(Object o) { + if (o instanceof ProviderKey) { + final ProviderKey other = (ProviderKey) o; + return Objects.equals(authority, other.authority) && userId == other.userId; + } + return false; + } + + @Override + public int hashCode() { + return ((authority != null) ? authority.hashCode() : 0) ^ userId; + } + } +} diff --git a/Bcore/black-fake/src/main/java/android/app/ContentProviderHolder.java b/Bcore/black-fake/src/main/java/android/app/ContentProviderHolder.java new file mode 100644 index 00000000..645cbb70 --- /dev/null +++ b/Bcore/black-fake/src/main/java/android/app/ContentProviderHolder.java @@ -0,0 +1,19 @@ +package android.app; + +import android.content.IContentProvider; +import android.content.pm.ProviderInfo; +import android.os.IBinder; + +/** + * Created by Milk on 2021/5/7. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + */ +public class ContentProviderHolder { + public final ProviderInfo info = null; + public IContentProvider provider; + public IBinder connection; +} diff --git a/Bcore/black-fake/src/main/java/android/content/IContentProvider.java b/Bcore/black-fake/src/main/java/android/content/IContentProvider.java new file mode 100644 index 00000000..ad5b2132 --- /dev/null +++ b/Bcore/black-fake/src/main/java/android/content/IContentProvider.java @@ -0,0 +1,14 @@ +package android.content; + +import android.os.IInterface; + +/** + * Created by Milk on 2021/5/7. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + */ +public interface IContentProvider extends IInterface { +} diff --git a/Bcore/black-fake/src/main/java/android/content/SyncInfo.java b/Bcore/black-fake/src/main/java/android/content/SyncInfo.java new file mode 100644 index 00000000..c1ba2c83 --- /dev/null +++ b/Bcore/black-fake/src/main/java/android/content/SyncInfo.java @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.content; + +import android.accounts.Account; +import android.os.Parcel; +import android.os.Parcelable; + +/** + * Information about the sync operation that is currently underway. + */ +public class SyncInfo implements Parcelable { + /** + * Used when the caller receiving this object doesn't have permission to access the accounts + * on device. + * @See Manifest.permission.GET_ACCOUNTS + */ + private static final Account REDACTED_ACCOUNT = new Account("*****", "*****"); + + /** @hide */ + public final int authorityId; + + /** + * The {@link Account} that is currently being synced. + */ + public final Account account; + + /** + * The authority of the provider that is currently being synced. + */ + public final String authority; + + /** + * The start time of the current sync operation in milliseconds since boot. + * This is represented in elapsed real time. + * See {@link android.os.SystemClock#elapsedRealtime()}. + */ + public final long startTime; + + /** + * Creates a SyncInfo object with an unusable Account. Used when the caller receiving this + * object doesn't have access to the accounts on the device. + * @See Manifest.permission.GET_ACCOUNTS + * @hide + */ + public static SyncInfo createAccountRedacted( + int authorityId, String authority, long startTime) { + throw new RuntimeException("Stub!"); + } + + /** @hide */ + public SyncInfo(int authorityId, Account account, String authority, long startTime) { + throw new RuntimeException("Stub!"); + } + + /** @hide */ + public SyncInfo(SyncInfo other) { + throw new RuntimeException("Stub!"); + } + + /** @hide */ + public int describeContents() { + return 0; + } + + /** @hide */ + public void writeToParcel(Parcel parcel, int flags) { + parcel.writeInt(authorityId); + parcel.writeParcelable(account, flags); + parcel.writeString(authority); + parcel.writeLong(startTime); + } + + SyncInfo(Parcel parcel) { + throw new RuntimeException("Stub!"); + } + + public static final Creator CREATOR = new Creator() { + public SyncInfo createFromParcel(Parcel in) { + return new SyncInfo(in); + } + + public SyncInfo[] newArray(int size) { + return new SyncInfo[size]; + } + }; +} diff --git a/Bcore/black-fake/src/main/java/android/content/SyncStatusInfo.java b/Bcore/black-fake/src/main/java/android/content/SyncStatusInfo.java new file mode 100644 index 00000000..20c00afb --- /dev/null +++ b/Bcore/black-fake/src/main/java/android/content/SyncStatusInfo.java @@ -0,0 +1,174 @@ +package android.content; + +import android.os.Parcel; +import android.os.Parcelable; +import android.util.Log; + +import java.util.ArrayList; + +public class SyncStatusInfo implements Parcelable { + static final int VERSION = 2; + + public final int authorityId; + public long totalElapsedTime; + public int numSyncs; + public int numSourcePoll; + public int numSourceServer; + public int numSourceLocal; + public int numSourceUser; + public int numSourcePeriodic; + public long lastSuccessTime; + public int lastSuccessSource; + public long lastFailureTime; + public int lastFailureSource; + public String lastFailureMesg; + public long initialFailureTime; + public boolean pending; + public boolean initialize; + + // Warning: It is up to the external caller to ensure there are + // no race conditions when accessing this list + private ArrayList periodicSyncTimes; + + private static final String TAG = "Sync"; + + public SyncStatusInfo(int authorityId) { + this.authorityId = authorityId; + } + + public int getLastFailureMesgAsInt(int def) { + return 0; + } + + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel parcel, int flags) { + parcel.writeInt(VERSION); + parcel.writeInt(authorityId); + parcel.writeLong(totalElapsedTime); + parcel.writeInt(numSyncs); + parcel.writeInt(numSourcePoll); + parcel.writeInt(numSourceServer); + parcel.writeInt(numSourceLocal); + parcel.writeInt(numSourceUser); + parcel.writeLong(lastSuccessTime); + parcel.writeInt(lastSuccessSource); + parcel.writeLong(lastFailureTime); + parcel.writeInt(lastFailureSource); + parcel.writeString(lastFailureMesg); + parcel.writeLong(initialFailureTime); + parcel.writeInt(pending ? 1 : 0); + parcel.writeInt(initialize ? 1 : 0); + if (periodicSyncTimes != null) { + parcel.writeInt(periodicSyncTimes.size()); + for (long periodicSyncTime : periodicSyncTimes) { + parcel.writeLong(periodicSyncTime); + } + } else { + parcel.writeInt(-1); + } + } + + public SyncStatusInfo(Parcel parcel) { + int version = parcel.readInt(); + if (version != VERSION && version != 1) { + Log.w("SyncStatusInfo", "Unknown version: " + version); + } + authorityId = parcel.readInt(); + totalElapsedTime = parcel.readLong(); + numSyncs = parcel.readInt(); + numSourcePoll = parcel.readInt(); + numSourceServer = parcel.readInt(); + numSourceLocal = parcel.readInt(); + numSourceUser = parcel.readInt(); + lastSuccessTime = parcel.readLong(); + lastSuccessSource = parcel.readInt(); + lastFailureTime = parcel.readLong(); + lastFailureSource = parcel.readInt(); + lastFailureMesg = parcel.readString(); + initialFailureTime = parcel.readLong(); + pending = parcel.readInt() != 0; + initialize = parcel.readInt() != 0; + if (version == 1) { + periodicSyncTimes = null; + } else { + int N = parcel.readInt(); + if (N < 0) { + periodicSyncTimes = null; + } else { + periodicSyncTimes = new ArrayList(); + for (int i=0; i(other.periodicSyncTimes); + } + } + + public void setPeriodicSyncTime(int index, long when) { + // The list is initialized lazily when scheduling occurs so we need to make sure + // we initialize elements < index to zero (zero is ignore for scheduling purposes) + ensurePeriodicSyncTimeSize(index); + periodicSyncTimes.set(index, when); + } + + public long getPeriodicSyncTime(int index) { + if (periodicSyncTimes != null && index < periodicSyncTimes.size()) { + return periodicSyncTimes.get(index); + } else { + return 0; + } + } + + public void removePeriodicSyncTime(int index) { + if (periodicSyncTimes != null && index < periodicSyncTimes.size()) { + periodicSyncTimes.remove(index); + } + } + + public static final Creator CREATOR = new Creator() { + public SyncStatusInfo createFromParcel(Parcel in) { + return new SyncStatusInfo(in); + } + + public SyncStatusInfo[] newArray(int size) { + return new SyncStatusInfo[size]; + } + }; + + private void ensurePeriodicSyncTimeSize(int index) { + if (periodicSyncTimes == null) { + periodicSyncTimes = new ArrayList<>(0); + } + + final int requiredSize = index + 1; + if (periodicSyncTimes.size() < requiredSize) { + for (int i = periodicSyncTimes.size(); i < requiredSize; i++) { + periodicSyncTimes.add((long) 0); + } + } + } +} \ No newline at end of file diff --git a/Bcore/black-fake/src/main/java/android/content/pm/ManifestDigest.java b/Bcore/black-fake/src/main/java/android/content/pm/ManifestDigest.java new file mode 100644 index 00000000..46150c21 --- /dev/null +++ b/Bcore/black-fake/src/main/java/android/content/pm/ManifestDigest.java @@ -0,0 +1,65 @@ +package android.content.pm; + +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.jar.Attributes; + +/** + * Created by Milk on 2021/5/7. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + */ +public class ManifestDigest implements Parcelable { + + ManifestDigest(final byte[] digest) { + throw new RuntimeException("Stub!"); + } + + private ManifestDigest(final Parcel source) { + throw new RuntimeException("Stub!"); + } + + static ManifestDigest fromAttributes(final Attributes attributes) { + throw new RuntimeException("Stub!"); + } + + @Override + public int describeContents() { + throw new RuntimeException("Stub!"); + } + + @Override + public boolean equals(Object o) { + throw new RuntimeException("Stub!"); + } + + @Override + public int hashCode() { + throw new RuntimeException("Stub!"); + } + + @Override + public String toString() { + throw new RuntimeException("Stub!"); + } + + @Override + public void writeToParcel(final Parcel dest, final int flags) { + throw new RuntimeException("Stub!"); + } + + public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { + public ManifestDigest createFromParcel(Parcel source) { + return new ManifestDigest(source); + } + + public ManifestDigest[] newArray(int size) { + return new ManifestDigest[size]; + } + }; + +} \ No newline at end of file diff --git a/Bcore/black-fake/src/main/java/android/content/pm/PackageParser.java b/Bcore/black-fake/src/main/java/android/content/pm/PackageParser.java new file mode 100644 index 00000000..9063faad --- /dev/null +++ b/Bcore/black-fake/src/main/java/android/content/pm/PackageParser.java @@ -0,0 +1,703 @@ +package android.content.pm; + +import android.annotation.SuppressLint; +import android.content.ComponentName; +import android.content.IntentFilter; +import android.content.res.TypedArray; +import android.os.Bundle; +import android.util.ArrayMap; +import android.util.ArraySet; +import android.util.DisplayMetrics; + +import java.io.File; +import java.io.PrintWriter; +import java.security.PublicKey; +import java.security.cert.Certificate; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +public class PackageParser { + + public final static int PARSE_IS_SYSTEM = 1 << 0; + public final static int PARSE_CHATTY = 1 << 1; + public final static int PARSE_MUST_BE_APK = 1 << 2; + public final static int PARSE_IGNORE_PROCESSES = 1 << 3; + public final static int PARSE_FORWARD_LOCK = 1 << 4; + public final static int PARSE_EXTERNAL_STORAGE = 1 << 5; + public final static int PARSE_IS_SYSTEM_DIR = 1 << 6; + public final static int PARSE_IS_PRIVILEGED = 1 << 7; + public final static int PARSE_COLLECT_CERTIFICATES = 1 << 8; + public final static int PARSE_TRUSTED_OVERLAY = 1 << 9; + + public static class NewPermissionInfo { + public final String name; + public final int sdkVersion; + public final int fileVersion; + + public NewPermissionInfo(String name, int sdkVersion, int fileVersion) { + throw new RuntimeException("Stub!"); + } + } + + public static class SplitPermissionInfo { + public final String rootPerm; + public final String[] newPerms; + public final int targetSdk; + + public SplitPermissionInfo(String rootPerm, String[] newPerms, int targetSdk) { + throw new RuntimeException("Stub!"); + } + } + + public static final PackageParser.NewPermissionInfo NEW_PERMISSIONS[] = new PackageParser.NewPermissionInfo[]{ + new PackageParser.NewPermissionInfo(android.Manifest.permission.WRITE_EXTERNAL_STORAGE, android.os.Build.VERSION_CODES.DONUT, 0), + new PackageParser.NewPermissionInfo(android.Manifest.permission.READ_PHONE_STATE, android.os.Build.VERSION_CODES.DONUT, 0) + }; + + static class ParsePackageItemArgs { + final Package owner; + final String[] outError; + final int nameRes; + final int labelRes; + final int iconRes; + final int logoRes; + final int bannerRes; + + String tag; + TypedArray sa; + + ParsePackageItemArgs(final Package owner, final String[] outError, final int nameRes, final int labelRes, final int iconRes, final int logoRes, final int bannerRes) { throw new RuntimeException("Stub!"); + } + } + + static class ParseComponentArgs extends ParsePackageItemArgs { + final String[] sepProcesses; + final int processRes; + final int descriptionRes; + final int enabledRes; + int flags; + + ParseComponentArgs(final Package owner, final String[] outError, final int nameRes, final int labelRes, final int iconRes, final int logoRes, final int bannerRes, final String[] sepProcesses, final int processRes, final int descriptionRes, final int enabledRes) { + super(owner, outError, nameRes, labelRes, iconRes, logoRes, bannerRes); + throw new RuntimeException("Stub!"); + } + } + + public static class PackageLite { + public final String packageName; + public final int versionCode; + public final int installLocation; + public final VerifierInfo[] verifiers; + + /** Names of any split APKs, ordered by parsed splitName */ + public final String[] splitNames; + + /** + * Path where this package was found on disk. For monolithic packages + * this is path to single base APK file; for cluster packages this is + * path to the cluster directory. + */ + public final String codePath; + + /** Path of base APK */ + public final String baseCodePath; + /** Paths of any split APKs, ordered by parsed splitName */ + public final String[] splitCodePaths; + + /** Revision code of base APK */ + public final int baseRevisionCode; + /** Revision codes of any split APKs, ordered by parsed splitName */ + public final int[] splitRevisionCodes; + + public final boolean coreApp; + public final boolean multiArch; + public final boolean extractNativeLibs; + + public PackageLite(final String codePath, final ApkLite baseApk, final String[] splitNames, final String[] splitCodePaths, final int[] splitRevisionCodes) { + throw new RuntimeException("Stub!"); + } + + public List getAllCodePaths() { + throw new RuntimeException("Stub!"); + } + } + + public static class ApkLite { + public final String codePath; + public final String packageName; + public final String splitName; + public final int versionCode; + public final int revisionCode; + public final int installLocation; + public final VerifierInfo[] verifiers; + public final Signature[] signatures; + public final boolean coreApp; + public final boolean multiArch; + public final boolean extractNativeLibs; + + public ApkLite(final String codePath, final String packageName, final String splitName, final int versionCode, final int revisionCode, final int installLocation, final List verifiers, final Signature[] signatures, final boolean coreApp, final boolean multiArch, final boolean extractNativeLibs) { + throw new RuntimeException("Stub!"); + } + } + + /** + * For Android 5.0+ + */ + public PackageParser() { + throw new RuntimeException("Stub!"); + } + + public PackageParser(final String archiveSourcePath) { + throw new RuntimeException("Stub!"); + } + + public void setSeparateProcesses(final String[] procs) { + throw new RuntimeException("Stub!"); + } + + public void setOnlyCoreApps(final boolean onlyCoreApps) { + throw new RuntimeException("Stub!"); + } + + public void setDisplayMetrics(final DisplayMetrics metrics) { + throw new RuntimeException("Stub!"); + } + + public static final boolean isApkFile(final File file) { + throw new RuntimeException("Stub!"); + } + + public static PackageInfo generatePackageInfo(final PackageParser.Package p, final int gids[], final int flags, final long firstInstallTime, final long lastUpdateTime, final Set grantedPermissions, final PackageUserState state) { + throw new RuntimeException("Stub!"); + } + + public static boolean isAvailable(final PackageUserState state) { + throw new RuntimeException("Stub!"); + } + + public static PackageInfo generatePackageInfo(final PackageParser.Package p, final int gids[], final int flags, final long firstInstallTime, final long lastUpdateTime, final Set grantedPermissions, final PackageUserState state, final int userId) { + throw new RuntimeException("Stub!"); + } + + /** + * Parse only lightweight details about the package at the given location. + * Automatically detects if the package is a monolithic style (single APK + * file) or cluster style (directory of APKs). + *

+ * This performs sanity checking on cluster style packages, such as + * requiring identical package name and version codes, a single base APK, + * and unique split names. + * + * @see PackageParser#parsePackage(File, int) + */ + public static PackageLite parsePackageLite(final File packageFile, final int flags) throws PackageParserException { + throw new RuntimeException("Stub!"); + } + + /** + * Parse the package at the given location. Automatically detects if the + * package is a monolithic style (single APK file) or cluster style + * (directory of APKs). + *

+ * This performs sanity checking on cluster style packages, such as + * requiring identical package name and version codes, a single base APK, + * and unique split names. + *

+ * Note that this does not perform signature verification; that + * must be done separately in {@link #collectCertificates(Package, int)}. + * + * @see #parsePackageLite(File, int) + * @since Android 5.0+ + */ + public Package parsePackage(final File packageFile, final int flags) throws PackageParserException { + throw new RuntimeException("Stub!"); + } + + /** + * + * @param sourceFile + * @param destCodePath + * @param metrics + * @param flags + * @return + * @since Android 2.3+ + */ + public Package parsePackage(final File sourceFile, final String destCodePath, final DisplayMetrics metrics, final int flags) { + throw new RuntimeException("Stub!"); + } + + public void collectManifestDigest(final Package pkg) throws PackageParserException { + throw new RuntimeException("Stub!"); + } + + public void collectCertificates(final Package pkg, final int flags) throws PackageParserException { + throw new RuntimeException("Stub!"); + } + + /** + * Utility method that retrieves lightweight details about a single APK + * file, including package name, split name, and install location. + * + * @param apkFile path to a single APK + * @param flags optional parse flags, such as + * {@link #PARSE_COLLECT_CERTIFICATES} + */ + public static ApkLite parseApkLite(final File apkFile, final int flags) throws PackageParserException { + throw new RuntimeException("Stub!"); + } + + /** + * Representation of a full package parsed from APK files on disk. A package + * consists of a single base APK, and zero or more split APKs. + */ + public final static class Package { + + public String packageName; + + /** Names of any split APKs, ordered by parsed splitName */ + public String[] splitNames; + + // TODO: work towards making these paths invariant + + public String volumeUuid; + + /** + * Path where this package was found on disk. For monolithic packages + * this is path to single base APK file; for cluster packages this is + * path to the cluster directory. + */ + public String codePath; + + /** Path of base APK */ + public String baseCodePath; + /** Paths of any split APKs, ordered by parsed splitName */ + public String[] splitCodePaths; + + /** Revision code of base APK */ + public int baseRevisionCode; + /** Revision codes of any split APKs, ordered by parsed splitName */ + public int[] splitRevisionCodes; + + /** Flags of any split APKs; ordered by parsed splitName */ + public int[] splitFlags; + + /** + * Private flags of any split APKs; ordered by parsed splitName. + * + * {@hide} + */ + public int[] splitPrivateFlags; + + public boolean baseHardwareAccelerated; + + // For now we only support one application per package. + public final ApplicationInfo applicationInfo = new ApplicationInfo(); + + public final ArrayList permissions = new ArrayList(0); + public final ArrayList permissionGroups = new ArrayList(0); + public final ArrayList activities = new ArrayList(0); + public final ArrayList receivers = new ArrayList(0); + public final ArrayList providers = new ArrayList(0); + public final ArrayList services = new ArrayList(0); + public final ArrayList instrumentation = new ArrayList(0); + + public final ArrayList requestedPermissions = new ArrayList(); + + public ArrayList protectedBroadcasts; + + public ArrayList libraryNames = null; + public ArrayList usesLibraries = null; + public ArrayList usesOptionalLibraries = null; + public String[] usesLibraryFiles = null; + + public ArrayList preferredActivityFilters = null; + + public ArrayList mOriginalPackages = null; + public String mRealPackage = null; + public ArrayList mAdoptPermissions = null; + + // We store the application meta-data independently to avoid multiple unwanted references + public Bundle mAppMetaData = null; + + // The version code declared for this package. + public int mVersionCode; + + // The version name declared for this package. + public String mVersionName; + + // The shared user id that this package wants to use. + public String mSharedUserId; + + // The shared user label that this package wants to use. + public int mSharedUserLabel; + + // Signatures that were read from the package. + public Signature[] mSignatures; + public SigningDetails mSigningDetails; + public Certificate[][] mCertificates; + + // For use by package manager service for quick lookup of + // preferred up order. + public int mPreferredOrder = 0; + + // For use by package manager to keep track of where it needs to do dexopt. +// public final ArraySet mDexOptPerformed = new ArraySet<>(4); + + // For use by package manager to keep track of when a package was last used. + public long mLastPackageUsageTimeInMills; + + // // User set enabled state. + // public int mSetEnabled = PackageManager.COMPONENT_ENABLED_STATE_DEFAULT; + // + // // Whether the package has been stopped. + // public boolean mSetStopped = false; + + // Additional data supplied by callers. + public Object mExtras; + + // Applications hardware preferences + public ArrayList configPreferences = null; + + // Applications requested features + public ArrayList reqFeatures = null; + + // Applications requested feature groups + public ArrayList featureGroups = null; + + public int installLocation; + + public boolean coreApp; + + /* An app that's required for all users and cannot be uninstalled for a user */ + public boolean mRequiredForAllUsers; + + /* The restricted account authenticator type that is used by this application */ + public String mRestrictedAccountType; + + /* The required account type without which this application will not function */ + public String mRequiredAccountType; + + /** + * Digest suitable for comparing whether this package's manifest is the + * same as another. + */ + public ManifestDigest manifestDigest; + + public String mOverlayTarget; + public int mOverlayPriority; + public boolean mTrustedOverlay; + + /** + * Data used to feed the KeySetManagerService + */ + public ArraySet mSigningKeys; + public ArraySet mUpgradeKeySets; + public ArrayMap> mKeySetMapping; + + /** + * The install time abi override for this package, if any. + * + * TODO: This seems like a horrible place to put the abiOverride because + * this isn't something the packageParser parsers. However, this fits in with + * the rest of the PackageManager where package scanning randomly pushes + * and prods fields out of {@code this.applicationInfo}. + */ + public String cpuAbiOverride; + + public Package(String packageName) { + throw new RuntimeException("Stub!"); + } + + public List getAllCodePaths() { + throw new RuntimeException("Stub!"); + } + + /** + * Filtered set of {@link #getAllCodePaths()} that excludes + * resource-only APKs. + */ + public List getAllCodePathsExcludingResourceOnly() { + throw new RuntimeException("Stub!"); + } + + public void setPackageName(final String newName) { + throw new RuntimeException("Stub!"); + } + + public boolean hasComponentClassName(final String name) { + throw new RuntimeException("Stub!"); + } + + public boolean isForwardLocked() { + throw new RuntimeException("Stub!"); + } + + public boolean isSystemApp() { + throw new RuntimeException("Stub!"); + } + + public boolean isPrivilegedApp() { + throw new RuntimeException("Stub!"); + } + + public boolean isUpdatedSystemApp() { + throw new RuntimeException("Stub!"); + } + + public boolean canHaveOatDir() { + throw new RuntimeException("Stub!"); + } + + @Override + public String toString() { + throw new RuntimeException("Stub!"); + } + } + + public static class Component { + public final Package owner; + public final ArrayList intents; + public final String className; + public Bundle metaData; + + ComponentName componentName; + String componentShortName; + + public Component(final Package owner) { + throw new RuntimeException("Stub!"); + } + + public Component(final ParsePackageItemArgs args, final PackageItemInfo outInfo) { + throw new RuntimeException("Stub!"); + } + + public Component(final ParseComponentArgs args, final ComponentInfo outInfo) { + throw new RuntimeException("Stub!"); + } + + public Component(final Component clone) { + throw new RuntimeException("Stub!"); + } + + public ComponentName getComponentName() { + throw new RuntimeException("Stub!"); + } + + public void appendComponentShortName(final StringBuilder sb) { + throw new RuntimeException("Stub!"); + } + + public void printComponentShortName(final PrintWriter pw) { + throw new RuntimeException("Stub!"); + } + + public void setPackageName(final String packageName) { + throw new RuntimeException("Stub!"); + } + } + + public final static class Permission extends Component { + public final PermissionInfo info; + public boolean tree; + public PermissionGroup group; + + public Permission(final Package owner) { + super(owner); + throw new RuntimeException("Stub!"); + } + + public Permission(final Package owner, final PermissionInfo info) { + super(owner); + throw new RuntimeException("Stub!"); + } + + @Override + public void setPackageName(final String packageName) { + throw new RuntimeException("Stub!"); + } + + @Override + public String toString() { + throw new RuntimeException("Stub!"); + } + } + + public final static class PermissionGroup extends Component { + public final PermissionGroupInfo info; + + public PermissionGroup(final Package owner) { + super(owner); + throw new RuntimeException("Stub!"); + } + + public PermissionGroup(final Package owner, final PermissionGroupInfo info) { + super(owner); + throw new RuntimeException("Stub!"); + } + + @Override + public void setPackageName(final String packageName) { + throw new RuntimeException("Stub!"); + } + + @Override + public String toString() { + throw new RuntimeException("Stub!"); + } + } + + public final static class Activity extends Component { + public final ActivityInfo info; + + public Activity(final ParseComponentArgs args, final ActivityInfo info) { + super(args, info); + throw new RuntimeException("Stub!"); + } + + @Override + public void setPackageName(final String packageName) { + throw new RuntimeException("Stub!"); + } + + @Override + public String toString() { + throw new RuntimeException("Stub!"); + } + } + + public final static class Service extends Component { + public final ServiceInfo info; + + public Service(final ParseComponentArgs args, final ServiceInfo info) { + super(args, info); + throw new RuntimeException("Stub!"); + } + + @Override + public void setPackageName(final String packageName) { + throw new RuntimeException("Stub!"); + } + + @Override + public String toString() { + throw new RuntimeException("Stub!"); + } + } + + public final static class Provider extends Component { + public final ProviderInfo info; + public boolean syncable; + + public Provider(final ParseComponentArgs args, final ProviderInfo info) { + super(args, info); + throw new RuntimeException("Stub!"); + } + + public Provider(final Provider existingProvider) { + super(existingProvider); + throw new RuntimeException("Stub!"); + } + + @Override + public void setPackageName(final String packageName) { + throw new RuntimeException("Stub!"); + } + + @Override + public String toString() { + throw new RuntimeException("Stub!"); + } + } + + public final static class Instrumentation extends Component { + public final InstrumentationInfo info; + + public Instrumentation(final ParsePackageItemArgs args, final InstrumentationInfo info) { + super(args, info); + throw new RuntimeException("Stub!"); + } + + @Override + public void setPackageName(final String packageName) { + throw new RuntimeException("Stub!"); + } + + @Override + public String toString() { + throw new RuntimeException("Stub!"); + } + } + + @SuppressLint("ParcelCreator") + public static class IntentInfo extends IntentFilter { + public boolean hasDefault; + public int labelRes; + public CharSequence nonLocalizedLabel; + public int icon; + public int logo; + public int banner; + public int preferred; + } + + @SuppressLint("ParcelCreator") + public final static class ActivityIntentInfo extends IntentInfo { + public final Activity activity; + + public ActivityIntentInfo(final Activity activity) { + throw new RuntimeException("Stub!"); + } + + @Override + public String toString() { + throw new RuntimeException("Stub!"); + } + } + + @SuppressLint("ParcelCreator") + public final static class ServiceIntentInfo extends IntentInfo { + public final Service service; + + public ServiceIntentInfo(final Service service) { + throw new RuntimeException("Stub!"); + } + + @Override + public String toString() { + throw new RuntimeException("Stub!"); + } + } + + @SuppressLint("ParcelCreator") + public static final class ProviderIntentInfo extends IntentInfo { + public final Provider provider; + + public ProviderIntentInfo(final Provider provider) { + throw new RuntimeException("Stub!"); + } + + @Override + public String toString() { + throw new RuntimeException("Stub!"); + } + } + + public static class PackageParserException extends Exception { + + public PackageParserException(int error, String detailMessage) { + super(detailMessage); + throw new RuntimeException("Stub!"); + } + + public PackageParserException(int error, String detailMessage, Throwable throwable) { + super(detailMessage, throwable); + throw new RuntimeException("Stub!"); + } + + } + + public static class SigningDetails { + public static final SigningDetails UNKNOWN = null; + public Signature[] signatures; + } +} \ No newline at end of file diff --git a/Bcore/black-fake/src/main/java/android/content/pm/PackageUserState.java b/Bcore/black-fake/src/main/java/android/content/pm/PackageUserState.java new file mode 100644 index 00000000..6dfaf259 --- /dev/null +++ b/Bcore/black-fake/src/main/java/android/content/pm/PackageUserState.java @@ -0,0 +1,38 @@ +package android.content.pm; + +import android.util.ArraySet; + +/** + * Created by Milk on 2021/5/7. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + */ +public class PackageUserState { + + public boolean stopped; + public boolean notLaunched; + public boolean installed; + public boolean hidden; // Is the app restricted by owner / admin + public int enabled; + public boolean blockUninstall; + + public String lastDisableAppCaller; + + public ArraySet disabledComponents; + public ArraySet enabledComponents; + + public int domainVerificationStatus; + public int appLinkGeneration; + + public PackageUserState() { + throw new RuntimeException("Stub!"); + } + + public PackageUserState(final PackageUserState o) { + throw new RuntimeException("Stub!"); + } + +} diff --git a/Bcore/black-fake/src/main/java/android/content/pm/VerifierInfo.java b/Bcore/black-fake/src/main/java/android/content/pm/VerifierInfo.java new file mode 100644 index 00000000..a8bfabc3 --- /dev/null +++ b/Bcore/black-fake/src/main/java/android/content/pm/VerifierInfo.java @@ -0,0 +1,45 @@ +package android.content.pm; + +import android.os.Parcel; +import android.os.Parcelable; + +import java.security.PublicKey; + +/** + * Created by Milk on 2021/5/7. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + */ +public class VerifierInfo implements Parcelable { + + public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { + public VerifierInfo createFromParcel(final Parcel source) { + return new VerifierInfo(source); + } + + public VerifierInfo[] newArray(final int size) { + return new VerifierInfo[size]; + } + }; + + public VerifierInfo(final String packageName, final PublicKey publicKey) { + throw new RuntimeException("Stub!"); + } + + private VerifierInfo(final Parcel source) { + throw new RuntimeException("Stub!"); + } + + @Override + public int describeContents() { + throw new RuntimeException("Stub!"); + } + + @Override + public void writeToParcel(final Parcel dest, final int flags) { + throw new RuntimeException("Stub!"); + } +} \ No newline at end of file diff --git a/Bcore/black-fake/src/main/java/android/location/LocationRequest.java b/Bcore/black-fake/src/main/java/android/location/LocationRequest.java new file mode 100644 index 00000000..6737f2f4 --- /dev/null +++ b/Bcore/black-fake/src/main/java/android/location/LocationRequest.java @@ -0,0 +1,33 @@ +package android.location; + +import android.os.Parcel; +import android.os.Parcelable; + +public final class LocationRequest implements Parcelable { + + public String getProvider() { + return null; + } + + + public static final Creator CREATOR = new Creator() { + @Override + public LocationRequest createFromParcel(Parcel in) { + return null; + } + + @Override + public LocationRequest[] newArray(int size) { + return null; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + } +} \ No newline at end of file diff --git a/Bcore/black-fake/src/main/java/android/os/ParcelableException.java b/Bcore/black-fake/src/main/java/android/os/ParcelableException.java new file mode 100644 index 00000000..52830cb4 --- /dev/null +++ b/Bcore/black-fake/src/main/java/android/os/ParcelableException.java @@ -0,0 +1,60 @@ +package android.os; + +import android.annotation.TargetApi; + +import java.io.IOException; + +/** + * Wrapper class that offers to transport typical {@link Throwable} across a + * {@link Binder} call. This class is typically used to transport exceptions + * that cannot be modified to add {@link Parcelable} behavior, such as + * {@link IOException}. + *

    + *
  • The wrapped throwable must be defined as system class (that is, it must + * be in the same {@link ClassLoader} as {@link Parcelable}). + *
  • The wrapped throwable must support the + * {@link Throwable#Throwable(String)} constructor. + *
  • The receiver side must catch any thrown {@link ParcelableException} and + * call {@link #maybeRethrow(Class)} for all expected exception types. + *
+ */ +@TargetApi(Build.VERSION_CODES.O) +public final class ParcelableException extends RuntimeException implements Parcelable { + public ParcelableException(Throwable t) { + super(t); + } + + public void maybeRethrow(Class clazz) throws T { + throw new RuntimeException("Stub!"); + } + + public static Throwable readFromParcel(Parcel in) { + throw new RuntimeException("Stub!"); + } + + public static void writeToParcel(Parcel out, Throwable t) { + throw new RuntimeException("Stub!"); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + throw new RuntimeException("Stub!"); + } + + public static final Creator CREATOR = new Creator() { + @Override + public ParcelableException createFromParcel(Parcel source) { + return new ParcelableException(readFromParcel(source)); + } + + @Override + public ParcelableException[] newArray(int size) { + return new ParcelableException[size]; + } + }; +} diff --git a/Bcore/black-fake/src/main/java/mirror/MirrorReflection.java b/Bcore/black-fake/src/main/java/mirror/MirrorReflection.java new file mode 100644 index 00000000..8dbc69a2 --- /dev/null +++ b/Bcore/black-fake/src/main/java/mirror/MirrorReflection.java @@ -0,0 +1,398 @@ +package mirror; + +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Member; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; + +/** + * Created by canyie on 2019/10/24. + */ +@SuppressWarnings({"unchecked", "unused", "WeakerAccess"}) +public final class MirrorReflection { + + private Class clazz; + + private MirrorReflection( Class clazz) { + this.clazz = clazz; + } + + + public Class getClazz() { + return clazz; + } + + public static MirrorReflection on( String name) { + return new MirrorReflection(findClass(name)); + } + + public static MirrorReflection on( String name, ClassLoader loader) { + return new MirrorReflection(findClass(name, loader)); + } + + public static MirrorReflection on( Class clazz) { + return new MirrorReflection(clazz); + } + + public static MethodWrapper wrap( Method method) { + return new MethodWrapper(method); + } + + public static StaticMethodWrapper wrapStatic( Method method) { + return new StaticMethodWrapper(method); + } + + public MethodWrapper method( String name, Class... parameterTypes) { + return method(clazz, name, parameterTypes); + } + + public static MethodWrapper method( String className, String name, Class... parameterTypes) { + return method(findClass(className), name, parameterTypes); + } + + public static MethodWrapper method( Class clazz, String name, Class... parameterTypes) { + Method method = getMethod(clazz, name, parameterTypes); + if ((parameterTypes == null || parameterTypes.length == 0) && method == null) { + method = findMethodNoChecks(clazz, name); + } + return wrap(method); + } + + + public StaticMethodWrapper staticMethod( String name, Class... parameterTypes) { + return staticMethod(clazz, name, parameterTypes); + } + + public static StaticMethodWrapper staticMethod( String className, String name, Class... parameterTypes) { + return staticMethod(findClass(className), name, parameterTypes); + } + + public static StaticMethodWrapper staticMethod( Class clazz, String name, Class... parameterTypes) { + Method method = getMethod(clazz, name, parameterTypes); + if ((parameterTypes == null || parameterTypes.length == 0) && method == null) { + method = findMethodNoChecks(clazz, name); + } + return wrapStatic(method); + } + + public static FieldWrapper wrap( Field field) { + return new FieldWrapper<>(field); + } + + public FieldWrapper field( String name) { + return field(clazz, name); + } + + public static FieldWrapper field( String className, String name) { + return field(findClass(className), name); + } + + public static FieldWrapper field( Class clazz, String name) { + return wrap(getField(clazz, name)); + } + + public static ConstructorWrapper wrap( Constructor constructor) { + return new ConstructorWrapper<>(constructor); + } + + public ConstructorWrapper constructor( Class... parameterTypes) { + return wrap(getConstructor(clazz, parameterTypes)); + } + + + + public static Class findClassOrNull( String name) { + try { + return Class.forName(name); + } catch (ClassNotFoundException ignored) { + return null; + } + } + + + public static Class findClassOrNull( String name, ClassLoader loader) { + try { + return Class.forName(name, true, loader); + } catch (ClassNotFoundException ignored) { + return null; + } + } + + + public static Class findClass( String name) { + try { + return Class.forName(name); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } + return null; + } + + + public static Class findClass( String name, ClassLoader loader) { + try { + return Class.forName(name, true, loader); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } + return null; + } + + public static Method getMethod( Class clazz, String name, Class... parameterTypes) { + return findMethod(clazz, name, parameterTypes); + } + + public static Method getMethod( Class clazz, String name) { + return findMethod(clazz, name); + } + + private static String getParameterTypesMessage( Class[] parameterTypes) { + if (parameterTypes == null || parameterTypes.length == 0) { + return "()"; + } + StringBuilder sb = new StringBuilder("("); + boolean isFirst = true; + for (Class type : parameterTypes) { + if (isFirst) { + isFirst = false; + } else { + sb.append(","); + } + sb.append(type.getName()); + } + return sb.append(')').toString(); + } + + + public static Method findMethod( Class clazz, String name, Class... parameterTypes) { + checkForFindMethod(clazz, name, parameterTypes); + return findMethodNoChecks(clazz, name, parameterTypes); + } + + + public static Method findMethodNoChecks(Class clazz, String name, Class... parameterTypes) { + while (clazz != null) { + try { + Method method = clazz.getDeclaredMethod(name, parameterTypes); + method.setAccessible(true); + return method; + } catch (NoSuchMethodException ignored) { + } + clazz = clazz.getSuperclass(); + } + return null; + } + + + public static Method findMethodNoChecks(Class clazz, String name) { + try { + Method[] methods = clazz.getDeclaredMethods(); + for (Method method : methods) { + if (method.getName().equals(name)) { + method.setAccessible(true); + return method; + } + } + } catch (Throwable ignored) { + } + return null; + } + + private static void checkForFindMethod(Class clazz, String name, Class... parameterTypes) { + if (parameterTypes != null) { + for (int i = 0; i < parameterTypes.length; i++) { + if (parameterTypes[i] == null) { + throw new NullPointerException("parameterTypes[" + i + "] == null"); + } + } + } + + } + + + public static Field getField( Class clazz, String name) { + return findField(clazz, name); + } + + + public static Field findField(Class clazz, String name) { + return findFieldNoChecks(clazz, name); + } + + + public static Field findFieldNoChecks( Class clazz, String name) { + while (clazz != null) { + try { + Field field = clazz.getDeclaredField(name); + field.setAccessible(true); + return field; + } catch (NoSuchFieldException ignored) { + } + clazz = clazz.getSuperclass(); + } + return null; + } + + public static Constructor getConstructor( Class clazz, Class... parameterTypes) { + return findConstructor(clazz, parameterTypes); + } + + public static Constructor findConstructor( Class clazz, Class... parameterTypes) { + checkForFindConstructor(clazz, parameterTypes); + return findConstructorNoChecks(clazz, parameterTypes); + } + + public static Constructor findConstructorNoChecks( Class clazz, Class... parameterTypes) { + try { + Constructor constructor = (Constructor) clazz.getDeclaredConstructor(parameterTypes); + constructor.setAccessible(true); + return constructor; + } catch (NoSuchMethodException ignored) { + } + return null; + } + + private static void checkForFindConstructor(Class clazz, Class... parameterTypes) { + if (parameterTypes != null) { + for (int i = 0; i < parameterTypes.length; i++) { + if (parameterTypes[i] == null) { + throw new NullPointerException("parameterTypes[" + i + "] == null"); + } + } + } + } + + public boolean isInstance( Object instance) { + return clazz.isInstance(instance); + } + + public int getModifiers() { + return clazz.getModifiers(); + } + + public boolean isLambdaClass() { + return isLambdaClass(clazz); + } + + + public static boolean isLambdaClass(Class clazz) { + return clazz.getName().contains("$$Lambda$"); + } + + public static boolean isProxyClass(Class clazz) { + return Proxy.isProxyClass(clazz); + } + + public static void throwUnchecked(Throwable e) throws T { + throw (T) e; + } + + public static class MemberWrapper { + M member; + + MemberWrapper(M member) { + if (member == null) + return; + member.setAccessible(true); + this.member = member; + } + + + public final M unwrap() { + return member; + } + + public final int getModifiers() { + return member.getModifiers(); + } + + public final Class getDeclaringClass() { + return member.getDeclaringClass(); + } + } + + public static class MethodWrapper extends MemberWrapper { + MethodWrapper(Method method) { + super(method); + } + + public T call(Object instance, Object... args) { + try { + return (T) member.invoke(instance, args); + } catch (Throwable ignored) { + } + return null; + } + + public T callWithException(Object instance, Object... args) throws Throwable { + return (T) member.invoke(instance, args); + } + } + + public static class StaticMethodWrapper extends MemberWrapper { + StaticMethodWrapper(Method method) { + super(method); + } + + public T call(Object... args) { + try { + return (T) member.invoke(null, args); + } catch (Throwable ignored) { + } + return null; + } + + public T callWithException(Object... args) throws Throwable { + return (T) member.invoke(null, args); + } + } + + public static class FieldWrapper extends MemberWrapper { + FieldWrapper(Field field) { + super(field); + } + + public T get(Object instance) { + try { + return (T) member.get(instance); + } catch (Throwable ignored) { + return null; + } + } + + public T get() { + return get(null); + } + + public void set(Object instance, Object value) { + try { + member.set(instance, value); + } catch (Throwable ignored) { + } + } + + public void set(Object value) { + set(null, value); + } + + public Class getType() { + return member.getType(); + } + } + + public static class ConstructorWrapper extends MemberWrapper> { + ConstructorWrapper(Constructor constructor) { + super(constructor); + } + + public T newInstance(Object... args) { + try { + return member.newInstance(args); + } catch (Throwable ignored) { + return null; + } + } + } +} \ No newline at end of file diff --git a/Bcore/black-fake/src/main/java/mirror/android/app/ActivityManagerNative.java b/Bcore/black-fake/src/main/java/mirror/android/app/ActivityManagerNative.java new file mode 100644 index 00000000..737efd69 --- /dev/null +++ b/Bcore/black-fake/src/main/java/mirror/android/app/ActivityManagerNative.java @@ -0,0 +1,21 @@ +package mirror.android.app; + + +import android.os.IInterface; + +import mirror.MirrorReflection; + +/** + * Created by Milk on 5/20/21. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + */ +public class ActivityManagerNative { + public static final MirrorReflection REF = MirrorReflection.on("android.app.ActivityManagerNative"); + + public static MirrorReflection.FieldWrapper gDefault = REF.field("gDefault"); + public static MirrorReflection.StaticMethodWrapper getDefault = REF.staticMethod("getDefault"); +} diff --git a/Bcore/black-fake/src/main/java/mirror/android/app/ActivityManagerOreo.java b/Bcore/black-fake/src/main/java/mirror/android/app/ActivityManagerOreo.java new file mode 100644 index 00000000..4f880dea --- /dev/null +++ b/Bcore/black-fake/src/main/java/mirror/android/app/ActivityManagerOreo.java @@ -0,0 +1,20 @@ +package mirror.android.app; + +import android.os.IInterface; + +import mirror.MirrorReflection; + +/** + * Created by Milk on 5/20/21. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + */ +public class ActivityManagerOreo { + public static final MirrorReflection REF = MirrorReflection.on("android.app.ActivityManager"); + + public static MirrorReflection.MethodWrapper getService = REF.method("getService"); + public static MirrorReflection.FieldWrapper IActivityManagerSingleton = REF.field("IActivityManagerSingleton"); +} diff --git a/Bcore/black-fake/src/main/java/mirror/android/app/ActivityThread.java b/Bcore/black-fake/src/main/java/mirror/android/app/ActivityThread.java new file mode 100644 index 00000000..1bb95e14 --- /dev/null +++ b/Bcore/black-fake/src/main/java/mirror/android/app/ActivityThread.java @@ -0,0 +1,63 @@ +package mirror.android.app; + + +import android.app.Activity; +import android.app.Application; +import android.app.Instrumentation; +import android.content.ComponentName; +import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.content.pm.ApplicationInfo; +import android.content.pm.ProviderInfo; +import android.os.Handler; +import android.os.IBinder; +import android.os.IInterface; + +import java.util.List; + +import mirror.MirrorReflection; + +/** + * Created by Milk on 5/20/21. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + */ +public class ActivityThread { + public static final String NAME = "android.app.ActivityThread"; + public static final MirrorReflection REF = MirrorReflection.on(NAME); + + public static MirrorReflection.StaticMethodWrapper currentActivityThread = REF.staticMethod("currentActivityThread"); + public static MirrorReflection.FieldWrapper mBoundApplication = REF.field("mBoundApplication"); + public static MirrorReflection.FieldWrapper mH = REF.field("mH"); + public static MirrorReflection.FieldWrapper mInitialApplication = REF.field("mInitialApplication"); + public static MirrorReflection.FieldWrapper mInstrumentation = REF.field("mInstrumentation"); + public static MirrorReflection.FieldWrapper sPackageManager = REF.field("sPackageManager"); + public static MirrorReflection.MethodWrapper getApplicationThread = REF.method("getApplicationThread"); + public static MirrorReflection.MethodWrapper getSystemContext = REF.method("getSystemContext"); + + + public static class ActivityClientRecord { + public static final MirrorReflection REF = MirrorReflection.on("android.app.ActivityThread$ActivityClientRecord"); + public static MirrorReflection.FieldWrapper activity = REF.field("activity"); + public static MirrorReflection.FieldWrapper activityInfo = REF.field("activityInfo"); + public static MirrorReflection.FieldWrapper intent = REF.field("intent"); + } + + public static class AppBindData { + public static final MirrorReflection REF = MirrorReflection.on("android.app.ActivityThread$AppBindData"); + public static MirrorReflection.FieldWrapper appInfo = REF.field("appInfo"); + public static MirrorReflection.FieldWrapper info = REF.field("info"); + public static MirrorReflection.FieldWrapper processName = REF.field("processName"); + public static MirrorReflection.FieldWrapper instrumentationName = REF.field("instrumentationName"); + public static MirrorReflection.FieldWrapper> providers = REF.field("providers"); + } + + public static class H { + public static final MirrorReflection REF = MirrorReflection.on("android.app.ActivityThread$H"); + public static MirrorReflection.FieldWrapper LAUNCH_ACTIVITY = REF.field("LAUNCH_ACTIVITY"); + public static MirrorReflection.FieldWrapper EXECUTE_TRANSACTION = REF.field("EXECUTE_TRANSACTION"); + } +} diff --git a/Bcore/black-fake/src/main/java/mirror/android/app/AppOpsManager.java b/Bcore/black-fake/src/main/java/mirror/android/app/AppOpsManager.java new file mode 100644 index 00000000..b7fe594a --- /dev/null +++ b/Bcore/black-fake/src/main/java/mirror/android/app/AppOpsManager.java @@ -0,0 +1,18 @@ +package mirror.android.app; + +import android.os.IInterface; + +import mirror.MirrorReflection; + +/** + * Created by Milk on 5/20/21. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + */ +public class AppOpsManager { + public static final MirrorReflection REF = MirrorReflection.on(android.app.AppOpsManager.class); + public static MirrorReflection.FieldWrapper mService = REF.field("mService"); +} diff --git a/Bcore/black-fake/src/main/java/mirror/android/app/ApplicationThreadNative.java b/Bcore/black-fake/src/main/java/mirror/android/app/ApplicationThreadNative.java new file mode 100644 index 00000000..26173cef --- /dev/null +++ b/Bcore/black-fake/src/main/java/mirror/android/app/ApplicationThreadNative.java @@ -0,0 +1,20 @@ +package mirror.android.app; + +import android.os.IBinder; +import android.os.IInterface; + +import mirror.MirrorReflection; + +/** + * Created by Milk on 5/20/21. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + */ +public class ApplicationThreadNative { + public static final MirrorReflection REF = MirrorReflection.on("android.app.ApplicationThreadNative"); + + public static MirrorReflection.MethodWrapper asInterface = REF.method("asInterface", IBinder.class); +} diff --git a/Bcore/black-fake/src/main/java/mirror/android/app/ContextImpl.java b/Bcore/black-fake/src/main/java/mirror/android/app/ContextImpl.java new file mode 100644 index 00000000..174d75b7 --- /dev/null +++ b/Bcore/black-fake/src/main/java/mirror/android/app/ContextImpl.java @@ -0,0 +1,15 @@ +package mirror.android.app; + + +import android.content.pm.PackageManager; + +import mirror.MirrorReflection; + +public class ContextImpl { + public static final MirrorReflection REF = MirrorReflection.on("android.app.ContextImpl"); + + public static MirrorReflection.FieldWrapper mBasePackageName = REF.field("mBasePackageName"); + public static MirrorReflection.FieldWrapper mPackageInfo = REF.field("mPackageInfo"); + public static MirrorReflection.FieldWrapper mPackageManager = REF.field("mPackageManager"); + public static MirrorReflection.FieldWrapper mOpPackageName = REF.field("mOpPackageName"); +} diff --git a/Bcore/black-fake/src/main/java/mirror/android/app/IActivityManager.java b/Bcore/black-fake/src/main/java/mirror/android/app/IActivityManager.java new file mode 100644 index 00000000..be895e4d --- /dev/null +++ b/Bcore/black-fake/src/main/java/mirror/android/app/IActivityManager.java @@ -0,0 +1,9 @@ +package mirror.android.app; + +import mirror.MirrorReflection; + +public class IActivityManager { + public static final MirrorReflection REF = MirrorReflection.on("android.app.IActivityManager"); + + public static MirrorReflection.MethodWrapper startActivity = REF.method("startActivity"); +} diff --git a/Bcore/black-fake/src/main/java/mirror/android/app/IActivityTaskManager.java b/Bcore/black-fake/src/main/java/mirror/android/app/IActivityTaskManager.java new file mode 100644 index 00000000..63deb14f --- /dev/null +++ b/Bcore/black-fake/src/main/java/mirror/android/app/IActivityTaskManager.java @@ -0,0 +1,15 @@ +package mirror.android.app; + +import android.os.IBinder; +import android.os.IInterface; + +import mirror.MirrorReflection; + +public class IActivityTaskManager { + + public static class Stub { + public static final MirrorReflection REF = MirrorReflection.on("android.app.IActivityTaskManager$Stub"); + + public static MirrorReflection.StaticMethodWrapper asInterface = REF.staticMethod("asInterface", IBinder.class); + } +} diff --git a/Bcore/black-fake/src/main/java/mirror/android/app/IApplicationThreadOreo.java b/Bcore/black-fake/src/main/java/mirror/android/app/IApplicationThreadOreo.java new file mode 100644 index 00000000..e76093e8 --- /dev/null +++ b/Bcore/black-fake/src/main/java/mirror/android/app/IApplicationThreadOreo.java @@ -0,0 +1,13 @@ +package mirror.android.app; + +import android.os.IBinder; +import android.os.IInterface; + +import mirror.MirrorReflection; + +public class IApplicationThreadOreo { + public static final class Stub { + public static final MirrorReflection REF = MirrorReflection.on("android.app.IApplicationThread$Stub"); + public static MirrorReflection.StaticMethodWrapper asInterface = REF.staticMethod("asInterface", IBinder.class); + } +} diff --git a/Bcore/black-fake/src/main/java/mirror/android/app/LoadedApk.java b/Bcore/black-fake/src/main/java/mirror/android/app/LoadedApk.java new file mode 100644 index 00000000..da79ac54 --- /dev/null +++ b/Bcore/black-fake/src/main/java/mirror/android/app/LoadedApk.java @@ -0,0 +1,14 @@ +package mirror.android.app; + +import android.app.Application; +import android.app.Instrumentation; +import android.content.pm.ApplicationInfo; +import mirror.MirrorReflection; + +public class LoadedApk { + public static final MirrorReflection REF = MirrorReflection.on("android.app.LoadedApk"); + public static MirrorReflection.FieldWrapper mApplicationInfo = REF.field("mApplicationInfo"); + public static MirrorReflection.MethodWrapper makeApplication = REF.method("makeApplication", boolean.class, Instrumentation.class); + + public static MirrorReflection.FieldWrapper mSecurityViolation = REF.field("mSecurityViolation"); +} \ No newline at end of file diff --git a/Bcore/black-fake/src/main/java/mirror/android/app/job/IJobScheduler.java b/Bcore/black-fake/src/main/java/mirror/android/app/job/IJobScheduler.java new file mode 100644 index 00000000..c90c642e --- /dev/null +++ b/Bcore/black-fake/src/main/java/mirror/android/app/job/IJobScheduler.java @@ -0,0 +1,13 @@ +package mirror.android.app.job; + +import android.os.IBinder; +import android.os.IInterface; + +import mirror.MirrorReflection; + +public class IJobScheduler { + public static class Stub { + public static final MirrorReflection REF = MirrorReflection.on("android.app.job.IJobScheduler$Stub"); + public static MirrorReflection.StaticMethodWrapper asInterface = REF.staticMethod("asInterface", IBinder.class); + } +} diff --git a/Bcore/black-fake/src/main/java/mirror/android/app/servertransaction/ClientTransaction.java b/Bcore/black-fake/src/main/java/mirror/android/app/servertransaction/ClientTransaction.java new file mode 100644 index 00000000..4af075cf --- /dev/null +++ b/Bcore/black-fake/src/main/java/mirror/android/app/servertransaction/ClientTransaction.java @@ -0,0 +1,11 @@ +package mirror.android.app.servertransaction; + +import java.util.List; + +import mirror.MirrorReflection; + +public class ClientTransaction { + public static final MirrorReflection REF = MirrorReflection.on("android.app.servertransaction.ClientTransaction"); + + public static MirrorReflection.FieldWrapper> mActivityCallbacks = REF.field("mActivityCallbacks"); +} diff --git a/Bcore/black-fake/src/main/java/mirror/android/app/servertransaction/LaunchActivityItem.java b/Bcore/black-fake/src/main/java/mirror/android/app/servertransaction/LaunchActivityItem.java new file mode 100644 index 00000000..cb5ea4bc --- /dev/null +++ b/Bcore/black-fake/src/main/java/mirror/android/app/servertransaction/LaunchActivityItem.java @@ -0,0 +1,11 @@ +package mirror.android.app.servertransaction; + +import android.content.Intent; + +import mirror.MirrorReflection; + +public class LaunchActivityItem { + public static final MirrorReflection REF = MirrorReflection.on("android.app.servertransaction.LaunchActivityItem"); + + public static MirrorReflection.FieldWrapper mIntent = REF.field("mIntent"); +} diff --git a/Bcore/black-fake/src/main/java/mirror/android/content/pm/ApplicationInfoL.java b/Bcore/black-fake/src/main/java/mirror/android/content/pm/ApplicationInfoL.java new file mode 100644 index 00000000..1edfa17f --- /dev/null +++ b/Bcore/black-fake/src/main/java/mirror/android/content/pm/ApplicationInfoL.java @@ -0,0 +1,14 @@ +package mirror.android.content.pm; + +import android.content.pm.ApplicationInfo; + +import mirror.MirrorReflection; + +public class ApplicationInfoL { + public static final MirrorReflection REF = MirrorReflection.on(ApplicationInfo.class); + public static MirrorReflection.FieldWrapper primaryCpuAbi = REF.field("primaryCpuAbi"); + public static MirrorReflection.FieldWrapper scanPublicSourceDir = REF.field("scanPublicSourceDir"); + public static MirrorReflection.FieldWrapper scanSourceDir = REF.field("scanSourceDir"); + public static MirrorReflection.FieldWrapper secondaryCpuAbi = REF.field("secondaryCpuAbi"); + public static MirrorReflection.FieldWrapper splitPublicSourceDirs = REF.field("splitPublicSourceDirs"); +} diff --git a/Bcore/black-fake/src/main/java/mirror/android/content/pm/ApplicationInfoN.java b/Bcore/black-fake/src/main/java/mirror/android/content/pm/ApplicationInfoN.java new file mode 100644 index 00000000..aacc210f --- /dev/null +++ b/Bcore/black-fake/src/main/java/mirror/android/content/pm/ApplicationInfoN.java @@ -0,0 +1,14 @@ +package mirror.android.content.pm; + +import android.content.pm.ApplicationInfo; + +import mirror.MirrorReflection; + +public class ApplicationInfoN { + public static final MirrorReflection REF = MirrorReflection.on(ApplicationInfo.class); + + public static MirrorReflection.FieldWrapper deviceProtectedDataDir = REF.field("deviceProtectedDataDir"); + public static MirrorReflection.FieldWrapper deviceEncryptedDataDir = REF.field("deviceEncryptedDataDir"); + public static MirrorReflection.FieldWrapper credentialProtectedDataDir = REF.field("credentialProtectedDataDir"); + public static MirrorReflection.FieldWrapper credentialEncryptedDataDir = REF.field("credentialEncryptedDataDir"); +} diff --git a/Bcore/black-fake/src/main/java/mirror/android/content/pm/ILauncherApps.java b/Bcore/black-fake/src/main/java/mirror/android/content/pm/ILauncherApps.java new file mode 100644 index 00000000..30733801 --- /dev/null +++ b/Bcore/black-fake/src/main/java/mirror/android/content/pm/ILauncherApps.java @@ -0,0 +1,13 @@ +package mirror.android.content.pm; + +import android.os.IInterface; + +import mirror.MirrorReflection; + +public class ILauncherApps { + + public static final class Stub { + public static final MirrorReflection REF = MirrorReflection.on("android.content.pm.ILauncherApps$Stub"); + public static MirrorReflection.StaticMethodWrapper asInterface = REF.staticMethod("asInterface"); + } +} diff --git a/Bcore/black-fake/src/main/java/mirror/android/content/pm/PackageParser.java b/Bcore/black-fake/src/main/java/mirror/android/content/pm/PackageParser.java new file mode 100644 index 00000000..b91cae33 --- /dev/null +++ b/Bcore/black-fake/src/main/java/mirror/android/content/pm/PackageParser.java @@ -0,0 +1,13 @@ +package mirror.android.content.pm; + +import android.util.DisplayMetrics; +import java.io.File; +import mirror.MirrorReflection; + +public class PackageParser { + public static final MirrorReflection REF = MirrorReflection.on(android.content.pm.PackageParser.class); + + public static MirrorReflection.MethodWrapper collectCertificates = REF.method("collectCertificates", android.content.pm.PackageParser.Package.class, int.class); + public static MirrorReflection.ConstructorWrapper constructor = REF.constructor(String.class); + public static MirrorReflection.MethodWrapper parsePackage = REF.method("parsePackage", File.class, String.class, DisplayMetrics.class, int.class); +} diff --git a/Bcore/black-fake/src/main/java/mirror/android/content/pm/PackageParserJellyBean.java b/Bcore/black-fake/src/main/java/mirror/android/content/pm/PackageParserJellyBean.java new file mode 100644 index 00000000..df9326c7 --- /dev/null +++ b/Bcore/black-fake/src/main/java/mirror/android/content/pm/PackageParserJellyBean.java @@ -0,0 +1,16 @@ +package mirror.android.content.pm; + +import android.content.pm.PackageParser; +import android.util.DisplayMetrics; + +import java.io.File; + +import mirror.MirrorReflection; + +public class PackageParserJellyBean { + public static final MirrorReflection REF = MirrorReflection.on(android.content.pm.PackageParser.class); + + public static MirrorReflection.MethodWrapper collectCertificates = REF.method("collectCertificates", PackageParser.Package.class, int.class); + public static MirrorReflection.ConstructorWrapper constructor = REF.constructor(String.class); + public static MirrorReflection.MethodWrapper parsePackage = REF.method("parsePackage", File.class, String.class, DisplayMetrics.class, int.class); +} diff --git a/Bcore/black-fake/src/main/java/mirror/android/content/pm/PackageParserJellyBean17.java b/Bcore/black-fake/src/main/java/mirror/android/content/pm/PackageParserJellyBean17.java new file mode 100644 index 00000000..51a9d981 --- /dev/null +++ b/Bcore/black-fake/src/main/java/mirror/android/content/pm/PackageParserJellyBean17.java @@ -0,0 +1,15 @@ +package mirror.android.content.pm; + +import android.content.pm.PackageParser; +import android.util.DisplayMetrics; + +import java.io.File; + +import mirror.MirrorReflection; + +public class PackageParserJellyBean17 { + public static final MirrorReflection REF = MirrorReflection.on(android.content.pm.PackageParser.class); + public static MirrorReflection.MethodWrapper collectCertificates = REF.method("collectCertificates", PackageParser.Package.class, int.class); + public static MirrorReflection.ConstructorWrapper constructor = REF.constructor(String.class); + public static MirrorReflection.MethodWrapper parsePackage = REF.method("parsePackage", File.class, String.class, DisplayMetrics.class, int.class); +} diff --git a/Bcore/black-fake/src/main/java/mirror/android/content/pm/PackageParserLollipop.java b/Bcore/black-fake/src/main/java/mirror/android/content/pm/PackageParserLollipop.java new file mode 100644 index 00000000..0b403a5e --- /dev/null +++ b/Bcore/black-fake/src/main/java/mirror/android/content/pm/PackageParserLollipop.java @@ -0,0 +1,15 @@ +package mirror.android.content.pm; + +import android.content.pm.PackageParser; + +import java.io.File; + +import mirror.MirrorReflection; + +public class PackageParserLollipop { + public static final MirrorReflection REF = MirrorReflection.on(android.content.pm.PackageParser.class); + + public static MirrorReflection.MethodWrapper collectCertificates = REF.method("collectCertificates", PackageParser.Package.class, int.class); + public static MirrorReflection.ConstructorWrapper constructor = REF.constructor(); + public static MirrorReflection.MethodWrapper parsePackage = REF.method("parsePackage", File.class, int.class); +} diff --git a/Bcore/black-fake/src/main/java/mirror/android/content/pm/PackageParserLollipop22.java b/Bcore/black-fake/src/main/java/mirror/android/content/pm/PackageParserLollipop22.java new file mode 100644 index 00000000..b8088edc --- /dev/null +++ b/Bcore/black-fake/src/main/java/mirror/android/content/pm/PackageParserLollipop22.java @@ -0,0 +1,15 @@ +package mirror.android.content.pm; + +import android.content.pm.PackageParser; + +import java.io.File; + +import mirror.MirrorReflection; + +public class PackageParserLollipop22 { + public static final MirrorReflection REF = MirrorReflection.on(android.content.pm.PackageParser.class); + + public static MirrorReflection.MethodWrapper collectCertificates = REF.method("collectCertificates", PackageParser.Package.class, int.class); + public static MirrorReflection.ConstructorWrapper constructor = REF.constructor(); + public static MirrorReflection.MethodWrapper parsePackage = REF.method("parsePackage", File.class, int.class); +} diff --git a/Bcore/black-fake/src/main/java/mirror/android/content/pm/PackageParserMarshmallow.java b/Bcore/black-fake/src/main/java/mirror/android/content/pm/PackageParserMarshmallow.java new file mode 100644 index 00000000..d5ebf6d0 --- /dev/null +++ b/Bcore/black-fake/src/main/java/mirror/android/content/pm/PackageParserMarshmallow.java @@ -0,0 +1,15 @@ +package mirror.android.content.pm; + +import android.content.pm.PackageParser; + +import java.io.File; + +import mirror.MirrorReflection; + +public class PackageParserMarshmallow { + public static final MirrorReflection REF = MirrorReflection.on(android.content.pm.PackageParser.class); + + public static MirrorReflection.MethodWrapper collectCertificates = REF.method("collectCertificates", PackageParser.Package.class, int.class); + public static MirrorReflection.ConstructorWrapper constructor = REF.constructor(); + public static MirrorReflection.MethodWrapper parsePackage = REF.method("parsePackage", File.class, int.class); +} diff --git a/Bcore/black-fake/src/main/java/mirror/android/content/pm/PackageParserNougat.java b/Bcore/black-fake/src/main/java/mirror/android/content/pm/PackageParserNougat.java new file mode 100644 index 00000000..bce0f1fa --- /dev/null +++ b/Bcore/black-fake/src/main/java/mirror/android/content/pm/PackageParserNougat.java @@ -0,0 +1,10 @@ +package mirror.android.content.pm; + +import android.content.pm.PackageParser; + +import mirror.MirrorReflection; + +public class PackageParserNougat { + public static final MirrorReflection REF = MirrorReflection.on(android.content.pm.PackageParser.class); + public static MirrorReflection.StaticMethodWrapper collectCertificates = REF.staticMethod("collectCertificates", PackageParser.Package.class, int.class); +} diff --git a/Bcore/black-fake/src/main/java/mirror/android/content/pm/PackageParserPie.java b/Bcore/black-fake/src/main/java/mirror/android/content/pm/PackageParserPie.java new file mode 100644 index 00000000..a63c77d4 --- /dev/null +++ b/Bcore/black-fake/src/main/java/mirror/android/content/pm/PackageParserPie.java @@ -0,0 +1,11 @@ +package mirror.android.content.pm; + +import android.content.pm.PackageParser; + +import mirror.MirrorReflection; + +public class PackageParserPie { + public static final MirrorReflection REF = MirrorReflection.on(android.content.pm.PackageParser.class); + + public static MirrorReflection.StaticMethodWrapper collectCertificates = REF.staticMethod("collectCertificates", PackageParser.Package.class, boolean.class); +} diff --git a/Bcore/black-fake/src/main/java/mirror/android/content/pm/ParceledListSlice.java b/Bcore/black-fake/src/main/java/mirror/android/content/pm/ParceledListSlice.java new file mode 100644 index 00000000..d8400d01 --- /dev/null +++ b/Bcore/black-fake/src/main/java/mirror/android/content/pm/ParceledListSlice.java @@ -0,0 +1,13 @@ +package mirror.android.content.pm; + +import android.os.Parcelable; + +import mirror.MirrorReflection; + +public class ParceledListSlice { + public static final MirrorReflection REF = MirrorReflection.on("android.content.pm.ParceledListSlice"); + public static MirrorReflection.MethodWrapper append = REF.method("append"); + public static MirrorReflection.ConstructorWrapper constructor = REF.constructor(); + + public static MirrorReflection.MethodWrapper setLastSlice = REF.method("setLastSlice"); +} diff --git a/Bcore/black-fake/src/main/java/mirror/android/content/pm/ParceledListSliceJBMR2.java b/Bcore/black-fake/src/main/java/mirror/android/content/pm/ParceledListSliceJBMR2.java new file mode 100644 index 00000000..cd1f8b2b --- /dev/null +++ b/Bcore/black-fake/src/main/java/mirror/android/content/pm/ParceledListSliceJBMR2.java @@ -0,0 +1,12 @@ +package mirror.android.content.pm; + +import android.os.Parcelable; + +import java.util.List; + +import mirror.MirrorReflection; + +public class ParceledListSliceJBMR2 { + public static final MirrorReflection REF = MirrorReflection.on("android.content.pm.ParceledListSlice"); + public static MirrorReflection.ConstructorWrapper constructor = REF.constructor(List.class); +} diff --git a/Bcore/black-fake/src/main/java/mirror/android/content/pm/SigningInfo.java b/Bcore/black-fake/src/main/java/mirror/android/content/pm/SigningInfo.java new file mode 100644 index 00000000..5f15a6eb --- /dev/null +++ b/Bcore/black-fake/src/main/java/mirror/android/content/pm/SigningInfo.java @@ -0,0 +1,19 @@ +package mirror.android.content.pm; + +import android.content.pm.PackageParser; + +import mirror.MirrorReflection; + +/** + * Created by Milk on 4/16/21. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + */ +public class SigningInfo { + public static final MirrorReflection REF = MirrorReflection.on("android.content.pm.SigningInfo"); + + public static MirrorReflection.FieldWrapper mSigningDetails = REF.field("mSigningDetails"); +} diff --git a/Bcore/black-fake/src/main/java/mirror/android/os/BaseBundle.java b/Bcore/black-fake/src/main/java/mirror/android/os/BaseBundle.java new file mode 100644 index 00000000..f3d9c6d4 --- /dev/null +++ b/Bcore/black-fake/src/main/java/mirror/android/os/BaseBundle.java @@ -0,0 +1,10 @@ +package mirror.android.os; + +import android.os.Parcel; + +import mirror.MirrorReflection; + +public class BaseBundle { + public static final MirrorReflection REF = MirrorReflection.on("android.os.BaseBundle"); + public static MirrorReflection.FieldWrapper mParcelledData = REF.field("mParcelledData"); +} \ No newline at end of file diff --git a/Bcore/black-fake/src/main/java/mirror/android/os/Bundle.java b/Bcore/black-fake/src/main/java/mirror/android/os/Bundle.java new file mode 100644 index 00000000..5cf55e43 --- /dev/null +++ b/Bcore/black-fake/src/main/java/mirror/android/os/Bundle.java @@ -0,0 +1,13 @@ +package mirror.android.os; + +import android.os.IBinder; + +import mirror.MirrorReflection; + +public class Bundle { + public static final MirrorReflection REF = MirrorReflection.on(android.os.Bundle.class); + + public static MirrorReflection.MethodWrapper putIBinder = REF.method("putIBinder", String.class, IBinder.class); + + public static MirrorReflection.MethodWrapper getIBinder = REF.method("getIBinder", String.class); +} diff --git a/Bcore/black-fake/src/main/java/mirror/android/os/BundleICS.java b/Bcore/black-fake/src/main/java/mirror/android/os/BundleICS.java new file mode 100644 index 00000000..d9bc44ac --- /dev/null +++ b/Bcore/black-fake/src/main/java/mirror/android/os/BundleICS.java @@ -0,0 +1,10 @@ +package mirror.android.os; + +import android.os.Parcel; + +import mirror.MirrorReflection; + +public class BundleICS { + public static final MirrorReflection REF = MirrorReflection.on(android.os.Bundle.class); + public static MirrorReflection.FieldWrapper mParcelledData = REF.field("mParcelledData"); +} \ No newline at end of file diff --git a/Bcore/black-fake/src/main/java/mirror/android/os/Handler.java b/Bcore/black-fake/src/main/java/mirror/android/os/Handler.java new file mode 100644 index 00000000..cd9fd551 --- /dev/null +++ b/Bcore/black-fake/src/main/java/mirror/android/os/Handler.java @@ -0,0 +1,9 @@ +package mirror.android.os; + +import mirror.MirrorReflection; + +public class Handler { + public static final MirrorReflection REF = MirrorReflection.on(android.os.Handler.class); + + public static MirrorReflection.FieldWrapper mCallback = REF.field("mCallback"); +} diff --git a/Bcore/black-fake/src/main/java/mirror/android/os/IDeviceIdentifiersPolicyService.java b/Bcore/black-fake/src/main/java/mirror/android/os/IDeviceIdentifiersPolicyService.java new file mode 100644 index 00000000..e646b0a8 --- /dev/null +++ b/Bcore/black-fake/src/main/java/mirror/android/os/IDeviceIdentifiersPolicyService.java @@ -0,0 +1,13 @@ +package mirror.android.os; + +import android.os.IBinder; +import android.os.IInterface; + +import mirror.MirrorReflection; + +public class IDeviceIdentifiersPolicyService { + public static class Stub { + public static final MirrorReflection REF = MirrorReflection.on("android.os.IDeviceIdentifiersPolicyService$Stub"); + public static MirrorReflection.StaticMethodWrapper asInterface = REF.staticMethod("asInterface", IBinder.class); + } +} diff --git a/Bcore/black-fake/src/main/java/mirror/android/os/ServiceManager.java b/Bcore/black-fake/src/main/java/mirror/android/os/ServiceManager.java new file mode 100644 index 00000000..7746cd15 --- /dev/null +++ b/Bcore/black-fake/src/main/java/mirror/android/os/ServiceManager.java @@ -0,0 +1,16 @@ +package mirror.android.os; + +import android.os.IBinder; +import android.os.IInterface; + +import java.util.Map; + +import mirror.MirrorReflection; + +public class ServiceManager { + public static final MirrorReflection REF = MirrorReflection.on("android.os.ServiceManager"); + public static MirrorReflection.StaticMethodWrapper addService = REF.staticMethod("addService", String.class, IBinder.class); + public static MirrorReflection.StaticMethodWrapper getIServiceManager = REF.staticMethod("getIServiceManager"); + public static MirrorReflection.StaticMethodWrapper getService = REF.staticMethod("getService"); + public static MirrorReflection.FieldWrapper> sCache = REF.field("sCache"); +} diff --git a/Bcore/black-fake/src/main/java/mirror/android/os/mount/IMountService.java b/Bcore/black-fake/src/main/java/mirror/android/os/mount/IMountService.java new file mode 100644 index 00000000..04cce04f --- /dev/null +++ b/Bcore/black-fake/src/main/java/mirror/android/os/mount/IMountService.java @@ -0,0 +1,13 @@ +package mirror.android.os.mount; + +import android.os.IBinder; +import android.os.IInterface; + +import mirror.MirrorReflection; + +public class IMountService { + public static class Stub { + public static final MirrorReflection REF = MirrorReflection.on("android.os.storage.IMountService$Stub"); + public static MirrorReflection.StaticMethodWrapper asInterface = REF.staticMethod("asInterface", IBinder.class); + } +} diff --git a/Bcore/black-fake/src/main/java/mirror/android/os/storage/IStorageManager.java b/Bcore/black-fake/src/main/java/mirror/android/os/storage/IStorageManager.java new file mode 100644 index 00000000..206c09af --- /dev/null +++ b/Bcore/black-fake/src/main/java/mirror/android/os/storage/IStorageManager.java @@ -0,0 +1,14 @@ +package mirror.android.os.storage; + +import android.os.IBinder; +import android.os.IInterface; + +import mirror.MirrorReflection; + +public class IStorageManager { + public static class Stub { + public static final MirrorReflection REF = MirrorReflection.on("android.os.storage.IStorageManager$Stub"); + + public static MirrorReflection.StaticMethodWrapper asInterface = REF.staticMethod("asInterface", IBinder.class); + } +} \ No newline at end of file diff --git a/Bcore/black-fake/src/main/java/mirror/android/os/storage/StorageManager.java b/Bcore/black-fake/src/main/java/mirror/android/os/storage/StorageManager.java new file mode 100644 index 00000000..aee64b8a --- /dev/null +++ b/Bcore/black-fake/src/main/java/mirror/android/os/storage/StorageManager.java @@ -0,0 +1,19 @@ +package mirror.android.os.storage; + +import android.os.storage.StorageVolume; + +import mirror.MirrorReflection; + +/** + * Created by Milk on 4/10/21. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + */ +public class StorageManager { + public static final MirrorReflection REF = MirrorReflection.on("android.os.storage.StorageManager"); + + public static MirrorReflection.StaticMethodWrapper getVolumeList = REF.staticMethod("getVolumeList", int.class, int.class); +} diff --git a/Bcore/black-fake/src/main/java/mirror/android/os/storage/StorageVolume.java b/Bcore/black-fake/src/main/java/mirror/android/os/storage/StorageVolume.java new file mode 100644 index 00000000..3adf15c6 --- /dev/null +++ b/Bcore/black-fake/src/main/java/mirror/android/os/storage/StorageVolume.java @@ -0,0 +1,20 @@ +package mirror.android.os.storage; + +import java.io.File; + +import mirror.MirrorReflection; + +/** + * Created by Milk on 4/10/21. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + */ +public class StorageVolume { + public static final MirrorReflection REF = MirrorReflection.on("android.os.storage.StorageVolume"); + + public static MirrorReflection.FieldWrapper mPath = REF.field("mPath"); + public static MirrorReflection.FieldWrapper mInternalPath = REF.field("mInternalPath"); +} diff --git a/Bcore/black-fake/src/main/java/mirror/android/util/Singleton.java b/Bcore/black-fake/src/main/java/mirror/android/util/Singleton.java new file mode 100644 index 00000000..f61f2c74 --- /dev/null +++ b/Bcore/black-fake/src/main/java/mirror/android/util/Singleton.java @@ -0,0 +1,10 @@ +package mirror.android.util; + + +import mirror.MirrorReflection; + +public class Singleton { + public static final MirrorReflection REF = MirrorReflection.on("android.util.Singleton"); + public static MirrorReflection.MethodWrapper get = REF.method("get"); + public static MirrorReflection.FieldWrapper mInstance = REF.field("mInstance"); +} diff --git a/Bcore/black-fake/src/main/java/mirror/com/android/internal/app/IAppOpsService.java b/Bcore/black-fake/src/main/java/mirror/com/android/internal/app/IAppOpsService.java new file mode 100644 index 00000000..8e0c5b12 --- /dev/null +++ b/Bcore/black-fake/src/main/java/mirror/com/android/internal/app/IAppOpsService.java @@ -0,0 +1,15 @@ +package mirror.com.android.internal.app; + +import android.os.IBinder; +import android.os.IInterface; + +import mirror.MirrorReflection; + + +public class IAppOpsService { + public static class Stub { + public static final String NAME = "com.android.internal.app.IAppOpsService$Stub"; + private static final MirrorReflection REF = MirrorReflection.on(NAME); + public static MirrorReflection.StaticMethodWrapper asInterface = REF.staticMethod("asInterface", IBinder.class); + } +} \ No newline at end of file diff --git a/Bcore/black-fake/src/main/java/mirror/com/android/internal/telephony/ITelephony.java b/Bcore/black-fake/src/main/java/mirror/com/android/internal/telephony/ITelephony.java new file mode 100644 index 00000000..58b0edcb --- /dev/null +++ b/Bcore/black-fake/src/main/java/mirror/com/android/internal/telephony/ITelephony.java @@ -0,0 +1,14 @@ +package mirror.com.android.internal.telephony; + +import android.os.IBinder; +import android.os.IInterface; + +import mirror.MirrorReflection; + +public class ITelephony { + public static class Stub { + public static final MirrorReflection REF = MirrorReflection.on("com.android.internal.telephony.ITelephony$Stub"); + + public static MirrorReflection.StaticMethodWrapper asInterface = REF.staticMethod("asInterface", IBinder.class); + } +} diff --git a/Bcore/black-fake/src/main/java/mirror/com/android/internal/telephony/ITelephonyRegistry.java b/Bcore/black-fake/src/main/java/mirror/com/android/internal/telephony/ITelephonyRegistry.java new file mode 100644 index 00000000..a1209111 --- /dev/null +++ b/Bcore/black-fake/src/main/java/mirror/com/android/internal/telephony/ITelephonyRegistry.java @@ -0,0 +1,14 @@ +package mirror.com.android.internal.telephony; + +import android.os.IBinder; +import android.os.IInterface; + +import mirror.MirrorReflection; + +public class ITelephonyRegistry { + + public static class Stub { + public static final MirrorReflection REF = MirrorReflection.on("com.android.internal.telephony.ITelephonyRegistry$Stub"); + public static MirrorReflection.StaticMethodWrapper asInterface = REF.staticMethod("asInterface", IBinder.class); + } +} diff --git a/Bcore/black-fake/src/main/java/mirror/libcore/io/Libcore.java b/Bcore/black-fake/src/main/java/mirror/libcore/io/Libcore.java new file mode 100644 index 00000000..8b230059 --- /dev/null +++ b/Bcore/black-fake/src/main/java/mirror/libcore/io/Libcore.java @@ -0,0 +1,10 @@ +package mirror.libcore.io; + +import mirror.MirrorReflection; + +public class Libcore { + public static final String NAME = "libcore.io.Libcore"; + public static final MirrorReflection REF = MirrorReflection.on(NAME); + + public static MirrorReflection.FieldWrapper os = REF.field("os"); +} diff --git a/Bcore/black-hook/.gitignore b/Bcore/black-hook/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/Bcore/black-hook/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/Bcore/black-hook/build.gradle b/Bcore/black-hook/build.gradle new file mode 100644 index 00000000..50d2c8bf --- /dev/null +++ b/Bcore/black-hook/build.gradle @@ -0,0 +1,62 @@ +plugins { + id 'com.android.library' +} + +android { + compileSdkVersion 30 + buildToolsVersion "30.0.3" + + defaultConfig { + minSdkVersion 21 + targetSdkVersion 30 + versionCode 1 + versionName "1.0" + + consumerProguardFiles "consumer-rules.pro" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + dexOptions { + preDexLibraries false + maxProcessCount 8 + javaMaxHeapSize "4g" + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + aaptOptions { + cruncherEnabled = false + useNewCruncher = false + } + testOptions { + unitTests.returnDefaultValues = true + } + lintOptions { + checkReleaseBuilds false + abortOnError false + warningsAsErrors false + disable "UnusedResources", 'RestrictedApi' + textOutput "stdout" + textReport false + check 'NewApi', 'InlinedApi' + } +} + + +tasks.withType(Javadoc) { + options.addStringOption('Xdoclint:none', '-quiet') + options.addStringOption('encoding', 'UTF-8') + options.addStringOption('charSet', 'UTF-8') +} + +dependencies { + + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation 'com.google.android.material:material:1.3.0' +} \ No newline at end of file diff --git a/Bcore/black-hook/consumer-rules.pro b/Bcore/black-hook/consumer-rules.pro new file mode 100644 index 00000000..e69de29b diff --git a/Bcore/black-hook/proguard-rules.pro b/Bcore/black-hook/proguard-rules.pro new file mode 100644 index 00000000..481bb434 --- /dev/null +++ b/Bcore/black-hook/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# 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 \ No newline at end of file diff --git a/Bcore/black-hook/src/main/AndroidManifest.xml b/Bcore/black-hook/src/main/AndroidManifest.xml new file mode 100644 index 00000000..df9d7518 --- /dev/null +++ b/Bcore/black-hook/src/main/AndroidManifest.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/Bcore/black-hook/src/main/java/top/niunaijun/jnihook/MethodUtils.java b/Bcore/black-hook/src/main/java/top/niunaijun/jnihook/MethodUtils.java new file mode 100644 index 00000000..b8ba47c5 --- /dev/null +++ b/Bcore/black-hook/src/main/java/top/niunaijun/jnihook/MethodUtils.java @@ -0,0 +1,93 @@ +package top.niunaijun.jnihook; + +/** + * Created by Milk on 3/8/21. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + */ + +import androidx.annotation.Keep; + +import java.lang.reflect.Method; + +@Keep +public class MethodUtils { + + // native call + public static String getDeclaringClass(final Method method) { + return method.getDeclaringClass().getName().replace(".", "/"); + } + + // native call + public static String getMethodName(final Method method) { + return method.getName(); + } + + // native call + public static String getDesc(final Method method) { + final StringBuffer buf = new StringBuffer(); + buf.append("("); + final Class[] types = method.getParameterTypes(); + for (int i = 0; i < types.length; ++i) { + buf.append(getDesc(types[i])); + } + buf.append(")"); + buf.append(getDesc(method.getReturnType())); + return buf.toString(); + } + + private static String getDesc(final Class returnType) { + if (returnType.isPrimitive()) { + return getPrimitiveLetter(returnType); + } + if (returnType.isArray()) { + return "[" + getDesc(returnType.getComponentType()); + } + return "L" + getType(returnType) + ";"; + } + + private static String getType(final Class parameterType) { + if (parameterType.isArray()) { + return "[" + getDesc(parameterType.getComponentType()); + } + if (!parameterType.isPrimitive()) { + final String clsName = parameterType.getName(); + return clsName.replaceAll("\\.", "/"); + } + return getPrimitiveLetter(parameterType); + } + + private static String getPrimitiveLetter(final Class type) { + if (Integer.TYPE.equals(type)) { + return "I"; + } + if (Void.TYPE.equals(type)) { + return "V"; + } + if (Boolean.TYPE.equals(type)) { + return "Z"; + } + if (Character.TYPE.equals(type)) { + return "C"; + } + if (Byte.TYPE.equals(type)) { + return "B"; + } + if (Short.TYPE.equals(type)) { + return "S"; + } + if (Float.TYPE.equals(type)) { + return "F"; + } + if (Long.TYPE.equals(type)) { + return "J"; + } + if (Double.TYPE.equals(type)) { + return "D"; + } + throw new IllegalStateException("Type: " + type.getCanonicalName() + " is not a primitive type"); + } +} \ No newline at end of file diff --git a/Bcore/black-hook/src/main/java/top/niunaijun/jnihook/ReflectCore.java b/Bcore/black-hook/src/main/java/top/niunaijun/jnihook/ReflectCore.java new file mode 100644 index 00000000..4057630d --- /dev/null +++ b/Bcore/black-hook/src/main/java/top/niunaijun/jnihook/ReflectCore.java @@ -0,0 +1,37 @@ +package top.niunaijun.jnihook; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; + +import top.niunaijun.jnihook.jni.JniHook; + +/** + * Created by Milk on 2021/5/7. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + */ +public class ReflectCore { + + public static void set(Class clazz) { + try { + Field accessFlags = Class.class.getDeclaredField("accessFlags"); + accessFlags.setAccessible(true); + int o = (int) accessFlags.get(clazz); + accessFlags.set(clazz, o | 0x0001); + } catch (Throwable e) { + e.printStackTrace(); + } + for (Method declaredMethod : clazz.getDeclaredMethods()) { + JniHook.setAccessible(clazz, declaredMethod); + } + for (Field declaredField : clazz.getDeclaredFields()) { + JniHook.setAccessible(clazz, declaredField); + } + for (Class declaredClass : clazz.getDeclaredClasses()) { + set(declaredClass); + } + } +} diff --git a/Bcore/black-hook/src/main/java/top/niunaijun/jnihook/jni/JniHook.java b/Bcore/black-hook/src/main/java/top/niunaijun/jnihook/jni/JniHook.java new file mode 100644 index 00000000..0b4eba07 --- /dev/null +++ b/Bcore/black-hook/src/main/java/top/niunaijun/jnihook/jni/JniHook.java @@ -0,0 +1,26 @@ +package top.niunaijun.jnihook.jni; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; + +/** + * Created by Milk on 3/7/21. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + */ +public final class JniHook { + public static final int NATIVE_OFFSET = 0; + + public static final int NATIVE_OFFSET_2 = 0; + + public static final native void nativeOffset(); + + public static final native void nativeOffset2(); + + public static native void setAccessible(Class clazz, Method method); + + public static native void setAccessible(Class clazz, Field field); +} diff --git a/Bcore/build.gradle b/Bcore/build.gradle new file mode 100644 index 00000000..837a011f --- /dev/null +++ b/Bcore/build.gradle @@ -0,0 +1,74 @@ +apply plugin: 'com.android.library' + +android { + compileSdkVersion 28 + buildToolsVersion "28.0.2" + + defaultConfig { + minSdkVersion 21 + targetSdkVersion 28 + versionCode 1 + versionName "1.0" + + consumerProguardFiles "consumer-rules.pro" + ndk { + // 设置支持的SO库架构 + abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86' + } + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + externalNativeBuild { + ndkBuild { + path file('src/main/jni/Android.mk') + } + } + dexOptions { + preDexLibraries false + maxProcessCount 8 + javaMaxHeapSize "4g" + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + aaptOptions { + cruncherEnabled = false + useNewCruncher = false + } + testOptions { + unitTests.returnDefaultValues = true + } + lintOptions { + checkReleaseBuilds false + abortOnError false + warningsAsErrors false + disable "UnusedResources", 'RestrictedApi' + textOutput "stdout" + textReport false + check 'NewApi', 'InlinedApi' + } +} + + +tasks.withType(Javadoc) { + options.addStringOption('Xdoclint:none', '-quiet') + options.addStringOption('encoding', 'UTF-8') + options.addStringOption('charSet', 'UTF-8') +} + + +dependencies { + implementation fileTree(dir: "libs", include: ["*.jar"]) + implementation 'androidx.appcompat:appcompat:1.2.0' + + implementation 'me.weishu:free_reflection:3.0.1' + implementation project(':Bcore:black-hook') + implementation project(':Bcore:black-fake') + implementation 'com.swift.sandhook:hooklib:4.2.0' +} \ No newline at end of file diff --git a/Bcore/consumer-rules.pro b/Bcore/consumer-rules.pro new file mode 100644 index 00000000..de119130 --- /dev/null +++ b/Bcore/consumer-rules.pro @@ -0,0 +1,27 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# 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 + +-keep class top.niunaijun.blackbox.** {*; } +-keep class top.niunaijun.jnihook.** {*; } +-keep class mirror.** {*; } +-keep class android.** {*; } +-keep class com.android.** {*; } diff --git a/Bcore/proguard-rules.pro b/Bcore/proguard-rules.pro new file mode 100644 index 00000000..de119130 --- /dev/null +++ b/Bcore/proguard-rules.pro @@ -0,0 +1,27 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# 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 + +-keep class top.niunaijun.blackbox.** {*; } +-keep class top.niunaijun.jnihook.** {*; } +-keep class mirror.** {*; } +-keep class android.** {*; } +-keep class com.android.** {*; } diff --git a/Bcore/src/main/AndroidManifest.xml b/Bcore/src/main/AndroidManifest.xml new file mode 100644 index 00000000..71450130 --- /dev/null +++ b/Bcore/src/main/AndroidManifest.xml @@ -0,0 +1,2558 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Bcore/src/main/aidl/android/accounts/IAccountAuthenticator.aidl b/Bcore/src/main/aidl/android/accounts/IAccountAuthenticator.aidl new file mode 100644 index 00000000..fe5c9eaf --- /dev/null +++ b/Bcore/src/main/aidl/android/accounts/IAccountAuthenticator.aidl @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.accounts; + +import android.accounts.IAccountAuthenticatorResponse; +import android.accounts.Account; +import android.os.Bundle; + +/** + * Service that allows the interaction with an authentication server. + * @hide + */ +interface IAccountAuthenticator { + /** + * prompts the user for account information and adds the result to the IAccountManager + */ + void addAccount(in IAccountAuthenticatorResponse response, String accountType, + String authTokenType, in String[] requiredFeatures, in Bundle options); + + /** + * prompts the user for the credentials of the account + */ + void confirmCredentials(in IAccountAuthenticatorResponse response, in Account account, + in Bundle options); + + /** + * gets the password by either prompting the user or querying the IAccountManager + */ + void getAuthToken(in IAccountAuthenticatorResponse response, in Account account, + String authTokenType, in Bundle options); + + /** + * Gets the user-visible label of the given authtoken type. + */ + void getAuthTokenLabel(in IAccountAuthenticatorResponse response, String authTokenType); + + /** + * prompts the user for a new password and writes it to the IAccountManager + */ + void updateCredentials(in IAccountAuthenticatorResponse response, in Account account, + String authTokenType, in Bundle options); + + /** + * launches an activity that lets the user edit and set the properties for an authenticator + */ + void editProperties(in IAccountAuthenticatorResponse response, String accountType); + + /** + * returns a Bundle where the boolean value BOOLEAN_RESULT_KEY is set if the account has the + * specified features + */ + void hasFeatures(in IAccountAuthenticatorResponse response, in Account account, + in String[] features); + + /** + * Gets whether or not the account is allowed to be removed. + */ + void getAccountRemovalAllowed(in IAccountAuthenticatorResponse response, in Account account); + + /** + * Returns a Bundle containing the required credentials to copy the account across users. + */ + void getAccountCredentialsForCloning(in IAccountAuthenticatorResponse response, + in Account account); + + /** + * Uses the Bundle containing credentials from another instance of the authenticator to create + * a copy of the account on this user. + */ + void addAccountFromCredentials(in IAccountAuthenticatorResponse response, in Account account, + in Bundle accountCredentials); +} diff --git a/Bcore/src/main/aidl/android/accounts/IAccountAuthenticatorResponse.aidl b/Bcore/src/main/aidl/android/accounts/IAccountAuthenticatorResponse.aidl new file mode 100644 index 00000000..5218bd74 --- /dev/null +++ b/Bcore/src/main/aidl/android/accounts/IAccountAuthenticatorResponse.aidl @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.accounts; +import android.os.Bundle; + +/** + * The interface used to return responses from an {@link IAccountAuthenticator} + */ +interface IAccountAuthenticatorResponse { + void onResult(in Bundle value); + void onRequestContinued(); + void onError(int errorCode, String errorMessage); +} diff --git a/Bcore/src/main/aidl/android/accounts/IAccountManagerResponse.aidl b/Bcore/src/main/aidl/android/accounts/IAccountManagerResponse.aidl new file mode 100644 index 00000000..ed33d905 --- /dev/null +++ b/Bcore/src/main/aidl/android/accounts/IAccountManagerResponse.aidl @@ -0,0 +1,11 @@ +package android.accounts; + +import android.os.Bundle; + +/** + * The interface used to return responses for asynchronous calls to the {@link IAccountManager} + */ +interface IAccountManagerResponse { + void onResult(in Bundle value); + void onError(int errorCode, String errorMessage); +} diff --git a/Bcore/src/main/aidl/android/app/IActivityManager/ContentProviderHolder.aidl b/Bcore/src/main/aidl/android/app/IActivityManager/ContentProviderHolder.aidl new file mode 100644 index 00000000..8643b692 --- /dev/null +++ b/Bcore/src/main/aidl/android/app/IActivityManager/ContentProviderHolder.aidl @@ -0,0 +1,4 @@ +// ContentProviderHolder.aidl +package android.app.IActivityManager; + +parcelable ContentProviderHolder; \ No newline at end of file diff --git a/Bcore/src/main/aidl/android/app/IServiceConnection.aidl b/Bcore/src/main/aidl/android/app/IServiceConnection.aidl new file mode 100644 index 00000000..e312ecb4 --- /dev/null +++ b/Bcore/src/main/aidl/android/app/IServiceConnection.aidl @@ -0,0 +1,26 @@ +/* //device/java/android/android/app/IServiceConnection.aidl +** +** Copyright 2007, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +package android.app; + +import android.content.ComponentName; + +/** @hide */ +interface IServiceConnection { + void connected(in ComponentName name, IBinder service); +} + diff --git a/Bcore/src/main/aidl/android/app/IStopUserCallback.aidl b/Bcore/src/main/aidl/android/app/IStopUserCallback.aidl new file mode 100644 index 00000000..19ac1d5d --- /dev/null +++ b/Bcore/src/main/aidl/android/app/IStopUserCallback.aidl @@ -0,0 +1,27 @@ +/* +** Copyright 2012, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +package android.app; + +/** + * Callback to find out when we have finished stopping a user. + * {@hide} + */ +interface IStopUserCallback +{ + void userStopped(int userId); + void userStopAborted(int userId); +} diff --git a/Bcore/src/main/aidl/android/app/IWallpaperManagerCallback.aidl b/Bcore/src/main/aidl/android/app/IWallpaperManagerCallback.aidl new file mode 100644 index 00000000..9bc19435 --- /dev/null +++ b/Bcore/src/main/aidl/android/app/IWallpaperManagerCallback.aidl @@ -0,0 +1,24 @@ +/* //device/java/android/android/app/IServiceConnection.aidl +** +** Copyright 2007, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +package android.app; + +/** @hide */ +interface IWallpaperManagerCallback { + void onWallpaperChanged(); +} + diff --git a/Bcore/src/main/aidl/android/app/job/IJobCallback.aidl b/Bcore/src/main/aidl/android/app/job/IJobCallback.aidl new file mode 100644 index 00000000..e567268b --- /dev/null +++ b/Bcore/src/main/aidl/android/app/job/IJobCallback.aidl @@ -0,0 +1,62 @@ +/** + * Copyright 2014, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app.job; + +import android.app.job.JobWorkItem; + +/** + * The server side of the JobScheduler IPC protocols. The app-side implementation + * invokes on this interface to indicate completion of the (asynchronous) instructions + * issued by the server. + * + * In all cases, the 'who' parameter is the caller's service binder, used to track + * which Job Service instance is reporting. + * + */ +interface IJobCallback { + /** + * Immediate callback to the system after sending a start signal, used to quickly detect ANR. + * + * @param jobId Unique integer used to identify this job. + * @param ongoing True to indicate that the client is processing the job. False if the job is + * complete + */ + void acknowledgeStartMessage(int jobId, boolean ongoing); + /** + * Immediate callback to the system after sending a stop signal, used to quickly detect ANR. + * + * @param jobId Unique integer used to identify this job. + * @param reschedule Whether or not to reschedule this job. + */ + void acknowledgeStopMessage(int jobId, boolean reschedule); + /* + * Called to deqeue next work item for the job. + */ + JobWorkItem dequeueWork(int jobId); + /* + * Called to report that job has completed processing a work item. + */ + boolean completeWork(int jobId, int workId); + /* + * Tell the job manager that the client is done with its execution, so that it can go on to + * the next one and stop attributing wakelock time to us etc. + * + * @param jobId Unique integer used to identify this job. + * @param reschedule Whether or not to reschedule this job. + */ + void jobFinished(int jobId, boolean reschedule); +} diff --git a/Bcore/src/main/aidl/android/app/job/IJobService.aidl b/Bcore/src/main/aidl/android/app/job/IJobService.aidl new file mode 100644 index 00000000..7992da31 --- /dev/null +++ b/Bcore/src/main/aidl/android/app/job/IJobService.aidl @@ -0,0 +1,15 @@ +package android.app.job; + +import android.app.job.JobParameters; + +/** + * Interface that the framework uses to communicate with application code that implements a + * JobService. End user code does not implement this interface directly; instead, the app's + * service implementation will extend android.app.job.JobService. + */ +interface IJobService { + /** Begin execution of application's job. */ + void startJob(in JobParameters jobParams); + /** Stop execution of application's job. */ + void stopJob(in JobParameters jobParams); +} diff --git a/Bcore/src/main/aidl/android/app/job/JobWorkItem.aidl b/Bcore/src/main/aidl/android/app/job/JobWorkItem.aidl new file mode 100644 index 00000000..4634ec2d --- /dev/null +++ b/Bcore/src/main/aidl/android/app/job/JobWorkItem.aidl @@ -0,0 +1,4 @@ +// JobWorkItem.aidl +package android.app.job; + +parcelable JobWorkItem; diff --git a/Bcore/src/main/aidl/android/content/IIntentReceiver.aidl b/Bcore/src/main/aidl/android/content/IIntentReceiver.aidl new file mode 100644 index 00000000..40d9303e --- /dev/null +++ b/Bcore/src/main/aidl/android/content/IIntentReceiver.aidl @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.content; + +import android.content.Intent; +import android.os.Bundle; + +/** + * System private API for dispatching intent broadcasts. This is given to the + * activity manager as part of registering for an intent broadcasts, and is + * called when it receives intents. + * + */ +interface IIntentReceiver { + void performReceive(in Intent intent, int resultCode, String data, + in Bundle extras, boolean ordered, boolean sticky, int sendingUser); +} + diff --git a/Bcore/src/main/aidl/android/content/ISyncAdapter.aidl b/Bcore/src/main/aidl/android/content/ISyncAdapter.aidl new file mode 100644 index 00000000..2325fffe --- /dev/null +++ b/Bcore/src/main/aidl/android/content/ISyncAdapter.aidl @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.content; + +import android.accounts.Account; +import android.os.Bundle; +import android.content.ISyncContext; + +/** + * Interface used to control the sync activity on a SyncAdapter + */ +interface ISyncAdapter { + /** + * Initiate a sync for this account. SyncAdapter-specific parameters may + * be specified in extras, which is guaranteed to not be null. + * + * @param syncContext the ISyncContext used to indicate the progress of the sync. When + * the sync is finished (successfully or not) ISyncContext.onFinished() must be called. + * @param authority the authority that should be synced + * @param account the account that should be synced + * @param extras SyncAdapter-specific parameters + */ + void startSync(ISyncContext syncContext, String authority, + in Account account, in Bundle extras); + + /** + * Cancel the most recently initiated sync. Due to race conditions, this may arrive + * after the ISyncContext.onFinished() for that sync was called. + * @param syncContext the ISyncContext that was passed to {@link #startSync} + */ + void cancelSync(ISyncContext syncContext); + + /** + * Initialize the SyncAdapter for this account and authority. + * + * @param account the account that should be synced + * @param authority the authority that should be synced + */ + void initialize(in Account account, String authority); +} diff --git a/Bcore/src/main/aidl/android/content/ISyncContext.aidl b/Bcore/src/main/aidl/android/content/ISyncContext.aidl new file mode 100644 index 00000000..6d18a1ce --- /dev/null +++ b/Bcore/src/main/aidl/android/content/ISyncContext.aidl @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.content; + +import android.content.SyncResult; + +/** + * Interface used by the SyncAdapter to indicate its progress. + * @hide + */ +interface ISyncContext { + /** + * Call to indicate that the SyncAdapter is making progress. E.g., if this SyncAdapter + * downloads or sends records to/from the server, this may be called after each record + * is downloaded or uploaded. + */ + void sendHeartbeat(); + + /** + * Signal that the corresponding sync session is completed. + * @param result information about this sync session + */ + void onFinished(in SyncResult result); +} diff --git a/Bcore/src/main/aidl/android/content/ISyncStatusObserver.aidl b/Bcore/src/main/aidl/android/content/ISyncStatusObserver.aidl new file mode 100644 index 00000000..88ea23a0 --- /dev/null +++ b/Bcore/src/main/aidl/android/content/ISyncStatusObserver.aidl @@ -0,0 +1,6 @@ +package android.content; + + +interface ISyncStatusObserver { + void onStatusChanged(int which); +} diff --git a/Bcore/src/main/aidl/android/content/SyncStatusInfo.aidl b/Bcore/src/main/aidl/android/content/SyncStatusInfo.aidl new file mode 100644 index 00000000..fe0293c9 --- /dev/null +++ b/Bcore/src/main/aidl/android/content/SyncStatusInfo.aidl @@ -0,0 +1,3 @@ +package android.content; + +parcelable SyncStatusInfo; \ No newline at end of file diff --git a/Bcore/src/main/aidl/android/content/pm/IPackageDataObserver.aidl b/Bcore/src/main/aidl/android/content/pm/IPackageDataObserver.aidl new file mode 100644 index 00000000..118ba5a0 --- /dev/null +++ b/Bcore/src/main/aidl/android/content/pm/IPackageDataObserver.aidl @@ -0,0 +1,10 @@ +package android.content.pm; + +/** + * API for package data change related callbacks from the Package Manager. + * Some usage scenarios include deletion of cache directory, generate + * statistics related to code, data, cache usage(TODO) + */ +interface IPackageDataObserver { + void onRemoveCompleted(in String packageName, boolean succeeded); +} diff --git a/Bcore/src/main/aidl/android/content/pm/IPackageDeleteObserver2.aidl b/Bcore/src/main/aidl/android/content/pm/IPackageDeleteObserver2.aidl new file mode 100644 index 00000000..8ab0c5cd --- /dev/null +++ b/Bcore/src/main/aidl/android/content/pm/IPackageDeleteObserver2.aidl @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.content.pm; + +import android.content.Intent; + +interface IPackageDeleteObserver2 { + void onUserActionRequired(in Intent intent); + void onPackageDeleted(String packageName, int returnCode, String msg); +} diff --git a/Bcore/src/main/aidl/android/content/pm/IPackageInstallObserver.aidl b/Bcore/src/main/aidl/android/content/pm/IPackageInstallObserver.aidl new file mode 100644 index 00000000..4ec7d4bf --- /dev/null +++ b/Bcore/src/main/aidl/android/content/pm/IPackageInstallObserver.aidl @@ -0,0 +1,9 @@ +package android.content.pm; + +/** + * API for installation callbacks from the Package Manager. + */ +interface IPackageInstallObserver { + void packageInstalled(in String packageName, int returnCode); +} + diff --git a/Bcore/src/main/aidl/android/content/pm/IPackageInstallObserver2.aidl b/Bcore/src/main/aidl/android/content/pm/IPackageInstallObserver2.aidl new file mode 100644 index 00000000..6894622b --- /dev/null +++ b/Bcore/src/main/aidl/android/content/pm/IPackageInstallObserver2.aidl @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.content.pm; + +import android.content.Intent; +import android.os.Bundle; + +/** + * API for installation callbacks from the Package Manager. In certain result cases + * additional information will be provided. + */ +interface IPackageInstallObserver2 { + void onUserActionRequired(in Intent intent); + + /** + * The install operation has completed. {@code returnCode} holds a numeric code + * indicating success or failure. In certain cases the {@code extras} Bundle will + * contain additional details: + * + *

+ * + * + * + * + *
INSTALL_FAILED_DUPLICATE_PERMISSIONTwo strings are provided in the extras bundle: EXTRA_EXISTING_PERMISSION + * is the name of the permission that the app is attempting to define, and + * EXTRA_EXISTING_PACKAGE is the package name of the app which has already + * defined the permission.
+ */ + void onPackageInstalled(String basePackageName, int returnCode, String msg, in Bundle extras); +} diff --git a/Bcore/src/main/aidl/android/content/pm/IPackageInstallerCallback.aidl b/Bcore/src/main/aidl/android/content/pm/IPackageInstallerCallback.aidl new file mode 100644 index 00000000..14b0d36b --- /dev/null +++ b/Bcore/src/main/aidl/android/content/pm/IPackageInstallerCallback.aidl @@ -0,0 +1,9 @@ +package android.content.pm; + +interface IPackageInstallerCallback { + void onSessionCreated(int sessionId); + void onSessionBadgingChanged(int sessionId); + void onSessionActiveChanged(int sessionId, boolean active); + void onSessionProgressChanged(int sessionId, float progress); + void onSessionFinished(int sessionId, boolean success); +} diff --git a/Bcore/src/main/aidl/android/content/pm/IPackageInstallerSession.aidl b/Bcore/src/main/aidl/android/content/pm/IPackageInstallerSession.aidl new file mode 100644 index 00000000..c2c70fe7 --- /dev/null +++ b/Bcore/src/main/aidl/android/content/pm/IPackageInstallerSession.aidl @@ -0,0 +1,20 @@ +package android.content.pm; + +import android.content.pm.IPackageInstallObserver2; +import android.content.IntentSender; +import android.os.ParcelFileDescriptor; + +interface IPackageInstallerSession { + void setClientProgress(float progress); + void addClientProgress(float progress); + + String[] getNames(); + ParcelFileDescriptor openWrite(String name, long offsetBytes, long lengthBytes); + ParcelFileDescriptor openRead(String name); + + void removeSplit(String splitName); + + void close(); + void commit(in IntentSender statusReceiver); + void abandon(); +} diff --git a/Bcore/src/main/aidl/android/database/IContentObserver.aidl b/Bcore/src/main/aidl/android/database/IContentObserver.aidl new file mode 100644 index 00000000..e395dc75 --- /dev/null +++ b/Bcore/src/main/aidl/android/database/IContentObserver.aidl @@ -0,0 +1,13 @@ +package android.database; + +import android.net.Uri; + +interface IContentObserver +{ + /** + * This method is called when an update occurs to the cursor that is being + * observed. selfUpdate is true if the update was caused by a call to + * commit on the cursor that is being observed. + */ + void onChange(boolean selfUpdate, in Uri uri, int userId); +} diff --git a/Bcore/src/main/aidl/android/location/ILocationListener.aidl b/Bcore/src/main/aidl/android/location/ILocationListener.aidl new file mode 100644 index 00000000..1571ae26 --- /dev/null +++ b/Bcore/src/main/aidl/android/location/ILocationListener.aidl @@ -0,0 +1,13 @@ +// ILocationListener.aidl +package android.location; + +import android.location.Location; +import android.os.Bundle; + +interface ILocationListener +{ + void onLocationChanged(in Location location); + void onStatusChanged(String provider, int status, in Bundle extras); + void onProviderEnabled(String provider); + void onProviderDisabled(String provider); +} \ No newline at end of file diff --git a/Bcore/src/main/aidl/android/net/IConnectivityManager.aidl b/Bcore/src/main/aidl/android/net/IConnectivityManager.aidl new file mode 100644 index 00000000..c26314e5 --- /dev/null +++ b/Bcore/src/main/aidl/android/net/IConnectivityManager.aidl @@ -0,0 +1,18 @@ +package android.net; + +import android.net.NetworkInfo; +import android.net.LinkProperties; + +interface IConnectivityManager { + + NetworkInfo getActiveNetworkInfo(); + NetworkInfo getActiveNetworkInfoForUid(int uid, boolean ignoreBlocked); + + NetworkInfo getNetworkInfo(int networkType); + NetworkInfo[] getAllNetworkInfo(); + boolean isActiveNetworkMetered(); + boolean requestRouteToHostAddress(int networkType, int address); + LinkProperties getActiveLinkProperties(); + LinkProperties getLinkProperties(int networkType); + +} \ No newline at end of file diff --git a/Bcore/src/main/aidl/android/net/wifi/IWifiScanner.aidl b/Bcore/src/main/aidl/android/net/wifi/IWifiScanner.aidl new file mode 100644 index 00000000..3e71a5f8 --- /dev/null +++ b/Bcore/src/main/aidl/android/net/wifi/IWifiScanner.aidl @@ -0,0 +1,11 @@ +package android.net.wifi; + +import android.os.Messenger; +import android.os.Bundle; + +interface IWifiScanner +{ + Messenger getMessenger(); + + Bundle getAvailableChannels(int band); +} diff --git a/Bcore/src/main/aidl/android/os/ISystemUpdateManager.aidl b/Bcore/src/main/aidl/android/os/ISystemUpdateManager.aidl new file mode 100644 index 00000000..cfce2106 --- /dev/null +++ b/Bcore/src/main/aidl/android/os/ISystemUpdateManager.aidl @@ -0,0 +1,9 @@ +package android.os; + +import android.os.Bundle; +import android.os.PersistableBundle; + +interface ISystemUpdateManager { + Bundle retrieveSystemUpdateInfo(); + void updateSystemUpdateInfo(in PersistableBundle data); +} \ No newline at end of file diff --git a/Bcore/src/main/aidl/com/android/internal/widget/ILockSettings.aidl b/Bcore/src/main/aidl/com/android/internal/widget/ILockSettings.aidl new file mode 100644 index 00000000..7c725f89 --- /dev/null +++ b/Bcore/src/main/aidl/com/android/internal/widget/ILockSettings.aidl @@ -0,0 +1,6 @@ +package com.android.internal.widget; + +interface ILockSettings { + void setRecoverySecretTypes(in int[] secretTypes); + int[] getRecoverySecretTypes(); +} \ No newline at end of file diff --git a/Bcore/src/main/aidl/top/niunaijun/blackbox/core/IBActivityThread.aidl b/Bcore/src/main/aidl/top/niunaijun/blackbox/core/IBActivityThread.aidl new file mode 100644 index 00000000..bc379998 --- /dev/null +++ b/Bcore/src/main/aidl/top/niunaijun/blackbox/core/IBActivityThread.aidl @@ -0,0 +1,15 @@ +// IBActivityThread.aidl +package top.niunaijun.blackbox.core; + +// Declare any non-default types here with import statements + +import android.os.IBinder; +import android.content.ComponentName; +import android.content.Intent; +import java.util.List; +import android.content.pm.ResolveInfo; + +interface IBActivityThread { + IBinder getActivityThread(); + void bindApplication(); +} diff --git a/Bcore/src/main/aidl/top/niunaijun/blackbox/core/system/am/IBActivityManagerService.aidl b/Bcore/src/main/aidl/top/niunaijun/blackbox/core/system/am/IBActivityManagerService.aidl new file mode 100644 index 00000000..19502048 --- /dev/null +++ b/Bcore/src/main/aidl/top/niunaijun/blackbox/core/system/am/IBActivityManagerService.aidl @@ -0,0 +1,39 @@ +// IBActivityManagerService.aidl +package top.niunaijun.blackbox.core.system.am; + +import android.content.Intent; +import android.content.ComponentName; +import android.content.pm.ServiceInfo; +import android.content.pm.ProviderInfo; +import android.os.IBinder; +import java.lang.String; +import android.app.IServiceConnection; +import top.niunaijun.blackbox.entity.AppConfig; +import android.os.Bundle; + +// Declare any non-default types here with import statements + +interface IBActivityManagerService { + AppConfig initProcess(String packageName, String processName, int userId); + void restartProcess(String packageName, String processName, int userId); + + void startActivity(in Intent intent, int userId); + int startActivityAms(int userId, in Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode, int flags, in Bundle options); + int startActivities(int userId, in Intent[] intent, in String[] resolvedType, IBinder resultTo, in Bundle options); + + ComponentName startService(in Intent intent, String resolvedType, int userId); + int stopService(in Intent intent,in String resolvedType, int userId); + + Intent bindService(in Intent service, in IBinder binder, String resolvedType, int userId); + void unbindService(in IBinder binder, int userId); + + void onStartCommand(in Intent proxyIntent, int userId); + void onServiceDestroy(in Intent proxyIntent, int userId); + + Intent sendBroadcast(in Intent intent, String resolvedType, int userId); + + void onActivityCreated(int taskId, IBinder token, IBinder activityRecord); + void onActivityResumed(IBinder token); + void onActivityDestroyed(IBinder token); + void onFinishActivity(IBinder token); +} diff --git a/Bcore/src/main/aidl/top/niunaijun/blackbox/core/system/os/IBStorageManagerService.aidl b/Bcore/src/main/aidl/top/niunaijun/blackbox/core/system/os/IBStorageManagerService.aidl new file mode 100644 index 00000000..275c20ee --- /dev/null +++ b/Bcore/src/main/aidl/top/niunaijun/blackbox/core/system/os/IBStorageManagerService.aidl @@ -0,0 +1,12 @@ +// IBStorageManagerService.aidl +package top.niunaijun.blackbox.core.system.os; + +import android.os.storage.StorageVolume; +import java.lang.String; +import android.net.Uri; + +// Declare any non-default types here with import statements + +interface IBStorageManagerService { + StorageVolume[] getVolumeList(int uid, String packageName, int flags, int userId); +} diff --git a/Bcore/src/main/aidl/top/niunaijun/blackbox/core/system/pm/BPackageSettings.aidl b/Bcore/src/main/aidl/top/niunaijun/blackbox/core/system/pm/BPackageSettings.aidl new file mode 100644 index 00000000..fd330248 --- /dev/null +++ b/Bcore/src/main/aidl/top/niunaijun/blackbox/core/system/pm/BPackageSettings.aidl @@ -0,0 +1,6 @@ +// BPackageSettings.aidl +package top.niunaijun.blackbox.core.system.pm; + +// Declare any non-default types here with import statements + +parcelable BPackageSettings; \ No newline at end of file diff --git a/Bcore/src/main/aidl/top/niunaijun/blackbox/core/system/pm/IBPackageInstallerService.aidl b/Bcore/src/main/aidl/top/niunaijun/blackbox/core/system/pm/IBPackageInstallerService.aidl new file mode 100644 index 00000000..cc4e3336 --- /dev/null +++ b/Bcore/src/main/aidl/top/niunaijun/blackbox/core/system/pm/IBPackageInstallerService.aidl @@ -0,0 +1,13 @@ +// IBPackageInstallerService.aidl +package top.niunaijun.blackbox.core.system.pm; + +import top.niunaijun.blackbox.core.system.pm.BPackageSettings; +import top.niunaijun.blackbox.entity.pm.InstallOption; + +// Declare any non-default types here with import statements + +interface IBPackageInstallerService { + int installPackageAsUser(in BPackageSettings file, int userId); + int uninstallPackageAsUser(in BPackageSettings file, boolean removeApp, int userId); + int updatePackage(in BPackageSettings file); +} diff --git a/Bcore/src/main/aidl/top/niunaijun/blackbox/core/system/pm/IBPackageManagerService.aidl b/Bcore/src/main/aidl/top/niunaijun/blackbox/core/system/pm/IBPackageManagerService.aidl new file mode 100644 index 00000000..1ad735a0 --- /dev/null +++ b/Bcore/src/main/aidl/top/niunaijun/blackbox/core/system/pm/IBPackageManagerService.aidl @@ -0,0 +1,45 @@ +// IBPackageManagerService.aidl +package top.niunaijun.blackbox.core.system.pm; + +// Declare any non-default types here with import statements +import android.content.pm.ApplicationInfo; +import android.content.pm.ResolveInfo; +import android.content.pm.PackageInfo; +import android.content.pm.ServiceInfo; +import android.content.pm.ActivityInfo; +import android.content.pm.ProviderInfo; +import android.content.Intent; +import android.content.ComponentName; +import java.util.List; +import top.niunaijun.blackbox.entity.pm.InstallResult; +import top.niunaijun.blackbox.entity.pm.InstallOption; +import top.niunaijun.blackbox.entity.pm.InstalledPackage; + + +interface IBPackageManagerService { + ResolveInfo resolveService(in Intent intent, int flags, String resolvedType, int userId); + ResolveInfo resolveActivity(in Intent intent, int flags, String resolvedType, int userId); + ProviderInfo resolveContentProvider(String authority, int flag, int userId); + ResolveInfo resolveIntent(in Intent intent, String resolvedType, int flags, int userId); + + ApplicationInfo getApplicationInfo(String packageName, int flags, int userId); + PackageInfo getPackageInfo(String packageName, int flags, int userId); + ServiceInfo getServiceInfo(in ComponentName component, int flags, int userId); + ActivityInfo getReceiverInfo(in ComponentName componentName, int flags, int userId); + ActivityInfo getActivityInfo(in ComponentName component, int flags, int userId); + ProviderInfo getProviderInfo(in ComponentName component, int flags, int userId); + List getInstalledApplications(int flags, int userId); + List getInstalledPackages(int flags, int userId); + + List queryIntentActivities(in Intent intent, int flags, String resolvedType, int userId); + List queryBroadcastReceivers(in Intent intent, int flags, String resolvedType, int userId); + List queryContentProviders(String processName, int uid, int flags, int userId); + + InstallResult installPackageAsUser(String file, in InstallOption option, int userId); + void uninstallPackageAsUser(String packageName, int userId); + void uninstallPackage(String packageName); + void deleteUser(int userId); + + boolean isInstalled(String packageName, int userId); + List getInstalledPackagesAsUser(int userId); +} diff --git a/Bcore/src/main/aidl/top/niunaijun/blackbox/core/system/user/BUserInfo.aidl b/Bcore/src/main/aidl/top/niunaijun/blackbox/core/system/user/BUserInfo.aidl new file mode 100644 index 00000000..573c98d3 --- /dev/null +++ b/Bcore/src/main/aidl/top/niunaijun/blackbox/core/system/user/BUserInfo.aidl @@ -0,0 +1,5 @@ +// BUserInfo.aidl +package top.niunaijun.blackbox.core.system.user; + +// Declare any non-default types here with import statements +parcelable BUserInfo; diff --git a/Bcore/src/main/aidl/top/niunaijun/blackbox/core/system/user/IBUserManagerService.aidl b/Bcore/src/main/aidl/top/niunaijun/blackbox/core/system/user/IBUserManagerService.aidl new file mode 100644 index 00000000..896e8658 --- /dev/null +++ b/Bcore/src/main/aidl/top/niunaijun/blackbox/core/system/user/IBUserManagerService.aidl @@ -0,0 +1,15 @@ +// IBUserManagerService.aidl +package top.niunaijun.blackbox.core.system.user; + +// Declare any non-default types here with import statements +import top.niunaijun.blackbox.core.system.user.BUserInfo; +import java.util.List; + + +interface IBUserManagerService { + BUserInfo getUserInfo(int userId); + boolean exists(int userId); + BUserInfo createUser(int userId); + List getUsers(); + void deleteUser(int userId); +} diff --git a/Bcore/src/main/aidl/top/niunaijun/blackbox/entity/AppConfig.aidl b/Bcore/src/main/aidl/top/niunaijun/blackbox/entity/AppConfig.aidl new file mode 100644 index 00000000..aac73d99 --- /dev/null +++ b/Bcore/src/main/aidl/top/niunaijun/blackbox/entity/AppConfig.aidl @@ -0,0 +1,6 @@ +// AppConfig.aidl +package top.niunaijun.blackbox.entity; + +// Declare any non-default types here with import statements + +parcelable AppConfig; \ No newline at end of file diff --git a/Bcore/src/main/aidl/top/niunaijun/blackbox/entity/pm/InstallOption.aidl b/Bcore/src/main/aidl/top/niunaijun/blackbox/entity/pm/InstallOption.aidl new file mode 100644 index 00000000..ab8cacb1 --- /dev/null +++ b/Bcore/src/main/aidl/top/niunaijun/blackbox/entity/pm/InstallOption.aidl @@ -0,0 +1,3 @@ +package top.niunaijun.blackbox.entity.pm; + +parcelable InstallOption; \ No newline at end of file diff --git a/Bcore/src/main/aidl/top/niunaijun/blackbox/entity/pm/InstallResult.aidl b/Bcore/src/main/aidl/top/niunaijun/blackbox/entity/pm/InstallResult.aidl new file mode 100644 index 00000000..bb15a61e --- /dev/null +++ b/Bcore/src/main/aidl/top/niunaijun/blackbox/entity/pm/InstallResult.aidl @@ -0,0 +1,3 @@ +package top.niunaijun.blackbox.entity.pm; + +parcelable InstallResult; \ No newline at end of file diff --git a/Bcore/src/main/aidl/top/niunaijun/blackbox/entity/pm/InstalledModule.aidl b/Bcore/src/main/aidl/top/niunaijun/blackbox/entity/pm/InstalledModule.aidl new file mode 100644 index 00000000..1fd84cb4 --- /dev/null +++ b/Bcore/src/main/aidl/top/niunaijun/blackbox/entity/pm/InstalledModule.aidl @@ -0,0 +1,3 @@ +package top.niunaijun.blackbox.entity.pm; + +parcelable InstalledModule; \ No newline at end of file diff --git a/Bcore/src/main/aidl/top/niunaijun/blackbox/entity/pm/InstalledPackage.aidl b/Bcore/src/main/aidl/top/niunaijun/blackbox/entity/pm/InstalledPackage.aidl new file mode 100644 index 00000000..b66321fb --- /dev/null +++ b/Bcore/src/main/aidl/top/niunaijun/blackbox/entity/pm/InstalledPackage.aidl @@ -0,0 +1,3 @@ +package top.niunaijun.blackbox.entity.pm; + +parcelable InstalledPackage; \ No newline at end of file diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/BlackBoxCore.java b/Bcore/src/main/java/top/niunaijun/blackbox/BlackBoxCore.java new file mode 100644 index 00000000..c5347128 --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/BlackBoxCore.java @@ -0,0 +1,276 @@ +package top.niunaijun.blackbox; + +import android.annotation.SuppressLint; +import android.app.ActivityManager; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.net.Uri; +import android.os.Build; +import android.os.Bundle; +import android.os.Environment; +import android.os.IBinder; +import android.os.Process; + +import top.niunaijun.blackbox.app.configuration.ClientConfiguration; +import top.niunaijun.blackbox.proxy.ProxyManifest; +import top.niunaijun.blackbox.app.configuration.AppLifecycleCallback; +import top.niunaijun.blackbox.fake.hook.HookManager; +import top.niunaijun.blackbox.entity.pm.InstallOption; +import top.niunaijun.blackbox.entity.pm.InstallResult; +import top.niunaijun.blackbox.core.system.DaemonService; +import top.niunaijun.blackbox.utils.FileUtils; +import top.niunaijun.blackbox.utils.ShellUtils; +import top.niunaijun.blackbox.utils.compat.BuildCompat; +import top.niunaijun.blackbox.utils.compat.BundleCompat; +import top.niunaijun.blackbox.utils.provider.ProviderCall; +import top.niunaijun.blackbox.fake.frameworks.BActivityManager; +import top.niunaijun.blackbox.fake.frameworks.BPackageManager; + +import java.io.File; +import java.util.HashMap; +import java.util.Map; + +import me.weishu.reflection.Reflection; +import mirror.android.app.ActivityThread; +import top.niunaijun.blackbox.fake.frameworks.BStorageManager; +import top.niunaijun.blackbox.core.system.ServiceManager; + +/** + * Created by Milk on 3/30/21. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + */ +@SuppressLint("StaticFieldLeak") +public class BlackBoxCore extends ClientConfiguration { + public static final String TAG = "BlackBoxCore"; + private static final int USER_ID = 0; + + private static final BlackBoxCore sBlackBoxCore = new BlackBoxCore(); + private static Context sContext; + private ProcessType mProcessType; + private final Map mServices = new HashMap<>(); + private ClientConfiguration mClientConfiguration; + private AppLifecycleCallback mAppLifecycleCallback = AppLifecycleCallback.EMPTY; + + public static BlackBoxCore get() { + return sBlackBoxCore; + } + + public static PackageManager getPackageManager() { + return sContext.getPackageManager(); + } + + public static String getHostPkg() { + return get().getHostPackageName(); + } + + public static Context getContext() { + return sContext; + } + + public void doAttachBaseContext(Context context, ClientConfiguration clientConfiguration) { + if (clientConfiguration == null) { + throw new IllegalArgumentException("ClientConfiguration is null!"); + } + Reflection.unseal(context); + sContext = context; + mClientConfiguration = clientConfiguration; + mClientConfiguration.init(); + String processName = getProcessName(getContext()); + if (processName.equals(BlackBoxCore.getHostPkg())) { + mProcessType = ProcessType.Main; + startLogcat(); + } else if (processName.endsWith(getContext().getString(R.string.black_box_service_name))) { + mProcessType = ProcessType.Server; + } else { + mProcessType = ProcessType.BAppClient; + } + if (BlackBoxCore.get().isVirtualProcess()) { + if (processName.endsWith("p0")) { +// android.os.Debug.waitForDebugger(); + } +// android.os.Debug.waitForDebugger(); + } + if (isServerProcess()) { + Intent intent = new Intent(); + intent.setClass(getContext(), DaemonService.class); + if (BuildCompat.isOreo()) { + getContext().startForegroundService(intent); + } else { + getContext().startService(intent); + } + } + HookManager.get().init(); + } + + public void doCreate() { + // fix contentProvider + if (isVirtualProcess()) { + } + if (!isServerProcess()) { + initService(); + } + } + + private void initService() { + get().getService(ServiceManager.ACTIVITY_MANAGER); + get().getService(ServiceManager.PACKAGE_MANAGER); + get().getService(ServiceManager.STORAGE_MANAGER); + } + + public static Object mainThread() { + return ActivityThread.currentActivityThread.call(); + } + + public void startActivity(Intent intent, int userId) { + getBActivityManager().startActivity(intent, userId); + } + + public static BPackageManager getBPackageManager() { + return BPackageManager.get(); + } + + public static BActivityManager getBActivityManager() { + return BActivityManager.get(); + } + + public static BStorageManager getBStorageManager() { + return BStorageManager.get(); + } + + public boolean launchApk(String packageName) { + Intent launchIntentForPackage = getBPackageManager().getLaunchIntentForPackage(packageName, USER_ID); + if (launchIntentForPackage == null) { + return false; + } + startActivity(launchIntentForPackage, USER_ID); + return true; + } + + public boolean isInstalled(String packageName) { + return getBPackageManager().isInstalled(packageName, USER_ID); + } + + public void uninstallPackage(String packageName) { + getBPackageManager().uninstallPackageAsUser(packageName, USER_ID); + } + + public InstallResult installPackage(String packageName) { + try { + PackageInfo packageInfo = getPackageManager().getPackageInfo(packageName, 0); + return getBPackageManager().installPackageAsUser(packageInfo.applicationInfo.sourceDir, InstallOption.installBySystem(), USER_ID); + } catch (PackageManager.NameNotFoundException e) { + e.printStackTrace(); + return new InstallResult().installError(e.getMessage()); + } + } + + public InstallResult installPackage(File apk) { + return getBPackageManager().installPackageAsUser(apk.getAbsolutePath(), InstallOption.installByStorage(), USER_ID); + } + + public InstallResult installPackage(Uri apk) { + return getBPackageManager().installPackageAsUser(apk.toString(), InstallOption.installByStorage().makeUriFile(), USER_ID); + } + + public AppLifecycleCallback getAppLifecycleCallback() { + return mAppLifecycleCallback; + } + + public void setAppLifecycleCallback(AppLifecycleCallback appLifecycleCallback) { + if (appLifecycleCallback == null) { + throw new IllegalArgumentException("AppLifecycleCallback is null!"); + } + mAppLifecycleCallback = appLifecycleCallback; + } + + public IBinder getService(String name) { + IBinder binder = mServices.get(name); + if (binder != null && binder.isBinderAlive()) { + return binder; + } + Bundle bundle = new Bundle(); + bundle.putString("_VM_|_server_name_", name); + Bundle vm = ProviderCall.callSafely(ProxyManifest.getBindProvider(), "VM", null, bundle); + assert vm != null; + binder = BundleCompat.getBinder(vm, "_VM_|_server_"); + mServices.put(name, binder); + return binder; + } + + /** + * Process type + */ + private enum ProcessType { + /** + * Server process + */ + Server, + /** + * Virtual app process + */ + BAppClient, + /** + * Main process + */ + Main, + } + + public boolean isVirtualProcess() { + return mProcessType == ProcessType.BAppClient; + } + + public boolean isMainProcess() { + return mProcessType == ProcessType.Main; + } + + public boolean isServerProcess() { + return mProcessType == ProcessType.Server; + } + + @Override + public String getHostPackageName() { + return mClientConfiguration.getHostPackageName(); + } + + @Override + public String getDexDumpDir() { + return mClientConfiguration.getDexDumpDir(); + } + + private void startLogcat() { + File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), getContext().getPackageName() + "_logcat.txt"); + FileUtils.deleteDir(file); + ShellUtils.execCommand("logcat -c", false); + ShellUtils.execCommand("logcat >> " + file.getAbsolutePath() + " &", false); + } + + private static String getProcessName(Context context) { + int pid = Process.myPid(); + String processName = null; + ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); + for (ActivityManager.RunningAppProcessInfo info : am.getRunningAppProcesses()) { + if (info.pid == pid) { + processName = info.processName; + break; + } + } + if (processName == null) { + throw new RuntimeException("processName = null"); + } + return processName; + } + + public static boolean is64Bit() { + if (BuildCompat.isM()) { + return Process.is64Bit(); + } else { + return Build.CPU_ABI.equals("arm64-v8a"); + } + } +} diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/app/BActivityThread.java b/Bcore/src/main/java/top/niunaijun/blackbox/app/BActivityThread.java new file mode 100644 index 00000000..3b459215 --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/app/BActivityThread.java @@ -0,0 +1,222 @@ +package top.niunaijun.blackbox.app; + +import android.app.Application; +import android.app.Instrumentation; +import android.content.ComponentName; +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.ProviderInfo; +import android.os.Build; +import android.os.ConditionVariable; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; + +import mirror.android.app.ActivityThread; +import mirror.android.app.ContextImpl; +import mirror.android.app.LoadedApk; +import top.niunaijun.blackbox.core.IBActivityThread; +import top.niunaijun.blackbox.core.VMCore; +import top.niunaijun.blackbox.entity.AppConfig; +import top.niunaijun.blackbox.core.IOCore; +import top.niunaijun.blackbox.utils.compat.ContextCompat; +import top.niunaijun.blackbox.BlackBoxCore; + +/** + * Created by Milk on 3/31/21. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + */ +public class BActivityThread extends IBActivityThread.Stub { + public static final String TAG = "BActivityThread"; + + private static BActivityThread sBActivityThread; + private AppBindData mBoundApplication; + private Application mInitialApplication; + private AppConfig mAppConfig; + private final List mProviders = new ArrayList<>(); + + public static BActivityThread currentActivityThread() { + if (sBActivityThread == null) { + synchronized (BActivityThread.class) { + if (sBActivityThread == null) { + sBActivityThread = new BActivityThread(); + } + } + } + return sBActivityThread; + } + + public static synchronized AppConfig getAppConfig() { + return currentActivityThread().mAppConfig; + } + + public static List getProviders() { + return currentActivityThread().mProviders; + } + + public static String getAppProcessName() { + if (getAppConfig() != null) { + return getAppConfig().processName; + } else if (currentActivityThread().mBoundApplication != null) { + return currentActivityThread().mBoundApplication.processName; + } else { + return null; + } + } + + public static String getAppPackageName() { + if (getAppConfig() != null) { + return getAppConfig().packageName; + } else if (currentActivityThread().mInitialApplication != null) { + return currentActivityThread().mInitialApplication.getPackageName(); + } else { + return null; + } + } + + public static Application getApplication() { + return currentActivityThread().mInitialApplication; + } + + public static int getAppPid() { + return getAppConfig() == null ? -1 : getAppConfig().bpid; + } + + public static int getAppUid() { + return getAppConfig() == null ? 10000 : getAppConfig().buid; + } + + public static int getBaseAppUid() { + return getAppConfig() == null ? 10000 : getAppConfig().baseBUid; + } + + public static int getUid() { + return getAppConfig() == null ? -1 : getAppConfig().uid; + } + + public static int getUserId() { + return getAppConfig() == null ? 0 : getAppConfig().userId; + } + + public void initProcess(AppConfig appConfig) { + if (this.mAppConfig != null) { + throw new RuntimeException("reject init process: " + appConfig.processName + ", this process is : " + this.mAppConfig.processName); + } + this.mAppConfig = appConfig; + } + + public boolean isInit() { + return mBoundApplication != null; + } + + public void bindApplication(final String packageName, final String processName) { + if (Looper.myLooper() != Looper.getMainLooper()) { + final ConditionVariable conditionVariable = new ConditionVariable(); + new Handler(Looper.getMainLooper()).post(() -> { + handleBindApplication(packageName, processName); + conditionVariable.open(); + }); + conditionVariable.block(); + } else { + handleBindApplication(packageName, processName); + } + } + + public synchronized void handleBindApplication(String packageName, String processName) { + PackageInfo packageInfo = BlackBoxCore.getBPackageManager().getPackageInfo(packageName, PackageManager.GET_PROVIDERS, BActivityThread.getUserId()); + if (packageInfo == null) + return; + ApplicationInfo applicationInfo = packageInfo.applicationInfo; + if (packageInfo.providers == null) { + packageInfo.providers = new ProviderInfo[]{}; + } + mProviders.addAll(Arrays.asList(packageInfo.providers)); + + Object boundApplication = ActivityThread.mBoundApplication.get(BlackBoxCore.mainThread()); + + Context packageContext = createPackageContext(applicationInfo); + Object loadedApk = ContextImpl.mPackageInfo.get(packageContext); + LoadedApk.mSecurityViolation.set(loadedApk, false); + // fix applicationInfo + LoadedApk.mApplicationInfo.set(loadedApk, applicationInfo); + + VMCore.init(Build.VERSION.SDK_INT); + assert packageContext != null; + IOCore.get().enableRedirect(packageContext); + + AppBindData bindData = new AppBindData(); + bindData.appInfo = applicationInfo; + bindData.processName = processName; + bindData.info = loadedApk; + bindData.providers = mProviders; + + ActivityThread.AppBindData.instrumentationName.set(boundApplication, + new ComponentName(bindData.appInfo.packageName, Instrumentation.class.getName())); + ActivityThread.AppBindData.appInfo.set(boundApplication, bindData.appInfo); + ActivityThread.AppBindData.info.set(boundApplication, bindData.info); + ActivityThread.AppBindData.processName.set(boundApplication, bindData.processName); + ActivityThread.AppBindData.providers.set(boundApplication, bindData.providers); + + mBoundApplication = bindData; + + Application application; + BlackBoxCore.get().getAppLifecycleCallback().beforeCreateApplication(packageName, processName, packageContext); + try { + application = LoadedApk.makeApplication.call(loadedApk, false, null); + + mInitialApplication = application; + ActivityThread.mInitialApplication.set(BlackBoxCore.mainThread(), mInitialApplication); + ContextCompat.fix((Context) ActivityThread.getSystemContext.call(BlackBoxCore.mainThread())); + ContextCompat.fix(mInitialApplication); + + if (Objects.equals(packageName, processName)) { + VMCore.dumpDex(mInitialApplication.getClassLoader(), processName); + } + } catch (Throwable e) { + e.printStackTrace(); + throw new RuntimeException("Unable to makeApplication", e); + } finally { + BlackBoxCore.get().uninstallPackage(packageName); + } + } + + private Context createPackageContext(ApplicationInfo info) { + try { + return BlackBoxCore.getContext().createPackageContext(info.packageName, + Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY); + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + @Override + public IBinder getActivityThread() { + return ActivityThread.getApplicationThread.call(BlackBoxCore.mainThread()); + } + + @Override + public void bindApplication() { + if (!isInit()) { + bindApplication(getAppPackageName(), getAppProcessName()); + } + } + + public static class AppBindData { + String processName; + ApplicationInfo appInfo; + List providers; + Object info; + } +} diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/app/configuration/AppLifecycleCallback.java b/Bcore/src/main/java/top/niunaijun/blackbox/app/configuration/AppLifecycleCallback.java new file mode 100644 index 00000000..ebd5a3de --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/app/configuration/AppLifecycleCallback.java @@ -0,0 +1,30 @@ +package top.niunaijun.blackbox.app.configuration; + +import android.app.Application; +import android.content.Context; + +/** + * Created by Milk on 5/5/21. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + */ +public class AppLifecycleCallback { + public static AppLifecycleCallback EMPTY = new AppLifecycleCallback() { + + }; + + public void beforeCreateApplication(String packageName, String processName, Context context) { + + } + + public void beforeApplicationOnCreate(String packageName, String processName, Application application) { + + } + + public void afterApplicationOnCreate(String packageName, String processName, Application application) { + + } +} diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/app/configuration/ClientConfiguration.java b/Bcore/src/main/java/top/niunaijun/blackbox/app/configuration/ClientConfiguration.java new file mode 100644 index 00000000..36c2185e --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/app/configuration/ClientConfiguration.java @@ -0,0 +1,32 @@ +package top.niunaijun.blackbox.app.configuration; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +import top.niunaijun.blackbox.BlackBoxCore; +import top.niunaijun.blackbox.utils.FileUtils; + +/** + * Created by Milk on 5/4/21. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + */ +public abstract class ClientConfiguration { + private File mExternalFilesDir; + + public final void init() { + mExternalFilesDir = BlackBoxCore.getContext().getExternalCacheDir().getParentFile(); + } + + public abstract String getHostPackageName(); + + public String getDexDumpDir() { + File dump = new File(mExternalFilesDir, "dump"); + FileUtils.mkdirs(dump); + return dump.getAbsolutePath(); + } +} diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/core/IOCore.java b/Bcore/src/main/java/top/niunaijun/blackbox/core/IOCore.java new file mode 100644 index 00000000..d52ae305 --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/core/IOCore.java @@ -0,0 +1,140 @@ +package top.niunaijun.blackbox.core; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.text.TextUtils; + +import java.io.File; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Objects; + +import top.niunaijun.blackbox.BlackBoxCore; +import top.niunaijun.blackbox.app.BActivityThread; +import top.niunaijun.blackbox.utils.FileUtils; + +/** + * Created by Milk on 4/9/21. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + */ +@SuppressLint("SdCardPath") +public class IOCore { + private static final IOCore sIOCore = new IOCore(); + private final Map mRedirectMap = new LinkedHashMap<>(); + + private static final Map> sCachePackageRedirect = new HashMap<>(); + + public static IOCore get() { + return sIOCore; + } + + // /data/data/com.google/ -----> /data/data/com.virtual/data/com.google/ + public void addRedirect(String origPath, String redirectPath) { + if (TextUtils.isEmpty(origPath) || TextUtils.isEmpty(redirectPath) || mRedirectMap.get(origPath) != null) + return; + mRedirectMap.put(origPath, redirectPath); + File redirectFile = new File(redirectPath); + if (!redirectFile.exists()) { + FileUtils.mkdirs(redirectPath); + } + VMCore.addIORule(origPath, redirectPath); + } + + public String redirectPath(String path) { + if (TextUtils.isEmpty(path)) + return path; + for (String orig : mRedirectMap.keySet()) { + if (path.startsWith(orig)) { + path = path.replace(orig, Objects.requireNonNull(mRedirectMap.get(orig))); + break; + } + } + return path; + } + + public File redirectPath(File path) { + if (path == null) + return null; + String pathStr = path.getAbsolutePath(); + return new File(redirectPath(pathStr)); + } + + public String redirectPath(String path, Map rule) { + if (TextUtils.isEmpty(path)) + return path; + for (String orig : rule.keySet()) { + if (path.startsWith(orig)) { + path = path.replace(orig, Objects.requireNonNull(rule.get(orig))); + break; + } + } + return path; + } + + public File redirectPath(File path, Map rule) { + if (path == null) + return null; + String pathStr = path.getAbsolutePath(); + return new File(redirectPath(pathStr, rule)); + } + + // 由于正常情况Application已完成重定向,以下重定向是怕代码写死。 + public void enableRedirect(Context context) { + Map rule = new LinkedHashMap<>(); + String packageName = context.getPackageName(); + + try { + ApplicationInfo packageInfo = BlackBoxCore.getBPackageManager().getApplicationInfo(packageName, PackageManager.GET_META_DATA, BActivityThread.getUserId()); + rule.put("/data/data/" + packageName + "/lib", packageInfo.nativeLibraryDir); + rule.put("/data/user/0/" + packageName + "/lib", packageInfo.nativeLibraryDir); + + rule.put("/data/data/" + packageName, packageInfo.dataDir); + rule.put("/data/user/0/" + packageName, packageInfo.dataDir); + + if (BlackBoxCore.getContext().getExternalCacheDir() != null && context.getExternalCacheDir() != null) { + File external = context.getExternalCacheDir().getParentFile(); + + // sdcard + rule.put("/sdcard/Android/data/" + packageName, + external.getAbsolutePath()); + rule.put("/sdcard/android/data/" + packageName, external.getAbsolutePath()); + + rule.put("/storage/emulated/0/android/data/" + packageName, + external.getAbsolutePath()); + rule.put("/storage/emulated/0/Android/data/" + packageName, + external.getAbsolutePath()); + + rule.put("/storage/emulated/0/Android/data/" + packageName + "/files", + new File(external.getAbsolutePath(), "files").getAbsolutePath()); + rule.put("/storage/emulated/0/Android/data/" + packageName + "/cache", + new File(external.getAbsolutePath(), "cache").getAbsolutePath()); + } + } catch (Exception e) { + e.printStackTrace(); + } + for (String key : rule.keySet()) { + get().addRedirect(key, rule.get(key)); + } + VMCore.enableIO(); + } + + private void hideRoot(Map rule) { + rule.put("/system/app/Superuser.apk", "/system/app/Superuser.apk-fake"); + rule.put("/sbin/su", "/sbin/su-fake"); + rule.put("/system/bin/su", "/system/bin/su-fake"); + rule.put("/system/xbin/su", "/system/xbin/su-fake"); + rule.put("/data/local/xbin/su", "/data/local/xbin/su-fake"); + rule.put("/data/local/bin/su", "/data/local/bin/su-fake"); + rule.put("/system/sd/xbin/su", "/system/sd/xbin/su-fake"); + rule.put("/system/bin/failsafe/su", "/system/bin/failsafe/su-fake"); + rule.put("/data/local/su", "/data/local/su-fake"); + rule.put("/su/bin/su", "/su/bin/su-fake"); + } +} diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/core/VMCore.java b/Bcore/src/main/java/top/niunaijun/blackbox/core/VMCore.java new file mode 100644 index 00000000..bb980b40 --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/core/VMCore.java @@ -0,0 +1,101 @@ +package top.niunaijun.blackbox.core; + + +import androidx.annotation.Keep; + +import java.io.File; +import java.io.IOException; +import java.util.List; + +import dalvik.system.DexFile; +import top.niunaijun.blackbox.BlackBoxCore; +import top.niunaijun.blackbox.utils.FileUtils; +import top.niunaijun.blackbox.utils.Reflector; +import top.niunaijun.blackbox.utils.compat.DexFileCompat; + +import static top.niunaijun.blackbox.core.env.BEnvironment.EMPTY_JAR; + +/** + * Created by Milk on 4/9/21. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + */ +public class VMCore { + public static final String TAG = "VMCoreJava"; + + static { + new File(""); + if (BlackBoxCore.is64Bit()) { + try { + System.loadLibrary("vm64"); + } catch (Throwable e) { + System.loadLibrary("vm"); + } + } else { + System.loadLibrary("vm"); + } + } + + public static native void init(int apiLevel); + + public static native void enableIO(); + + public static native void addIORule(String targetPath, String relocatePath); + + public static native void hideXposed(); + + private static native void dumpDex(long cookie, String dir); + + public static void dumpDex(ClassLoader classLoader, String packageName) { + List cookies = DexFileCompat.getCookies(classLoader); + for (Long cookie : cookies) { + if (cookie == 0) + continue; + File file = new File(BlackBoxCore.get().getDexDumpDir(), packageName); + FileUtils.mkdirs(file); + dumpDex(cookie, file.getAbsolutePath()); + } + } + + @Keep + public static int getCallingUid(int origCallingUid) { +// if (origCallingUid > 0 && origCallingUid < Process.FIRST_APPLICATION_UID) +// return origCallingUid; +// // 非用户应用 +// if (origCallingUid > Process.LAST_APPLICATION_UID) +// return origCallingUid; +// +// Log.d(TAG, "origCallingUid: " + origCallingUid + " => " + BClient.getBaseVUid()); +// return BClient.getBaseVUid(); + return origCallingUid; + } + + @Keep + public static String redirectPath(String path) { + return IOCore.get().redirectPath(path); + } + + @Keep + public static File redirectPath(File path) { + return IOCore.get().redirectPath(path); + } + + @Keep + public static long[] loadEmptyDex() { + try { + DexFile dexFile = new DexFile(EMPTY_JAR); + List cookies = DexFileCompat.getCookies(dexFile); + long[] longs = new long[cookies.size()]; + for (int i = 0; i < cookies.size(); i++) { + longs[i] = cookies.get(i); + } + return longs; + } catch (Exception e) { + e.printStackTrace(); + } + return new long[]{}; + } +} diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/core/env/AppSystemEnv.java b/Bcore/src/main/java/top/niunaijun/blackbox/core/env/AppSystemEnv.java new file mode 100644 index 00000000..d7962af6 --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/core/env/AppSystemEnv.java @@ -0,0 +1,68 @@ +package top.niunaijun.blackbox.core.env; + +import android.content.ComponentName; + +import java.util.ArrayList; +import java.util.List; + +import top.niunaijun.blackbox.BlackBoxCore; + +/** + * Created by Milk on 4/21/21. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + */ +public class AppSystemEnv { + private static final List sSystemPackages = new ArrayList<>(); + private static final List sSuPackages = new ArrayList<>(); + private static final List sXposedPackages = new ArrayList<>(); + private static final List sPreInstallPackages = new ArrayList<>(); + + static { + sSystemPackages.add("android"); + sSystemPackages.add("com.google.android.webview"); + sSystemPackages.add("com.google.android.webview.dev"); + sSystemPackages.add("com.google.android.webview.beta"); + sSystemPackages.add("com.google.android.webview.canary"); + sSystemPackages.add("com.android.webview"); + sSystemPackages.add("com.android.camera"); + sSystemPackages.add(BlackBoxCore.getHostPkg()); + + // 华为 + sSystemPackages.add("com.huawei.webview"); + + // oppo + sSystemPackages.add("com.coloros.safecenter"); + + // su + sSuPackages.add("com.noshufou.android.su"); + sSuPackages.add("com.noshufou.android.su.elite"); + sSuPackages.add("eu.chainfire.supersu"); + sSuPackages.add("com.koushikdutta.superuser"); + sSuPackages.add("com.thirdparty.superuser"); + sSuPackages.add("com.yellowes.su"); + + sXposedPackages.add("de.robv.android.xposed.installer"); + + sPreInstallPackages.add("com.huawei.hwid"); + } + + public static boolean isOpenPackage(String packageName) { + return sSystemPackages.contains(packageName); + } + + public static boolean isOpenPackage(ComponentName componentName) { + return componentName != null && isOpenPackage(componentName.getPackageName()); + } + + public static boolean isBlackPackage(String packageName) { + return false; + } + + public static List getPreInstallPackages() { + return sPreInstallPackages; + } +} diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/core/env/BEnvironment.java b/Bcore/src/main/java/top/niunaijun/blackbox/core/env/BEnvironment.java new file mode 100644 index 00000000..f7dc913e --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/core/env/BEnvironment.java @@ -0,0 +1,123 @@ +package top.niunaijun.blackbox.core.env; + +import java.io.File; +import java.util.Locale; + +import top.niunaijun.blackbox.BlackBoxCore; +import top.niunaijun.blackbox.utils.FileUtils; + +/** + * Created by Milk on 4/22/21. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + */ +public class BEnvironment { + private static final File sVirtualRoot = new File(BlackBoxCore.getContext().getCacheDir().getParent(), "virtual"); + private static final File sExternalVirtualRoot = BlackBoxCore.getContext().getExternalFilesDir("virtual"); + + public static File JUNIT_JAR = new File(getCacheDir(), "junit.jar"); + public static File EMPTY_JAR = new File(getCacheDir(), "empty.jar"); + + public static void load() { + FileUtils.mkdirs(sVirtualRoot); + FileUtils.mkdirs(sExternalVirtualRoot); + FileUtils.mkdirs(getSystemDir()); + FileUtils.mkdirs(getCacheDir()); + } + + public static File getVirtualRoot() { + return sVirtualRoot; + } + + public static File getExternalVirtualRoot() { + return sExternalVirtualRoot; + } + + public static File getSystemDir() { + return new File(sVirtualRoot, "system"); + } + + public static File getCacheDir() { + return new File(sVirtualRoot, "cache"); + } + + public static File getUserInfoConf() { + return new File(getSystemDir(), "user.conf"); + } + + public static File getUidConf() { + return new File(getSystemDir(), "uid.conf"); + } + + public static File getXPModuleConf() { + return new File(getSystemDir(), "xposed-module.conf"); + } + + public static File getPackageConf(String packageName) { + return new File(getAppDir(packageName), "package.conf"); + } + + public static File getExternalUserDir(int userId) { + return new File(sExternalVirtualRoot, String.format(Locale.CHINA, "storage/emulated/%d/", userId)); + } + + public static File getUserDir(int userId) { + return new File(sVirtualRoot, String.format(Locale.CHINA, "data/user/%d", userId)); + } + + public static File getDeDataDir(String packageName, int userId) { + return new File(sVirtualRoot, String.format(Locale.CHINA, "data/user_de/%d/%s", userId, packageName)); + } + + public static File getExternalDataDir(String packageName, int userId) { + return new File(getExternalUserDir(userId), String.format(Locale.CHINA, "Android/data/%s", packageName)); + } + + + public static File getDataDir(String packageName, int userId) { + return new File(sVirtualRoot, String.format(Locale.CHINA, "data/user/%d/%s", userId, packageName)); + } + + public static File getExternalDataFilesDir(String packageName, int userId) { + return new File(getExternalDataDir(packageName, userId), "files"); + } + + public static File getDataFilesDir(String packageName, int userId) { + return new File(getDataDir(packageName, userId), "files"); + } + + public static File getExternalDataCacheDir(String packageName, int userId) { + return new File(getExternalDataDir(packageName, userId), "cache"); + } + + public static File getDataCacheDir(String packageName, int userId) { + return new File(getDataDir(packageName, userId), "cache"); + } + + public static File getDataLibDir(String packageName, int userId) { + return new File(getDataDir(packageName, userId), "lib"); + } + + public static File getDataDatabasesDir(String packageName, int userId) { + return new File(getDataDir(packageName, userId), "databases"); + } + + public static File getAppRootDir() { + return getAppDir(""); + } + + public static File getAppDir(String packageName) { + return new File(sVirtualRoot, "data/app/" + packageName); + } + + public static File getBaseApkDir(String packageName) { + return new File(sVirtualRoot, "data/app/" + packageName + "/base.apk"); + } + + public static File getAppLibDir(String packageName) { + return new File(getAppDir(packageName), "lib"); + } +} diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/core/system/BProcessManager.java b/Bcore/src/main/java/top/niunaijun/blackbox/core/system/BProcessManager.java new file mode 100644 index 00000000..2229f5fd --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/core/system/BProcessManager.java @@ -0,0 +1,293 @@ +package top.niunaijun.blackbox.core.system; + +import android.app.ActivityManager; +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.os.Binder; +import android.os.Bundle; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.Log; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import top.niunaijun.blackbox.BlackBoxCore; +import top.niunaijun.blackbox.entity.AppConfig; +import top.niunaijun.blackbox.proxy.ProxyManifest; +import top.niunaijun.blackbox.core.system.pm.BPackageManagerService; +import top.niunaijun.blackbox.core.system.user.BUserHandle; +import top.niunaijun.blackbox.utils.Slog; +import top.niunaijun.blackbox.utils.compat.ApplicationThreadCompat; +import top.niunaijun.blackbox.utils.compat.BundleCompat; +import top.niunaijun.blackbox.utils.provider.ProviderCall; +import top.niunaijun.blackbox.core.IBActivityThread; + +/** + * Created by Milk on 4/2/21. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + */ +public class BProcessManager { + public static final String TAG = "BProcessManager"; + + public static BProcessManager sVProcessManager = new BProcessManager(); + private final Map> mProcessMap = new HashMap<>(); + private final List mPidsSelfLocked = new ArrayList<>(); + private final Object mProcessLock = new Object(); + + public static BProcessManager get() { + return sVProcessManager; + } + + public ProcessRecord startProcessLocked(String packageName, String processName, int userId, int bpid, int callingUid, int callingPid) { + ApplicationInfo info = BPackageManagerService.get().getApplicationInfo(packageName, 0, userId); + if (info == null) + return null; + ProcessRecord app; + int buid = BUserHandle.getUid(userId, BPackageManagerService.get().getAppId(packageName)); + Map vProcess = mProcessMap.get(buid); + + if (vProcess == null) { + vProcess = new HashMap<>(); + } + synchronized (mProcessLock) { + if (bpid == -1) { + app = vProcess.get(processName); + if (app != null) { + if (app.initLock != null) { + app.initLock.block(); + } + if (app.bActivityThread != null) { + return app; + } + } + bpid = getUsingBPidL(); + Slog.d(TAG, "init bUid = " + buid + ", bPid = " + bpid); + } + if (bpid == -1) { + throw new RuntimeException("No processes available"); + } + app = new ProcessRecord(info, processName, 0, bpid, callingUid); + app.uid = buid; + app.buid = buid; + app.userId = userId; + app.baseBUid = BUserHandle.getAppId(info.uid); + + vProcess.put(processName, app); + mPidsSelfLocked.add(app); + + mProcessMap.put(app.buid, vProcess); + if (!initAppProcessL(app)) { + //init process fail + vProcess.remove(processName); + mPidsSelfLocked.remove(app); + app = null; + } else { + app.pid = getPid(BlackBoxCore.getContext(), ProxyManifest.getProcessName(app.bpid)); + } + } + return app; + } + + private int getUsingBPidL() { + ActivityManager manager = (ActivityManager) BlackBoxCore.getContext().getSystemService(Context.ACTIVITY_SERVICE); + List runningAppProcesses = manager.getRunningAppProcesses(); + for (int i = 0; i < ProxyManifest.FREE_COUNT; i++) { + boolean using = false; + for (ProcessRecord processRecord : mPidsSelfLocked) { + if (processRecord.bpid == i) { + using = true; + break; + } + } + if (using) + continue; + return i; + } + return -1; + } + + public void restartAppProcess(String packageName, String processName, int userId) { + synchronized (mProcessLock) { + int callingUid = Binder.getCallingUid(); + int callingPid = Binder.getCallingPid(); + ProcessRecord app; + synchronized (mProcessLock) { + app = findProcessByPid(callingPid); + } + if (app == null) { + String stubProcessName = getProcessName(BlackBoxCore.getContext(), callingPid); + int bpid = parseBPid(stubProcessName); + startProcessLocked(packageName, processName, userId, bpid, callingUid, callingPid); + } + } + } + + private int parseBPid(String stubProcessName) { + String prefix; + if (stubProcessName == null) { + return -1; + } else { + prefix = BlackBoxCore.getHostPkg() + ":p"; + } + if (stubProcessName.startsWith(prefix)) { + try { + return Integer.parseInt(stubProcessName.substring(prefix.length())); + } catch (NumberFormatException e) { + // ignore + } + } + return -1; + } + + private boolean initAppProcessL(ProcessRecord record) { + Log.d(TAG, "initProcess: " + record.processName); + AppConfig appConfig = record.getClientConfig(); + Bundle bundle = new Bundle(); + bundle.putParcelable(AppConfig.KEY, appConfig); + Bundle init = ProviderCall.callSafely(record.getProviderAuthority(), "_Black_|_init_process_", null, bundle); + IBinder appThread = BundleCompat.getBinder(init, "_Black_|_client_"); + if (appThread == null || !appThread.isBinderAlive()) { + return false; + } + attachClientL(record, appThread); + return true; + } + + private void attachClientL(final ProcessRecord app, final IBinder appThread) { + IBActivityThread activityThread = IBActivityThread.Stub.asInterface(appThread); + if (activityThread == null) { + app.kill(); + return; + } + try { + appThread.linkToDeath(new IBinder.DeathRecipient() { + @Override + public void binderDied() { + Log.d(TAG, "App Died: " + app.processName); + appThread.unlinkToDeath(this, 0); + onProcessDie(app); + } + }, 0); + } catch (RemoteException e) { + e.printStackTrace(); + } + app.bActivityThread = activityThread; + try { + app.appThread = ApplicationThreadCompat.asInterface(activityThread.getActivityThread()); + } catch (RemoteException e) { + e.printStackTrace(); + } + app.initLock.open(); + } + + public void onProcessDie(ProcessRecord record) { + synchronized (mProcessLock) { + record.kill(); + Map remove = mProcessMap.remove(record.buid); + if (remove != null) + remove.remove(record.processName); + mPidsSelfLocked.remove(record); + } + } + + public ProcessRecord findProcessRecord(String packageName, String processName, int userId) { + synchronized (mProcessLock) { + int appId = BPackageManagerService.get().getAppId(packageName); + int buid = BUserHandle.getUid(userId, appId); + Map processRecordMap = mProcessMap.get(buid); + if (processRecordMap == null) + return null; + return processRecordMap.get(processName); + } + } + + public void killAllByPackageName(String packageName) { + synchronized (mProcessLock) { + synchronized (mPidsSelfLocked) { + List tmp = new ArrayList<>(mPidsSelfLocked); + int appId = BPackageManagerService.get().getAppId(packageName); + for (ProcessRecord processRecord : mPidsSelfLocked) { + int appId1 = BUserHandle.getAppId(processRecord.buid); + if (appId == appId1) { + mProcessMap.remove(processRecord.buid); + tmp.remove(processRecord); + processRecord.kill(); + } + } + mPidsSelfLocked.clear(); + mPidsSelfLocked.addAll(tmp); + } + } + } + + public void killPackageAsUser(String packageName, int userId) { + synchronized (mProcessLock) { + int buid = BUserHandle.getUid(userId, BPackageManagerService.get().getAppId(packageName)); + Map process = mProcessMap.get(buid); + if (process == null) + return; + for (ProcessRecord value : process.values()) { + value.kill(); + } + mProcessMap.remove(buid); + } + } + + + public int getUserIdByCallingPid(int callingPid) { + synchronized (mProcessLock) { + ProcessRecord callingProcess = BProcessManager.get().findProcessByPid(callingPid); + if (callingProcess == null) { + return 0; + } + return callingProcess.userId; + } + } + + public ProcessRecord findProcessByPid(int pid) { + synchronized (mPidsSelfLocked) { + for (ProcessRecord processRecord : mPidsSelfLocked) { + if (processRecord.pid == pid) + return processRecord; + } + return null; + } + } + + private static String getProcessName(Context context, int pid) { + String processName = null; + ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); + for (ActivityManager.RunningAppProcessInfo info : am.getRunningAppProcesses()) { + if (info.pid == pid) { + processName = info.processName; + break; + } + } + if (processName == null) { + throw new RuntimeException("processName = null"); + } + return processName; + } + + public static int getPid(Context context, String processName) { + try { + ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); + List runningAppProcesses = manager.getRunningAppProcesses(); + for (ActivityManager.RunningAppProcessInfo runningAppProcess : runningAppProcesses) { + if (runningAppProcess.processName.equals(processName)) { + return runningAppProcess.pid; + } + } + } catch (Throwable e) { + e.printStackTrace(); + } + return -1; + } +} diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/core/system/BlackBoxSystem.java b/Bcore/src/main/java/top/niunaijun/blackbox/core/system/BlackBoxSystem.java new file mode 100644 index 00000000..0046105b --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/core/system/BlackBoxSystem.java @@ -0,0 +1,80 @@ +package top.niunaijun.blackbox.core.system; + +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; + +import java.io.IOException; +import java.io.InputStream; +import java.util.List; + +import top.niunaijun.blackbox.core.env.BEnvironment; +import top.niunaijun.blackbox.BlackBoxCore; +import top.niunaijun.blackbox.core.env.AppSystemEnv; +import top.niunaijun.blackbox.entity.pm.InstallOption; +import top.niunaijun.blackbox.core.system.am.BActivityManagerService; +import top.niunaijun.blackbox.core.system.os.BStorageManagerService; +import top.niunaijun.blackbox.core.system.pm.BPackageInstallerService; +import top.niunaijun.blackbox.core.system.pm.BPackageManagerService; +import top.niunaijun.blackbox.core.system.user.BUserHandle; +import top.niunaijun.blackbox.core.system.user.BUserManagerService; +import top.niunaijun.blackbox.utils.FileUtils; + +import static top.niunaijun.blackbox.core.env.BEnvironment.EMPTY_JAR; +import static top.niunaijun.blackbox.core.env.BEnvironment.JUNIT_JAR; + +/** + * Created by Milk on 4/22/21. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + */ +public class BlackBoxSystem { + private static BlackBoxSystem sBlackBoxSystem; + + public static BlackBoxSystem getSystem() { + if (sBlackBoxSystem == null) { + synchronized (BlackBoxSystem.class) { + if (sBlackBoxSystem == null) { + sBlackBoxSystem = new BlackBoxSystem(); + } + } + } + return sBlackBoxSystem; + } + + public void startup() { + BEnvironment.load(); + + BPackageManagerService.get().systemReady(); + BUserManagerService.get().systemReady(); + BActivityManagerService.get().systemReady(); + BStorageManagerService.get().systemReady(); + BPackageInstallerService.get().systemReady(); + + List preInstallPackages = AppSystemEnv.getPreInstallPackages(); + for (String preInstallPackage : preInstallPackages) { + try { + if (!BPackageManagerService.get().isInstalled(preInstallPackage, BUserHandle.USER_ALL)) { + PackageInfo packageInfo = BlackBoxCore.getPackageManager().getPackageInfo(preInstallPackage, 0); + BPackageManagerService.get().installPackageAsUser(packageInfo.applicationInfo.sourceDir, InstallOption.installBySystem(), BUserHandle.USER_ALL); + } + } catch (PackageManager.NameNotFoundException ignored) { + } + } + initJarEnv(); + } + + private void initJarEnv() { + try { + InputStream junit = BlackBoxCore.getContext().getAssets().open("junit.jar"); + FileUtils.copyFile(junit, JUNIT_JAR); + + InputStream empty = BlackBoxCore.getContext().getAssets().open("empty.jar"); + FileUtils.copyFile(empty, EMPTY_JAR); + } catch (IOException e) { + e.printStackTrace(); + } + } +} diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/core/system/DaemonService.java b/Bcore/src/main/java/top/niunaijun/blackbox/core/system/DaemonService.java new file mode 100644 index 00000000..9fceb06c --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/core/system/DaemonService.java @@ -0,0 +1,106 @@ +package top.niunaijun.blackbox.core.system; + +import android.app.Notification; +import android.app.NotificationChannel; +import android.app.NotificationManager; +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.graphics.Color; +import android.os.IBinder; +import android.util.Log; + +import androidx.core.app.NotificationCompat; + +import top.niunaijun.blackbox.R; +import top.niunaijun.blackbox.utils.compat.BuildCompat; + + +/** + * Created by Milk on 3/2/21. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + */ +public class DaemonService extends Service { + public static final String TAG = "DaemonService"; + private static final int NOTIFY_ID = (int) (System.currentTimeMillis() / 1000); + + @Override + public IBinder onBind(Intent intent) { + return null; + } + + @Override + public void onCreate() { + super.onCreate(); + initNotificationManager(); + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + Intent innerIntent = new Intent(this, DaemonInnerService.class); + startService(innerIntent); + if (BuildCompat.isOreo()) { + showNotification(); + } + return START_STICKY; + } + + @Override + public void onDestroy() { + super.onDestroy(); + Log.d(TAG, "onDestroy"); + } + + private void showNotification() { + NotificationCompat.Builder builder = new NotificationCompat.Builder(getApplicationContext(), getPackageName() + ".blackbox") + .setPriority(NotificationCompat.PRIORITY_MAX); + startForeground(NOTIFY_ID, builder.build()); + } + + private void initNotificationManager() { + NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); + String CHANNEL_ONE_ID = getPackageName() + ".blackbox"; + String CHANNEL_ONE_NAME = "blackbox"; + if (BuildCompat.isOreo()) { + NotificationChannel notificationChannel = new NotificationChannel(CHANNEL_ONE_ID, + CHANNEL_ONE_NAME, NotificationManager.IMPORTANCE_HIGH); + notificationChannel.enableLights(true); + notificationChannel.setLightColor(Color.RED); + notificationChannel.setShowBadge(true); + notificationChannel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC); + nm.createNotificationChannel(notificationChannel); + } + } + + public static class DaemonInnerService extends Service { + @Override + public void onCreate() { + Log.i(TAG, "DaemonInnerService -> onCreate"); + super.onCreate(); + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + Log.i(TAG, "DaemonInnerService -> onStartCommand"); + NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); + nm.cancel(NOTIFY_ID); + stopSelf(); + return super.onStartCommand(intent, flags, startId); + } + + @Override + public IBinder onBind(Intent intent) { + return null; + } + + @Override + public void onDestroy() { + Log.i(TAG, "DaemonInnerService -> onDestroy"); + super.onDestroy(); + } + } +} diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/core/system/ISystemService.java b/Bcore/src/main/java/top/niunaijun/blackbox/core/system/ISystemService.java new file mode 100644 index 00000000..7149f555 --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/core/system/ISystemService.java @@ -0,0 +1,13 @@ +package top.niunaijun.blackbox.core.system; + +/** + * Created by Milk on 4/22/21. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + */ +public interface ISystemService { + void systemReady(); +} diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/core/system/ProcessRecord.java b/Bcore/src/main/java/top/niunaijun/blackbox/core/system/ProcessRecord.java new file mode 100644 index 00000000..5d9a1d74 --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/core/system/ProcessRecord.java @@ -0,0 +1,138 @@ +package top.niunaijun.blackbox.core.system; + +import android.content.pm.ApplicationInfo; +import android.os.Binder; +import android.os.ConditionVariable; + +import android.os.IInterface; +import android.os.Parcel; +import android.os.Parcelable; +import android.os.Process; +import android.text.TextUtils; + +import java.util.Arrays; + +import top.niunaijun.blackbox.entity.AppConfig; +import top.niunaijun.blackbox.core.IBActivityThread; +import top.niunaijun.blackbox.proxy.ProxyManifest; + +public class ProcessRecord extends Binder implements Parcelable { + public final ApplicationInfo info; + final public String processName; + public IBActivityThread bActivityThread; + public IInterface appThread; + public int pid; + public int uid; + public int buid; + public int bpid; + public int callingVUid; + public int userId; + public int baseBUid; + + public ConditionVariable initLock = new ConditionVariable(); + + public ProcessRecord(ApplicationInfo info, String processName, int buid, int bpid, int callingVUid) { + this.info = info; + this.buid = buid; + this.bpid = bpid; + this.userId = 0; + this.callingVUid = callingVUid; + this.processName = processName; + } + + public int getCallingBUid() { + return callingVUid; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ProcessRecord that = (ProcessRecord) o; + return pid == that.pid && + buid == that.buid && + bpid == that.bpid && + uid == that.uid && + userId == that.userId && + baseBUid == that.baseBUid && + TextUtils.equals(processName, that.processName); + } + + @Override + public int hashCode() { + return Arrays.hashCode(new Object[]{processName, pid, buid, bpid, uid, pid, userId}); + } + + public String getProviderAuthority() { + return ProxyManifest.getProxyAuthorities(bpid); + } + + public AppConfig getClientConfig() { + AppConfig config = new AppConfig(); + config.packageName = info.packageName; + config.processName = processName; + config.bpid = bpid; + config.buid = buid; + config.uid = uid; + config.userId = userId; + config.token = this; + config.baseBUid = baseBUid; + return config; + } + + public void kill() { + if (pid > 0) { + try { + Process.killProcess(pid); + } catch (Throwable e) { + e.printStackTrace(); + } + } + } + + public String getPackageName() { + return info.packageName; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeParcelable(this.info, flags); + dest.writeString(this.processName); + dest.writeInt(this.pid); + dest.writeInt(this.buid); + dest.writeInt(this.bpid); + dest.writeInt(this.uid); + dest.writeInt(this.callingVUid); + dest.writeInt(this.userId); + dest.writeInt(this.baseBUid); + } + + protected ProcessRecord(Parcel in) { + this.info = in.readParcelable(ApplicationInfo.class.getClassLoader()); + this.processName = in.readString(); + this.pid = in.readInt(); + this.buid = in.readInt(); + this.bpid = in.readInt(); + this.uid = in.readInt(); + this.callingVUid = in.readInt(); + this.userId = in.readInt(); + this.baseBUid = in.readInt(); + } + + public static final Creator CREATOR = new Creator() { + @Override + public ProcessRecord createFromParcel(Parcel source) { + return new ProcessRecord(source); + } + + @Override + public ProcessRecord[] newArray(int size) { + return new ProcessRecord[size]; + } + }; +} diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/core/system/ServiceManager.java b/Bcore/src/main/java/top/niunaijun/blackbox/core/system/ServiceManager.java new file mode 100644 index 00000000..5b7c50bc --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/core/system/ServiceManager.java @@ -0,0 +1,56 @@ +package top.niunaijun.blackbox.core.system; + +import android.os.IBinder; + +import java.util.HashMap; +import java.util.Map; + +import top.niunaijun.blackbox.core.system.am.BActivityManagerService; +import top.niunaijun.blackbox.core.system.os.BStorageManagerService; +import top.niunaijun.blackbox.core.system.pm.BPackageManagerService; +import top.niunaijun.blackbox.core.system.user.BUserManagerService; + +/** + * Created by Milk on 3/31/21. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + */ +public class ServiceManager { + private static ServiceManager sServiceManager = null; + public static final String ACTIVITY_MANAGER = "activity_manager"; + public static final String PACKAGE_MANAGER = "package_manager"; + public static final String STORAGE_MANAGER = "storage_manager"; + public static final String USER_MANAGER = "user_manager"; + public static final String Xposed_MANAGER = "Xposed_manager"; + + private final Map mCaches = new HashMap<>(); + + public static ServiceManager get() { + if (sServiceManager == null) { + synchronized (ServiceManager.class) { + if (sServiceManager == null) { + sServiceManager = new ServiceManager(); + } + } + } + return sServiceManager; + } + + public static IBinder getService(String name) { + return get().getServiceInternal(name); + } + + private ServiceManager() { + mCaches.put(ACTIVITY_MANAGER, BActivityManagerService.get()); + mCaches.put(PACKAGE_MANAGER, BPackageManagerService.get()); + mCaches.put(STORAGE_MANAGER, BStorageManagerService.get()); + mCaches.put(USER_MANAGER, BUserManagerService.get()); + } + + public IBinder getServiceInternal(String name) { + return mCaches.get(name); + } +} diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/core/system/SystemCallProvider.java b/Bcore/src/main/java/top/niunaijun/blackbox/core/system/SystemCallProvider.java new file mode 100644 index 00000000..54859f06 --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/core/system/SystemCallProvider.java @@ -0,0 +1,74 @@ +package top.niunaijun.blackbox.core.system; + +import android.content.ContentProvider; +import android.content.ContentValues; +import android.database.Cursor; +import android.net.Uri; +import android.os.Bundle; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import top.niunaijun.blackbox.utils.compat.BundleCompat; + +/** + * Created by Milk on 3/31/21. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + */ +public class SystemCallProvider extends ContentProvider { + @Override + public boolean onCreate() { + return initSystem(); + } + + private boolean initSystem() { + BlackBoxSystem.getSystem().startup(); + return true; + } + + @Nullable + @Override + public Bundle call(@NonNull String method, @Nullable String arg, @Nullable Bundle extras) { + if (method.equals("VM")) { + Bundle bundle = new Bundle(); + if (extras != null) { + String name = extras.getString("_VM_|_server_name_"); + BundleCompat.putBinder(bundle, "_VM_|_server_", ServiceManager.getService(name)); + } + return bundle; + } + return super.call(method, arg, extras); + } + + @Nullable + @Override + public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) { + return null; + } + + @Nullable + @Override + public String getType(@NonNull Uri uri) { + return null; + } + + @Nullable + @Override + public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) { + return null; + } + + @Override + public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) { + return 0; + } + + @Override + public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) { + return 0; + } +} diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/core/system/am/ActiveServices.java b/Bcore/src/main/java/top/niunaijun/blackbox/core/system/am/ActiveServices.java new file mode 100644 index 00000000..dc1bf5ee --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/core/system/am/ActiveServices.java @@ -0,0 +1,114 @@ +package top.niunaijun.blackbox.core.system.am; + +import android.content.Intent; +import android.content.pm.ResolveInfo; +import android.os.IBinder; +import android.util.Log; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; + +import top.niunaijun.blackbox.core.system.pm.BPackageManagerService; + +/** + * Created by Milk on 4/7/21. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + */ +public class ActiveServices { + public static final String TAG = "ActiveServices"; + + private final Map mRunningServiceRecords = new HashMap<>(); + private final Map mConnectedServices = new HashMap<>(); + + public void startService(Intent intent, String resolvedType, int userId) { + + } + + public int stopService(Intent intent, String resolvedType, int userId) { +// ResolveInfo resolveInfo = resolveService(intent, resolvedType, userId); + synchronized (mRunningServiceRecords) { + RunningServiceRecord runningServiceRecord = findRunningServiceRecord(intent); + if (runningServiceRecord == null) { + return 0; + } + if (runningServiceRecord.mBindCount.get() > 0) { + Log.d(TAG, "There are also connections"); + return 0; + } + + runningServiceRecord.mStartId.set(0); + } + return 0; + } + + public void unbindService(IBinder binder, int userId) { + ConnectedServiceRecord connectedService = mConnectedServices.get(binder); + if (connectedService == null) { + return; + } + RunningServiceRecord runningServiceRecord = getOrCreateRunningServiceRecord(connectedService.mIntent); + runningServiceRecord.mConnectedServiceRecord = null; + runningServiceRecord.mBindCount.decrementAndGet(); + mConnectedServices.remove(binder); + } + + public void onStartCommand(Intent proxyIntent, int userId) { + } + + public void onServiceDestroy(Intent proxyIntent, int userId) { + } + + private RunningServiceRecord getOrCreateRunningServiceRecord(Intent intent) { + RunningServiceRecord runningServiceRecord = findRunningServiceRecord(intent); + if (runningServiceRecord == null) { + runningServiceRecord = new RunningServiceRecord(); + mRunningServiceRecords.put(new Intent.FilterComparison(intent), runningServiceRecord); + } + return runningServiceRecord; + } + + private RunningServiceRecord findRunningServiceRecord(Intent intent) { + return mRunningServiceRecords.get(new Intent.FilterComparison(intent)); + } + + private ResolveInfo resolveService(Intent intent, String resolvedType, int userId) { + return BPackageManagerService.get().resolveService(intent, 0, resolvedType, userId); + } + + private ConnectedServiceRecord findConnectedServiceRecord(Intent intent) { + RunningServiceRecord runningServiceRecord = mRunningServiceRecords.get(intent); + if (runningServiceRecord == null) + return null; + return runningServiceRecord.mConnectedServiceRecord; + } + + public static class RunningServiceRecord { + // onStartCommand startId + private AtomicInteger mStartId = new AtomicInteger(1); + private AtomicInteger mBindCount = new AtomicInteger(0); + // 正在连接的服务 + private ConnectedServiceRecord mConnectedServiceRecord; + + public int getAndIncrementStartId() { + return mStartId.getAndIncrement(); + } + + public int decrementBindCountAndGet() { + return mBindCount.decrementAndGet(); + } + + public int incrementBindCountAndGet() { + return mBindCount.incrementAndGet(); + } + } + + public static class ConnectedServiceRecord { + private IBinder mIBinder; + private Intent mIntent; + } +} diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/core/system/am/ActivityRecord.java b/Bcore/src/main/java/top/niunaijun/blackbox/core/system/am/ActivityRecord.java new file mode 100644 index 00000000..7d3e880d --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/core/system/am/ActivityRecord.java @@ -0,0 +1,42 @@ +package top.niunaijun.blackbox.core.system.am; + +import android.content.ComponentName; +import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.os.Binder; +import android.os.IBinder; + +import top.niunaijun.blackbox.core.system.ProcessRecord; + + +/** + * Created by Milk on 4/9/21. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + */ +public class ActivityRecord extends Binder { + public TaskRecord task; + public IBinder token; + public IBinder resultTo; + public ActivityInfo info; + public ComponentName component; + public Intent intent; + public int userId; + public boolean finished; + public ProcessRecord processRecord; + + public static ActivityRecord create(Intent intent, ActivityInfo info, IBinder resultTo, int userId) { + ActivityRecord record = new ActivityRecord(); + record.intent = intent; + record.info = info; + record.component = new ComponentName(info.packageName, info.name); + record.resultTo = resultTo; + record.userId = userId; + return record; + } + + +} diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/core/system/am/ActivityStack.java b/Bcore/src/main/java/top/niunaijun/blackbox/core/system/am/ActivityStack.java new file mode 100644 index 00000000..5b1cd825 --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/core/system/am/ActivityStack.java @@ -0,0 +1,468 @@ +package top.niunaijun.blackbox.core.system.am; + +import android.app.ActivityManager; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.content.pm.ResolveInfo; +import android.os.Binder; +import android.os.Bundle; +import android.os.IBinder; +import android.os.IInterface; +import android.os.Process; +import android.util.Log; + +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import mirror.android.app.ActivityManagerNative; +import mirror.android.app.IActivityManager; +import top.niunaijun.blackbox.BlackBoxCore; +import top.niunaijun.blackbox.proxy.ProxyManifest; +import top.niunaijun.blackbox.core.system.pm.BPackageManagerService; +import top.niunaijun.blackbox.utils.ComponentUtils; +import top.niunaijun.blackbox.proxy.record.ProxyActivityRecord; +import top.niunaijun.blackbox.core.system.ProcessRecord; +import top.niunaijun.blackbox.core.system.BProcessManager; + +import static android.content.pm.PackageManager.GET_ACTIVITIES; + +/** + * Created by Milk on 4/5/21. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + */ +public class ActivityStack { + private ActivityManager mAms; + private final Map mTasks = new LinkedHashMap<>(); + private final Set mLaunchingActivities = new HashSet<>(); + + public ActivityStack() { + mAms = (ActivityManager) BlackBoxCore.getContext().getSystemService(Context.ACTIVITY_SERVICE); + } + + public boolean containsFlag(Intent intent, int flag) { + return (intent.getFlags() & flag) != 0; + } + + public int startActivitiesLocked(int userId, Intent[] intents, String[] resolvedTypes, IBinder resultTo, Bundle options) { + if (intents == null) { + throw new NullPointerException("intents is null"); + } + if (resolvedTypes == null) { + throw new NullPointerException("resolvedTypes is null"); + } + if (intents.length != resolvedTypes.length) { + throw new IllegalArgumentException("intents are length different than resolvedTypes"); + } + for (int i = 0; i < intents.length; i++) { + startActivityLocked(userId, intents[i], resolvedTypes[i], resultTo, null, -1, 0, options); + } + return 0; + } + + public int startActivityLocked(int userId, Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode, int flags, Bundle options) { + synchronized (mTasks) { + synchronizeTasks(); + } + + ResolveInfo resolveInfo = BPackageManagerService.get().resolveActivity(intent, GET_ACTIVITIES, resolvedType, userId); + if (resolveInfo == null || resolveInfo.activityInfo == null) { + return 0; + } + Log.d("TestActivity", "startActivityLocked : " + intent.getComponent().toString()); + ActivityInfo activityInfo = resolveInfo.activityInfo; + + ActivityRecord sourceRecord = findActivityRecordByToken(userId, resultTo); + if (sourceRecord == null) { + resultTo = null; + } + TaskRecord sourceTask = null; + if (sourceRecord != null) { + sourceTask = sourceRecord.task; + } + + String taskAffinity = ComponentUtils.getTaskAffinity(activityInfo); + + int launchModeFlags = 0; + boolean singleTop = containsFlag(intent, Intent.FLAG_ACTIVITY_SINGLE_TOP) || activityInfo.launchMode == ActivityInfo.LAUNCH_SINGLE_TOP; + boolean newTask = containsFlag(intent, Intent.FLAG_ACTIVITY_NEW_TASK); + boolean clearTop = containsFlag(intent, Intent.FLAG_ACTIVITY_CLEAR_TOP); + boolean clearTask = containsFlag(intent, Intent.FLAG_ACTIVITY_CLEAR_TASK); + + TaskRecord taskRecord = null; + switch (activityInfo.launchMode) { + case ActivityInfo.LAUNCH_SINGLE_TOP: + case ActivityInfo.LAUNCH_MULTIPLE: + case ActivityInfo.LAUNCH_SINGLE_TASK: + taskRecord = findTaskRecordByTaskAffinityLocked(userId, taskAffinity); + if (taskRecord == null && !newTask) { + taskRecord = sourceTask; + } + break; + case ActivityInfo.LAUNCH_SINGLE_INSTANCE: + taskRecord = findTaskRecordByTaskAffinityLocked(userId, taskAffinity); + break; + } + + // 如果还没有task则新启动一个task + if (taskRecord == null || taskRecord.needNewTask()) { + return startActivityInNewTaskLocked(userId, intent, activityInfo, resultTo, launchModeFlags); + } + // 移至前台 + mAms.moveTaskToFront(taskRecord.id, 0); + + boolean notStartToFront = false; + if (clearTop || singleTop || clearTask) { + notStartToFront = true; + } + + boolean startTaskToFront = !notStartToFront + && ComponentUtils.intentFilterEquals(taskRecord.rootIntent, intent) + && taskRecord.rootIntent.getFlags() == intent.getFlags(); + + if (startTaskToFront) + return 0; + + ActivityRecord topActivityRecord = taskRecord.getTopActivityRecord(); + ActivityRecord targetActivityRecord = findActivityRecordByComponentName(userId, ComponentUtils.toComponentName(activityInfo)); + ActivityRecord newIntentRecord = null; + boolean ignore = false; + + if (clearTop) { + if (targetActivityRecord != null) { + // 目标栈上面所有activity出栈 + synchronized (targetActivityRecord.task.activities) { + for (int i = targetActivityRecord.task.activities.size() - 1; i >= 0; i--) { + ActivityRecord next = targetActivityRecord.task.activities.get(i); + if (next != targetActivityRecord) { + next.finished = true; + Log.d("TestActivity", "makerFinish: " + next.component.toString()); + } else { + if (singleTop) { + newIntentRecord = targetActivityRecord; + } else { + // clearTop并且不是singleTop,目标也finish,重建。 + targetActivityRecord.finished = true; + } + break; + } + } + } + } + } + + if (singleTop && !clearTop) { + if (ComponentUtils.intentFilterEquals(topActivityRecord.intent, intent)) { + newIntentRecord = topActivityRecord; + } else { + synchronized (mLaunchingActivities) { + for (ActivityRecord launchingActivity : mLaunchingActivities) { + if (!launchingActivity.finished && launchingActivity.component.equals(intent.getComponent())) { + // todo update onNewIntent from intent + ignore = true; + } + } + } + } + } + + if (activityInfo.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK && !clearTop) { + if (ComponentUtils.intentFilterEquals(topActivityRecord.intent, intent)) { + newIntentRecord = topActivityRecord; + } else { + ActivityRecord record = findActivityRecordByComponentName(userId, ComponentUtils.toComponentName(activityInfo)); + if (record != null) { + // 需要调用目标onNewIntent + newIntentRecord = record; + // 目标栈上面所有activity出栈 + synchronized (taskRecord.activities) { + for (int i = taskRecord.activities.size() - 1; i >= 0; i--) { + ActivityRecord next = taskRecord.activities.get(i); + if (next != record) { + next.finished = true; + } else { + break; + } + } + } + } + } + } + + if (activityInfo.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) { + newIntentRecord = topActivityRecord; + } + + // clearTask finish All + if (clearTask && newTask) { + for (ActivityRecord activity : taskRecord.activities) { + activity.finished = true; + } + } + + finishAllActivity(userId); + + if (newIntentRecord != null) { + // 通知onNewIntent + deliverNewIntentLocked(newIntentRecord, intent); + return 0; + } else if (ignore) { + return 0; + } + + if (resultTo == null) { + ActivityRecord top = taskRecord.getTopActivityRecord(); + if (top != null) { + resultTo = top.token; + } + } else if (sourceTask != null) { + ActivityRecord top = sourceTask.getTopActivityRecord(); + if (top != null) { + resultTo = top.token; + } + } + return startActivityInSourceTask(intent, + resolvedType, resultTo, resultWho, requestCode, flags, options, userId, topActivityRecord, activityInfo, launchModeFlags); + } + + private void deliverNewIntentLocked(ActivityRecord activityRecord, Intent intent) { + + } + + private Intent startActivityProcess(int userId, Intent intent, ActivityInfo + info, ActivityRecord record, int callingUid) { + ProxyActivityRecord stubRecord = new ProxyActivityRecord(userId, info, intent, record); + ProcessRecord targetApp = BProcessManager.get().startProcessLocked(info.packageName, info.processName, userId, -1, Binder.getCallingUid(), Binder.getCallingPid()); + if (targetApp == null) { + throw new RuntimeException("Unable to create process, name:" + info.name); + } + return getStartStubActivityIntentInner(intent, targetApp.bpid, userId, stubRecord, info); + } + + private int startActivityInNewTaskLocked(int userId, Intent intent, ActivityInfo + activityInfo, IBinder resultTo, int launchMode) { + ActivityRecord record = newActivityRecord(intent, activityInfo, resultTo, userId); + Intent shadow = startActivityProcess(userId, intent, activityInfo, record, Process.myUid()); + + shadow.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK); + shadow.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT); + shadow.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + shadow.addFlags(launchMode); + + BlackBoxCore.getContext().startActivity(shadow); + return 0; + } + + private int startActivityInSourceTask(Intent intent, String resolvedType, + IBinder resultTo, String resultWho, int requestCode, int flags, + Bundle options, + int userId, ActivityRecord sourceRecord, ActivityInfo activityInfo, int launchMode) { + ActivityRecord selfRecord = newActivityRecord(intent, activityInfo, resultTo, userId); + Intent shadow = startActivityProcess(userId, intent, activityInfo, selfRecord, Process.myUid()); + shadow.addFlags(launchMode); + return realStartActivityLocked(sourceRecord.processRecord.appThread, shadow, resolvedType, resultTo, resultWho, requestCode, flags, options); + } + + private int realStartActivityLocked(IInterface appThread, Intent intent, String + resolvedType, + IBinder resultTo, String resultWho, int requestCode, int flags, + Bundle options) { + Class[] classes = IActivityManager.startActivity.unwrap().getParameterTypes(); + Object[] args = new Object[classes.length]; + + int index = 0; + args[index++] = appThread; + args[index++] = BlackBoxCore.getHostPkg(); + args[index++] = intent; + args[index++] = resolvedType; + args[index++] = resultTo; + args[index++] = resultWho; + args[index++] = requestCode; + args[index++] = flags; + args[index++] = null; + args[index++] = options; + + try { + IActivityManager.startActivity.call(ActivityManagerNative.getDefault.call(), args); + } catch (Throwable e) { + e.printStackTrace(); + } + return 0; + } + + private ActivityRecord getTopActivityRecord() { + synchronized (mTasks) { + synchronizeTasks(); + } + List tasks = new LinkedList<>(mTasks.values()); + if (tasks.isEmpty()) + return null; + return tasks.get(tasks.size() - 1).getTopActivityRecord(); + } + + private Intent getStartStubActivityIntentInner(Intent intent, int vpid, + int userId, ProxyActivityRecord target, + ActivityInfo activityInfo) { + Intent shadow = new Intent(); + shadow.setComponent(new ComponentName(BlackBoxCore.getHostPkg(), ProxyManifest.getProxyActivity(vpid))); + ProxyActivityRecord.saveStub(shadow, intent, target.mActivityInfo, target.mActivityRecord, target.mUserId); + return shadow; + } + + private void finishAllActivity(int userId) { + for (TaskRecord task : mTasks.values()) { + for (ActivityRecord activity : task.activities) { + if (activity.userId == userId) { + if (activity.finished) { + + } + } + } + } + } + + ActivityRecord newActivityRecord(Intent intent, ActivityInfo info, IBinder resultTo, + int userId) { + ActivityRecord targetRecord = ActivityRecord.create(intent, info, resultTo, userId); + synchronized (mLaunchingActivities) { + mLaunchingActivities.add(targetRecord); + } + return targetRecord; + } + + private ActivityRecord findActivityRecordByComponentName(int userId, ComponentName + componentName) { + ActivityRecord record = null; + for (TaskRecord next : mTasks.values()) { + if (userId == next.userId) { + for (ActivityRecord activity : next.activities) { + if (activity.component.equals(componentName)) { + record = activity; + break; + } + } + } + } + return record; + } + + private ActivityRecord findActivityRecordByToken(int userId, IBinder token) { + ActivityRecord record = null; + if (token != null) { + for (TaskRecord next : mTasks.values()) { + if (userId == next.userId) { + for (ActivityRecord activity : next.activities) { + if (activity.token == token) { + record = activity; + break; + } + } + } + } + } + return record; + } + + private TaskRecord findTaskRecordByTaskAffinityLocked(int userId, String taskAffinity) { + synchronized (mTasks) { + for (TaskRecord next : mTasks.values()) { + if (userId == next.userId && next.taskAffinity.equals(taskAffinity)) + return next; + } + return null; + } + } + + private TaskRecord findTaskRecordByTokenLocked(int userId, IBinder token) { + synchronized (mTasks) { + for (TaskRecord next : mTasks.values()) { + if (userId == next.userId) { + for (ActivityRecord activity : next.activities) { + if (activity.token == token) { + return next; + } + } + } + } + return null; + } + } + + public void onActivityCreated(ProcessRecord processRecord, int taskId, IBinder + token, ActivityRecord record) { + synchronized (mLaunchingActivities) { + mLaunchingActivities.remove(record); + } + synchronized (mTasks) { + synchronizeTasks(); + TaskRecord taskRecord = mTasks.get(taskId); + if (taskRecord == null) { + taskRecord = new TaskRecord(taskId, record.userId, ComponentUtils.getTaskAffinity(record.info)); + taskRecord.rootIntent = record.intent; + mTasks.put(taskId, taskRecord); + } + record.token = token; + record.processRecord = processRecord; + record.task = taskRecord; + taskRecord.addTopActivity(record); + } + } + + public void onActivityResumed(int userId, IBinder token) { + synchronized (mTasks) { + synchronizeTasks(); + ActivityRecord activityRecord = findActivityRecordByToken(userId, token); + if (activityRecord == null) { + return; + } + activityRecord.task.removeActivity(activityRecord); + activityRecord.task.addTopActivity(activityRecord); + } + } + + public void onActivityDestroyed(int userId, IBinder token) { + synchronized (mTasks) { + synchronizeTasks(); + ActivityRecord activityRecord = findActivityRecordByToken(userId, token); + if (activityRecord == null) { + return; + } + activityRecord.finished = true; + activityRecord.task.removeActivity(activityRecord); + } + } + + public void onFinishActivity(int userId, IBinder token) { + synchronized (mTasks) { + synchronizeTasks(); + ActivityRecord activityRecord = findActivityRecordByToken(userId, token); + if (activityRecord == null) { + return; + } + activityRecord.finished = true; + } + } + + private void synchronizeTasks() { + List recentTasks = mAms.getRecentTasks(100, 0); + Map newTacks = new LinkedHashMap<>(); + for (int i = recentTasks.size() - 1; i >= 0; i--) { + ActivityManager.RecentTaskInfo next = recentTasks.get(i); + TaskRecord taskRecord = mTasks.get(next.id); + if (taskRecord == null) + continue; + newTacks.put(next.id, taskRecord); + } + mTasks.clear(); + mTasks.putAll(newTacks); + } +} diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/core/system/am/BActivityManagerService.java b/Bcore/src/main/java/top/niunaijun/blackbox/core/system/am/BActivityManagerService.java new file mode 100644 index 00000000..59827172 --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/core/system/am/BActivityManagerService.java @@ -0,0 +1,222 @@ +package top.niunaijun.blackbox.core.system.am; + +import android.content.ComponentName; +import android.content.Intent; +import android.content.pm.ProviderInfo; +import android.content.pm.ResolveInfo; +import android.os.Binder; +import android.os.Bundle; +import android.os.IBinder; +import android.os.RemoteException; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import top.niunaijun.blackbox.BlackBoxCore; +import top.niunaijun.blackbox.entity.AppConfig; +import top.niunaijun.blackbox.core.system.ISystemService; +import top.niunaijun.blackbox.core.system.pm.BPackageManagerService; +import top.niunaijun.blackbox.core.system.ProcessRecord; +import top.niunaijun.blackbox.core.system.BProcessManager; + +import static android.content.pm.PackageManager.GET_META_DATA; + +/** + * Created by Milk on 3/31/21. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + */ +public class BActivityManagerService extends IBActivityManagerService.Stub implements ISystemService { + public static final String TAG = "VActivityManagerService"; + private static final BActivityManagerService sService = new BActivityManagerService(); + private final Map mUserSpace = new HashMap<>(); + + public static BActivityManagerService get() { + return sService; + } + + @Override + public ComponentName startService(Intent intent, String resolvedType, int userId) { + UserSpace userSpace = getOrCreateSpaceLocked(userId); + synchronized (userSpace.mActiveServices) { + userSpace.mActiveServices.startService(intent, resolvedType, userId); + } + return null; + } + + @Override + public Intent sendBroadcast(Intent intent, String resolvedType, int userId) throws RemoteException { + List resolves = BPackageManagerService.get().queryBroadcastReceivers(intent, GET_META_DATA, resolvedType, userId); + + for (ResolveInfo resolve : resolves) { + ProcessRecord processRecord = BProcessManager.get().startProcessLocked(resolve.activityInfo.packageName, resolve.activityInfo.processName, userId, -1, Binder.getCallingUid(), Binder.getCallingPid()); + if (processRecord == null) { +// throw new RuntimeException("Unable to create process " + resolve.activityInfo.name); + continue; + } + processRecord.bActivityThread.bindApplication(); + } + + if (intent.getPackage() != null) { + intent.setPackage(BlackBoxCore.getHostPkg()); + } + if (intent.getComponent() != null) { + intent.setComponent(null); +// Intent shadow = new Intent(); +// shadow.setPackage(VirtualCore.getHostPkg()); +// shadow.setAction(StubManifest.getStubReceiver()); +// StubBroadcastRecord.saveStub(shadow, intent, receivers, userId); + } + return intent; + } + + @Override + public void onActivityCreated(int taskId, IBinder token, IBinder activityRecord) throws RemoteException { + int callingPid = Binder.getCallingPid(); + ProcessRecord process = BProcessManager.get().findProcessByPid(callingPid); + if (process == null) { + return; + } + ActivityRecord record = (ActivityRecord) activityRecord; + UserSpace userSpace = getOrCreateSpaceLocked(process.userId); + synchronized (userSpace.mStack) { + userSpace.mStack.onActivityCreated(process, taskId, token, record); + } + } + + @Override + public void onActivityResumed(IBinder token) throws RemoteException { + int callingPid = Binder.getCallingPid(); + ProcessRecord process = BProcessManager.get().findProcessByPid(callingPid); + if (process == null) { + return; + } + UserSpace userSpace = getOrCreateSpaceLocked(process.userId); + synchronized (userSpace.mStack) { + userSpace.mStack.onActivityResumed(process.userId, token); + } + } + + @Override + public void onActivityDestroyed(IBinder token) throws RemoteException { + int callingPid = Binder.getCallingPid(); + ProcessRecord process = BProcessManager.get().findProcessByPid(callingPid); + if (process == null) { + return; + } + UserSpace userSpace = getOrCreateSpaceLocked(process.userId); + synchronized (userSpace.mStack) { + userSpace.mStack.onActivityDestroyed(process.userId, token); + } + } + + @Override + public void onFinishActivity(IBinder token) throws RemoteException { + int callingPid = Binder.getCallingPid(); + ProcessRecord process = BProcessManager.get().findProcessByPid(callingPid); + if (process == null) { + return; + } + UserSpace userSpace = getOrCreateSpaceLocked(process.userId); + synchronized (userSpace.mStack) { + userSpace.mStack.onFinishActivity(process.userId, token); + } + } + + @Override + public void onStartCommand(Intent intent, int userId) throws RemoteException { + UserSpace userSpace = getOrCreateSpaceLocked(userId); + synchronized (userSpace.mActiveServices) { + userSpace.mActiveServices.onStartCommand(intent, userId); + } + } + + @Override + public void onServiceDestroy(Intent proxyIntent, int userId) throws RemoteException { + UserSpace userSpace = getOrCreateSpaceLocked(userId); + synchronized (userSpace.mActiveServices) { + userSpace.mActiveServices.onServiceDestroy(proxyIntent, userId); + } + } + + @Override + public int stopService(Intent intent, String resolvedType, int userId) { + UserSpace userSpace = getOrCreateSpaceLocked(userId); + synchronized (userSpace.mActiveServices) { + return userSpace.mActiveServices.stopService(intent, resolvedType, userId); + } + } + + @Override + public Intent bindService(Intent service, IBinder binder, String resolvedType, int userId) throws RemoteException { + UserSpace userSpace = getOrCreateSpaceLocked(userId); + synchronized (userSpace.mActiveServices) { + return null; + } + } + + @Override + public void unbindService(IBinder binder, int userId) throws RemoteException { + UserSpace userSpace = getOrCreateSpaceLocked(userId); + synchronized (userSpace.mActiveServices) { + userSpace.mActiveServices.unbindService(binder, userId); + } + } + + @Override + public AppConfig initProcess(String packageName, String processName, int userId) throws RemoteException { + ProcessRecord processRecord = BProcessManager.get().startProcessLocked(packageName, processName, userId, -1, Binder.getCallingUid(), Binder.getCallingPid()); + if (processRecord == null) + return null; + return processRecord.getClientConfig(); + } + + @Override + public void restartProcess(String packageName, String processName, int userId) throws RemoteException { + BProcessManager.get().restartAppProcess(packageName, processName, userId); + } + + @Override + public void startActivity(Intent intent, int userId) { + UserSpace userSpace = getOrCreateSpaceLocked(userId); + synchronized (userSpace.mStack) { + userSpace.mStack.startActivityLocked(userId, intent, null, null, null, -1, -1, null); + } + } + + @Override + public int startActivityAms(int userId, Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode, int flags, Bundle options) throws RemoteException { + UserSpace space = getOrCreateSpaceLocked(userId); + synchronized (space.mStack) { + return space.mStack.startActivityLocked(userId, intent, resolvedType, resultTo, resultWho, requestCode, flags, options); + } + } + + @Override + public int startActivities(int userId, Intent[] intent, String[] resolvedType, IBinder resultTo, Bundle options) throws RemoteException { + UserSpace space = getOrCreateSpaceLocked(userId); + synchronized (space.mStack) { + return space.mStack.startActivitiesLocked(userId, intent, resolvedType, resultTo, options); + } + } + + private UserSpace getOrCreateSpaceLocked(int userId) { + synchronized (mUserSpace) { + UserSpace userSpace = mUserSpace.get(userId); + if (userSpace != null) + return userSpace; + userSpace = new UserSpace(); + mUserSpace.put(userId, userSpace); + return userSpace; + } + } + + @Override + public void systemReady() { + + } +} diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/core/system/am/TaskRecord.java b/Bcore/src/main/java/top/niunaijun/blackbox/core/system/am/TaskRecord.java new file mode 100644 index 00000000..c1fa2927 --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/core/system/am/TaskRecord.java @@ -0,0 +1,55 @@ +package top.niunaijun.blackbox.core.system.am; + +import android.content.Intent; + +import java.util.LinkedList; +import java.util.List; + +/** + * Created by Milk on 4/9/21. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + */ +public class TaskRecord { + public int id; + public int userId; + public String taskAffinity; + public Intent rootIntent; + public final List activities = new LinkedList<>(); + + public TaskRecord(int id, int userId, String taskAffinity) { + this.id = id; + this.userId = userId; + this.taskAffinity = taskAffinity; + } + + public boolean needNewTask() { + for (ActivityRecord activity : activities) { + if (!activity.finished) { + return false; + } + } + return true; + } + + public void addTopActivity(ActivityRecord record) { + activities.add(record); + } + + public void removeActivity(ActivityRecord record) { + activities.remove(record); + } + + public ActivityRecord getTopActivityRecord() { + for (int i = activities.size() - 1; i >= 0; i--) { + ActivityRecord activityRecord = activities.get(i); + if (!activityRecord.finished) { + return activityRecord; + } + } + return null; + } +} diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/core/system/am/UserSpace.java b/Bcore/src/main/java/top/niunaijun/blackbox/core/system/am/UserSpace.java new file mode 100644 index 00000000..9171770a --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/core/system/am/UserSpace.java @@ -0,0 +1,14 @@ +package top.niunaijun.blackbox.core.system.am; + +/** + * Created by Milk on 4/25/21. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + */ +public class UserSpace { + public final ActiveServices mActiveServices = new ActiveServices(); + public final ActivityStack mStack = new ActivityStack(); +} diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/core/system/os/BStorageManagerService.java b/Bcore/src/main/java/top/niunaijun/blackbox/core/system/os/BStorageManagerService.java new file mode 100644 index 00000000..f7d592d0 --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/core/system/os/BStorageManagerService.java @@ -0,0 +1,56 @@ +package top.niunaijun.blackbox.core.system.os; + +import android.net.Uri; +import android.os.Process; +import android.os.RemoteException; +import android.os.storage.StorageVolume; + +import java.io.File; + +import top.niunaijun.blackbox.core.env.BEnvironment; +import top.niunaijun.blackbox.BlackBoxCore; +import top.niunaijun.blackbox.proxy.ProxyManifest; +import top.niunaijun.blackbox.core.system.ISystemService; +import top.niunaijun.blackbox.core.system.user.BUserHandle; +import top.niunaijun.blackbox.utils.compat.BuildCompat; + +/** + * Created by Milk on 4/10/21. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + */ +public class BStorageManagerService extends IBStorageManagerService.Stub implements ISystemService { + private static final BStorageManagerService sService = new BStorageManagerService(); + + public static BStorageManagerService get() { + return sService; + } + + public BStorageManagerService() { + } + + @Override + public StorageVolume[] getVolumeList(int uid, String packageName, int flags, int userId) throws RemoteException { + try { + StorageVolume[] storageVolumes = mirror.android.os.storage.StorageManager.getVolumeList.call(BUserHandle.getUserId(Process.myUid()), 0); + for (StorageVolume storageVolume : storageVolumes) { + mirror.android.os.storage.StorageVolume.mPath.set(storageVolume, BEnvironment.getExternalUserDir(userId)); + if (BuildCompat.isPie()) { + mirror.android.os.storage.StorageVolume.mInternalPath.set(storageVolume, BEnvironment.getExternalUserDir(userId)); + } + } + return storageVolumes; + } catch (Exception e) { + e.printStackTrace(); + } + return new StorageVolume[]{}; + } + + @Override + public void systemReady() { + + } +} diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/core/system/pm/BPackage.java b/Bcore/src/main/java/top/niunaijun/blackbox/core/system/pm/BPackage.java new file mode 100644 index 00000000..89fa078c --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/core/system/pm/BPackage.java @@ -0,0 +1,732 @@ +package top.niunaijun.blackbox.core.system.pm; + +import android.content.ComponentName; +import android.content.IntentFilter; +import android.content.pm.ActivityInfo; +import android.content.pm.ApplicationInfo; +import android.content.pm.ConfigurationInfo; +import android.content.pm.FeatureInfo; +import android.content.pm.InstrumentationInfo; +import android.content.pm.PackageParser; +import android.content.pm.PermissionGroupInfo; +import android.content.pm.PermissionInfo; +import android.content.pm.ProviderInfo; +import android.content.pm.ServiceInfo; +import android.content.pm.Signature; +import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.ArrayList; + +import top.niunaijun.blackbox.utils.compat.BuildCompat; + +/** + * Created by Milk on 4/21/21. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + */ +public class BPackage implements Parcelable { + public ArrayList activities = new ArrayList(0); + public ArrayList receivers = new ArrayList(0); + public ArrayList providers = new ArrayList(0); + public ArrayList services = new ArrayList(0); + public ArrayList instrumentation = new ArrayList(0); + public ArrayList permissions = new ArrayList(0); + public ArrayList permissionGroups = new ArrayList(0); + public ArrayList requestedPermissions = new ArrayList(); + public Signature[] mSignatures; + public SigningDetails mSigningDetails; + public Bundle mAppMetaData; + public BPackageSettings mExtras; + public String packageName; + public int mPreferredOrder; + public String mSharedUserId; + public ArrayList usesLibraries; + public ArrayList usesOptionalLibraries; + public int mVersionCode; + public ApplicationInfo applicationInfo; + public String mVersionName; + public String baseCodePath; + + public int mSharedUserLabel; + // Applications hardware preferences + public ArrayList configPreferences = null; + // Applications requested features + public ArrayList reqFeatures = null; + + public BPackage(PackageParser.Package aPackage) { + this.activities = new ArrayList<>(aPackage.activities.size()); + for (PackageParser.Activity activity : aPackage.activities) { + Activity selfActivity = new Activity(activity); + for (ActivityIntentInfo intent : selfActivity.intents) { + intent.activity = selfActivity; + } + selfActivity.owner = this; + this.activities.add(selfActivity); + } + + this.receivers = new ArrayList<>(aPackage.receivers.size()); + for (PackageParser.Activity receiver : aPackage.receivers) { + Activity selfReceiver = new Activity(receiver); + for (ActivityIntentInfo intent : selfReceiver.intents) { + intent.activity = selfReceiver; + } + selfReceiver.owner = this; + this.receivers.add(selfReceiver); + } + + this.providers = new ArrayList<>(aPackage.providers.size()); + for (PackageParser.Provider provider : aPackage.providers) { + Provider selfProvider = new Provider(provider); + for (ProviderIntentInfo intent : selfProvider.intents) { + intent.provider = selfProvider; + } + selfProvider.owner = this; + this.providers.add(selfProvider); + } + + this.services = new ArrayList<>(aPackage.services.size()); + for (PackageParser.Service service : aPackage.services) { + Service selfService = new Service(service); + for (ServiceIntentInfo intent : selfService.intents) { + intent.service = selfService; + } + selfService.owner = this; + this.services.add(selfService); + } + + this.instrumentation = new ArrayList<>(aPackage.instrumentation.size()); + for (PackageParser.Instrumentation instrumentation1 : aPackage.instrumentation) { + Instrumentation selfInstrumentation = new Instrumentation(instrumentation1); + selfInstrumentation.owner = this; + this.instrumentation.add(selfInstrumentation); + } + + this.permissions = new ArrayList<>(aPackage.permissions.size()); + for (PackageParser.Permission permission : aPackage.permissions) { + Permission selfPermission = new Permission(permission); + selfPermission.owner = this; + this.permissions.add(selfPermission); + } + + this.permissionGroups = new ArrayList<>(aPackage.permissionGroups.size()); + for (PackageParser.PermissionGroup permissionGroup : aPackage.permissionGroups) { + PermissionGroup selfPermissionGroup = new PermissionGroup(permissionGroup); + selfPermissionGroup.owner = this; + this.permissionGroups.add(selfPermissionGroup); + } + + this.requestedPermissions = aPackage.requestedPermissions; + if (BuildCompat.isPie()) { + this.mSigningDetails = new SigningDetails(aPackage.mSigningDetails); + this.mSignatures = this.mSigningDetails.signatures; + } else { + this.mSignatures = aPackage.mSignatures; + } + this.mAppMetaData = aPackage.mAppMetaData; + // this.mExtras = new BPackageSettings((PackageSetting) aPackage.mExtras); + this.packageName = aPackage.packageName; + this.mPreferredOrder = aPackage.mPreferredOrder; + this.mSharedUserId = aPackage.mSharedUserId; + this.usesLibraries = aPackage.usesLibraries; + this.usesOptionalLibraries = aPackage.usesOptionalLibraries; + this.mVersionCode = aPackage.mVersionCode; + this.applicationInfo = aPackage.applicationInfo; + this.mVersionName = aPackage.mVersionName; + this.baseCodePath = aPackage.baseCodePath; + this.mSharedUserLabel = aPackage.mSharedUserLabel; + this.configPreferences = aPackage.configPreferences; + this.reqFeatures = aPackage.reqFeatures; + } + + protected BPackage(Parcel in) { + int N = in.readInt(); + this.activities = new ArrayList<>(N); + while (N-- > 0) { + Activity activity = new Activity(in); + for (ActivityIntentInfo intent : activity.intents) { + intent.activity = activity; + } + activity.owner = this; + this.activities.add(activity); + } + + N = in.readInt(); + this.receivers = new ArrayList<>(N); + while (N-- > 0) { + Activity activity = new Activity(in); + for (ActivityIntentInfo intent : activity.intents) { + intent.activity = activity; + } + activity.owner = this; + this.receivers.add(activity); + } + + N = in.readInt(); + this.providers = new ArrayList<>(N); + while (N-- > 0) { + Provider provider = new Provider(in); + for (ProviderIntentInfo intent : provider.intents) { + intent.provider = provider; + } + provider.owner = this; + this.providers.add(provider); + } + + N = in.readInt(); + this.services = new ArrayList<>(N); + while (N-- > 0) { + Service service = new Service(in); + for (ServiceIntentInfo intent : service.intents) { + intent.service = service; + } + service.owner = this; + this.services.add(service); + } + + N = in.readInt(); + this.instrumentation = new ArrayList<>(N); + while (N-- > 0) { + Instrumentation instrumentation = new Instrumentation(in); + instrumentation.owner = this; + this.instrumentation.add(instrumentation); + } + + N = in.readInt(); + this.permissions = new ArrayList<>(N); + while (N-- > 0) { + Permission permission = new Permission(in); + permission.owner = this; + this.permissions.add(permission); + } + + N = in.readInt(); + this.permissionGroups = new ArrayList<>(N); + while (N-- > 0) { + PermissionGroup permissionGroup = new PermissionGroup(in); + permissionGroup.owner = this; + this.permissionGroups.add(permissionGroup); + } + + in.readStringList(this.requestedPermissions); + if (BuildCompat.isPie()) { + this.mSigningDetails = in.readParcelable(SigningDetails.class.getClassLoader()); + } + this.mSignatures = in.createTypedArray(Signature.CREATOR); + this.mAppMetaData = in.readBundle(Bundle.class.getClassLoader()); +// this.mExtras = in.readParcelable(BPackageSettings.class.getClassLoader()); + this.packageName = in.readString(); + this.mPreferredOrder = in.readInt(); + this.mSharedUserId = in.readString(); + this.usesLibraries = in.createStringArrayList(); + this.usesOptionalLibraries = in.createStringArrayList(); + this.mVersionCode = in.readInt(); + this.applicationInfo = in.readParcelable(ApplicationInfo.class.getClassLoader()); + this.mVersionName = in.readString(); + this.baseCodePath = in.readString(); + this.mSharedUserLabel = in.readInt(); + this.configPreferences = in.createTypedArrayList(ConfigurationInfo.CREATOR); + this.reqFeatures = in.createTypedArrayList(FeatureInfo.CREATOR); + } + + public final static class Activity extends Component { + public ActivityInfo info; + + public Activity(PackageParser.Activity activity) { + super(activity); + this.info = activity.info; + if (activity.intents != null) { + int size = activity.intents.size(); + this.intents = new ArrayList<>(size); + for (PackageParser.ActivityIntentInfo intent : activity.intents) { + this.intents.add(new ActivityIntentInfo(intent)); + } + } + } + + public Activity(Parcel parcel) { + super(parcel); + this.info = parcel.readParcelable(ActivityInfo.class.getClassLoader()); + int N = parcel.readInt(); + this.intents = new ArrayList<>(N); + while (N-- > 0) { + IntentInfo intentInfo = parcel.readParcelable(BPackage.class.getClassLoader()); + this.intents.add(new ActivityIntentInfo(intentInfo)); + } + } + } + + public static final class Service extends Component { + public ServiceInfo info; + + public Service(PackageParser.Service service) { + super(service); + info = service.info; + if (service.intents != null) { + int size = service.intents.size(); + intents = new ArrayList<>(size); + for (PackageParser.ServiceIntentInfo intent : service.intents) { + intents.add(new ServiceIntentInfo(intent)); + } + } + } + + public Service(Parcel parcel) { + super(parcel); + info = parcel.readParcelable(ServiceInfo.class.getClassLoader()); + int N = parcel.readInt(); + intents = new ArrayList<>(N); + while (N-- > 0) { + IntentInfo intentInfo = parcel.readParcelable(BPackage.class.getClassLoader()); + intents.add(new ServiceIntentInfo(intentInfo)); + } + } + } + + public static final class Provider extends Component { + public ProviderInfo info; + + public Provider(PackageParser.Provider provider) { + super(provider); + info = provider.info; + if (provider.intents != null) { + int size = provider.intents.size(); + intents = new ArrayList<>(size); + for (PackageParser.ProviderIntentInfo intent : provider.intents) { + intents.add(new ProviderIntentInfo(intent)); + } + } + } + + public Provider(Parcel parcel) { + super(parcel); + info = parcel.readParcelable(ProviderInfo.class.getClassLoader()); + int N = parcel.readInt(); + intents = new ArrayList<>(N); + while (N-- > 0) { + IntentInfo intentInfo = parcel.readParcelable(BPackage.class.getClassLoader()); + intents.add(new ProviderIntentInfo(intentInfo)); + } + } + } + + public static final class Instrumentation extends Component { + public InstrumentationInfo info; + + public Instrumentation(PackageParser.Instrumentation instrumentation) { + super(instrumentation); + info = instrumentation.info; + if (instrumentation.intents != null) { + int size = instrumentation.intents.size(); + this.intents = new ArrayList<>(size); + for (PackageParser.IntentInfo intent : instrumentation.intents) { + this.intents.add(new IntentInfo(intent)); + } + } + } + + public Instrumentation(Parcel parcel) { + super(parcel); + this.info = parcel.readParcelable(InstrumentationInfo.class.getClassLoader()); + int N = parcel.readInt(); + this.intents = new ArrayList<>(N); + while (N-- > 0) { + IntentInfo intentInfo = parcel.readParcelable(BPackage.class.getClassLoader()); + this.intents.add(intentInfo); + } + } + } + + public static final class Permission extends Component { + public PermissionInfo info; + + public Permission(PackageParser.Permission permission) { + super(permission); + this.info = permission.info; + if (permission.intents != null) { + int size = permission.intents.size(); + this.intents = new ArrayList<>(size); + for (PackageParser.IntentInfo intent : permission.intents) { + this.intents.add(new IntentInfo(intent)); + } + } + } + + public Permission(Parcel parcel) { + super(parcel); + this.info = parcel.readParcelable(Permission.class.getClassLoader()); + int N = parcel.readInt(); + this.intents = new ArrayList<>(N); + while (N-- > 0) { + IntentInfo intentInfo = parcel.readParcelable(BPackage.class.getClassLoader()); + this.intents.add(intentInfo); + } + } + } + + public static final class PermissionGroup extends Component { + public PermissionGroupInfo info; + + public PermissionGroup(PackageParser.PermissionGroup group) { + super(group); + this.info = group.info; + if (group.intents != null) { + int size = group.intents.size(); + this.intents = new ArrayList<>(size); + for (PackageParser.IntentInfo intent : group.intents) { + this.intents.add(new IntentInfo(intent)); + } + } + } + + public PermissionGroup(Parcel parcel) { + super(parcel); + this.info = parcel.readParcelable(PermissionGroup.class.getClassLoader()); + int N = parcel.readInt(); + this.intents = new ArrayList<>(N); + while (N-- > 0) { + IntentInfo intentInfo = parcel.readParcelable(BPackage.class.getClassLoader()); + this.intents.add(intentInfo); + } + } + } + + public static class ActivityIntentInfo extends IntentInfo { + public Activity activity; + + public ActivityIntentInfo(PackageParser.IntentInfo intentInfo) { + super(intentInfo); + } + + public ActivityIntentInfo(IntentInfo intentInfo) { + super(intentInfo); + } + } + + public static class ServiceIntentInfo extends IntentInfo { + public Service service; + + public ServiceIntentInfo(PackageParser.IntentInfo intentInfo) { + super(intentInfo); + } + + public ServiceIntentInfo(IntentInfo intentInfo) { + super(intentInfo); + } + } + + public static class ProviderIntentInfo extends IntentInfo { + public Provider provider; + + public ProviderIntentInfo(PackageParser.IntentInfo intentInfo) { + super(intentInfo); + } + + public ProviderIntentInfo(IntentInfo intentInfo) { + super(intentInfo); + } + } + + public static final class SigningDetails implements Parcelable { + public Signature[] signatures; + + public static final PackageParser.SigningDetails UNKNOWN = null; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeTypedArray(this.signatures, flags); + } + + public SigningDetails(PackageParser.SigningDetails signingDetails) { + this.signatures = signingDetails.signatures; + } + + protected SigningDetails(Parcel in) { + this.signatures = in.createTypedArray(Signature.CREATOR); + } + + public static final Creator CREATOR = new Creator() { + @Override + public SigningDetails createFromParcel(Parcel source) { + return new SigningDetails(source); + } + + @Override + public SigningDetails[] newArray(int size) { + return new SigningDetails[size]; + } + }; + } + + public static class IntentInfo implements Parcelable { + public IntentFilter intentFilter; + public boolean hasDefault; + public int labelRes; + public String nonLocalizedLabel; + public int icon; + public int logo; + public int banner; + + public IntentInfo(PackageParser.IntentInfo intentInfo) { + this.intentFilter = intentInfo; + this.hasDefault = intentInfo.hasDefault; + this.labelRes = intentInfo.labelRes; + this.nonLocalizedLabel = intentInfo.nonLocalizedLabel == null ? null : intentInfo.nonLocalizedLabel.toString(); + this.icon = intentInfo.icon; + this.logo = intentInfo.logo; + this.banner = intentInfo.banner; + } + + public IntentInfo(IntentInfo intentInfo) { + this.intentFilter = intentInfo.intentFilter; + this.hasDefault = intentInfo.hasDefault; + this.labelRes = intentInfo.labelRes; + this.nonLocalizedLabel = intentInfo.nonLocalizedLabel == null ? null : intentInfo.nonLocalizedLabel.toString(); + this.icon = intentInfo.icon; + this.logo = intentInfo.logo; + this.banner = intentInfo.banner; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeParcelable(this.intentFilter, flags); + dest.writeByte(this.hasDefault ? (byte) 1 : (byte) 0); + dest.writeInt(this.labelRes); + dest.writeString(this.nonLocalizedLabel); + dest.writeInt(this.icon); + dest.writeInt(this.logo); + dest.writeInt(this.banner); + } + + protected IntentInfo(Parcel in) { + this.intentFilter = in.readParcelable(BPackage.class.getClassLoader()); + this.hasDefault = in.readByte() != 0; + this.labelRes = in.readInt(); + this.nonLocalizedLabel = in.readString(); + this.icon = in.readInt(); + this.logo = in.readInt(); + this.banner = in.readInt(); + } + + public static final Creator CREATOR = new Creator() { + @Override + public IntentInfo createFromParcel(Parcel source) { + return new IntentInfo(source); + } + + @Override + public IntentInfo[] newArray(int size) { + return new IntentInfo[size]; + } + }; + } + + public static class Component { + public BPackage owner; + public ArrayList intents; + public String className; + public Bundle metaData; + public ComponentName componentName; + + public Component(Parcel parcel) { + this.className = parcel.readString(); + this.metaData = parcel.readBundle(Bundle.class.getClassLoader()); + } + + public Component(PackageParser.Component component) { + this.className = component.className; + this.metaData = component.metaData; + } + + public ComponentName getComponentName() { + if (componentName != null) { + return componentName; + } + if (className != null) { + componentName = new ComponentName(owner.packageName, + className); + } + return componentName; + } + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + int size = this.activities.size(); + dest.writeInt(size); + for (Activity activity : this.activities) { + dest.writeString(activity.className); + dest.writeBundle(activity.metaData); + + dest.writeParcelable(activity.info, flags); + if (activity.intents != null) { + int N = activity.intents.size(); + dest.writeInt(N); + while (N-- > 0) { + dest.writeParcelable(activity.intents.get(N), flags); + } + } else { + dest.writeInt(0); + } + } + + size = this.receivers.size(); + dest.writeInt(size); + for (Activity receiver : this.receivers) { + dest.writeString(receiver.className); + dest.writeBundle(receiver.metaData); + + dest.writeParcelable(receiver.info, flags); + if (receiver.intents != null) { + int N = receiver.intents.size(); + dest.writeInt(N); + while (N-- > 0) { + dest.writeParcelable(receiver.intents.get(N), flags); + } + } else { + dest.writeInt(0); + } + } + + size = this.providers.size(); + dest.writeInt(size); + for (Provider provider : this.providers) { + dest.writeString(provider.className); + dest.writeBundle(provider.metaData); + + dest.writeParcelable(provider.info, flags); + if (provider.intents != null) { + int N = provider.intents.size(); + dest.writeInt(N); + while (N-- > 0) { + dest.writeParcelable(provider.intents.get(N), flags); + } + } else { + dest.writeInt(0); + } + } + + size = this.services.size(); + dest.writeInt(size); + for (Service service : this.services) { + dest.writeString(service.className); + dest.writeBundle(service.metaData); + + dest.writeParcelable(service.info, flags); + if (service.intents != null) { + int N = service.intents.size(); + dest.writeInt(N); + while (N-- > 0) { + dest.writeParcelable(service.intents.get(N), flags); + } + } else { + dest.writeInt(0); + } + } + + size = this.instrumentation.size(); + dest.writeInt(size); + for (Instrumentation instrumentation : this.instrumentation) { + dest.writeString(instrumentation.className); + dest.writeBundle(instrumentation.metaData); + + dest.writeParcelable(instrumentation.info, flags); + if (instrumentation.intents != null) { + int N = instrumentation.intents.size(); + dest.writeInt(N); + while (N-- > 0) { + dest.writeParcelable(instrumentation.intents.get(N), flags); + } + } else { + dest.writeInt(0); + } + } + + size = this.permissions.size(); + dest.writeInt(size); + for (Permission permission : this.permissions) { + dest.writeString(permission.className); + dest.writeBundle(permission.metaData); + + dest.writeParcelable(permission.info, flags); + if (permission.intents != null) { + int N = permission.intents.size(); + dest.writeInt(N); + while (N-- > 0) { + dest.writeParcelable(permission.intents.get(N), flags); + } + } else { + dest.writeInt(0); + } + } + + size = this.permissionGroups.size(); + dest.writeInt(size); + for (PermissionGroup permissionGroup : this.permissionGroups) { + dest.writeString(permissionGroup.className); + dest.writeBundle(permissionGroup.metaData); + + dest.writeParcelable(permissionGroup.info, flags); + if (permissionGroup.intents != null) { + int N = permissionGroup.intents.size(); + dest.writeInt(N); + while (N-- > 0) { + dest.writeParcelable(permissionGroup.intents.get(N), flags); + } + } else { + dest.writeInt(0); + } + } + + dest.writeStringList(this.requestedPermissions); + if (BuildCompat.isPie()) { + dest.writeParcelable(this.mSigningDetails, flags); + } + dest.writeTypedArray(this.mSignatures, flags); + dest.writeBundle(this.mAppMetaData); +// dest.writeParcelable(this.mExtras, flags); + dest.writeString(this.packageName); + dest.writeInt(this.mPreferredOrder); + dest.writeString(this.mSharedUserId); + dest.writeStringList(this.usesLibraries); + dest.writeStringList(this.usesOptionalLibraries); + dest.writeInt(this.mVersionCode); + dest.writeParcelable(this.applicationInfo, flags); + dest.writeString(this.mVersionName); + dest.writeString(this.baseCodePath); + dest.writeInt(this.mSharedUserLabel); + dest.writeTypedList(this.configPreferences); + dest.writeTypedList(this.reqFeatures); + } + + public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { + @Override + public BPackage createFromParcel(Parcel source) { + return new BPackage(source); + } + + @Override + public BPackage[] newArray(int size) { + return new BPackage[size]; + } + }; +} diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/core/system/pm/BPackageInstallerService.java b/Bcore/src/main/java/top/niunaijun/blackbox/core/system/pm/BPackageInstallerService.java new file mode 100644 index 00000000..f1b0180c --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/core/system/pm/BPackageInstallerService.java @@ -0,0 +1,95 @@ +package top.niunaijun.blackbox.core.system.pm; + +import android.os.RemoteException; + +import java.util.ArrayList; +import java.util.List; + +import top.niunaijun.blackbox.entity.pm.InstallOption; +import top.niunaijun.blackbox.core.system.ISystemService; +import top.niunaijun.blackbox.core.system.pm.installer.CopyExecutor; +import top.niunaijun.blackbox.core.system.pm.installer.CreatePackageExecutor; +import top.niunaijun.blackbox.core.system.pm.installer.CreateUserExecutor; +import top.niunaijun.blackbox.core.system.pm.installer.Executor; +import top.niunaijun.blackbox.core.system.pm.installer.RemoveAppExecutor; +import top.niunaijun.blackbox.core.system.pm.installer.RemoveUserExecutor; +import top.niunaijun.blackbox.utils.Slog; + +/** + * Created by Milk on 4/21/21. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + */ +public class BPackageInstallerService extends IBPackageInstallerService.Stub implements ISystemService { + private static final BPackageInstallerService sService = new BPackageInstallerService(); + + public static BPackageInstallerService get() { + return sService; + } + + public static final String TAG = "BPackageInstallerService"; + + @Override + public int installPackageAsUser(BPackageSettings ps, int userId) throws RemoteException { + List executors = new ArrayList<>(); + // 创建用户环境相关操作 + executors.add(new CreateUserExecutor()); + // 创建应用环境相关操作 + executors.add(new CreatePackageExecutor()); + // 拷贝应用相关文件 + executors.add(new CopyExecutor()); + InstallOption option = ps.installOption; + for (Executor executor : executors) { + int exec = executor.exec(ps, option, userId); + Slog.d(TAG, "installPackageAsUser: " + executor.getClass().getSimpleName() + " exec: " + exec); + if (exec != 0) { + return exec; + } + } + return 0; + } + + @Override + public int uninstallPackageAsUser(BPackageSettings ps, boolean removeApp, int userId) { + List executors = new ArrayList<>(); + if (removeApp) { + // 移除App + executors.add(new RemoveAppExecutor()); + } + // 移除用户相关目录 + executors.add(new RemoveUserExecutor()); + InstallOption option = ps.installOption; + for (Executor executor : executors) { + int exec = executor.exec(ps, option, userId); + Slog.d(TAG, "uninstallPackageAsUser: " + executor.getClass().getSimpleName() + " exec: " + exec); + if (exec != 0) { + return exec; + } + } + return 0; + } + + @Override + public int updatePackage(BPackageSettings ps) { + List executors = new ArrayList<>(); + executors.add(new CreatePackageExecutor()); + executors.add(new CopyExecutor()); + InstallOption option = ps.installOption; + for (Executor executor : executors) { + int exec = executor.exec(ps, option, -1); + Slog.d(TAG, "updatePackage: " + executor.getClass().getSimpleName() + " exec: " + exec); + if (exec != 0) { + return exec; + } + } + return 0; + } + + @Override + public void systemReady() { + + } +} diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/core/system/pm/BPackageManagerService.java b/Bcore/src/main/java/top/niunaijun/blackbox/core/system/pm/BPackageManagerService.java new file mode 100644 index 00000000..e1e2941d --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/core/system/pm/BPackageManagerService.java @@ -0,0 +1,714 @@ +package top.niunaijun.blackbox.core.system.pm; + +import android.content.ComponentName; +import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageParser; +import android.content.pm.ProviderInfo; +import android.content.pm.ResolveInfo; +import android.content.pm.ServiceInfo; +import android.net.Uri; +import android.os.Binder; +import android.os.RemoteException; +import android.text.TextUtils; + +import java.io.File; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.UUID; + +import top.niunaijun.blackbox.core.env.BEnvironment; +import top.niunaijun.blackbox.BlackBoxCore; +import top.niunaijun.blackbox.entity.pm.InstallOption; +import top.niunaijun.blackbox.entity.pm.InstallResult; +import top.niunaijun.blackbox.entity.pm.InstalledPackage; +import top.niunaijun.blackbox.core.system.BProcessManager; +import top.niunaijun.blackbox.core.system.ISystemService; +import top.niunaijun.blackbox.core.system.user.BUserManagerService; +import top.niunaijun.blackbox.utils.AbiUtils; +import top.niunaijun.blackbox.utils.FileUtils; +import top.niunaijun.blackbox.utils.Slog; +import top.niunaijun.blackbox.utils.compat.PackageParserCompat; + +import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE; + + +/** + * Created by Milk on 4/1/21. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + */ +public class BPackageManagerService extends IBPackageManagerService.Stub implements ISystemService { + public static final String TAG = "VPackageManagerService"; + public static BPackageManagerService sService = new BPackageManagerService(); + private final Settings mSettings = new Settings(); + private final ComponentResolver mComponentResolver; + private static final BUserManagerService sUserManager = BUserManagerService.get(); + private final List mPackageMonitors = new ArrayList<>(); + + final Map mPackages = mSettings.mPackages; + final Object mInstallLock = new Object(); + + public static BPackageManagerService get() { + return sService; + } + + public BPackageManagerService() { + mComponentResolver = new ComponentResolver(); + } + + @Override + public ApplicationInfo getApplicationInfo(String packageName, int flags, int userId) { + if (!sUserManager.exists(userId)) return null; + if (Objects.equals(packageName, BlackBoxCore.getHostPkg())) { + try { + return BlackBoxCore.getPackageManager().getApplicationInfo(packageName, flags); + } catch (PackageManager.NameNotFoundException e) { + e.printStackTrace(); + } + return null; + } + flags = updateFlags(flags, userId); + // reader + synchronized (mPackages) { + // Normalize package name to handle renamed packages and static libs + BPackageSettings ps = mPackages.get(packageName); + if (ps != null) { + BPackage p = ps.pkg; + return PackageManagerCompat.generateApplicationInfo(p, flags, ps.readUserState(userId), userId); + } + } + return null; + } + + @Override + public ResolveInfo resolveService(Intent intent, int flags, String resolvedType, int userId) { + if (!sUserManager.exists(userId)) return null; + List query = queryIntentServicesInternal( + intent, resolvedType, flags, userId); + if (query != null) { + if (query.size() >= 1) { + // If there is more than one service with the same priority, + // just arbitrarily pick the first one. + return query.get(0); + } + } + return null; + } + + private List queryIntentServicesInternal(Intent intent, String resolvedType, int flags, int userId) { + ComponentName comp = intent.getComponent(); + if (comp == null) { + if (intent.getSelector() != null) { + intent = intent.getSelector(); + comp = intent.getComponent(); + } + } + if (comp != null) { + final List list = new ArrayList<>(1); + final ServiceInfo si = getServiceInfo(comp, flags, userId); + if (si != null) { + // When specifying an explicit component, we prevent the service from being + // used when either 1) the service is in an instant application and the + // caller is not the same instant application or 2) the calling package is + // ephemeral and the activity is not visible to ephemeral applications. + final ResolveInfo ri = new ResolveInfo(); + ri.serviceInfo = si; + list.add(ri); + } + return list; + } + + // reader + synchronized (mPackages) { + String pkgName = intent.getPackage(); + if (pkgName == null) { + return null; + } + BPackageSettings bPackageSettings = mPackages.get(pkgName); + if (bPackageSettings != null) { + final BPackage pkg = bPackageSettings.pkg; + return mComponentResolver.queryServices(intent, resolvedType, flags, pkg.services, + userId); + } + return Collections.emptyList(); + } + } + + @Override + public ResolveInfo resolveActivity(Intent intent, int flags, String resolvedType, int userId) { + if (!sUserManager.exists(userId)) return null; + List resolves = queryIntentActivities(intent, resolvedType, flags, userId); + return chooseBestActivity(intent, resolvedType, flags, resolves); + } + + @Override + public ProviderInfo resolveContentProvider(String authority, int flags, int userId) { + if (!sUserManager.exists(userId)) return null; + return mComponentResolver.queryProvider(authority, flags, userId); + } + + @Override + public ResolveInfo resolveIntent(Intent intent, String resolvedType, int flags, int userId) { + if (!sUserManager.exists(userId)) return null; + List resolves = queryIntentActivities(intent, resolvedType, flags, userId); + return chooseBestActivity(intent, resolvedType, flags, resolves); + } + + private ResolveInfo chooseBestActivity(Intent intent, String resolvedType, + int flags, List query) { + if (query != null) { + final int N = query.size(); + if (N == 1) { + return query.get(0); + } else if (N > 1) { + // If there is more than one activity with the same priority, + // then let the user decide between them. + ResolveInfo r0 = query.get(0); + ResolveInfo r1 = query.get(1); + // If the first activity has a higher priority, or a different + // default, then it is always desirable to pick it. + if (r0.priority != r1.priority + || r0.preferredOrder != r1.preferredOrder + || r0.isDefault != r1.isDefault) { + return query.get(0); + } + } + } + return null; + } + + private List queryIntentActivities(Intent intent, + String resolvedType, int flags, int userId) { + final String pkgName = intent.getPackage(); + ComponentName comp = intent.getComponent(); + if (comp == null) { + if (intent.getSelector() != null) { + intent = intent.getSelector(); + comp = intent.getComponent(); + } + } + + if (comp != null) { + final List list = new ArrayList<>(1); + final ActivityInfo ai = getActivity(comp, flags, userId); + if (ai != null) { + // When specifying an explicit component, we prevent the activity from being + // used when either 1) the calling package is normal and the activity is within + // an ephemeral application or 2) the calling package is ephemeral and the + // activity is not visible to ephemeral applications. + final ResolveInfo ri = new ResolveInfo(); + ri.activityInfo = ai; + list.add(ri); + return list; + } + } + + // reader + synchronized (mPackages) { + if (pkgName != null) { + return mComponentResolver.queryActivities(intent, resolvedType, flags, userId); + } + } + return Collections.emptyList(); + } + + + private ActivityInfo getActivity(ComponentName component, int flags, + int userId) { + flags = updateFlags(flags, userId); + synchronized (mPackages) { + BPackage.Activity a = mComponentResolver.getActivity(component); + + if (a != null) { + BPackageSettings ps = mPackages.get(component.getPackageName()); + if (ps == null) return null; + return PackageManagerCompat.generateActivityInfo(a, flags, ps.readUserState(userId), userId); + } + } + return null; + } + + @Override + public PackageInfo getPackageInfo(String packageName, int flags, int userId) { + if (!sUserManager.exists(userId)) return null; + if (Objects.equals(packageName, BlackBoxCore.getHostPkg())) { + try { + return BlackBoxCore.getPackageManager().getPackageInfo(packageName, flags); + } catch (PackageManager.NameNotFoundException e) { + e.printStackTrace(); + } + return null; + } + + flags = updateFlags(flags, userId); + // reader + synchronized (mPackages) { + // Normalize package name to handle renamed packages and static libs + BPackageSettings ps = mPackages.get(packageName); + if (ps != null) { + return PackageManagerCompat.generatePackageInfo(ps, flags, ps.readUserState(userId), userId); + } + } + return null; + } + + @Override + public ServiceInfo getServiceInfo(ComponentName component, int flags, int userId) { + if (!sUserManager.exists(userId)) return null; + synchronized (mPackages) { + BPackage.Service s = mComponentResolver.getService(component); + if (s != null) { + BPackageSettings ps = mPackages.get(component.getPackageName()); + if (ps == null) return null; + return PackageManagerCompat.generateServiceInfo( + s, flags, ps.readUserState(userId), userId); + } + } + return null; + } + + @Override + public ActivityInfo getReceiverInfo(ComponentName component, int flags, int userId) { + if (!sUserManager.exists(userId)) return null; + synchronized (mPackages) { + BPackage.Activity a = mComponentResolver.getReceiver(component); + if (a != null) { + BPackageSettings ps = mPackages.get(component.getPackageName()); + if (ps == null) return null; + return PackageManagerCompat.generateActivityInfo( + a, flags, ps.readUserState(userId), userId); + } + } + return null; + } + + @Override + public ActivityInfo getActivityInfo(ComponentName component, int flags, int userId) { + if (!sUserManager.exists(userId)) return null; + synchronized (mPackages) { + BPackage.Activity a = mComponentResolver.getActivity(component); + + if (a != null) { + BPackageSettings ps = mPackages.get(component.getPackageName()); + if (ps == null) return null; + return PackageManagerCompat.generateActivityInfo( + a, flags, ps.readUserState(userId), userId); + } + } + return null; + } + + @Override + public ProviderInfo getProviderInfo(ComponentName component, int flags, int userId) { + if (!sUserManager.exists(userId)) return null; + synchronized (mPackages) { + BPackage.Provider p = mComponentResolver.getProvider(component); + if (p != null) { + BPackageSettings ps = mPackages.get(component.getPackageName()); + if (ps == null) return null; + return PackageManagerCompat.generateProviderInfo( + p, flags, ps.readUserState(userId), userId); + } + } + return null; + } + + @Override + public List getInstalledApplications(int flags, int userId) { + return getInstalledApplicationsListInternal(flags, userId, Binder.getCallingUid()); + } + + @Override + public List getInstalledPackages(int flags, int userId) { + final int callingUid = Binder.getCallingUid(); +// if (getInstantAppPackageName(callingUid) != null) { +// return ParceledListSlice.emptyList(); +// } + if (!sUserManager.exists(userId)) return Collections.emptyList(); + + // writer + synchronized (mPackages) { + ArrayList list; + list = new ArrayList<>(mPackages.size()); + for (BPackageSettings ps : mPackages.values()) { +// if (filterSharedLibPackageLPr(ps, callingUid, userId, flags)) { +// continue; +// } +// if (filterAppAccessLPr(ps, callingUid, userId)) { +// continue; +// } + final PackageInfo pi = getPackageInfo(ps.pkg.packageName, flags, userId); + if (pi != null) { + list.add(pi); + } + } + return new ArrayList<>(list); + } + } + + private List getInstalledApplicationsListInternal(int flags, int userId, + int callingUid) { + if (!sUserManager.exists(userId)) return Collections.emptyList(); + + // writer + synchronized (mPackages) { + ArrayList list; + list = new ArrayList<>(mPackages.size()); + Collection packageSettings = mPackages.values(); + for (BPackageSettings ps : packageSettings) { +// if (filterSharedLibPackageLPr(ps, Binder.getCallingUid(), userId, flags)) { +// continue; +// } +// if (filterAppAccessLPr(ps, callingUid, userId)) { +// continue; +// } + ApplicationInfo ai = PackageManagerCompat.generateApplicationInfo(ps.pkg, flags, + ps.readUserState(userId), userId); + if (ai != null) { + list.add(ai); + } + } + return list; + } + } + + @Override + public List queryIntentActivities(Intent intent, int flags, String resolvedType, int userId) throws RemoteException { + if (!sUserManager.exists(userId)) return Collections.emptyList(); + final String pkgName = intent.getPackage(); + ComponentName comp = intent.getComponent(); + if (comp == null) { + if (intent.getSelector() != null) { + intent = intent.getSelector(); + comp = intent.getComponent(); + } + } + + if (comp != null) { + final List list = new ArrayList<>(1); + final ActivityInfo ai = getActivityInfo(comp, flags, userId); + if (ai != null) { + // When specifying an explicit component, we prevent the activity from being + // used when either 1) the calling package is normal and the activity is within + // an ephemeral application or 2) the calling package is ephemeral and the + // activity is not visible to ephemeral applications. + final ResolveInfo ri = new ResolveInfo(); + ri.activityInfo = ai; + list.add(ri); + } + return list; + } + + // reader + List result; + synchronized (mPackages) { + if (pkgName != null) { + BPackageSettings bPackageSettings = mPackages.get(pkgName); + result = null; + if (bPackageSettings != null) { + final BPackage pkg = bPackageSettings.pkg; + + result = mComponentResolver.queryActivities( + intent, resolvedType, flags, pkg.activities, userId); + } + if (result == null || result.size() == 0) { + // the caller wants to resolve for a particular package; however, there + // were no installed results, so, try to find an ephemeral result + if (result == null) { + result = new ArrayList<>(); + } + } + return result; + } + } + return Collections.emptyList(); + } + + @Override + public List queryBroadcastReceivers(Intent intent, int flags, String resolvedType, int userId) throws RemoteException { + if (!sUserManager.exists(userId)) return Collections.emptyList(); + + ComponentName comp = intent.getComponent(); + if (comp == null) { + if (intent.getSelector() != null) { + intent = intent.getSelector(); + comp = intent.getComponent(); + } + } + if (comp != null) { + final List list = new ArrayList<>(1); + final ActivityInfo ai = getReceiverInfo(comp, flags, userId); + if (ai != null) { + // When specifying an explicit component, we prevent the activity from being + // used when either 1) the calling package is normal and the activity is within + // an instant application or 2) the calling package is ephemeral and the + // activity is not visible to instant applications. + ResolveInfo ri = new ResolveInfo(); + ri.activityInfo = ai; + list.add(ri); + } + return list; + } + + // reader + synchronized (mPackages) { + String pkgName = intent.getPackage(); + BPackageSettings bPackageSettings = mPackages.get(pkgName); + if (bPackageSettings != null) { + final BPackage pkg = bPackageSettings.pkg; + return mComponentResolver.queryReceivers( + intent, resolvedType, flags, pkg.receivers, userId); + } + return Collections.emptyList(); + } + } + + @Override + public List queryContentProviders(String processName, int uid, int flags, int userId) throws RemoteException { + if (!sUserManager.exists(userId)) return Collections.emptyList(); + + List providers = new ArrayList<>(); + if (TextUtils.isEmpty(processName)) + return providers; + providers.addAll(mComponentResolver.queryProviders(processName, null, flags, userId)); + return providers; + } + + @Override + public InstallResult installPackageAsUser(String file, InstallOption option, int userId) { + synchronized (mInstallLock) { + return installPackageAsUserLocked(file, option, userId); + } + } + + @Override + public void uninstallPackageAsUser(String packageName, int userId) throws RemoteException { + synchronized (mInstallLock) { + synchronized (mPackages) { + BPackageSettings ps = mPackages.get(packageName); + if (ps == null) + return; + if (!isInstalled(packageName, userId)) { + return; + } + boolean removeApp = ps.getUserState().size() <= 1; + BProcessManager.get().killPackageAsUser(packageName, userId); + int i = BPackageInstallerService.get().uninstallPackageAsUser(ps, removeApp, userId); + if (i < 0) { + // todo + } + + if (removeApp) { + mPackages.remove(packageName); + mComponentResolver.removeAllComponents(ps.pkg); + mSettings.scanPackage(); + } else { + ps.removeUser(userId); + ps.save(); + } + onPackageUninstalled(packageName, userId); + } + } + } + + @Override + public void uninstallPackage(String packageName) { + synchronized (mInstallLock) { + synchronized (mPackages) { + BPackageSettings ps = mPackages.get(packageName); + if (ps == null) + return; + BProcessManager.get().killAllByPackageName(packageName); + for (Integer userId : ps.getUserIds()) { + int i = BPackageInstallerService.get().uninstallPackageAsUser(ps, true, userId); + if (i < 0) { + continue; + } + onPackageUninstalled(packageName, userId); + } + mPackages.remove(packageName); + mComponentResolver.removeAllComponents(ps.pkg); + mSettings.scanPackage(); + } + } + } + + @Override + public void deleteUser(int userId) throws RemoteException { + synchronized (mPackages) { + for (BPackageSettings ps : mPackages.values()) { + uninstallPackageAsUser(ps.pkg.packageName, userId); + } + } + } + + @Override + public boolean isInstalled(String packageName, int userId) { + if (!sUserManager.exists(userId)) return false; + synchronized (mPackages) { + BPackageSettings ps = mPackages.get(packageName); + if (ps == null) + return false; + return ps.getInstalled(userId); + } + } + + @Override + public List getInstalledPackagesAsUser(int userId) { + if (!sUserManager.exists(userId)) return Collections.emptyList(); + synchronized (mPackages) { + List installedPackages = new ArrayList<>(); + for (BPackageSettings ps : mPackages.values()) { + if (ps.getInstalled(userId)) { + InstalledPackage installedPackage = new InstalledPackage(); + installedPackage.userId = userId; + installedPackage.packageName = ps.pkg.packageName; + installedPackages.add(installedPackage); + } + } + return installedPackages; + } + } + + private InstallResult installPackageAsUserLocked(String file, InstallOption option, int userId) { + InstallResult result = new InstallResult(); + File apkFile = null; + try { + if (!sUserManager.exists(userId)) { + sUserManager.createUser(userId); + } + if (option.isFlag(InstallOption.FLAG_URI_FILE)) { + apkFile = new File(BEnvironment.getCacheDir(), UUID.randomUUID().toString() + ".apk"); + InputStream inputStream = BlackBoxCore.getContext().getContentResolver().openInputStream(Uri.parse(file)); + FileUtils.copyFile(inputStream, apkFile); + } else { + apkFile = new File(file); + } + + boolean support = AbiUtils.isSupport(apkFile); + if (!support) { + return result.installError(BlackBoxCore.is64Bit() ? "not support armeabi-v7a abi" : "not support arm64-v8a abi"); + } + + PackageParser.Package aPackage = parserApk(apkFile.getAbsolutePath()); + if (aPackage == null) { + return result.installError("parser apk error."); + } + result.packageName = aPackage.packageName; + + BPackageSettings bPackageSettings = mSettings.getPackageLPw(aPackage.packageName, aPackage); + bPackageSettings.installOption = option; + + // stop pkg + BProcessManager.get().killPackageAsUser(aPackage.packageName, userId); + + int i = BPackageInstallerService.get().installPackageAsUser(bPackageSettings, userId); + if (i < 0) { + return result.installError("install apk error."); + } + synchronized (mPackages) { + bPackageSettings.setInstalled(true, userId); + bPackageSettings.save(); + } + mComponentResolver.addAllComponents(bPackageSettings.pkg); + mSettings.scanPackage(); + onPackageInstalled(bPackageSettings.pkg.packageName, userId); + return result; + } catch (Throwable t) { + t.printStackTrace(); + } finally { + if (apkFile != null && option.isFlag(InstallOption.FLAG_URI_FILE)) { + FileUtils.deleteDir(apkFile); + } + } + return result; + } + + private PackageParser.Package parserApk(String file) { + try { + PackageParser parser = PackageParserCompat.createParser(new File(file)); + PackageParser.Package aPackage = PackageParserCompat.parsePackage(parser, new File(file), 0); + PackageParserCompat.collectCertificates(parser, aPackage, 0); + return aPackage; + } catch (Throwable t) { + t.printStackTrace(); + } + return null; + } + + static String fixProcessName(String defProcessName, String processName) { + if (processName == null) { + return defProcessName; + } + return processName; + } + + /** + * Update given flags based on encryption status of current user. + */ + private int updateFlags(int flags, int userId) { + if ((flags & (PackageManager.MATCH_DIRECT_BOOT_UNAWARE + | PackageManager.MATCH_DIRECT_BOOT_AWARE)) != 0) { + // Caller expressed an explicit opinion about what encryption + // aware/unaware components they want to see, so fall through and + // give them what they want + } else { + // Caller expressed no opinion, so match based on user state + flags |= PackageManager.MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE; + } + return flags; + } + + public int getAppId(String packageName) { + BPackageSettings bPackageSettings = mPackages.get(packageName); + if (bPackageSettings != null) + return bPackageSettings.appId; + return -1; + } + + Settings getSettings() { + return mSettings; + } + + public void addPackageMonitor(PackageMonitor monitor) { + mPackageMonitors.add(monitor); + } + + public void removePackageMonitor(PackageMonitor monitor) { + mPackageMonitors.add(monitor); + } + + void onPackageUninstalled(String packageName, int userId) { + for (PackageMonitor packageMonitor : mPackageMonitors) { + packageMonitor.onPackageUninstalled(packageName, userId); + } + Slog.d(TAG, "onPackageUninstalled: " + packageName + ", userId: " + userId); + } + + void onPackageInstalled(String packageName, int userId) { + for (PackageMonitor packageMonitor : mPackageMonitors) { + packageMonitor.onPackageInstalled(packageName, userId); + } + Slog.d(TAG, "onPackageInstalled: " + packageName + ", userId: " + userId); + } + + @Override + public void systemReady() { + mSettings.scanPackage(); + for (BPackageSettings value : mPackages.values()) { + mComponentResolver.addAllComponents(value.pkg); + } + } +} \ No newline at end of file diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/core/system/pm/BPackageSettings.java b/Bcore/src/main/java/top/niunaijun/blackbox/core/system/pm/BPackageSettings.java new file mode 100644 index 00000000..3fafc968 --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/core/system/pm/BPackageSettings.java @@ -0,0 +1,159 @@ +package top.niunaijun.blackbox.core.system.pm; + +import android.os.Parcel; +import android.os.Parcelable; +import android.util.AtomicFile; + +import java.io.FileOutputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import top.niunaijun.blackbox.core.env.BEnvironment; +import top.niunaijun.blackbox.entity.pm.InstallOption; +import top.niunaijun.blackbox.core.system.user.BUserHandle; +import top.niunaijun.blackbox.utils.CloseUtils; +import top.niunaijun.blackbox.utils.FileUtils; + +/** + * Created by Milk on 4/21/21. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + */ +public class BPackageSettings implements Parcelable { + public BPackage pkg; + public int appId; + public InstallOption installOption; + public Map userState = new HashMap<>(); + static final BPackageUserState DEFAULT_USER_STATE = new BPackageUserState(); + + public BPackageSettings() { + } + + public List getUserState() { + return new ArrayList<>(userState.values()); + } + + public List getUserIds() { + return new ArrayList<>(userState.keySet()); + } + + public void setInstalled(boolean inst, int userId) { + modifyUserState(userId).installed = inst; + } + + public boolean getInstalled(int userId) { + return readUserState(userId).installed; + } + + public boolean getStopped(int userId) { + return readUserState(userId).stopped; + } + + public void setStopped(boolean stop, int userId) { + modifyUserState(userId).stopped = stop; + } + + public boolean getHidden(int userId) { + return readUserState(userId).hidden; + } + + public void setHidden(boolean hidden, int userId) { + modifyUserState(userId).hidden = hidden; + } + + public void removeUser(int userId) { + userState.remove(userId); + } + + public BPackageUserState readUserState(int userId) { + BPackageUserState state = userState.get(userId); + if (state == null) { + state = new BPackageUserState(); + } + state = new BPackageUserState(state); + + if (userState.get(BUserHandle.USER_ALL) != null) { + state.installed = true; + } + return state; + } + + private BPackageUserState modifyUserState(int userId) { + BPackageUserState state = userState.get(userId); + if (state == null) { + state = new BPackageUserState(); + userState.put(userId, state); + } + return state; + } + + public boolean save() { + synchronized (this) { + Parcel parcel = Parcel.obtain(); + AtomicFile atomicFile = new AtomicFile(BEnvironment.getPackageConf(pkg.packageName)); + FileOutputStream fileOutputStream = null; + try { + writeToParcel(parcel, 0); + parcel.setDataPosition(0); + fileOutputStream = atomicFile.startWrite(); + FileUtils.writeParcelToOutput(parcel, fileOutputStream); + atomicFile.finishWrite(fileOutputStream); + return true; + } catch (Throwable e) { + e.printStackTrace(); + atomicFile.failWrite(fileOutputStream); + return false; + } finally { + parcel.recycle(); + CloseUtils.close(fileOutputStream); + } + } + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeParcelable(this.pkg, flags); + dest.writeInt(this.appId); + dest.writeParcelable(this.installOption, flags); + dest.writeInt(this.userState.size()); + for (Map.Entry entry : this.userState.entrySet()) { + dest.writeValue(entry.getKey()); + dest.writeParcelable(entry.getValue(), flags); + } + } + + protected BPackageSettings(Parcel in) { + this.pkg = in.readParcelable(BPackage.class.getClassLoader()); + this.appId = in.readInt(); + this.installOption = in.readParcelable(InstallOption.class.getClassLoader()); + int userStateSize = in.readInt(); + this.userState = new HashMap(userStateSize); + for (int i = 0; i < userStateSize; i++) { + Integer key = (Integer) in.readValue(Integer.class.getClassLoader()); + BPackageUserState value = in.readParcelable(BPackageUserState.class.getClassLoader()); + this.userState.put(key, value); + } + } + + public static final Creator CREATOR = new Creator() { + @Override + public BPackageSettings createFromParcel(Parcel source) { + return new BPackageSettings(source); + } + + @Override + public BPackageSettings[] newArray(int size) { + return new BPackageSettings[size]; + } + }; +} diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/core/system/pm/BPackageUserState.java b/Bcore/src/main/java/top/niunaijun/blackbox/core/system/pm/BPackageUserState.java new file mode 100644 index 00000000..d31339c2 --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/core/system/pm/BPackageUserState.java @@ -0,0 +1,66 @@ +package top.niunaijun.blackbox.core.system.pm; + +import android.os.Parcel; +import android.os.Parcelable; + +/** + * Created by Milk on 4/27/21. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + */ +public class BPackageUserState implements Parcelable { + public boolean installed; + public boolean stopped; + public boolean hidden; + + public BPackageUserState() { + this.installed = false; + this.stopped = true; + this.hidden = false; + } + + public static BPackageUserState create() { + BPackageUserState state = new BPackageUserState(); + state.installed = true; + return state; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeByte(this.installed ? (byte) 1 : (byte) 0); + dest.writeByte(this.stopped ? (byte) 1 : (byte) 0); + dest.writeByte(this.hidden ? (byte) 1 : (byte) 0); + } + + protected BPackageUserState(Parcel in) { + this.installed = in.readByte() != 0; + this.stopped = in.readByte() != 0; + this.hidden = in.readByte() != 0; + } + + public BPackageUserState(BPackageUserState state) { + this.installed = state.installed; + this.stopped = state.stopped; + this.hidden = state.hidden; + } + + public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { + @Override + public BPackageUserState createFromParcel(Parcel source) { + return new BPackageUserState(source); + } + + @Override + public BPackageUserState[] newArray(int size) { + return new BPackageUserState[size]; + } + }; +} diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/core/system/pm/ComponentResolver.java b/Bcore/src/main/java/top/niunaijun/blackbox/core/system/pm/ComponentResolver.java new file mode 100644 index 00000000..73965d64 --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/core/system/pm/ComponentResolver.java @@ -0,0 +1,642 @@ +package top.niunaijun.blackbox.core.system.pm; + +import android.content.ComponentName; +import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.content.pm.PackageManager; +import android.content.pm.ProviderInfo; +import android.content.pm.ResolveInfo; +import android.content.pm.ServiceInfo; +import android.util.ArrayMap; + +import java.util.ArrayList; +import java.util.List; + +import top.niunaijun.blackbox.utils.Slog; + + +/** + * Created by Milk on 4/14/21. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + */ +public class ComponentResolver { + public static final String TAG = "ComponentResolver"; + + private final Object mLock = new Object(); + + /** + * All available activities, for your resolving pleasure. + */ + private final ActivityIntentResolver mActivities = new ActivityIntentResolver(); + + /** + * All available providers, for your resolving pleasure. + */ + private final ProviderIntentResolver mProviders = new ProviderIntentResolver(); + + /** + * All available receivers, for your resolving pleasure. + */ + private final ActivityIntentResolver mReceivers = new ActivityIntentResolver(); + + /** + * All available services, for your resolving pleasure. + */ + private final ServiceIntentResolver mServices = new ServiceIntentResolver(); + /** + * Mapping from provider authority [first directory in content URI codePath) to provider. + */ + private final ArrayMap mProvidersByAuthority = new ArrayMap<>(); + + public ComponentResolver() { + } + + void addAllComponents(BPackage pkg) { + final ArrayList newIntents = new ArrayList<>(); + synchronized (mLock) { + addActivitiesLocked(pkg, newIntents); + addServicesLocked(pkg); + addProvidersLocked(pkg); + addReceiversLocked(pkg); + } + } + + void removeAllComponents(BPackage pkg) { + synchronized (mLock) { + removeAllComponentsLocked(pkg); + } + } + + private void removeAllComponentsLocked(BPackage pkg) { + int componentSize; + StringBuilder r; + int i; + + componentSize = pkg.activities.size(); + r = null; + for (i = 0; i < componentSize; i++) { + BPackage.Activity a = pkg.activities.get(i); + mActivities.removeActivity(a, "activity"); + } + componentSize = pkg.providers.size(); + r = null; + for (i = 0; i < componentSize; i++) { + BPackage.Provider p = pkg.providers.get(i); + mProviders.removeProvider(p); + if (p.info.authority == null) { + // Another content provider with this authority existed when this app was + // installed, so this authority is null. Ignore it as we don't have to + // unregister the provider. + continue; + } + String[] names = p.info.authority.split(";"); + for (int j = 0; j < names.length; j++) { + if (mProvidersByAuthority.get(names[j]) == p) { + mProvidersByAuthority.remove(names[j]); + } + } + } + + componentSize = pkg.receivers.size(); + r = null; + for (i = 0; i < componentSize; i++) { + BPackage.Activity a = pkg.receivers.get(i); + mReceivers.removeActivity(a, "receiver"); + } + + componentSize = pkg.services.size(); + r = null; + for (i = 0; i < componentSize; i++) { + BPackage.Service s = pkg.services.get(i); + mServices.removeService(s); + } + } + + private void addActivitiesLocked(BPackage pkg, + List newIntents) { + final int activitiesSize = pkg.activities.size(); + for (int i = 0; i < activitiesSize; i++) { + BPackage.Activity a = pkg.activities.get(i); + a.info.processName = + BPackageManagerService.fixProcessName(pkg.applicationInfo.processName, a.info.processName); + mActivities.addActivity(a, "activity", newIntents); + } + } + + private void addProvidersLocked(BPackage pkg) { + final int providersSize = pkg.providers.size(); + for (int i = 0; i < providersSize; i++) { + BPackage.Provider p = pkg.providers.get(i); + p.info.processName = BPackageManagerService.fixProcessName(pkg.applicationInfo.processName, + p.info.processName); + mProviders.addProvider(p); + if (p.info.authority != null) { + String[] names = p.info.authority.split(";"); + p.info.authority = null; + for (String name : names) { + if (!mProvidersByAuthority.containsKey(name)) { + mProvidersByAuthority.put(name, p); + if (p.info.authority == null) { + p.info.authority = name; + } else { + p.info.authority = p.info.authority + ";" + name; + } + } else { + final BPackage.Provider other = + mProvidersByAuthority.get(name); + final ComponentName component = + (other != null && other.getComponentName() != null) + ? other.getComponentName() : null; + final String packageName = + component != null ? component.getPackageName() : "?"; + Slog.w(TAG, "Skipping provider name " + name + + " (in package " + pkg.applicationInfo.packageName + ")" + + ": name already used by " + packageName); + } + } + } + } + } + + private void addReceiversLocked(BPackage pkg) { + final int receiversSize = pkg.receivers.size(); + for (int i = 0; i < receiversSize; i++) { + BPackage.Activity a = pkg.receivers.get(i); + a.info.processName = BPackageManagerService.fixProcessName(pkg.applicationInfo.processName, + a.info.processName); + mReceivers.addActivity(a, "receiver", null); + } + } + + private void addServicesLocked(BPackage pkg) { + final int servicesSize = pkg.services.size(); + for (int i = 0; i < servicesSize; i++) { + BPackage.Service s = pkg.services.get(i); + s.info.processName = BPackageManagerService.fixProcessName(pkg.applicationInfo.processName, + s.info.processName); + mServices.addService(s); + } + } + + + /** + * Returns the given activity + */ + BPackage.Activity getActivity(ComponentName component) { + synchronized (mLock) { + return mActivities.mActivities.get(component); + } + } + + /** + * Returns the given provider + */ + BPackage.Provider getProvider(ComponentName component) { + synchronized (mLock) { + return mProviders.mProviders.get(component); + } + } + + /** + * Returns the given receiver + */ + BPackage.Activity getReceiver(ComponentName component) { + synchronized (mLock) { + return mReceivers.mActivities.get(component); + } + } + + /** + * Returns the given service + */ + BPackage.Service getService(ComponentName component) { + synchronized (mLock) { + return mServices.mServices.get(component); + } + } + + List queryActivities(Intent intent, String resolvedType, int flags, int userId) { + synchronized (mLock) { + return mActivities.queryIntent(intent, resolvedType, flags, userId); + } + } + + List queryActivities(Intent intent, String resolvedType, int flags, + List activities, int userId) { + synchronized (mLock) { + return mActivities.queryIntentForPackage( + intent, resolvedType, flags, activities, userId); + } + } + + List queryProviders(Intent intent, String resolvedType, int flags, int userId) { + synchronized (mLock) { + return mProviders.queryIntent(intent, resolvedType, flags, userId); + } + } + + List queryProviders(Intent intent, String resolvedType, int flags, + List providers, int userId) { + synchronized (mLock) { + return mProviders.queryIntentForPackage(intent, resolvedType, flags, providers, userId); + } + } + + List queryProviders(String processName, String metaDataKey, int flags, + int userId) { + List providerList = new ArrayList<>(); + synchronized (mLock) { + for (int i = mProviders.mProviders.size() - 1; i >= 0; --i) { + final BPackage.Provider p = mProviders.mProviders.valueAt(i); + final BPackageSettings ps = (BPackageSettings) p.owner.mExtras; + if (ps == null) { + continue; + } + if (p.info.authority == null) { + continue; + } + if (processName != null && (!p.info.processName.equals(processName))) { + continue; + } + // See PM.queryContentProviders()'s javadoc for why we have the metaData parameter. + if (metaDataKey != null + && (p.metaData == null || !p.metaData.containsKey(metaDataKey))) { + continue; + } + final ProviderInfo info = PackageManagerCompat.generateProviderInfo(p, flags, ps.readUserState(userId), userId); + if (info == null) { + continue; + } +// if (providerList == null) { +// providerList = new ArrayList<>(i + 1); +// } + providerList.add(info); + } + } + return providerList; + } + + ProviderInfo queryProvider(String authority, int flags, int userId) { + synchronized (mLock) { + final BPackage.Provider p = mProvidersByAuthority.get(authority); + if (p == null) { + return null; + } + BPackageSettings ps = p.owner.mExtras; + return PackageManagerCompat.generateProviderInfo(p, flags, ps.readUserState(userId), userId); + } + } + + List queryReceivers(Intent intent, String resolvedType, int flags, int userId) { + synchronized (mLock) { + return mReceivers.queryIntent(intent, resolvedType, flags, userId); + } + } + + List queryReceivers(Intent intent, String resolvedType, int flags, + List receivers, int userId) { + synchronized (mLock) { + return mReceivers.queryIntentForPackage(intent, resolvedType, flags, receivers, userId); + } + } + + List queryServices(Intent intent, String resolvedType, int flags, int userId) { + synchronized (mLock) { + return mServices.queryIntent(intent, resolvedType, flags, userId); + } + } + + List queryServices(Intent intent, String resolvedType, int flags, + List services, int userId) { + synchronized (mLock) { + return mServices.queryIntentForPackage(intent, resolvedType, flags, services, userId); + } + } + + + private static final class ServiceIntentResolver extends IntentResolver { + + @Override + public List queryIntent(Intent intent, String resolvedType, + boolean defaultOnly, int userId) { + mFlags = defaultOnly ? PackageManager.MATCH_DEFAULT_ONLY : 0; + return super.queryIntent(intent, resolvedType, defaultOnly, userId); + } + + List queryIntent(Intent intent, String resolvedType, int flags, + int userId) { + mFlags = flags; + return super.queryIntent(intent, resolvedType, + (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0, + userId); + } + + List queryIntentForPackage(Intent intent, String resolvedType, + int flags, List packageServices, int userId) { + if (packageServices == null) { + return null; + } + mFlags = flags; + final boolean defaultOnly = (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0; + final int servicesSize = packageServices.size(); + ArrayList listCut = new ArrayList<>(servicesSize); + + ArrayList intentFilters; + for (int i = 0; i < servicesSize; ++i) { + intentFilters = packageServices.get(i).intents; + if (intentFilters != null && intentFilters.size() > 0) { + BPackage.ServiceIntentInfo[] array = + new BPackage.ServiceIntentInfo[intentFilters.size()]; + intentFilters.toArray(array); + listCut.add(array); + } + } + return super.queryIntentFromList(intent, resolvedType, defaultOnly, listCut, userId); + } + + void addService(BPackage.Service s) { + mServices.put(s.getComponentName(), s); + final int intentsSize = s.intents.size(); + int j; + for (j = 0; j < intentsSize; j++) { + BPackage.ServiceIntentInfo intent = s.intents.get(j); + addFilter(intent); + } + } + + void removeService(BPackage.Service s) { + mServices.remove(s.getComponentName()); + final int intentsSize = s.intents.size(); + int j; + for (j = 0; j < intentsSize; j++) { + BPackage.ServiceIntentInfo intent = s.intents.get(j); + removeFilter(intent); + } + } + + @Override + protected boolean isPackageForFilter(String packageName, + BPackage.ServiceIntentInfo info) { + return packageName.equals(info.service.owner.packageName); + } + + @Override + protected BPackage.ServiceIntentInfo[] newArray(int size) { + return new BPackage.ServiceIntentInfo[size]; + } + + @Override + protected ResolveInfo newResult(BPackage.ServiceIntentInfo filter, int match, int userId) { + final BPackage.ServiceIntentInfo info = (BPackage.ServiceIntentInfo) filter; + final BPackage.Service service = info.service; + BPackageSettings ps = (BPackageSettings) service.owner.mExtras; + if (ps == null) { + return null; + } + ServiceInfo si = PackageManagerCompat.generateServiceInfo(service, mFlags, ps.readUserState(userId), userId); + + final ResolveInfo res = new ResolveInfo(); + res.serviceInfo = si; + if ((mFlags & PackageManager.GET_RESOLVED_FILTER) != 0) { + res.filter = filter.intentFilter; + } + res.priority = info.intentFilter.getPriority(); + res.preferredOrder = service.owner.mPreferredOrder; + res.match = match; + res.isDefault = info.hasDefault; + res.labelRes = info.labelRes; + res.nonLocalizedLabel = info.nonLocalizedLabel; + res.icon = info.icon; + return res; + } + + // Keys are String (activity class name), values are Activity. + private final ArrayMap mServices = new ArrayMap<>(); + private int mFlags; + } + + + private static final class ActivityIntentResolver extends IntentResolver { + + @Override + public List queryIntent(Intent intent, String resolvedType, + boolean defaultOnly, int userId) { + mFlags = (defaultOnly ? PackageManager.MATCH_DEFAULT_ONLY : 0); + return super.queryIntent(intent, resolvedType, defaultOnly, userId); + } + + List queryIntent(Intent intent, String resolvedType, int flags, + int userId) { + mFlags = flags; + return super.queryIntent(intent, resolvedType, + (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0, + userId); + } + + List queryIntentForPackage(Intent intent, String resolvedType, + int flags, List packageActivities, int userId) { + if (packageActivities == null) { + return null; + } + mFlags = flags; + final boolean defaultOnly = (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0; + final int activitiesSize = packageActivities.size(); + ArrayList listCut = new ArrayList<>(activitiesSize); + + ArrayList intentFilters; + for (int i = 0; i < activitiesSize; ++i) { + intentFilters = packageActivities.get(i).intents; + if (intentFilters != null && intentFilters.size() > 0) { + BPackage.ActivityIntentInfo[] array = + new BPackage.ActivityIntentInfo[intentFilters.size()]; + intentFilters.toArray(array); + listCut.add(array); + } + } + return super.queryIntentFromList(intent, resolvedType, defaultOnly, listCut, userId); + } + + private void addActivity(BPackage.Activity a, String type, + List newIntents) { + mActivities.put(a.getComponentName(), a); + final int intentsSize = a.intents.size(); + for (int j = 0; j < intentsSize; j++) { + BPackage.ActivityIntentInfo intent = a.intents.get(j); + if (newIntents != null && "activity".equals(type)) { + newIntents.add(intent); + addFilter(intent); + } + } + } + + private void removeActivity(BPackage.Activity a, String type) { + mActivities.remove(a.getComponentName()); + final int intentsSize = a.intents.size(); + for (int j = 0; j < intentsSize; j++) { + BPackage.ActivityIntentInfo intent = a.intents.get(j); + removeFilter(intent); + } + } + + @Override + protected boolean isPackageForFilter(String packageName, + BPackage.ActivityIntentInfo info) { + return packageName.equals(info.activity.owner.packageName); + } + + @Override + protected BPackage.ActivityIntentInfo[] newArray(int size) { + return new BPackage.ActivityIntentInfo[size]; + } + + @Override + protected ResolveInfo newResult(BPackage.ActivityIntentInfo info, int match, int userId) { + final BPackage.Activity activity = info.activity; + BPackageSettings ps = (BPackageSettings) activity.owner.mExtras; + if (ps == null) { + return null; + } + ActivityInfo ai = + PackageManagerCompat.generateActivityInfo(activity, mFlags, ps.readUserState(userId), userId); + + final ResolveInfo res = new ResolveInfo(); + res.activityInfo = ai; + if ((mFlags & PackageManager.GET_RESOLVED_FILTER) != 0) { + res.filter = info.intentFilter; + } + res.priority = info.intentFilter.getPriority(); + res.preferredOrder = activity.owner.mPreferredOrder; + //System.out.println("Result: " + res.activityInfo.className + + // " = " + res.priority); + res.match = match; + res.isDefault = info.hasDefault; + res.labelRes = info.labelRes; + res.nonLocalizedLabel = info.nonLocalizedLabel; + res.icon = info.icon; + return res; + } + + // Keys are String (activity class name), values are Activity. + private final ArrayMap mActivities = + new ArrayMap<>(); + private int mFlags; + } + + private static final class ProviderIntentResolver + extends IntentResolver { + @Override + public List queryIntent(Intent intent, String resolvedType, + boolean defaultOnly, int userId) { + mFlags = defaultOnly ? PackageManager.MATCH_DEFAULT_ONLY : 0; + return super.queryIntent(intent, resolvedType, defaultOnly, userId); + } + + List queryIntent(Intent intent, String resolvedType, int flags, + int userId) { + mFlags = flags; + return super.queryIntent(intent, resolvedType, + (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0, + userId); + } + + List queryIntentForPackage(Intent intent, String resolvedType, + int flags, List packageProviders, int userId) { + if (packageProviders == null) { + return null; + } + mFlags = flags; + final boolean defaultOnly = (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0; + final int providersSize = packageProviders.size(); + ArrayList listCut = new ArrayList<>(providersSize); + + ArrayList intentFilters; + for (int i = 0; i < providersSize; ++i) { + intentFilters = packageProviders.get(i).intents; + if (intentFilters != null && intentFilters.size() > 0) { + BPackage.ProviderIntentInfo[] array = + new BPackage.ProviderIntentInfo[intentFilters.size()]; + intentFilters.toArray(array); + listCut.add(array); + } + } + return super.queryIntentFromList(intent, resolvedType, defaultOnly, listCut, userId); + } + + void addProvider(BPackage.Provider p) { + mProviders.put(p.getComponentName(), p); + final int intentsSize = p.intents.size(); + int j; + for (j = 0; j < intentsSize; j++) { + BPackage.ProviderIntentInfo intent = p.intents.get(j); + addFilter(intent); + } + } + + void removeProvider(BPackage.Provider p) { + mProviders.remove(p.getComponentName()); + final int intentsSize = p.intents.size(); + int j; + for (j = 0; j < intentsSize; j++) { + BPackage.ProviderIntentInfo intent = p.intents.get(j); + removeFilter(intent); + } + } + + @Override + protected boolean allowFilterResult( + BPackage.ProviderIntentInfo filter, List dest) { + ProviderInfo filterPi = filter.provider.info; + for (int i = dest.size() - 1; i >= 0; i--) { + ProviderInfo destPi = dest.get(i).providerInfo; + if (destPi.name.equals(filterPi.name) + && destPi.packageName.equals(filterPi.packageName)) { + return false; + } + } + return true; + } + + @Override + protected BPackage.ProviderIntentInfo[] newArray(int size) { + return new BPackage.ProviderIntentInfo[size]; + } + + @Override + protected boolean isPackageForFilter(String packageName, + BPackage.ProviderIntentInfo info) { + return packageName.equals(info.provider.owner.packageName); + } + + @Override + protected ResolveInfo newResult(BPackage.ProviderIntentInfo filter, int match, int userId) { + final BPackage.ProviderIntentInfo info = filter; + final BPackage.Provider provider = info.provider; + BPackageSettings ps = (BPackageSettings) provider.owner.mExtras; + if (ps == null) { + return null; + } + + ProviderInfo pi = PackageManagerCompat.generateProviderInfo(provider, mFlags, ps.readUserState(userId), userId); + final ResolveInfo res = new ResolveInfo(); + res.providerInfo = pi; + if ((mFlags & PackageManager.GET_RESOLVED_FILTER) != 0) { + res.filter = filter.intentFilter; + } + res.priority = info.intentFilter.getPriority(); + res.preferredOrder = provider.owner.mPreferredOrder; + res.match = match; + res.isDefault = info.hasDefault; + res.labelRes = info.labelRes; + res.nonLocalizedLabel = info.nonLocalizedLabel; + res.icon = info.icon; + return res; + } + + private final ArrayMap mProviders = new ArrayMap<>(); + private int mFlags; + } +} diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/core/system/pm/FastImmutableArraySet.java b/Bcore/src/main/java/top/niunaijun/blackbox/core/system/pm/FastImmutableArraySet.java new file mode 100644 index 00000000..1744f126 --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/core/system/pm/FastImmutableArraySet.java @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package top.niunaijun.blackbox.core.system.pm; + +import java.util.AbstractSet; +import java.util.Iterator; + +/** + * A fast immutable set wrapper for an array that is optimized for non-concurrent iteration. + * The same iterator instance is reused each time to avoid creating lots of garbage. + * Iterating over an array in this fashion is 2.5x faster than iterating over a {@link HashSet} + * so it is worth copying the contents of the set to an array when iterating over it + * hundreds of times. + * @hide + */ +public final class FastImmutableArraySet extends AbstractSet { + FastIterator mIterator; + T[] mContents; + + public FastImmutableArraySet(T[] contents) { + mContents = contents; + } + + @Override + public Iterator iterator() { + FastIterator it = mIterator; + if (it == null) { + it = new FastIterator(mContents); + mIterator = it; + } else { + it.mIndex = 0; + } + return it; + } + + @Override + public int size() { + return mContents.length; + } + + private static final class FastIterator implements Iterator { + private final T[] mContents; + int mIndex; + + public FastIterator(T[] contents) { + mContents = contents; + } + + @Override + public boolean hasNext() { + return mIndex != mContents.length; + } + + @Override + public T next() { + return mContents[mIndex++]; + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + } +} diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/core/system/pm/IntentResolver.java b/Bcore/src/main/java/top/niunaijun/blackbox/core/system/pm/IntentResolver.java new file mode 100644 index 00000000..c823eed7 --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/core/system/pm/IntentResolver.java @@ -0,0 +1,741 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package top.niunaijun.blackbox.core.system.pm; + +import android.content.Intent; +import android.content.IntentFilter; +import android.net.Uri; +import android.util.ArrayMap; +import android.util.Log; +import android.util.LogPrinter; +import android.util.MutableInt; +import android.util.PrintWriterPrinter; +import android.util.Printer; + +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +import top.niunaijun.blackbox.utils.Slog; + +/** + * {@hide} + */ +public abstract class IntentResolver { + final private static String TAG = "IntentResolver"; + final private static boolean DEBUG = false; + final private static boolean localLOGV = DEBUG || false; + final private static boolean localVerificationLOGV = DEBUG || false; + + public void addFilter(F f) { + if (localLOGV) { + Slog.v(TAG, "Adding filter: " + f); + f.intentFilter.dump(new LogPrinter(Log.VERBOSE, TAG), " "); + Slog.v(TAG, " Building Lookup Maps:"); + } + + mFilters.add(f); + int numS = register_intent_filter(f, f.intentFilter.schemesIterator(), + mSchemeToFilter, " Scheme: "); + int numT = register_mime_types(f, " Type: "); + if (numS == 0 && numT == 0) { + register_intent_filter(f, f.intentFilter.actionsIterator(), + mActionToFilter, " Action: "); + } + if (numT != 0) { + register_intent_filter(f, f.intentFilter.actionsIterator(), + mTypedActionToFilter, " TypedAction: "); + } + } + + public static boolean filterEquals(IntentFilter f1, IntentFilter f2) { + int s1 = f1.countActions(); + int s2 = f2.countActions(); + if (s1 != s2) { + return false; + } + for (int i=0; i collectFilters(F[] array, IntentFilter matching) { + ArrayList res = null; + if (array != null) { + for (int i=0; i(); + } + res.add(cur); + } + } + } + return res; + } + + public ArrayList findFilters(IntentFilter matching) { + if (matching.countDataSchemes() == 1) { + // Fast case. + return collectFilters(mSchemeToFilter.get(matching.getDataScheme(0)), matching); + } else if (matching.countDataTypes() != 0 && matching.countActions() == 1) { + // Another fast case. + return collectFilters(mTypedActionToFilter.get(matching.getAction(0)), matching); + } else if (matching.countDataTypes() == 0 && matching.countDataSchemes() == 0 + && matching.countActions() == 1) { + // Last fast case. + return collectFilters(mActionToFilter.get(matching.getAction(0)), matching); + } else { + ArrayList res = null; + for (F cur : mFilters) { + if (filterEquals(cur.intentFilter, matching)) { + if (res == null) { + res = new ArrayList<>(); + } + res.add(cur); + } + } + return res; + } + } + + public void removeFilter(F f) { + removeFilterInternal(f); + mFilters.remove(f); + } + + void removeFilterInternal(F f) { + if (localLOGV) { + Slog.v(TAG, "Removing filter: " + f); + f.intentFilter.dump(new LogPrinter(Log.VERBOSE, TAG), " "); + Slog.v(TAG, " Cleaning Lookup Maps:"); + } + + int numS = unregister_intent_filter(f, f.intentFilter.schemesIterator(), + mSchemeToFilter, " Scheme: "); + int numT = unregister_mime_types(f, " Type: "); + if (numS == 0 && numT == 0) { + unregister_intent_filter(f, f.intentFilter.actionsIterator(), + mActionToFilter, " Action: "); + } + if (numT != 0) { + unregister_intent_filter(f, f.intentFilter.actionsIterator(), + mTypedActionToFilter, " TypedAction: "); + } + } + + boolean dumpMap(PrintWriter out, String titlePrefix, String title, + String prefix, ArrayMap map, String packageName, + boolean printFilter, boolean collapseDuplicates) { + final String eprefix = prefix + " "; + final String fprefix = prefix + " "; + final ArrayMap found = new ArrayMap<>(); + boolean printedSomething = false; + Printer printer = null; + for (int mapi=0; mapi { + private final Iterator mI; + private F mCur; + + IteratorWrapper(Iterator it) { + mI = it; + } + + public boolean hasNext() { + return mI.hasNext(); + } + + public F next() { + return (mCur = mI.next()); + } + + public void remove() { + if (mCur != null) { + removeFilterInternal(mCur); + } + mI.remove(); + } + + } + + /** + * Returns an iterator allowing filters to be removed. + */ + public Iterator filterIterator() { + return new IteratorWrapper(mFilters.iterator()); + } + + /** + * Returns a read-only set of the filters. + */ + public Set filterSet() { + return Collections.unmodifiableSet(mFilters); + } + + public List queryIntentFromList(Intent intent, String resolvedType, boolean defaultOnly, + ArrayList listCut, int userId) { + ArrayList resultList = new ArrayList(); + + final boolean debug = localLOGV || + ((intent.getFlags() & Intent.FLAG_DEBUG_LOG_RESOLUTION) != 0); + + FastImmutableArraySet categories = getFastIntentCategories(intent); + final String scheme = intent.getScheme(); + int N = listCut.size(); + for (int i = 0; i < N; ++i) { + buildResolveList(intent, categories, debug, defaultOnly, resolvedType, scheme, + listCut.get(i), resultList, userId); + } + filterResults(resultList); +// sortResults(resultList); + return resultList; + } + + public List queryIntent(Intent intent, String resolvedType, boolean defaultOnly, + int userId) { + String scheme = intent.getScheme(); + + ArrayList finalList = new ArrayList(); + + final boolean debug = localLOGV || + ((intent.getFlags() & Intent.FLAG_DEBUG_LOG_RESOLUTION) != 0); + + if (debug) Slog.v( + TAG, "Resolving type=" + resolvedType + " scheme=" + scheme + + " defaultOnly=" + defaultOnly + " userId=" + userId + " of " + intent); + + F[] firstTypeCut = null; + F[] secondTypeCut = null; + F[] thirdTypeCut = null; + F[] schemeCut = null; + + // If the intent includes a MIME type, then we want to collect all of + // the filters that match that MIME type. + if (resolvedType != null) { + int slashpos = resolvedType.indexOf('/'); + if (slashpos > 0) { + final String baseType = resolvedType.substring(0, slashpos); + if (!baseType.equals("*")) { + if (resolvedType.length() != slashpos+2 + || resolvedType.charAt(slashpos+1) != '*') { + // Not a wild card, so we can just look for all filters that + // completely match or wildcards whose base type matches. + firstTypeCut = mTypeToFilter.get(resolvedType); + if (debug) Slog.v(TAG, "First type cut: " + Arrays.toString(firstTypeCut)); + secondTypeCut = mWildTypeToFilter.get(baseType); + if (debug) Slog.v(TAG, "Second type cut: " + + Arrays.toString(secondTypeCut)); + } else { + // We can match anything with our base type. + firstTypeCut = mBaseTypeToFilter.get(baseType); + if (debug) Slog.v(TAG, "First type cut: " + Arrays.toString(firstTypeCut)); + secondTypeCut = mWildTypeToFilter.get(baseType); + if (debug) Slog.v(TAG, "Second type cut: " + + Arrays.toString(secondTypeCut)); + } + // Any */* types always apply, but we only need to do this + // if the intent type was not already */*. + thirdTypeCut = mWildTypeToFilter.get("*"); + if (debug) Slog.v(TAG, "Third type cut: " + Arrays.toString(thirdTypeCut)); + } else if (intent.getAction() != null) { + // The intent specified any type ({@literal *}/*). This + // can be a whole heck of a lot of things, so as a first + // cut let's use the action instead. + firstTypeCut = mTypedActionToFilter.get(intent.getAction()); + if (debug) Slog.v(TAG, "Typed Action list: " + Arrays.toString(firstTypeCut)); + } + } + } + + // If the intent includes a data URI, then we want to collect all of + // the filters that match its scheme (we will further refine matches + // on the authority and path by directly matching each resulting filter). + if (scheme != null) { + schemeCut = mSchemeToFilter.get(scheme); + if (debug) Slog.v(TAG, "Scheme list: " + Arrays.toString(schemeCut)); + } + + // If the intent does not specify any data -- either a MIME type or + // a URI -- then we will only be looking for matches against empty + // data. + if (resolvedType == null && scheme == null && intent.getAction() != null) { + firstTypeCut = mActionToFilter.get(intent.getAction()); + if (debug) Slog.v(TAG, "Action list: " + Arrays.toString(firstTypeCut)); + } + + FastImmutableArraySet categories = getFastIntentCategories(intent); + if (firstTypeCut != null) { + buildResolveList(intent, categories, debug, defaultOnly, resolvedType, + scheme, firstTypeCut, finalList, userId); + } + if (secondTypeCut != null) { + buildResolveList(intent, categories, debug, defaultOnly, resolvedType, + scheme, secondTypeCut, finalList, userId); + } + if (thirdTypeCut != null) { + buildResolveList(intent, categories, debug, defaultOnly, resolvedType, + scheme, thirdTypeCut, finalList, userId); + } + if (schemeCut != null) { + buildResolveList(intent, categories, debug, defaultOnly, resolvedType, + scheme, schemeCut, finalList, userId); + } + filterResults(finalList); +// sortResults(finalList); + + if (debug) { + Slog.v(TAG, "Final result list:"); + for (int i=0; i dest) { + return true; + } + + /** + * Returns whether the object associated with the given filter is + * "stopped", that is whether it should not be included in the result + * if the intent requests to excluded stopped objects. + */ + protected boolean isFilterStopped(F filter, int userId) { + return false; + } + + /** + * Returns whether this filter is owned by this package. This must be + * implemented to provide correct filtering of Intents that have + * specified a package name they are to be delivered to. + */ + protected abstract boolean isPackageForFilter(String packageName, F filter); + + protected abstract F[] newArray(int size); + + @SuppressWarnings("unchecked") + protected R newResult(F filter, int match, int userId) { + return (R)filter; + } + + @SuppressWarnings("unchecked") + protected void sortResults(List results) { + Collections.sort(results, mResolvePrioritySorter); + } + + /** + * Apply filtering to the results. This happens before the results are sorted. + */ + protected void filterResults(List results) { + } + + protected void dumpFilter(PrintWriter out, String prefix, F filter) { + out.print(prefix); out.println(filter); + } + + protected Object filterToLabel(F filter) { + return "IntentFilter"; + } + + protected void dumpFilterLabel(PrintWriter out, String prefix, Object label, int count) { + out.print(prefix); out.print(label); out.print(": "); out.println(count); + } + + private final void addFilter(ArrayMap map, String name, F filter) { + F[] array = map.get(name); + if (array == null) { + array = newArray(2); + map.put(name, array); + array[0] = filter; + } else { + final int N = array.length; + int i = N; + while (i > 0 && array[i-1] == null) { + i--; + } + if (i < N) { + array[i] = filter; + } else { + F[] newa = newArray((N*3)/2); + System.arraycopy(array, 0, newa, 0, N); + newa[N] = filter; + map.put(name, newa); + } + } + } + + private final int register_mime_types(F filter, String prefix) { + final Iterator i = filter.intentFilter.typesIterator(); + if (i == null) { + return 0; + } + + int num = 0; + while (i.hasNext()) { + String name = i.next(); + num++; + if (localLOGV) Slog.v(TAG, prefix + name); + String baseName = name; + final int slashpos = name.indexOf('/'); + if (slashpos > 0) { + baseName = name.substring(0, slashpos).intern(); + } else { + name = name + "/*"; + } + + addFilter(mTypeToFilter, name, filter); + + if (slashpos > 0) { + addFilter(mBaseTypeToFilter, baseName, filter); + } else { + addFilter(mWildTypeToFilter, baseName, filter); + } + } + + return num; + } + + private final int unregister_mime_types(F filter, String prefix) { + final Iterator i = filter.intentFilter.typesIterator(); + if (i == null) { + return 0; + } + + int num = 0; + while (i.hasNext()) { + String name = i.next(); + num++; + if (localLOGV) Slog.v(TAG, prefix + name); + String baseName = name; + final int slashpos = name.indexOf('/'); + if (slashpos > 0) { + baseName = name.substring(0, slashpos).intern(); + } else { + name = name + "/*"; + } + + remove_all_objects(mTypeToFilter, name, filter); + + if (slashpos > 0) { + remove_all_objects(mBaseTypeToFilter, baseName, filter); + } else { + remove_all_objects(mWildTypeToFilter, baseName, filter); + } + } + return num; + } + + private final int register_intent_filter(F filter, Iterator i, + ArrayMap dest, String prefix) { + if (i == null) { + return 0; + } + + int num = 0; + while (i.hasNext()) { + String name = i.next(); + num++; + if (localLOGV) Slog.v(TAG, prefix + name); + addFilter(dest, name, filter); + } + return num; + } + + private final int unregister_intent_filter(F filter, Iterator i, + ArrayMap dest, String prefix) { + if (i == null) { + return 0; + } + + int num = 0; + while (i.hasNext()) { + String name = i.next(); + num++; + if (localLOGV) Slog.v(TAG, prefix + name); + remove_all_objects(dest, name, filter); + } + return num; + } + + private final void remove_all_objects(ArrayMap map, String name, + Object object) { + F[] array = map.get(name); + if (array != null) { + int LAST = array.length-1; + while (LAST >= 0 && array[LAST] == null) { + LAST--; + } + for (int idx=LAST; idx>=0; idx--) { + if (array[idx] == object) { + final int remain = LAST - idx; + if (remain > 0) { + System.arraycopy(array, idx+1, array, idx, remain); + } + array[LAST] = null; + LAST--; + } + } + if (LAST < 0) { + map.remove(name); + } else if (LAST < (array.length/2)) { + F[] newa = newArray(LAST+2); + System.arraycopy(array, 0, newa, 0, LAST+1); + map.put(name, newa); + } + } + } + + private static FastImmutableArraySet getFastIntentCategories(Intent intent) { + final Set categories = intent.getCategories(); + if (categories == null) { + return null; + } + return new FastImmutableArraySet(categories.toArray(new String[categories.size()])); + } + + private void buildResolveList(Intent intent, FastImmutableArraySet categories, + boolean debug, boolean defaultOnly, String resolvedType, String scheme, + F[] src, List dest, int userId) { + final String action = intent.getAction(); + final Uri data = intent.getData(); + final String packageName = intent.getPackage(); + +// final boolean excludingStopped = intent.isExcludingStopped(); + + final int N = src != null ? src.length : 0; + boolean hasNonDefaults = false; + int i; + F filter; + for (i=0; i= 0) { + if (debug) Slog.v(TAG, " Filter matched! match=0x" + + Integer.toHexString(match) + " hasDefault=" + + filter.intentFilter.hasCategory(Intent.CATEGORY_DEFAULT)); + if (!defaultOnly || filter.intentFilter.hasCategory(Intent.CATEGORY_DEFAULT)) { + final R oneResult = newResult(filter, match, userId); + if (debug) Slog.v(TAG, " Created result: " + oneResult); + if (oneResult != null) { + dest.add(oneResult); + } + } else { + hasNonDefaults = true; + } + } else { + if (debug) { + String reason; + switch (match) { + case IntentFilter.NO_MATCH_ACTION: reason = "action"; break; + case IntentFilter.NO_MATCH_CATEGORY: reason = "category"; break; + case IntentFilter.NO_MATCH_DATA: reason = "data"; break; + case IntentFilter.NO_MATCH_TYPE: reason = "type"; break; + default: reason = "unknown reason"; break; + } + Slog.v(TAG, " Filter did not match: " + reason); + } + } + } + + if (debug && hasNonDefaults) { + if (dest.size() == 0) { + Slog.v(TAG, "resolveIntent failed: found match, but none with CATEGORY_DEFAULT"); + } else if (dest.size() > 1) { + Slog.v(TAG, "resolveIntent: multiple matches, only some with CATEGORY_DEFAULT"); + } + } + } + + // Sorts a List of IntentFilter objects into descending priority order. + @SuppressWarnings("rawtypes") + private static final Comparator mResolvePrioritySorter = new Comparator() { + public int compare(Object o1, Object o2) { + final int q1 = ((IntentFilter) o1).getPriority(); + final int q2 = ((IntentFilter) o2).getPriority(); + return (q1 > q2) ? -1 : ((q1 < q2) ? 1 : 0); + } + }; + + /** + * All filters that have been registered. + */ + private final HashSet mFilters = new HashSet<>(); + + /** + * All of the MIME types that have been registered, such as "image/jpeg", + * "image/*", or "{@literal *}/*". + */ + private final ArrayMap mTypeToFilter = new ArrayMap(); + + /** + * The base names of all of all fully qualified MIME types that have been + * registered, such as "image" or "*". Wild card MIME types such as + * "image/*" will not be here. + */ + private final ArrayMap mBaseTypeToFilter = new ArrayMap(); + + /** + * The base names of all of the MIME types with a sub-type wildcard that + * have been registered. For example, a filter with "image/*" will be + * included here as "image" but one with "image/jpeg" will not be + * included here. This also includes the "*" for the "{@literal *}/*" + * MIME type. + */ + private final ArrayMap mWildTypeToFilter = new ArrayMap(); + + /** + * All of the URI schemes (such as http) that have been registered. + */ + private final ArrayMap mSchemeToFilter = new ArrayMap(); + + /** + * All of the actions that have been registered, but only those that did + * not specify data. + */ + private final ArrayMap mActionToFilter = new ArrayMap(); + + /** + * All of the actions that have been registered and specified a MIME type. + */ + private final ArrayMap mTypedActionToFilter = new ArrayMap(); +} diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/core/system/pm/PackageManagerCompat.java b/Bcore/src/main/java/top/niunaijun/blackbox/core/system/pm/PackageManagerCompat.java new file mode 100644 index 00000000..0f5d2aa0 --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/core/system/pm/PackageManagerCompat.java @@ -0,0 +1,355 @@ +package top.niunaijun.blackbox.core.system.pm; + +import android.annotation.SuppressLint; +import android.content.pm.ActivityInfo; +import android.content.pm.ApplicationInfo; +import android.content.pm.ConfigurationInfo; +import android.content.pm.FeatureInfo; +import android.content.pm.InstrumentationInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageParser; +import android.content.pm.PermissionInfo; +import android.content.pm.ProviderInfo; +import android.content.pm.ServiceInfo; +import android.os.Build; + +import java.util.HashSet; +import java.util.Set; + +import mirror.android.content.pm.ApplicationInfoL; +import mirror.android.content.pm.ApplicationInfoN; +import mirror.android.content.pm.SigningInfo; +import top.niunaijun.blackbox.core.env.BEnvironment; +import top.niunaijun.blackbox.BlackBoxCore; +import top.niunaijun.blackbox.core.env.AppSystemEnv; +import top.niunaijun.blackbox.utils.ArrayUtils; +import top.niunaijun.blackbox.utils.FileUtils; +import top.niunaijun.blackbox.utils.compat.BuildCompat; + +/** + * Created by Milk on 4/15/21. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + */ +@SuppressLint("SdCardPath") +public class PackageManagerCompat { + + public static PackageInfo generatePackageInfo(BPackageSettings ps, int flags, BPackageUserState state, int userId) { + if (ps == null) { + return null; + } + BPackage p = ps.pkg; + if (p != null) { + PackageInfo packageInfo = null; + try { + packageInfo = generatePackageInfo(p, flags, 0, 0, state, userId); + } catch (Throwable ignored) { + } + return packageInfo; + } + return null; + } + + public static PackageInfo generatePackageInfo(BPackage p, int flags, long firstInstallTime, long lastUpdateTime, BPackageUserState state, int userId) { + if (!checkUseInstalledOrHidden(flags, state, p.applicationInfo)) { + return null; + } + + PackageInfo pi = null; + try { + pi = BlackBoxCore.getPackageManager().getPackageInfo(BlackBoxCore.getHostPkg(), flags); + } catch (PackageManager.NameNotFoundException e) { + e.printStackTrace(); + } + if (pi == null) { + return null; + } + pi.packageName = p.packageName; + pi.versionCode = p.mVersionCode; + pi.versionName = p.mVersionName; + pi.sharedUserId = p.mSharedUserId; + pi.sharedUserLabel = p.mSharedUserLabel; + pi.applicationInfo = generateApplicationInfo(p, flags, state, userId); + + pi.firstInstallTime = firstInstallTime; + pi.lastUpdateTime = lastUpdateTime; + if (!p.requestedPermissions.isEmpty()) { + String[] requestedPermissions = new String[p.requestedPermissions.size()]; + p.requestedPermissions.toArray(requestedPermissions); + pi.requestedPermissions = requestedPermissions; + } + + if ((flags & PackageManager.GET_GIDS) != 0) { + pi.gids = new int[]{}; + } + if ((flags & PackageManager.GET_CONFIGURATIONS) != 0) { + int N = p.configPreferences != null ? p.configPreferences.size() : 0; + if (N > 0) { + pi.configPreferences = new ConfigurationInfo[N]; + p.configPreferences.toArray(pi.configPreferences); + } + N = p.reqFeatures != null ? p.reqFeatures.size() : 0; + if (N > 0) { + pi.reqFeatures = new FeatureInfo[N]; + p.reqFeatures.toArray(pi.reqFeatures); + } + } + if ((flags & PackageManager.GET_ACTIVITIES) != 0) { + pi.activities = null; + final int N = p.activities.size(); + if (N > 0) { + int num = 0; + final ActivityInfo[] res = new ActivityInfo[N]; + for (int i = 0; i < N; i++) { + final BPackage.Activity a = p.activities.get(i); + res[num++] = generateActivityInfo(a, flags, state, userId); + } + pi.activities = ArrayUtils.trimToSize(res, num); + } + } + if ((flags & PackageManager.GET_RECEIVERS) != 0) { + pi.receivers = null; + final int N = p.receivers.size(); + if (N > 0) { + int num = 0; + final ActivityInfo[] res = new ActivityInfo[N]; + for (int i = 0; i < N; i++) { + final BPackage.Activity a = p.receivers.get(i); + res[num++] = generateActivityInfo(a, flags, state, userId); + } + pi.receivers = ArrayUtils.trimToSize(res, num); + } + } + if ((flags & PackageManager.GET_SERVICES) != 0) { + pi.services = null; + final int N = p.services.size(); + if (N > 0) { + int num = 0; + final ServiceInfo[] res = new ServiceInfo[N]; + for (int i = 0; i < N; i++) { + final BPackage.Service s = p.services.get(i); + res[num++] = generateServiceInfo(s, flags, state, userId); + } + pi.services = ArrayUtils.trimToSize(res, num); + } + } + if ((flags & PackageManager.GET_PROVIDERS) != 0) { + pi.providers = null; + final int N = p.providers.size(); + if (N > 0) { + int num = 0; + final ProviderInfo[] res = new ProviderInfo[N]; + for (int i = 0; i < N; i++) { + final BPackage.Provider pr = p.providers.get(i); + ProviderInfo providerInfo = generateProviderInfo(pr, flags, state, userId); + if (providerInfo != null) { + res[num++] = providerInfo; + } + } + pi.providers = ArrayUtils.trimToSize(res, num); + } + } + if ((flags & PackageManager.GET_INSTRUMENTATION) != 0) { + pi.instrumentation = null; + int N = p.instrumentation.size(); + if (N > 0) { + pi.instrumentation = new InstrumentationInfo[N]; + for (int i = 0; i < N; i++) { + pi.instrumentation[i] = generateInstrumentationInfo( + p.instrumentation.get(i), flags); + } + } + } + if ((flags & PackageManager.GET_PERMISSIONS) != 0) { + pi.permissions = null; + int N = p.permissions.size(); + if (N > 0) { + pi.permissions = new PermissionInfo[N]; + for (int i = 0; i < N; i++) { + pi.permissions[i] = generatePermissionInfo(p.permissions.get(i), flags); + } + } + pi.requestedPermissions = null; + N = p.requestedPermissions.size(); + if (N > 0) { + pi.requestedPermissions = new String[N]; + pi.requestedPermissionsFlags = new int[N]; + for (int i = 0; i < N; i++) { + final String perm = p.requestedPermissions.get(i); + pi.requestedPermissions[i] = perm; + // The notion of required permissions is deprecated but for compatibility. +// pi.requestedPermissionsFlags[i] |= PackageInfo.REQUESTED_PERMISSION_REQUIRED; +// if (grantedPermissions != null && grantedPermissions.contains(perm)) { +// pi.requestedPermissionsFlags[i] |= PackageInfo.REQUESTED_PERMISSION_GRANTED; +// } + } + } + } + if ((flags & PackageManager.GET_SIGNATURES) != 0) { + if (BuildCompat.isPie()) { + pi.signatures = p.mSigningDetails.signatures; + } else { + pi.signatures = p.mSignatures; + } + } + if (BuildCompat.isPie()) { + if ((flags & PackageManager.GET_SIGNING_CERTIFICATES) != 0) { + PackageParser.SigningDetails signingDetails = PackageParser.SigningDetails.UNKNOWN; + signingDetails.signatures = p.mSigningDetails.signatures; + SigningInfo.mSigningDetails.set(pi.signingInfo, signingDetails); + } + } + return pi; + } + + public static ActivityInfo generateActivityInfo(BPackage.Activity a, int flags, BPackageUserState state, int userId) { + if (!checkUseInstalledOrHidden(flags, state, a.info.applicationInfo)) { + return null; + } + // Make shallow copies so we can store the metadata safely + ActivityInfo ai = new ActivityInfo(a.info); + ai.metaData = a.metaData; + ai.processName = BPackageManagerService.fixProcessName(ai.packageName, ai.processName); + ai.applicationInfo = generateApplicationInfo(a.owner, flags, state, userId); + return ai; + } + + public static ServiceInfo generateServiceInfo(BPackage.Service s, int flags, BPackageUserState state, int userId) { + if (!checkUseInstalledOrHidden(flags, state, s.info.applicationInfo)) { + return null; + } + // Make shallow copies so we can store the metadata safely + ServiceInfo si = new ServiceInfo(s.info); + si.metaData = s.metaData; + si.processName = BPackageManagerService.fixProcessName(si.packageName, si.processName); + si.applicationInfo = generateApplicationInfo(s.owner, flags, state, userId); + return si; + } + + public static ProviderInfo generateProviderInfo(BPackage.Provider p, int flags, BPackageUserState state, int userId) { + if (!checkUseInstalledOrHidden(flags, state, p.info.applicationInfo)) { + return null; + } + // Make shallow copies so we can store the metadata safely + ProviderInfo pi = new ProviderInfo(p.info); + if (pi.authority == null) + return null; + pi.metaData = p.metaData; + pi.processName = BPackageManagerService.fixProcessName(pi.packageName, pi.processName); + if ((flags & PackageManager.GET_URI_PERMISSION_PATTERNS) == 0) { + pi.uriPermissionPatterns = null; + } + pi.applicationInfo = generateApplicationInfo(p.owner, flags, state, userId); + return pi; + } + + public static PermissionInfo generatePermissionInfo( + BPackage.Permission p, int flags) { + if (p == null) return null; + if ((flags & PackageManager.GET_META_DATA) == 0) { + return p.info; + } + PermissionInfo pi = new PermissionInfo(p.info); + pi.metaData = p.metaData; + return pi; + } + + public static InstrumentationInfo generateInstrumentationInfo( + BPackage.Instrumentation i, int flags) { + if (i == null) return null; + if ((flags & PackageManager.GET_META_DATA) == 0) { + return i.info; + } + InstrumentationInfo ii = new InstrumentationInfo(i.info); + ii.metaData = i.metaData; + return ii; + } + + public static ApplicationInfo generateApplicationInfo(BPackage p, int flags, BPackageUserState state, int userId) { + if (!checkUseInstalledOrHidden(flags, state, p.applicationInfo)) { + return null; + } + ApplicationInfo baseApplication; + try { + baseApplication = BlackBoxCore.getPackageManager().getApplicationInfo(BlackBoxCore.getHostPkg(), flags); + } catch (Exception e) { + return null; + } + String sourceDir = p.baseCodePath; + if (p.applicationInfo == null) { + p.applicationInfo = BlackBoxCore.getPackageManager() + .getPackageArchiveInfo(sourceDir, 0).applicationInfo; + } + ApplicationInfo ai = new ApplicationInfo(p.applicationInfo); + if ((flags & PackageManager.GET_META_DATA) != 0) { + ai.metaData = p.mAppMetaData; + } + ai.dataDir = BEnvironment.getDataDir(ai.packageName, userId).getAbsolutePath(); + ai.nativeLibraryDir = BEnvironment.getAppLibDir(ai.packageName).getAbsolutePath(); + ai.processName = BPackageManagerService.fixProcessName(p.packageName, ai.packageName); + ai.publicSourceDir = sourceDir; + ai.sourceDir = sourceDir; +// ai.uid = p.mExtras.appId; + ai.uid = baseApplication.uid; + + if (BuildCompat.isL()) { + ApplicationInfoL.primaryCpuAbi.set(ai, Build.CPU_ABI); + ApplicationInfoL.scanPublicSourceDir.set(ai, ApplicationInfoL.scanPublicSourceDir.get(baseApplication)); + ApplicationInfoL.scanSourceDir.set(ai, ApplicationInfoL.scanSourceDir.get(baseApplication)); + } + if (BuildCompat.isN()) { + ai.deviceProtectedDataDir = BEnvironment.getDeDataDir(p.packageName, userId).getAbsolutePath(); + + if (ApplicationInfoN.deviceEncryptedDataDir != null) { + ApplicationInfoN.deviceEncryptedDataDir.set(ai, ai.deviceProtectedDataDir); + } + if (ApplicationInfoN.credentialEncryptedDataDir != null) { + ApplicationInfoN.credentialEncryptedDataDir.set(ai, ai.dataDir); + } + if (ApplicationInfoN.deviceProtectedDataDir != null) { + ApplicationInfoN.deviceProtectedDataDir.set(ai, ai.deviceProtectedDataDir); + } + if (ApplicationInfoN.credentialProtectedDataDir != null) { + ApplicationInfoN.credentialProtectedDataDir.set(ai, ai.dataDir); + } + } + fixJar(ai); + return ai; + } + + private static boolean checkUseInstalledOrHidden(int flags, BPackageUserState state, + ApplicationInfo appInfo) { + if (AppSystemEnv.isBlackPackage(appInfo.packageName)) + return false; + // Returns false if the package is hidden system app until installed. + if (!state.installed || state.hidden) { + return false; + } + return true; + } + + private static void fixJar(ApplicationInfo info) { + String APACHE_LEGACY_JAR = "/system/framework/org.apache.http.legacy.boot.jar"; + String APACHE_LEGACY_JAR_Q = "/system/framework/org.apache.http.legacy.jar"; + Set sharedLibraryFileList = new HashSet<>(); + if (BuildCompat.isQ()) { + if (!FileUtils.isExist(APACHE_LEGACY_JAR_Q)) { + sharedLibraryFileList.add(APACHE_LEGACY_JAR); + } else { + sharedLibraryFileList.add(APACHE_LEGACY_JAR_Q); + } + } else { + sharedLibraryFileList.add(APACHE_LEGACY_JAR); + } +// if (BXposedManagerService.get().isXPEnable()) { +// ApplicationInfo base = BlackBoxCore.getContext().getApplicationInfo(); +// sharedLibraryFileList.add(base.sourceDir); +// } + sharedLibraryFileList.add(BEnvironment.JUNIT_JAR.getAbsolutePath()); + info.sharedLibraryFiles = sharedLibraryFileList.toArray(new String[]{}); + } +} diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/core/system/pm/PackageMonitor.java b/Bcore/src/main/java/top/niunaijun/blackbox/core/system/pm/PackageMonitor.java new file mode 100644 index 00000000..b71eff70 --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/core/system/pm/PackageMonitor.java @@ -0,0 +1,15 @@ +package top.niunaijun.blackbox.core.system.pm; + +/** + * Created by Milk on 5/2/21. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + */ +public interface PackageMonitor { + void onPackageUninstalled(String packageName, int userId); + + void onPackageInstalled(String packageName, int userId); +} diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/core/system/pm/Settings.java b/Bcore/src/main/java/top/niunaijun/blackbox/core/system/pm/Settings.java new file mode 100644 index 00000000..a7264687 --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/core/system/pm/Settings.java @@ -0,0 +1,197 @@ +package top.niunaijun.blackbox.core.system.pm; + +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageParser; +import android.os.Parcel; +import android.os.Process; +import android.util.ArrayMap; +import android.util.AtomicFile; + +import java.io.File; +import java.io.FileOutputStream; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import top.niunaijun.blackbox.core.env.BEnvironment; +import top.niunaijun.blackbox.BlackBoxCore; +import top.niunaijun.blackbox.entity.pm.InstallOption; +import top.niunaijun.blackbox.core.system.BProcessManager; +import top.niunaijun.blackbox.core.system.user.BUserHandle; +import top.niunaijun.blackbox.utils.FileUtils; +import top.niunaijun.blackbox.utils.Slog; + +/** + * Created by Milk on 4/13/21. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + */ +/*public*/ class Settings { + public static final String TAG = "Settings"; + + final ArrayMap mPackages = new ArrayMap<>(); + private final Map mAppIds = new HashMap<>(); + private int mCurrUid = 0; + + public Settings() { + synchronized (mPackages) { + loadUidLP(); + } + } + + BPackageSettings getPackageLPw(String name, PackageParser.Package aPackage) { + BPackageSettings pkgSettings; + BPackageSettings origSettings = new BPackageSettings(); + origSettings.pkg = new BPackage(aPackage); + origSettings.pkg.mExtras = origSettings; + origSettings.pkg.applicationInfo = PackageManagerCompat.generateApplicationInfo(origSettings.pkg, 0, BPackageUserState.create(), 0); + synchronized (mPackages) { + pkgSettings = mPackages.get(name); + if (pkgSettings != null) { + origSettings.appId = pkgSettings.appId; + origSettings.userState = pkgSettings.userState; + } else { + boolean b = registerAppIdLPw(origSettings); + if (!b) { + throw new RuntimeException("registerAppIdLPw err."); + } + } + } + return origSettings; + } + + boolean registerAppIdLPw(BPackageSettings p) { + boolean createdNew = false; + if (p.appId == 0) { + // Assign new user ID + p.appId = acquireAndRegisterNewAppIdLPw(p); + createdNew = true; + } + if (p.appId < 0) { + createdNew = false; +// PackageManagerService.reportSettingsProblem(Log.WARN, +// "Package " + p.name + " could not be assigned a valid UID"); +// throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE, +// "Package " + p.name + " could not be assigned a valid UID"); + } + saveUidLP(); + return createdNew; + } + + private int acquireAndRegisterNewAppIdLPw(BPackageSettings obj) { + // Let's be stupidly inefficient for now... + Integer integer = mAppIds.get(obj.pkg.packageName); + if (integer != null) + return integer; + + if (mCurrUid >= Process.LAST_APPLICATION_UID) { + return -1; + } + mCurrUid++; + mAppIds.put(obj.pkg.packageName, mCurrUid); + return Process.FIRST_APPLICATION_UID + mCurrUid; + } + + private void saveUidLP() { + Parcel parcel = Parcel.obtain(); + FileOutputStream fileOutputStream = null; + AtomicFile atomicFile = new AtomicFile(BEnvironment.getUidConf()); + try { + Set pkgName = mPackages.keySet(); + for (String s : new HashSet<>(mAppIds.keySet())) { + if (!pkgName.contains(s)) { + mAppIds.remove(s); + } + } + parcel.writeInt(mCurrUid); + parcel.writeMap(mAppIds); + + fileOutputStream = atomicFile.startWrite(); + FileUtils.writeParcelToOutput(parcel, fileOutputStream); + atomicFile.finishWrite(fileOutputStream); + } catch (Exception e) { + e.printStackTrace(); + atomicFile.failWrite(fileOutputStream); + } finally { + parcel.recycle(); + } + } + + private void loadUidLP() { + Parcel parcel = Parcel.obtain(); + try { + byte[] uidBytes = FileUtils.toByteArray(BEnvironment.getUidConf()); + parcel.unmarshall(uidBytes, 0, uidBytes.length); + parcel.setDataPosition(0); + + mCurrUid = parcel.readInt(); + HashMap hashMap = parcel.readHashMap(HashMap.class.getClassLoader()); + synchronized (mAppIds) { + mAppIds.clear(); + mAppIds.putAll(hashMap); + } + } catch (Exception e) { +// e.printStackTrace(); + } finally { + parcel.recycle(); + } + } + + public void scanPackage() { + synchronized (mPackages) { + File appRootDir = BEnvironment.getAppRootDir(); + FileUtils.mkdirs(appRootDir); + File[] apps = appRootDir.listFiles(); + for (File app : apps) { + if (!app.isDirectory()) { + continue; + } + updatePackageLP(app); + } + } + } + + private void updatePackageLP(File app) { + String packageName = app.getName(); + Parcel packageSettingsIn = Parcel.obtain(); + File packageConf = BEnvironment.getPackageConf(packageName); + try { + byte[] bPackageSettingsBytes = FileUtils.toByteArray(packageConf); + + packageSettingsIn.unmarshall(bPackageSettingsBytes, 0, bPackageSettingsBytes.length); + packageSettingsIn.setDataPosition(0); + + BPackageSettings bPackageSettings = new BPackageSettings(packageSettingsIn); + if (bPackageSettings.installOption.isFlag(InstallOption.FLAG_SYSTEM)) { + PackageInfo packageInfo = BlackBoxCore.getPackageManager().getPackageInfo(packageName, PackageManager.GET_META_DATA); + String currPackageSourcePath = packageInfo.applicationInfo.sourceDir; + if (!currPackageSourcePath.equals(bPackageSettings.pkg.baseCodePath)) { + // update baseCodePath And Re install + BProcessManager.get().killAllByPackageName(bPackageSettings.pkg.packageName); + bPackageSettings.pkg.baseCodePath = currPackageSourcePath; + BPackageInstallerService.get().updatePackage(bPackageSettings); + } + } + bPackageSettings.pkg.mExtras = bPackageSettings; + bPackageSettings.pkg.applicationInfo = PackageManagerCompat.generateApplicationInfo(bPackageSettings.pkg, 0, BPackageUserState.create(), 0); + bPackageSettings.save(); + mPackages.put(bPackageSettings.pkg.packageName, bPackageSettings); + Slog.d(TAG, "loaded Package: " + packageName); + } catch (Throwable e) { + e.printStackTrace(); + // bad package + FileUtils.deleteDir(app); + mPackages.remove(packageName); + BProcessManager.get().killAllByPackageName(packageName); + BPackageManagerService.get().onPackageUninstalled(packageName, BUserHandle.USER_ALL); + Slog.d(TAG, "bad Package: " + packageName); + } finally { + packageSettingsIn.recycle(); + } + } +} diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/core/system/pm/installer/CopyExecutor.java b/Bcore/src/main/java/top/niunaijun/blackbox/core/system/pm/installer/CopyExecutor.java new file mode 100644 index 00000000..a7dcfc2f --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/core/system/pm/installer/CopyExecutor.java @@ -0,0 +1,56 @@ +package top.niunaijun.blackbox.core.system.pm.installer; + + +import java.io.File; +import java.io.IOException; + +import top.niunaijun.blackbox.core.env.BEnvironment; +import top.niunaijun.blackbox.entity.pm.InstallOption; +import top.niunaijun.blackbox.core.system.pm.BPackageSettings; +import top.niunaijun.blackbox.utils.FileUtils; +import top.niunaijun.blackbox.utils.NativeUtils; + +/** + * Created by Milk on 4/24/21. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + * 拷贝文件相关 + */ +public class CopyExecutor implements Executor { + + @Override + public int exec(BPackageSettings ps, InstallOption option, int userId) { + try { + NativeUtils.copyNativeLib(new File(ps.pkg.baseCodePath), BEnvironment.getAppLibDir(ps.pkg.packageName)); + } catch (Exception e) { + e.printStackTrace(); + return -1; + } + if (option.isFlag(InstallOption.FLAG_STORAGE)) { + // 外部安装 + File origFile = new File(ps.pkg.baseCodePath); + File newFile = BEnvironment.getBaseApkDir(ps.pkg.packageName); + try { + if (option.isFlag(InstallOption.FLAG_URI_FILE)) { + boolean b = FileUtils.renameTo(origFile, newFile); + if (!b) { + FileUtils.copyFile(origFile, newFile); + } + } else { + FileUtils.copyFile(origFile, newFile); + } + // update baseCodePath + ps.pkg.baseCodePath = newFile.getAbsolutePath(); + } catch (IOException e) { + e.printStackTrace(); + return -1; + } + } else if (option.isFlag(InstallOption.FLAG_SYSTEM)) { + // 系统安装 + } + return 0; + } +} diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/core/system/pm/installer/CreatePackageExecutor.java b/Bcore/src/main/java/top/niunaijun/blackbox/core/system/pm/installer/CreatePackageExecutor.java new file mode 100644 index 00000000..5eec9940 --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/core/system/pm/installer/CreatePackageExecutor.java @@ -0,0 +1,28 @@ +package top.niunaijun.blackbox.core.system.pm.installer; + +import top.niunaijun.blackbox.core.env.BEnvironment; +import top.niunaijun.blackbox.entity.pm.InstallOption; +import top.niunaijun.blackbox.core.system.pm.BPackageSettings; +import top.niunaijun.blackbox.utils.FileUtils; + +/** + * Created by Milk on 4/24/21. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + * 创建包相关的信息 + */ +public class CreatePackageExecutor implements Executor { + + @Override + public int exec(BPackageSettings ps, InstallOption option, int userId) { + FileUtils.deleteDir(BEnvironment.getAppDir(ps.pkg.packageName)); + + // create app dir + FileUtils.mkdirs(BEnvironment.getAppDir(ps.pkg.packageName)); + FileUtils.mkdirs(BEnvironment.getAppLibDir(ps.pkg.packageName)); + return 0; + } +} diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/core/system/pm/installer/CreateUserExecutor.java b/Bcore/src/main/java/top/niunaijun/blackbox/core/system/pm/installer/CreateUserExecutor.java new file mode 100644 index 00000000..4ca23b0c --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/core/system/pm/installer/CreateUserExecutor.java @@ -0,0 +1,40 @@ +package top.niunaijun.blackbox.core.system.pm.installer; + +import top.niunaijun.blackbox.core.env.BEnvironment; +import top.niunaijun.blackbox.entity.pm.InstallOption; +import top.niunaijun.blackbox.core.system.pm.BPackageSettings; +import top.niunaijun.blackbox.utils.FileUtils; + +/** + * Created by Milk on 4/24/21. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + * 创建用户相关 + */ +public class CreateUserExecutor implements Executor { + + @Override + public int exec(BPackageSettings ps, InstallOption option, int userId) { + String packageName = ps.pkg.packageName; + FileUtils.deleteDir(BEnvironment.getDataLibDir(packageName, userId)); + + // create user dir + FileUtils.mkdirs(BEnvironment.getDataDir(packageName, userId)); + FileUtils.mkdirs(BEnvironment.getDataCacheDir(packageName, userId)); + FileUtils.mkdirs(BEnvironment.getDataFilesDir(packageName, userId)); + FileUtils.mkdirs(BEnvironment.getDataDatabasesDir(packageName, userId)); + FileUtils.mkdirs(BEnvironment.getDeDataDir(packageName, userId)); + +// try { +// // /data/data/xx/lib -> /data/app/xx/lib +// FileUtils.createSymlink(BEnvironment.getAppLibDir(ps.pkg.packageName).getAbsolutePath(), BEnvironment.getDataLibDir(packageName, userId).getAbsolutePath()); +// } catch (Exception e) { +// e.printStackTrace(); +// return -1; +// } + return 0; + } +} diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/core/system/pm/installer/Executor.java b/Bcore/src/main/java/top/niunaijun/blackbox/core/system/pm/installer/Executor.java new file mode 100644 index 00000000..a8278ddd --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/core/system/pm/installer/Executor.java @@ -0,0 +1,18 @@ +package top.niunaijun.blackbox.core.system.pm.installer; + +import top.niunaijun.blackbox.entity.pm.InstallOption; +import top.niunaijun.blackbox.core.system.pm.BPackageSettings; + +/** + * Created by Milk on 4/24/21. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + */ +public interface Executor { + public static final String TAG = "InstallExecutor"; + + int exec(BPackageSettings ps, InstallOption option, int userId); +} diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/core/system/pm/installer/RemoveAppExecutor.java b/Bcore/src/main/java/top/niunaijun/blackbox/core/system/pm/installer/RemoveAppExecutor.java new file mode 100644 index 00000000..296d1ace --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/core/system/pm/installer/RemoveAppExecutor.java @@ -0,0 +1,22 @@ +package top.niunaijun.blackbox.core.system.pm.installer; + +import top.niunaijun.blackbox.core.env.BEnvironment; +import top.niunaijun.blackbox.entity.pm.InstallOption; +import top.niunaijun.blackbox.core.system.pm.BPackageSettings; +import top.niunaijun.blackbox.utils.FileUtils; + +/** + * Created by Milk on 4/27/21. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + */ +public class RemoveAppExecutor implements Executor { + @Override + public int exec(BPackageSettings ps, InstallOption option, int userId) { + FileUtils.deleteDir(BEnvironment.getAppDir(ps.pkg.packageName)); + return 0; + } +} diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/core/system/pm/installer/RemoveUserExecutor.java b/Bcore/src/main/java/top/niunaijun/blackbox/core/system/pm/installer/RemoveUserExecutor.java new file mode 100644 index 00000000..0c24a7f3 --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/core/system/pm/installer/RemoveUserExecutor.java @@ -0,0 +1,29 @@ +package top.niunaijun.blackbox.core.system.pm.installer; + +import top.niunaijun.blackbox.core.env.BEnvironment; +import top.niunaijun.blackbox.entity.pm.InstallOption; +import top.niunaijun.blackbox.core.system.pm.BPackageSettings; +import top.niunaijun.blackbox.core.system.pm.BPackageUserState; +import top.niunaijun.blackbox.utils.FileUtils; + +/** + * Created by Milk on 4/24/21. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + * 创建用户相关 + */ +public class RemoveUserExecutor implements Executor { + + @Override + public int exec(BPackageSettings ps, InstallOption option, int userId) { + String packageName = ps.pkg.packageName; + // delete user dir + FileUtils.deleteDir(BEnvironment.getDataDir(packageName, userId)); + FileUtils.deleteDir(BEnvironment.getDeDataDir(packageName, userId)); + FileUtils.deleteDir(BEnvironment.getExternalDataDir(packageName, userId)); + return 0; + } +} diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/core/system/user/BUserHandle.java b/Bcore/src/main/java/top/niunaijun/blackbox/core/system/user/BUserHandle.java new file mode 100644 index 00000000..fe959b07 --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/core/system/user/BUserHandle.java @@ -0,0 +1,469 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package top.niunaijun.blackbox.core.system.user; + +import android.os.Binder; +import android.os.Parcel; +import android.os.Parcelable; +import android.os.Process; + +/** + * Representation of a user on the device. + */ +public final class BUserHandle implements Parcelable { + // NOTE: keep logic in sync with system/core/libcutils/multiuser.c + + /** + * @hide Range of uids allocated for a user. + */ + public static final int PER_USER_RANGE = 100000; + + /** + * @hide A user id to indicate all users on the device + */ + public static final int USER_ALL = -1; + + /** + * @hide A user handle to indicate all users on the device + */ + public static final BUserHandle ALL = new BUserHandle(USER_ALL); + + /** + * @hide A user id to indicate the currently active user + */ + public static final int USER_CURRENT = -2; + + /** + * @hide A user handle to indicate the current user of the device + */ + public static final BUserHandle CURRENT = new BUserHandle(USER_CURRENT); + + /** + * @hide A user id to indicate that we would like to send to the current + * user, but if this is calling from a user process then we will send it + * to the caller's user instead of failing with a security exception + */ + public static final int USER_CURRENT_OR_SELF = -3; + + + public static final int USER_XPOSED = -4; + + /** + * @hide A user handle to indicate that we would like to send to the current + * user, but if this is calling from a user process then we will send it + * to the caller's user instead of failing with a security exception + */ + public static final BUserHandle CURRENT_OR_SELF = new BUserHandle(USER_CURRENT_OR_SELF); + + /** + * @hide An undefined user id + */ + public static final int USER_NULL = -10000; + + /** + * @hide A user id constant to indicate the "owner" user of the device + * @deprecated Consider using either {@link BUserHandle#USER_SYSTEM} constant or + * check the target user's flag {@link }. + */ + @Deprecated + public static final int USER_OWNER = 0; + + /** + * @hide A user handle to indicate the primary/owner user of the device + * @deprecated Consider using either {@link BUserHandle#SYSTEM} constant or + * check the target user's flag {@link }. + */ + @Deprecated + public static final BUserHandle OWNER = new BUserHandle(USER_OWNER); + + /** + * @hide A user id constant to indicate the "system" user of the device + */ + public static final int USER_SYSTEM = 0; + + /** + * @hide A user serial constant to indicate the "system" user of the device + */ + public static final int USER_SERIAL_SYSTEM = 0; + + /** + * @hide A user handle to indicate the "system" user of the device + */ + public static final BUserHandle SYSTEM = new BUserHandle(USER_SYSTEM); + + /** + * @hide Enable multi-user related side effects. Set this to false if + * there are problems with single user use-cases. + */ + public static final boolean MU_ENABLED = true; + + /** + * @hide + */ + public static final int ERR_GID = -1; + /** + * @hide + */ + public static final int AID_ROOT = 0; + /** + * @hide + */ + public static final int AID_APP_START = android.os.Process.FIRST_APPLICATION_UID; + /** + * @hide + */ + public static final int AID_APP_END = android.os.Process.LAST_APPLICATION_UID; + /** + * @hide + */ + public static final int AID_SHARED_GID_START = 50000; + /** + * @hide + */ + public static final int AID_CACHE_GID_START = 20000; + + final int mHandle; + + /** + * Checks to see if the user id is the same for the two uids, i.e., they belong to the same + * user. + * + * @hide + */ + public static boolean isSameUser(int uid1, int uid2) { + return getUserId(uid1) == getUserId(uid2); + } + + /** + * Checks to see if both uids are referring to the same app id, ignoring the user id part of the + * uids. + * + * @param uid1 uid to compare + * @param uid2 other uid to compare + * @return whether the appId is the same for both uids + * @hide + */ + public static boolean isSameApp(int uid1, int uid2) { + return getAppId(uid1) == getAppId(uid2); + } + + /** + * Whether a UID belongs to a regular app. *Note* "Not a regular app" does not mean + * "it's system", because of isolated UIDs. Use {@link #isCore} for that. + * + * @hide + */ + public static boolean isApp(int uid) { + if (uid > 0) { + final int appId = getAppId(uid); + return appId >= Process.FIRST_APPLICATION_UID && appId <= Process.LAST_APPLICATION_UID; + } else { + return false; + } + } + + /** + * Whether a UID belongs to a system core component or not. + * + * @hide + */ + public static boolean isCore(int uid) { + if (uid >= 0) { + final int appId = getAppId(uid); + return appId < Process.FIRST_APPLICATION_UID; + } else { + return false; + } + } + + /** + * Returns the user for a given uid. + * + * @param uid A uid for an application running in a particular user. + * @return A {@link BUserHandle} for that user. + */ + public static BUserHandle getUserHandleForUid(int uid) { + return of(getUserId(uid)); + } + + /** + * Returns the user id for a given uid. + * + * @hide + */ + public static int getUserId(int uid) { + if (MU_ENABLED) { + return uid / PER_USER_RANGE; + } else { + return BUserHandle.USER_SYSTEM; + } + } + + /** + * @hide + */ + public static int getCallingUserId() { + return getUserId(Binder.getCallingUid()); + } + + /** + * @hide + */ + public static int getCallingAppId() { + return getAppId(Binder.getCallingUid()); + } + + /** + * @hide + */ + public static BUserHandle of(int userId) { + return userId == USER_SYSTEM ? SYSTEM : new BUserHandle(userId); + } + + /** + * Returns the uid that is composed from the userId and the appId. + * + * @hide + */ + public static int getUid(int userId, int appId) { + if (MU_ENABLED) { + return userId * PER_USER_RANGE + (appId % PER_USER_RANGE); + } else { + return appId; + } + } + + /** + * Returns the app id (or base uid) for a given uid, stripping out the user id from it. + * + * @hide + */ + public static int getAppId(int uid) { + return uid % PER_USER_RANGE; + } + + /** + * Returns the gid shared between all apps with this userId. + * + * @hide + */ + public static int getUserGid(int userId) { + return getUid(userId, 9997 /*Process.SHARED_USER_GID*/); + } + + /** + * @hide + */ + public static int getSharedAppGid(int uid) { + return getSharedAppGid(getUserId(uid), getAppId(uid)); + } + + /** + * @hide + */ + public static int getSharedAppGid(int userId, int appId) { + if (appId >= AID_APP_START && appId <= AID_APP_END) { + return (appId - AID_APP_START) + AID_SHARED_GID_START; + } else if (appId >= AID_ROOT && appId <= AID_APP_START) { + return appId; + } else { + return -1; + } + } + + /** + * Returns the app id for a given shared app gid. Returns -1 if the ID is invalid. + * @hide + */ +// public static int getAppIdFromSharedAppGid(int gid) { +// final int appId = getAppId(gid) + Process.FIRST_APPLICATION_UID +// - Process.FIRST_SHARED_APPLICATION_GID; +// if (appId < 0 || appId >= Process.FIRST_SHARED_APPLICATION_GID) { +// return -1; +// } +// return appId; +// } + + /** + * @hide + */ + public static int getCacheAppGid(int uid) { + return getCacheAppGid(getUserId(uid), getAppId(uid)); + } + + /** + * @hide + */ + public static int getCacheAppGid(int userId, int appId) { + if (appId >= AID_APP_START && appId <= AID_APP_END) { + return getUid(userId, (appId - AID_APP_START) + AID_CACHE_GID_START); + } else { + return -1; + } + } + + /** + * @hide + */ + public static int parseUserArg(String arg) { + int userId; + if ("all".equals(arg)) { + userId = BUserHandle.USER_ALL; + } else if ("current".equals(arg) || "cur".equals(arg)) { + userId = BUserHandle.USER_CURRENT; + } else { + try { + userId = Integer.parseInt(arg); + } catch (NumberFormatException e) { + throw new IllegalArgumentException("Bad user number: " + arg); + } + } + return userId; + } + + /** + * Returns the user id of the current process + * + * @return user id of the current process + * @hide + */ + public static int myUserId() { + return getUserId(Process.myUid()); + } + + /** + * Returns true if this UserHandle refers to the owner user; false otherwise. + * + * @return true if this UserHandle refers to the owner user; false otherwise. + * @hide + * @deprecated please use {@link #isSystem()} or check for + */ + @Deprecated + public boolean isOwner() { + return this.equals(OWNER); + } + + /** + * @return true if this UserHandle refers to the system user; false otherwise. + * @hide + */ + public boolean isSystem() { + return this.equals(SYSTEM); + } + + /** + * @hide + */ + public BUserHandle(int h) { + mHandle = h; + } + + /** + * Returns the userId stored in this UserHandle. + * + * @hide + */ + public int getIdentifier() { + return mHandle; + } + + @Override + public String toString() { + return "UserHandle{" + mHandle + "}"; + } + + @Override + public boolean equals(Object obj) { + try { + if (obj != null) { + BUserHandle other = (BUserHandle) obj; + return mHandle == other.mHandle; + } + } catch (ClassCastException e) { + } + return false; + } + + @Override + public int hashCode() { + return mHandle; + } + + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel out, int flags) { + out.writeInt(mHandle); + } + + /** + * Write a UserHandle to a Parcel, handling null pointers. Must be + * read with {@link #readFromParcel(Parcel)}. + * + * @param h The UserHandle to be written. + * @param out The Parcel in which the UserHandle will be placed. + * @see #readFromParcel(Parcel) + */ + public static void writeToParcel(BUserHandle h, Parcel out) { + if (h != null) { + h.writeToParcel(out, 0); + } else { + out.writeInt(USER_NULL); + } + } + + /** + * Read a UserHandle from a Parcel that was previously written + * with {@link #writeToParcel(BUserHandle, Parcel)}, returning either + * a null or new object as appropriate. + * + * @param in The Parcel from which to read the UserHandle + * @return Returns a new UserHandle matching the previously written + * object, or null if a null had been written. + * @see #writeToParcel(BUserHandle, Parcel) + */ + public static BUserHandle readFromParcel(Parcel in) { + int h = in.readInt(); + return h != USER_NULL ? new BUserHandle(h) : null; + } + + public static final Parcelable.Creator CREATOR + = new Creator() { + public BUserHandle createFromParcel(Parcel in) { + return new BUserHandle(in); + } + + public BUserHandle[] newArray(int size) { + return new BUserHandle[size]; + } + }; + + /** + * Instantiate a new UserHandle from the data in a Parcel that was + * previously written with {@link #writeToParcel(Parcel, int)}. Note that you + * must not use this with data written by + * {@link #writeToParcel(BUserHandle, Parcel)} since it is not possible + * to handle a null UserHandle here. + * + * @param in The Parcel containing the previously written UserHandle, + * positioned at the location in the buffer where it was written. + */ + public BUserHandle(Parcel in) { + mHandle = in.readInt(); + } +} diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/core/system/user/BUserInfo.java b/Bcore/src/main/java/top/niunaijun/blackbox/core/system/user/BUserInfo.java new file mode 100644 index 00000000..37e4978b --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/core/system/user/BUserInfo.java @@ -0,0 +1,68 @@ +package top.niunaijun.blackbox.core.system.user; + +import android.os.Parcel; +import android.os.Parcelable; + +import top.niunaijun.blackbox.core.system.pm.BPackage; +import top.niunaijun.blackbox.core.system.pm.BPackageSettings; + +/** + * Created by Milk on 4/22/21. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + */ +public class BUserInfo implements Parcelable { + public int id; + public BUserStatus status; + public String name; + public long createTime; + + BUserInfo() { + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(this.id); + dest.writeInt(this.status == null ? -1 : this.status.ordinal()); + dest.writeString(this.name); + dest.writeLong(this.createTime); + } + + protected BUserInfo(Parcel in) { + this.id = in.readInt(); + int tmpStatus = in.readInt(); + this.status = tmpStatus == -1 ? null : BUserStatus.values()[tmpStatus]; + this.name = in.readString(); + this.createTime = in.readLong(); + } + + public static final Creator CREATOR = new Creator() { + @Override + public BUserInfo createFromParcel(Parcel source) { + return new BUserInfo(source); + } + + @Override + public BUserInfo[] newArray(int size) { + return new BUserInfo[size]; + } + }; + + @Override + public String toString() { + return "BUserInfo{" + + "id=" + id + + ", status=" + status + + ", name='" + name + '\'' + + ", createTime=" + createTime + + '}'; + } +} diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/core/system/user/BUserManagerService.java b/Bcore/src/main/java/top/niunaijun/blackbox/core/system/user/BUserManagerService.java new file mode 100644 index 00000000..2f223a05 --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/core/system/user/BUserManagerService.java @@ -0,0 +1,166 @@ +package top.niunaijun.blackbox.core.system.user; + +import android.os.Parcel; +import android.os.RemoteException; + +import androidx.core.util.AtomicFile; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +import top.niunaijun.blackbox.core.env.BEnvironment; +import top.niunaijun.blackbox.core.system.ISystemService; +import top.niunaijun.blackbox.core.system.pm.BPackageManagerService; +import top.niunaijun.blackbox.utils.CloseUtils; +import top.niunaijun.blackbox.utils.FileUtils; + +/** + * Created by Milk on 4/22/21. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + */ +public class BUserManagerService extends IBUserManagerService.Stub implements ISystemService { + private static BUserManagerService sService = new BUserManagerService(); + public final HashMap mUsers = new HashMap<>(); + public final Object mUserLock = new Object(); + + public static BUserManagerService get() { + return sService; + } + + @Override + public void systemReady() { + scanUserL(); + } + + @Override + public BUserInfo getUserInfo(int userId) { + synchronized (mUserLock) { + return mUsers.get(userId); + } + } + + @Override + public boolean exists(int userId) { + synchronized (mUsers) { + return mUsers.get(userId) != null; + } + } + + @Override + public BUserInfo createUser(int userId) throws RemoteException { + synchronized (mUserLock) { + if (exists(userId)) { + return getUserInfo(userId); + } + return createUserLocked(userId); + } + } + + @Override + public List getUsers() { + synchronized (mUsers) { + ArrayList bUsers = new ArrayList<>(); + for (BUserInfo value : mUsers.values()) { + if (value.id >= 0) { + bUsers.add(value); + } + } + return bUsers; + } + } + + public List getAllUsers() { + synchronized (mUsers) { + return new ArrayList<>(mUsers.values()); + } + } + + @Override + public void deleteUser(int userId) throws RemoteException { + synchronized (mUserLock) { + synchronized (mUsers) { + BPackageManagerService.get().deleteUser(userId); + + mUsers.remove(userId); + saveUserInfoLocked(); + FileUtils.deleteDir(BEnvironment.getUserDir(userId)); + FileUtils.deleteDir(BEnvironment.getExternalUserDir(userId)); + } + } + } + + private BUserInfo createUserLocked(int userId) { + BUserInfo bUserInfo = new BUserInfo(); + bUserInfo.id = userId; + bUserInfo.status = BUserStatus.ENABLE; + mUsers.put(userId, bUserInfo); + synchronized (mUsers) { + saveUserInfoLocked(); + } + return bUserInfo; + } + + private void saveUserInfoLocked() { + Parcel parcel = Parcel.obtain(); + AtomicFile atomicFile = new AtomicFile(BEnvironment.getUserInfoConf()); + FileOutputStream fileOutputStream = null; + try { + ArrayList bUsers = new ArrayList<>(mUsers.values()); + parcel.writeTypedList(bUsers); + try { + fileOutputStream = atomicFile.startWrite(); + FileUtils.writeParcelToOutput(parcel, fileOutputStream); + atomicFile.finishWrite(fileOutputStream); + } catch (IOException e) { + e.printStackTrace(); + atomicFile.failWrite(fileOutputStream); + } finally { + CloseUtils.close(fileOutputStream); + } + } finally { + parcel.recycle(); + } + } + + private void scanUserL() { + synchronized (mUserLock) { + Parcel parcel = Parcel.obtain(); + InputStream is = null; + try { + File userInfoConf = BEnvironment.getUserInfoConf(); + if (!userInfoConf.exists()) { + return; + } + is = new FileInputStream(BEnvironment.getUserInfoConf()); + byte[] bytes = FileUtils.toByteArray(is); + parcel.unmarshall(bytes, 0, bytes.length); + parcel.setDataPosition(0); + + ArrayList loadUsers = parcel.createTypedArrayList(BUserInfo.CREATOR); + if (loadUsers == null) + return; + synchronized (mUsers) { + mUsers.clear(); + for (BUserInfo loadUser : loadUsers) { + mUsers.put(loadUser.id, loadUser); + } + } + } catch (Exception e) { + e.printStackTrace(); + } finally { + parcel.recycle(); + CloseUtils.close(is); + } + } + } +} \ No newline at end of file diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/core/system/user/BUserStatus.java b/Bcore/src/main/java/top/niunaijun/blackbox/core/system/user/BUserStatus.java new file mode 100644 index 00000000..1bd1070c --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/core/system/user/BUserStatus.java @@ -0,0 +1,13 @@ +package top.niunaijun.blackbox.core.system.user; + +/** + * Created by Milk on 4/22/21. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + */ +public enum BUserStatus { + ENABLE, DISABLE +} diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/entity/AppConfig.java b/Bcore/src/main/java/top/niunaijun/blackbox/entity/AppConfig.java new file mode 100644 index 00000000..c2841d8f --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/entity/AppConfig.java @@ -0,0 +1,70 @@ +package top.niunaijun.blackbox.entity; + +import android.os.IBinder; +import android.os.Parcel; +import android.os.Parcelable; + + +/** + * Created by Milk on 4/1/21. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + */ +public class AppConfig implements Parcelable { + public static final String KEY = "BlackBox_client_config"; + + public String packageName; + public String processName; + public int bpid; + public int buid; + public int uid; + public int userId; + public int baseBUid; + public IBinder token; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(this.packageName); + dest.writeString(this.processName); + dest.writeInt(this.bpid); + dest.writeInt(this.buid); + dest.writeInt(this.uid); + dest.writeInt(this.userId); + dest.writeInt(this.baseBUid); + dest.writeStrongBinder(token); + } + + public AppConfig() { + } + + protected AppConfig(Parcel in) { + this.packageName = in.readString(); + this.processName = in.readString(); + this.bpid = in.readInt(); + this.buid = in.readInt(); + this.uid = in.readInt(); + this.userId = in.readInt(); + this.baseBUid = in.readInt(); + this.token = in.readStrongBinder(); + } + + public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { + @Override + public AppConfig createFromParcel(Parcel source) { + return new AppConfig(source); + } + + @Override + public AppConfig[] newArray(int size) { + return new AppConfig[size]; + } + }; +} diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/entity/ServiceRecord.java b/Bcore/src/main/java/top/niunaijun/blackbox/entity/ServiceRecord.java new file mode 100644 index 00000000..83ffbce5 --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/entity/ServiceRecord.java @@ -0,0 +1,129 @@ +package top.niunaijun.blackbox.entity; + +import android.app.Service; +import android.content.Intent; +import android.os.IBinder; +import android.os.RemoteException; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * Created by Milk on 4/7/21. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + */ +public class ServiceRecord { + private Service mService; + private Map mBounds = new HashMap<>(); + private boolean rebind; + private int mStartId; + + public class BoundInfo { + private IBinder mIBinder; + private AtomicInteger mBindCount = new AtomicInteger(0); + + public int incrementAndGetBindCount() { + return mBindCount.incrementAndGet(); + } + + public int decrementAndGetBindCount() { + return mBindCount.decrementAndGet(); + } + + public IBinder getIBinder() { + return mIBinder; + } + + public void setIBinder(IBinder IBinder) { + mIBinder = IBinder; + } + } + + public int getStartId() { + return mStartId; + } + + public void setStartId(int startId) { + mStartId = startId; + } + + public Service getService() { + return mService; + } + + public void setService(Service service) { + mService = service; + } + + public IBinder getBinder(Intent intent) { + BoundInfo boundInfo = getOrCreateBoundInfo(intent); + return boundInfo.getIBinder(); + } + + public boolean hasBinder(Intent intent) { + BoundInfo boundInfo = getOrCreateBoundInfo(intent); + return boundInfo.getIBinder() != null; + } + + public void addBinder(Intent intent, final IBinder iBinder) { + final Intent.FilterComparison filterComparison = new Intent.FilterComparison(intent); + BoundInfo boundInfo = getOrCreateBoundInfo(intent); + if (boundInfo == null) { + boundInfo = new BoundInfo(); + mBounds.put(filterComparison, boundInfo); + } + boundInfo.setIBinder(iBinder); + try { + iBinder.linkToDeath(new IBinder.DeathRecipient() { + @Override + public void binderDied() { + iBinder.unlinkToDeath(this, 0); + mBounds.remove(filterComparison); + } + }, 0); + } catch (RemoteException e) { + e.printStackTrace(); + } + } + + public int incrementAndGetBindCount(Intent intent) { + BoundInfo boundInfo = getOrCreateBoundInfo(intent); + return boundInfo.incrementAndGetBindCount(); + } + + public boolean decreaseConnectionCount(Intent intent) { + Intent.FilterComparison filterComparison = new Intent.FilterComparison(intent); + BoundInfo boundInfo = mBounds.get(filterComparison); + if (boundInfo == null) + return true; + int i = boundInfo.decrementAndGetBindCount(); + if (i <= 0) { +// mBounds.remove(filterComparison); + return true; + } + return false; + } + + public BoundInfo getOrCreateBoundInfo(Intent intent) { + Intent.FilterComparison filterComparison = new Intent.FilterComparison(intent); + BoundInfo boundInfo = mBounds.get(filterComparison); + if (boundInfo == null) { + boundInfo = new BoundInfo(); + mBounds.put(filterComparison, boundInfo); + } + return boundInfo; + } + + public boolean isRebind() { + return rebind; + } + + public void setRebind(boolean rebind) { + this.rebind = rebind; + } +} diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/entity/pm/InstallOption.java b/Bcore/src/main/java/top/niunaijun/blackbox/entity/pm/InstallOption.java new file mode 100644 index 00000000..8ef9fe25 --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/entity/pm/InstallOption.java @@ -0,0 +1,70 @@ +package top.niunaijun.blackbox.entity.pm; + +import android.os.Parcel; +import android.os.Parcelable; + +/** + * Created by Milk on 4/21/21. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + */ +public class InstallOption implements Parcelable { + public static final int FLAG_SYSTEM = 1; + public static final int FLAG_STORAGE = 1 << 1; + public static final int FLAG_URI_FILE = 1 << 3; + + public int flags = 0; + + public static InstallOption installBySystem() { + InstallOption installOption = new InstallOption(); + installOption.flags = installOption.flags | FLAG_SYSTEM; + return installOption; + } + + public static InstallOption installByStorage() { + InstallOption installOption = new InstallOption(); + installOption.flags = installOption.flags | FLAG_STORAGE; + return installOption; + } + + public InstallOption makeUriFile() { + this.flags |= FLAG_URI_FILE; + return this; + } + + public boolean isFlag(int flag) { + return (flags & flag) != 0; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(this.flags); + } + + public InstallOption() { + } + + protected InstallOption(Parcel in) { + this.flags = in.readInt(); + } + + public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { + @Override + public InstallOption createFromParcel(Parcel source) { + return new InstallOption(source); + } + + @Override + public InstallOption[] newArray(int size) { + return new InstallOption[size]; + } + }; +} diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/entity/pm/InstallResult.java b/Bcore/src/main/java/top/niunaijun/blackbox/entity/pm/InstallResult.java new file mode 100644 index 00000000..89c61a91 --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/entity/pm/InstallResult.java @@ -0,0 +1,71 @@ +package top.niunaijun.blackbox.entity.pm; + +import android.os.Parcel; +import android.os.Parcelable; + +import top.niunaijun.blackbox.utils.Slog; + +/** + * Created by Milk on 4/20/21. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + */ +public class InstallResult implements Parcelable { + public static final String TAG = "InstallResult"; + + public boolean success = true; + public String packageName; + public String msg; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeByte(this.success ? (byte) 1 : (byte) 0); + dest.writeString(this.packageName); + dest.writeString(this.msg); + } + + public InstallResult() { + } + + protected InstallResult(Parcel in) { + this.success = in.readByte() != 0; + this.packageName = in.readString(); + this.msg = in.readString(); + } + + public InstallResult installError(String msg) { + this.msg = msg; + this.success = false; + Slog.d(TAG, msg); + return this; + } + + public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { + @Override + public InstallResult createFromParcel(Parcel source) { + return new InstallResult(source); + } + + @Override + public InstallResult[] newArray(int size) { + return new InstallResult[size]; + } + }; + + @Override + public String toString() { + return "InstallResult{" + + "success=" + success + + ", packageName='" + packageName + '\'' + + ", msg='" + msg + '\'' + + '}'; + } +} diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/entity/pm/InstalledModule.java b/Bcore/src/main/java/top/niunaijun/blackbox/entity/pm/InstalledModule.java new file mode 100644 index 00000000..0559e164 --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/entity/pm/InstalledModule.java @@ -0,0 +1,72 @@ +package top.niunaijun.blackbox.entity.pm; + +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.os.Parcel; +import android.os.Parcelable; + +import top.niunaijun.blackbox.BlackBoxCore; +import top.niunaijun.blackbox.core.system.user.BUserHandle; + +/** + * Created by Milk on 5/2/21. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + */ +public class InstalledModule implements Parcelable { + public String packageName; + public String name; + public String desc; + public String main; + public boolean enable; + + public InstalledModule() { + } + + + public ApplicationInfo getApplication() { + return BlackBoxCore.getBPackageManager().getApplicationInfo(packageName, PackageManager.GET_META_DATA, BUserHandle.USER_XPOSED); + } + + public PackageInfo getPackageInfo() { + return BlackBoxCore.getBPackageManager().getPackageInfo(packageName, PackageManager.GET_META_DATA, BUserHandle.USER_XPOSED); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(this.packageName); + dest.writeString(this.name); + dest.writeString(this.desc); + dest.writeString(this.main); + dest.writeByte(this.enable ? (byte) 1 : (byte) 0); + } + + protected InstalledModule(Parcel in) { + this.packageName = in.readString(); + this.name = in.readString(); + this.desc = in.readString(); + this.main = in.readString(); + this.enable = in.readByte() != 0; + } + + public static final Creator CREATOR = new Creator() { + @Override + public InstalledModule createFromParcel(Parcel source) { + return new InstalledModule(source); + } + + @Override + public InstalledModule[] newArray(int size) { + return new InstalledModule[size]; + } + }; +} diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/entity/pm/InstalledPackage.java b/Bcore/src/main/java/top/niunaijun/blackbox/entity/pm/InstalledPackage.java new file mode 100644 index 00000000..b842096c --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/entity/pm/InstalledPackage.java @@ -0,0 +1,81 @@ +package top.niunaijun.blackbox.entity.pm; + +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.Objects; + +import top.niunaijun.blackbox.BlackBoxCore; +import top.niunaijun.blackbox.core.system.pm.BPackageSettings; + +/** + * Created by Milk on 4/20/21. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + */ +public class InstalledPackage implements Parcelable { + public int userId; + public String packageName; + + public ApplicationInfo getApplication() { + return BlackBoxCore.getBPackageManager().getApplicationInfo(packageName, PackageManager.GET_META_DATA, userId); + } + + public PackageInfo getPackageInfo() { + return BlackBoxCore.getBPackageManager().getPackageInfo(packageName, PackageManager.GET_META_DATA, userId); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(this.userId); + dest.writeString(this.packageName); + } + + public InstalledPackage() { + } + + public InstalledPackage(String packageName) { + this.packageName = packageName; + } + + protected InstalledPackage(Parcel in) { + this.userId = in.readInt(); + this.packageName = in.readString(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + InstalledPackage that = (InstalledPackage) o; + return Objects.equals(packageName, that.packageName); + } + + @Override + public int hashCode() { + return Objects.hash(packageName); + } + + public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { + @Override + public InstalledPackage createFromParcel(Parcel source) { + return new InstalledPackage(source); + } + + @Override + public InstalledPackage[] newArray(int size) { + return new InstalledPackage[size]; + } + }; +} diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/entity/pm/XposedConfig.java b/Bcore/src/main/java/top/niunaijun/blackbox/entity/pm/XposedConfig.java new file mode 100644 index 00000000..9e161755 --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/entity/pm/XposedConfig.java @@ -0,0 +1,61 @@ +package top.niunaijun.blackbox.entity.pm; + +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.HashMap; +import java.util.Map; + +/** + * Created by Milk on 5/2/21. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + */ +public class XposedConfig implements Parcelable { + public boolean enable; + public Map moduleState = new HashMap<>(); + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeByte(this.enable ? (byte) 1 : (byte) 0); + dest.writeInt(this.moduleState.size()); + for (Map.Entry entry : this.moduleState.entrySet()) { + dest.writeString(entry.getKey()); + dest.writeValue(entry.getValue()); + } + } + + public XposedConfig() { + } + + public XposedConfig(Parcel in) { + this.enable = in.readByte() != 0; + int mModuleStateSize = in.readInt(); + this.moduleState = new HashMap(mModuleStateSize); + for (int i = 0; i < mModuleStateSize; i++) { + String key = in.readString(); + Boolean value = (Boolean) in.readValue(Boolean.class.getClassLoader()); + this.moduleState.put(key, value); + } + } + + public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { + @Override + public XposedConfig createFromParcel(Parcel source) { + return new XposedConfig(source); + } + + @Override + public XposedConfig[] newArray(int size) { + return new XposedConfig[size]; + } + }; +} diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/fake/delegate/AppInstrumentation.java b/Bcore/src/main/java/top/niunaijun/blackbox/fake/delegate/AppInstrumentation.java new file mode 100644 index 00000000..8e797832 --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/fake/delegate/AppInstrumentation.java @@ -0,0 +1,123 @@ +package top.niunaijun.blackbox.fake.delegate; + +import android.app.Activity; +import android.app.Application; +import android.app.Instrumentation; +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.util.Log; + +import java.lang.reflect.Field; + +import mirror.android.app.ActivityThread; +import top.niunaijun.blackbox.BlackBoxCore; +import top.niunaijun.blackbox.fake.hook.HookManager; +import top.niunaijun.blackbox.fake.hook.IInjectHook; +import top.niunaijun.blackbox.utils.compat.ContextCompat; +import top.niunaijun.blackbox.fake.service.HCallbackProxy; + +public final class AppInstrumentation extends BaseInstrumentationDelegate implements IInjectHook { + + private static final String TAG = AppInstrumentation.class.getSimpleName(); + + private static AppInstrumentation sAppInstrumentation; + + public static AppInstrumentation get() { + if (sAppInstrumentation == null) { + synchronized (AppInstrumentation.class) { + if (sAppInstrumentation == null) { + sAppInstrumentation = new AppInstrumentation(); + } + } + } + return sAppInstrumentation; + } + + public AppInstrumentation() { + } + + @Override + public void injectHook() { + try { + Instrumentation mInstrumentation = getCurrInstrumentation(); + if (mInstrumentation == this || checkInstrumentation(mInstrumentation)) + return; + mBaseInstrumentation = (Instrumentation) mInstrumentation; + ActivityThread.mInstrumentation.set(BlackBoxCore.mainThread(), this); + } catch (Exception e) { + e.printStackTrace(); + } + } + + private Instrumentation getCurrInstrumentation() { + Object currentActivityThread = BlackBoxCore.mainThread(); + return ActivityThread.mInstrumentation.get(currentActivityThread); + } + + @Override + public boolean isBadEnv() { + return !checkInstrumentation(getCurrInstrumentation()); + } + + private boolean checkInstrumentation(Instrumentation instrumentation) { + if (instrumentation instanceof AppInstrumentation) { + return true; + } + Class clazz = instrumentation.getClass(); + if (Instrumentation.class.equals(clazz)) { + return false; + } + do { + assert clazz != null; + Field[] fields = clazz.getDeclaredFields(); + for (Field field : fields) { + if (Instrumentation.class.isAssignableFrom(field.getType())) { + field.setAccessible(true); + try { + Object obj = field.get(instrumentation); + if ((obj instanceof AppInstrumentation)) { + return true; + } + } catch (Exception e) { + return false; + } + } + } + clazz = clazz.getSuperclass(); + } while (!Instrumentation.class.equals(clazz)); + return false; + } + + private void checkHCallback() { + HookManager.get().checkEnv(HCallbackProxy.class); + } + + @Override + public Application newApplication(ClassLoader cl, String className, Context context) throws InstantiationException, IllegalAccessException, ClassNotFoundException { + ContextCompat.fix(context); + return super.newApplication(cl, className, context); + } + + @Override + public void callActivityOnCreate(Activity activity, Bundle icicle) { + checkHCallback(); + Log.d(TAG, "callActivityOnCreate: " + activity.getClass().getName()); + ContextCompat.fix(activity); + super.callActivityOnCreate(activity, icicle); + } + + @Override + public void callApplicationOnCreate(Application app) { + checkHCallback(); + super.callApplicationOnCreate(app); + } + + public Activity newActivity(ClassLoader cl, String className, Intent intent) throws InstantiationException, IllegalAccessException, ClassNotFoundException { + try { + return super.newActivity(cl, className, intent); + } catch (ClassNotFoundException e) { + return mBaseInstrumentation.newActivity(cl, className, intent); + } + } +} diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/fake/delegate/BaseInstrumentationDelegate.java b/Bcore/src/main/java/top/niunaijun/blackbox/fake/delegate/BaseInstrumentationDelegate.java new file mode 100644 index 00000000..110d4ff9 --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/fake/delegate/BaseInstrumentationDelegate.java @@ -0,0 +1,423 @@ +package top.niunaijun.blackbox.fake.delegate; + + +import android.annotation.TargetApi; +import android.app.Activity; +import android.app.Application; +import android.app.Fragment; +import android.app.Instrumentation; +import android.app.UiAutomation; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.ActivityInfo; +import android.os.Build; +import android.os.Bundle; +import android.os.IBinder; +import android.os.Looper; +import android.os.PersistableBundle; +import android.os.TestLooperManager; +import android.os.UserHandle; +import android.view.KeyEvent; +import android.view.MotionEvent; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import top.niunaijun.blackbox.utils.Reflector; + +public class BaseInstrumentationDelegate extends Instrumentation { + + protected Instrumentation mBaseInstrumentation; + + + @Override + public void onCreate(Bundle arguments) { + mBaseInstrumentation.onCreate(arguments); + } + + @Override + public void start() { + mBaseInstrumentation.start(); + } + + @Override + public void onStart() { + mBaseInstrumentation.onStart(); + } + + @Override + public boolean onException(Object obj, Throwable e) { + return mBaseInstrumentation.onException(obj, e); + } + + @Override + public void sendStatus(int resultCode, Bundle results) { + mBaseInstrumentation.sendStatus(resultCode, results); + } + + @Override + public void addResults(Bundle results) { + mBaseInstrumentation.addResults(results); + } + + @Override + public void finish(int resultCode, Bundle results) { + mBaseInstrumentation.finish(resultCode, results); + } + + @Override + public void setAutomaticPerformanceSnapshots() { + mBaseInstrumentation.setAutomaticPerformanceSnapshots(); + } + + @Override + public void startPerformanceSnapshot() { + mBaseInstrumentation.startPerformanceSnapshot(); + } + + @Override + public void endPerformanceSnapshot() { + mBaseInstrumentation.endPerformanceSnapshot(); + } + + @Override + public void onDestroy() { + mBaseInstrumentation.onDestroy(); + } + + @Override + public Context getContext() { + return mBaseInstrumentation.getContext(); + } + + @Override + public ComponentName getComponentName() { + return mBaseInstrumentation.getComponentName(); + } + + @Override + public Context getTargetContext() { + return mBaseInstrumentation.getTargetContext(); + } + + @Override + public boolean isProfiling() { + return mBaseInstrumentation.isProfiling(); + } + + @Override + public void startProfiling() { + mBaseInstrumentation.startProfiling(); + } + + @Override + public void stopProfiling() { + mBaseInstrumentation.stopProfiling(); + } + + @Override + public void setInTouchMode(boolean inTouch) { + mBaseInstrumentation.setInTouchMode(inTouch); + } + + @Override + public void waitForIdle(Runnable recipient) { + mBaseInstrumentation.waitForIdle(recipient); + } + + @Override + public void waitForIdleSync() { + mBaseInstrumentation.waitForIdleSync(); + } + + @Override + public void runOnMainSync(Runnable runner) { + mBaseInstrumentation.runOnMainSync(runner); + } + + @Override + public Activity startActivitySync(Intent intent) { + return mBaseInstrumentation.startActivitySync(intent); + } + + @Override + public void addMonitor(ActivityMonitor monitor) { + mBaseInstrumentation.addMonitor(monitor); + } + + @Override + public ActivityMonitor addMonitor(IntentFilter filter, ActivityResult result, boolean block) { + return mBaseInstrumentation.addMonitor(filter, result, block); + } + + @Override + public ActivityMonitor addMonitor(String cls, ActivityResult result, boolean block) { + return mBaseInstrumentation.addMonitor(cls, result, block); + } + + @Override + public boolean checkMonitorHit(ActivityMonitor monitor, int minHits) { + return mBaseInstrumentation.checkMonitorHit(monitor, minHits); + } + + @Override + public Activity waitForMonitor(ActivityMonitor monitor) { + return mBaseInstrumentation.waitForMonitor(monitor); + } + + @Override + public Activity waitForMonitorWithTimeout(ActivityMonitor monitor, long timeOut) { + return mBaseInstrumentation.waitForMonitorWithTimeout(monitor, timeOut); + } + + @Override + public void removeMonitor(ActivityMonitor monitor) { + mBaseInstrumentation.removeMonitor(monitor); + } + + @Override + public boolean invokeMenuActionSync(Activity targetActivity, int id, int flag) { + return mBaseInstrumentation.invokeMenuActionSync(targetActivity, id, flag); + } + + @Override + public boolean invokeContextMenuAction(Activity targetActivity, int id, int flag) { + return mBaseInstrumentation.invokeContextMenuAction(targetActivity, id, flag); + } + + @Override + public void sendStringSync(String text) { + mBaseInstrumentation.sendStringSync(text); + } + + @Override + public void sendKeySync(KeyEvent event) { + mBaseInstrumentation.sendKeySync(event); + } + + @Override + public void sendKeyDownUpSync(int key) { + mBaseInstrumentation.sendKeyDownUpSync(key); + } + + @Override + public void sendCharacterSync(int keyCode) { + mBaseInstrumentation.sendCharacterSync(keyCode); + } + + @Override + public void sendPointerSync(MotionEvent event) { + mBaseInstrumentation.sendPointerSync(event); + } + + @Override + public void sendTrackballEventSync(MotionEvent event) { + mBaseInstrumentation.sendTrackballEventSync(event); + } + + @Override + public Application newApplication(ClassLoader cl, String className, Context context) throws ClassNotFoundException, IllegalAccessException, InstantiationException { + return mBaseInstrumentation.newApplication(cl, className, context); + } + + @Override + public void callApplicationOnCreate(Application app) { + mBaseInstrumentation.callApplicationOnCreate(app); + } + + @Override + public Activity newActivity(Class clazz, Context context, IBinder token, Application application, Intent intent, ActivityInfo info, CharSequence title, Activity parent, String id, Object lastNonConfigurationInstance) throws IllegalAccessException, InstantiationException { + return mBaseInstrumentation.newActivity(clazz, context, token, application, intent, info, title, parent, id, lastNonConfigurationInstance); + } + + @Override + public Activity newActivity(ClassLoader cl, String className, Intent intent) throws ClassNotFoundException, IllegalAccessException, InstantiationException { + return mBaseInstrumentation.newActivity(cl, className, intent); + } + + @Override + public void callActivityOnCreate(Activity activity, Bundle icicle) { + mBaseInstrumentation.callActivityOnCreate(activity, icicle); + } + + @Override + public void callActivityOnCreate(Activity activity, Bundle icicle, PersistableBundle persistentState) { + mBaseInstrumentation.callActivityOnCreate(activity, icicle, persistentState); + } + + @Override + public void callActivityOnDestroy(Activity activity) { + mBaseInstrumentation.callActivityOnDestroy(activity); + } + + @Override + public void callActivityOnRestoreInstanceState(Activity activity, Bundle savedInstanceState) { + mBaseInstrumentation.callActivityOnRestoreInstanceState(activity, savedInstanceState); + } + + @Override + public void callActivityOnRestoreInstanceState(Activity activity, Bundle savedInstanceState, PersistableBundle persistentState) { + mBaseInstrumentation.callActivityOnRestoreInstanceState(activity, savedInstanceState, persistentState); + } + + @Override + public void callActivityOnPostCreate(Activity activity, Bundle icicle) { + mBaseInstrumentation.callActivityOnPostCreate(activity, icicle); + } + + @Override + public void callActivityOnPostCreate(Activity activity, Bundle icicle, PersistableBundle persistentState) { + mBaseInstrumentation.callActivityOnPostCreate(activity, icicle, persistentState); + } + + @Override + public void callActivityOnNewIntent(Activity activity, Intent intent) { + mBaseInstrumentation.callActivityOnNewIntent(activity, intent); + } + + @Override + public void callActivityOnStart(Activity activity) { + mBaseInstrumentation.callActivityOnStart(activity); + } + + @Override + public void callActivityOnRestart(Activity activity) { + mBaseInstrumentation.callActivityOnRestart(activity); + } + + @Override + public void callActivityOnResume(Activity activity) { + mBaseInstrumentation.callActivityOnResume(activity); + } + + @Override + public void callActivityOnStop(Activity activity) { + mBaseInstrumentation.callActivityOnStop(activity); + } + + @Override + public void callActivityOnSaveInstanceState(Activity activity, Bundle outState) { + mBaseInstrumentation.callActivityOnSaveInstanceState(activity, outState); + } + + @Override + public void callActivityOnSaveInstanceState(Activity activity, Bundle outState, PersistableBundle outPersistentState) { + mBaseInstrumentation.callActivityOnSaveInstanceState(activity, outState, outPersistentState); + } + + @Override + public void callActivityOnPause(Activity activity) { + mBaseInstrumentation.callActivityOnPause(activity); + } + + @Override + public void callActivityOnUserLeaving(Activity activity) { + mBaseInstrumentation.callActivityOnUserLeaving(activity); + } + + @Override + public void startAllocCounting() { + mBaseInstrumentation.startAllocCounting(); + } + + @Override + public void stopAllocCounting() { + mBaseInstrumentation.stopAllocCounting(); + } + + @Override + public Bundle getAllocCounts() { + return mBaseInstrumentation.getAllocCounts(); + } + + @Override + public Bundle getBinderCounts() { + return mBaseInstrumentation.getBinderCounts(); + } + + @Override + public UiAutomation getUiAutomation() { + return mBaseInstrumentation.getUiAutomation(); + } + + public ActivityResult execStartActivity(Context context, IBinder binder, IBinder binder1, Activity activity, Intent intent, int i, Bundle bundle) throws Throwable { + return invokeExecStartActivity(mBaseInstrumentation, + Context.class, + IBinder.class, + IBinder.class, + Activity.class, + Intent.class, + Integer.TYPE, + Bundle.class).callByCaller(mBaseInstrumentation, new Object[]{context, binder, binder1, activity, intent, i, bundle}); + } + + public ActivityResult execStartActivity(Context context, IBinder binder, IBinder binder1, String str, Intent intent, int i, Bundle bundle) throws Throwable { + return invokeExecStartActivity(mBaseInstrumentation, + Context.class, + IBinder.class, + IBinder.class, + String.class, + Intent.class, + Integer.TYPE, + Bundle.class).callByCaller(mBaseInstrumentation, new Object[]{context, binder, binder1, str, intent, i, bundle}); + } + + public ActivityResult execStartActivity(Context context, IBinder binder, IBinder binder1, Fragment fragment, Intent intent, int i) throws Throwable { + return invokeExecStartActivity(mBaseInstrumentation, + Context.class, + IBinder.class, + IBinder.class, + Fragment.class, + Intent.class, + Integer.TYPE).callByCaller(mBaseInstrumentation, new Object[]{context, binder, binder1, fragment, intent, i}); + } + + public ActivityResult execStartActivity(Context context, IBinder binder, IBinder binder1, Activity activity, Intent intent, int i) throws Throwable { + return invokeExecStartActivity(mBaseInstrumentation, + Context.class, + IBinder.class, + IBinder.class, + Activity.class, + Intent.class, + Integer.TYPE).callByCaller(mBaseInstrumentation, new Object[]{context, binder, binder1, activity, intent, i}); + } + + public ActivityResult execStartActivity(Context context, IBinder binder, IBinder binder1, Fragment fragment, Intent intent, int i, Bundle bundle) throws Throwable { + return invokeExecStartActivity(mBaseInstrumentation, + Context.class, + IBinder.class, + IBinder.class, + Fragment.class, + Intent.class, + Integer.TYPE, + Bundle.class).callByCaller(mBaseInstrumentation, new Object[]{context, binder, binder1, fragment, intent, i, bundle}); + } + + @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) + public ActivityResult execStartActivity(Context context, IBinder iBinder, IBinder iBinder2, Activity activity, Intent intent, int i, Bundle bundle, UserHandle userHandle) throws Throwable { + return invokeExecStartActivity(mBaseInstrumentation, + Context.class, + IBinder.class, + IBinder.class, + Activity.class, + Intent.class, + Integer.TYPE, + Bundle.class, + UserHandle.class).callByCaller(mBaseInstrumentation, new Object[]{context, iBinder, iBinder2, activity, intent, i, bundle, userHandle}); + } + + private static Reflector invokeExecStartActivity(Object obj, Class... args) throws NoSuchMethodException { + Class cls = obj.getClass(); + while (cls != null) { + try { + return Reflector.on(obj.getClass()) + .method("execStartActivity", args); + } catch (Exception e) { + cls = cls.getSuperclass(); + } + } + throw new NoSuchMethodException(); + } +} diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/fake/frameworks/BActivityManager.java b/Bcore/src/main/java/top/niunaijun/blackbox/fake/frameworks/BActivityManager.java new file mode 100644 index 00000000..41d0bcf0 --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/fake/frameworks/BActivityManager.java @@ -0,0 +1,41 @@ +package top.niunaijun.blackbox.fake.frameworks; + +import android.content.Intent; +import android.os.RemoteException; + +import top.niunaijun.blackbox.BlackBoxCore; +import top.niunaijun.blackbox.core.system.ServiceManager; +import top.niunaijun.blackbox.core.system.am.IBActivityManagerService; + +/** + * Created by Milk on 4/14/21. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + */ +public class BActivityManager { + private static final BActivityManager sActivityManager = new BActivityManager(); + private IBActivityManagerService mService; + + public static BActivityManager get() { + return sActivityManager; + } + + public void startActivity(Intent intent, int userId) { + try { + getService().startActivity(intent, userId); + } catch (Exception e) { + e.printStackTrace(); + } + } + + private IBActivityManagerService getService() { + if (mService != null && mService.asBinder().isBinderAlive()) { + return mService; + } + mService = IBActivityManagerService.Stub.asInterface(BlackBoxCore.get().getService(ServiceManager.ACTIVITY_MANAGER)); + return getService(); + } +} diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/fake/frameworks/BPackageManager.java b/Bcore/src/main/java/top/niunaijun/blackbox/fake/frameworks/BPackageManager.java new file mode 100644 index 00000000..9fdad32a --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/fake/frameworks/BPackageManager.java @@ -0,0 +1,258 @@ +package top.niunaijun.blackbox.fake.frameworks; + +import android.content.ComponentName; +import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; +import android.content.pm.ProviderInfo; +import android.content.pm.ResolveInfo; +import android.content.pm.ServiceInfo; +import android.os.RemoteException; + +import java.util.Collections; +import java.util.List; + +import top.niunaijun.blackbox.BlackBoxCore; +import top.niunaijun.blackbox.entity.pm.InstallOption; +import top.niunaijun.blackbox.entity.pm.InstallResult; +import top.niunaijun.blackbox.entity.pm.InstalledPackage; +import top.niunaijun.blackbox.core.system.ServiceManager; +import top.niunaijun.blackbox.core.system.pm.IBPackageManagerService; + +/** + * Created by Milk on 4/14/21. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + */ +public class BPackageManager { + private static final BPackageManager sPackageManager = new BPackageManager(); + private IBPackageManagerService mService; + + public static BPackageManager get() { + return sPackageManager; + } + + public Intent getLaunchIntentForPackage(String packageName, int userId) { + Intent intentToResolve = new Intent(Intent.ACTION_MAIN); + intentToResolve.addCategory(Intent.CATEGORY_INFO); + intentToResolve.setPackage(packageName); + List ris = queryIntentActivities(intentToResolve, + 0, + intentToResolve.resolveTypeIfNeeded(BlackBoxCore.getContext().getContentResolver()), + userId); + + // Otherwise, try to find a main launcher activity. + if (ris == null || ris.size() <= 0) { + // reuse the intent instance + intentToResolve.removeCategory(Intent.CATEGORY_INFO); + intentToResolve.addCategory(Intent.CATEGORY_LAUNCHER); + intentToResolve.setPackage(packageName); + ris = queryIntentActivities(intentToResolve, + 0, + intentToResolve.resolveTypeIfNeeded(BlackBoxCore.getContext().getContentResolver()), + userId); + } + if (ris == null || ris.size() <= 0) { + return null; + } + Intent intent = new Intent(intentToResolve); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.setClassName(ris.get(0).activityInfo.packageName, + ris.get(0).activityInfo.name); + return intent; + } + + public ResolveInfo resolveService(Intent intent, int flags, String resolvedType, int userId) { + try { + return getService().resolveService(intent, flags, resolvedType, userId); + } catch (RemoteException e) { + crash(e); + } + return null; + } + + public ResolveInfo resolveActivity(Intent intent, int flags, String resolvedType, int userId) { + try { + return getService().resolveActivity(intent, flags, resolvedType, userId); + } catch (RemoteException e) { + crash(e); + } + return null; + } + + public ProviderInfo resolveContentProvider(String authority, int flags, int userId) { + try { + return getService().resolveContentProvider(authority, flags, userId); + } catch (RemoteException e) { + crash(e); + } + return null; + } + + public ResolveInfo resolveIntent(Intent intent, String resolvedType, int flags, int userId) { + try { + return getService().resolveIntent(intent, resolvedType, flags, userId); + } catch (RemoteException e) { + crash(e); + } + return null; + } + + public ApplicationInfo getApplicationInfo(String packageName, int flags, int userId) { + try { + return getService().getApplicationInfo(packageName, flags, userId); + } catch (RemoteException e) { + crash(e); + } + return null; + } + + public PackageInfo getPackageInfo(String packageName, int flags, int userId) { + try { + return getService().getPackageInfo(packageName, flags, userId); + } catch (RemoteException e) { + crash(e); + } + return null; + } + + public ServiceInfo getServiceInfo(ComponentName component, int flags, int userId) { + try { + return getService().getServiceInfo(component, flags, userId); + } catch (RemoteException e) { + crash(e); + } + return null; + } + + public ActivityInfo getReceiverInfo(ComponentName componentName, int flags, int userId) { + try { + return getService().getReceiverInfo(componentName, flags, userId); + } catch (RemoteException e) { + crash(e); + } + return null; + } + + public ActivityInfo getActivityInfo(ComponentName component, int flags, int userId) { + try { + return getService().getActivityInfo(component, flags, userId); + } catch (RemoteException e) { + crash(e); + } + return null; + } + + public ProviderInfo getProviderInfo(ComponentName component, int flags, int userId) { + try { + return getService().getProviderInfo(component, flags, userId); + } catch (RemoteException e) { + crash(e); + } + return null; + } + + public List queryIntentActivities(Intent intent, int flags, String resolvedType, int userId) { + try { + return getService().queryIntentActivities(intent, flags, resolvedType, userId); + } catch (RemoteException e) { + crash(e); + } + return null; + } + + public List queryBroadcastReceivers(Intent intent, int flags, String resolvedType, int userId) { + try { + return getService().queryBroadcastReceivers(intent, flags, resolvedType, userId); + } catch (RemoteException e) { + crash(e); + } + return null; + } + + public List queryContentProviders(String processName, int uid, int flags, int userId) { + try { + return getService().queryContentProviders(processName, uid, flags, userId); + } catch (RemoteException e) { + crash(e); + } + return null; + } + + public InstallResult installPackageAsUser(String file, InstallOption option, int userId) { + try { + return getService().installPackageAsUser(file, option, userId); + } catch (RemoteException e) { + crash(e); + } + return null; + } + + public List getInstalledApplications(int flags, int userId) { + try { + return getService().getInstalledApplications(flags, userId); + } catch (RemoteException e) { + e.printStackTrace(); + } + return Collections.emptyList(); + } + + public List getInstalledPackages(int flags, int userId) { + try { + return getService().getInstalledPackages(flags, userId); + } catch (RemoteException e) { + e.printStackTrace(); + } + return Collections.emptyList(); + } + + public void uninstallPackageAsUser(String packageName, int userId) { + try { + getService().uninstallPackageAsUser(packageName, userId); + } catch (RemoteException e) { + e.printStackTrace(); + } + } + + public void uninstallPackage(String packageName) { + try { + getService().uninstallPackage(packageName); + } catch (RemoteException e) { + e.printStackTrace(); + } + } + + public boolean isInstalled(String packageName, int userId) { + try { + return getService().isInstalled(packageName, userId); + } catch (RemoteException e) { + e.printStackTrace(); + } + return false; + } + + public List getInstalledPackagesAsUser(int userId) { + try { + return getService().getInstalledPackagesAsUser(userId); + } catch (RemoteException e) { + e.printStackTrace(); + } + return Collections.emptyList(); + } + + private void crash(Throwable e) { + e.printStackTrace(); + } + + private IBPackageManagerService getService() { + if (mService != null && mService.asBinder().isBinderAlive()) { + return mService; + } + mService = IBPackageManagerService.Stub.asInterface(BlackBoxCore.get().getService(ServiceManager.PACKAGE_MANAGER)); + return getService(); + } +} diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/fake/frameworks/BStorageManager.java b/Bcore/src/main/java/top/niunaijun/blackbox/fake/frameworks/BStorageManager.java new file mode 100644 index 00000000..56ef82eb --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/fake/frameworks/BStorageManager.java @@ -0,0 +1,42 @@ +package top.niunaijun.blackbox.fake.frameworks; + +import android.os.RemoteException; +import android.os.storage.StorageVolume; + +import top.niunaijun.blackbox.BlackBoxCore; +import top.niunaijun.blackbox.core.system.ServiceManager; +import top.niunaijun.blackbox.core.system.os.IBStorageManagerService; + +/** + * Created by Milk on 4/14/21. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + */ +public class BStorageManager { + private static final BStorageManager sStorageManager = new BStorageManager(); + private IBStorageManagerService mService; + + public static BStorageManager get() { + return sStorageManager; + } + + public StorageVolume[] getVolumeList(int uid, String packageName, int flags, int userId) { + try { + return getService().getVolumeList(uid, packageName, flags, userId); + } catch (RemoteException e) { + e.printStackTrace(); + } + return new StorageVolume[]{}; + } + + private IBStorageManagerService getService() { + if (mService != null && mService.asBinder().isBinderAlive()) { + return mService; + } + mService = IBStorageManagerService.Stub.asInterface(BlackBoxCore.get().getService(ServiceManager.STORAGE_MANAGER)); + return getService(); + } +} diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/fake/hook/BinderInvocationStub.java b/Bcore/src/main/java/top/niunaijun/blackbox/fake/hook/BinderInvocationStub.java new file mode 100644 index 00000000..bcddbce7 --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/fake/hook/BinderInvocationStub.java @@ -0,0 +1,87 @@ +package top.niunaijun.blackbox.fake.hook; + +import android.os.IBinder; +import android.os.IInterface; +import android.os.Parcel; +import android.os.RemoteException; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import java.io.FileDescriptor; +import java.util.Map; + +import mirror.android.os.ServiceManager; + +/** + * Created by Milk on 3/30/21. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + */ +public abstract class BinderInvocationStub extends ClassInvocationStub implements IBinder { + private IBinder mBaseBinder; + + public BinderInvocationStub(IBinder baseBinder) { + mBaseBinder = baseBinder; + } + + @Override + protected void onBindMethod() { + } + + @Nullable + @Override + public String getInterfaceDescriptor() throws RemoteException { + return mBaseBinder.getInterfaceDescriptor(); + } + + @Override + public boolean pingBinder() { + return mBaseBinder.pingBinder(); + } + + @Override + public boolean isBinderAlive() { + return mBaseBinder.isBinderAlive(); + } + + @Nullable + @Override + public IInterface queryLocalInterface(@NonNull String descriptor) { + return (IInterface) getProxyInvocation(); + } + + @Override + public void dump(@NonNull FileDescriptor fd, @Nullable String[] args) throws RemoteException { + mBaseBinder.dump(fd, args); + } + + @Override + public void dumpAsync(@NonNull FileDescriptor fd, @Nullable String[] args) throws RemoteException { + mBaseBinder.dumpAsync(fd, args); + } + + @Override + public boolean transact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags) throws RemoteException { + return mBaseBinder.transact(code, data, reply, flags); + } + + @Override + public void linkToDeath(@NonNull DeathRecipient recipient, int flags) throws RemoteException { + mBaseBinder.linkToDeath(recipient, flags); + } + + @Override + public boolean unlinkToDeath(@NonNull DeathRecipient recipient, int flags) { + return mBaseBinder.unlinkToDeath(recipient, flags); + } + + + protected void replaceSystemService(String name) { + Map services = ServiceManager.sCache.get(); + services.put(name, this); + } +} diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/fake/hook/ClassInvocationStub.java b/Bcore/src/main/java/top/niunaijun/blackbox/fake/hook/ClassInvocationStub.java new file mode 100644 index 00000000..75ecba63 --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/fake/hook/ClassInvocationStub.java @@ -0,0 +1,118 @@ +package top.niunaijun.blackbox.fake.hook; + +import android.text.TextUtils; +import android.util.Log; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.util.HashMap; +import java.util.Map; + +import top.niunaijun.blackbox.utils.MethodParameterUtils; + +/** + * Created by Milk on 3/30/21. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + */ +public abstract class ClassInvocationStub implements InvocationHandler, IInjectHook { + public static final String TAG = ClassInvocationStub.class.getSimpleName(); + + private Map mMethodHookMap = new HashMap<>(); + private Object mBase; + private Object mProxyInvocation; + + protected abstract Object getWho(); + + protected abstract void inject(Object baseInvocation, Object proxyInvocation); + + protected void onBindMethod() { + + } + + protected Object getProxyInvocation() { + return mProxyInvocation; + } + + protected Object getBase() { + return mBase; + } + + @Override + public void injectHook() { + mBase = getWho(); + mProxyInvocation = Proxy.newProxyInstance(mBase.getClass().getClassLoader(), MethodParameterUtils.getAllInterface(mBase.getClass()), this); + inject(mBase, mProxyInvocation); + + onBindMethod(); + Class[] declaredClasses = this.getClass().getDeclaredClasses(); + for (Class declaredClass : declaredClasses) { + initAnnotation(declaredClass); + } + ScanClass scanClass = this.getClass().getAnnotation(ScanClass.class); + if (scanClass != null) { + for (Class aClass : scanClass.value()) { + for (Class declaredClass : aClass.getDeclaredClasses()) { + initAnnotation(declaredClass); + } + } + } + } + + protected void initAnnotation(Class clazz) { + ProxyMethod proxyMethod = clazz.getAnnotation(ProxyMethod.class); + if (proxyMethod != null) { + final String name = proxyMethod.name(); + if (!TextUtils.isEmpty(name)) { + try { + addMethodHook(name, (MethodHook) clazz.newInstance()); + } catch (Throwable t) { + t.printStackTrace(); + } + } + } + ProxyMethods proxyMethods = clazz.getAnnotation(ProxyMethods.class); + if (proxyMethods != null) { + String[] value = proxyMethods.value(); + for (String name : value) { + try { + addMethodHook(name, (MethodHook) clazz.newInstance()); + } catch (Throwable t) { + t.printStackTrace(); + } + } + } + } + + protected void addMethodHook(MethodHook methodHook) { + mMethodHookMap.put(methodHook.getMethodName(), methodHook); + } + + protected void addMethodHook(String name, MethodHook methodHook) { + mMethodHookMap.put(name, methodHook); + } + + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + MethodHook methodHook = mMethodHookMap.get(method.getName()); + if (methodHook == null) { + try { + return method.invoke(mBase, args); + } catch (Throwable e) { + throw e.getCause(); + } + } + + Object result = methodHook.beforeHook(mBase, method, args); + if (result != null) { + return result; + } + result = methodHook.hook(mBase, method, args); + result = methodHook.afterHook(result); + return result; + } +} diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/fake/hook/HookManager.java b/Bcore/src/main/java/top/niunaijun/blackbox/fake/hook/HookManager.java new file mode 100644 index 00000000..34a078c9 --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/fake/hook/HookManager.java @@ -0,0 +1,110 @@ +package top.niunaijun.blackbox.fake.hook; + +import android.util.Log; + +import java.util.HashMap; +import java.util.Map; + +import top.niunaijun.blackbox.fake.service.IActivityManagerProxy; +import top.niunaijun.blackbox.fake.service.IActivityTaskManagerProxy; +import top.niunaijun.blackbox.fake.service.HCallbackProxy; +import top.niunaijun.blackbox.fake.service.IAppOpsManagerProxy; +import top.niunaijun.blackbox.fake.service.IJobServiceProxy; +import top.niunaijun.blackbox.fake.service.ITelephonyRegistryProxy; +import top.niunaijun.blackbox.fake.service.IDeviceIdentifiersPolicyProxy; +import top.niunaijun.blackbox.fake.service.IStorageManagerProxy; +import top.niunaijun.blackbox.fake.service.ILauncherAppsProxy; +import top.niunaijun.blackbox.fake.service.IPackageManagerProxy; +import top.niunaijun.blackbox.BlackBoxCore; +import top.niunaijun.blackbox.fake.delegate.AppInstrumentation; +import top.niunaijun.blackbox.fake.service.libcore.OsStub; +import top.niunaijun.blackbox.fake.service.ITelephonyManagerProxy; +import top.niunaijun.blackbox.utils.compat.BuildCompat; + +/** + * Created by Milk on 3/30/21. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + */ +public class HookManager { + public static final String TAG = "HookManager"; + + private static final HookManager sHookManager = new HookManager(); + + private final Map, IInjectHook> mInjectors = new HashMap<>(); + + public static HookManager get() { + return sHookManager; + } + + public void init() { + if (BlackBoxCore.get().isVirtualProcess()) { + addInjector(new OsStub()); + addInjector(new IActivityManagerProxy()); + addInjector(new IPackageManagerProxy()); + addInjector(new ITelephonyManagerProxy()); + addInjector(new HCallbackProxy()); + addInjector(new IAppOpsManagerProxy()); + addInjector(new IStorageManagerProxy()); + addInjector(new ILauncherAppsProxy()); + addInjector(new IJobServiceProxy()); + addInjector(new ITelephonyRegistryProxy()); + + addInjector(AppInstrumentation.get()); + + // 11.0 + if (BuildCompat.isR()) { + } + // 10.0 + if (BuildCompat.isQ()) { + addInjector(new IActivityTaskManagerProxy()); + } + // 9.0 + if (BuildCompat.isPie()) { + } + // 8.0 + if (BuildCompat.isOreo()) { + addInjector(new IDeviceIdentifiersPolicyProxy()); + } + // 7.1 + if (BuildCompat.isN_MR1()) { + } + // 7.0 + if (BuildCompat.isN()) { + } + // 6.0 + if (BuildCompat.isM()) { + } + // 5.0 + if (BuildCompat.isL()) { + addInjector(new IJobServiceProxy()); + } + } + injectAll(); + } + + public void checkEnv(Class clazz) { + IInjectHook iInjectHook = mInjectors.get(clazz); + if (iInjectHook != null && iInjectHook.isBadEnv()) { + Log.d(TAG, "checkEnv: " + clazz.getSimpleName() + " is bad env"); + iInjectHook.injectHook(); + } + } + + void addInjector(IInjectHook injectHook) { + mInjectors.put(injectHook.getClass(), injectHook); + } + + void injectAll() { + for (IInjectHook value : mInjectors.values()) { + try { + value.injectHook(); + } catch (Exception e) { + e.printStackTrace(); + } + } + } +} diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/fake/hook/IInjectHook.java b/Bcore/src/main/java/top/niunaijun/blackbox/fake/hook/IInjectHook.java new file mode 100644 index 00000000..dd2fe201 --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/fake/hook/IInjectHook.java @@ -0,0 +1,15 @@ +package top.niunaijun.blackbox.fake.hook; + +/** + * Created by Milk on 3/30/21. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + */ +public interface IInjectHook { + void injectHook(); + + boolean isBadEnv(); +} diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/fake/hook/MethodHook.java b/Bcore/src/main/java/top/niunaijun/blackbox/fake/hook/MethodHook.java new file mode 100644 index 00000000..db549de2 --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/fake/hook/MethodHook.java @@ -0,0 +1,27 @@ +package top.niunaijun.blackbox.fake.hook; + +import java.lang.reflect.Method; + +/** + * Created by Milk on 3/30/21. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + */ +public abstract class MethodHook { + protected String getMethodName() { + return null; + } + + protected Object afterHook(Object result) throws Throwable { + return result; + } + + protected Object beforeHook(Object who, Method method, Object[] args) throws Throwable { + return null; + } + + protected abstract Object hook(Object who, Method method, Object[] args) throws Throwable; +} diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/fake/hook/ProxyMethod.java b/Bcore/src/main/java/top/niunaijun/blackbox/fake/hook/ProxyMethod.java new file mode 100644 index 00000000..48d5dae2 --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/fake/hook/ProxyMethod.java @@ -0,0 +1,20 @@ +package top.niunaijun.blackbox.fake.hook; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Created by Milk on 2021/5/9. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface ProxyMethod { + String name(); +} diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/fake/hook/ProxyMethods.java b/Bcore/src/main/java/top/niunaijun/blackbox/fake/hook/ProxyMethods.java new file mode 100644 index 00000000..3f95ec80 --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/fake/hook/ProxyMethods.java @@ -0,0 +1,20 @@ +package top.niunaijun.blackbox.fake.hook; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Created by Milk on 2021/5/9. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface ProxyMethods { + String[] value() default {}; +} diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/fake/hook/ScanClass.java b/Bcore/src/main/java/top/niunaijun/blackbox/fake/hook/ScanClass.java new file mode 100644 index 00000000..36c316d0 --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/fake/hook/ScanClass.java @@ -0,0 +1,20 @@ +package top.niunaijun.blackbox.fake.hook; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Created by Milk on 2021/5/9. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface ScanClass { + Class[] value() default {}; +} \ No newline at end of file diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/fake/service/ActivityManagerCommonProxy.java b/Bcore/src/main/java/top/niunaijun/blackbox/fake/service/ActivityManagerCommonProxy.java new file mode 100644 index 00000000..cde651f6 --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/fake/service/ActivityManagerCommonProxy.java @@ -0,0 +1,58 @@ +package top.niunaijun.blackbox.fake.service; + +import java.lang.reflect.Method; + +import top.niunaijun.blackbox.fake.hook.MethodHook; +import top.niunaijun.blackbox.fake.hook.ProxyMethod; + +/** + * Created by Milk on 4/21/21. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + */ +public class ActivityManagerCommonProxy { + public static final String TAG = "CommonStub"; + + @ProxyMethod(name = "startActivity") + public static class StartActivity extends MethodHook { + @Override + protected Object hook(Object who, Method method, Object[] args) throws Throwable { + return 0; + } + } + + @ProxyMethod(name = "startActivities") + public static class StartActivities extends MethodHook { + @Override + protected Object hook(Object who, Method method, Object[] args) throws Throwable { + return 0; + } + } + + @ProxyMethod(name = "activityResumed") + public static class ActivityResumed extends MethodHook { + @Override + protected Object hook(Object who, Method method, Object[] args) throws Throwable { + return method.invoke(who, args); + } + } + + @ProxyMethod(name = "activityDestroyed") + public static class ActivityDestroyed extends MethodHook { + @Override + protected Object hook(Object who, Method method, Object[] args) throws Throwable { + return method.invoke(who, args); + } + } + + @ProxyMethod(name = "finishActivity") + public static class FinishActivity extends MethodHook { + @Override + protected Object hook(Object who, Method method, Object[] args) throws Throwable { + return method.invoke(who, args); + } + } +} diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/fake/service/HCallbackProxy.java b/Bcore/src/main/java/top/niunaijun/blackbox/fake/service/HCallbackProxy.java new file mode 100644 index 00000000..bdb1fa3b --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/fake/service/HCallbackProxy.java @@ -0,0 +1,137 @@ +package top.niunaijun.blackbox.fake.service; + +import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.os.Handler; +import android.os.IBinder; +import android.os.Message; + +import androidx.annotation.NonNull; + +import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; + +import mirror.android.app.ActivityManagerNative; +import mirror.android.app.ActivityThread; +import mirror.android.app.IActivityManager; +import mirror.android.app.servertransaction.ClientTransaction; +import mirror.android.app.servertransaction.LaunchActivityItem; +import top.niunaijun.blackbox.BlackBoxCore; +import top.niunaijun.blackbox.app.BActivityThread; +import top.niunaijun.blackbox.fake.hook.IInjectHook; +import top.niunaijun.blackbox.proxy.record.ProxyActivityRecord; +import top.niunaijun.blackbox.utils.compat.BuildCompat; + +/** + * Created by Milk on 3/31/21. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + */ +public class HCallbackProxy implements IInjectHook, Handler.Callback { + public static final String TAG = "HCallbackStub"; + private Handler.Callback mOtherCallback; + private AtomicBoolean mBeing = new AtomicBoolean(false); + + private Handler.Callback getHCallback() { + return mirror.android.os.Handler.mCallback.get(getH()); + } + + private Handler getH() { + Object currentActivityThread = BlackBoxCore.mainThread(); + return ActivityThread.mH.get(currentActivityThread); + } + + @Override + public void injectHook() { + mOtherCallback = getHCallback(); + if (mOtherCallback != null && (mOtherCallback == this || mOtherCallback.getClass().getName().equals(this.getClass().getName()))) { + mOtherCallback = null; + } + mirror.android.os.Handler.mCallback.set(getH(), this); + } + + @Override + public boolean isBadEnv() { + Handler.Callback hCallback = getHCallback(); + return hCallback != null && hCallback != this; + } + + @Override + public boolean handleMessage(@NonNull Message msg) { + if (!mBeing.getAndSet(true)) { + try { + if (BuildCompat.isPie()) { + if (msg.what == ActivityThread.H.EXECUTE_TRANSACTION.get()) { + if (handleLaunchActivity(msg.obj)) { + getH().sendMessageAtFrontOfQueue(Message.obtain(msg)); + return true; + } + } + } else { + if (msg.what == ActivityThread.H.LAUNCH_ACTIVITY.get()) { + if (handleLaunchActivity(msg.obj)) { + getH().sendMessageAtFrontOfQueue(Message.obtain(msg)); + return true; + } + } + } + if (mOtherCallback != null) { + return mOtherCallback.handleMessage(msg); + } + return false; + } finally { + mBeing.set(false); + } + } + return false; + } + + private Object getLaunchActivityItem(Object clientTransaction) { + List mActivityCallbacks = ClientTransaction.mActivityCallbacks.get(clientTransaction); + + for (Object obj : mActivityCallbacks) { + if (LaunchActivityItem.REF.getClazz().getName().equals(obj.getClass().getCanonicalName())) { + return obj; + } + } + return null; + } + + private boolean handleLaunchActivity(Object client) { + Object r; + if (BuildCompat.isPie()) { + // ClientTransaction + r = getLaunchActivityItem(client); + } else { + // ActivityClientRecord + r = client; + } + if (r == null) + return false; + + Intent intent; + if (BuildCompat.isPie()) { + intent = LaunchActivityItem.mIntent.get(r); + } else { + intent = ActivityThread.ActivityClientRecord.intent.get(r); + } + + if (intent == null) + return false; + + ProxyActivityRecord stubRecord = ProxyActivityRecord.create(intent); + ActivityInfo activityInfo = stubRecord.mActivityInfo; + if (activityInfo != null) { + // bind + if (!BActivityThread.currentActivityThread().isInit()) { + BActivityThread.currentActivityThread().bindApplication(activityInfo.packageName, + activityInfo.processName); + return false; + } + } + return false; + } +} diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/fake/service/IActivityManagerProxy.java b/Bcore/src/main/java/top/niunaijun/blackbox/fake/service/IActivityManagerProxy.java new file mode 100644 index 00000000..5ccf1446 --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/fake/service/IActivityManagerProxy.java @@ -0,0 +1,148 @@ +package top.niunaijun.blackbox.fake.service; + +import java.lang.reflect.Method; +import mirror.android.app.ActivityManagerNative; +import mirror.android.app.ActivityManagerOreo; + +import mirror.android.util.Singleton; +import top.niunaijun.blackbox.fake.hook.ProxyMethod; +import top.niunaijun.blackbox.fake.hook.ScanClass; +import top.niunaijun.blackbox.fake.hook.ClassInvocationStub; +import top.niunaijun.blackbox.fake.hook.MethodHook; +import top.niunaijun.blackbox.utils.compat.BuildCompat; + + +/** + * Created by Milk on 3/30/21. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + */ +@ScanClass(ActivityManagerCommonProxy.class) +public class IActivityManagerProxy extends ClassInvocationStub { + public static final String TAG = "ActivityManagerStub"; + + @Override + protected Object getWho() { + Object iActivityManager = null; + if (BuildCompat.isOreo()) { + iActivityManager = ActivityManagerOreo.IActivityManagerSingleton.get(); + } else if (BuildCompat.isL()) { + iActivityManager = ActivityManagerNative.gDefault.get(); + } + return Singleton.get.call(iActivityManager); + } + + @Override + protected void inject(Object base, Object proxy) { + Object iActivityManager = null; + if (BuildCompat.isOreo()) { + iActivityManager = ActivityManagerOreo.IActivityManagerSingleton.get(); + } else if (BuildCompat.isL()) { + iActivityManager = ActivityManagerNative.gDefault.get(); + } + Singleton.mInstance.set(iActivityManager, proxy); + } + + @Override + public boolean isBadEnv() { + return false; + } + + @ProxyMethod(name = "startService") + public static class StartService extends MethodHook { + @Override + protected Object hook(Object who, Method method, Object[] args) throws Throwable { + return 0; + } + } + + @ProxyMethod(name = "stopService") + public static class StopService extends MethodHook { + @Override + protected Object hook(Object who, Method method, Object[] args) throws Throwable { + return 0; + } + } + + @ProxyMethod(name = "bindService") + public static class BindService extends MethodHook { + + @Override + protected Object hook(Object who, Method method, Object[] args) throws Throwable { + return 0; + } + } + + // 10.0 + @ProxyMethod(name = "bindIsolatedService") + public static class BindIsolatedService extends BindService { + @Override + protected Object beforeHook(Object who, Method method, Object[] args) throws Throwable { + // instanceName + args[6] = null; + return super.beforeHook(who, method, args); + } + } + + @ProxyMethod(name = "unbindService") + public static class UnbindService extends MethodHook { + + @Override + protected Object hook(Object who, Method method, Object[] args) throws Throwable { + return 0; + } + } + + @ProxyMethod(name = "getIntentSender") + public static class GetIntentSender extends MethodHook { + @Override + protected Object hook(Object who, Method method, Object[] args) throws Throwable { + return 0; + } + } + + @ProxyMethod(name = "getIntentSenderWithFeature") + public static class GetIntentSenderWithFeature extends GetIntentSender { + } + + @ProxyMethod(name = "broadcastIntentWithFeature") + public static class BroadcastIntentWithFeature extends BroadcastIntent { + } + + @ProxyMethod(name = "broadcastIntent") + public static class BroadcastIntent extends MethodHook { + @Override + protected Object hook(Object who, Method method, Object[] args) throws Throwable { + return 0; + } + } + + @ProxyMethod(name = "sendIntentSender") + public static class SendIntentSender extends MethodHook { + + @Override + protected Object hook(Object who, Method method, Object[] args) throws Throwable { + return 0; + } + } + + @ProxyMethod(name = "registerReceiver") + public static class RegisterReceiver extends MethodHook { + + @Override + protected Object hook(Object who, Method method, Object[] args) throws Throwable { + return null; + } + } + + @ProxyMethod(name = "grantUriPermission") + public static class GrantUriPermission extends MethodHook { + @Override + protected Object hook(Object who, Method method, Object[] args) throws Throwable { + return 0; + } + } +} diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/fake/service/IActivityTaskManagerProxy.java b/Bcore/src/main/java/top/niunaijun/blackbox/fake/service/IActivityTaskManagerProxy.java new file mode 100644 index 00000000..4d898c5c --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/fake/service/IActivityTaskManagerProxy.java @@ -0,0 +1,38 @@ +package top.niunaijun.blackbox.fake.service; + +import mirror.android.app.IActivityTaskManager; +import mirror.android.os.ServiceManager; +import top.niunaijun.blackbox.fake.hook.BinderInvocationStub; +import top.niunaijun.blackbox.fake.hook.ScanClass; + +/** + * Created by Milk on 3/31/21. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + */ +@ScanClass(ActivityManagerCommonProxy.class) +public class IActivityTaskManagerProxy extends BinderInvocationStub { + public static final String TAG = "ActivityTaskManager"; + + public IActivityTaskManagerProxy() { + super(ServiceManager.getService.call("activity_task")); + } + + @Override + protected Object getWho() { + return IActivityTaskManager.Stub.asInterface.call(ServiceManager.getService.call("activity_task")); + } + + @Override + protected void inject(Object baseInvocation, Object proxyInvocation) { + replaceSystemService("activity_task"); + } + + @Override + public boolean isBadEnv() { + return false; + } +} diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/fake/service/IAppOpsManagerProxy.java b/Bcore/src/main/java/top/niunaijun/blackbox/fake/service/IAppOpsManagerProxy.java new file mode 100644 index 00000000..36470c3b --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/fake/service/IAppOpsManagerProxy.java @@ -0,0 +1,86 @@ +package top.niunaijun.blackbox.fake.service; + +import android.app.AppOpsManager; +import android.content.Context; +import android.os.IBinder; +import android.os.IInterface; + +import java.lang.reflect.Method; + +import mirror.android.os.ServiceManager; +import mirror.com.android.internal.app.IAppOpsService; +import top.niunaijun.blackbox.BlackBoxCore; +import top.niunaijun.blackbox.fake.hook.BinderInvocationStub; +import top.niunaijun.blackbox.fake.hook.MethodHook; +import top.niunaijun.blackbox.fake.hook.ProxyMethod; +import top.niunaijun.blackbox.utils.MethodParameterUtils; + +/** + * Created by Milk on 4/2/21. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + */ +public class IAppOpsManagerProxy extends BinderInvocationStub { + public IAppOpsManagerProxy() { + super(ServiceManager.getService.call(Context.APP_OPS_SERVICE)); + } + + @Override + protected Object getWho() { + IBinder call = ServiceManager.getService.call(Context.APP_OPS_SERVICE); + return IAppOpsService.Stub.asInterface.call(call); + } + + @Override + protected void inject(Object baseInvocation, Object proxyInvocation) { + if (mirror.android.app.AppOpsManager.mService != null) { + AppOpsManager appOpsManager = (AppOpsManager) BlackBoxCore.getContext().getSystemService(Context.APP_OPS_SERVICE); + try { + mirror.android.app.AppOpsManager.mService.set(appOpsManager, (IInterface) getProxyInvocation()); + } catch (Exception e) { + e.printStackTrace(); + } + } + replaceSystemService(Context.APP_OPS_SERVICE); + } + + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + MethodParameterUtils.replaceFirstAppPkg(args); + MethodParameterUtils.replaceLastUserId(args); + return super.invoke(proxy, method, args); + } + + @Override + public boolean isBadEnv() { + return false; + } + + @ProxyMethod(name = "checkPackage") + public static class CheckPackage extends MethodHook { + @Override + protected Object hook(Object who, Method method, Object[] args) throws Throwable { + // todo + return AppOpsManager.MODE_ALLOWED; + } + } + + @ProxyMethod(name = "checkPackage") + public static class CheckOperation extends MethodHook { + @Override + protected Object hook(Object who, Method method, Object[] args) throws Throwable { + return method.invoke(who, args); + } + } + + @ProxyMethod(name = "noteOperation") + public static class NoteOperation extends MethodHook { + @Override + protected Object hook(Object who, Method method, Object[] args) throws Throwable { + return method.invoke(who, args); + } + } +} diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/fake/service/IDeviceIdentifiersPolicyProxy.java b/Bcore/src/main/java/top/niunaijun/blackbox/fake/service/IDeviceIdentifiersPolicyProxy.java new file mode 100644 index 00000000..6616cde2 --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/fake/service/IDeviceIdentifiersPolicyProxy.java @@ -0,0 +1,52 @@ +package top.niunaijun.blackbox.fake.service; + + +import java.lang.reflect.Method; + +import mirror.android.os.IDeviceIdentifiersPolicyService; +import mirror.android.os.ServiceManager; +import top.niunaijun.blackbox.BlackBoxCore; +import top.niunaijun.blackbox.fake.hook.BinderInvocationStub; +import top.niunaijun.blackbox.fake.hook.MethodHook; +import top.niunaijun.blackbox.fake.hook.ProxyMethod; +import top.niunaijun.blackbox.utils.Md5Utils; + +/** + * Created by Milk on 4/3/21. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + */ +public class IDeviceIdentifiersPolicyProxy extends BinderInvocationStub { + + public IDeviceIdentifiersPolicyProxy() { + super(ServiceManager.getService.call("device_identifiers")); + } + + @Override + protected Object getWho() { + return IDeviceIdentifiersPolicyService.Stub.asInterface.call(ServiceManager.getService.call("device_identifiers")); + } + + @Override + protected void inject(Object baseInvocation, Object proxyInvocation) { + replaceSystemService("device_identifiers"); + } + + @Override + public boolean isBadEnv() { + return false; + } + + @ProxyMethod(name = "getSerialForPackage") + public static class x extends MethodHook { + @Override + protected Object hook(Object who, Method method, Object[] args) throws Throwable { +// args[0] = BlackBoxCore.getHostPkg(); +// return method.invoke(who, args); + return Md5Utils.md5(BlackBoxCore.getHostPkg()); + } + } +} diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/fake/service/IJobServiceProxy.java b/Bcore/src/main/java/top/niunaijun/blackbox/fake/service/IJobServiceProxy.java new file mode 100644 index 00000000..e91412b7 --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/fake/service/IJobServiceProxy.java @@ -0,0 +1,68 @@ +package top.niunaijun.blackbox.fake.service; + +import android.content.Context; +import android.os.IBinder; + +import java.lang.reflect.Method; + +import mirror.android.app.job.IJobScheduler; +import mirror.android.os.ServiceManager; +import top.niunaijun.blackbox.fake.hook.BinderInvocationStub; +import top.niunaijun.blackbox.fake.hook.MethodHook; +import top.niunaijun.blackbox.fake.hook.ProxyMethod; + +/** + * Created by Milk on 4/2/21. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + */ +public class IJobServiceProxy extends BinderInvocationStub { + public static final String TAG = "JobServiceStub"; + + public IJobServiceProxy() { + super(ServiceManager.getService.call(Context.JOB_SCHEDULER_SERVICE)); + } + + @Override + protected Object getWho() { + IBinder jobScheduler = ServiceManager.getService.call("jobscheduler"); + return IJobScheduler.Stub.asInterface.call(jobScheduler); + } + + @Override + protected void inject(Object baseInvocation, Object proxyInvocation) { + replaceSystemService(Context.JOB_SCHEDULER_SERVICE); + } + + @ProxyMethod(name = "schedule") + public static class Schedule extends MethodHook { + @Override + protected Object hook(Object who, Method method, Object[] args) throws Throwable { + return 0; + } + } + + @ProxyMethod(name = "cancel") + public static class Cancel extends MethodHook { + @Override + protected Object hook(Object who, Method method, Object[] args) throws Throwable { + return 0; + } + } + + @ProxyMethod(name = "cancelAll") + public static class CancelAll extends MethodHook { + @Override + protected Object hook(Object who, Method method, Object[] args) throws Throwable { + return 0; + } + } + + @Override + public boolean isBadEnv() { + return false; + } +} diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/fake/service/ILauncherAppsProxy.java b/Bcore/src/main/java/top/niunaijun/blackbox/fake/service/ILauncherAppsProxy.java new file mode 100644 index 00000000..2c095cf3 --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/fake/service/ILauncherAppsProxy.java @@ -0,0 +1,53 @@ +package top.niunaijun.blackbox.fake.service; + +import android.content.Context; + +import java.lang.reflect.Method; + +import mirror.android.content.pm.ILauncherApps; +import mirror.android.os.ServiceManager; +import top.niunaijun.blackbox.fake.hook.BinderInvocationStub; +import top.niunaijun.blackbox.utils.MethodParameterUtils; + +/** + * Created by Milk on 4/13/21. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + */ +public class ILauncherAppsProxy extends BinderInvocationStub { + + public ILauncherAppsProxy() { + super(ServiceManager.getService.call(Context.LAUNCHER_APPS_SERVICE)); + } + + @Override + protected Object getWho() { + return ILauncherApps.Stub.asInterface.call(ServiceManager.getService.call(Context.LAUNCHER_APPS_SERVICE)); + } + + @Override + protected void inject(Object baseInvocation, Object proxyInvocation) { + replaceSystemService(Context.LAUNCHER_APPS_SERVICE); + } + + @Override + public boolean isBadEnv() { + return false; + } + + @Override + protected void onBindMethod() { + super.onBindMethod(); + } + + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + MethodParameterUtils.replaceFirstAppPkg(args); + // todo shouldHideFromSuggestions + return super.invoke(proxy, method, args); + } + +} diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/fake/service/IPackageManagerProxy.java b/Bcore/src/main/java/top/niunaijun/blackbox/fake/service/IPackageManagerProxy.java new file mode 100644 index 00000000..1f9e5d8e --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/fake/service/IPackageManagerProxy.java @@ -0,0 +1,265 @@ +package top.niunaijun.blackbox.fake.service; + +import android.content.ComponentName; +import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.ProviderInfo; +import android.content.pm.ResolveInfo; +import android.content.pm.ServiceInfo; +import android.os.IInterface; +import android.os.Process; + +import java.lang.reflect.Method; +import java.util.List; + +import mirror.android.app.ActivityThread; +import mirror.android.app.ContextImpl; +import top.niunaijun.blackbox.BlackBoxCore; +import top.niunaijun.blackbox.app.BActivityThread; +import top.niunaijun.blackbox.fake.hook.BinderInvocationStub; +import top.niunaijun.blackbox.fake.hook.MethodHook; +import top.niunaijun.blackbox.core.env.AppSystemEnv; +import top.niunaijun.blackbox.fake.hook.ProxyMethod; +import top.niunaijun.blackbox.utils.MethodParameterUtils; +import top.niunaijun.blackbox.utils.Reflector; +import top.niunaijun.blackbox.utils.compat.ParceledListSliceCompat; + +/** + * Created by Milk on 3/30/21. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + */ +public class IPackageManagerProxy extends BinderInvocationStub { + public static final String TAG = "PackageManagerStub"; + + public IPackageManagerProxy() { + super(ActivityThread.sPackageManager.get().asBinder()); + } + + @Override + protected Object getWho() { + return ActivityThread.sPackageManager.get(); + } + + @Override + protected void inject(Object baseInvocation, Object proxyInvocation) { + ActivityThread.sPackageManager.set((IInterface) proxyInvocation); + replaceSystemService("package"); + Object systemContext = ActivityThread.getSystemContext.call(BlackBoxCore.mainThread()); + PackageManager packageManager = ContextImpl.mPackageManager.get(systemContext); + if (packageManager != null) { + try { + Reflector.on("android.app.ApplicationPackageManager") + .field("mPM") + .set(packageManager, proxyInvocation); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + @Override + public boolean isBadEnv() { + return false; + } + + @ProxyMethod(name = "resolveIntent") + public static class ResolveIntent extends MethodHook { + @Override + protected Object hook(Object who, Method method, Object[] args) throws Throwable { + Intent intent = (Intent) args[0]; + String resolvedType = (String) args[1]; + int flags = (int) args[2]; + ResolveInfo resolveInfo = BlackBoxCore.getBPackageManager().resolveIntent(intent, resolvedType, flags, BActivityThread.getUserId()); + if (resolveInfo != null) { + return resolveInfo; + } + return method.invoke(who, args); + } + } + + @ProxyMethod(name = "setComponentEnabledSetting") + public static class SetComponentEnabledSetting extends MethodHook { + @Override + protected Object hook(Object who, Method method, Object[] args) throws Throwable { + // todo + return 0; + } + } + + @ProxyMethod(name = "getPackageInfo") + public static class GetPackageInfo extends MethodHook { + @Override + protected Object hook(Object who, Method method, Object[] args) throws Throwable { + String packageName = (String) args[0]; + int flag = (int) args[1]; +// if (ClientSystemEnv.isFakePackage(packageName)) { +// packageName = BlackBoxCore.getHostPkg(); +// } + PackageInfo packageInfo = BlackBoxCore.getBPackageManager().getPackageInfo(packageName, flag, BActivityThread.getUserId()); + if (packageInfo != null) { + return packageInfo; + } + if (AppSystemEnv.isOpenPackage(packageName)) { + return method.invoke(who, args); + } + return null; + } + } + + @ProxyMethod(name = "getPackageUid") + public static class GetPackageUid extends MethodHook { + @Override + protected Object hook(Object who, Method method, Object[] args) throws Throwable { + MethodParameterUtils.replaceFirstAppPkg(args); + return method.invoke(who, args); + } + } + + @ProxyMethod(name = "getProviderInfo") + public static class GetProviderInfo extends MethodHook { + @Override + protected Object hook(Object who, Method method, Object[] args) throws Throwable { + ComponentName componentName = (ComponentName) args[0]; + int flags = (int) args[1]; + ProviderInfo providerInfo = BlackBoxCore.getBPackageManager().getProviderInfo(componentName, flags, BActivityThread.getUserId()); + if (providerInfo != null) + return providerInfo; + if (AppSystemEnv.isOpenPackage(componentName)) { + return method.invoke(who, args); + } + return null; + } + } + + @ProxyMethod(name = "getReceiverInfo") + public static class GetReceiverInfo extends MethodHook { + @Override + protected Object hook(Object who, Method method, Object[] args) throws Throwable { + ComponentName componentName = (ComponentName) args[0]; + int flags = (int) args[1]; + ActivityInfo receiverInfo = BlackBoxCore.getBPackageManager().getReceiverInfo(componentName, flags, BActivityThread.getUserId()); + if (receiverInfo != null) + return receiverInfo; + if (AppSystemEnv.isOpenPackage(componentName)) { + return method.invoke(who, args); + } + return null; + } + } + + @ProxyMethod(name = "getActivityInfo") + public static class GetActivityInfo extends MethodHook { + @Override + protected Object hook(Object who, Method method, Object[] args) throws Throwable { + ComponentName componentName = (ComponentName) args[0]; + int flags = (int) args[1]; + ActivityInfo activityInfo = BlackBoxCore.getBPackageManager().getActivityInfo(componentName, flags, BActivityThread.getUserId()); + if (activityInfo != null) + return activityInfo; + if (AppSystemEnv.isOpenPackage(componentName)) { + return method.invoke(who, args); + } + return null; + } + } + + @ProxyMethod(name = "getServiceInfo") + public static class GetServiceInfo extends MethodHook { + + @Override + protected Object hook(Object who, Method method, Object[] args) throws Throwable { + ComponentName componentName = (ComponentName) args[0]; + int flags = (int) args[1]; + ServiceInfo serviceInfo = BlackBoxCore.getBPackageManager().getServiceInfo(componentName, flags, BActivityThread.getUserId()); + if (serviceInfo != null) + return serviceInfo; + if (AppSystemEnv.isOpenPackage(componentName)) { + return method.invoke(who, args); + } + return null; + } + } + + @ProxyMethod(name = "getInstalledApplications") + public static class GetInstalledApplications extends MethodHook { + + @Override + protected Object hook(Object who, Method method, Object[] args) throws Throwable { + int flags = (int) args[0]; + List installedApplications = BlackBoxCore.getBPackageManager().getInstalledApplications(flags, BActivityThread.getUserId()); + return ParceledListSliceCompat.create(installedApplications); + } + } + + @ProxyMethod(name = "getInstalledPackages") + public static class GetInstalledPackages extends MethodHook { + + @Override + protected Object hook(Object who, Method method, Object[] args) throws Throwable { + int flags = (int) args[0]; + List installedPackages = BlackBoxCore.getBPackageManager().getInstalledPackages(flags, BActivityThread.getUserId()); + return ParceledListSliceCompat.create(installedPackages); + } + } + + @ProxyMethod(name = "getApplicationInfo") + public static class GetApplicationInfo extends MethodHook { + @Override + protected Object hook(Object who, Method method, Object[] args) throws Throwable { + String packageName = (String) args[0]; + int flags = (int) args[1]; +// if (ClientSystemEnv.isFakePackage(packageName)) { +// packageName = BlackBoxCore.getHostPkg(); +// } + ApplicationInfo applicationInfo = BlackBoxCore.getBPackageManager().getApplicationInfo(packageName, flags, BActivityThread.getUserId()); + if (applicationInfo != null) { + return applicationInfo; + } + if (AppSystemEnv.isOpenPackage(packageName)) { + return method.invoke(who, args); + } + return null; + } + } + + @ProxyMethod(name = "queryContentProviders") + public static class QueryContentProviders extends MethodHook { + @Override + protected Object hook(Object who, Method method, Object[] args) throws Throwable { + int flags = (int) args[2]; + List providers = BlackBoxCore.getBPackageManager(). + queryContentProviders(BActivityThread.getAppProcessName(), Process.myUid(), flags, BActivityThread.getUserId()); + return ParceledListSliceCompat.create(providers); + } + } + + @ProxyMethod(name = "resolveContentProvider") + public static class ResolveContentProvider extends MethodHook { + @Override + protected Object hook(Object who, Method method, Object[] args) throws Throwable { + String authority = (String) args[0]; + int flags = (int) args[1]; + ProviderInfo providerInfo = BlackBoxCore.getBPackageManager().resolveContentProvider(authority, flags, BActivityThread.getUserId()); + if (providerInfo == null) { + return method.invoke(who, args); + } + return providerInfo; + } + } + + @ProxyMethod(name = "canRequestPackageInstalls") + public static class CanRequestPackageInstalls extends MethodHook { + @Override + protected Object hook(Object who, Method method, Object[] args) throws Throwable { + MethodParameterUtils.replaceFirstAppPkg(args); + return method.invoke(who, args); + } + } +} diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/fake/service/IStorageManagerProxy.java b/Bcore/src/main/java/top/niunaijun/blackbox/fake/service/IStorageManagerProxy.java new file mode 100644 index 00000000..c5bcd494 --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/fake/service/IStorageManagerProxy.java @@ -0,0 +1,66 @@ +package top.niunaijun.blackbox.fake.service; + +import android.os.IInterface; +import android.os.Process; + +import java.lang.reflect.Method; + +import mirror.android.os.ServiceManager; +import mirror.android.os.mount.IMountService; +import mirror.android.os.storage.IStorageManager; +import top.niunaijun.blackbox.BlackBoxCore; +import top.niunaijun.blackbox.app.BActivityThread; +import top.niunaijun.blackbox.fake.hook.BinderInvocationStub; +import top.niunaijun.blackbox.fake.hook.MethodHook; +import top.niunaijun.blackbox.fake.hook.ProxyMethod; +import top.niunaijun.blackbox.utils.compat.BuildCompat; + +/** + * Created by Milk on 4/10/21. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + */ +public class IStorageManagerProxy extends BinderInvocationStub { + + public IStorageManagerProxy() { + super(ServiceManager.getService.call("mount")); + } + + @Override + protected Object getWho() { + IInterface mount; + if (BuildCompat.isOreo()) { + mount = IStorageManager.Stub.asInterface.call(ServiceManager.getService.call("mount")); + } else { + mount = IMountService.Stub.asInterface.call(ServiceManager.getService.call("mount")); + } + return mount; + } + + @Override + protected void inject(Object baseInvocation, Object proxyInvocation) { + replaceSystemService("mount"); + } + + @Override + public boolean isBadEnv() { + return false; + } + + @ProxyMethod(name = "getVolumeList") + public static class GetVolumeList extends MethodHook { + @Override + protected Object hook(Object who, Method method, Object[] args) throws Throwable { + if (args == null) { + return BlackBoxCore.getBStorageManager().getVolumeList(Process.myUid(), null, 0, BActivityThread.getUserId()); + } + int uid = (int) args[0]; + String packageName = (String) args[1]; + int flags = (int) args[2]; + return BlackBoxCore.getBStorageManager().getVolumeList(uid, packageName, flags, BActivityThread.getUserId()); + } + } +} diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/fake/service/ITelephonyManagerProxy.java b/Bcore/src/main/java/top/niunaijun/blackbox/fake/service/ITelephonyManagerProxy.java new file mode 100644 index 00000000..78723733 --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/fake/service/ITelephonyManagerProxy.java @@ -0,0 +1,81 @@ +package top.niunaijun.blackbox.fake.service; + +import android.content.Context; +import android.os.IBinder; + +import java.lang.reflect.Method; + +import mirror.android.os.ServiceManager; +import mirror.com.android.internal.telephony.ITelephony; +import top.niunaijun.blackbox.BlackBoxCore; +import top.niunaijun.blackbox.fake.hook.BinderInvocationStub; +import top.niunaijun.blackbox.fake.hook.MethodHook; +import top.niunaijun.blackbox.fake.hook.ProxyMethod; +import top.niunaijun.blackbox.utils.Md5Utils; + +/** + * Created by Milk on 4/2/21. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + */ +public class ITelephonyManagerProxy extends BinderInvocationStub { + + public ITelephonyManagerProxy() { + super(ServiceManager.getService.call(Context.TELEPHONY_SERVICE)); + } + + @Override + protected Object getWho() { + IBinder telephony = ServiceManager.getService.call(Context.TELEPHONY_SERVICE); + return ITelephony.Stub.asInterface.call(telephony); + } + + @Override + protected void inject(Object baseInvocation, Object proxyInvocation) { + replaceSystemService(Context.TELEPHONY_SERVICE); + } + + @Override + public boolean isBadEnv() { + return false; + } + + @ProxyMethod(name = "getDeviceId") + public static class GetDeviceId extends MethodHook { + @Override + protected Object hook(Object who, Method method, Object[] args) throws Throwable { +// MethodParameterUtils.replaceFirstAppPkg(args); +// return method.invoke(who, args); + return Md5Utils.md5(BlackBoxCore.getHostPkg()); + } + } + + @ProxyMethod(name = "getImeiForSlot") + public static class getImeiForSlot extends MethodHook { + @Override + protected Object hook(Object who, Method method, Object[] args) throws Throwable { +// MethodParameterUtils.replaceFirstAppPkg(args); +// return method.invoke(who, args); + return Md5Utils.md5(BlackBoxCore.getHostPkg()); + } + } + + @ProxyMethod(name = "isUserDataEnabled") + public static class IsUserDataEnabled extends MethodHook { + @Override + protected Object hook(Object who, Method method, Object[] args) throws Throwable { + return true; + } + } + + @ProxyMethod(name = "getSubscriberId") + public static class GetSubscriberId extends MethodHook { + @Override + protected Object hook(Object who, Method method, Object[] args) throws Throwable { + return Md5Utils.md5(BlackBoxCore.getHostPkg()); + } + } +} diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/fake/service/ITelephonyRegistryProxy.java b/Bcore/src/main/java/top/niunaijun/blackbox/fake/service/ITelephonyRegistryProxy.java new file mode 100644 index 00000000..63e42a31 --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/fake/service/ITelephonyRegistryProxy.java @@ -0,0 +1,59 @@ +package top.niunaijun.blackbox.fake.service; + +import java.lang.reflect.Method; + +import mirror.android.os.ServiceManager; +import mirror.com.android.internal.telephony.ITelephonyRegistry; +import top.niunaijun.blackbox.fake.hook.BinderInvocationStub; +import top.niunaijun.blackbox.fake.hook.MethodHook; +import top.niunaijun.blackbox.fake.hook.ProxyMethod; +import top.niunaijun.blackbox.utils.MethodParameterUtils; + +/** + * Created by Milk on 2021/5/17. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + */ +public class ITelephonyRegistryProxy extends BinderInvocationStub { + public ITelephonyRegistryProxy() { + super(ServiceManager.getService.call("telephony.registry")); + } + + @Override + protected Object getWho() { + return ITelephonyRegistry.Stub.asInterface.call(ServiceManager.getService.call("telephony.registry")); + } + + @Override + protected void inject(Object baseInvocation, Object proxyInvocation) { + replaceSystemService("telephony.registry"); + } + + @Override + public boolean isBadEnv() { + return false; + } + + @ProxyMethod(name = "listenForSubscriber") + public static class ListenForSubscriber extends MethodHook { + + @Override + protected Object hook(Object who, Method method, Object[] args) throws Throwable { + MethodParameterUtils.replaceFirstAppPkg(args); + return method.invoke(who, args); + } + } + + @ProxyMethod(name = "listen") + public static class Listen extends MethodHook { + + @Override + protected Object hook(Object who, Method method, Object[] args) throws Throwable { + MethodParameterUtils.replaceFirstAppPkg(args); + return method.invoke(who, args); + } + } +} diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/fake/service/base/ValueMethodProxy.java b/Bcore/src/main/java/top/niunaijun/blackbox/fake/service/base/ValueMethodProxy.java new file mode 100644 index 00000000..12765d50 --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/fake/service/base/ValueMethodProxy.java @@ -0,0 +1,25 @@ +package top.niunaijun.blackbox.fake.service.base; + +import java.lang.reflect.Method; + +import top.niunaijun.blackbox.fake.hook.MethodHook; + +public class ValueMethodProxy extends MethodHook { + + Object mValue; + String mName; + + public ValueMethodProxy(String name, Object value) { + mValue = value; + mName = name; + } + + public String getName() { + return mName; + } + + @Override + protected Object hook(Object who, Method method, Object[] args) throws Throwable { + return mValue; + } +} diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/fake/service/libcore/OsStub.java b/Bcore/src/main/java/top/niunaijun/blackbox/fake/service/libcore/OsStub.java new file mode 100644 index 00000000..3f796919 --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/fake/service/libcore/OsStub.java @@ -0,0 +1,61 @@ +package top.niunaijun.blackbox.fake.service.libcore; + +import java.lang.reflect.Method; + +import mirror.libcore.io.Libcore; +import top.niunaijun.blackbox.fake.hook.ClassInvocationStub; +import top.niunaijun.blackbox.core.IOCore; + +/** + * Created by Milk on 4/9/21. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + */ +public class OsStub extends ClassInvocationStub { + public static final String TAG = "OsStub"; + private Object mBase; + + public OsStub() { + mBase = Libcore.os.get(); + } + + @Override + protected Object getWho() { + return mBase; + } + + @Override + protected void inject(Object baseInvocation, Object proxyInvocation) { + Libcore.os.set(proxyInvocation); + } + + @Override + protected void onBindMethod() { + } + + @Override + public boolean isBadEnv() { + return Libcore.os.get() != getProxyInvocation(); + } + + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + if (args != null) { + for (int i = 0; i < args.length; i++) { + if (args[i] == null) + continue; + if (args[i] instanceof String && ((String) args[i]).startsWith("/")) { + String orig = (String) args[i]; + args[i] = IOCore.get().redirectPath(orig); +// if (!ObjectsCompat.equals(orig, args[i])) { +// Log.d(TAG, "redirectPath: " + orig + " => " + args[i]); +// } + } + } + } + return super.invoke(proxy, method, args); + } +} diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/proxy/ProxyActivity.java b/Bcore/src/main/java/top/niunaijun/blackbox/proxy/ProxyActivity.java new file mode 100644 index 00000000..d2ac75b2 --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/proxy/ProxyActivity.java @@ -0,0 +1,430 @@ +package top.niunaijun.blackbox.proxy; + +import android.app.Activity; +import android.os.Bundle; +import android.util.Log; + +import androidx.annotation.Nullable; + +import top.niunaijun.blackbox.app.BActivityThread; +import top.niunaijun.blackbox.fake.hook.HookManager; +import top.niunaijun.blackbox.fake.service.HCallbackProxy; +import top.niunaijun.blackbox.proxy.record.ProxyActivityRecord; + +/** + * Created by Milk on 3/28/21. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + */ +public class ProxyActivity extends Activity { + public static final String TAG = "StubActivity"; + + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + finish(); + } + + public static class P0 extends ProxyActivity { + + } + + public static class P1 extends ProxyActivity { + + } + + public static class P2 extends ProxyActivity { + + } + + public static class P3 extends ProxyActivity { + + } + + public static class P4 extends ProxyActivity { + + } + + public static class P5 extends ProxyActivity { + + } + + public static class P6 extends ProxyActivity { + + } + + public static class P7 extends ProxyActivity { + + } + + public static class P8 extends ProxyActivity { + + } + + public static class P9 extends ProxyActivity { + + } + + public static class P10 extends ProxyActivity { + + } + + public static class P11 extends ProxyActivity { + + } + + public static class P12 extends ProxyActivity { + + } + + public static class P13 extends ProxyActivity { + + } + + public static class P14 extends ProxyActivity { + + } + + public static class P15 extends ProxyActivity { + + } + + public static class P16 extends ProxyActivity { + + } + + public static class P17 extends ProxyActivity { + + } + + public static class P18 extends ProxyActivity { + + } + + public static class P19 extends ProxyActivity { + + } + + public static class P20 extends ProxyActivity { + + } + + public static class P21 extends ProxyActivity { + + } + + public static class P22 extends ProxyActivity { + + } + + public static class P23 extends ProxyActivity { + + } + + public static class P24 extends ProxyActivity { + + } + + public static class P25 extends ProxyActivity { + + } + + public static class P26 extends ProxyActivity { + + } + + public static class P27 extends ProxyActivity { + + } + + public static class P28 extends ProxyActivity { + + } + + public static class P29 extends ProxyActivity { + + } + + public static class P30 extends ProxyActivity { + + } + + public static class P31 extends ProxyActivity { + + } + + public static class P32 extends ProxyActivity { + + } + + public static class P33 extends ProxyActivity { + + } + + public static class P34 extends ProxyActivity { + + } + + public static class P35 extends ProxyActivity { + + } + + public static class P36 extends ProxyActivity { + + } + + public static class P37 extends ProxyActivity { + + } + + public static class P38 extends ProxyActivity { + + } + + public static class P39 extends ProxyActivity { + + } + + public static class P40 extends ProxyActivity { + + } + + public static class P41 extends ProxyActivity { + + } + + public static class P42 extends ProxyActivity { + + } + + public static class P43 extends ProxyActivity { + + } + + public static class P44 extends ProxyActivity { + + } + + public static class P45 extends ProxyActivity { + + } + + public static class P46 extends ProxyActivity { + + } + + public static class P47 extends ProxyActivity { + + } + + public static class P48 extends ProxyActivity { + + } + + public static class P49 extends ProxyActivity { + + } + + public static class P50 extends ProxyActivity { + + } + + public static class P51 extends ProxyActivity { + + } + + public static class P52 extends ProxyActivity { + + } + + public static class P53 extends ProxyActivity { + + } + + public static class P54 extends ProxyActivity { + + } + + public static class P55 extends ProxyActivity { + + } + + public static class P56 extends ProxyActivity { + + } + + public static class P57 extends ProxyActivity { + + } + + public static class P58 extends ProxyActivity { + + } + + public static class P59 extends ProxyActivity { + + } + + public static class P60 extends ProxyActivity { + + } + + public static class P61 extends ProxyActivity { + + } + + public static class P62 extends ProxyActivity { + + } + + public static class P63 extends ProxyActivity { + + } + + public static class P64 extends ProxyActivity { + + } + + public static class P65 extends ProxyActivity { + + } + + public static class P66 extends ProxyActivity { + + } + + public static class P67 extends ProxyActivity { + + } + + public static class P68 extends ProxyActivity { + + } + + public static class P69 extends ProxyActivity { + + } + + public static class P70 extends ProxyActivity { + + } + + public static class P71 extends ProxyActivity { + + } + + public static class P72 extends ProxyActivity { + + } + + public static class P73 extends ProxyActivity { + + } + + public static class P74 extends ProxyActivity { + + } + + public static class P75 extends ProxyActivity { + + } + + public static class P76 extends ProxyActivity { + + } + + public static class P77 extends ProxyActivity { + + } + + public static class P78 extends ProxyActivity { + + } + + public static class P79 extends ProxyActivity { + + } + + public static class P80 extends ProxyActivity { + + } + + public static class P81 extends ProxyActivity { + + } + + public static class P82 extends ProxyActivity { + + } + + public static class P83 extends ProxyActivity { + + } + + public static class P84 extends ProxyActivity { + + } + + public static class P85 extends ProxyActivity { + + } + + public static class P86 extends ProxyActivity { + + } + + public static class P87 extends ProxyActivity { + + } + + public static class P88 extends ProxyActivity { + + } + + public static class P89 extends ProxyActivity { + + } + + public static class P90 extends ProxyActivity { + + } + + public static class P91 extends ProxyActivity { + + } + + public static class P92 extends ProxyActivity { + + } + + public static class P93 extends ProxyActivity { + + } + + public static class P94 extends ProxyActivity { + + } + + public static class P95 extends ProxyActivity { + + } + + public static class P96 extends ProxyActivity { + + } + + public static class P97 extends ProxyActivity { + + } + + public static class P98 extends ProxyActivity { + + } + + public static class P99 extends ProxyActivity { + + } +} diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/proxy/ProxyContentProvider.java b/Bcore/src/main/java/top/niunaijun/blackbox/proxy/ProxyContentProvider.java new file mode 100644 index 00000000..ec1b67cf --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/proxy/ProxyContentProvider.java @@ -0,0 +1,473 @@ +package top.niunaijun.blackbox.proxy; + +import android.content.ContentProvider; +import android.content.ContentValues; +import android.database.Cursor; +import android.net.Uri; +import android.os.Bundle; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import top.niunaijun.blackbox.entity.AppConfig; +import top.niunaijun.blackbox.app.BActivityThread; +import top.niunaijun.blackbox.utils.compat.BundleCompat; + +/** + * Created by Milk on 3/30/21. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + */ +public class ProxyContentProvider extends ContentProvider { + @Override + public boolean onCreate() { + return false; + } + + @Nullable + @Override + public Bundle call(@NonNull String method, @Nullable String arg, @Nullable Bundle extras) { + if (method.equals("_Black_|_init_process_")) { + assert extras != null; + extras.setClassLoader(AppConfig.class.getClassLoader()); + AppConfig appConfig = extras.getParcelable(AppConfig.KEY); + BActivityThread.currentActivityThread().initProcess(appConfig); + + Bundle bundle = new Bundle(); + BundleCompat.putBinder(bundle, "_Black_|_client_", BActivityThread.currentActivityThread()); + return bundle; + } + return super.call(method, arg, extras); + } + + @Nullable + @Override + public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) { + return null; + } + + @Nullable + @Override + public String getType(@NonNull Uri uri) { + return null; + } + + @Nullable + @Override + public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) { + return null; + } + + @Override + public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) { + return 0; + } + + @Override + public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) { + return 0; + } + + public static class P0 extends ProxyContentProvider { + + } + + public static class P1 extends ProxyContentProvider { + + } + + public static class P2 extends ProxyContentProvider { + + } + + public static class P3 extends ProxyContentProvider { + + } + + public static class P4 extends ProxyContentProvider { + + } + + public static class P5 extends ProxyContentProvider { + + } + + public static class P6 extends ProxyContentProvider { + + } + + public static class P7 extends ProxyContentProvider { + + } + + public static class P8 extends ProxyContentProvider { + + } + + public static class P9 extends ProxyContentProvider { + + } + + public static class P10 extends ProxyContentProvider { + + } + + public static class P11 extends ProxyContentProvider { + + } + + public static class P12 extends ProxyContentProvider { + + } + + public static class P13 extends ProxyContentProvider { + + } + + public static class P14 extends ProxyContentProvider { + + } + + public static class P15 extends ProxyContentProvider { + + } + + public static class P16 extends ProxyContentProvider { + + } + + public static class P17 extends ProxyContentProvider { + + } + + public static class P18 extends ProxyContentProvider { + + } + + public static class P19 extends ProxyContentProvider { + + } + + public static class P20 extends ProxyContentProvider { + + } + + public static class P21 extends ProxyContentProvider { + + } + + public static class P22 extends ProxyContentProvider { + + } + + public static class P23 extends ProxyContentProvider { + + } + + public static class P24 extends ProxyContentProvider { + + } + + public static class P25 extends ProxyContentProvider { + + } + + public static class P26 extends ProxyContentProvider { + + } + + public static class P27 extends ProxyContentProvider { + + } + + public static class P28 extends ProxyContentProvider { + + } + + public static class P29 extends ProxyContentProvider { + + } + + public static class P30 extends ProxyContentProvider { + + } + + public static class P31 extends ProxyContentProvider { + + } + + public static class P32 extends ProxyContentProvider { + + } + + public static class P33 extends ProxyContentProvider { + + } + + public static class P34 extends ProxyContentProvider { + + } + + public static class P35 extends ProxyContentProvider { + + } + + public static class P36 extends ProxyContentProvider { + + } + + public static class P37 extends ProxyContentProvider { + + } + + public static class P38 extends ProxyContentProvider { + + } + + public static class P39 extends ProxyContentProvider { + + } + + public static class P40 extends ProxyContentProvider { + + } + + public static class P41 extends ProxyContentProvider { + + } + + public static class P42 extends ProxyContentProvider { + + } + + public static class P43 extends ProxyContentProvider { + + } + + public static class P44 extends ProxyContentProvider { + + } + + public static class P45 extends ProxyContentProvider { + + } + + public static class P46 extends ProxyContentProvider { + + } + + public static class P47 extends ProxyContentProvider { + + } + + public static class P48 extends ProxyContentProvider { + + } + + public static class P49 extends ProxyContentProvider { + + } + + public static class P50 extends ProxyContentProvider { + + } + + public static class P51 extends ProxyContentProvider { + + } + + public static class P52 extends ProxyContentProvider { + + } + + public static class P53 extends ProxyContentProvider { + + } + + public static class P54 extends ProxyContentProvider { + + } + + public static class P55 extends ProxyContentProvider { + + } + + public static class P56 extends ProxyContentProvider { + + } + + public static class P57 extends ProxyContentProvider { + + } + + public static class P58 extends ProxyContentProvider { + + } + + public static class P59 extends ProxyContentProvider { + + } + + public static class P60 extends ProxyContentProvider { + + } + + public static class P61 extends ProxyContentProvider { + + } + + public static class P62 extends ProxyContentProvider { + + } + + public static class P63 extends ProxyContentProvider { + + } + + public static class P64 extends ProxyContentProvider { + + } + + public static class P65 extends ProxyContentProvider { + + } + + public static class P66 extends ProxyContentProvider { + + } + + public static class P67 extends ProxyContentProvider { + + } + + public static class P68 extends ProxyContentProvider { + + } + + public static class P69 extends ProxyContentProvider { + + } + + public static class P70 extends ProxyContentProvider { + + } + + public static class P71 extends ProxyContentProvider { + + } + + public static class P72 extends ProxyContentProvider { + + } + + public static class P73 extends ProxyContentProvider { + + } + + public static class P74 extends ProxyContentProvider { + + } + + public static class P75 extends ProxyContentProvider { + + } + + public static class P76 extends ProxyContentProvider { + + } + + public static class P77 extends ProxyContentProvider { + + } + + public static class P78 extends ProxyContentProvider { + + } + + public static class P79 extends ProxyContentProvider { + + } + + public static class P80 extends ProxyContentProvider { + + } + + public static class P81 extends ProxyContentProvider { + + } + + public static class P82 extends ProxyContentProvider { + + } + + public static class P83 extends ProxyContentProvider { + + } + + public static class P84 extends ProxyContentProvider { + + } + + public static class P85 extends ProxyContentProvider { + + } + + public static class P86 extends ProxyContentProvider { + + } + + public static class P87 extends ProxyContentProvider { + + } + + public static class P88 extends ProxyContentProvider { + + } + + public static class P89 extends ProxyContentProvider { + + } + + public static class P90 extends ProxyContentProvider { + + } + + public static class P91 extends ProxyContentProvider { + + } + + public static class P92 extends ProxyContentProvider { + + } + + public static class P93 extends ProxyContentProvider { + + } + + public static class P94 extends ProxyContentProvider { + + } + + public static class P95 extends ProxyContentProvider { + + } + + public static class P96 extends ProxyContentProvider { + + } + + public static class P97 extends ProxyContentProvider { + + } + + public static class P98 extends ProxyContentProvider { + + } + + public static class P99 extends ProxyContentProvider { + + } +} diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/proxy/ProxyManifest.java b/Bcore/src/main/java/top/niunaijun/blackbox/proxy/ProxyManifest.java new file mode 100644 index 00000000..49c9f15f --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/proxy/ProxyManifest.java @@ -0,0 +1,41 @@ +package top.niunaijun.blackbox.proxy; + +import java.util.Locale; + +import top.niunaijun.blackbox.BlackBoxCore; + +/** + * Created by Milk on 4/1/21. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + */ +public class ProxyManifest { + public static final int FREE_COUNT = 100; + + public static boolean isProxy(String msg) { + return getBindProvider().equals(msg) || msg.contains("proxy_content_provider_"); + } + + public static String getBindProvider() { + return BlackBoxCore.getHostPkg() + ".blackbox.SystemCallProvider"; + } + + public static String getProxyAuthorities(int index) { + return String.format(Locale.CHINA, "%s.proxy_content_provider_%d", BlackBoxCore.getHostPkg(), index); + } + + public static String getProxyActivity(int index) { + return String.format(Locale.CHINA, "top.niunaijun.blackbox.proxy.ProxyActivity$P%d", index); + } + + public static String getProxyFileProvider() { + return BlackBoxCore.getHostPkg() + ".blackbox.FileProvider"; + } + + public static String getProcessName(int bPid) { + return BlackBoxCore.getHostPkg() + ":p" + bPid; + } +} diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/proxy/record/ProxyActivityRecord.java b/Bcore/src/main/java/top/niunaijun/blackbox/proxy/record/ProxyActivityRecord.java new file mode 100644 index 00000000..8e87b6d7 --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/proxy/record/ProxyActivityRecord.java @@ -0,0 +1,44 @@ +package top.niunaijun.blackbox.proxy.record; + +import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.os.IBinder; + +import top.niunaijun.blackbox.utils.compat.BundleCompat; + +/** + * Created by Milk on 3/31/21. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + */ +public class ProxyActivityRecord { + public int mUserId; + public ActivityInfo mActivityInfo; + public Intent mTarget; + public IBinder mActivityRecord; + + public ProxyActivityRecord(int userId, ActivityInfo activityInfo, Intent target, IBinder activityRecord) { + mUserId = userId; + mActivityInfo = activityInfo; + mTarget = target; + mActivityRecord = activityRecord; + } + + public static void saveStub(Intent shadow, Intent target, ActivityInfo activityInfo, IBinder activityRecord, int userId) { + shadow.putExtra("_VM_|_user_id_", userId); + shadow.putExtra("_VM_|_activity_info_", activityInfo); + shadow.putExtra("_VM_|_target_", target); + BundleCompat.putBinder(shadow, "_VM_|_activity_record_v_", activityRecord); + } + + public static ProxyActivityRecord create(Intent intent) { + int userId = intent.getIntExtra("_VM_|_user_id_", 0); + ActivityInfo activityInfo = intent.getParcelableExtra("_VM_|_activity_info_"); + Intent target = intent.getParcelableExtra("_VM_|_target_"); + IBinder activityRecord = BundleCompat.getBinder(intent, "_VM_|_activity_record_v_"); + return new ProxyActivityRecord(userId, activityInfo, target, activityRecord); + } +} diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/utils/AbiUtils.java b/Bcore/src/main/java/top/niunaijun/blackbox/utils/AbiUtils.java new file mode 100644 index 00000000..1e3623fe --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/utils/AbiUtils.java @@ -0,0 +1,77 @@ +package top.niunaijun.blackbox.utils; + +import java.io.File; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; + +import top.niunaijun.blackbox.BlackBoxCore; + +/** + * Created by Milk on 3/2/21. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + */ +public class AbiUtils { + private final Set mLibs = new HashSet<>(); + private static final Map sAbiUtilsMap = new HashMap<>(); + + public static boolean isSupport(File apkFile) { + AbiUtils abiUtils = sAbiUtilsMap.get(apkFile); + if (abiUtils == null) { + abiUtils = new AbiUtils(apkFile); + sAbiUtilsMap.put(apkFile, abiUtils); + } + if (abiUtils.isEmptyAib()) { + return true; + } + + if (BlackBoxCore.is64Bit()) { + return abiUtils.is64Bit(); + } else { + return abiUtils.is32Bit(); + } + } + + public AbiUtils(File apkFile) { + ZipFile zipFile = null; + try { + zipFile = new ZipFile(apkFile); + Enumeration entries = zipFile.entries(); + while (entries.hasMoreElements()) { + ZipEntry zipEntry = entries.nextElement(); + String name = zipEntry.getName(); + if (name.startsWith("lib/arm64-v8a")) { + mLibs.add("arm64-v8a"); + } else if (name.startsWith("lib/armeabi")) { + mLibs.add("armeabi"); + } else if (name.startsWith("lib/armeabi-v7a")) { + mLibs.add("armeabi-v7a"); + } + } + } catch (Exception e) { + e.printStackTrace(); + } finally { + CloseUtils.close(zipFile); + } + } + + public boolean is64Bit() { + return mLibs.contains("arm64-v8a"); + } + + public boolean is32Bit() { + return mLibs.contains("armeabi") || mLibs.contains("armeabi-v7a"); + } + + public boolean isEmptyAib() { + return mLibs.isEmpty(); + } +} diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/utils/ArrayUtils.java b/Bcore/src/main/java/top/niunaijun/blackbox/utils/ArrayUtils.java new file mode 100644 index 00000000..fad4c7cb --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/utils/ArrayUtils.java @@ -0,0 +1,146 @@ +package top.niunaijun.blackbox.utils; + +import java.util.Arrays; +import java.util.Objects; + +public class ArrayUtils { + + public static T[] trimToSize(T[] array, int size) { + if (array == null || size == 0) { + return null; + } else if (array.length == size) { + return array; + } else { + return Arrays.copyOf(array, size); + } + } + + public static Object[] push(Object[] array, Object item) + { + Object[] longer = new Object[array.length + 1]; + System.arraycopy(array, 0, longer, 0, array.length); + longer[array.length] = item; + return longer; + } + + public static boolean contains(T[] array, T value) { + return indexOf(array, value) != -1; + } + public static boolean contains(int[] array, int value) { + if (array == null) return false; + for (int element : array) { + if (element == value) { + return true; + } + } + return false; + } + + /** + * Return first index of {@code value} in {@code array}, or {@code -1} if + * not found. + */ + public static int indexOf(T[] array, T value) { + if (array == null) return -1; + for (int i = 0; i < array.length; i++) { + if (Objects.equals(array[i], value)) return i; + } + return -1; + } + + public static int protoIndexOf(Class[] array, Class type) { + if (array == null) return -1; + for (int i = 0; i < array.length; i++) { + if (array[i] == type) return i; + } + return -1; + } + + public static int indexOfFirst(Object[] array, Class type) { + if (!isEmpty(array)) { + int N = -1; + for (Object one : array) { + N++; + if (one != null && type == one.getClass()) { + return N; + } + } + } + return -1; + } + + public static int protoIndexOf(Class[] array, Class type, int sequence) { + if (array == null) { + return -1; + } + while (sequence < array.length) { + if (type == array[sequence]) { + return sequence; + } + sequence++; + } + return -1; + } + + + public static int indexOfObject(Object[] array, Class type, int sequence) { + if (array == null) { + return -1; + } + while (sequence < array.length) { + if (type.isInstance(array[sequence])) { + return sequence; + } + sequence++; + } + return -1; + } + + + public static int indexOf(Object[] array, Class type, int sequence) { + if (!isEmpty(array)) { + int N = -1; + for (Object one : array) { + N++; + if (one != null && one.getClass() == type) { + if (--sequence <= 0) { + return N; + } + } + } + } + return -1; + } + + public static int indexOfLast(Object[] array, Class type) { + if (!isEmpty(array)) { + for (int N = array.length; N > 0; N--) { + Object one = array[N - 1]; + if (one != null && one.getClass() == type) { + return N - 1; + } + } + } + return -1; + } + + public static boolean isEmpty(T[] array) { + return array == null || array.length == 0; + } + + @SuppressWarnings("unchecked") + public static T getFirst(Object[] args, Class clazz) { + int index = indexOfFirst(args, clazz); + if (index != -1) { + return (T) args[index]; + } + return null; + } + + + public static void checkOffsetAndCount(int arrayLength, int offset, int count) throws ArrayIndexOutOfBoundsException { + if ((offset | count) < 0 || offset > arrayLength || arrayLength - offset < count) { + throw new ArrayIndexOutOfBoundsException(offset); + } + } +} diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/utils/CloseUtils.java b/Bcore/src/main/java/top/niunaijun/blackbox/utils/CloseUtils.java new file mode 100644 index 00000000..7abee15d --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/utils/CloseUtils.java @@ -0,0 +1,28 @@ +package top.niunaijun.blackbox.utils; + +import java.io.Closeable; +import java.io.IOException; + +/** + * Created by sunwanquan on 2019/6/18. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + */ +public class CloseUtils { + public static void close(Closeable... closeables) { + if (closeables == null) { + return; + } + for (Closeable closeable : closeables) { + if (closeable != null) { + try { + closeable.close(); + } catch (IOException ignored) { + } + } + } + } +} diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/utils/ComponentUtils.java b/Bcore/src/main/java/top/niunaijun/blackbox/utils/ComponentUtils.java new file mode 100644 index 00000000..82435e38 --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/utils/ComponentUtils.java @@ -0,0 +1,116 @@ +package top.niunaijun.blackbox.utils; + +import android.content.ComponentName; +import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.content.pm.ComponentInfo; +import android.content.pm.ProviderInfo; + +import java.util.Objects; + +import top.niunaijun.blackbox.app.BActivityThread; + +import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE; + +public class ComponentUtils { + + public static boolean isRequestInstall(Intent intent) { + return "application/vnd.android.package-archive".equals(intent.getType()); + } + + public static boolean isSelf(Intent intent) { + ComponentName component = intent.getComponent(); + if (component == null || BActivityThread.getAppPackageName() == null) return false; + return component.getPackageName().equals(BActivityThread.getAppPackageName()); + } + + public static boolean isSelf(Intent[] intent) { + for (Intent intent1 : intent) { + if (!isSelf(intent1)) { + return false; + } + } + return true; + } + + public static boolean isGmsService(Intent intent) { + String aPackage = intent.getPackage(); + return "com.google.android.gms".equals(aPackage); + } + + public static String getTaskAffinity(ActivityInfo info) { + if (info.launchMode == LAUNCH_SINGLE_INSTANCE) { + return "-SingleInstance-" + info.packageName + "/" + info.name; + } else if (info.taskAffinity == null && info.applicationInfo.taskAffinity == null) { + return info.packageName; + } else if (info.taskAffinity != null) { + return info.taskAffinity; + } + return info.applicationInfo.taskAffinity; + } + + public static String getFirstAuthority(ProviderInfo info) { + if (info == null) { + return null; + } + String[] authorities = info.authority.split(";"); + return authorities.length == 0 ? info.authority : authorities[0]; + } + + public static boolean intentFilterEquals(Intent a, Intent b) { + if (a != null && b != null) { + if (!Objects.equals(a.getAction(), b.getAction())) { + return false; + } + if (!Objects.equals(a.getData(), b.getData())) { + return false; + } + if (!Objects.equals(a.getType(), b.getType())) { + return false; + } + Object pkgA = a.getPackage(); + if (pkgA == null && a.getComponent() != null) { + pkgA = a.getComponent().getPackageName(); + } + String pkgB = b.getPackage(); + if (pkgB == null && b.getComponent() != null) { + pkgB = b.getComponent().getPackageName(); + } + if (!Objects.equals(pkgA, pkgB)) { + return false; + } + if (!Objects.equals(a.getComponent(), b.getComponent())) { + return false; + } + if (!Objects.equals(a.getCategories(), b.getCategories())) { + return false; + } + } + return true; + } + + public static String getProcessName(ComponentInfo componentInfo) { + String processName = componentInfo.processName; + if (processName == null) { + processName = componentInfo.packageName; + componentInfo.processName = processName; + } + return processName; + } + + public static boolean isSameComponent(ComponentInfo first, ComponentInfo second) { + + if (first != null && second != null) { + String pkg1 = first.packageName + ""; + String pkg2 = second.packageName + ""; + String name1 = first.name + ""; + String name2 = second.name + ""; + return pkg1.equals(pkg2) && name1.equals(name2); + } + return false; + } + + public static ComponentName toComponentName(ComponentInfo componentInfo) { + return new ComponentName(componentInfo.packageName, componentInfo.name); + } +} diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/utils/FileUtils.java b/Bcore/src/main/java/top/niunaijun/blackbox/utils/FileUtils.java new file mode 100644 index 00000000..e6340f69 --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/utils/FileUtils.java @@ -0,0 +1,512 @@ +package top.niunaijun.blackbox.utils; + +import android.content.Context; +import android.database.Cursor; +import android.net.Uri; +import android.os.Build; +import android.os.Parcel; +import android.system.Os; +import android.text.TextUtils; + +import java.io.BufferedOutputStream; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.Closeable; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.RandomAccessFile; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.channels.Channels; +import java.nio.channels.FileChannel; +import java.nio.channels.ReadableByteChannel; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +public class FileUtils { + + public static int count(File file) { + if (!file.exists()) { + return -1; + } + if (file.isFile()) { + return 1; + } + if (file.isDirectory()) { + String[] fs = file.list(); + return fs == null ? 0 : fs.length; + } + return 0; + } + + public static String getFilenameExt(String filename) { + int dotPos = filename.lastIndexOf('.'); + if (dotPos == -1) { + return ""; + } + return filename.substring(dotPos + 1); + } + + public static File changeExt(File f, String targetExt) { + String outPath = f.getAbsolutePath(); + if (!getFilenameExt(outPath).equals(targetExt)) { + int dotPos = outPath.lastIndexOf("."); + if (dotPos > 0) { + outPath = outPath.substring(0, dotPos + 1) + targetExt; + } else { + outPath = outPath + "." + targetExt; + } + return new File(outPath); + } + return f; + } + + public static boolean renameTo(File origFile, File newFile) { + return origFile.renameTo(newFile); + } + + public static String readToString(String fileName) throws IOException { + InputStream is = new FileInputStream(fileName); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + int i; + while ((i = is.read()) != -1) { + baos.write(i); + } + return baos.toString(); + } + + public static Parcel readToParcel(File file) throws IOException { + Parcel in = Parcel.obtain(); + byte[] bytes = toByteArray(file); + in.unmarshall(bytes, 0, bytes.length); + in.setDataPosition(0); + return in; + } + + /** + * @param path + * @param mode {@link FileMode} + */ + public static void chmod(String path, int mode) throws Exception { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + try { + Os.chmod(path, mode); + return; + } catch (Exception e) { + // ignore + } + } + + File file = new File(path); + String cmd = "chmod "; + if (file.isDirectory()) { + cmd += " -R "; + } + String cmode = String.format("%o", mode); + Runtime.getRuntime().exec(cmd + cmode + " " + path).waitFor(); + } + + public static void createSymlink(String oldPath, String newPath) throws Exception { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + try { + Os.link(oldPath, newPath); + return; + } catch (Throwable e) { + //ignore + } + } + Runtime.getRuntime().exec("ln -s " + oldPath + " " + newPath).waitFor(); + } + + public static boolean isSymlink(File file) throws IOException { + if (file == null) + throw new NullPointerException("File must not be null"); + File canon; + if (file.getParent() == null) { + canon = file; + } else { + File canonDir = file.getParentFile().getCanonicalFile(); + canon = new File(canonDir, file.getName()); + } + return !canon.getCanonicalFile().equals(canon.getAbsoluteFile()); + } + + public static void writeParcelToFile(Parcel p, File file) throws IOException { + FileOutputStream fos = new FileOutputStream(file); + fos.write(p.marshall()); + fos.close(); + } + + public static void writeParcelToOutput(Parcel p, FileOutputStream fos) throws IOException { + fos.write(p.marshall()); + } + + public static byte[] toByteArray(File file) throws IOException { + FileInputStream fileInputStream = new FileInputStream(file); + try { + return toByteArray(fileInputStream); + } finally { + closeQuietly(fileInputStream); + } + } + + public static byte[] toByteArray(InputStream inStream) throws IOException { + ByteArrayOutputStream swapStream = new ByteArrayOutputStream(); + byte[] buff = new byte[100]; + int rc; + while ((rc = inStream.read(buff, 0, 100)) > 0) { + swapStream.write(buff, 0, rc); + } + return swapStream.toByteArray(); + } + + public static int deleteDir(File dir) { + int count = 0; + if (dir.isDirectory()) { + boolean link = false; + try { + link = isSymlink(dir); + } catch (Exception e) { + //ignore + } + if (!link) { + String[] children = dir.list(); + for (String file : children) { + count += deleteDir(new File(dir, file)); + } + } + } + if (dir.delete()) { + count++; + } + return count; + } + + public static int deleteDir(String dir) { + return deleteDir(new File(dir)); + } + + public static void writeToFile(InputStream dataIns, File target) throws IOException { + final int BUFFER = 1024; + BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(target)); + int count; + byte data[] = new byte[BUFFER]; + while ((count = dataIns.read(data, 0, BUFFER)) != -1) { + bos.write(data, 0, count); + } + bos.close(); + } + + public static void writeToFile(byte[] data, File target) throws IOException { + FileOutputStream fo = null; + ReadableByteChannel src = null; + FileChannel out = null; + try { + src = Channels.newChannel(new ByteArrayInputStream(data)); + fo = new FileOutputStream(target); + out = fo.getChannel(); + out.transferFrom(src, 0, data.length); + } finally { + if (fo != null) { + fo.close(); + } + if (src != null) { + src.close(); + } + if (out != null) { + out.close(); + } + } + } + + public static void copyFile(InputStream inputStream, File target) { + FileOutputStream outputStream = null; + try { + outputStream = new FileOutputStream(target); + byte[] data = new byte[4096]; + int len; + while ((len = inputStream.read(data)) != -1) { + outputStream.write(data, 0, len); + } + outputStream.flush(); + } catch (Throwable e) { + //ignore + } finally { + closeQuietly(inputStream); + closeQuietly(outputStream); + } + } + + public static void copyFile(File source, File target) throws IOException { + FileInputStream inputStream = null; + FileOutputStream outputStream = null; + try { + inputStream = new FileInputStream(source); + outputStream = new FileOutputStream(target); + FileChannel iChannel = inputStream.getChannel(); + FileChannel oChannel = outputStream.getChannel(); + + ByteBuffer buffer = ByteBuffer.allocate(1024); + while (true) { + buffer.clear(); + int r = iChannel.read(buffer); + if (r == -1) + break; + buffer.limit(buffer.position()); + buffer.position(0); + oChannel.write(buffer); + } + } finally { + closeQuietly(inputStream); + closeQuietly(outputStream); + } + } + + public static void closeQuietly(Closeable closeable) { + if (closeable != null) { + try { + closeable.close(); + } catch (Exception ignored) { + } + } + } + + public static int peekInt(byte[] bytes, int value, ByteOrder endian) { + int v2; + int v0; + if (endian == ByteOrder.BIG_ENDIAN) { + v0 = value + 1; + v2 = v0 + 1; + v0 = (bytes[v0] & 255) << 16 | (bytes[value] & 255) << 24 | (bytes[v2] & 255) << 8 | bytes[v2 + 1] & 255; + } else { + v0 = value + 1; + v2 = v0 + 1; + v0 = (bytes[v0] & 255) << 8 | bytes[value] & 255 | (bytes[v2] & 255) << 16 | (bytes[v2 + 1] & 255) << 24; + } + + return v0; + } + + private static boolean isValidExtFilenameChar(char c) { + switch (c) { + case '\0': + case '/': + return false; + default: + return true; + } + } + + /** + * Check if given filename is valid for an ext4 filesystem. + */ + public static boolean isValidExtFilename(String name) { + return (name != null) && name.equals(buildValidExtFilename(name)); + } + + /** + * Mutate the given filename to make it valid for an ext4 filesystem, + * replacing any invalid characters with "_". + */ + public static String buildValidExtFilename(String name) { + if (TextUtils.isEmpty(name) || ".".equals(name) || "..".equals(name)) { + return "(invalid)"; + } + final StringBuilder res = new StringBuilder(name.length()); + for (int i = 0; i < name.length(); i++) { + final char c = name.charAt(i); + if (isValidExtFilenameChar(c)) { + res.append(c); + } else { + res.append('_'); + } + } + return res.toString(); + } + + public static void mkdirs(File path) { + if (!path.exists()) + path.mkdirs(); + } + + public static void mkdirs(String path) { + mkdirs(new File(path)); + } + + public static boolean isExist(String path) { + return new File(path).exists(); + } + + public static boolean canRead(String path) { + return new File(path).canRead(); + } + + public interface FileMode { + int MODE_ISUID = 04000; + int MODE_ISGID = 02000; + int MODE_ISVTX = 01000; + int MODE_IRUSR = 00400; + int MODE_IWUSR = 00200; + int MODE_IXUSR = 00100; + int MODE_IRGRP = 00040; + int MODE_IWGRP = 00020; + int MODE_IXGRP = 00010; + int MODE_IROTH = 00004; + int MODE_IWOTH = 00002; + int MODE_IXOTH = 00001; + + int MODE_755 = MODE_IRUSR | MODE_IWUSR | MODE_IXUSR + | MODE_IRGRP | MODE_IXGRP + | MODE_IROTH | MODE_IXOTH; + } + + /** + * Lock the specified fle + */ + public static class FileLock { + private static FileLock singleton; + private Map mRefCountMap = new ConcurrentHashMap(); + + public static FileLock getInstance() { + if (singleton == null) { + singleton = new FileLock(); + } + return singleton; + } + + private int RefCntInc(String filePath, java.nio.channels.FileLock fileLock, RandomAccessFile randomAccessFile, + FileChannel fileChannel) { + int refCount; + if (this.mRefCountMap.containsKey(filePath)) { + FileLockCount fileLockCount = this.mRefCountMap.get(filePath); + int i = fileLockCount.mRefCount; + fileLockCount.mRefCount = i + 1; + refCount = i; + } else { + refCount = 1; + this.mRefCountMap.put(filePath, new FileLockCount(fileLock, refCount, randomAccessFile, fileChannel)); + + } + return refCount; + } + + private int RefCntDec(String filePath) { + int refCount = 0; + if (this.mRefCountMap.containsKey(filePath)) { + FileLockCount fileLockCount = this.mRefCountMap.get(filePath); + int i = fileLockCount.mRefCount - 1; + fileLockCount.mRefCount = i; + refCount = i; + if (refCount <= 0) { + this.mRefCountMap.remove(filePath); + } + } + return refCount; + } + + public boolean LockExclusive(File targetFile) { + + if (targetFile == null) { + return false; + } + try { + File lockFile = new File(targetFile.getParentFile().getAbsolutePath().concat("/lock")); + if (!lockFile.exists()) { + lockFile.createNewFile(); + } + RandomAccessFile randomAccessFile = new RandomAccessFile(lockFile.getAbsolutePath(), "rw"); + FileChannel channel = randomAccessFile.getChannel(); + java.nio.channels.FileLock lock = channel.lock(); + if (!lock.isValid()) { + return false; + } + RefCntInc(lockFile.getAbsolutePath(), lock, randomAccessFile, channel); + return true; + } catch (Exception e) { + return false; + } + } + + /** + * unlock odex file + **/ + public void unLock(File targetFile) { + + File lockFile = new File(targetFile.getParentFile().getAbsolutePath().concat("/lock")); + if (!lockFile.exists()) { + return; + } + if (this.mRefCountMap.containsKey(lockFile.getAbsolutePath())) { + FileLockCount fileLockCount = this.mRefCountMap.get(lockFile.getAbsolutePath()); + if (fileLockCount != null) { + java.nio.channels.FileLock fileLock = fileLockCount.mFileLock; + RandomAccessFile randomAccessFile = fileLockCount.fOs; + FileChannel fileChannel = fileLockCount.fChannel; + try { + if (RefCntDec(lockFile.getAbsolutePath()) <= 0) { + if (fileLock != null && fileLock.isValid()) { + fileLock.release(); + } + if (randomAccessFile != null) { + randomAccessFile.close(); + } + if (fileChannel != null) { + fileChannel.close(); + } + } + } catch (IOException e) { + e.printStackTrace(); + } + } + } + } + + private class FileLockCount { + FileChannel fChannel; + RandomAccessFile fOs; + java.nio.channels.FileLock mFileLock; + int mRefCount; + + FileLockCount(java.nio.channels.FileLock fileLock, int mRefCount, RandomAccessFile fOs, + FileChannel fChannel) { + this.mFileLock = fileLock; + this.mRefCount = mRefCount; + this.fOs = fOs; + this.fChannel = fChannel; + } + } + } + + private static String getDataColumn(Context context, Uri uri, String selection, String[] selectionArgs) { + Cursor cursor = null; + final String column = "_data"; + final String[] projection = {column}; + try { + cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null); + if (cursor != null && cursor.moveToFirst()) { + final int column_index = cursor.getColumnIndexOrThrow(column); + return cursor.getString(column_index); + } + } finally { + if (cursor != null) + cursor.close(); + } + return null; + } + + private static boolean isExternalStorageDocument(Uri uri) { + return "com.android.externalstorage.documents".equals(uri.getAuthority()); + } + + private static boolean isDownloadsDocument(Uri uri) { + return "com.android.providers.downloads.documents".equals(uri.getAuthority()); + } + + private static boolean isMediaDocument(Uri uri) { + return "com.android.providers.media.documents".equals(uri.getAuthority()); + } + +} diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/utils/Md5Utils.java b/Bcore/src/main/java/top/niunaijun/blackbox/utils/Md5Utils.java new file mode 100644 index 00000000..08e01cd6 --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/utils/Md5Utils.java @@ -0,0 +1,100 @@ +package top.niunaijun.blackbox.utils; + +/** + * Created by admin on 2017/1/8. + */ + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + + +public class Md5Utils { + + private static final char[] hexDigits = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', + 'e', 'f' }; + + + public static String md5(String input) { + if (input == null) + return null; + try { + MessageDigest messageDigest = MessageDigest.getInstance("MD5"); + byte[] inputByteArray = input.getBytes("utf-8"); + messageDigest.update(inputByteArray); + byte[] resultByteArray = messageDigest.digest(); + return byteArrayToHex(resultByteArray); + } catch (Exception e) { + return null; + } + } + + public static String md5(File file) { + try { + if (!file.isFile()) { + + return null; + } + + FileInputStream in = new FileInputStream(file); + + String result = md5(in); + + in.close(); + + return result; + + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + + return null; + } + + public static String md5(InputStream in) { + + try { + MessageDigest messagedigest = MessageDigest.getInstance("MD5"); + + byte[] buffer = new byte[1024]; + int read = 0; + while ((read = in.read(buffer)) != -1) { + messagedigest.update(buffer, 0, read); + } + + in.close(); + + String result = byteArrayToHex(messagedigest.digest()); + + return result; + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + + return null; + } + + private static String byteArrayToHex(byte[] byteArray) { + + char[] resultCharArray = new char[byteArray.length * 2]; + int index = 0; + for (byte b : byteArray) { + resultCharArray[index++] = hexDigits[b >>> 4 & 0xf]; + resultCharArray[index++] = hexDigits[b & 0xf]; + } + + return new String(resultCharArray); + + } + +} diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/utils/MethodParameterUtils.java b/Bcore/src/main/java/top/niunaijun/blackbox/utils/MethodParameterUtils.java new file mode 100644 index 00000000..5ed27107 --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/utils/MethodParameterUtils.java @@ -0,0 +1,58 @@ +package top.niunaijun.blackbox.utils; + +import android.os.Process; + +import java.util.Arrays; +import java.util.HashSet; + +import top.niunaijun.blackbox.app.BActivityThread; +import top.niunaijun.blackbox.BlackBoxCore; + +public class MethodParameterUtils { + + + public static String replaceFirstAppPkg(Object[] args) { + if (args == null) { + return null; + } + for (int i = 0; i < args.length; i++) { + if (args[i] instanceof String) { + String value = (String) args[i]; + if (BlackBoxCore.get().isInstalled(value)) { + args[i] = BlackBoxCore.getHostPkg(); + return value; + } + } + } + return null; + } + + public static void replaceLastUserId(Object[] args){ + int index = ArrayUtils.indexOfLast(args, Integer.class); + if (index != -1) { + int uid = (int) args[index]; + if (uid == BActivityThread.getUid()) { + args[index] = Process.myUid(); + } + } + } + + public static Class[] getAllInterface(Class clazz){ + HashSet> classes = new HashSet<>(); + getAllInterfaces(clazz,classes); + Class[] result=new Class[classes.size()]; + classes.toArray(result); + return result; + } + + + public static void getAllInterfaces(Class clazz, HashSet> interfaceCollection) { + Class[] classes = clazz.getInterfaces(); + if (classes.length != 0) { + interfaceCollection.addAll(Arrays.asList(classes)); + } + if (clazz.getSuperclass() != Object.class) { + getAllInterfaces(clazz.getSuperclass(), interfaceCollection); + } + } +} diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/utils/NativeUtils.java b/Bcore/src/main/java/top/niunaijun/blackbox/utils/NativeUtils.java new file mode 100644 index 00000000..b8f31648 --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/utils/NativeUtils.java @@ -0,0 +1,111 @@ +package top.niunaijun.blackbox.utils; + +import android.os.Build; +import android.util.Log; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Enumeration; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; + + +/** + * Created by Milk on 2/24/21. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + */ +public class NativeUtils { + public static final String TAG = "VirtualM"; + + public static void copyNativeLib(File apk, File nativeLibDir) throws Exception { + long startTime = System.currentTimeMillis(); + if (!nativeLibDir.exists()) { + nativeLibDir.mkdirs(); + } + try (ZipFile zipfile = new ZipFile(apk.getAbsolutePath())) { + if (findAndCopyNativeLib(zipfile, Build.CPU_ABI, nativeLibDir)) { + return; + } + + findAndCopyNativeLib(zipfile, "armeabi", nativeLibDir); + } finally { + Log.d(TAG, "Done! +" + (System.currentTimeMillis() - startTime) + "ms"); + } + } + + + private static boolean findAndCopyNativeLib(ZipFile zipfile, String cpuArch, File nativeLibDir) throws Exception { + Log.d(TAG, "Try to copy plugin's cup arch: " + cpuArch); + boolean findLib = false; + boolean findSo = false; + byte buffer[] = null; + String libPrefix = "lib/" + cpuArch + "/"; + ZipEntry entry; + Enumeration e = zipfile.entries(); + + while (e.hasMoreElements()) { + entry = (ZipEntry) e.nextElement(); + String entryName = entry.getName(); + if (!findLib && !entryName.startsWith("lib/")) { + continue; + } + findLib = true; + if (!entryName.endsWith(".so") || !entryName.startsWith(libPrefix)) { + continue; + } + + if (buffer == null) { + findSo = true; + Log.d(TAG, "Found plugin's cup arch dir: " + cpuArch); + buffer = new byte[8192]; + } + + String libName = entryName.substring(entryName.lastIndexOf('/') + 1); + Log.d(TAG, "verify so " + libName); +// File abiDir = new File(nativeLibDir, cpuArch); +// if (!abiDir.exists()) { +// abiDir.mkdirs(); +// } + + File libFile = new File(nativeLibDir, libName); + if (libFile.exists() && libFile.length() == entry.getSize()) { + Log.d(TAG, libName + " skip copy"); + continue; + } + FileOutputStream fos = new FileOutputStream(libFile); + Log.d(TAG, "copy so " + entry.getName() + " of " + cpuArch); + copySo(buffer, zipfile.getInputStream(entry), fos); + } + + if (!findLib) { + Log.d(TAG, "Fast skip all!"); + return true; + } + + return findSo; + } + + private static void copySo(byte[] buffer, InputStream input, OutputStream output) throws IOException { + BufferedInputStream bufferedInput = new BufferedInputStream(input); + BufferedOutputStream bufferedOutput = new BufferedOutputStream(output); + int count; + + while ((count = bufferedInput.read(buffer)) > 0) { + bufferedOutput.write(buffer, 0, count); + } + bufferedOutput.flush(); + bufferedOutput.close(); + output.close(); + bufferedInput.close(); + input.close(); + } +} diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/utils/Reflector.java b/Bcore/src/main/java/top/niunaijun/blackbox/utils/Reflector.java new file mode 100644 index 00000000..37f853ae --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/utils/Reflector.java @@ -0,0 +1,435 @@ +package top.niunaijun.blackbox.utils; + + + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Member; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; + +/** + * Created by qiaopu on 2018/4/26. + */ +public class Reflector { + public static final String LOG_TAG ="Reflector"; + + protected Class mType; + protected Object mCaller; + protected Constructor mConstructor; + protected Field mField; + protected Method mMethod; + + + public static Reflector on(String name) throws Exception { + return on(name, true, Reflector.class.getClassLoader()); + } + + public static Reflector on(String name, boolean initialize) throws Exception { + return on(name, initialize, Reflector.class.getClassLoader()); + } + + public static Reflector on(String name, boolean initialize, ClassLoader loader) throws Exception { + try { + return on(Class.forName(name, initialize, loader)); + } catch (Throwable e) { + throw new Exception("Oops!", e); + } + } + + public static Reflector on(Class type) { + Reflector reflector = new Reflector(); + reflector.mType = type; + return reflector; + } + + public static Reflector with(Object caller) throws Exception { + return on(caller.getClass()).bind(caller); + } + + protected Reflector() { + + } + + public Reflector constructor(Class... parameterTypes) throws Exception { + try { + mConstructor = mType.getDeclaredConstructor(parameterTypes); + mConstructor.setAccessible(true); + mField = null; + mMethod = null; + return this; + } catch (Throwable e) { + throw new Exception("Oops!", e); + } + } + + @SuppressWarnings("unchecked") + public R newInstance(Object... initargs) throws Exception { + if (mConstructor == null) { + throw new Exception("Constructor was null!"); + } + try { + return (R) mConstructor.newInstance(initargs); + } catch (InvocationTargetException e) { + throw new Exception("Oops!", e.getTargetException()); + } catch (Throwable e) { + throw new Exception("Oops!", e); + } + } + + protected Object checked(Object caller) throws Exception { + if (caller == null || mType.isInstance(caller)) { + return caller; + } + throw new Exception("Caller [" + caller + "] is not a instance of type [" + mType + "]!"); + } + + protected void check(Object caller, Member member, String name) throws Exception { + if (member == null) { + throw new Exception(name + " was null!"); + } + if (caller == null && !Modifier.isStatic(member.getModifiers())) { + throw new Exception("Need a caller!"); + } + checked(caller); + } + + public Reflector bind(Object caller) throws Exception { + mCaller = checked(caller); + return this; + } + + public Reflector unbind() { + mCaller = null; + return this; + } + + public Reflector field(String name) throws Exception { + try { + mField = findField(name); + mField.setAccessible(true); + mConstructor = null; + mMethod = null; + return this; + } catch (Throwable e) { + throw new Exception("Oops!", e); + } + } + + protected Field findField(String name) throws NoSuchFieldException { + try { + return mType.getField(name); + } catch (NoSuchFieldException e) { + for (Class cls = mType; cls != null; cls = cls.getSuperclass()) { + try { + return cls.getDeclaredField(name); + } catch (NoSuchFieldException ex) { + // Ignored + } + } + throw e; + } + } + + @SuppressWarnings("unchecked") + public R get() throws Exception { + return get(mCaller); + } + + @SuppressWarnings("unchecked") + public R get(Object caller) throws Exception { + check(caller, mField, "Field"); + try { + return (R) mField.get(caller); + } catch (Throwable e) { + throw new Exception("Oops!", e); + } + } + + public Reflector set(Object value) throws Exception { + return set(mCaller, value); + } + + public Reflector set(Object caller, Object value) throws Exception { + check(caller, mField, "Field"); + try { + mField.set(caller, value); + return this; + } catch (Throwable e) { + throw new Exception("Oops!", e); + } + } + + public Reflector method(String name, Class... parameterTypes) throws Exception { + try { + mMethod = findMethod(name, parameterTypes); + mMethod.setAccessible(true); + mConstructor = null; + mField = null; + return this; + } catch (NoSuchMethodException e) { + throw new Exception("Oops!", e); + } + } + + protected Method findMethod(String name, Class... parameterTypes) throws NoSuchMethodException { + try { + return mType.getMethod(name, parameterTypes); + } catch (NoSuchMethodException e) { + for (Class cls = mType; cls != null; cls = cls.getSuperclass()) { + try { + return cls.getDeclaredMethod(name, parameterTypes); + } catch (NoSuchMethodException ex) { + // Ignored + } + } + throw e; + } + } + + public R call(Object... args) throws Exception { + return callByCaller(mCaller, args); + } + + @SuppressWarnings("unchecked") + public R callByCaller(Object caller, Object... args) throws Exception { + check(caller, mMethod, "Method"); + try { + return (R) mMethod.invoke(caller, args); + } catch (InvocationTargetException e) { + throw new Exception("Oops!", e.getTargetException()); + } catch (Throwable e) { + throw new Exception("Oops!", e); + } + } + + public static class QuietReflector extends Reflector { + + protected Throwable mIgnored; + + public static QuietReflector on(String name) { + return on(name, true, QuietReflector.class.getClassLoader()); + } + + public static QuietReflector on(String name, boolean initialize) { + return on(name, initialize, QuietReflector.class.getClassLoader()); + } + + public static QuietReflector on(String name, boolean initialize, ClassLoader loader) { + Class cls = null; + try { + cls = Class.forName(name, initialize, loader); + return on(cls, null); + } catch (Throwable e) { +// Log.w(LOG_TAG, "Oops!", e); + return on(cls, e); + } + } + + public static QuietReflector on(Class type) { + return on(type, (type == null) ? new Exception("Type was null!") : null); + } + + private static QuietReflector on(Class type, Throwable ignored) { + QuietReflector reflector = new QuietReflector(); + reflector.mType = type; + reflector.mIgnored = ignored; + return reflector; + } + + public static QuietReflector with(Object caller) { + if (caller == null) { + return on((Class) null); + } + return on(caller.getClass()).bind(caller); + } + + protected QuietReflector() { + + } + + public Throwable getIgnored() { + return mIgnored; + } + + protected boolean skip() { + return skipAlways() || mIgnored != null; + } + + protected boolean skipAlways() { + return mType == null; + } + + @Override + public QuietReflector constructor(Class... parameterTypes) { + if (skipAlways()) { + return this; + } + try { + mIgnored = null; + super.constructor(parameterTypes); + } catch (Throwable e) { + mIgnored = e; +// Log.w(LOG_TAG, "Oops!", e); + } + return this; + } + + @Override + public R newInstance(Object... initargs) { + if (skip()) { + return null; + } + try { + mIgnored = null; + return super.newInstance(initargs); + } catch (Throwable e) { + mIgnored = e; +// Log.w(LOG_TAG, "Oops!", e); + } + return null; + } + + @Override + public QuietReflector bind(Object obj) { + if (skipAlways()) { + return this; + } + try { + mIgnored = null; + super.bind(obj); + } catch (Throwable e) { + mIgnored = e; +// Log.w(LOG_TAG, "Oops!", e); + } + return this; + } + + @Override + public QuietReflector unbind() { + super.unbind(); + return this; + } + + @Override + public QuietReflector field(String name) { + if (skipAlways()) { + return this; + } + try { + mIgnored = null; + super.field(name); + } catch (Throwable e) { + mIgnored = e; +// Log.w(LOG_TAG, "Oops!", e); + } + return this; + } + + @Override + public R get() { + if (skip()) { + return null; + } + try { + mIgnored = null; + return super.get(); + } catch (Throwable e) { + mIgnored = e; +// Log.w(LOG_TAG, "Oops!", e); + } + return null; + } + + @Override + public R get(Object caller) { + if (skip()) { + return null; + } + try { + mIgnored = null; + return super.get(caller); + } catch (Throwable e) { + mIgnored = e; +// Log.w(LOG_TAG, "Oops!", e); + } + return null; + } + + @Override + public QuietReflector set(Object value) { + if (skip()) { + return this; + } + try { + mIgnored = null; + super.set(value); + } catch (Throwable e) { + mIgnored = e; +// Log.w(LOG_TAG, "Oops!", e); + } + return this; + } + + @Override + public QuietReflector set(Object caller, Object value) { + if (skip()) { + return this; + } + try { + mIgnored = null; + super.set(caller, value); + } catch (Throwable e) { + mIgnored = e; +// Log.w(LOG_TAG, "Oops!", e); + } + return this; + } + + @Override + public QuietReflector method(String name, Class... parameterTypes) { + if (skipAlways()) { + return this; + } + try { + mIgnored = null; + super.method(name, parameterTypes); + } catch (Throwable e) { + mIgnored = e; +// Log.w(LOG_TAG, "Oops!", e); + } + return this; + } + + @Override + public R call(Object... args) { + if (skip()) { + return null; + } + try { + mIgnored = null; + return super.call(args); + } catch (Throwable e) { + mIgnored = e; +// Log.w(LOG_TAG, "Oops!", e); + } + return null; + } + + @Override + public R callByCaller(Object caller, Object... args) { + if (skip()) { + return null; + } + try { + mIgnored = null; + return super.callByCaller(caller, args); + } catch (Throwable e) { + mIgnored = e; +// Log.w(LOG_TAG, "Oops!", e); + } + return null; + } + } +} diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/utils/ShellUtils.java b/Bcore/src/main/java/top/niunaijun/blackbox/utils/ShellUtils.java new file mode 100644 index 00000000..f8624f7e --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/utils/ShellUtils.java @@ -0,0 +1,195 @@ +package top.niunaijun.blackbox.utils; + +import java.io.BufferedReader; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.List; + +public class ShellUtils { + public static final String COMMAND_SU = "su"; + public static final String COMMAND_SH = "sh"; + public static final String COMMAND_EXIT = "exit\n"; + public static final String COMMAND_LINE_END = "\n"; + + + private ShellUtils() { + throw new AssertionError(); + } + + + /** + * check whether has root permission + * + * @return + */ + public static boolean checkRootPermission() { + return execCommand("echo root", true, false).result == 0; + } + + + /** + * execute shell command, default return result msg + * + * @param command command + * @param isRoot whether need to run with root + * @return + * @see ShellUtils#execCommand(String[], boolean, boolean) + */ + public static CommandResult execCommand(String command, boolean isRoot) { + return execCommand(new String[]{command}, isRoot, true); + } + + + /** + * execute shell commands, default return result msg + * + * @param commands command list + * @param isRoot whether need to run with root + * @return + * @see ShellUtils#execCommand(String[], boolean, boolean) + */ + public static CommandResult execCommand(List commands, boolean isRoot) { + return execCommand(commands == null ? null : commands.toArray(new String[]{}), isRoot, true); + } + + /** + * execute shell commands, default return result msg + * + * @param commands command array + * @param isRoot whether need to run with root + * @return + * @see ShellUtils#execCommand(String[], boolean, boolean) + */ + public static CommandResult execCommand(String[] commands, boolean isRoot) { + return execCommand(commands, isRoot, true); + } + + + /** + * execute shell command + * + * @param command command + * @param isRoot whether need to run with root + * @param isNeedResultMsg whether need result msg + * @return + * @see ShellUtils#execCommand(String[], boolean, boolean) + */ + public static CommandResult execCommand(String command, boolean isRoot, boolean isNeedResultMsg) { + return execCommand(new String[]{command}, isRoot, isNeedResultMsg); + } + + + /** + * execute shell commands + * + * @param commands command list + * @param isRoot whether need to run with root + * @param isNeedResultMsg whether need result msg + * @return + * @see ShellUtils#execCommand(String[], boolean, boolean) + */ + public static CommandResult execCommand(List commands, boolean isRoot, boolean isNeedResultMsg) { + return execCommand(commands == null ? null : commands.toArray(new String[]{}), isRoot, isNeedResultMsg); + } + + /** + * execute shell commands + * + * @param commands command array + * @param isRoot whether need to run with root + * @param isNeedResultMsg whether need result msg + * @return
    + *
  • if isNeedResultMsg is false, {@link CommandResult#successMsg} is null and + *
  • if {@link CommandResult#result} is -1, there maybe some excepiton.
  • + *
+ */ + public static CommandResult execCommand(String[] commands, boolean isRoot, boolean isNeedResultMsg) { + int result = -1; + if (commands == null || commands.length == 0) { + return new CommandResult(result, null); + } + Process process = null; + BufferedReader successResult = null; + StringBuilder successMsg = null; + DataOutputStream os = null; + try { + process = Runtime.getRuntime().exec(isRoot ? COMMAND_SU : COMMAND_SH); + os = new DataOutputStream(process.getOutputStream()); + for (String command : commands) { + if (command == null) { + continue; + } + os.write(command.getBytes()); + os.writeBytes(COMMAND_LINE_END); + os.flush(); + } + os.writeBytes(COMMAND_EXIT); + os.flush(); + result = process.waitFor(); + if (isNeedResultMsg) { + successMsg = new StringBuilder(); + successResult = new BufferedReader(new InputStreamReader(process.getInputStream())); + String s; + while ((s = successResult.readLine()) != null) { + successMsg.append(s + "\n"); + } + } + } catch (IOException e) { + e.printStackTrace(); + } catch (Exception e) { + e.printStackTrace(); + } finally { + try { + if (os != null) { + os.close(); + } + if (successResult != null) { + successResult.close(); + } + } catch (IOException e) { + e.printStackTrace(); + } + if (process != null) { + process.destroy(); + } + } + return new CommandResult(result, successMsg == null ? null : successMsg.toString()); + } + + + /** + * result of command + *
    + *
  • {@link CommandResult#result} means result of command, 0 means normal, else means error, same to excute in + * linux shell
  • + *
  • {@link CommandResult#successMsg} means success message of command result
  • + *
+ * + * @author Trinea 2013-5-16 + */ + public static class CommandResult { + + + /** + * result of command + **/ + public int result; + /** + * success message of command result + **/ + public String successMsg; + + + + public CommandResult(int result) { + this.result = result; + } + + + public CommandResult(int result, String successMsg) { + this.result = result; + this.successMsg = successMsg; + } + } +} diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/utils/Slog.java b/Bcore/src/main/java/top/niunaijun/blackbox/utils/Slog.java new file mode 100644 index 00000000..5930753b --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/utils/Slog.java @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package top.niunaijun.blackbox.utils; + +import android.util.Log; + +/** + * @hide + */ +public final class Slog { + /** @hide */ public static final int LOG_ID_SYSTEM = 3; + + private Slog() { + } + + public static int v(String tag, String msg) { + return Log.println(Log.VERBOSE, tag, msg); + } + + public static int v(String tag, String msg, Throwable tr) { + return Log.println(Log.VERBOSE, tag, + msg + '\n' + Log.getStackTraceString(tr)); + } + + + public static int d(String tag, String msg) { + return Log.println(Log.DEBUG, tag, msg); + } + + + public static int d(String tag, String msg, Throwable tr) { + return Log.println(Log.DEBUG, tag, + msg + '\n' + Log.getStackTraceString(tr)); + } + + + public static int i(String tag, String msg) { + return Log.println(Log.INFO, tag, msg); + } + + public static int i(String tag, String msg, Throwable tr) { + return Log.println(Log.INFO, tag, + msg + '\n' + Log.getStackTraceString(tr)); + } + + + public static int w(String tag, String msg) { + return Log.println(Log.WARN, tag, msg); + } + + + public static int w(String tag, String msg, Throwable tr) { + return Log.println(Log.WARN, tag, + msg + '\n' + Log.getStackTraceString(tr)); + } + + public static int w(String tag, Throwable tr) { + return Log.println(Log.WARN, tag, Log.getStackTraceString(tr)); + } + + + public static int e(String tag, String msg) { + return Log.println(Log.ERROR, tag, msg); + } + + + public static int e(String tag, String msg, Throwable tr) { + return Log.println(Log.ERROR, tag, + msg + '\n' + Log.getStackTraceString(tr)); + } + + public static int println(int priority, String tag, String msg) { + return Log.println(priority, tag, msg); + } +} + diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/utils/compat/ApplicationThreadCompat.java b/Bcore/src/main/java/top/niunaijun/blackbox/utils/compat/ApplicationThreadCompat.java new file mode 100644 index 00000000..c921dce8 --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/utils/compat/ApplicationThreadCompat.java @@ -0,0 +1,17 @@ +package top.niunaijun.blackbox.utils.compat; + +import android.os.IBinder; +import android.os.IInterface; + +import mirror.android.app.ApplicationThreadNative; +import mirror.android.app.IApplicationThreadOreo; + +public class ApplicationThreadCompat { + + public static IInterface asInterface(IBinder binder) { + if (BuildCompat.isOreo()) { + return IApplicationThreadOreo.Stub.asInterface.call(binder); + } + return ApplicationThreadNative.asInterface.call(binder); + } +} diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/utils/compat/BuildCompat.java b/Bcore/src/main/java/top/niunaijun/blackbox/utils/compat/BuildCompat.java new file mode 100644 index 00000000..ad9082f0 --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/utils/compat/BuildCompat.java @@ -0,0 +1,145 @@ +package top.niunaijun.blackbox.utils.compat; + +import android.os.Build; + +public class BuildCompat { + + public static int getPreviewSDKInt() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + try { + return Build.VERSION.PREVIEW_SDK_INT; + } catch (Throwable e) { + // ignore + } + } + return 0; + } + + // 12 + public static boolean isS() { + return Build.VERSION.SDK_INT >= 31 || (Build.VERSION.SDK_INT >= 30 && Build.VERSION.PREVIEW_SDK_INT == 1); + } + + // 11 + public static boolean isR() { + return Build.VERSION.SDK_INT >= 30 || (Build.VERSION.SDK_INT >= 29 && Build.VERSION.PREVIEW_SDK_INT == 1); + } + + // 10 + public static boolean isQ() { + return Build.VERSION.SDK_INT >= 29 || (Build.VERSION.SDK_INT >= 28 && Build.VERSION.PREVIEW_SDK_INT == 1); + } + + // 9 + public static boolean isPie() { + return Build.VERSION.SDK_INT >= Build.VERSION_CODES.P || (Build.VERSION.SDK_INT >= 27 && Build.VERSION.PREVIEW_SDK_INT == 1); + } + + // 8 + public static boolean isOreo() { + return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O || (Build.VERSION.SDK_INT >= 25 && Build.VERSION.PREVIEW_SDK_INT == 1); + } + + // 7 + public static boolean isN() { + return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N || (Build.VERSION.SDK_INT >= 23 && Build.VERSION.PREVIEW_SDK_INT == 1); + } + + // 7.1 + public static boolean isN_MR1() { + return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1 || (Build.VERSION.SDK_INT >= 24 && Build.VERSION.PREVIEW_SDK_INT == 1); + } + + // 6 + public static boolean isM() { + return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M; + } + + // 5 + public static boolean isL() { + return Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP; + } + + // 5 + public static boolean isL_MR1() { + return Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1; + } + + public static boolean isSamsung() { + return "samsung".equalsIgnoreCase(Build.BRAND) || "samsung".equalsIgnoreCase(Build.MANUFACTURER); + } + + public static boolean isEMUI() { + if (Build.DISPLAY.toUpperCase().startsWith("EMUI")) { + return true; + } + String property = SystemPropertiesCompat.get("ro.build.version.emui"); + return property != null && property.contains("EmotionUI"); + } + + public static boolean isMIUI() { + return SystemPropertiesCompat.getInt("ro.miui.ui.version.code", 0) > 0; + } + + public static boolean isFlyme() { + return Build.DISPLAY.toLowerCase().contains("flyme"); + } + + public static boolean isColorOS() { + return SystemPropertiesCompat.isExist("ro.build.version.opporom") + || SystemPropertiesCompat.isExist("ro.rom.different.version"); + } + + public static boolean is360UI() { + String property = SystemPropertiesCompat.get("ro.build.uiversion"); + return property != null && property.toUpperCase().contains("360UI"); + } + + public static boolean isLetv() { + return Build.MANUFACTURER.equalsIgnoreCase("Letv"); + } + + public static boolean isVivo() { + return SystemPropertiesCompat.isExist("ro.vivo.os.build.display.id"); + } + + + private static ROMType sRomType; + + public static ROMType getROMType() { + if (sRomType == null) { + if (isEMUI()) { + sRomType = ROMType.EMUI; + } else if (isMIUI()) { + sRomType = ROMType.MIUI; + } else if (isFlyme()) { + sRomType = ROMType.FLYME; + } else if (isColorOS()) { + sRomType = ROMType.COLOR_OS; + } else if (is360UI()) { + sRomType = ROMType._360; + } else if (isLetv()) { + sRomType = ROMType.LETV; + } else if (isVivo()) { + sRomType = ROMType.VIVO; + } else if (isSamsung()) { + sRomType = ROMType.SAMSUNG; + } else { + sRomType = ROMType.OTHER; + } + } + return sRomType; + } + + public enum ROMType { + EMUI, + MIUI, + FLYME, + COLOR_OS, + LETV, + VIVO, + _360, + SAMSUNG, + OTHER + } +} \ No newline at end of file diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/utils/compat/BundleCompat.java b/Bcore/src/main/java/top/niunaijun/blackbox/utils/compat/BundleCompat.java new file mode 100644 index 00000000..ab43a8d9 --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/utils/compat/BundleCompat.java @@ -0,0 +1,62 @@ +package top.niunaijun.blackbox.utils.compat; + +import android.content.Intent; +import android.os.Build; +import android.os.Bundle; +import android.os.IBinder; +import android.os.Parcel; + +import mirror.android.os.BaseBundle; +import mirror.android.os.BundleICS; + +public class BundleCompat { + public static IBinder getBinder(Bundle bundle, String key) { + if (Build.VERSION.SDK_INT >= 18) { + return bundle.getBinder(key); + } else { + return mirror.android.os.Bundle.getIBinder.call(bundle, key); + } + } + + public static void putBinder(Bundle bundle, String key, IBinder value) { + if (Build.VERSION.SDK_INT >= 18) { + bundle.putBinder(key, value); + } else { + mirror.android.os.Bundle.putIBinder.call(bundle, key, value); + } + } + + public static void putBinder(Intent intent, String key, IBinder value) { + Bundle bundle = new Bundle(); + putBinder(bundle, "binder", value); + intent.putExtra(key, bundle); + } + + public static IBinder getBinder(Intent intent, String key) { + Bundle bundle = intent.getBundleExtra(key); + if (bundle != null) { + return getBinder(bundle, "binder"); + } + return null; + } + + public static void clearParcelledData(Bundle bundle) { + Parcel obtain = Parcel.obtain(); + obtain.writeInt(0); + obtain.setDataPosition(0); + Parcel parcel; + if (BaseBundle.REF.getClazz() != null) { + parcel = BaseBundle.mParcelledData.get(bundle); + if (parcel != null) { + parcel.recycle(); + } + BaseBundle.mParcelledData.set(bundle, obtain); + } else if (BundleICS.REF.getClazz() != null) { + parcel = BundleICS.mParcelledData.get(bundle); + if (parcel != null) { + parcel.recycle(); + } + BundleICS.mParcelledData.set(bundle, obtain); + } + } +} diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/utils/compat/ContentProviderCompat.java b/Bcore/src/main/java/top/niunaijun/blackbox/utils/compat/ContentProviderCompat.java new file mode 100644 index 00000000..5444f3cf --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/utils/compat/ContentProviderCompat.java @@ -0,0 +1,89 @@ +package top.niunaijun.blackbox.utils.compat; + +import android.content.ContentProviderClient; +import android.content.Context; +import android.net.Uri; +import android.os.Build; +import android.os.Build.VERSION; +import android.os.Bundle; +import android.os.RemoteException; +import android.os.SystemClock; + +public class ContentProviderCompat { + + public static Bundle call(Context context, Uri uri, String method, String arg, Bundle extras, int retryCount) throws IllegalAccessException { + if (VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) { + return context.getContentResolver().call(uri, method, arg, extras); + } + ContentProviderClient client = acquireContentProviderClientRetry(context, uri, retryCount); + try { + if (client == null) { + throw new IllegalAccessException(); + } + return client.call(method, arg, extras); + } catch (RemoteException e) { + throw new IllegalAccessException(e.getMessage()); + } finally { + releaseQuietly(client); + } + } + + + private static ContentProviderClient acquireContentProviderClient(Context context, Uri uri) { + try { + if (VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { + return context.getContentResolver().acquireUnstableContentProviderClient(uri); + } + return context.getContentResolver().acquireContentProviderClient(uri); + } catch (SecurityException e) { + e.printStackTrace(); + } + return null; + } + + public static ContentProviderClient acquireContentProviderClientRetry(Context context, Uri uri, int retryCount) { + ContentProviderClient client = acquireContentProviderClient(context, uri); + if (client == null) { + int retry = 0; + while (retry < retryCount && client == null) { + SystemClock.sleep(100); + retry++; + client = acquireContentProviderClient(context, uri); + } + } + return client; + } + + public static ContentProviderClient acquireContentProviderClientRetry(Context context, String name, int retryCount) { + ContentProviderClient client = acquireContentProviderClient(context, name); + if (client == null) { + int retry = 0; + while (retry < retryCount && client == null) { + SystemClock.sleep(100); + retry++; + client = acquireContentProviderClient(context, name); + } + } + return client; + } + + private static ContentProviderClient acquireContentProviderClient(Context context, String name) { + if (VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { + return context.getContentResolver().acquireUnstableContentProviderClient(name); + } + return context.getContentResolver().acquireContentProviderClient(name); + } + + private static void releaseQuietly(ContentProviderClient client) { + if (client != null) { + try { + if (VERSION.SDK_INT >= Build.VERSION_CODES.N) { + client.close(); + } else { + client.release(); + } + } catch (Exception ignored) { + } + } + } +} \ No newline at end of file diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/utils/compat/ContextCompat.java b/Bcore/src/main/java/top/niunaijun/blackbox/utils/compat/ContextCompat.java new file mode 100644 index 00000000..85e4e0ab --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/utils/compat/ContextCompat.java @@ -0,0 +1,43 @@ +package top.niunaijun.blackbox.utils.compat; + +import android.content.Context; +import android.content.ContextWrapper; + +import mirror.android.app.ContextImpl; +import top.niunaijun.blackbox.BlackBoxCore; + +/** + * Created by Milk on 3/31/21. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + */ +public class ContextCompat { + public static final String TAG = "ContextFixer"; + + public static void fix(Context context) { + try { + int deep = 0; + while (context instanceof ContextWrapper) { + context = ((ContextWrapper) context).getBaseContext(); + deep++; + if (deep >= 10) { + return; + } + } + ContextImpl.mPackageManager.set(context, null); + try { + context.getPackageManager(); + } catch (Throwable e) { + e.printStackTrace(); + } + + ContextImpl.mBasePackageName.set(context, BlackBoxCore.getHostPkg()); + ContextImpl.mOpPackageName.set(context, BlackBoxCore.getHostPkg()); + } catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/utils/compat/DexFileCompat.java b/Bcore/src/main/java/top/niunaijun/blackbox/utils/compat/DexFileCompat.java new file mode 100644 index 00000000..a017162f --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/utils/compat/DexFileCompat.java @@ -0,0 +1,89 @@ +package top.niunaijun.blackbox.utils.compat; + +import java.util.ArrayList; +import java.util.List; + +import dalvik.system.DexFile; +import top.niunaijun.blackbox.utils.Reflector; + +/** + * Created by Milk on 2021/5/16. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + */ +public class DexFileCompat { + + public static List getCookies(ClassLoader classLoader) { + List cookies = new ArrayList<>(); + List dexFiles = getDexFiles(classLoader); + for (DexFile dexFile : dexFiles) { + cookies.addAll(getCookies(dexFile)); + } + return cookies; + } + + public static List getCookies(DexFile dexFile) { + List cookies = new ArrayList<>(); + if (dexFile == null) + return cookies; + try { + Object object = Reflector.with(dexFile) + .field("mCookie") + .get(); + if (BuildCompat.isM()) { + for (long l : (long[]) object) { + cookies.add(l); + } + } else { + cookies.add((long) object); + } + } catch (Exception e) { + e.printStackTrace(); + } + return cookies; + } + + private static List getDexFiles(ClassLoader classLoader) { + List dexFiles = new ArrayList<>(); + Object[] dexElements = getDexElements(classLoader); + for (Object dexElement : dexElements) { + try { + dexFiles.add(Reflector.with(dexElement) + .field("dexFile") + .get()); + } catch (Exception e) { + e.printStackTrace(); + } + } + return dexFiles; + } + + private static Object[] getDexElements(ClassLoader classLoader) { + Object dexPathList = getDexPathList(classLoader); + if (dexPathList == null) { + return new Object[]{}; + } + try { + return Reflector.with(dexPathList) + .field("dexElements") + .get(); + } catch (Exception e) { + e.printStackTrace(); + } + return new Object[]{}; + } + + private static Object getDexPathList(ClassLoader classLoader) { + try { + return Reflector.on("dalvik.system.BaseDexClassLoader") + .field("pathList") + .get(classLoader); + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } +} diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/utils/compat/PackageParserCompat.java b/Bcore/src/main/java/top/niunaijun/blackbox/utils/compat/PackageParserCompat.java new file mode 100644 index 00000000..9eeff6c4 --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/utils/compat/PackageParserCompat.java @@ -0,0 +1,56 @@ +package top.niunaijun.blackbox.utils.compat; + +import android.content.pm.PackageParser; +import android.content.pm.PackageParser.Package; +import android.util.DisplayMetrics; +import java.io.File; + +import mirror.android.content.pm.PackageParserLollipop; +import mirror.android.content.pm.PackageParserLollipop22; +import mirror.android.content.pm.PackageParserMarshmallow; +import mirror.android.content.pm.PackageParserNougat; +import mirror.android.content.pm.PackageParserPie; + +public class PackageParserCompat { + + public static PackageParser createParser(File packageFile) { + if (BuildCompat.isM()) { + return PackageParserMarshmallow.constructor.newInstance(); + } else if (BuildCompat.isL_MR1()) { + return PackageParserLollipop22.constructor.newInstance(); + } else if (BuildCompat.isL()) { + return PackageParserLollipop.constructor.newInstance(); + } else { + return mirror.android.content.pm.PackageParser.constructor.newInstance(packageFile.getAbsolutePath()); + } + } + + public static Package parsePackage(PackageParser parser, File packageFile, int flags) throws Throwable { + if (BuildCompat.isM()) { + return PackageParserMarshmallow.parsePackage.callWithException(parser, packageFile, flags); + } else if (BuildCompat.isL_MR1()) { + return PackageParserLollipop22.parsePackage.callWithException(parser, packageFile, flags); + } else if (BuildCompat.isL()) { + return PackageParserLollipop.parsePackage.callWithException(parser, packageFile, flags); + } else { + return mirror.android.content.pm.PackageParser.parsePackage.callWithException(parser, packageFile, null, + new DisplayMetrics(), flags); + } + } + + public static void collectCertificates(PackageParser parser, Package p, int flags) throws Throwable { + if (BuildCompat.isPie()) { + PackageParserPie.collectCertificates.callWithException(p, true/*skipVerify*/); + } else if (BuildCompat.isN()) { + PackageParserNougat.collectCertificates.callWithException(p, flags); + } else if (BuildCompat.isM()) { + PackageParserMarshmallow.collectCertificates.callWithException(parser, p, flags); + } else if (BuildCompat.isL_MR1()) { + PackageParserLollipop22.collectCertificates.callWithException(parser, p, flags); + } else if (BuildCompat.isL()) { + PackageParserLollipop.collectCertificates.callWithException(parser, p, flags); + } else { + mirror.android.content.pm.PackageParser.collectCertificates.call(parser, p, flags); + } + } +} diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/utils/compat/ParceledListSliceCompat.java b/Bcore/src/main/java/top/niunaijun/blackbox/utils/compat/ParceledListSliceCompat.java new file mode 100644 index 00000000..4446236d --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/utils/compat/ParceledListSliceCompat.java @@ -0,0 +1,32 @@ +package top.niunaijun.blackbox.utils.compat; + +import java.lang.reflect.Method; +import java.util.List; + +import mirror.android.content.pm.ParceledListSlice; +import mirror.android.content.pm.ParceledListSliceJBMR2; + +public class ParceledListSliceCompat { + + public static boolean isReturnParceledListSlice(Method method) { + return method != null && method.getReturnType() == ParceledListSlice.REF.getClazz(); + } + + public static boolean isParceledListSlice(Object obj) { + return obj != null && obj.getClass() == ParceledListSlice.REF.getClazz(); + } + + public static Object create(List list) { + if (ParceledListSliceJBMR2.constructor != null) { + return ParceledListSliceJBMR2.constructor.newInstance(list); + } else { + Object slice = ParceledListSlice.constructor.newInstance(); + for (Object item : list) { + ParceledListSlice.append.call(slice, item); + } + ParceledListSlice.setLastSlice.call(slice, true); + return slice; + } + } + +} diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/utils/compat/SystemPropertiesCompat.java b/Bcore/src/main/java/top/niunaijun/blackbox/utils/compat/SystemPropertiesCompat.java new file mode 100644 index 00000000..8e344fc1 --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/utils/compat/SystemPropertiesCompat.java @@ -0,0 +1,47 @@ +package top.niunaijun.blackbox.utils.compat; + +import android.text.TextUtils; + +import top.niunaijun.blackbox.utils.Reflector; + + +public class SystemPropertiesCompat { + + public static String get(String key, String def) { + try { + return (String) Reflector.on("android.os.SystemProperties") + .method("get", String.class, String.class) + .call(key, def); + } catch (Exception e) { + e.printStackTrace(); + } + return def; + } + + public static String get(String key) { + try { + return (String) Reflector.on("android.os.SystemProperties") + .method("get", String.class) + .call(key); + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + public static boolean isExist(String key) { + return !TextUtils.isEmpty(get(key)); + } + + public static int getInt(String key, int def) { + try { + return (int) Reflector.on("android.os.SystemProperties") + .method("getInt", String.class, int.class) + .call(key, def); + } catch (Exception e) { + e.printStackTrace(); + } + return def; + } + +} diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/utils/provider/ProviderCall.java b/Bcore/src/main/java/top/niunaijun/blackbox/utils/provider/ProviderCall.java new file mode 100644 index 00000000..f7e94378 --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/utils/provider/ProviderCall.java @@ -0,0 +1,103 @@ +package top.niunaijun.blackbox.utils.provider; + +import android.content.Context; +import android.net.Uri; +import android.os.Bundle; +import android.os.Parcelable; + +import java.io.Serializable; + +import top.niunaijun.blackbox.utils.compat.ContentProviderCompat; +import top.niunaijun.blackbox.BlackBoxCore; + +/** + * Created by Milk on 4/1/21. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + */ +public class ProviderCall { + public static Bundle callSafely(String authority, String methodName, String arg, Bundle bundle) { + try { + return call(authority, BlackBoxCore.get().getContext(), methodName, arg, bundle, 5); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + return null; + } + + public static Bundle call(String authority, Context context, String method, String arg, Bundle bundle, int retryCount) throws IllegalAccessException { + Uri uri = Uri.parse("content://" + authority); + return ContentProviderCompat.call(context, uri, method, arg, bundle, retryCount); + } + + public static final class Builder { + + private Context context; + + private Bundle bundle = new Bundle(); + + private String method; + private String auth; + private String arg; + private int retryCount = 5; + + public Builder(Context context, String auth) { + this.context = context; + this.auth = auth; + } + + public Builder methodName(String name) { + this.method = name; + return this; + } + + public Builder arg(String arg) { + this.arg = arg; + return this; + } + + public Builder addArg(String key, Object value) { + if (value != null) { + if (value instanceof Boolean) { + bundle.putBoolean(key, (Boolean) value); + } else if (value instanceof Integer) { + bundle.putInt(key, (Integer) value); + } else if (value instanceof String) { + bundle.putString(key, (String) value); + } else if (value instanceof Serializable) { + bundle.putSerializable(key, (Serializable) value); + } else if (value instanceof Bundle) { + bundle.putBundle(key, (Bundle) value); + } else if (value instanceof Parcelable) { + bundle.putParcelable(key, (Parcelable) value); + } else if (value instanceof int[]) { + bundle.putIntArray(key, (int[]) value); + } else { + throw new IllegalArgumentException("Unknown type " + value.getClass() + " in Bundle."); + } + } + return this; + } + + public Builder retry(int retryCount) { + this.retryCount = retryCount; + return this; + } + + public Bundle call() throws IllegalAccessException { + return ProviderCall.call(auth, context, method, arg, bundle, retryCount); + } + + public Bundle callSafely() { + try { + return call(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + return null; + } + } +} diff --git a/Bcore/src/main/jni/Android.mk b/Bcore/src/main/jni/Android.mk new file mode 100644 index 00000000..1345edbf --- /dev/null +++ b/Bcore/src/main/jni/Android.mk @@ -0,0 +1,40 @@ +LOCAL_PATH := $(call my-dir) +MAIN_LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +ifeq ($(TARGET_ARCH_ABI),arm64-v8a) + LOCAL_MODULE := vm64 + LOCAL_CFLAGS := -DCORE_SO_NAME=\"libvm64.so\" +else + LOCAL_MODULE := vm + LOCAL_CFLAGS := -DCORE_SO_NAME=\"libvm.so\" +endif + +LOCAL_CFLAGS += -Wno-error=format-security -fpermissive -O2 +LOCAL_CFLAGS += -DLOG_TAG=\"VM++\" +LOCAL_CFLAGS += -fno-rtti -fno-exceptions +LOCAL_CPPFLAGS += -std=c++11 + +LOCAL_C_INCLUDES += $(MAIN_LOCAL_PATH) + +LOCAL_SRC_FILES := JniHook/JniHook.cpp \ + Utils/fake_dlfcn.cpp \ + Utils/HexDump.cpp \ + Utils/PointerCheck.cpp \ + IO.cpp \ + Substrate/hde64.c \ + Substrate/SubstrateDebug.cpp \ + Substrate/SubstrateHook.cpp \ + Substrate/SubstratePosixMemory.cpp \ + Substrate/And64InlineHook.cpp \ + Hook/BaseHook.cpp \ + Hook/RuntimeHook.cpp \ + Hook/VMClassLoaderHook.cpp \ + Hook/UnixFileSystemHook.cpp \ + Hook/BinderHook.cpp \ + DexDump.cpp \ + VmCore.cpp \ + +LOCAL_LDLIBS := -llog -latomic + +include $(BUILD_SHARED_LIBRARY) \ No newline at end of file diff --git a/Bcore/src/main/jni/Application.mk b/Bcore/src/main/jni/Application.mk new file mode 100644 index 00000000..e0b55346 --- /dev/null +++ b/Bcore/src/main/jni/Application.mk @@ -0,0 +1,6 @@ +APP_ABI := 'armeabi-v7a', 'arm64-v8a', 'armeabi', 'x86' +APP_PLATFORM := android-21 +APP_STL := c++_static +APP_OPTIM := release +VA_ROOT := $(call my-dir) +NDK_MODULE_PATH := $(NDK_MODULE_PATH):$(VA_ROOT) \ No newline at end of file diff --git a/Bcore/src/main/jni/DexDump.cpp b/Bcore/src/main/jni/DexDump.cpp new file mode 100644 index 00000000..9a092164 --- /dev/null +++ b/Bcore/src/main/jni/DexDump.cpp @@ -0,0 +1,89 @@ +// +// Created by Milk on 2021/5/16. +// + +#include "DexDump.h" +#include "Utils/HexDump.h" +#include "Log.h" +#include "VmCore.h" +#include "Utils/PointerCheck.h" + +#include +#include +#include +#include +#include "unistd.h" + +using namespace std; +static int beginOffset = -2; +static int sizeOffset = -2; + + +void init(JNIEnv *env) { + jlongArray emptyCookie = VmCore::loadEmptyDex(env); + jsize arrSize = env->GetArrayLength(emptyCookie); + if (env->ExceptionCheck() == JNI_TRUE) { + return; + } + jlong* long_data = env->GetLongArrayElements(emptyCookie, nullptr); + + for (int i = 0; i < arrSize; ++i) { + jlong cookie = long_data[i]; + if (cookie == 0) { + continue; + } + auto dex = reinterpret_cast(cookie); + for (int ii = 0; ii < 10; ++ii) { + auto value = *(size_t *) (dex + ii * sizeof(size_t)); + if (value == 1872) { + sizeOffset = ii; + beginOffset = sizeOffset - 1; + // auto dexBegin = *(size_t *) (dex + beginOffset * sizeof(size_t)); + // HexDump(reinterpret_cast(dexBegin), 10, 0); + env->ReleaseLongArrayElements(emptyCookie, long_data, 0); + return; + } + } + } + env->ReleaseLongArrayElements(emptyCookie, long_data, 0); + beginOffset = -1; + sizeOffset = -1; +} + +void DexDump::dumpDex(JNIEnv *env, jlong cookie, jstring dir) { + if (beginOffset == -2 || sizeOffset == -2) { + init(env); + } + if (beginOffset == -1 || sizeOffset == -1) { + ALOGD("dumpDex not support!"); + return; + } + char magic[8] = {0x64, 0x65, 0x78, 0x0a, 0x30, 0x33, 0x35, 0x00}; + auto base = reinterpret_cast(cookie); + auto begin = *(size_t *) (base + beginOffset * sizeof(size_t)); + auto size = *(size_t *) (base + sizeOffset * sizeof(size_t)); + if (!PointerCheck::check(reinterpret_cast(begin))) { + return; + } + + auto dirC = env->GetStringUTFChars(dir, 0); + + auto buffer = malloc(size); + memcpy(buffer, reinterpret_cast(begin), size); + // fix magic + memcpy(buffer, magic, sizeof(magic)); + + char path[1024]; + sprintf(path, "%s/dex_%ld.dex", dirC, size); + auto fd = open(path, O_CREAT | O_WRONLY, 0600); + ssize_t w = write(fd, buffer, size); + fsync(fd); + if (w > 0) { + ALOGD("dump dex ======> %s", path); + } else { + remove(path); + } + close(fd); + free(buffer); + env->ReleaseStringUTFChars(dir, dirC); +} diff --git a/Bcore/src/main/jni/DexDump.h b/Bcore/src/main/jni/DexDump.h new file mode 100644 index 00000000..4323e5b1 --- /dev/null +++ b/Bcore/src/main/jni/DexDump.h @@ -0,0 +1,19 @@ +// +// Created by Milk on 2021/5/16. +// + +#ifndef BLACKBOX_DEXDUMP_H +#define BLACKBOX_DEXDUMP_H +#include + +#define HOOK_FUN(ret, func, ...) \ + ret (*orig_##func)(__VA_ARGS__); \ + ret new_##func(__VA_ARGS__) + +class DexDump { +public: + static void dumpDex(JNIEnv *env, jlong cookie, jstring dir); +}; + + +#endif //BLACKBOX_DEXDUMP_H diff --git a/Bcore/src/main/jni/Hook/BaseHook.cpp b/Bcore/src/main/jni/Hook/BaseHook.cpp new file mode 100644 index 00000000..01a6901f --- /dev/null +++ b/Bcore/src/main/jni/Hook/BaseHook.cpp @@ -0,0 +1,10 @@ +// +// Created by Milk on 4/10/21. +// + +#include "BaseHook.h" + + +void BaseHook::init(JNIEnv *env) { + +} diff --git a/Bcore/src/main/jni/Hook/BaseHook.h b/Bcore/src/main/jni/Hook/BaseHook.h new file mode 100644 index 00000000..cbda4e9c --- /dev/null +++ b/Bcore/src/main/jni/Hook/BaseHook.h @@ -0,0 +1,17 @@ +// +// Created by Milk on 4/9/21. +// + +#ifndef VIRTUALM_BASEHOOK_H +#define VIRTUALM_BASEHOOK_H + +#include +#include + +class BaseHook { +public: + static void init(JNIEnv *env); +}; + + +#endif //VIRTUALM_BASEHOOK_H diff --git a/Bcore/src/main/jni/Hook/BinderHook.cpp b/Bcore/src/main/jni/Hook/BinderHook.cpp new file mode 100644 index 00000000..fbfc2c08 --- /dev/null +++ b/Bcore/src/main/jni/Hook/BinderHook.cpp @@ -0,0 +1,23 @@ +// +// Created by Milk on 4/25/21. +// + +#include "BinderHook.h" +#include +#include +#include "UnixFileSystemHook.h" +#import "JniHook/JniHook.h" + + + +HOOK_JNI(jint, getCallingUid, JNIEnv *env, jobject obj) { + int orig = orig_getCallingUid(env, obj); + return VmCore::getCallingUid(env, orig); +} + + +void BinderHook::init(JNIEnv *env) { + const char *clazz = "android/os/Binder"; +// JniHook::HookJniFun(env, clazz, "getCallingUid", "()I", (void *) new_getCallingUid, +// (void **) (&orig_getCallingUid), true); +} \ No newline at end of file diff --git a/Bcore/src/main/jni/Hook/BinderHook.h b/Bcore/src/main/jni/Hook/BinderHook.h new file mode 100644 index 00000000..ee5e18c6 --- /dev/null +++ b/Bcore/src/main/jni/Hook/BinderHook.h @@ -0,0 +1,16 @@ +// +// Created by Milk on 4/25/21. +// + +#ifndef BLACKBOX_BINDERHOOK_H +#define BLACKBOX_BINDERHOOK_H + + +#include "BaseHook.h" + +class BinderHook : public BaseHook{ +public: + static void init(JNIEnv *env); +}; + +#endif //BLACKBOX_BINDERHOOK_H diff --git a/Bcore/src/main/jni/Hook/RuntimeHook.cpp b/Bcore/src/main/jni/Hook/RuntimeHook.cpp new file mode 100644 index 00000000..aa680ea0 --- /dev/null +++ b/Bcore/src/main/jni/Hook/RuntimeHook.cpp @@ -0,0 +1,39 @@ +// +// Created by Milk on 5/5/21. +// + +#include "RuntimeHook.h" +#import "JniHook/JniHook.h" +#include "VmCore.h" + +HOOK_JNI(jstring, nativeLoad, JNIEnv *env, jobject obj, jstring name, jobject class_loader) { + const char *nameC = env->GetStringUTFChars(name, JNI_FALSE); + ALOGD("nativeLoad: %s", nameC); + jstring result = orig_nativeLoad(env, obj, name, class_loader); + env->ReleaseStringUTFChars(name, nameC); + return result; +} + +HOOK_JNI(jstring, nativeLoad2, JNIEnv *env, jobject obj, jstring name, jobject class_loader, + jobject caller) { + const char *nameC = env->GetStringUTFChars(name, JNI_FALSE); + ALOGD("nativeLoad: %s", nameC); + jstring result = orig_nativeLoad2(env, obj, name, class_loader, caller); + env->ReleaseStringUTFChars(name, nameC); + return result; +} + +void RuntimeHook::init(JNIEnv *env) { + const char *className = "java/lang/Runtime"; + if (VmCore::getApiLevel() >= __ANDROID_API_Q__) { + JniHook::HookJniFun(env, className, "nativeLoad", + "(Ljava/lang/String;Ljava/lang/ClassLoader;Ljava/lang/Class;)Ljava/lang/String;", + (void *) new_nativeLoad2, + (void **) (&orig_nativeLoad2), true); + } else { + JniHook::HookJniFun(env, className, "nativeLoad", + "(Ljava/lang/String;Ljava/lang/ClassLoader;)Ljava/lang/String;", + (void *) new_nativeLoad, + (void **) (&orig_nativeLoad), true); + } +} diff --git a/Bcore/src/main/jni/Hook/RuntimeHook.h b/Bcore/src/main/jni/Hook/RuntimeHook.h new file mode 100644 index 00000000..9028ae33 --- /dev/null +++ b/Bcore/src/main/jni/Hook/RuntimeHook.h @@ -0,0 +1,18 @@ +// +// Created by Milk on 5/5/21. +// + +#ifndef BLACKBOX_RUNTIMEHOOK_H +#define BLACKBOX_RUNTIMEHOOK_H + + +#include "BaseHook.h" +#include + +class RuntimeHook : public BaseHook { +public: + static void init(JNIEnv *env); +}; + + +#endif //BLACKBOX_RUNTIMEHOOK_H diff --git a/Bcore/src/main/jni/Hook/UnixFileSystemHook.cpp b/Bcore/src/main/jni/Hook/UnixFileSystemHook.cpp new file mode 100644 index 00000000..4c9300de --- /dev/null +++ b/Bcore/src/main/jni/Hook/UnixFileSystemHook.cpp @@ -0,0 +1,137 @@ +// +// Created by Milk on 4/9/21. +// + +#include +#include "UnixFileSystemHook.h" +#import "JniHook/JniHook.h" +#include "BaseHook.h" + +/* + * Class: java_io_UnixFileSystem + * Method: canonicalize0 + * Signature: (Ljava/lang/String;)Ljava/lang/String; + */ +HOOK_JNI(jstring, canonicalize0, JNIEnv *env, jobject obj, jstring path) { + jstring redirect = IO::redirectPath(env, path); + return orig_canonicalize0(env, obj, redirect); +} + +/* + * Class: java_io_UnixFileSystem + * Method: getBooleanAttributes0 + * Signature: (Ljava/lang/String;)I + */ +HOOK_JNI(jint, getBooleanAttributes0, JNIEnv *env, jobject obj, jstring abspath) { + jstring redirect = IO::redirectPath(env, abspath); + return orig_getBooleanAttributes0(env, obj, redirect); +} + +/* + * Class: java_io_UnixFileSystem + * Method: getLastModifiedTime0 + * Signature: (Ljava/io/File;)J + */ +HOOK_JNI(jlong, getLastModifiedTime0, JNIEnv *env, jobject obj, jobject path) { + jobject redirect = IO::redirectPath(env, path); + return orig_getLastModifiedTime0(env, obj, redirect); +} + +/* + * Class: java_io_UnixFileSystem + * Method: setPermission0 + * Signature: (Ljava/io/File;IZZ)Z + */ +HOOK_JNI(jboolean, setPermission0, JNIEnv *env, jobject obj, jobject file, jint access, + jboolean enable, jboolean owneronly) { + jobject redirect = IO::redirectPath(env, file); + return orig_setPermission0(env, obj, redirect, access, enable, owneronly); +} + +/* + * Class: java_io_UnixFileSystem + * Method: createFileExclusively0 + * Signature: (Ljava/lang/String;)Z + */ +HOOK_JNI(jboolean, createFileExclusively0, JNIEnv *env, jobject obj, jstring path) { + jstring redirect = IO::redirectPath(env, path); + return orig_createFileExclusively0(env, obj, redirect); +} + +/* + * Class: java_io_UnixFileSystem + * Method: list0 + * Signature: (Ljava/io/File;)[Ljava/lang/String; + */ +HOOK_JNI(jobjectArray, list0, JNIEnv *env, jobject obj, jobject file) { + jobject redirect = IO::redirectPath(env, file); + return orig_list0(env, obj, redirect); +} + +/* + * Class: java_io_UnixFileSystem + * Method: createDirectory0 + * Signature: (Ljava/io/File;)Z + */ +HOOK_JNI(jboolean, createDirectory0, JNIEnv *env, jobject obj, jobject path) { + jobject redirect = IO::redirectPath(env, path); + return orig_createDirectory0(env, obj, redirect); +} + +/* + * Class: java_io_UnixFileSystem + * Method: setLastModifiedTime0 + * Signature: (Ljava/io/File;J)Z + */ +HOOK_JNI(jboolean, setLastModifiedTime0, JNIEnv *env, jobject obj, jobject file, jobject time) { + jobject redirect = IO::redirectPath(env, file); + return orig_setLastModifiedTime0(env, obj, redirect, time); +} + +/* + * Class: java_io_UnixFileSystem + * Method: setReadOnly0 + * Signature: (Ljava/io/File;)Z + */ +HOOK_JNI(jboolean, setReadOnly0, JNIEnv *env, jobject obj, jobject file) { + jobject redirect = IO::redirectPath(env, file); + return orig_setReadOnly0(env, obj, redirect); +} + +/* + * Class: java_io_UnixFileSystem + * Method: getSpace0 + * Signature: (Ljava/io/File;I)J + */ +HOOK_JNI(jboolean, getSpace0, JNIEnv *env, jobject obj, jobject file, jint t) { + jobject redirect = IO::redirectPath(env, file); + return orig_getSpace0(env, obj, redirect, t); +} + +void UnixFileSystemHook::init(JNIEnv *env) { + const char *className = "java/io/UnixFileSystem"; + JniHook::HookJniFun(env, className, "canonicalize0", "(Ljava/lang/String;)Ljava/lang/String;", + (void *) new_canonicalize0, (void **) (&orig_canonicalize0), false); + JniHook::HookJniFun(env, className, "getBooleanAttributes0", "(Ljava/lang/String;)I", + (void *) new_getBooleanAttributes0, + (void **) (&orig_getBooleanAttributes0), false); + JniHook::HookJniFun(env, className, "getLastModifiedTime0", "(Ljava/io/File;)J", + (void *) new_getLastModifiedTime0, (void **) (&orig_getLastModifiedTime0), + false); + JniHook::HookJniFun(env, className, "setPermission0", "(Ljava/io/File;IZZ)Z", + (void *) new_setPermission0, (void **) (&orig_setPermission0), false); + JniHook::HookJniFun(env, className, "createFileExclusively0", "(Ljava/lang/String;)Z", + (void *) new_createFileExclusively0, + (void **) (&orig_createFileExclusively0), false); + JniHook::HookJniFun(env, className, "list0", "(Ljava/io/File;)[Ljava/lang/String;", + (void *) new_list0, (void **) (&orig_list0), false); + JniHook::HookJniFun(env, className, "createDirectory0", "(Ljava/io/File;)Z", + (void *) new_createDirectory0, (void **) (&orig_createDirectory0), false); + JniHook::HookJniFun(env, className, "setLastModifiedTime0", "(Ljava/io/File;J)Z", + (void *) new_setLastModifiedTime0, (void **) (&orig_setLastModifiedTime0), + false); + JniHook::HookJniFun(env, className, "setReadOnly0", "(Ljava/io/File;)Z", + (void *) new_setReadOnly0, (void **) (&orig_setReadOnly0), false); + JniHook::HookJniFun(env, className, "getSpace0", "(Ljava/io/File;I)J", + (void *) new_getSpace0, (void **) (&orig_getSpace0), false); +} \ No newline at end of file diff --git a/Bcore/src/main/jni/Hook/UnixFileSystemHook.h b/Bcore/src/main/jni/Hook/UnixFileSystemHook.h new file mode 100644 index 00000000..5c5d6271 --- /dev/null +++ b/Bcore/src/main/jni/Hook/UnixFileSystemHook.h @@ -0,0 +1,17 @@ +// +// Created by Milk on 4/9/21. +// + +#ifndef VIRTUALM_UNIXFILESYSTEMHOOK_H +#define VIRTUALM_UNIXFILESYSTEMHOOK_H + + +#include "BaseHook.h" + +class UnixFileSystemHook : public BaseHook { +public: + static void init(JNIEnv *env); +}; + + +#endif //VIRTUALM_UNIXFILESYSTEMHOOK_H diff --git a/Bcore/src/main/jni/Hook/VMClassLoaderHook.cpp b/Bcore/src/main/jni/Hook/VMClassLoaderHook.cpp new file mode 100644 index 00000000..df13de5c --- /dev/null +++ b/Bcore/src/main/jni/Hook/VMClassLoaderHook.cpp @@ -0,0 +1,41 @@ +// +// Created by Milk on 2021/5/5. +// + +// +// Created by Milk on 5/5/21. +// + +#include +#include "VMClassLoaderHook.h" +#import "JniHook/JniHook.h" +static bool hideXposedClass = false; + +HOOK_JNI(jobject, findLoadedClass, JNIEnv *env, jobject obj, jobject class_loader, jstring name) { + const char * nameC = env->GetStringUTFChars(name, JNI_FALSE); +// ALOGD("findLoadedClass: %s", nameC); + if (hideXposedClass) { + if (strstr(nameC, "de/robv/android/xposed/") || + strstr(nameC, "me/weishu/epic") || + strstr(nameC, "me/weishu/exposed") || + strstr(nameC, "de.robv.android") || + strstr(nameC, "me.weishu.epic") || + strstr(nameC, "me.weishu.exposed")) { + return nullptr; + } + } + jobject result = orig_findLoadedClass(env, obj, class_loader, name); + env->ReleaseStringUTFChars(name, nameC); + return result; +} + +void VMClassLoaderHook::init(JNIEnv *env) { + const char *className = "java/lang/VMClassLoader"; + JniHook::HookJniFun(env, className, "findLoadedClass", "(Ljava/lang/ClassLoader;Ljava/lang/String;)Ljava/lang/Class;", + (void *) new_findLoadedClass, + (void **) (&orig_findLoadedClass), true); +} + +void VMClassLoaderHook::hideXposed() { + hideXposedClass = true; +} diff --git a/Bcore/src/main/jni/Hook/VMClassLoaderHook.h b/Bcore/src/main/jni/Hook/VMClassLoaderHook.h new file mode 100644 index 00000000..f8584b03 --- /dev/null +++ b/Bcore/src/main/jni/Hook/VMClassLoaderHook.h @@ -0,0 +1,19 @@ +// +// Created by Milk on 2021/5/5. +// + +#ifndef BLACKBOX_VMCLASSLOADERHOOK_H +#define BLACKBOX_VMCLASSLOADERHOOK_H + + +#include "BaseHook.h" +#include + +class VMClassLoaderHook : public BaseHook { +public: + static void hideXposed(); + static void init(JNIEnv *env); +}; + + +#endif //BLACKBOX_VMCLASSLOADERHOOK_H diff --git a/Bcore/src/main/jni/IO.cpp b/Bcore/src/main/jni/IO.cpp new file mode 100644 index 00000000..5c557eab --- /dev/null +++ b/Bcore/src/main/jni/IO.cpp @@ -0,0 +1,78 @@ +// +// Created by Milk on 4/10/21. +// + +#include "IO.h" +#include "Log.h" + +jmethodID getAbsolutePathMethodId; + +list relocate_rule; + +char *replace(const char *str, const char *src, const char *dst) { + const char *pos = str; + int count = 0; + while ((pos = strstr(pos, src))) { + count++; + pos += strlen(src); + } + + size_t result_len = strlen(str) + (strlen(dst) - strlen(src)) * count + 1; + char *result = (char *) malloc(result_len); + memset(result, 0, strlen(result)); + + const char *left = str; + const char *right = nullptr; + + while ((right = strstr(left, src))) { + strncat(result, left, right - left); + strcat(result, dst); + right += strlen(src); + left = right; + } + strcat(result, left); + return result; +} + +const char *IO::redirectPath(const char *__path) { + list::iterator iterator; + for (iterator = relocate_rule.begin(); iterator != relocate_rule.end(); ++iterator) { + IO::RelocateInfo info = *iterator; + if (strstr(__path, info.targetPath) && !strstr(__path, "/virtual/")) { + char *ret = replace(__path, info.targetPath, info.relocatePath); + // ALOGD("redirectPath %s => %s", __path, ret); + return ret; + } + } + return __path; +} + +jstring IO::redirectPath(JNIEnv *env, jstring path) { +// const char * pathC = env->GetStringUTFChars(path, JNI_FALSE); +// const char *redirect = redirectPath(pathC); +// env->ReleaseStringUTFChars(path, pathC); +// return env->NewStringUTF(redirect); + return VmCore::redirectPathString(env, path); +} + +jobject IO::redirectPath(JNIEnv *env, jobject path) { +// auto pathStr = +// reinterpret_cast(env->CallObjectMethod(path, getAbsolutePathMethodId)); +// jstring redirect = redirectPath(env, pathStr); +// jobject file = env->NewObject(fileClazz, fileNew, redirect); +// env->DeleteLocalRef(pathStr); +// env->DeleteLocalRef(redirect); + return VmCore::redirectPathFile(env, path); +} + +void IO::addRule(const char *targetPath, const char *relocatePath) { + IO::RelocateInfo info{}; + info.targetPath = targetPath; + info.relocatePath = relocatePath; + relocate_rule.push_back(info); +} + +void IO::init(JNIEnv *env) { + jclass tmpFile = env->FindClass("java/io/File"); + getAbsolutePathMethodId = env->GetMethodID(tmpFile, "getAbsolutePath", "()Ljava/lang/String;"); +} diff --git a/Bcore/src/main/jni/IO.h b/Bcore/src/main/jni/IO.h new file mode 100644 index 00000000..61d77e05 --- /dev/null +++ b/Bcore/src/main/jni/IO.h @@ -0,0 +1,35 @@ +// +// Created by Milk on 4/10/21. +// + +#ifndef VIRTUALM_IO_H +#define VIRTUALM_IO_H + +#include + +#include +#include +#include "VmCore.h" + +using namespace std; + +class IO { +public: + static void init(JNIEnv *env); + + struct RelocateInfo { + const char *targetPath; + const char *relocatePath; + }; + + static void addRule(const char *targetPath, const char *relocatePath); + + static jstring redirectPath(JNIEnv *env, jstring path); + + static jobject redirectPath(JNIEnv *env, jobject path); + + static const char *redirectPath(const char *__path); +}; + + +#endif //VIRTUALM_IO_H diff --git a/Bcore/src/main/jni/JniHook/ArtMethod.h b/Bcore/src/main/jni/JniHook/ArtMethod.h new file mode 100644 index 00000000..3131085f --- /dev/null +++ b/Bcore/src/main/jni/JniHook/ArtMethod.h @@ -0,0 +1,40 @@ +// +// Created by Milk on 3/7/21. +// +#include + +#ifndef ARTHOOK_ART_METHOD_H +#define ARTHOOK_ART_METHOD_H + +#define __ANDROID_API_R__ 30 +#define __ANDROID_API_Q__ 29 +#define __ANDROID_API_P__ 28 + +static constexpr uint32_t kAccPublic = 0x0001; // class, field, method, ic +static constexpr uint32_t kAccPrivate = 0x0002; // field, method, ic +static constexpr uint32_t kAccProtected = 0x0004; // field, method, ic +static constexpr uint32_t kAccStatic = 0x0008; // field, method, ic +static constexpr uint32_t kAccFinal = 0x0010; // class, field, method, ic +static constexpr uint32_t kAccSynchronized = 0x0020; // method (only allowed on natives) +static constexpr uint32_t kAccSuper = 0x0020; // class (not used in dex) +static constexpr uint32_t kAccVolatile = 0x0040; // field +static constexpr uint32_t kAccBridge = 0x0040; // method (1.5) +static constexpr uint32_t kAccTransient = 0x0080; // field +static constexpr uint32_t kAccVarargs = 0x0080; // method (1.5) +static constexpr uint32_t kAccNative = 0x0100; // method +static constexpr uint32_t kAccInterface = 0x0200; // class, ic +static constexpr uint32_t kAccAbstract = 0x0400; // class, method, ic +static constexpr uint32_t kAccStrict = 0x0800; // method +static constexpr uint32_t kAccSynthetic = 0x1000; // class, field, method, ic +static constexpr uint32_t kAccAnnotation = 0x2000; // class, ic (1.5) +static constexpr uint32_t kAccEnum = 0x4000; // class, field, ic (1.5) + +static constexpr uint32_t kAccPublicApi = 0x10000000; // field, method +static constexpr uint32_t kAccCorePlatformApi = 0x20000000; // field, method + +// Native method flags are set when linking the methods based on the presence of the +// @dalvik.annotation.optimization.{Fast,Critical}Native annotations with build visibility. +// Reuse the values of kAccSkipAccessChecks and kAccMiranda which are not used for native methods. +static constexpr uint32_t kAccFastNative = 0x00080000; // method (runtime; native only) +static constexpr uint32_t kAccCriticalNative = 0x00200000; // method (runtime; native only) +#endif //ARTHOOK_ART_METHOD_H diff --git a/Bcore/src/main/jni/JniHook/JniHook.cpp b/Bcore/src/main/jni/JniHook/JniHook.cpp new file mode 100644 index 00000000..270e0cec --- /dev/null +++ b/Bcore/src/main/jni/JniHook/JniHook.cpp @@ -0,0 +1,277 @@ +// +// Created by Milk on 3/8/21. +// + +#include +#include "JniHook.h" +#include "Log.h" +#include "ArtMethod.h" + +static struct { + int api_level; + unsigned int art_field_size; + int art_field_flags_offset; + + unsigned int art_method_size; + int art_method_flags_offset; + int art_method_native_offset; + + int class_flags_offset; + + jclass method_utils_class; + jmethodID get_method_desc_id; + jmethodID get_method_declaring_class_id; + jmethodID get_method_name_id; + +} HookEnv; + +static const char *GetMethodDesc(JNIEnv *env, jobject javaMethod) { + auto desc = reinterpret_cast(env->CallStaticObjectMethod(HookEnv.method_utils_class, + HookEnv.get_method_desc_id, + javaMethod)); + return env->GetStringUTFChars(desc, JNI_FALSE); +} + +static const char *GetMethodDeclaringClass(JNIEnv *env, jobject javaMethod) { + auto desc = reinterpret_cast(env->CallStaticObjectMethod(HookEnv.method_utils_class, + HookEnv.get_method_declaring_class_id, + javaMethod)); + return env->GetStringUTFChars(desc, JNI_FALSE); +} + +static const char *GetMethodName(JNIEnv *env, jobject javaMethod) { + auto desc = reinterpret_cast(env->CallStaticObjectMethod(HookEnv.method_utils_class, + HookEnv.get_method_name_id, + javaMethod)); + return env->GetStringUTFChars(desc, JNI_FALSE); +} + +inline static uint32_t GetAccessFlags(const char *art_method) { + return *reinterpret_cast(art_method + HookEnv.art_method_flags_offset); +} + +inline static bool SetAccessFlags(char *art_method, uint32_t flags) { + *reinterpret_cast(art_method + HookEnv.art_method_flags_offset) = flags; + return true; +} + +inline static bool AddAccessFlag(char *art_method, uint32_t flag) { + uint32_t old_flag = GetAccessFlags(art_method); + uint32_t new_flag = old_flag | flag; + return new_flag != old_flag && SetAccessFlags(art_method, new_flag); +} + +inline static bool ClearAccessFlag(char *art_method, uint32_t flag) { + uint32_t old_flag = GetAccessFlags(art_method); + uint32_t new_flag = old_flag & ~flag; + return new_flag != old_flag && SetAccessFlags(art_method, new_flag); +} + +inline static bool HasAccessFlag(char *art_method, uint32_t flag) { + uint32_t flags = GetAccessFlags(art_method); + return (flags & flag) == flag; +} + +inline static bool ClearFastNativeFlag(char *art_method) { + // FastNative + return HookEnv.api_level < __ANDROID_API_P__ && ClearAccessFlag(art_method, kAccFastNative); +} + +static void *GetArtMethod(JNIEnv *env, jclass clazz, jmethodID methodId) { + if (HookEnv.api_level >= __ANDROID_API_Q__) { + jclass executable = env->FindClass("java/lang/reflect/Executable"); + jfieldID artId = env->GetFieldID(executable, "artMethod", "J"); + jobject method = env->ToReflectedMethod(clazz, methodId, true); + return reinterpret_cast(env->GetLongField(method, artId)); + } else { + return methodId; + } +} + +static void *GetFieldMethod(JNIEnv *env, jobject field) { + if (HookEnv.api_level >= __ANDROID_API_Q__) { + jclass fieldClass = env->FindClass("java/lang/reflect/Field"); + jmethodID getArtField = env->GetMethodID(fieldClass, "getArtField", "()J"); + return reinterpret_cast(env->CallLongMethod(field, getArtField)); + } else { + return env->FromReflectedField(field); + } +} + +bool CheckFlags(void *artMethod) { + char *method = static_cast(artMethod); + if (!HasAccessFlag(method, kAccNative)) { + ALOGE("not native method"); + return false; + } + ClearFastNativeFlag(method); + return true; +} + +void JniHook::HookJniFun(JNIEnv *env, jobject java_method, void *new_fun, + void **orig_fun, bool is_static) { + const char *class_name = GetMethodDeclaringClass(env, java_method); + const char *method_name = GetMethodName(env, java_method); + const char *sign = GetMethodDesc(env, java_method); + HookJniFun(env, class_name, method_name, sign, new_fun, orig_fun, is_static); +} + +void +JniHook::HookJniFun(JNIEnv *env, const char *class_name, const char *method_name, const char *sign, + void *new_fun, void **orig_fun, bool is_static) { + if (HookEnv.art_method_native_offset == 0) { + return; + } + jclass clazz = env->FindClass(class_name); + if (!clazz) { + ALOGD("findClass fail: %s %s", class_name, method_name); + env->ExceptionClear(); + return; + } + jmethodID method = nullptr; + if (is_static) { + method = env->GetStaticMethodID(clazz, method_name, sign); + } else { + method = env->GetMethodID(clazz, method_name, sign); + } + if (!method) { + env->ExceptionClear(); + ALOGD("get method id fail: %s %s", class_name, method_name); + return; + } + JNINativeMethod gMethods[] = { + {method_name, sign, (void *) new_fun}, + }; + + auto artMethod = reinterpret_cast(GetArtMethod(env, clazz, method)); + if (!CheckFlags(artMethod)) { + ALOGE("check flags error. class:%s, method:%s", class_name, method_name); + return; + } + *orig_fun = reinterpret_cast(artMethod[HookEnv.art_method_native_offset]); + if (env->RegisterNatives(clazz, gMethods, 1) < 0) { + ALOGE("jni hook error. class:%s, method:%s", class_name, method_name); + return; + } + // FastNative + if (HookEnv.api_level == __ANDROID_API_O__ || HookEnv.api_level == __ANDROID_API_O_MR1__) { + AddAccessFlag((char *) artMethod, kAccFastNative); + } + ALOGD("register class:%s, method:%s success!", class_name, method_name); +} + +__attribute__((section (".mytext"))) JNICALL void native_offset + (JNIEnv *env, jclass obj) { +} + +__attribute__((section (".mytext"))) JNICALL void native_offset2 + (JNIEnv *env, jclass obj) { +} + +__attribute__((section (".mytext"))) JNICALL void set_method_accessible + (JNIEnv *env, jclass obj, jclass clazz, jobject method) { + jmethodID methodId = env->FromReflectedMethod(method); + char *art_method = static_cast(GetArtMethod(env, clazz, methodId)); + AddAccessFlag(art_method, kAccPublic); + if (HookEnv.api_level >= __ANDROID_API_Q__) { + AddAccessFlag(art_method, kAccPublicApi); + } +} + +__attribute__((section (".mytext"))) JNICALL void set_field_accessible + (JNIEnv *env, jclass obj, jclass clazz, jobject field) { + char *artField = static_cast(GetFieldMethod(env, field)); + AddAccessFlag(artField, kAccPublic); + if (HookEnv.api_level >= __ANDROID_API_Q__) { + AddAccessFlag(artField, kAccPublicApi); + } + ClearAccessFlag(artField, kAccFinal); +} + +void registerNative(JNIEnv *env) { + jclass clazz = env->FindClass("top/niunaijun/jnihook/jni/JniHook"); + JNINativeMethod gMethods[] = { + {"nativeOffset", "()V", (void *) native_offset}, + {"nativeOffset2", "()V", (void *) native_offset2}, + {"setAccessible", "(Ljava/lang/Class;Ljava/lang/reflect/Method;)V", (void *) set_method_accessible}, + {"setAccessible", "(Ljava/lang/Class;Ljava/lang/reflect/Field;)V", (void *) set_field_accessible}, + }; + if (env->RegisterNatives(clazz, gMethods, sizeof(gMethods) / sizeof(gMethods[0])) < 0) { + ALOGE("jni register error."); + } +} + +void JniHook::InitJniHook(JNIEnv *env, int api_level) { + registerNative(env); + HookEnv.api_level = api_level; + + jclass clazz = env->FindClass("top/niunaijun/jnihook/jni/JniHook"); + jmethodID nativeOffsetId = env->GetStaticMethodID(clazz, "nativeOffset", "()V"); + jmethodID nativeOffset2Id = env->GetStaticMethodID(clazz, "nativeOffset2", "()V"); + + jfieldID nativeOffsetFieldId = env->GetStaticFieldID(clazz, "NATIVE_OFFSET", "I"); + jfieldID nativeOffsetField2Id = env->GetStaticFieldID(clazz, "NATIVE_OFFSET_2", "I"); + + void *nativeOffsetField = GetFieldMethod(env, env->ToReflectedField(clazz, nativeOffsetFieldId, + true)); + void *nativeOffsetField2 = GetFieldMethod(env, env->ToReflectedField(clazz, nativeOffsetField2Id, + true)); + HookEnv.art_field_size = (size_t) nativeOffsetField2 - (size_t) nativeOffsetField; + + void *nativeOffset = GetArtMethod(env, clazz, nativeOffsetId); + void *nativeOffset2 = GetArtMethod(env, clazz, nativeOffset2Id); + HookEnv.art_method_size = (size_t) nativeOffset2 - (size_t) nativeOffset; + + // calc native offset + auto artMethod = reinterpret_cast(nativeOffset); + for (int i = 0; i < HookEnv.art_method_size; ++i) { + if (reinterpret_cast(artMethod[i]) == native_offset) { + HookEnv.art_method_native_offset = i; + break; + } + } + + uint32_t flags = 0x0; + flags = flags | kAccPublic; + flags = flags | kAccStatic; + flags = flags | kAccNative; + flags = flags | kAccFinal; + if (api_level >= __ANDROID_API_Q__) { + flags = flags | kAccPublicApi; + } + + char *start = reinterpret_cast(artMethod); + for (int i = 1; i < HookEnv.art_method_size; ++i) { + auto value = *(uint32_t *) (start + i * sizeof(uint32_t)); + if (value == flags) { + HookEnv.art_method_flags_offset = i * sizeof(uint32_t); + break; + } + } + + flags = 0x0; + flags = flags | kAccPublic; + flags = flags | kAccStatic; + flags = flags | kAccFinal; + if (api_level >= __ANDROID_API_Q__) { + flags = flags | kAccPublicApi; + } + char *fieldStart = reinterpret_cast(nativeOffsetField); + for (int i = 1; i < HookEnv.art_field_size; ++i) { + auto value = *(int32_t *) (fieldStart + i * sizeof(int32_t)); + if (value == flags) { + HookEnv.art_field_flags_offset = i * sizeof(int32_t); + break; + } + } + + HookEnv.method_utils_class = env->FindClass("top/niunaijun/jnihook/MethodUtils"); + HookEnv.get_method_desc_id = env->GetStaticMethodID(HookEnv.method_utils_class, "getDesc", + "(Ljava/lang/reflect/Method;)Ljava/lang/String;"); + HookEnv.get_method_declaring_class_id = env->GetStaticMethodID(HookEnv.method_utils_class, + "getDeclaringClass", + "(Ljava/lang/reflect/Method;)Ljava/lang/String;"); + HookEnv.get_method_name_id = env->GetStaticMethodID(HookEnv.method_utils_class, "getMethodName", + "(Ljava/lang/reflect/Method;)Ljava/lang/String;"); +} + diff --git a/Bcore/src/main/jni/JniHook/JniHook.h b/Bcore/src/main/jni/JniHook/JniHook.h new file mode 100644 index 00000000..5138513f --- /dev/null +++ b/Bcore/src/main/jni/JniHook/JniHook.h @@ -0,0 +1,22 @@ +// +// Created by Milk on 3/8/21. +// + +#ifndef VIRTUAL_APP_JNIHOOK_H +#define VIRTUAL_APP_JNIHOOK_H + +#include "ArtMethod.h" + +#define HOOK_JNI(ret, func, ...) \ + ret (*orig_##func)(__VA_ARGS__); \ + ret new_##func(__VA_ARGS__) + +class JniHook { +public: + static void InitJniHook(JNIEnv *env, int api_level); + static void HookJniFun(JNIEnv *env, const char *class_name, const char *method_name, const char *sign, void * new_fun, void ** orig_fun, + bool is_static); + static void HookJniFun(JNIEnv *env, jobject java_method, void *new_fun, void **orig_fun, bool is_static); +}; + +#endif //VIRTUAL_APP_JNIHOOK_H diff --git a/Bcore/src/main/jni/Log.h b/Bcore/src/main/jni/Log.h new file mode 100644 index 00000000..2b2c96a0 --- /dev/null +++ b/Bcore/src/main/jni/Log.h @@ -0,0 +1,18 @@ +#include + +#define TAG "VmCore" + +#if 1 +#define log_print_error(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__) +#define log_print_debug(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__) +#else +#define log_print_error(...) +#endif + +#define ALOGE(...) log_print_error(__VA_ARGS__) +#define ALOGD(...) log_print_debug(__VA_ARGS__) + +#ifndef SPEED_LOG_H +#define SPEED_LOG_H 1 + +#endif diff --git a/Bcore/src/main/jni/Substrate/And64InlineHook.cpp b/Bcore/src/main/jni/Substrate/And64InlineHook.cpp new file mode 100644 index 00000000..31349574 --- /dev/null +++ b/Bcore/src/main/jni/Substrate/And64InlineHook.cpp @@ -0,0 +1,567 @@ +/* + * @date : 2018/04/18 + * @author : Rprop (r_prop@outlook.com) + * https://github.com/Rprop/And64InlineHook + */ +/* + MIT License + + Copyright (c) 2018 Rprop (r_prop@outlook.com) + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + */ +#define __STDC_FORMAT_MACROS + +#if defined(__aarch64__) + +#include +#include +#include +#include +#include "And64InlineHook.hpp" +#define A64_MAX_INSTRUCTIONS 5 +#define A64_MAX_REFERENCES (A64_MAX_INSTRUCTIONS * 2) +#define A64_NOP 0xd503201fu +#define A64_JNIEXPORT __attribute__((visibility("default"))) +#define __flush_cache(c, n) __builtin___clear_cache(reinterpret_cast(c), reinterpret_cast(c) + n) + + +typedef uint32_t *__restrict *__restrict instruction; +typedef struct +{ + struct fix_info + { + uint32_t *bp; + uint32_t ls; // left-shift counts + uint32_t ad; // & operand + }; + struct insns_info + { + union + { + uint64_t insu; + int64_t ins; + void *insp; + }; + fix_info fmap[A64_MAX_REFERENCES]; + }; + int64_t basep; + int64_t endp; + insns_info dat[A64_MAX_INSTRUCTIONS]; + +public: + inline bool is_in_fixing_range(const int64_t absolute_addr) { + return absolute_addr >= this->basep && absolute_addr < this->endp; + } + inline intptr_t get_ref_ins_index(const int64_t absolute_addr) { + return static_cast((absolute_addr - this->basep) / sizeof(uint32_t)); + } + inline intptr_t get_and_set_current_index(uint32_t *__restrict inp, uint32_t *__restrict outp) { + intptr_t current_idx = this->get_ref_ins_index(reinterpret_cast(inp)); + this->dat[current_idx].insp = outp; + return current_idx; + } + inline void reset_current_ins(const intptr_t idx, uint32_t *__restrict outp) { + this->dat[idx].insp = outp; + } + void insert_fix_map(const intptr_t idx, uint32_t *bp, uint32_t ls = 0u, uint32_t ad = 0xffffffffu) { + for (auto &f : this->dat[idx].fmap) { + if (f.bp == NULL) { + f.bp = bp; + f.ls = ls; + f.ad = ad; + return; + } //if + } + // What? GGing.. + } + void process_fix_map(const intptr_t idx) { + for (auto &f : this->dat[idx].fmap) { + if (f.bp == NULL) break; + *(f.bp) = *(f.bp) | (((int32_t(this->dat[idx].ins - reinterpret_cast(f.bp)) >> 2) << f.ls) & f.ad); + f.bp = NULL; + } + } +} context; + +//------------------------------------------------------------------------- + +static bool __fix_branch_imm(instruction inpp, instruction outpp, context *ctxp) +{ + static constexpr uint32_t mbits = 6u; + static constexpr uint32_t mask = 0xfc000000u; // 0b11111100000000000000000000000000 + static constexpr uint32_t rmask = 0x03ffffffu; // 0b00000011111111111111111111111111 + static constexpr uint32_t op_b = 0x14000000u; // "b" ADDR_PCREL26 + static constexpr uint32_t op_bl = 0x94000000u; // "bl" ADDR_PCREL26 + + const uint32_t ins = *(*inpp); + const uint32_t opc = ins & mask; + switch (opc) { + case op_b: + case op_bl: + { + intptr_t current_idx = ctxp->get_and_set_current_index(*inpp, *outpp); + int64_t absolute_addr = reinterpret_cast(*inpp) + (static_cast(ins << mbits) >> (mbits - 2u)); // sign-extended + int64_t new_pc_offset = static_cast(absolute_addr - reinterpret_cast(*outpp)) >> 2; // shifted + bool special_fix_type = ctxp->is_in_fixing_range(absolute_addr); + // whether the branch should be converted to absolute jump + if (!special_fix_type && llabs(new_pc_offset) >= (rmask >> 1)) { + bool b_aligned = (reinterpret_cast(*outpp + 2) & 7u) == 0u; + if (opc == op_b) { + if (b_aligned != true) { + (*outpp)[0] = A64_NOP; + ctxp->reset_current_ins(current_idx, ++(*outpp)); + } //if + (*outpp)[0] = 0x58000051u; // LDR X17, #0x8 + (*outpp)[1] = 0xd61f0220u; // BR X17 + memcpy(*outpp + 2, &absolute_addr, sizeof(absolute_addr)); + *outpp += 4; + } else { + if (b_aligned == true) { + (*outpp)[0] = A64_NOP; + ctxp->reset_current_ins(current_idx, ++(*outpp)); + } //if + (*outpp)[0] = 0x58000071u; // LDR X17, #12 + (*outpp)[1] = 0x1000009eu; // ADR X30, #16 + (*outpp)[2] = 0xd61f0220u; // BR X17 + memcpy(*outpp + 3, &absolute_addr, sizeof(absolute_addr)); + *outpp += 5; + } //if + } else { + if (special_fix_type) { + intptr_t ref_idx = ctxp->get_ref_ins_index(absolute_addr); + if (ref_idx <= current_idx) { + new_pc_offset = static_cast(ctxp->dat[ref_idx].ins - reinterpret_cast(*outpp)) >> 2; + } else { + ctxp->insert_fix_map(ref_idx, *outpp, 0u, rmask); + new_pc_offset = 0; + } //if + } //if + + (*outpp)[0] = opc | (new_pc_offset & ~mask); + ++(*outpp); + } //if + + ++(*inpp); + return ctxp->process_fix_map(current_idx), true; + } + } + return false; +} + +//------------------------------------------------------------------------- + +static bool __fix_cond_comp_test_branch(instruction inpp, instruction outpp, context *ctxp) +{ + static constexpr uint32_t lsb = 5u; + static constexpr uint32_t lmask01 = 0xff00001fu; // 0b11111111000000000000000000011111 + static constexpr uint32_t mask0 = 0xff000010u; // 0b11111111000000000000000000010000 + static constexpr uint32_t op_bc = 0x54000000u; // "b.c" ADDR_PCREL19 + static constexpr uint32_t mask1 = 0x7f000000u; // 0b01111111000000000000000000000000 + static constexpr uint32_t op_cbz = 0x34000000u; // "cbz" Rt, ADDR_PCREL19 + static constexpr uint32_t op_cbnz = 0x35000000u; // "cbnz" Rt, ADDR_PCREL19 + static constexpr uint32_t lmask2 = 0xfff8001fu; // 0b11111111111110000000000000011111 + static constexpr uint32_t mask2 = 0x7f000000u; // 0b01111111000000000000000000000000 + static constexpr uint32_t op_tbz = 0x36000000u; // 0b00110110000000000000000000000000 "tbz" Rt, BIT_NUM, ADDR_PCREL14 + static constexpr uint32_t op_tbnz = 0x37000000u; // 0b00110111000000000000000000000000 "tbnz" Rt, BIT_NUM, ADDR_PCREL14 + + const uint32_t ins = *(*inpp); + uint32_t lmask = lmask01; + if ((ins & mask0) != op_bc) { + uint32_t opc = ins & mask1; + if (opc != op_cbz && opc != op_cbnz) { + opc = ins & mask2; + if (opc != op_tbz && opc != op_tbnz) { + return false; + } //if + lmask = lmask2; + } //if + } //if + + intptr_t current_idx = ctxp->get_and_set_current_index(*inpp, *outpp); + int64_t absolute_addr = reinterpret_cast(*inpp) + ((ins & ~lmask) >> (lsb - 2u)); + int64_t new_pc_offset = static_cast(absolute_addr - reinterpret_cast(*outpp)) >> 2; // shifted + bool special_fix_type = ctxp->is_in_fixing_range(absolute_addr); + if (!special_fix_type && llabs(new_pc_offset) >= (~lmask >> (lsb + 1))) { + if ((reinterpret_cast(*outpp + 4) & 7u) != 0u) { + (*outpp)[0] = A64_NOP; + ctxp->reset_current_ins(current_idx, ++(*outpp)); + } //if + (*outpp)[0] = (((8u >> 2u) << lsb) & ~lmask) | (ins & lmask); // B.C #0x8 + (*outpp)[1] = 0x14000005u; // B #0x14 + (*outpp)[2] = 0x58000051u; // LDR X17, #0x8 + (*outpp)[3] = 0xd61f0220u; // BR X17 + memcpy(*outpp + 4, &absolute_addr, sizeof(absolute_addr)); + *outpp += 6; + } else { + if (special_fix_type) { + intptr_t ref_idx = ctxp->get_ref_ins_index(absolute_addr); + if (ref_idx <= current_idx) { + new_pc_offset = static_cast(ctxp->dat[ref_idx].ins - reinterpret_cast(*outpp)) >> 2; + } else { + ctxp->insert_fix_map(ref_idx, *outpp, lsb, ~lmask); + new_pc_offset = 0; + } //if + } //if + + (*outpp)[0] = (static_cast(new_pc_offset << lsb) & ~lmask) | (ins & lmask); + ++(*outpp); + } //if + + ++(*inpp); + return ctxp->process_fix_map(current_idx), true; +} + +//------------------------------------------------------------------------- + +static bool __fix_loadlit(instruction inpp, instruction outpp, context *ctxp) +{ + const uint32_t ins = *(*inpp); + + // memory prefetch("prfm"), just skip it + // http://infocenter.arm.com/help/topic/com.arm.doc.100069_0608_00_en/pge1427897420050.html + if ((ins & 0xff000000u) == 0xd8000000u) { + ctxp->process_fix_map(ctxp->get_and_set_current_index(*inpp, *outpp)); + ++(*inpp); + return true; + } //if + + static constexpr uint32_t msb = 8u; + static constexpr uint32_t lsb = 5u; + static constexpr uint32_t mask_30 = 0x40000000u; // 0b01000000000000000000000000000000 + static constexpr uint32_t mask_31 = 0x80000000u; // 0b10000000000000000000000000000000 + static constexpr uint32_t lmask = 0xff00001fu; // 0b11111111000000000000000000011111 + static constexpr uint32_t mask_ldr = 0xbf000000u; // 0b10111111000000000000000000000000 + static constexpr uint32_t op_ldr = 0x18000000u; // 0b00011000000000000000000000000000 "LDR Wt/Xt, label" | ADDR_PCREL19 + static constexpr uint32_t mask_ldrv = 0x3f000000u; // 0b00111111000000000000000000000000 + static constexpr uint32_t op_ldrv = 0x1c000000u; // 0b00011100000000000000000000000000 "LDR St/Dt/Qt, label" | ADDR_PCREL19 + static constexpr uint32_t mask_ldrsw = 0xff000000u; // 0b11111111000000000000000000000000 + static constexpr uint32_t op_ldrsw = 0x98000000u; // "LDRSW Xt, label" | ADDR_PCREL19 | load register signed word + // LDR S0, #0 | 0b00011100000000000000000000000000 | 32-bit + // LDR D0, #0 | 0b01011100000000000000000000000000 | 64-bit + // LDR Q0, #0 | 0b10011100000000000000000000000000 | 128-bit + // INVALID | 0b11011100000000000000000000000000 | may be 256-bit + + uint32_t mask = mask_ldr; + uintptr_t faligned = (ins & mask_30) ? 7u : 3u; + if ((ins & mask_ldr) != op_ldr) { + mask = mask_ldrv; + if (faligned != 7u) + faligned = (ins & mask_31) ? 15u : 3u; + if ((ins & mask_ldrv) != op_ldrv) { + if ((ins & mask_ldrsw) != op_ldrsw) { + return false; + } //if + mask = mask_ldrsw; + faligned = 7u; + } //if + } //if + + intptr_t current_idx = ctxp->get_and_set_current_index(*inpp, *outpp); + int64_t absolute_addr = reinterpret_cast(*inpp) + ((static_cast(ins << msb) >> (msb + lsb - 2u)) & ~3u); + int64_t new_pc_offset = static_cast(absolute_addr - reinterpret_cast(*outpp)) >> 2; // shifted + bool special_fix_type = ctxp->is_in_fixing_range(absolute_addr); + // special_fix_type may encounter issue when there are mixed data and code + if (special_fix_type || (llabs(new_pc_offset) + (faligned + 1u - 4u) / 4u) >= (~lmask >> (lsb + 1))) { // inaccurate, but it works + while ((reinterpret_cast(*outpp + 2) & faligned) != 0u) { + *(*outpp)++ = A64_NOP; + } + ctxp->reset_current_ins(current_idx, *outpp); + + // Note that if memory at absolute_addr is writeable (non-const), we will fail to fetch it. + // And what's worse, we may unexpectedly overwrite something if special_fix_type is true... + uint32_t ns = static_cast((faligned + 1) / sizeof(uint32_t)); + (*outpp)[0] = (((8u >> 2u) << lsb) & ~mask) | (ins & lmask); // LDR #0x8 + (*outpp)[1] = 0x14000001u + ns; // B #0xc + memcpy(*outpp + 2, reinterpret_cast(absolute_addr), faligned + 1); + *outpp += 2 + ns; + } else { + faligned >>= 2; // new_pc_offset is shifted and 4-byte aligned + while ((new_pc_offset & faligned) != 0) { + *(*outpp)++ = A64_NOP; + new_pc_offset = static_cast(absolute_addr - reinterpret_cast(*outpp)) >> 2; + } + ctxp->reset_current_ins(current_idx, *outpp); + + (*outpp)[0] = (static_cast(new_pc_offset << lsb) & ~mask) | (ins & lmask); + ++(*outpp); + } //if + + ++(*inpp); + return ctxp->process_fix_map(current_idx), true; +} + +//------------------------------------------------------------------------- + +static bool __fix_pcreladdr(instruction inpp, instruction outpp, context *ctxp) +{ + // Load a PC-relative address into a register + // http://infocenter.arm.com/help/topic/com.arm.doc.100069_0608_00_en/pge1427897645644.html + static constexpr uint32_t msb = 8u; + static constexpr uint32_t lsb = 5u; + static constexpr uint32_t mask = 0x9f000000u; // 0b10011111000000000000000000000000 + static constexpr uint32_t rmask = 0x0000001fu; // 0b00000000000000000000000000011111 + static constexpr uint32_t lmask = 0xff00001fu; // 0b11111111000000000000000000011111 + static constexpr uint32_t fmask = 0x00ffffffu; // 0b00000000111111111111111111111111 + static constexpr uint32_t max_val = 0x001fffffu; // 0b00000000000111111111111111111111 + static constexpr uint32_t op_adr = 0x10000000u; // "adr" Rd, ADDR_PCREL21 + static constexpr uint32_t op_adrp = 0x90000000u; // "adrp" Rd, ADDR_ADRP + + const uint32_t ins = *(*inpp); + intptr_t current_idx; + switch (ins & mask) { + case op_adr: + { + current_idx = ctxp->get_and_set_current_index(*inpp, *outpp); + int64_t lsb_bytes = static_cast(ins << 1u) >> 30u; + int64_t absolute_addr = reinterpret_cast(*inpp) + (((static_cast(ins << msb) >> (msb + lsb - 2u)) & ~3u) | lsb_bytes); + int64_t new_pc_offset = static_cast(absolute_addr - reinterpret_cast(*outpp)); + bool special_fix_type = ctxp->is_in_fixing_range(absolute_addr); + if (!special_fix_type && llabs(new_pc_offset) >= (max_val >> 1)) { + if ((reinterpret_cast(*outpp + 2) & 7u) != 0u) { + (*outpp)[0] = A64_NOP; + ctxp->reset_current_ins(current_idx, ++(*outpp)); + } //if + + (*outpp)[0] = 0x58000000u | (((8u >> 2u) << lsb) & ~mask) | (ins & rmask); // LDR #0x8 + (*outpp)[1] = 0x14000003u; // B #0xc + memcpy(*outpp + 2, &absolute_addr, sizeof(absolute_addr)); + *outpp += 4; + } else { + if (special_fix_type) { + intptr_t ref_idx = ctxp->get_ref_ins_index(absolute_addr & ~3ull); + if (ref_idx <= current_idx) { + new_pc_offset = static_cast(ctxp->dat[ref_idx].ins - reinterpret_cast(*outpp)); + } else { + ctxp->insert_fix_map(ref_idx, *outpp, lsb, fmask); + new_pc_offset = 0; + } //if + } //if + + // the lsb_bytes will never be changed, so we can use lmask to keep it + (*outpp)[0] = (static_cast(new_pc_offset << (lsb - 2u)) & fmask) | (ins & lmask); + ++(*outpp); + } //if + } + break; + case op_adrp: + { + current_idx = ctxp->get_and_set_current_index(*inpp, *outpp); + int32_t lsb_bytes = static_cast(ins << 1u) >> 30u; + int64_t absolute_addr = (reinterpret_cast(*inpp) & ~0xfffll) + ((((static_cast(ins << msb) >> (msb + lsb - 2u)) & ~3u) | lsb_bytes) << 12); + if (ctxp->is_in_fixing_range(absolute_addr)) { + intptr_t ref_idx = ctxp->get_ref_ins_index(absolute_addr/* & ~3ull*/); + if (ref_idx > current_idx) { + // the bottom 12 bits of absolute_addr are masked out, + // so ref_idx must be less than or equal to current_idx! + } //if + + // *absolute_addr may be changed due to relocation fixing + *(*outpp)++ = ins; // 0x90000000u; + } else { + if ((reinterpret_cast(*outpp + 2) & 7u) != 0u) { + (*outpp)[0] = A64_NOP; + ctxp->reset_current_ins(current_idx, ++(*outpp)); + } //if + + (*outpp)[0] = 0x58000000u | (((8u >> 2u) << lsb) & ~mask) | (ins & rmask); // LDR #0x8 + (*outpp)[1] = 0x14000003u; // B #0xc + memcpy(*outpp + 2, &absolute_addr, sizeof(absolute_addr)); // potential overflow? + *outpp += 4; + } //if + } + break; + default: + return false; + } + + ctxp->process_fix_map(current_idx); + ++(*inpp); + return true; +} + +//------------------------------------------------------------------------- + +static void __fix_instructions(uint32_t *__restrict inp, int32_t count, uint32_t *__restrict outp) +{ + context ctx; + ctx.basep = reinterpret_cast(inp); + ctx.endp = reinterpret_cast(inp + count); + memset(ctx.dat, 0, sizeof(ctx.dat)); + static_assert(sizeof(ctx.dat) / sizeof(ctx.dat[0]) == A64_MAX_INSTRUCTIONS, + "please use A64_MAX_INSTRUCTIONS!"); + + uint32_t *const outp_base = outp; + + while (--count >= 0) { + if (__fix_branch_imm(&inp, &outp, &ctx)) continue; + if (__fix_cond_comp_test_branch(&inp, &outp, &ctx)) continue; + if (__fix_loadlit(&inp, &outp, &ctx)) continue; + if (__fix_pcreladdr(&inp, &outp, &ctx)) continue; + + // without PC-relative offset + ctx.process_fix_map(ctx.get_and_set_current_index(inp, outp)); + *(outp++) = *(inp++); + } + + static constexpr uint_fast64_t mask = 0x03ffffffu; // 0b00000011111111111111111111111111 + auto callback = reinterpret_cast(inp); + auto pc_offset = static_cast(callback - reinterpret_cast(outp)) >> 2; + if (llabs(pc_offset) >= (mask >> 1)) { + if ((reinterpret_cast(outp + 2) & 7u) != 0u) { + outp[0] = A64_NOP; + ++outp; + } //if + outp[0] = 0x58000051u; // LDR X17, #0x8 + outp[1] = 0xd61f0220u; // BR X17 + *reinterpret_cast(outp + 2) = callback; + outp += 4; + } else { + outp[0] = 0x14000000u | (pc_offset & mask); // "B" ADDR_PCREL26 + ++outp; + } //if + + const uintptr_t total = (outp - outp_base) * sizeof(uint32_t); + __flush_cache(outp_base, total); // necessary +} + +//------------------------------------------------------------------------- + +extern "C" { +#define __attribute __attribute__ +#define aligned(x) __aligned__(x) +#define __intval(p) reinterpret_cast(p) +#define __uintval(p) reinterpret_cast(p) +#define __ptr(p) reinterpret_cast(p) +#define __page_size 4096 +#define __page_align(n) __align_up(static_cast(n), __page_size) +#define __ptr_align(x) __ptr(__align_down(reinterpret_cast(x), __page_size)) +#define __align_up(x, n) (((x) + ((n) - 1)) & ~((n) - 1)) +#define __align_down(x, n) ((x) & -(n)) +#define __countof(x) static_cast(sizeof(x) / sizeof((x)[0])) // must be signed +#define __atomic_increase(p) __sync_add_and_fetch(p, 1) +#define __sync_cmpswap(p, v, n) __sync_bool_compare_and_swap(p, v, n) +#define __predict_true(exp) __builtin_expect((exp) != 0, 1) +#define __make_rwx(p, n) ::mprotect(__ptr_align(p), \ + __page_align(__uintval(p) + n) != __page_align(__uintval(p)) ? __page_align(n) + __page_size : __page_align(n), \ + PROT_READ | PROT_WRITE | PROT_EXEC) + +//------------------------------------------------------------------------- + +static __attribute((aligned(__page_size))) uint32_t __insns_pool[A64_MAX_BACKUPS][A64_MAX_INSTRUCTIONS * 10]; + +//------------------------------------------------------------------------- + +class A64HookInit +{ +public: + A64HookInit() + { + __make_rwx(__insns_pool, sizeof(__insns_pool)); + } +}; +static A64HookInit __init; + +//------------------------------------------------------------------------- + +static uint32_t *FastAllocateTrampoline() +{ + static_assert((A64_MAX_INSTRUCTIONS * 10 * sizeof(uint32_t)) % 8 == 0, "8-byte align"); + static volatile int32_t __index = -1; + + int32_t i = __atomic_increase(&__index); + if (__predict_true(i >= 0 && i < __countof(__insns_pool))) { + return __insns_pool[i]; + } //if + + return NULL; +} + +//------------------------------------------------------------------------- + +A64_JNIEXPORT void *A64HookFunctionV(void *const symbol, void *const replace, + void *const rwx, const uintptr_t rwx_size) +{ + static constexpr uint_fast64_t mask = 0x03ffffffu; // 0b00000011111111111111111111111111 + + uint32_t *trampoline = static_cast(rwx), *original = static_cast(symbol); + + static_assert(A64_MAX_INSTRUCTIONS >= 5, "please fix A64_MAX_INSTRUCTIONS!"); + auto pc_offset = static_cast(__intval(replace) - __intval(symbol)) >> 2; + if (llabs(pc_offset) >= (mask >>1)) { + int32_t count = (reinterpret_cast(original + 2) & 7u) != 0u ? 5 : 4; + if (trampoline) { + if (rwx_size < count * 10u) { + return NULL; + } //if + __fix_instructions(original, count, trampoline); + } //if + + if (__make_rwx(original, 5 * sizeof(uint32_t)) == 0) { + if (count == 5) { + original[0] = A64_NOP; + ++original; + } //if + original[0] = 0x58000051u; // LDR X17, #0x8 + original[1] = 0xd61f0220u; // BR X17 + *reinterpret_cast(original + 2) = __intval(replace); + __flush_cache(symbol, 5 * sizeof(uint32_t)); + + } else { + trampoline = NULL; + } //if + } else { + if (trampoline) { + if (rwx_size < 1u * 10u) { + return NULL; + } //if + __fix_instructions(original, 1, trampoline); + } //if + + if (__make_rwx(original, 1 * sizeof(uint32_t)) == 0) { + __sync_cmpswap(original, *original, 0x14000000u | (pc_offset & mask)); // "B" ADDR_PCREL26 + __flush_cache(symbol, 1 * sizeof(uint32_t)); + + } else { + trampoline = NULL; + } //if + } //if + + return trampoline; +} + +//------------------------------------------------------------------------- + +A64_JNIEXPORT void A64HookFunction(void *const symbol, void *const replace, void **result) +{ + void *trampoline = NULL; + if (result != NULL) { + trampoline = FastAllocateTrampoline(); + *result = trampoline; + if (trampoline == NULL) return; + } //if + assert(__make_rwx(symbol, 4096) == 0); + trampoline = A64HookFunctionV(symbol, replace, trampoline, A64_MAX_INSTRUCTIONS * 10u); + if (trampoline == NULL && result != NULL) { + *result = NULL; + } //if +} +} + +#endif // defined(__aarch64__) \ No newline at end of file diff --git a/Bcore/src/main/jni/Substrate/And64InlineHook.hpp b/Bcore/src/main/jni/Substrate/And64InlineHook.hpp new file mode 100644 index 00000000..07fd29bb --- /dev/null +++ b/Bcore/src/main/jni/Substrate/And64InlineHook.hpp @@ -0,0 +1,45 @@ +/* + * @date : 2018/04/18 + * @author : Rprop (r_prop@outlook.com) + * https://github.com/Rprop/And64InlineHook + */ +/* + MIT License + + Copyright (c) 2018 Rprop (r_prop@outlook.com) + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + */ +#pragma once + +#include + +#define A64_MAX_BACKUPS 256 + +#ifdef __cplusplus +extern "C" { +#endif + +void A64HookFunction(void *const symbol, void *const replace, void **result); +void *A64HookFunctionV(void *const symbol, void *const replace, + void *const rwx, const uintptr_t rwx_size); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/Bcore/src/main/jni/Substrate/Buffer.hpp b/Bcore/src/main/jni/Substrate/Buffer.hpp new file mode 100644 index 00000000..34d9df32 --- /dev/null +++ b/Bcore/src/main/jni/Substrate/Buffer.hpp @@ -0,0 +1,38 @@ +/* Cydia Substrate - Powerful Code Insertion Platform + * Copyright (C) 2008-2011 Jay Freeman (saurik) +*/ + +/* GNU Lesser General Public License, Version 3 {{{ */ +/* + * Substrate is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * Substrate is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Substrate. If not, see . +**/ +/* }}} */ + +#ifndef SUBSTRATE_BUFFER_HPP +#define SUBSTRATE_BUFFER_HPP + +#include + +template +_disused static _finline void MSWrite(uint8_t *&buffer, Type_ value) { + *reinterpret_cast(buffer) = value; + buffer += sizeof(Type_); +} + +_disused static _finline void MSWrite(uint8_t *&buffer, uint8_t *data, size_t size) { + memcpy(buffer, data, size); + buffer += size; +} + +#endif//SUBSTRATE_BUFFER_HPP diff --git a/Bcore/src/main/jni/Substrate/CydiaSubstrate.h b/Bcore/src/main/jni/Substrate/CydiaSubstrate.h new file mode 100644 index 00000000..bb806aa9 --- /dev/null +++ b/Bcore/src/main/jni/Substrate/CydiaSubstrate.h @@ -0,0 +1,152 @@ +/* Cydia Substrate - Powerful Code Insertion Platform + * Copyright (C) 2008-2011 Jay Freeman (saurik) +*/ + +/* GNU Lesser General Public License, Version 3 {{{ */ +/* + * Substrate is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * Substrate is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Substrate. If not, see . +**/ +/* }}} */ + +#ifndef SUBSTRATE_H_ +#define SUBSTRATE_H_ + +#ifdef __APPLE__ +#ifdef __cplusplus +extern "C" { +#endif +#include +#ifdef __cplusplus +} +#endif + +#include +#include +#endif + +#include +#include + +#define _finline \ + inline __attribute__((__always_inline__)) +#define _disused \ + __attribute__((__unused__)) + +#define _extern \ + extern "C" __attribute__((__visibility__("default"))) + +#ifdef __cplusplus +#define _default(value) = value +#else +#define _default(value) +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +bool MSHookProcess(pid_t pid, const char *library); + +typedef const void *MSImageRef; + +MSImageRef MSGetImageByName(const char *file); +void *MSFindSymbol(MSImageRef image, const char *name); + +void MSHookFunction(void *symbol, void *replace, void **result); + +#ifdef __APPLE__ +#ifdef __arm__ +__attribute__((__deprecated__)) +IMP MSHookMessage(Class _class, SEL sel, IMP imp, const char *prefix _default(NULL)); +#endif +void MSHookMessageEx(Class _class, SEL sel, IMP imp, IMP *result); +#endif + +#ifdef SubstrateInternal +typedef void *SubstrateAllocatorRef; +typedef struct __SubstrateProcess *SubstrateProcessRef; +typedef struct __SubstrateMemory *SubstrateMemoryRef; + +SubstrateProcessRef SubstrateProcessCreate(SubstrateAllocatorRef allocator, pid_t pid); +void SubstrateProcessRelease(SubstrateProcessRef process); + +SubstrateMemoryRef SubstrateMemoryCreate(SubstrateAllocatorRef allocator, SubstrateProcessRef process, void *data, size_t size); +void SubstrateMemoryRelease(SubstrateMemoryRef memory); +#endif + +#ifdef __cplusplus +} +#endif + +#ifdef __cplusplus + +#ifdef SubstrateInternal +struct SubstrateHookMemory { + SubstrateMemoryRef handle_; + + SubstrateHookMemory(SubstrateProcessRef process, void *data, size_t size) : + handle_(SubstrateMemoryCreate(NULL, NULL, data, size)) + { + } + + ~SubstrateHookMemory() { + if (handle_ != NULL) + SubstrateMemoryRelease(handle_); + } +}; +#endif + + +template +static inline void MSHookFunction(Type_ *symbol, Type_ *replace, Type_ **result) { + MSHookFunction( + reinterpret_cast(symbol), + reinterpret_cast(replace), + reinterpret_cast(result) + ); +} + +template +static inline void MSHookFunction(Type_ *symbol, Type_ *replace) { + return MSHookFunction(symbol, replace, reinterpret_cast(NULL)); +} + +template +static inline void MSHookSymbol(Type_ *&value, const char *name, MSImageRef image = NULL) { + value = reinterpret_cast(MSFindSymbol(image, name)); +} + +template +static inline void MSHookFunction(const char *name, Type_ *replace, Type_ **result = NULL) { + Type_ *symbol; + MSHookSymbol(symbol, name); + return MSHookFunction(symbol, replace, result); +} + +#endif + +#define MSHook(type, name, args...) \ + _disused static type (*_ ## name)(args); \ + static type $ ## name(args) + +#ifdef __cplusplus +#define MSHake(name) \ + &$ ## name, &_ ## name +#else +#define MSHake(name) \ + &$ ## name, (void **) &_ ## name +#endif + + +#endif//SUBSTRATE_H_ diff --git a/Bcore/src/main/jni/Substrate/SubstrateARM.hpp b/Bcore/src/main/jni/Substrate/SubstrateARM.hpp new file mode 100644 index 00000000..bd372e89 --- /dev/null +++ b/Bcore/src/main/jni/Substrate/SubstrateARM.hpp @@ -0,0 +1,67 @@ +/* Cydia Substrate - Powerful Code Insertion Platform + * Copyright (C) 2008-2011 Jay Freeman (saurik) +*/ + +/* GNU Lesser General Public License, Version 3 {{{ */ +/* + * Substrate is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * Substrate is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Substrate. If not, see . +**/ +/* }}} */ + +#ifndef SUBSTRATE_ARM_HPP +#define SUBSTRATE_ARM_HPP + +enum A$r { + A$r0, A$r1, A$r2, A$r3, + A$r4, A$r5, A$r6, A$r7, + A$r8, A$r9, A$r10, A$r11, + A$r12, A$r13, A$r14, A$r15, + A$sp = A$r13, + A$lr = A$r14, + A$pc = A$r15 +}; + +enum A$c { + A$eq, A$ne, A$cs, A$cc, + A$mi, A$pl, A$vs, A$vc, + A$hi, A$ls, A$ge, A$lt, + A$gt, A$le, A$al, + A$hs = A$cs, + A$lo = A$cc +}; + +#define _abs(im) abs((int)im) + +#define A$mrs_rm_cpsr(rd) /* mrs rd, cpsr */ \ + (0xe10f0000 | ((rd) << 12)) +#define A$msr_cpsr_f_rm(rm) /* msr cpsr_f, rm */ \ + (0xe128f000 | (rm)) +#define A$ldr_rd_$rn_im$(rd, rn, im) /* ldr rd, [rn, #im] */ \ + (0xe5100000 | ((im) < 0 ? 0 : 1 << 23) | ((rn) << 16) | ((rd) << 12) | _abs(im)) +#define A$str_rd_$rn_im$(rd, rn, im) /* sr rd, [rn, #im] */ \ + (0xe5000000 | ((im) < 0 ? 0 : 1 << 23) | ((rn) << 16) | ((rd) << 12) | _abs(im)) +#define A$sub_rd_rn_$im(rd, rn, im) /* sub, rd, rn, #im */ \ + (0xe2400000 | ((rn) << 16) | ((rd) << 12) | (im & 0xff)) +#define A$blx_rm(rm) /* blx rm */ \ + (0xe12fff30 | (rm)) +#define A$mov_rd_rm(rd, rm) /* mov rd, rm */ \ + (0xe1a00000 | ((rd) << 12) | (rm)) +#define A$ldmia_sp$_$rs$(rs) /* ldmia sp!, {rs} */ \ + (0xe8b00000 | (A$sp << 16) | (rs)) +#define A$stmdb_sp$_$rs$(rs) /* stmdb sp!, {rs} */ \ + (0xe9200000 | (A$sp << 16) | (rs)) +#define A$stmia_sp$_$r0$ 0xe8ad0001 /* stmia sp!, {r0} */ +#define A$bx_r0 0xe12fff10 /* bx r0 */ + +#endif//SUBSTRATE_ARM_HPP diff --git a/Bcore/src/main/jni/Substrate/SubstrateDebug.cpp b/Bcore/src/main/jni/Substrate/SubstrateDebug.cpp new file mode 100644 index 00000000..2df6ef48 --- /dev/null +++ b/Bcore/src/main/jni/Substrate/SubstrateDebug.cpp @@ -0,0 +1,96 @@ +/* Cydia Substrate - Powerful Code Insertion Platform + * Copyright (C) 2008-2011 Jay Freeman (saurik) +*/ + +/* GNU Lesser General Public License, Version 3 {{{ */ +/* + * Substrate is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * Substrate is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Substrate. If not, see . +**/ +/* }}} */ + +#include "SubstrateHook.h" +#include "SubstrateDebug.hpp" + +#include +#include +#include + +_extern bool MSDebug; +bool MSDebug = false; + +static char _MSHexChar(uint8_t value) { + return value < 0x20 || value >= 0x80 ? '.' : value; +} + +#define HexWidth_ 16 +#define HexDepth_ 4 + +void MSLogHexEx(const void *vdata, size_t size, size_t stride, const char *mark) { + const uint8_t *data((const uint8_t *) vdata); + + size_t i(0), j; + + char d[256]; + size_t b(0); + d[0] = '\0'; + + while (i != size) { + if (i % HexWidth_ == 0) { + if (mark != NULL) + b += sprintf(d + b, "\n[%s] ", mark); + b += sprintf(d + b, "0x%.3zx:", i); + } + + b += sprintf(d + b, " "); + + for (size_t q(0); q != stride; ++q) + b += sprintf(d + b, "%.2x", data[i + stride - q - 1]); + + i += stride; + + for (size_t q(1); q != stride; ++q) + b += sprintf(d + b, " "); + + if (i % HexDepth_ == 0) + b += sprintf(d + b, " "); + + if (i % HexWidth_ == 0) { + b += sprintf(d + b, " "); + for (j = i - HexWidth_; j != i; ++j) + b += sprintf(d + b, "%c", _MSHexChar(data[j])); + + lprintf("%s", d); + b = 0; + d[0] = '\0'; + } + } + + if (i % HexWidth_ != 0) { + for (j = i % HexWidth_; j != HexWidth_; ++j) + b += sprintf(d + b, " "); + for (j = 0; j != (HexWidth_ - i % HexWidth_ + HexDepth_ - 1) / HexDepth_; ++j) + b += sprintf(d + b, " "); + b += sprintf(d + b, " "); + for (j = i / HexWidth_ * HexWidth_; j != i; ++j) + b += sprintf(d + b, "%c", _MSHexChar(data[j])); + + lprintf("%s", d); + b = 0; + d[0] = '\0'; + } +} + +void MSLogHex(const void *vdata, size_t size, const char *mark) { + return MSLogHexEx(vdata, size, 1, mark); +} diff --git a/Bcore/src/main/jni/Substrate/SubstrateDebug.hpp b/Bcore/src/main/jni/Substrate/SubstrateDebug.hpp new file mode 100644 index 00000000..9c554c85 --- /dev/null +++ b/Bcore/src/main/jni/Substrate/SubstrateDebug.hpp @@ -0,0 +1,33 @@ +/* Cydia Substrate - Powerful Code Insertion Platform + * Copyright (C) 2008-2011 Jay Freeman (saurik) +*/ + +/* GNU Lesser General Public License, Version 3 {{{ */ +/* + * Substrate is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * Substrate is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Substrate. If not, see . +**/ +/* }}} */ + +#ifndef SUBSTRATE_DEBUG_HPP +#define SUBSTRATE_DEBUG_HPP + +#include "SubstrateLog.hpp" +#define lprintf(format, ...) \ + MSLog(MSLogLevelNotice, format, ## __VA_ARGS__) + +extern "C" bool MSDebug; +void MSLogHexEx(const void *vdata, size_t size, size_t stride, const char *mark = 0); +void MSLogHex(const void *vdata, size_t size, const char *mark = 0); + +#endif//SUBSTRATE_DEBUG_HPP diff --git a/Bcore/src/main/jni/Substrate/SubstrateHook.cpp b/Bcore/src/main/jni/Substrate/SubstrateHook.cpp new file mode 100644 index 00000000..8e68a045 --- /dev/null +++ b/Bcore/src/main/jni/Substrate/SubstrateHook.cpp @@ -0,0 +1,962 @@ +/* Cydia Substrate - Powerful Code Insertion Platform + * Copyright (C) 2008-2011 Jay Freeman (saurik) +*/ + +/* GNU Lesser General Public License, Version 3 {{{ */ +/* + * Substrate is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * Substrate is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Substrate. If not, see . +**/ +/* }}} */ + +#define SubstrateInternal + +#include "And64InlineHook.hpp" + +#include + +#define _trace() do { \ + MSLog(MSLogLevelNotice, "_trace(%u)", __LINE__); \ +} while (false) + +#if defined(__i386__) || defined(__x86_64__) +#include "hde64.h" +#endif + +#include "SubstrateDebug.hpp" +#include "And64InlineHook.hpp" +#include "CydiaSubstrate.h" + +#include +#include +#include + +#ifdef __arm__ +/* WebCore (ARM) PC-Relative: +X 1 ldr r*,[pc,r*] != + 2 fldd d*,[pc,#*] +X 5 str r*,[pc,r*] != + 8 flds s*,[pc,#*] + 400 ldr r*,[pc,r*] == + 515 add r*, pc,r* == +X 4790 ldr r*,[pc,#*] */ + +// x=0; while IFS= read -r line; do if [[ ${#line} -ne 0 && $line == +([^\;]): ]]; then x=2; elif [[ $line == ' +'* && $x -ne 0 ]]; then ((--x)); echo "$x${line}"; fi; done WebCore.pc +// grep pc WebCore.pc | cut -c 40- | sed -Ee 's/^ldr *(ip|r[0-9]*),\[pc,\#0x[0-9a-f]*\].*/ ldr r*,[pc,#*]/;s/^add *r[0-9]*,pc,r[0-9]*.*/ add r*, pc,r*/;s/^(st|ld)r *r([0-9]*),\[pc,r([0-9]*)\].*/ \1r r\2,[pc,r\3]/;s/^fld(s|d) *(s|d)[0-9]*,\[pc,#0x[0-9a-f]*].*/fld\1 \2*,[pc,#*]/' | sort | uniq -c | sort -n + +#include "SubstrateARM.hpp" +#include "And64InlineHook.hpp" + +#define T$Label(l, r) \ + (((r) - (l)) * 2 - 4 + ((l) % 2 == 0 ? 0 : 2)) + +#define T$pop_$r0$ 0xbc01 // pop {r0} +#define T$b(im) /* b im */ \ + (0xde00 | (im & 0xff)) +#define T$blx(rm) /* blx rm */ \ + (0x4780 | (rm << 3)) +#define T$bx(rm) /* bx rm */ \ + (0x4700 | (rm << 3)) +#define T$nop /* nop */ \ + (0x46c0) + +#define T$add_rd_rm(rd, rm) /* add rd, rm */ \ + (0x4400 | (((rd) & 0x8) >> 3 << 7) | (((rm) & 0x8) >> 3 << 6) | (((rm) & 0x7) << 3) | ((rd) & 0x7)) +#define T$push_r(r) /* push r... */ \ + (0xb400 | (((r) & (1 << A$lr)) >> A$lr << 8) | ((r) & 0xff)) +#define T$pop_r(r) /* pop r... */ \ + (0xbc00 | (((r) & (1 << A$pc)) >> A$pc << 8) | ((r) & 0xff)) +#define T$mov_rd_rm(rd, rm) /* mov rd, rm */ \ + (0x4600 | (((rd) & 0x8) >> 3 << 7) | (((rm) & 0x8) >> 3 << 6) | (((rm) & 0x7) << 3) | ((rd) & 0x7)) +#define T$ldr_rd_$rn_im_4$(rd, rn, im) /* ldr rd, [rn, #im * 4] */ \ + (0x6800 | (((im) & 0x1f) << 6) | ((rn) << 3) | (rd)) +#define T$ldr_rd_$pc_im_4$(rd, im) /* ldr rd, [PC, #im * 4] */ \ + (0x4800 | ((rd) << 8) | ((im) & 0xff)) +#define T$cmp_rn_$im(rn, im) /* cmp rn, #im */ \ + (0x2000 | ((rn) << 8) | ((im) & 0xff)) +#define T$it$_cd(cd, ms) /* it, cd */ \ + (0xbf00 | ((cd) << 4) | (ms)) +#define T$cbz$_rn_$im(op, rn, im) /* cbz rn, #im */ \ + (0xb100 | ((op) << 11) | (((im) & 0x40) >> 6 << 9) | (((im) & 0x3e) >> 1 << 3) | (rn)) +#define T$b$_$im(cond, im) /* b #im */ \ + (cond == A$al ? 0xe000 | (((im) >> 1) & 0x7ff) : 0xd000 | ((cond) << 8) | (((im) >> 1) & 0xff)) + +#define T1$ldr_rt_$rn_im$(rt, rn, im) /* ldr rt, [rn, #im] */ \ + (0xf850 | ((im < 0 ? 0 : 1) << 7) | (rn)) +#define T2$ldr_rt_$rn_im$(rt, rn, im) /* ldr rt, [rn, #im] */ \ + (((rt) << 12) | _abs(im)) + +#define T1$mrs_rd_apsr(rd) /* mrs rd, apsr */ \ + (0xf3ef) +#define T2$mrs_rd_apsr(rd) /* mrs rd, apsr */ \ + (0x8000 | ((rd) << 8)) + +#define T1$msr_apsr_nzcvqg_rn(rn) /* msr apsr, rn */ \ + (0xf380 | (rn)) +#define T2$msr_apsr_nzcvqg_rn(rn) /* msr apsr, rn */ \ + (0x8c00) +#define T$msr_apsr_nzcvqg_rn(rn) /* msr apsr, rn */ \ + (T2$msr_apsr_nzcvqg_rn(rn) << 16 | T1$msr_apsr_nzcvqg_rn(rn)) + +static inline bool A$pcrel$r(uint32_t ic) { + return (ic & 0x0c000000) == 0x04000000 && (ic & 0xf0000000) != 0xf0000000 && + (ic & 0x000f0000) == 0x000f0000; +} + +static inline bool T$32bit$i(uint16_t ic) { + return ((ic & 0xe000) == 0xe000 && (ic & 0x1800) != 0x0000); +} + +static inline bool T$pcrel$cbz(uint16_t ic) { + return (ic & 0xf500) == 0xb100; +} + +static inline bool T$pcrel$b(uint16_t ic) { + return (ic & 0xf000) == 0xd000 && (ic & 0x0e00) != 0x0e00; +} + +static inline bool T2$pcrel$b(uint16_t *ic) { + return (ic[0] & 0xf800) == 0xf000 && + ((ic[1] & 0xd000) == 0x9000 || ((ic[1] & 0xd000) == 0x8000 && (ic[0] & 0x0380) != 0x0380)); +} + +static inline bool T$pcrel$bl(uint16_t *ic) { + return (ic[0] & 0xf800) == 0xf000 && ((ic[1] & 0xd000) == 0xd000 || (ic[1] & 0xd001) == 0xc000); +} + +static inline bool T$pcrel$ldr(uint16_t ic) { + return (ic & 0xf800) == 0x4800; +} + +static inline bool T$pcrel$add(uint16_t ic) { + return (ic & 0xff78) == 0x4478; +} + +static inline bool T$pcrel$ldrw(uint16_t ic) { + return (ic & 0xff7f) == 0xf85f; +} + +static size_t MSGetInstructionWidthThumb(void *start) { + uint16_t *thumb(reinterpret_cast(start)); + return T$32bit$i(thumb[0]) ? 4 : 2; +} + +static size_t MSGetInstructionWidthARM(void *start) { + return 4; +} + +extern "C" size_t MSGetInstructionWidth(void *start) { + if ((reinterpret_cast(start) & 0x1) == 0) + return MSGetInstructionWidthARM(start); + else + return MSGetInstructionWidthThumb( + reinterpret_cast(reinterpret_cast(start) & ~0x1)); +} + +static size_t SubstrateHookFunctionThumb(SubstrateProcessRef process, void *symbol, void *replace, + void **result) { + if (symbol == NULL) + return 0; + printf("SubstrateHookFunctionThumb\n"); + uint16_t *area(reinterpret_cast(symbol)); + + unsigned align((reinterpret_cast(area) & 0x2) == 0 ? 0 : 1); + uint16_t *thumb(area + align); + + uint32_t *arm(reinterpret_cast(thumb + 2)); + uint16_t *trail(reinterpret_cast(arm + 2)); + + if ( + (align == 0 || area[0] == T$nop) && + thumb[0] == T$bx(A$pc) && + thumb[1] == T$nop && + arm[0] == A$ldr_rd_$rn_im$(A$pc, A$pc, 4 - 8) + ) { + if (result != NULL) + *result = reinterpret_cast(arm[1]); + + SubstrateHookMemory code(process, arm + 1, sizeof(uint32_t) * 1); + + arm[1] = reinterpret_cast(replace); + + return sizeof(arm[0]); + } + + size_t required((trail - area) * sizeof(uint16_t)); + + size_t used(0); + while (used < required) + used += MSGetInstructionWidthThumb(reinterpret_cast(area) + used); + used = (used + sizeof(uint16_t) - 1) / sizeof(uint16_t) * sizeof(uint16_t); + + size_t blank((used - required) / sizeof(uint16_t)); + + uint16_t backup[used / sizeof(uint16_t)]; + memcpy(backup, area, used); + + if (MSDebug) { + char name[16]; + sprintf(name, "%p", area); + MSLogHexEx(area, used + sizeof(uint16_t), 2, name); + } + + if (result != NULL) { + + size_t length(used); + for (unsigned offset(0); offset != used / sizeof(uint16_t); ++offset) + if (T$pcrel$ldr(backup[offset])) + length += 3 * sizeof(uint16_t); + else if (T$pcrel$b(backup[offset])) + length += 6 * sizeof(uint16_t); + else if (T2$pcrel$b(backup + offset)) { + length += 5 * sizeof(uint16_t); + ++offset; + } else if (T$pcrel$bl(backup + offset)) { + length += 5 * sizeof(uint16_t); + ++offset; + } else if (T$pcrel$cbz(backup[offset])) { + length += 16 * sizeof(uint16_t); + } else if (T$pcrel$ldrw(backup[offset])) { + length += 4 * sizeof(uint16_t); + ++offset; + } else if (T$pcrel$add(backup[offset])) + length += 6 * sizeof(uint16_t); + else if (T$32bit$i(backup[offset])) + ++offset; + + unsigned pad((length & 0x2) == 0 ? 0 : 1); + length += (pad + 2) * sizeof(uint16_t) + 2 * sizeof(uint32_t); + + uint16_t *buffer(reinterpret_cast(mmap( + NULL, length, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0 + ))); + + if (buffer == MAP_FAILED) { + MSLog(MSLogLevelError, "MS:Error:mmap() = %d", errno); + *result = NULL; + return 0; + } + + if (false) + fail: + { + munmap(buffer, length); + *result = NULL; + return 0; + } + + size_t start(pad), end(length / sizeof(uint16_t)); + uint32_t *trailer(reinterpret_cast(buffer + end)); + for (unsigned offset(0); offset != used / sizeof(uint16_t); ++offset) { + if (T$pcrel$ldr(backup[offset])) { + union { + uint16_t value; + + struct { + uint16_t immediate : 8; + uint16_t rd : 3; + uint16_t : 5; + }; + } bits = {backup[offset + 0]}; + + buffer[start + 0] = T$ldr_rd_$pc_im_4$(bits.rd, T$Label(start + 0, end - 2) / 4); + buffer[start + 1] = T$ldr_rd_$rn_im_4$(bits.rd, bits.rd, 0); + + // XXX: this code "works", but is "wrong": the mechanism is more complex than this + *--trailer = ((reinterpret_cast(area + offset) + 4) & ~0x2) + + bits.immediate * 4; + + start += 2; + end -= 2; + } else if (T$pcrel$b(backup[offset])) { + union { + uint16_t value; + + struct { + uint16_t imm8 : 8; + uint16_t cond : 4; + uint16_t /*1101*/ : 4; + }; + } bits = {backup[offset + 0]}; + + intptr_t jump(bits.imm8 << 1); + jump |= 1; + jump <<= 23; + jump >>= 23; + + buffer[start + 0] = T$b$_$im(bits.cond, (end - 6 - (start + 0)) * 2 - 4); + + *--trailer = reinterpret_cast(area + offset) + 4 + jump; + *--trailer = A$ldr_rd_$rn_im$(A$pc, A$pc, 4 - 8); + *--trailer = T$nop << 16 | T$bx(A$pc); + + start += 1; + end -= 6; + } else if (T2$pcrel$b(backup + offset)) { + union { + uint16_t value; + + struct { + uint16_t imm6 : 6; + uint16_t cond : 4; + uint16_t s : 1; + uint16_t : 5; + }; + } bits = {backup[offset + 0]}; + + union { + uint16_t value; + + struct { + uint16_t imm11 : 11; + uint16_t j2 : 1; + uint16_t a : 1; + uint16_t j1 : 1; + uint16_t : 2; + }; + } exts = {backup[offset + 1]}; + + intptr_t jump(1); + jump |= exts.imm11 << 1; + jump |= bits.imm6 << 12; + + if (exts.a) { + jump |= bits.s << 24; + jump |= (~(bits.s ^ exts.j1) & 0x1) << 23; + jump |= (~(bits.s ^ exts.j2) & 0x1) << 22; + jump |= bits.cond << 18; + jump <<= 7; + jump >>= 7; + } else { + jump |= bits.s << 20; + jump |= exts.j2 << 19; + jump |= exts.j1 << 18; + jump <<= 11; + jump >>= 11; + } + + buffer[start + 0] = T$b$_$im(exts.a ? A$al : bits.cond, + (end - 6 - (start + 0)) * 2 - 4); + + *--trailer = reinterpret_cast(area + offset) + 4 + jump; + *--trailer = A$ldr_rd_$rn_im$(A$pc, A$pc, 4 - 8); + *--trailer = T$nop << 16 | T$bx(A$pc); + + ++offset; + start += 1; + end -= 6; + } else if (T$pcrel$bl(backup + offset)) { + union { + uint16_t value; + + struct { + uint16_t immediate : 10; + uint16_t s : 1; + uint16_t : 5; + }; + } bits = {backup[offset + 0]}; + + union { + uint16_t value; + + struct { + uint16_t immediate : 11; + uint16_t j2 : 1; + uint16_t x : 1; + uint16_t j1 : 1; + uint16_t : 2; + }; + } exts = {backup[offset + 1]}; + + int32_t jump(0); + jump |= bits.s << 24; + jump |= (~(bits.s ^ exts.j1) & 0x1) << 23; + jump |= (~(bits.s ^ exts.j2) & 0x1) << 22; + jump |= bits.immediate << 12; + jump |= exts.immediate << 1; + jump |= exts.x; + jump <<= 7; + jump >>= 7; + + buffer[start + 0] = T$push_r(1 << A$r7); + buffer[start + 1] = T$ldr_rd_$pc_im_4$(A$r7, + ((end - 2 - (start + 1)) * 2 - 4 + 2) / 4); + buffer[start + 2] = T$mov_rd_rm(A$lr, A$r7); + buffer[start + 3] = T$pop_r(1 << A$r7); + buffer[start + 4] = T$blx(A$lr); + + *--trailer = reinterpret_cast(area + offset) + 4 + jump; + + ++offset; + start += 5; + end -= 2; + } else if (T$pcrel$cbz(backup[offset])) { + union { + uint16_t value; + + struct { + uint16_t rn : 3; + uint16_t immediate : 5; + uint16_t : 1; + uint16_t i : 1; + uint16_t : 1; + uint16_t op : 1; + uint16_t : 4; + }; + } bits = {backup[offset + 0]}; + + intptr_t jump(1); + jump |= bits.i << 6; + jump |= bits.immediate << 1; + + //jump <<= 24; + //jump >>= 24; + + unsigned rn(bits.rn); + unsigned rt(rn == A$r7 ? A$r6 : A$r7); + + buffer[start + 0] = T$push_r(1 << rt); + buffer[start + 1] = T1$mrs_rd_apsr(rt); + buffer[start + 2] = T2$mrs_rd_apsr(rt); + buffer[start + 3] = T$cbz$_rn_$im(bits.op, rn, (end - 10 - (start + 3)) * 2 - 4); + buffer[start + 4] = T1$msr_apsr_nzcvqg_rn(rt); + buffer[start + 5] = T2$msr_apsr_nzcvqg_rn(rt); + buffer[start + 6] = T$pop_r(1 << rt); + + *--trailer = reinterpret_cast(area + offset) + 4 + jump; + *--trailer = A$ldr_rd_$rn_im$(A$pc, A$pc, 4 - 8); + *--trailer = T$nop << 16 | T$bx(A$pc); + *--trailer = T$nop << 16 | T$pop_r(1 << rt); + *--trailer = T$msr_apsr_nzcvqg_rn(rt); + +#if 0 + if ((start & 0x1) == 0) + buffer[start++] = T$nop; + buffer[start++] = T$bx(A$pc); + buffer[start++] = T$nop; + + uint32_t *arm(reinterpret_cast(buffer + start)); + arm[0] = A$add(A$lr, A$pc, 1); + arm[1] = A$ldr_rd_$rn_im$(A$pc, A$pc, (trailer - arm) * sizeof(uint32_t) - 8); +#endif + + start += 7; + end -= 10; + } else if (T$pcrel$ldrw(backup[offset])) { + union { + uint16_t value; + + struct { + uint16_t : 7; + uint16_t u : 1; + uint16_t : 8; + }; + } bits = {backup[offset + 0]}; + + union { + uint16_t value; + + struct { + uint16_t immediate : 12; + uint16_t rt : 4; + }; + } exts = {backup[offset + 1]}; + + buffer[start + 0] = T1$ldr_rt_$rn_im$(exts.rt, A$pc, T$Label(start + 0, end - 2)); + buffer[start + 1] = T2$ldr_rt_$rn_im$(exts.rt, A$pc, T$Label(start + 0, end - 2)); + + buffer[start + 2] = T1$ldr_rt_$rn_im$(exts.rt, exts.rt, 0); + buffer[start + 3] = T2$ldr_rt_$rn_im$(exts.rt, exts.rt, 0); + + // XXX: this code "works", but is "wrong": the mechanism is more complex than this + *--trailer = ((reinterpret_cast(area + offset) + 4) & ~0x2) + + (bits.u == 0 ? -exts.immediate : exts.immediate); + + ++offset; + start += 4; + end -= 2; + } else if (T$pcrel$add(backup[offset])) { + union { + uint16_t value; + + struct { + uint16_t rd : 3; + uint16_t rm : 3; + uint16_t h2 : 1; + uint16_t h1 : 1; + uint16_t : 8; + }; + } bits = {backup[offset + 0]}; + + if (bits.h1) { + MSLog(MSLogLevelError, "MS:Error:pcrel(%u):add (rd > r7)", offset); + goto fail; + } + + unsigned rt(bits.rd == A$r7 ? A$r6 : A$r7); + + buffer[start + 0] = T$push_r(1 << rt); + buffer[start + 1] = T$mov_rd_rm(rt, (bits.h1 << 3) | bits.rd); + buffer[start + 2] = T$ldr_rd_$pc_im_4$(bits.rd, T$Label(start + 2, end - 2) / 4); + buffer[start + 3] = T$add_rd_rm((bits.h1 << 3) | bits.rd, rt); + buffer[start + 4] = T$pop_r(1 << rt); + *--trailer = reinterpret_cast(area + offset) + 4; + + start += 5; + end -= 2; + } else if (T$32bit$i(backup[offset])) { + buffer[start++] = backup[offset]; + buffer[start++] = backup[++offset]; + } else { + buffer[start++] = backup[offset]; + } + } + + buffer[start++] = T$bx(A$pc); + buffer[start++] = T$nop; + + uint32_t *transfer = reinterpret_cast(buffer + start); + transfer[0] = A$ldr_rd_$rn_im$(A$pc, A$pc, 4 - 8); + transfer[1] = reinterpret_cast(area + used / sizeof(uint16_t)) + 1; + + if (mprotect(buffer, length, PROT_READ | PROT_EXEC) == -1) { + MSLog(MSLogLevelError, "MS:Error:mprotect():%d", errno); + return 0; + } + + *result = reinterpret_cast(buffer + pad) + 1; + + if (MSDebug) { + char name[16]; + sprintf(name, "%p", *result); + MSLogHexEx(buffer, length, 2, name); + } + + } + + { + SubstrateHookMemory code(process, area, used); + + if (align != 0) + area[0] = T$nop; + + thumb[0] = T$bx(A$pc); + thumb[1] = T$nop; + + arm[0] = A$ldr_rd_$rn_im$(A$pc, A$pc, 4 - 8); + arm[1] = reinterpret_cast(replace); + + for (unsigned offset(0); offset != blank; ++offset) + trail[offset] = T$nop; + } + + if (MSDebug) { + char name[16]; + sprintf(name, "%p", area); + MSLogHexEx(area, used + sizeof(uint16_t), 2, name); + } + + return used; +} + +static size_t +SubstrateHookFunctionARM(SubstrateProcessRef process, void *symbol, void *replace, void **result) { + if (symbol == NULL) + return 0; + printf("SubstrateHookFunctionARM\n"); + uint32_t *area(reinterpret_cast(symbol)); + uint32_t *arm(area); + + const size_t used(8); + + uint32_t backup[used / sizeof(uint32_t)] = {arm[0], arm[1]}; + + if (MSDebug) { + char name[16]; + sprintf(name, "%p", area); + MSLogHexEx(area, used + sizeof(uint32_t), 4, name); + } + + if (result != NULL) { + + if (backup[0] == A$ldr_rd_$rn_im$(A$pc, A$pc, 4 - 8)) { + *result = reinterpret_cast(backup[1]); + + return sizeof(backup[0]); + } + + size_t length(used); + for (unsigned offset(0); offset != used / sizeof(uint32_t); ++offset) + if (A$pcrel$r(backup[offset])) { + if ((backup[offset] & 0x02000000) == 0 || + (backup[offset] & 0x0000f000 >> 12) != (backup[offset] & 0x0000000f)) + length += 2 * sizeof(uint32_t); + else + length += 4 * sizeof(uint32_t); + } + + length += 2 * sizeof(uint32_t); + + uint32_t *buffer(reinterpret_cast(mmap( + NULL, length, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0 + ))); + + if (buffer == MAP_FAILED) { + MSLog(MSLogLevelError, "MS:Error:mmap() = %d", errno); + *result = NULL; + return 0; + } + + if (false) + fail: + { + munmap(buffer, length); + *result = NULL; + return 0; + } + + size_t start(0), end(length / sizeof(uint32_t)); + uint32_t *trailer(reinterpret_cast(buffer + end)); + for (unsigned offset(0); offset != used / sizeof(uint32_t); ++offset) + if (A$pcrel$r(backup[offset])) { + union { + uint32_t value; + + struct { + uint32_t rm : 4; + uint32_t : 1; + uint32_t shift : 2; + uint32_t shiftamount : 5; + uint32_t rd : 4; + uint32_t rn : 4; + uint32_t l : 1; + uint32_t w : 1; + uint32_t b : 1; + uint32_t u : 1; + uint32_t p : 1; + uint32_t mode : 1; + uint32_t type : 2; + uint32_t cond : 4; + }; + } bits = {backup[offset + 0]}, copy(bits); + + bool guard; + if (bits.mode == 0 || bits.rd != bits.rm) { + copy.rn = bits.rd; + guard = false; + } else { + copy.rn = bits.rm != A$r0 ? A$r0 : A$r1; + guard = true; + } + + if (guard) + buffer[start++] = A$stmdb_sp$_$rs$((1 << copy.rn)); + + buffer[start + 0] = A$ldr_rd_$rn_im$(copy.rn, A$pc, + (end - 1 - (start + 0)) * 4 - 8); + buffer[start + 1] = copy.value; + + start += 2; + + if (guard) + buffer[start++] = A$ldmia_sp$_$rs$((1 << copy.rn)); + + *--trailer = reinterpret_cast(area + offset) + 8; + end -= 1; + } else + buffer[start++] = backup[offset]; + + buffer[start + 0] = A$ldr_rd_$rn_im$(A$pc, A$pc, 4 - 8); + buffer[start + 1] = reinterpret_cast(area + used / sizeof(uint32_t)); + + if (mprotect(buffer, length, PROT_READ | PROT_EXEC) == -1) { + MSLog(MSLogLevelError, "MS:Error:mprotect():%d", errno); + goto fail; + } + + *result = buffer; + + if (MSDebug) { + char name[16]; + sprintf(name, "%p", *result); + MSLogHexEx(buffer, length, 4, name); + } + + } + + { + SubstrateHookMemory code(process, symbol, used); + + arm[0] = A$ldr_rd_$rn_im$(A$pc, A$pc, 4 - 8); + arm[1] = reinterpret_cast(replace); + } + + if (MSDebug) { + char name[16]; + sprintf(name, "%p", area); + MSLogHexEx(area, used + sizeof(uint32_t), 4, name); + } + + return used; +} + +static size_t +SubstrateHookFunction(SubstrateProcessRef process, void *symbol, void *replace, void **result) { + if (MSDebug) + MSLog(MSLogLevelNotice, "SubstrateHookFunction(%p, %p, %p, %p)\n", process, symbol, replace, + result); + + if ((reinterpret_cast(symbol) & 0x1) == 0) + return SubstrateHookFunctionARM(process, symbol, replace, result); + else + return SubstrateHookFunctionThumb(process, reinterpret_cast( + reinterpret_cast(symbol) & ~0x1), replace, result); +} + +#endif + +#if defined(__i386__) || defined(__x86_64__) + +#include "SubstrateX86.hpp" + +static size_t MSGetInstructionWidthIntel(void *start) { + hde64s decode; + return hde64_disasm(start, &decode); +} + +static void SubstrateHookFunction(SubstrateProcessRef process, void *symbol, void *replace, void **result) { + if (MSDebug) + MSLog(MSLogLevelNotice, "MSHookFunction(%p, %p, %p)\n", symbol, replace, result); + if (symbol == NULL) + return; + + uintptr_t source(reinterpret_cast(symbol)); + uintptr_t target(reinterpret_cast(replace)); + + uint8_t *area(reinterpret_cast(symbol)); + + size_t required(MSSizeOfJump(target, source)); + + if (MSDebug) { + char name[16]; + sprintf(name, "%p", area); + MSLogHex(area, 32, name); + } + + size_t used(0); + while (used < required) { + size_t width(MSGetInstructionWidthIntel(area + used)); + if (width == 0) { + MSLog(MSLogLevelError, "MS:Error:MSGetInstructionWidthIntel(%p) == 0", area + used); + return; + } + + used += width; + } + + size_t blank(used - required); + + if (MSDebug) { + char name[16]; + sprintf(name, "%p", area); + MSLogHex(area, used + sizeof(uint16_t), name); + } + + uint8_t backup[used]; + memcpy(backup, area, used); + + if (result != NULL) { + + if (backup[0] == 0xe9) { + *result = reinterpret_cast(source + 5 + *reinterpret_cast(backup + 1)); + return; + } + + if (!ia32 && backup[0] == 0xff && backup[1] == 0x25) { + *result = *reinterpret_cast(source + 6 + *reinterpret_cast(backup + 2)); + return; + } + + size_t length(used + MSSizeOfJump(source + used)); + + for (size_t offset(0), width; offset != used; offset += width) { + hde64s decode; + hde64_disasm(backup + offset, &decode); + width = decode.len; + //_assert(width != 0 && offset + width <= used); + +#ifdef __LP64__ + if ((decode.modrm & 0xc7) == 0x05) { + if (decode.opcode == 0x8b) { + void *destiny(area + offset + width + int32_t(decode.disp.disp32)); + uint8_t reg(decode.rex_r << 3 | decode.modrm_reg); + length -= decode.len; + length += MSSizeOfPushPointer(destiny); + length += MSSizeOfPop(reg); + length += MSSizeOfMove64(); + } else { + MSLog(MSLogLevelError, "MS:Error: Unknown RIP-Relative (%.2x %.2x)", decode.opcode, decode.opcode2); + continue; + } + } else +#endif + + if (backup[offset] == 0xe8) { + int32_t relative(*reinterpret_cast(backup + offset + 1)); + void *destiny(area + offset + decode.len + relative); + + if (relative == 0) { + length -= decode.len; + length += MSSizeOfPushPointer(destiny); + } else { + length += MSSizeOfSkip(); + length += MSSizeOfJump(destiny); + } + } else if (backup[offset] == 0xeb) { + length -= decode.len; + length += MSSizeOfJump(area + offset + decode.len + *reinterpret_cast(backup + offset + 1)); + } else if (backup[offset] == 0xe9) { + length -= decode.len; + length += MSSizeOfJump(area + offset + decode.len + *reinterpret_cast(backup + offset + 1)); + } else if ( + backup[offset] == 0xe3 || + (backup[offset] & 0xf0) == 0x70 + // XXX: opcode2 & 0xf0 is 0x80? + ) { + length += decode.len; + length += MSSizeOfJump(area + offset + decode.len + *reinterpret_cast(backup + offset + 1)); + } + } + + uint8_t *buffer(reinterpret_cast(mmap( + NULL, length, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0 + ))); + + if (buffer == MAP_FAILED) { + MSLog(MSLogLevelError, "MS:Error:mmap() = %d", errno); + *result = NULL; + return; + } + + if (false) fail: { + munmap(buffer, length); + *result = NULL; + return; + } + + { + uint8_t *current(buffer); + + for (size_t offset(0), width; offset != used; offset += width) { + hde64s decode; + hde64_disasm(backup + offset, &decode); + width = decode.len; + //_assert(width != 0 && offset + width <= used); + +#ifdef __LP64__ + if ((decode.modrm & 0xc7) == 0x05) { + if (decode.opcode == 0x8b) { + void *destiny(area + offset + width + int32_t(decode.disp.disp32)); + uint8_t reg(decode.rex_r << 3 | decode.modrm_reg); + MSPushPointer(current, destiny); + MSWritePop(current, reg); + MSWriteMove64(current, reg, reg); + } else { + MSLog(MSLogLevelError, "MS:Error: Unknown RIP-Relative (%.2x %.2x)", decode.opcode, decode.opcode2); + goto copy; + } + } else +#endif + + if (backup[offset] == 0xe8) { + int32_t relative(*reinterpret_cast(backup + offset + 1)); + if (relative == 0) + MSPushPointer(current, area + offset + decode.len); + else { + MSWrite(current, 0xe8); + MSWrite(current, MSSizeOfSkip()); + void *destiny(area + offset + decode.len + relative); + MSWriteSkip(current, MSSizeOfJump(destiny, current + MSSizeOfSkip())); + MSWriteJump(current, destiny); + } + } else if (backup[offset] == 0xeb) + MSWriteJump(current, area + offset + decode.len + *reinterpret_cast(backup + offset + 1)); + else if (backup[offset] == 0xe9) + MSWriteJump(current, area + offset + decode.len + *reinterpret_cast(backup + offset + 1)); + else if ( + backup[offset] == 0xe3 || + (backup[offset] & 0xf0) == 0x70 + ) { + MSWrite(current, backup[offset]); + MSWrite(current, 2); + MSWrite(current, 0xeb); + void *destiny(area + offset + decode.len + *reinterpret_cast(backup + offset + 1)); + MSWrite(current, MSSizeOfJump(destiny, current + 1)); + MSWriteJump(current, destiny); + } else +#ifdef __LP64__ + copy: +#endif + { + MSWrite(current, backup + offset, width); + } + } + + MSWriteJump(current, area + used); + } + + if (mprotect(buffer, length, PROT_READ | PROT_EXEC) == -1) { + MSLog(MSLogLevelError, "MS:Error:mprotect():%d", errno); + goto fail; + } + + *result = buffer; + + if (MSDebug) { + char name[16]; + sprintf(name, "%p", *result); + MSLogHex(buffer, length, name); + } + + } + + { + SubstrateHookMemory code(process, area, used); + + uint8_t *current(area); + MSWriteJump(current, target); + for (unsigned offset(0); offset != blank; ++offset) + MSWrite(current, 0x90); + } + + if (MSDebug) { + char name[16]; + sprintf(name, "%p", area); + MSLogHex(area, used + sizeof(uint16_t), name); + } +} +#endif + +_extern void MSHookFunction(void *symbol, void *replace, void **result) { +#ifdef __aarch64__ + A64HookFunction(symbol, replace, result); +#else + SubstrateHookFunction(NULL, symbol, replace, result); +#endif +} + +#if defined(__APPLE__) && defined(__arm__) +_extern void _Z14MSHookFunctionPvS_PS_(void *symbol, void *replace, void **result) { + return MSHookFunction(symbol, replace, result); +} +#endif diff --git a/Bcore/src/main/jni/Substrate/SubstrateHook.h b/Bcore/src/main/jni/Substrate/SubstrateHook.h new file mode 100644 index 00000000..40a0296c --- /dev/null +++ b/Bcore/src/main/jni/Substrate/SubstrateHook.h @@ -0,0 +1,19 @@ +#ifndef __SUBSTRATEHOOK_H__ +#define __SUBSTRATEHOOK_H__ + + +#include + +#define _extern extern "C" __attribute__((__visibility__("default"))) + +#ifdef __cplusplus +extern "C" { +#endif + +void MSHookFunction(void *symbol, void *replace, void **result); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/Bcore/src/main/jni/Substrate/SubstrateLog.hpp b/Bcore/src/main/jni/Substrate/SubstrateLog.hpp new file mode 100644 index 00000000..3e572801 --- /dev/null +++ b/Bcore/src/main/jni/Substrate/SubstrateLog.hpp @@ -0,0 +1,40 @@ +/* Cydia Substrate - Powerful Code Insertion Platform + * Copyright (C) 2008-2011 Jay Freeman (saurik) +*/ + +/* GNU Lesser General Public License, Version 3 {{{ */ +/* + * Substrate is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * Substrate is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Substrate. If not, see . +**/ +/* }}} */ + +#ifndef SUBSTRATE_LOG_HPP +#define SUBSTRATE_LOG_HPP + +#if 0 +#include + +#define MSLog(level, format, ...) ((void)__android_log_print(level, "NNNN", format, __VA_ARGS__)) + +#define MSLogLevelNotice ANDROID_LOG_INFO +#define MSLogLevelWarning ANDROID_LOG_WARN +#define MSLogLevelError ANDROID_LOG_ERROR + +#else + +#define MSLog(level, format, ...) printf(format, __VA_ARGS__) + +#endif + +#endif//SUBSTRATE_LOG_HPP diff --git a/Bcore/src/main/jni/Substrate/SubstratePosixMemory.cpp b/Bcore/src/main/jni/Substrate/SubstratePosixMemory.cpp new file mode 100644 index 00000000..709cb228 --- /dev/null +++ b/Bcore/src/main/jni/Substrate/SubstratePosixMemory.cpp @@ -0,0 +1,75 @@ +/* Cydia Substrate - Powerful Code Insertion Platform + * Copyright (C) 2008-2011 Jay Freeman (saurik) +*/ + +/* GNU Lesser General Public License, Version 3 {{{ */ +/* + * Substrate is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * Substrate is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Substrate. If not, see . +**/ +/* }}} */ + +#define SubstrateInternal +#include "CydiaSubstrate.h" +#include "SubstrateLog.hpp" + +#include + +#include +#include +#include + +extern "C" void __clear_cache (void *beg, void *end); + +struct __SubstrateMemory { + void *address_; + size_t width_; + + __SubstrateMemory(void *address, size_t width) : + address_(address), + width_(width) + { + } +}; + +extern "C" SubstrateMemoryRef SubstrateMemoryCreate(SubstrateAllocatorRef allocator, SubstrateProcessRef process, void *data, size_t size) { + if (allocator != NULL) { + MSLog(MSLogLevelError, "MS:Error:allocator != %d", 0); + return NULL; + } + + if (size == 0) + return NULL; + + long page(sysconf(_SC_PAGESIZE)); // Portable applications should employ sysconf(_SC_PAGESIZE) instead of getpagesize + + uintptr_t base(reinterpret_cast(data) / page * page); + size_t width(((reinterpret_cast(data) + size - 1) / page + 1) * page - base); + void *address(reinterpret_cast(base)); + + if (mprotect(address, width, PROT_READ | PROT_WRITE | PROT_EXEC) == -1) { + MSLog(MSLogLevelError, "MS:Error:mprotect() = %d", errno); + return NULL; + } + + return new __SubstrateMemory(address, width); +} + +extern "C" void SubstrateMemoryRelease(SubstrateMemoryRef memory) { + if (mprotect(memory->address_, memory->width_, PROT_READ | PROT_WRITE | PROT_EXEC) == -1) + MSLog(MSLogLevelError, "MS:Error:mprotect() = %d", errno); + + __clear_cache(reinterpret_cast(memory->address_), reinterpret_cast(memory->address_) + memory->width_); + + delete memory; +} diff --git a/Bcore/src/main/jni/Substrate/SubstrateX86.hpp b/Bcore/src/main/jni/Substrate/SubstrateX86.hpp new file mode 100644 index 00000000..1e85f9ed --- /dev/null +++ b/Bcore/src/main/jni/Substrate/SubstrateX86.hpp @@ -0,0 +1,200 @@ +/* Cydia Substrate - Powerful Code Insertion Platform + * Copyright (C) 2008-2011 Jay Freeman (saurik) +*/ + +/* GNU Lesser General Public License, Version 3 {{{ */ +/* + * Substrate is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * Substrate is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Substrate. If not, see . +**/ +/* }}} */ + +#ifndef SUBSTRATE_X86_HPP +#define SUBSTRATE_X86_HPP + +#include "Buffer.hpp" + +#ifdef __LP64__ +static const bool ia32 = false; +#else +static const bool ia32 = true; +#endif + +enum I$r { + I$rax, I$rcx, I$rdx, I$rbx, + I$rsp, I$rbp, I$rsi, I$rdi, + I$r8, I$r9, I$r10, I$r11, + I$r12, I$r13, I$r14, I$r15, +}; + +_disused static bool MSIs32BitOffset(uintptr_t target, uintptr_t source) { + intptr_t offset(target - source); + return int32_t(offset) == offset; +} + +_disused static size_t MSSizeOfSkip() { + return 5; +} + +_disused static size_t MSSizeOfPushPointer(uintptr_t target) { + return uint64_t(target) >> 32 == 0 ? 5 : 13; +} + +_disused static size_t MSSizeOfPushPointer(void *target) { + return MSSizeOfPushPointer(reinterpret_cast(target)); +} + +_disused static size_t MSSizeOfJump(bool blind, uintptr_t target, uintptr_t source = 0) { + if (ia32 || (!blind && MSIs32BitOffset(target, source + 5))) + return MSSizeOfSkip(); + else + return MSSizeOfPushPointer(target) + 1; +} + +_disused static size_t MSSizeOfJump(uintptr_t target, uintptr_t source) { + return MSSizeOfJump(false, target, source); +} + +_disused static size_t MSSizeOfJump(uintptr_t target) { + return MSSizeOfJump(true, target); +} + +_disused static size_t MSSizeOfJump(void *target, void *source) { + return MSSizeOfJump(reinterpret_cast(target), reinterpret_cast(source)); +} + +_disused static size_t MSSizeOfJump(void *target) { + return MSSizeOfJump(reinterpret_cast(target)); +} + +_disused static void MSWriteSkip(uint8_t *¤t, ssize_t size) { + MSWrite(current, 0xe9); + MSWrite(current, size); +} + +_disused static void MSPushPointer(uint8_t *¤t, uintptr_t target) { + MSWrite(current, 0x68); + MSWrite(current, target); + + if (uint32_t high = uint64_t(target) >> 32) { + MSWrite(current, 0xc7); + MSWrite(current, 0x44); + MSWrite(current, 0x24); + MSWrite(current, 0x04); + MSWrite(current, high); + } +} + +_disused static void MSPushPointer(uint8_t *¤t, void *target) { + return MSPushPointer(current, reinterpret_cast(target)); +} + +_disused static void MSWriteCall(uint8_t *¤t, I$r target) { + if (target >> 3 != 0) + MSWrite(current, 0x40 | (target & 0x08) >> 3); + MSWrite(current, 0xff); + MSWrite(current, 0xd0 | (target & 0x07)); +} + +_disused static void MSWriteCall(uint8_t *¤t, uintptr_t target) { + uintptr_t source(reinterpret_cast(current)); + + if (ia32 || MSIs32BitOffset(target, source + 5)) { + MSWrite(current, 0xe8); + MSWrite(current, target - (source + 5)); + } else { + MSPushPointer(current, target); + + MSWrite(current, 0x83); + MSWrite(current, 0xc4); + MSWrite(current, 0x08); + + MSWrite(current, 0x67); + MSWrite(current, 0xff); + MSWrite(current, 0x54); + MSWrite(current, 0x24); + MSWrite(current, 0xf8); + } +} + +template +_disused static void MSWriteCall(uint8_t *¤t, Type_ *target) { + return MSWriteCall(current, reinterpret_cast(target)); +} + +_disused static void MSWriteJump(uint8_t *¤t, uintptr_t target) { + uintptr_t source(reinterpret_cast(current)); + + if (ia32 || MSIs32BitOffset(target, source + 5)) + MSWriteSkip(current, target - (source + 5)); + else { + MSPushPointer(current, target); + MSWrite(current, 0xc3); + } +} + +_disused static void MSWriteJump(uint8_t *¤t, void *target) { + return MSWriteJump(current, reinterpret_cast(target)); +} + +_disused static void MSWriteJump(uint8_t *¤t, I$r target) { + if (target >> 3 != 0) + MSWrite(current, 0x40 | (target & 0x08) >> 3); + MSWrite(current, 0xff); + MSWrite(current, 0xe0 | (target & 0x07)); +} + +_disused static void MSWritePop(uint8_t *¤t, uint8_t target) { + if (target >> 3 != 0) + MSWrite(current, 0x40 | (target & 0x08) >> 3); + MSWrite(current, 0x58 | (target & 0x07)); +} + +_disused static size_t MSSizeOfPop(uint8_t target) { + return target >> 3 != 0 ? 2 : 1; +} + +_disused static void MSWritePush(uint8_t *¤t, I$r target) { + if (target >> 3 != 0) + MSWrite(current, 0x40 | (target & 0x08) >> 3); + MSWrite(current, 0x50 | (target & 0x07)); +} + +_disused static void MSWriteAdd(uint8_t *¤t, I$r target, uint8_t source) { + MSWrite(current, 0x83); + MSWrite(current, 0xc4 | (target & 0x07)); + MSWrite(current, source); +} + +_disused static void MSWriteSet64(uint8_t *¤t, I$r target, uintptr_t source) { + MSWrite(current, 0x48 | (target & 0x08) >> 3 << 2); + MSWrite(current, 0xb8 | (target & 0x07)); + MSWrite(current, source); +} + +template +_disused static void MSWriteSet64(uint8_t *¤t, I$r target, Type_ *source) { + return MSWriteSet64(current, target, reinterpret_cast(source)); +} + +_disused static void MSWriteMove64(uint8_t *¤t, uint8_t source, uint8_t target) { + MSWrite(current, 0x48 | (target & 0x08) >> 3 << 2 | (source & 0x08) >> 3); + MSWrite(current, 0x8b); + MSWrite(current, (target & 0x07) << 3 | (source & 0x07)); +} + +_disused static size_t MSSizeOfMove64() { + return 3; +} + +#endif//SUBSTRATE_X86_HPP diff --git a/Bcore/src/main/jni/Substrate/hde64.c b/Bcore/src/main/jni/Substrate/hde64.c new file mode 100644 index 00000000..d69f0c68 --- /dev/null +++ b/Bcore/src/main/jni/Substrate/hde64.c @@ -0,0 +1,332 @@ +/* + * Hacker Disassembler Engine 64 C + * Copyright (c) 2008-2009, Vyacheslav Patkov. + * All rights reserved. + * + */ + +#include +#include + +#include "hde64.h" +#include "table64.h" + +unsigned int hde64_disasm(const void *code, hde64s *hs) +{ + uint8_t x, c, *p = (uint8_t *)code, cflags, opcode, pref = 0; + uint8_t *ht = hde64_table, m_mod, m_reg, m_rm, disp_size = 0; + uint8_t op64 = 0; + + memset(hs,0,sizeof(hde64s)); + char *tmp=(char*)hs; + + for (x = 16; x; x--) + switch (c = *p++) { + case 0xf3: + hs->p_rep = c; + pref |= PRE_F3; + break; + case 0xf2: + hs->p_rep = c; + pref |= PRE_F2; + break; + case 0xf0: + hs->p_lock = c; + pref |= PRE_LOCK; + break; + case 0x26: case 0x2e: case 0x36: + case 0x3e: case 0x64: case 0x65: + hs->p_seg = c; + pref |= PRE_SEG; + break; + case 0x66: + hs->p_66 = c; + pref |= PRE_66; + break; + case 0x67: + hs->p_67 = c; + pref |= PRE_67; + break; + default: + goto pref_done; + } + pref_done: + + hs->flags = (uint32_t)pref << 23; + + if (!pref) + pref |= PRE_NONE; + + if ((c & 0xf0) == 0x40) { + hs->flags |= F_PREFIX_REX; + if ((hs->rex_w = (c & 0xf) >> 3) && (*p & 0xf8) == 0xb8) + op64++; + hs->rex_r = (c & 7) >> 2; + hs->rex_x = (c & 3) >> 1; + hs->rex_b = c & 1; + if (((c = *p++) & 0xf0) == 0x40) { + opcode = c; + goto error_opcode; + } + } + + if ((hs->opcode = c) == 0x0f) { + hs->opcode2 = c = *p++; + ht += DELTA_OPCODES; + } else if (c >= 0xa0 && c <= 0xa3) { + op64++; + if (pref & PRE_67) + pref |= PRE_66; + else + pref &= ~PRE_66; + } + + opcode = c; + cflags = ht[ht[opcode / 4] + (opcode % 4)]; + + if (cflags == C_ERROR) { + error_opcode: + hs->flags |= F_ERROR | F_ERROR_OPCODE; + cflags = 0; + if ((opcode & -3) == 0x24) + cflags++; + } + + x = 0; + if (cflags & C_GROUP) { + uint16_t t; + t = *(uint16_t *)(ht + (cflags & 0x7f)); + cflags = (uint8_t)t; + x = (uint8_t)(t >> 8); + } + + if (hs->opcode2) { + ht = hde64_table + DELTA_PREFIXES; + if (ht[ht[opcode / 4] + (opcode % 4)] & pref) + hs->flags |= F_ERROR | F_ERROR_OPCODE; + } + + if (cflags & C_MODRM) { + hs->flags |= F_MODRM; + hs->modrm = c = *p++; + hs->modrm_mod = m_mod = c >> 6; + hs->modrm_rm = m_rm = c & 7; + hs->modrm_reg = m_reg = (c & 0x3f) >> 3; + + if (x && ((x << m_reg) & 0x80)) + hs->flags |= F_ERROR | F_ERROR_OPCODE; + + if (!hs->opcode2 && opcode >= 0xd9 && opcode <= 0xdf) { + uint8_t t = opcode - 0xd9; + if (m_mod == 3) { + ht = hde64_table + DELTA_FPU_MODRM + t*8; + t = ht[m_reg] << m_rm; + } else { + ht = hde64_table + DELTA_FPU_REG; + t = ht[t] << m_reg; + } + if (t & 0x80) + hs->flags |= F_ERROR | F_ERROR_OPCODE; + } + + if (pref & PRE_LOCK) { + if (m_mod == 3) { + hs->flags |= F_ERROR | F_ERROR_LOCK; + } else { + uint8_t *table_end, op = opcode; + if (hs->opcode2) { + ht = hde64_table + DELTA_OP2_LOCK_OK; + table_end = ht + DELTA_OP_ONLY_MEM - DELTA_OP2_LOCK_OK; + } else { + ht = hde64_table + DELTA_OP_LOCK_OK; + table_end = ht + DELTA_OP2_LOCK_OK - DELTA_OP_LOCK_OK; + op &= -2; + } + for (; ht != table_end; ht++) + if (*ht++ == op) { + if (!((*ht << m_reg) & 0x80)) + goto no_lock_error; + else + break; + } + hs->flags |= F_ERROR | F_ERROR_LOCK; + no_lock_error: + ; + } + } + + if (hs->opcode2) { + switch (opcode) { + case 0x20: case 0x22: + m_mod = 3; + if (m_reg > 4 || m_reg == 1) + goto error_operand; + else + goto no_error_operand; + case 0x21: case 0x23: + m_mod = 3; + if (m_reg == 4 || m_reg == 5) + goto error_operand; + else + goto no_error_operand; + } + } else { + switch (opcode) { + case 0x8c: + if (m_reg > 5) + goto error_operand; + else + goto no_error_operand; + case 0x8e: + if (m_reg == 1 || m_reg > 5) + goto error_operand; + else + goto no_error_operand; + } + } + + if (m_mod == 3) { + uint8_t *table_end; + if (hs->opcode2) { + ht = hde64_table + DELTA_OP2_ONLY_MEM; + table_end = ht + sizeof(hde64_table) - DELTA_OP2_ONLY_MEM; + } else { + ht = hde64_table + DELTA_OP_ONLY_MEM; + table_end = ht + DELTA_OP2_ONLY_MEM - DELTA_OP_ONLY_MEM; + } + for (; ht != table_end; ht += 2) + if (*ht++ == opcode) { + if (*ht++ & pref && !((*ht << m_reg) & 0x80)) + goto error_operand; + else + break; + } + goto no_error_operand; + } else if (hs->opcode2) { + switch (opcode) { + case 0x50: case 0xd7: case 0xf7: + if (pref & (PRE_NONE | PRE_66)) + goto error_operand; + break; + case 0xd6: + if (pref & (PRE_F2 | PRE_F3)) + goto error_operand; + break; + case 0xc5: + goto error_operand; + } + goto no_error_operand; + } else + goto no_error_operand; + + error_operand: + hs->flags |= F_ERROR | F_ERROR_OPERAND; + no_error_operand: + + c = *p++; + if (m_reg <= 1) { + if (opcode == 0xf6) + cflags |= C_IMM8; + else if (opcode == 0xf7) + cflags |= C_IMM_P66; + } + + switch (m_mod) { + case 0: + if (pref & PRE_67) { + if (m_rm == 6) + disp_size = 2; + } else + if (m_rm == 5) + disp_size = 4; + break; + case 1: + disp_size = 1; + break; + case 2: + disp_size = 2; + if (!(pref & PRE_67)) + disp_size <<= 1; + } + + if (m_mod != 3 && m_rm == 4) { + hs->flags |= F_SIB; + p++; + hs->sib = c; + hs->sib_scale = c >> 6; + hs->sib_index = (c & 0x3f) >> 3; + if ((hs->sib_base = c & 7) == 5 && !(m_mod & 1)) + disp_size = 4; + } + + p--; + switch (disp_size) { + case 1: + hs->flags |= F_DISP8; + hs->disp.disp8 = *p; + break; + case 2: + hs->flags |= F_DISP16; + hs->disp.disp16 = *(uint16_t *)p; + break; + case 4: + hs->flags |= F_DISP32; + hs->disp.disp32 = *(uint32_t *)p; + } + p += disp_size; + } else if (pref & PRE_LOCK) + hs->flags |= F_ERROR | F_ERROR_LOCK; + + if (cflags & C_IMM_P66) { + if (cflags & C_REL32) { + if (pref & PRE_66) { + hs->flags |= F_IMM16 | F_RELATIVE; + hs->imm.imm16 = *(uint16_t *)p; + p += 2; + goto disasm_done; + } + goto rel32_ok; + } + if (op64) { + hs->flags |= F_IMM64; + hs->imm.imm64 = *(uint64_t *)p; + p += 8; + } else if (!(pref & PRE_66)) { + hs->flags |= F_IMM32; + hs->imm.imm32 = *(uint32_t *)p; + p += 4; + } else + goto imm16_ok; + } + + + if (cflags & C_IMM16) { + imm16_ok: + hs->flags |= F_IMM16; + hs->imm.imm16 = *(uint16_t *)p; + p += 2; + } + if (cflags & C_IMM8) { + hs->flags |= F_IMM8; + hs->imm.imm8 = *p++; + } + + if (cflags & C_REL32) { + rel32_ok: + hs->flags |= F_IMM32 | F_RELATIVE; + hs->imm.imm32 = *(uint32_t *)p; + p += 4; + } else if (cflags & C_REL8) { + hs->flags |= F_IMM8 | F_RELATIVE; + hs->imm.imm8 = *p++; + } + + disasm_done: + + if ((hs->len = (uint8_t)(p-(uint8_t *)code)) > 15) { + hs->flags |= F_ERROR | F_ERROR_LENGTH; + hs->len = 15; + } + + return (unsigned int)hs->len; +} diff --git a/Bcore/src/main/jni/Substrate/hde64.h b/Bcore/src/main/jni/Substrate/hde64.h new file mode 100644 index 00000000..2fcc4cb2 --- /dev/null +++ b/Bcore/src/main/jni/Substrate/hde64.h @@ -0,0 +1,112 @@ +/* + * Hacker Disassembler Engine 64 + * Copyright (c) 2008-2009, Vyacheslav Patkov. + * All rights reserved. + * + * hde64.h: C/C++ header file + * + */ + +#ifndef _HDE64_H_ +#define _HDE64_H_ + +/* stdint.h - C99 standard header + * http://en.wikipedia.org/wiki/stdint.h + * + * if your compiler doesn't contain "stdint.h" header (for + * example, Microsoft Visual C++), you can download file: + * http://www.azillionmonkeys.com/qed/pstdint.h + * and change next line to: + * #include "pstdint.h" + */ +#include + +#define F_MODRM 0x00000001 +#define F_SIB 0x00000002 +#define F_IMM8 0x00000004 +#define F_IMM16 0x00000008 +#define F_IMM32 0x00000010 +#define F_IMM64 0x00000020 +#define F_DISP8 0x00000040 +#define F_DISP16 0x00000080 +#define F_DISP32 0x00000100 +#define F_RELATIVE 0x00000200 +#define F_ERROR 0x00001000 +#define F_ERROR_OPCODE 0x00002000 +#define F_ERROR_LENGTH 0x00004000 +#define F_ERROR_LOCK 0x00008000 +#define F_ERROR_OPERAND 0x00010000 +#define F_PREFIX_REPNZ 0x01000000 +#define F_PREFIX_REPX 0x02000000 +#define F_PREFIX_REP 0x03000000 +#define F_PREFIX_66 0x04000000 +#define F_PREFIX_67 0x08000000 +#define F_PREFIX_LOCK 0x10000000 +#define F_PREFIX_SEG 0x20000000 +#define F_PREFIX_REX 0x40000000 +#define F_PREFIX_ANY 0x7f000000 + +#define PREFIX_SEGMENT_CS 0x2e +#define PREFIX_SEGMENT_SS 0x36 +#define PREFIX_SEGMENT_DS 0x3e +#define PREFIX_SEGMENT_ES 0x26 +#define PREFIX_SEGMENT_FS 0x64 +#define PREFIX_SEGMENT_GS 0x65 +#define PREFIX_LOCK 0xf0 +#define PREFIX_REPNZ 0xf2 +#define PREFIX_REPX 0xf3 +#define PREFIX_OPERAND_SIZE 0x66 +#define PREFIX_ADDRESS_SIZE 0x67 + +#pragma pack(push,1) + +typedef struct { + uint8_t len; + uint8_t p_rep; + uint8_t p_lock; + uint8_t p_seg; + uint8_t p_66; + uint8_t p_67; + uint8_t rex; + uint8_t rex_w; + uint8_t rex_r; + uint8_t rex_x; + uint8_t rex_b; + uint8_t opcode; + uint8_t opcode2; + uint8_t modrm; + uint8_t modrm_mod; + uint8_t modrm_reg; + uint8_t modrm_rm; + uint8_t sib; + uint8_t sib_scale; + uint8_t sib_index; + uint8_t sib_base; + union { + uint8_t imm8; + uint16_t imm16; + uint32_t imm32; + uint64_t imm64; + } imm; + union { + uint8_t disp8; + uint16_t disp16; + uint32_t disp32; + } disp; + uint32_t flags; +} hde64s; + +#pragma pack(pop) + +#ifdef __cplusplus +extern "C" { +#endif + +/* __cdecl */ +unsigned int hde64_disasm(const void *code, hde64s *hs); + +#ifdef __cplusplus +} +#endif + +#endif /* _HDE64_H_ */ diff --git a/Bcore/src/main/jni/Substrate/table64.h b/Bcore/src/main/jni/Substrate/table64.h new file mode 100644 index 00000000..144f2907 --- /dev/null +++ b/Bcore/src/main/jni/Substrate/table64.h @@ -0,0 +1,74 @@ +/* + * Hacker Disassembler Engine 64 C + * Copyright (c) 2008-2009, Vyacheslav Patkov. + * All rights reserved. + * + */ + +#define C_NONE 0x00 +#define C_MODRM 0x01 +#define C_IMM8 0x02 +#define C_IMM16 0x04 +#define C_IMM_P66 0x10 +#define C_REL8 0x20 +#define C_REL32 0x40 +#define C_GROUP 0x80 +#define C_ERROR 0xff + +#define PRE_ANY 0x00 +#define PRE_NONE 0x01 +#define PRE_F2 0x02 +#define PRE_F3 0x04 +#define PRE_66 0x08 +#define PRE_67 0x10 +#define PRE_LOCK 0x20 +#define PRE_SEG 0x40 +#define PRE_ALL 0xff + +#define DELTA_OPCODES 0x4a +#define DELTA_FPU_REG 0xfd +#define DELTA_FPU_MODRM 0x104 +#define DELTA_PREFIXES 0x13c +#define DELTA_OP_LOCK_OK 0x1ae +#define DELTA_OP2_LOCK_OK 0x1c6 +#define DELTA_OP_ONLY_MEM 0x1d8 +#define DELTA_OP2_ONLY_MEM 0x1e7 + +unsigned char hde64_table[] = { + 0xa5,0xaa,0xa5,0xb8,0xa5,0xaa,0xa5,0xaa,0xa5,0xb8,0xa5,0xb8,0xa5,0xb8,0xa5, + 0xb8,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xac,0xc0,0xcc,0xc0,0xa1,0xa1, + 0xa1,0xa1,0xb1,0xa5,0xa5,0xa6,0xc0,0xc0,0xd7,0xda,0xe0,0xc0,0xe4,0xc0,0xea, + 0xea,0xe0,0xe0,0x98,0xc8,0xee,0xf1,0xa5,0xd3,0xa5,0xa5,0xa1,0xea,0x9e,0xc0, + 0xc0,0xc2,0xc0,0xe6,0x03,0x7f,0x11,0x7f,0x01,0x7f,0x01,0x3f,0x01,0x01,0xab, + 0x8b,0x90,0x64,0x5b,0x5b,0x5b,0x5b,0x5b,0x92,0x5b,0x5b,0x76,0x90,0x92,0x92, + 0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x6a,0x73,0x90, + 0x5b,0x52,0x52,0x52,0x52,0x5b,0x5b,0x5b,0x5b,0x77,0x7c,0x77,0x85,0x5b,0x5b, + 0x70,0x5b,0x7a,0xaf,0x76,0x76,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b, + 0x5b,0x5b,0x86,0x01,0x03,0x01,0x04,0x03,0xd5,0x03,0xd5,0x03,0xcc,0x01,0xbc, + 0x03,0xf0,0x03,0x03,0x04,0x00,0x50,0x50,0x50,0x50,0xff,0x20,0x20,0x20,0x20, + 0x01,0x01,0x01,0x01,0xc4,0x02,0x10,0xff,0xff,0xff,0x01,0x00,0x03,0x11,0xff, + 0x03,0xc4,0xc6,0xc8,0x02,0x10,0x00,0xff,0xcc,0x01,0x01,0x01,0x00,0x00,0x00, + 0x00,0x01,0x01,0x03,0x01,0xff,0xff,0xc0,0xc2,0x10,0x11,0x02,0x03,0x01,0x01, + 0x01,0xff,0xff,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0xff,0xff,0xff,0xff,0x10, + 0x10,0x10,0x10,0x02,0x10,0x00,0x00,0xc6,0xc8,0x02,0x02,0x02,0x02,0x06,0x00, + 0x04,0x00,0x02,0xff,0x00,0xc0,0xc2,0x01,0x01,0x03,0x03,0x03,0xca,0x40,0x00, + 0x0a,0x00,0x04,0x00,0x00,0x00,0x00,0x7f,0x00,0x33,0x01,0x00,0x00,0x00,0x00, + 0x00,0x00,0xff,0xbf,0xff,0xff,0x00,0x00,0x00,0x00,0x07,0x00,0x00,0xff,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff, + 0x00,0x00,0x00,0xbf,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7f,0x00,0x00, + 0xff,0x40,0x40,0x40,0x40,0x41,0x49,0x40,0x40,0x40,0x40,0x4c,0x42,0x40,0x40, + 0x40,0x40,0x40,0x40,0x40,0x40,0x4f,0x44,0x53,0x40,0x40,0x40,0x44,0x57,0x43, + 0x5c,0x40,0x60,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40, + 0x40,0x40,0x64,0x66,0x6e,0x6b,0x40,0x40,0x6a,0x46,0x40,0x40,0x44,0x46,0x40, + 0x40,0x5b,0x44,0x40,0x40,0x00,0x00,0x00,0x00,0x06,0x06,0x06,0x06,0x01,0x06, + 0x06,0x02,0x06,0x06,0x00,0x06,0x00,0x0a,0x0a,0x00,0x00,0x00,0x02,0x07,0x07, + 0x06,0x02,0x0d,0x06,0x06,0x06,0x0e,0x05,0x05,0x02,0x02,0x00,0x00,0x04,0x04, + 0x04,0x04,0x05,0x06,0x06,0x06,0x00,0x00,0x00,0x0e,0x00,0x00,0x08,0x00,0x10, + 0x00,0x18,0x00,0x20,0x00,0x28,0x00,0x30,0x00,0x80,0x01,0x82,0x01,0x86,0x00, + 0xf6,0xcf,0xfe,0x3f,0xab,0x00,0xb0,0x00,0xb1,0x00,0xb3,0x00,0xba,0xf8,0xbb, + 0x00,0xc0,0x00,0xc1,0x00,0xc7,0xbf,0x62,0xff,0x00,0x8d,0xff,0x00,0xc4,0xff, + 0x00,0xc5,0xff,0x00,0xff,0xff,0xeb,0x01,0xff,0x0e,0x12,0x08,0x00,0x13,0x09, + 0x00,0x16,0x08,0x00,0x17,0x09,0x00,0x2b,0x09,0x00,0xae,0xff,0x07,0xb2,0xff, + 0x00,0xb4,0xff,0x00,0xb5,0xff,0x00,0xc3,0x01,0x00,0xc7,0xff,0xbf,0xe7,0x08, + 0x00,0xf0,0x02,0x00 +}; diff --git a/Bcore/src/main/jni/Utils/HexDump.cpp b/Bcore/src/main/jni/Utils/HexDump.cpp new file mode 100644 index 00000000..51e3e4f2 --- /dev/null +++ b/Bcore/src/main/jni/Utils/HexDump.cpp @@ -0,0 +1,41 @@ +// +// Created by Milk on 2020/8/18. +// + +#include +#include +#include "HexDump.h" +#include "Log.h" + +void HexDump(char *buf, int len, int addr) { + int i, j, k; + char binstr[80]; + + for (i = 0; i < len; i++) { + if (0 == (i % 16)) { + sprintf(binstr, "%08x -", i + addr); + sprintf(binstr, "%s %02x", binstr, (unsigned char) buf[i]); + } else if (15 == (i % 16)) { + sprintf(binstr, "%s %02x", binstr, (unsigned char) buf[i]); + sprintf(binstr, "%s ", binstr); + for (j = i - 15; j <= i; j++) { + sprintf(binstr, "%s%c", binstr, ('!' < buf[j] && buf[j] <= '~') ? buf[j] : '.'); + } + ALOGE("%s\n", binstr); + } else { + sprintf(binstr, "%s %02x", binstr, (unsigned char) buf[i]); + } + } + if (0 != (i % 16)) { + k = 16 - (i % 16); + for (j = 0; j < k; j++) { + sprintf(binstr, "%s ", binstr); + } + sprintf(binstr, "%s ", binstr); + k = 16 - k; + for (j = i - k; j < i; j++) { + sprintf(binstr, "%s%c", binstr, ('!' < buf[j] && buf[j] <= '~') ? buf[j] : '.'); + } + ALOGE("%s\n", binstr); + } +} \ No newline at end of file diff --git a/Bcore/src/main/jni/Utils/HexDump.h b/Bcore/src/main/jni/Utils/HexDump.h new file mode 100644 index 00000000..b76ab6b2 --- /dev/null +++ b/Bcore/src/main/jni/Utils/HexDump.h @@ -0,0 +1,14 @@ +// +// Created by Milk on 2020/8/18. +// + +#ifndef SPEED_HEXDUMP_H +#define SPEED_HEXDUMP_H + + +class HexDump { + +}; +void HexDump(char *buf, int len, int addr); + +#endif //SPEED_HEXDUMP_H diff --git a/Bcore/src/main/jni/Utils/PointerCheck.cpp b/Bcore/src/main/jni/Utils/PointerCheck.cpp new file mode 100644 index 00000000..f058a2d2 --- /dev/null +++ b/Bcore/src/main/jni/Utils/PointerCheck.cpp @@ -0,0 +1,18 @@ +// +// Created by Milk on 2021/5/17. +// + +#include +#include +#include +#include "PointerCheck.h" + +bool PointerCheck::check(void *addr) { + int nullfd = open("/dev/random", O_WRONLY); + bool valid = true; + if (write(nullfd, (void *) addr, sizeof(addr)) < 0) { + valid = false; + } + close(nullfd); + return valid; +} diff --git a/Bcore/src/main/jni/Utils/PointerCheck.h b/Bcore/src/main/jni/Utils/PointerCheck.h new file mode 100644 index 00000000..20e40997 --- /dev/null +++ b/Bcore/src/main/jni/Utils/PointerCheck.h @@ -0,0 +1,15 @@ +// +// Created by Milk on 2021/5/17. +// + +#ifndef BLACKBOX_POINTERCHECK_H +#define BLACKBOX_POINTERCHECK_H + + +class PointerCheck { +public: + static bool check(void *addr); +}; + + +#endif //BLACKBOX_POINTERCHECK_H diff --git a/Bcore/src/main/jni/Utils/fake_dlfcn.cpp b/Bcore/src/main/jni/Utils/fake_dlfcn.cpp new file mode 100644 index 00000000..f18d785d --- /dev/null +++ b/Bcore/src/main/jni/Utils/fake_dlfcn.cpp @@ -0,0 +1,149 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include "fake_dlfcn.h" + +#ifdef __arm__ +#define Elf_Ehdr Elf32_Ehdr +#define Elf_Shdr Elf32_Shdr +#define Elf_Sym Elf32_Sym +#elif defined(__i386__) + #define Elf_Ehdr Elf32_Ehdr + #define Elf_Shdr Elf32_Shdr + #define Elf_Sym Elf32_Sym +#elif defined(__aarch64__) + #define Elf_Ehdr Elf64_Ehdr + #define Elf_Shdr Elf64_Shdr + #define Elf_Sym Elf64_Sym +#else + #error "Arch unknown, please port me" +#endif + +struct ctx { + intptr_t load_addr; + void *dynstr; + void *dynsym; + int nsyms; + off_t bias; +}; + +int fake_dlclose(void *handle) { + if (handle) { + struct ctx *ctx = (struct ctx *) handle; + if (ctx->dynsym) free(ctx->dynsym); /* we're saving dynsym and dynstr */ + if (ctx->dynstr) free(ctx->dynstr); /* from library file just in case */ + free(ctx); + } + return 0; +} + +void *fake_dlopen(const char *libpath, int flags) { + FILE *maps; + char buff[256]; + struct ctx *ctx = 0; + off_t load_addr, size; + int k, fd = -1, found = 0; + intptr_t shoff; + Elf_Ehdr *elf = (Elf_Ehdr *) MAP_FAILED; + + + maps = fopen("/proc/self/maps", "r"); + if (!maps) goto err_exit; + + while (!found && fgets(buff, sizeof(buff), maps)) + if (strstr(buff, "r-xp") && strstr(buff, libpath)) found = 1; + + fclose(maps); + + if (!found) goto err_exit; + + if (sscanf(buff, "%lx", &load_addr) != 1) + goto err_exit; + + /* Now, mmap the same library once again */ + + fd = open(libpath, O_RDONLY); + if (fd < 0) goto err_exit; + + size = lseek(fd, 0, SEEK_END); + if (size <= 0) goto err_exit; + + elf = (Elf_Ehdr *) mmap(0, size, PROT_READ, MAP_SHARED, fd, 0); + close(fd); + fd = -1; + + if (elf == MAP_FAILED) goto err_exit; + + ctx = (struct ctx *) calloc(1, sizeof(struct ctx)); + if (!ctx) goto err_exit; + + ctx->load_addr = (intptr_t) load_addr; + shoff = (intptr_t) elf + elf->e_shoff; + + for (k = 0; k < elf->e_shnum; k++, shoff += elf->e_shentsize) { + + Elf_Shdr *sh = (Elf_Shdr *) shoff; + switch (sh->sh_type) { + + case SHT_DYNSYM: + /* .dynsym */ + ctx->dynsym = malloc(sh->sh_size); + if (!ctx->dynsym) goto err_exit; + memcpy(ctx->dynsym, (const void *) ((intptr_t) elf + sh->sh_offset), sh->sh_size); + ctx->nsyms = (sh->sh_size / sizeof(Elf_Sym)); + break; + + case SHT_STRTAB: + if (ctx->dynstr) break; /* .dynstr is guaranteed to be the first STRTAB */ + ctx->dynstr = malloc(sh->sh_size); + if (!ctx->dynstr) goto err_exit; + memcpy(ctx->dynstr, (const void *) (((intptr_t) elf) + sh->sh_offset), sh->sh_size); + break; + + case SHT_PROGBITS: + if (!ctx->dynstr || !ctx->dynsym) break; + /* won't even bother checking against the section name */ + ctx->bias = (off_t) sh->sh_addr - (off_t) sh->sh_offset; + k = elf->e_shnum; /* exit for */ + break; + } + } + + munmap(elf, (size_t) size); + elf = 0; + + if (!ctx->dynstr || !ctx->dynsym) goto err_exit; + +#undef fatal + + + return ctx; + + err_exit: + if (fd >= 0) close(fd); + if (elf != MAP_FAILED) munmap(elf, size); + fake_dlclose(ctx); + return 0; +} + +void *fake_dlsym(void *handle, const char *name) { + int k; + struct ctx *ctx = (struct ctx *) handle; + Elf_Sym *sym = (Elf_Sym *) ctx->dynsym; + char *strings = (char *) ctx->dynstr; + + for (k = 0; k < ctx->nsyms; k++, sym++) + if (strcmp(strings + sym->st_name, name) == 0) { + /* NB: sym->st_value is an offset into the section for relocatables, + but a VMA for shared libs or exe files, so we have to subtract the bias */ + void *ret = (void *) (ctx->load_addr + sym->st_value - ctx->bias); + return ret; + } + return 0; + +} \ No newline at end of file diff --git a/Bcore/src/main/jni/Utils/fake_dlfcn.h b/Bcore/src/main/jni/Utils/fake_dlfcn.h new file mode 100644 index 00000000..b72a0cfb --- /dev/null +++ b/Bcore/src/main/jni/Utils/fake_dlfcn.h @@ -0,0 +1,10 @@ +#ifndef DLFCN_H +#define DLFCN_H + +void *fake_dlopen(const char *libpath, int flags); + +void *fake_dlsym(void *handle, const char *name); + +int fake_dlclose(void *handle); + +#endif \ No newline at end of file diff --git a/Bcore/src/main/jni/VmCore.cpp b/Bcore/src/main/jni/VmCore.cpp new file mode 100644 index 00000000..94053642 --- /dev/null +++ b/Bcore/src/main/jni/VmCore.cpp @@ -0,0 +1,154 @@ +// +// Created by Milk on 4/9/21. +// + +#include "VmCore.h" +#include "Log.h" +#include "IO.h" +#include +#include +#include +#include +#include +#include +#include "DexDump.h" +#include "Utils/HexDump.h" + +struct { + JavaVM *vm; + jclass VMCoreClass; + jmethodID getCallingUidId; + jmethodID redirectPathString; + jmethodID redirectPathFile; + jmethodID loadEmptyDex; + jmethodID loadEmptyDexL; + int api_level; +} VMEnv; + + +JNIEnv *getEnv() { + JNIEnv *env; + VMEnv.vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6); + return env; +} + +JNIEnv *ensureEnvCreated() { + JNIEnv *env = getEnv(); + if (env == NULL) { + VMEnv.vm->AttachCurrentThread(&env, NULL); + } + return env; +} + +int VmCore::getCallingUid(JNIEnv *env, int orig) { + env = ensureEnvCreated(); + return env->CallStaticIntMethod(VMEnv.VMCoreClass, VMEnv.getCallingUidId, orig); +} + +jstring VmCore::redirectPathString(JNIEnv *env, jstring path) { + env = ensureEnvCreated(); + return (jstring) env->CallStaticObjectMethod(VMEnv.VMCoreClass, VMEnv.redirectPathString, path); +} + +jobject VmCore::redirectPathFile(JNIEnv *env, jobject path) { + env = ensureEnvCreated(); + return env->CallStaticObjectMethod(VMEnv.VMCoreClass, VMEnv.redirectPathFile, path); +} + +jlongArray VmCore::loadEmptyDex(JNIEnv *env) { + env = ensureEnvCreated(); + return (jlongArray) env->CallStaticObjectMethod(VMEnv.VMCoreClass, VMEnv.loadEmptyDex); +} + +int VmCore::getApiLevel() { + return VMEnv.api_level; +} + +JavaVM *VmCore::getJavaVM() { + return VMEnv.vm; +} + +void nativeHook(JNIEnv *env) { + BaseHook::init(env); + UnixFileSystemHook::init(env); + VMClassLoaderHook::init(env); +// RuntimeHook::init(env); +// BinderHook::init(env); +} + +void hideXposed(JNIEnv *env, jclass clazz) { + ALOGD("set hideXposed"); + VMClassLoaderHook::hideXposed(); +} + +void init(JNIEnv *env, jobject clazz, jint api_level) { + ALOGD("VmCore init."); + VMEnv.api_level = api_level; + VMEnv.VMCoreClass = (jclass) env->NewGlobalRef(env->FindClass(VMCORE_CLASS)); + VMEnv.getCallingUidId = env->GetStaticMethodID(VMEnv.VMCoreClass, "getCallingUid", "(I)I"); + VMEnv.redirectPathString = env->GetStaticMethodID(VMEnv.VMCoreClass, "redirectPath", + "(Ljava/lang/String;)Ljava/lang/String;"); + VMEnv.redirectPathFile = env->GetStaticMethodID(VMEnv.VMCoreClass, "redirectPath", + "(Ljava/io/File;)Ljava/io/File;"); + VMEnv.loadEmptyDex = env->GetStaticMethodID(VMEnv.VMCoreClass, "loadEmptyDex", + "()[J"); + + JniHook::InitJniHook(env, api_level); +} + +void addIORule(JNIEnv *env, jclass clazz, jstring target_path, + jstring relocate_path) { + IO::addRule(env->GetStringUTFChars(target_path, JNI_FALSE), + env->GetStringUTFChars(relocate_path, JNI_FALSE)); +} + +void enableIO(JNIEnv *env, jclass clazz) { + IO::init(env); + nativeHook(env); +} + +void dumpDex(JNIEnv *env, jclass clazz, jlong cookie, jstring dir) { + DexDump::dumpDex(env, cookie, dir); +} + +static JNINativeMethod gMethods[] = { + {"hideXposed", "()V", (void *) hideXposed}, + {"addIORule", "(Ljava/lang/String;Ljava/lang/String;)V", (void *) addIORule}, + {"enableIO", "()V", (void *) enableIO}, + {"init", "(I)V", (void *) init}, + {"dumpDex", "(JLjava/lang/String;)V", (void *) dumpDex}, +}; + +int registerNativeMethods(JNIEnv *env, const char *className, + JNINativeMethod *gMethods, int numMethods) { + jclass clazz; + clazz = env->FindClass(className); + if (clazz == nullptr) { + return JNI_FALSE; + } + if (env->RegisterNatives(clazz, gMethods, numMethods) < 0) { + return JNI_FALSE; + } + return JNI_TRUE; +} + +int registerNatives(JNIEnv *env) { + if (!registerNativeMethods(env, VMCORE_CLASS, gMethods, + sizeof(gMethods) / sizeof(gMethods[0]))) + return JNI_FALSE; + return JNI_TRUE; +} + +void registerMethod(JNIEnv *jenv) { + registerNatives(jenv); +} + +JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) { + JNIEnv *env; + VMEnv.vm = vm; + if (vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6) != JNI_OK) { + return JNI_EVERSION; + } + registerMethod(env); + return JNI_VERSION_1_6; +} \ No newline at end of file diff --git a/Bcore/src/main/jni/VmCore.h b/Bcore/src/main/jni/VmCore.h new file mode 100644 index 00000000..04e442e0 --- /dev/null +++ b/Bcore/src/main/jni/VmCore.h @@ -0,0 +1,23 @@ +// +// Created by Milk on 4/9/21. +// + +#ifndef VIRTUALM_VMCORE_H +#define VIRTUALM_VMCORE_H + +#include + +#define VMCORE_CLASS "top/niunaijun/blackbox/core/VMCore" + +class VmCore { +public: + static JavaVM *getJavaVM(); + static int getApiLevel(); + static int getCallingUid(JNIEnv *env, int orig); + static jstring redirectPathString(JNIEnv *env, jstring path); + static jobject redirectPathFile(JNIEnv *env, jobject path); + static jlongArray loadEmptyDex(JNIEnv *env); +}; + + +#endif //VIRTUALM_VMCORE_H diff --git a/Bcore/src/main/res/drawable/ic_launcher.png b/Bcore/src/main/res/drawable/ic_launcher.png new file mode 100644 index 00000000..15ac6817 Binary files /dev/null and b/Bcore/src/main/res/drawable/ic_launcher.png differ diff --git a/Bcore/src/main/res/values/strings.xml b/Bcore/src/main/res/values/strings.xml new file mode 100644 index 00000000..71cc22e1 --- /dev/null +++ b/Bcore/src/main/res/values/strings.xml @@ -0,0 +1,4 @@ + + + :black + \ No newline at end of file diff --git a/Bcore/src/main/res/values/styles.xml b/Bcore/src/main/res/values/styles.xml new file mode 100644 index 00000000..9960095b --- /dev/null +++ b/Bcore/src/main/res/values/styles.xml @@ -0,0 +1,6 @@ + + + diff --git a/Bcore/src/main/res/xml/filepath.xml b/Bcore/src/main/res/xml/filepath.xml new file mode 100644 index 00000000..6e11ec24 --- /dev/null +++ b/Bcore/src/main/res/xml/filepath.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..b09cd785 --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/app/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle new file mode 100644 index 00000000..d4eceb03 --- /dev/null +++ b/app/build.gradle @@ -0,0 +1,45 @@ +plugins { + id 'com.android.application' +} + +android { + compileSdkVersion 30 + buildToolsVersion "30.0.3" + + defaultConfig { + applicationId "top.niunaijun.blackdex" + minSdkVersion 21 + targetSdkVersion 30 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + ndk { + // 设置支持的SO库架构 + abiFilters 'armeabi-v7a', 'x86' + } + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } +} + +dependencies { + + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation 'com.google.android.material:material:1.3.0' + implementation 'androidx.constraintlayout:constraintlayout:2.0.4' + testImplementation 'junit:junit:4.+' + androidTestImplementation 'androidx.test.ext:junit:1.1.2' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' + + implementation project(':Bcore') +} \ No newline at end of file diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 00000000..481bb434 --- /dev/null +++ b/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# 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 \ No newline at end of file diff --git a/app/src/androidTest/java/top/niunaijun/blackdex/ExampleInstrumentedTest.java b/app/src/androidTest/java/top/niunaijun/blackdex/ExampleInstrumentedTest.java new file mode 100644 index 00000000..a3fec209 --- /dev/null +++ b/app/src/androidTest/java/top/niunaijun/blackdex/ExampleInstrumentedTest.java @@ -0,0 +1,26 @@ +package top.niunaijun.blackdex; + +import android.content.Context; + +import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.*; + +/** + * Instrumented test, which will execute on an Android device. + * + * @see Testing documentation + */ +@RunWith(AndroidJUnit4.class) +public class ExampleInstrumentedTest { + @Test + public void useAppContext() { + // Context of the app under test. + Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); + assertEquals("top.niunaijun.blackdex", appContext.getPackageName()); + } +} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 00000000..75000f62 --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/java/top/niunaijun/blackdex/App.java b/app/src/main/java/top/niunaijun/blackdex/App.java new file mode 100644 index 00000000..d03fa834 --- /dev/null +++ b/app/src/main/java/top/niunaijun/blackdex/App.java @@ -0,0 +1,34 @@ +package top.niunaijun.blackdex; + +import android.app.Application; +import android.content.Context; + +import top.niunaijun.blackbox.BlackBoxCore; +import top.niunaijun.blackbox.app.configuration.ClientConfiguration; + +/** + * Created by Milk on 2021/5/20. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + */ +public class App extends Application { + @Override + protected void attachBaseContext(Context base) { + super.attachBaseContext(base); + BlackBoxCore.get().doAttachBaseContext(base, new ClientConfiguration() { + @Override + public String getHostPackageName() { + return base.getPackageName(); + } + }); + } + + @Override + public void onCreate() { + super.onCreate(); + BlackBoxCore.get().doCreate(); + } +} diff --git a/app/src/main/java/top/niunaijun/blackdex/MainActivity.java b/app/src/main/java/top/niunaijun/blackdex/MainActivity.java new file mode 100644 index 00000000..d6569cb7 --- /dev/null +++ b/app/src/main/java/top/niunaijun/blackdex/MainActivity.java @@ -0,0 +1,35 @@ +package top.niunaijun.blackdex; + +import androidx.appcompat.app.AppCompatActivity; + +import android.os.Bundle; +import android.util.Log; +import android.view.View; + +import java.io.File; + +import top.niunaijun.blackbox.BlackBoxCore; +import top.niunaijun.blackbox.entity.pm.InstallResult; + +public class MainActivity extends AppCompatActivity { + public static final String TAG = "MainActivity"; + public static final int USER_ID = 0; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + findViewById(R.id.btn_click).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + String hlx = "com.huluxia.gametools"; + boolean installed = BlackBoxCore.get().isInstalled(hlx); + if (!installed) { + InstallResult installResult = BlackBoxCore.get().installPackage(new File("/sdcard/huluxia.apk")); + Log.d(TAG, "onClick: " + installResult.toString()); + } + BlackBoxCore.get().launchApk(hlx); + } + }); + } +} \ No newline at end of file diff --git a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 00000000..2b068d11 --- /dev/null +++ b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 00000000..07d5da9c --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml new file mode 100644 index 00000000..636b9c72 --- /dev/null +++ b/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,15 @@ + + + +