├── .gitignore ├── ART.mk ├── Android.mk ├── Dalvik.mk ├── MODULE_LICENSE_APACHE2 ├── NOTICE ├── app_main.cpp ├── app_main2.cpp ├── fd_utils-inl.h ├── libxposed_art.cpp ├── libxposed_common.cpp ├── libxposed_common.h ├── libxposed_dalvik.cpp ├── libxposed_dalvik.h ├── xposed.cpp ├── xposed.h ├── xposed_logcat.cpp ├── xposed_logcat.h ├── xposed_offsets.h ├── xposed_safemode.cpp ├── xposed_safemode.h ├── xposed_service.cpp ├── xposed_service.h └── xposed_shared.h /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | -------------------------------------------------------------------------------- /ART.mk: -------------------------------------------------------------------------------- 1 | ########################################################## 2 | # Library for ART-specific functions 3 | ########################################################## 4 | 5 | include $(CLEAR_VARS) 6 | 7 | include art/build/Android.common_build.mk 8 | $(eval $(call set-target-local-clang-vars)) 9 | $(eval $(call set-target-local-cflags-vars,ndebug)) 10 | 11 | ifeq (1,$(strip $(shell expr $(PLATFORM_SDK_VERSION) \>= 23))) 12 | LOCAL_C_INCLUDES += \ 13 | external/valgrind \ 14 | external/valgrind/include 15 | else 16 | include external/libcxx/libcxx.mk 17 | LOCAL_C_INCLUDES += \ 18 | external/valgrind/main \ 19 | external/valgrind/main/include 20 | endif 21 | 22 | LOCAL_SRC_FILES += \ 23 | libxposed_common.cpp \ 24 | libxposed_art.cpp 25 | 26 | LOCAL_C_INCLUDES += \ 27 | art/runtime \ 28 | external/gtest/include 29 | 30 | ifeq (1,$(strip $(shell expr $(PLATFORM_SDK_VERSION) \>= 24))) 31 | LOCAL_C_INCLUDES += bionic/libc/private 32 | endif 33 | 34 | LOCAL_SHARED_LIBRARIES += \ 35 | libart \ 36 | liblog \ 37 | libcutils \ 38 | libandroidfw \ 39 | libnativehelper 40 | 41 | LOCAL_CFLAGS += \ 42 | -DPLATFORM_SDK_VERSION=$(PLATFORM_SDK_VERSION) \ 43 | -DXPOSED_WITH_SELINUX=1 44 | 45 | LOCAL_MODULE := libxposed_art 46 | LOCAL_MODULE_TAGS := optional 47 | LOCAL_STRIP_MODULE := keep_symbols 48 | LOCAL_MULTILIB := both 49 | 50 | # Always build both architectures (if applicable) 51 | ifeq ($(TARGET_IS_64_BIT),true) 52 | $(LOCAL_MODULE): $(LOCAL_MODULE)$(TARGET_2ND_ARCH_MODULE_SUFFIX) 53 | endif 54 | 55 | include $(BUILD_SHARED_LIBRARY) 56 | -------------------------------------------------------------------------------- /Android.mk: -------------------------------------------------------------------------------- 1 | ########################################################## 2 | # Customized app_process executable 3 | ########################################################## 4 | 5 | LOCAL_PATH:= $(call my-dir) 6 | include $(CLEAR_VARS) 7 | 8 | ifeq (1,$(strip $(shell expr $(PLATFORM_SDK_VERSION) \>= 21))) 9 | LOCAL_SRC_FILES := app_main2.cpp 10 | LOCAL_MULTILIB := both 11 | LOCAL_MODULE_STEM_32 := app_process32_xposed 12 | LOCAL_MODULE_STEM_64 := app_process64_xposed 13 | else 14 | LOCAL_SRC_FILES := app_main.cpp 15 | LOCAL_MODULE_STEM := app_process_xposed 16 | endif 17 | 18 | LOCAL_SRC_FILES += \ 19 | xposed.cpp \ 20 | xposed_logcat.cpp \ 21 | xposed_service.cpp \ 22 | xposed_safemode.cpp 23 | 24 | LOCAL_SHARED_LIBRARIES := \ 25 | libcutils \ 26 | libutils \ 27 | liblog \ 28 | libbinder \ 29 | libandroid_runtime \ 30 | libdl 31 | 32 | LOCAL_CFLAGS += -Wall -Werror -Wextra -Wunused 33 | LOCAL_CFLAGS += -DPLATFORM_SDK_VERSION=$(PLATFORM_SDK_VERSION) 34 | 35 | ifeq (1,$(strip $(shell expr $(PLATFORM_SDK_VERSION) \>= 17))) 36 | LOCAL_SHARED_LIBRARIES += libselinux 37 | LOCAL_CFLAGS += -DXPOSED_WITH_SELINUX=1 38 | endif 39 | 40 | ifeq (1,$(strip $(shell expr $(PLATFORM_SDK_VERSION) \>= 22))) 41 | LOCAL_WHOLE_STATIC_LIBRARIES := libsigchain 42 | LOCAL_LDFLAGS := -Wl,--version-script,art/sigchainlib/version-script.txt -Wl,--export-dynamic 43 | endif 44 | 45 | ifeq (1,$(strip $(shell expr $(PLATFORM_SDK_VERSION) \>= 23))) 46 | LOCAL_SHARED_LIBRARIES += libwilhelm 47 | endif 48 | 49 | LOCAL_MODULE := xposed 50 | LOCAL_MODULE_TAGS := optional 51 | LOCAL_STRIP_MODULE := keep_symbols 52 | 53 | # Always build both architectures (if applicable) 54 | ifeq ($(TARGET_IS_64_BIT),true) 55 | $(LOCAL_MODULE): $(LOCAL_MODULE)$(TARGET_2ND_ARCH_MODULE_SUFFIX) 56 | endif 57 | 58 | include $(BUILD_EXECUTABLE) 59 | 60 | ########################################################## 61 | # Library for Dalvik-/ART-specific functions 62 | ########################################################## 63 | ifeq (1,$(strip $(shell expr $(PLATFORM_SDK_VERSION) \>= 21))) 64 | include frameworks/base/cmds/xposed/ART.mk 65 | else 66 | include frameworks/base/cmds/xposed/Dalvik.mk 67 | endif 68 | -------------------------------------------------------------------------------- /Dalvik.mk: -------------------------------------------------------------------------------- 1 | ########################################################## 2 | # Library for Dalvik-specific functions 3 | ########################################################## 4 | 5 | include $(CLEAR_VARS) 6 | 7 | LOCAL_SRC_FILES := \ 8 | libxposed_common.cpp \ 9 | libxposed_dalvik.cpp 10 | 11 | LOCAL_C_INCLUDES += \ 12 | dalvik \ 13 | dalvik/vm \ 14 | external/stlport/stlport \ 15 | bionic \ 16 | bionic/libstdc++/include \ 17 | libcore/include 18 | 19 | LOCAL_SHARED_LIBRARIES := \ 20 | libdvm \ 21 | liblog \ 22 | libdl \ 23 | libnativehelper 24 | 25 | ifeq ($(PLATFORM_SDK_VERSION),15) 26 | LOCAL_SHARED_LIBRARIES += libutils 27 | else 28 | LOCAL_SHARED_LIBRARIES += libandroidfw 29 | endif 30 | 31 | LOCAL_CFLAGS := -Wall -Werror -Wextra -Wunused -Wno-unused-parameter 32 | LOCAL_CFLAGS += -DPLATFORM_SDK_VERSION=$(PLATFORM_SDK_VERSION) 33 | 34 | ifeq (1,$(strip $(shell expr $(PLATFORM_SDK_VERSION) \>= 17))) 35 | LOCAL_CFLAGS += -DXPOSED_WITH_SELINUX=1 36 | endif 37 | 38 | LOCAL_MODULE := libxposed_dalvik 39 | LOCAL_MODULE_TAGS := optional 40 | LOCAL_STRIP_MODULE := keep_symbols 41 | 42 | include $(BUILD_SHARED_LIBRARY) 43 | -------------------------------------------------------------------------------- /MODULE_LICENSE_APACHE2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rovo89/Xposed/aa7ae1dbd63decea012000d06c2ae01fa0916dc3/MODULE_LICENSE_APACHE2 -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | 2 | Original work Copyright (c) 2005-2008, The Android Open Source Project 3 | Modified work Copyright (c) 2013, rovo89 and Tungstwenty 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | 8 | Unless required by applicable law or agreed to in writing, software 9 | distributed under the License is distributed on an "AS IS" BASIS, 10 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | See the License for the specific language governing permissions and 12 | limitations under the License. 13 | 14 | 15 | Apache License 16 | Version 2.0, January 2004 17 | http://www.apache.org/licenses/ 18 | 19 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 20 | 21 | 1. Definitions. 22 | 23 | "License" shall mean the terms and conditions for use, reproduction, 24 | and distribution as defined by Sections 1 through 9 of this document. 25 | 26 | "Licensor" shall mean the copyright owner or entity authorized by 27 | the copyright owner that is granting the License. 28 | 29 | "Legal Entity" shall mean the union of the acting entity and all 30 | other entities that control, are controlled by, or are under common 31 | control with that entity. For the purposes of this definition, 32 | "control" means (i) the power, direct or indirect, to cause the 33 | direction or management of such entity, whether by contract or 34 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 35 | outstanding shares, or (iii) beneficial ownership of such entity. 36 | 37 | "You" (or "Your") shall mean an individual or Legal Entity 38 | exercising permissions granted by this License. 39 | 40 | "Source" form shall mean the preferred form for making modifications, 41 | including but not limited to software source code, documentation 42 | source, and configuration files. 43 | 44 | "Object" form shall mean any form resulting from mechanical 45 | transformation or translation of a Source form, including but 46 | not limited to compiled object code, generated documentation, 47 | and conversions to other media types. 48 | 49 | "Work" shall mean the work of authorship, whether in Source or 50 | Object form, made available under the License, as indicated by a 51 | copyright notice that is included in or attached to the work 52 | (an example is provided in the Appendix below). 53 | 54 | "Derivative Works" shall mean any work, whether in Source or Object 55 | form, that is based on (or derived from) the Work and for which the 56 | editorial revisions, annotations, elaborations, or other modifications 57 | represent, as a whole, an original work of authorship. For the purposes 58 | of this License, Derivative Works shall not include works that remain 59 | separable from, or merely link (or bind by name) to the interfaces of, 60 | the Work and Derivative Works thereof. 61 | 62 | "Contribution" shall mean any work of authorship, including 63 | the original version of the Work and any modifications or additions 64 | to that Work or Derivative Works thereof, that is intentionally 65 | submitted to Licensor for inclusion in the Work by the copyright owner 66 | or by an individual or Legal Entity authorized to submit on behalf of 67 | the copyright owner. For the purposes of this definition, "submitted" 68 | means any form of electronic, verbal, or written communication sent 69 | to the Licensor or its representatives, including but not limited to 70 | communication on electronic mailing lists, source code control systems, 71 | and issue tracking systems that are managed by, or on behalf of, the 72 | Licensor for the purpose of discussing and improving the Work, but 73 | excluding communication that is conspicuously marked or otherwise 74 | designated in writing by the copyright owner as "Not a Contribution." 75 | 76 | "Contributor" shall mean Licensor and any individual or Legal Entity 77 | on behalf of whom a Contribution has been received by Licensor and 78 | subsequently incorporated within the Work. 79 | 80 | 2. Grant of Copyright License. Subject to the terms and conditions of 81 | this License, each Contributor hereby grants to You a perpetual, 82 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 83 | copyright license to reproduce, prepare Derivative Works of, 84 | publicly display, publicly perform, sublicense, and distribute the 85 | Work and such Derivative Works in Source or Object form. 86 | 87 | 3. Grant of Patent License. Subject to the terms and conditions of 88 | this License, each Contributor hereby grants to You a perpetual, 89 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 90 | (except as stated in this section) patent license to make, have made, 91 | use, offer to sell, sell, import, and otherwise transfer the Work, 92 | where such license applies only to those patent claims licensable 93 | by such Contributor that are necessarily infringed by their 94 | Contribution(s) alone or by combination of their Contribution(s) 95 | with the Work to which such Contribution(s) was submitted. If You 96 | institute patent litigation against any entity (including a 97 | cross-claim or counterclaim in a lawsuit) alleging that the Work 98 | or a Contribution incorporated within the Work constitutes direct 99 | or contributory patent infringement, then any patent licenses 100 | granted to You under this License for that Work shall terminate 101 | as of the date such litigation is filed. 102 | 103 | 4. Redistribution. You may reproduce and distribute copies of the 104 | Work or Derivative Works thereof in any medium, with or without 105 | modifications, and in Source or Object form, provided that You 106 | meet the following conditions: 107 | 108 | (a) You must give any other recipients of the Work or 109 | Derivative Works a copy of this License; and 110 | 111 | (b) You must cause any modified files to carry prominent notices 112 | stating that You changed the files; and 113 | 114 | (c) You must retain, in the Source form of any Derivative Works 115 | that You distribute, all copyright, patent, trademark, and 116 | attribution notices from the Source form of the Work, 117 | excluding those notices that do not pertain to any part of 118 | the Derivative Works; and 119 | 120 | (d) If the Work includes a "NOTICE" text file as part of its 121 | distribution, then any Derivative Works that You distribute must 122 | include a readable copy of the attribution notices contained 123 | within such NOTICE file, excluding those notices that do not 124 | pertain to any part of the Derivative Works, in at least one 125 | of the following places: within a NOTICE text file distributed 126 | as part of the Derivative Works; within the Source form or 127 | documentation, if provided along with the Derivative Works; or, 128 | within a display generated by the Derivative Works, if and 129 | wherever such third-party notices normally appear. The contents 130 | of the NOTICE file are for informational purposes only and 131 | do not modify the License. You may add Your own attribution 132 | notices within Derivative Works that You distribute, alongside 133 | or as an addendum to the NOTICE text from the Work, provided 134 | that such additional attribution notices cannot be construed 135 | as modifying the License. 136 | 137 | You may add Your own copyright statement to Your modifications and 138 | may provide additional or different license terms and conditions 139 | for use, reproduction, or distribution of Your modifications, or 140 | for any such Derivative Works as a whole, provided Your use, 141 | reproduction, and distribution of the Work otherwise complies with 142 | the conditions stated in this License. 143 | 144 | 5. Submission of Contributions. Unless You explicitly state otherwise, 145 | any Contribution intentionally submitted for inclusion in the Work 146 | by You to the Licensor shall be under the terms and conditions of 147 | this License, without any additional terms or conditions. 148 | Notwithstanding the above, nothing herein shall supersede or modify 149 | the terms of any separate license agreement you may have executed 150 | with Licensor regarding such Contributions. 151 | 152 | 6. Trademarks. This License does not grant permission to use the trade 153 | names, trademarks, service marks, or product names of the Licensor, 154 | except as required for reasonable and customary use in describing the 155 | origin of the Work and reproducing the content of the NOTICE file. 156 | 157 | 7. Disclaimer of Warranty. Unless required by applicable law or 158 | agreed to in writing, Licensor provides the Work (and each 159 | Contributor provides its Contributions) on an "AS IS" BASIS, 160 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 161 | implied, including, without limitation, any warranties or conditions 162 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 163 | PARTICULAR PURPOSE. You are solely responsible for determining the 164 | appropriateness of using or redistributing the Work and assume any 165 | risks associated with Your exercise of permissions under this License. 166 | 167 | 8. Limitation of Liability. In no event and under no legal theory, 168 | whether in tort (including negligence), contract, or otherwise, 169 | unless required by applicable law (such as deliberate and grossly 170 | negligent acts) or agreed to in writing, shall any Contributor be 171 | liable to You for damages, including any direct, indirect, special, 172 | incidental, or consequential damages of any character arising as a 173 | result of this License or out of the use or inability to use the 174 | Work (including but not limited to damages for loss of goodwill, 175 | work stoppage, computer failure or malfunction, or any and all 176 | other commercial damages or losses), even if such Contributor 177 | has been advised of the possibility of such damages. 178 | 179 | 9. Accepting Warranty or Additional Liability. While redistributing 180 | the Work or Derivative Works thereof, You may choose to offer, 181 | and charge a fee for, acceptance of support, warranty, indemnity, 182 | or other liability obligations and/or rights consistent with this 183 | License. However, in accepting such obligations, You may act only 184 | on Your own behalf and on Your sole responsibility, not on behalf 185 | of any other Contributor, and only if You agree to indemnify, 186 | defend, and hold each Contributor harmless for any liability 187 | incurred by, or claims asserted against, such Contributor by reason 188 | of your accepting any such warranty or additional liability. 189 | 190 | END OF TERMS AND CONDITIONS 191 | 192 | -------------------------------------------------------------------------------- /app_main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Main entry of app process. 3 | * 4 | * Starts the interpreted runtime, then starts up the application. 5 | * 6 | */ 7 | 8 | #define LOG_TAG "appproc" 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #if PLATFORM_SDK_VERSION >= 16 19 | #include 20 | #endif 21 | 22 | #include 23 | #include 24 | 25 | #include "xposed.h" 26 | #include 27 | 28 | static bool isXposedLoaded = false; 29 | 30 | 31 | namespace android { 32 | 33 | void app_usage() 34 | { 35 | fprintf(stderr, 36 | "Usage: app_process [java-options] cmd-dir start-class-name [options]\n"); 37 | fprintf(stderr, " with Xposed support\n"); 38 | } 39 | 40 | void _atrace_set_tracing_enabled(bool enabled) 41 | { 42 | if (xposed::getSdkVersion() < 18) 43 | return; 44 | 45 | dlerror(); // Clear existing errors 46 | 47 | void (*PTR_atrace_set_tracing_enabled)(bool); 48 | *(void **) (&PTR_atrace_set_tracing_enabled) = dlsym(RTLD_DEFAULT, "atrace_set_tracing_enabled"); 49 | 50 | const char *error; 51 | if ((error = dlerror()) != NULL) { 52 | ALOGE("Could not find address for function atrace_set_tracing_enabled: %s", error); 53 | } else { 54 | PTR_atrace_set_tracing_enabled(enabled); 55 | } 56 | } 57 | 58 | class AppRuntime : public AndroidRuntime 59 | { 60 | public: 61 | AppRuntime() 62 | : mParentDir(NULL) 63 | , mClassName(NULL) 64 | , mClass(NULL) 65 | , mArgC(0) 66 | , mArgV(NULL) 67 | { 68 | } 69 | 70 | #if 0 71 | // this appears to be unused 72 | const char* getParentDir() const 73 | { 74 | return mParentDir; 75 | } 76 | #endif 77 | 78 | const char* getClassName() const 79 | { 80 | return mClassName; 81 | } 82 | 83 | virtual void onVmCreated(JNIEnv* env) 84 | { 85 | if (isXposedLoaded) 86 | xposed::onVmCreated(env); 87 | 88 | if (mClassName == NULL) { 89 | return; // Zygote. Nothing to do here. 90 | } 91 | 92 | /* 93 | * This is a little awkward because the JNI FindClass call uses the 94 | * class loader associated with the native method we're executing in. 95 | * If called in onStarted (from RuntimeInit.finishInit because we're 96 | * launching "am", for example), FindClass would see that we're calling 97 | * from a boot class' native method, and so wouldn't look for the class 98 | * we're trying to look up in CLASSPATH. Unfortunately it needs to, 99 | * because the "am" classes are not boot classes. 100 | * 101 | * The easiest fix is to call FindClass here, early on before we start 102 | * executing boot class Java code and thereby deny ourselves access to 103 | * non-boot classes. 104 | */ 105 | char* slashClassName = toSlashClassName(mClassName); 106 | mClass = env->FindClass(slashClassName); 107 | if (mClass == NULL) { 108 | ALOGE("ERROR: could not find class '%s'\n", mClassName); 109 | } 110 | free(slashClassName); 111 | 112 | mClass = reinterpret_cast(env->NewGlobalRef(mClass)); 113 | } 114 | 115 | virtual void onStarted() 116 | { 117 | sp proc = ProcessState::self(); 118 | ALOGV("App process: starting thread pool.\n"); 119 | proc->startThreadPool(); 120 | 121 | AndroidRuntime* ar = AndroidRuntime::getRuntime(); 122 | ar->callMain(mClassName, mClass, mArgC, mArgV); 123 | 124 | IPCThreadState::self()->stopProcess(); 125 | } 126 | 127 | virtual void onZygoteInit() 128 | { 129 | // Re-enable tracing now that we're no longer in Zygote. 130 | _atrace_set_tracing_enabled(true); 131 | 132 | sp proc = ProcessState::self(); 133 | ALOGV("App process: starting thread pool.\n"); 134 | proc->startThreadPool(); 135 | } 136 | 137 | virtual void onExit(int code) 138 | { 139 | if (mClassName == NULL) { 140 | // if zygote 141 | IPCThreadState::self()->stopProcess(); 142 | } 143 | 144 | AndroidRuntime::onExit(code); 145 | } 146 | 147 | 148 | const char* mParentDir; 149 | const char* mClassName; 150 | jclass mClass; 151 | int mArgC; 152 | const char* const* mArgV; 153 | }; 154 | 155 | } 156 | 157 | using namespace android; 158 | 159 | /* 160 | * sets argv0 to as much of newArgv0 as will fit 161 | */ 162 | static void setArgv0(const char *argv0, const char *newArgv0) 163 | { 164 | strlcpy(const_cast(argv0), newArgv0, strlen(argv0)); 165 | } 166 | 167 | int main(int argc, char* const argv[]) 168 | { 169 | if (xposed::handleOptions(argc, argv)) 170 | return 0; 171 | 172 | #if PLATFORM_SDK_VERSION >= 16 173 | #ifdef __arm__ 174 | /* 175 | * b/7188322 - Temporarily revert to the compat memory layout 176 | * to avoid breaking third party apps. 177 | * 178 | * THIS WILL GO AWAY IN A FUTURE ANDROID RELEASE. 179 | * 180 | * http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commitdiff;h=7dbaa466 181 | * changes the kernel mapping from bottom up to top-down. 182 | * This breaks some programs which improperly embed 183 | * an out of date copy of Android's linker. 184 | */ 185 | char value[PROPERTY_VALUE_MAX]; 186 | property_get("ro.kernel.qemu", value, ""); 187 | bool is_qemu = (strcmp(value, "1") == 0); 188 | if ((getenv("NO_ADDR_COMPAT_LAYOUT_FIXUP") == NULL) && !is_qemu) { 189 | int current = personality(0xFFFFFFFF); 190 | if ((current & ADDR_COMPAT_LAYOUT) == 0) { 191 | personality(current | ADDR_COMPAT_LAYOUT); 192 | setenv("NO_ADDR_COMPAT_LAYOUT_FIXUP", "1", 1); 193 | execv("/system/bin/app_process", argv); 194 | return -1; 195 | } 196 | } 197 | unsetenv("NO_ADDR_COMPAT_LAYOUT_FIXUP"); 198 | #endif 199 | #endif 200 | 201 | // These are global variables in ProcessState.cpp 202 | mArgC = argc; 203 | mArgV = argv; 204 | 205 | mArgLen = 0; 206 | for (int i=0; i 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include // for AID_SYSTEM 25 | 26 | #include "xposed.h" 27 | #include 28 | 29 | static bool isXposedLoaded = false; 30 | 31 | 32 | namespace android { 33 | 34 | static void app_usage() 35 | { 36 | fprintf(stderr, 37 | "Usage: app_process [java-options] cmd-dir start-class-name [options]\n"); 38 | fprintf(stderr, " with Xposed support\n"); 39 | } 40 | 41 | class AppRuntime : public AndroidRuntime 42 | { 43 | public: 44 | AppRuntime(char* argBlockStart, const size_t argBlockLength) 45 | : AndroidRuntime(argBlockStart, argBlockLength) 46 | , mClass(NULL) 47 | { 48 | } 49 | 50 | void setClassNameAndArgs(const String8& className, int argc, char * const *argv) { 51 | mClassName = className; 52 | for (int i = 0; i < argc; ++i) { 53 | mArgs.add(String8(argv[i])); 54 | } 55 | } 56 | 57 | virtual void onVmCreated(JNIEnv* env) 58 | { 59 | if (isXposedLoaded) 60 | xposed::onVmCreated(env); 61 | 62 | if (mClassName.isEmpty()) { 63 | return; // Zygote. Nothing to do here. 64 | } 65 | 66 | /* 67 | * This is a little awkward because the JNI FindClass call uses the 68 | * class loader associated with the native method we're executing in. 69 | * If called in onStarted (from RuntimeInit.finishInit because we're 70 | * launching "am", for example), FindClass would see that we're calling 71 | * from a boot class' native method, and so wouldn't look for the class 72 | * we're trying to look up in CLASSPATH. Unfortunately it needs to, 73 | * because the "am" classes are not boot classes. 74 | * 75 | * The easiest fix is to call FindClass here, early on before we start 76 | * executing boot class Java code and thereby deny ourselves access to 77 | * non-boot classes. 78 | */ 79 | char* slashClassName = toSlashClassName(mClassName.string()); 80 | mClass = env->FindClass(slashClassName); 81 | if (mClass == NULL) { 82 | ALOGE("ERROR: could not find class '%s'\n", mClassName.string()); 83 | env->ExceptionDescribe(); 84 | } 85 | free(slashClassName); 86 | 87 | mClass = reinterpret_cast(env->NewGlobalRef(mClass)); 88 | } 89 | 90 | virtual void onStarted() 91 | { 92 | sp proc = ProcessState::self(); 93 | ALOGV("App process: starting thread pool.\n"); 94 | proc->startThreadPool(); 95 | 96 | AndroidRuntime* ar = AndroidRuntime::getRuntime(); 97 | ar->callMain(mClassName, mClass, mArgs); 98 | 99 | IPCThreadState::self()->stopProcess(); 100 | } 101 | 102 | virtual void onZygoteInit() 103 | { 104 | #if PLATFORM_SDK_VERSION <= 22 105 | // Re-enable tracing now that we're no longer in Zygote. 106 | atrace_set_tracing_enabled(true); 107 | #endif 108 | 109 | sp proc = ProcessState::self(); 110 | ALOGV("App process: starting thread pool.\n"); 111 | proc->startThreadPool(); 112 | } 113 | 114 | virtual void onExit(int code) 115 | { 116 | if (mClassName.isEmpty()) { 117 | // if zygote 118 | IPCThreadState::self()->stopProcess(); 119 | } 120 | 121 | AndroidRuntime::onExit(code); 122 | } 123 | 124 | 125 | String8 mClassName; 126 | Vector mArgs; 127 | jclass mClass; 128 | }; 129 | 130 | } 131 | 132 | using namespace android; 133 | 134 | static size_t computeArgBlockSize(int argc, char* const argv[]) { 135 | // TODO: This assumes that all arguments are allocated in 136 | // contiguous memory. There isn't any documented guarantee 137 | // that this is the case, but this is how the kernel does it 138 | // (see fs/exec.c). 139 | // 140 | // Also note that this is a constant for "normal" android apps. 141 | // Since they're forked from zygote, the size of their command line 142 | // is the size of the zygote command line. 143 | // 144 | // We change the process name of the process by over-writing 145 | // the start of the argument block (argv[0]) with the new name of 146 | // the process, so we'd mysteriously start getting truncated process 147 | // names if the zygote command line decreases in size. 148 | uintptr_t start = reinterpret_cast(argv[0]); 149 | uintptr_t end = reinterpret_cast(argv[argc - 1]); 150 | end += strlen(argv[argc - 1]) + 1; 151 | return (end - start); 152 | } 153 | 154 | static void maybeCreateDalvikCache() { 155 | #if defined(__aarch64__) 156 | static const char kInstructionSet[] = "arm64"; 157 | #elif defined(__x86_64__) 158 | static const char kInstructionSet[] = "x86_64"; 159 | #elif defined(__arm__) 160 | static const char kInstructionSet[] = "arm"; 161 | #elif defined(__i386__) 162 | static const char kInstructionSet[] = "x86"; 163 | #elif defined (__mips__) && !defined(__LP64__) 164 | static const char kInstructionSet[] = "mips"; 165 | #elif defined (__mips__) && defined(__LP64__) 166 | static const char kInstructionSet[] = "mips64"; 167 | #else 168 | #error "Unknown instruction set" 169 | #endif 170 | const char* androidRoot = getenv("ANDROID_DATA"); 171 | LOG_ALWAYS_FATAL_IF(androidRoot == NULL, "ANDROID_DATA environment variable unset"); 172 | 173 | char dalvikCacheDir[PATH_MAX]; 174 | const int numChars = snprintf(dalvikCacheDir, PATH_MAX, 175 | "%s/dalvik-cache/%s", androidRoot, kInstructionSet); 176 | LOG_ALWAYS_FATAL_IF((numChars >= PATH_MAX || numChars < 0), 177 | "Error constructing dalvik cache : %s", strerror(errno)); 178 | 179 | int result = mkdir(dalvikCacheDir, 0711); 180 | LOG_ALWAYS_FATAL_IF((result < 0 && errno != EEXIST), 181 | "Error creating cache dir %s : %s", dalvikCacheDir, strerror(errno)); 182 | 183 | // We always perform these steps because the directory might 184 | // already exist, with wider permissions and a different owner 185 | // than we'd like. 186 | result = chown(dalvikCacheDir, AID_ROOT, AID_ROOT); 187 | LOG_ALWAYS_FATAL_IF((result < 0), "Error changing dalvik-cache ownership : %s", strerror(errno)); 188 | 189 | result = chmod(dalvikCacheDir, 0711); 190 | LOG_ALWAYS_FATAL_IF((result < 0), 191 | "Error changing dalvik-cache permissions : %s", strerror(errno)); 192 | } 193 | 194 | #if defined(__LP64__) 195 | static const char ABI_LIST_PROPERTY[] = "ro.product.cpu.abilist64"; 196 | static const char ZYGOTE_NICE_NAME[] = "zygote64"; 197 | #else 198 | static const char ABI_LIST_PROPERTY[] = "ro.product.cpu.abilist32"; 199 | static const char ZYGOTE_NICE_NAME[] = "zygote"; 200 | #endif 201 | 202 | static void runtimeStart(AppRuntime& runtime, const char *classname, const Vector& options, bool zygote) 203 | { 204 | #if PLATFORM_SDK_VERSION >= 23 205 | runtime.start(classname, options, zygote); 206 | #else 207 | // try newer variant (5.1.1_r19 and later) first 208 | void (*ptr1)(AppRuntime&, const char*, const Vector&, bool); 209 | *(void **) (&ptr1) = dlsym(RTLD_DEFAULT, "_ZN7android14AndroidRuntime5startEPKcRKNS_6VectorINS_7String8EEEb"); 210 | 211 | if (ptr1 != NULL) { 212 | ptr1(runtime, classname, options, zygote); 213 | return; 214 | } 215 | 216 | // fall back to older variant 217 | void (*ptr2)(AppRuntime&, const char*, const Vector&); 218 | *(void **) (&ptr2) = dlsym(RTLD_DEFAULT, "_ZN7android14AndroidRuntime5startEPKcRKNS_6VectorINS_7String8EEE"); 219 | 220 | if (ptr2 != NULL) { 221 | ptr2(runtime, classname, options); 222 | return; 223 | } 224 | 225 | // should not happen 226 | LOG_ALWAYS_FATAL("app_process: could not locate AndroidRuntime::start() method."); 227 | #endif 228 | } 229 | 230 | int main(int argc, char* const argv[]) 231 | { 232 | if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) < 0) { 233 | // Older kernels don't understand PR_SET_NO_NEW_PRIVS and return 234 | // EINVAL. Don't die on such kernels. 235 | if (errno != EINVAL) { 236 | LOG_ALWAYS_FATAL("PR_SET_NO_NEW_PRIVS failed: %s", strerror(errno)); 237 | return 12; 238 | } 239 | } 240 | 241 | if (xposed::handleOptions(argc, argv)) { 242 | return 0; 243 | } 244 | 245 | AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv)); 246 | // Process command line arguments 247 | // ignore argv[0] 248 | argc--; 249 | argv++; 250 | 251 | // Everything up to '--' or first non '-' arg goes to the vm. 252 | // 253 | // The first argument after the VM args is the "parent dir", which 254 | // is currently unused. 255 | // 256 | // After the parent dir, we expect one or more the following internal 257 | // arguments : 258 | // 259 | // --zygote : Start in zygote mode 260 | // --start-system-server : Start the system server. 261 | // --application : Start in application (stand alone, non zygote) mode. 262 | // --nice-name : The nice name for this process. 263 | // 264 | // For non zygote starts, these arguments will be followed by 265 | // the main class name. All remaining arguments are passed to 266 | // the main method of this class. 267 | // 268 | // For zygote starts, all remaining arguments are passed to the zygote. 269 | // main function. 270 | // 271 | // Note that we must copy argument string values since we will rewrite the 272 | // entire argument block when we apply the nice name to argv0. 273 | 274 | int i; 275 | for (i = 0; i < argc; i++) { 276 | if (argv[i][0] != '-') { 277 | break; 278 | } 279 | if (argv[i][1] == '-' && argv[i][2] == 0) { 280 | ++i; // Skip --. 281 | break; 282 | } 283 | runtime.addOption(strdup(argv[i])); 284 | } 285 | 286 | // Parse runtime arguments. Stop at first unrecognized option. 287 | bool zygote = false; 288 | bool startSystemServer = false; 289 | bool application = false; 290 | String8 niceName; 291 | String8 className; 292 | 293 | ++i; // Skip unused "parent dir" argument. 294 | while (i < argc) { 295 | const char* arg = argv[i++]; 296 | if (strcmp(arg, "--zygote") == 0) { 297 | zygote = true; 298 | niceName = ZYGOTE_NICE_NAME; 299 | } else if (strcmp(arg, "--start-system-server") == 0) { 300 | startSystemServer = true; 301 | } else if (strcmp(arg, "--application") == 0) { 302 | application = true; 303 | } else if (strncmp(arg, "--nice-name=", 12) == 0) { 304 | niceName.setTo(arg + 12); 305 | } else if (strncmp(arg, "--", 2) != 0) { 306 | className.setTo(arg); 307 | break; 308 | } else { 309 | --i; 310 | break; 311 | } 312 | } 313 | 314 | Vector args; 315 | if (!className.isEmpty()) { 316 | // We're not in zygote mode, the only argument we need to pass 317 | // to RuntimeInit is the application argument. 318 | // 319 | // The Remainder of args get passed to startup class main(). Make 320 | // copies of them before we overwrite them with the process name. 321 | args.add(application ? String8("application") : String8("tool")); 322 | runtime.setClassNameAndArgs(className, argc - i, argv + i); 323 | } else { 324 | // We're in zygote mode. 325 | maybeCreateDalvikCache(); 326 | 327 | if (startSystemServer) { 328 | args.add(String8("start-system-server")); 329 | } 330 | 331 | char prop[PROP_VALUE_MAX]; 332 | if (property_get(ABI_LIST_PROPERTY, prop, NULL) == 0) { 333 | LOG_ALWAYS_FATAL("app_process: Unable to determine ABI list from property %s.", 334 | ABI_LIST_PROPERTY); 335 | return 11; 336 | } 337 | 338 | String8 abiFlag("--abi-list="); 339 | abiFlag.append(prop); 340 | args.add(abiFlag); 341 | 342 | // In zygote mode, pass all remaining arguments to the zygote 343 | // main() method. 344 | for (; i < argc; ++i) { 345 | args.add(String8(argv[i])); 346 | } 347 | } 348 | 349 | if (!niceName.isEmpty()) { 350 | runtime.setArgv0(niceName.string()); 351 | set_process_name(niceName.string()); 352 | } 353 | 354 | if (zygote) { 355 | isXposedLoaded = xposed::initialize(true, startSystemServer, NULL, argc, argv); 356 | runtimeStart(runtime, isXposedLoaded ? XPOSED_CLASS_DOTS_ZYGOTE : "com.android.internal.os.ZygoteInit", args, zygote); 357 | } else if (className) { 358 | isXposedLoaded = xposed::initialize(false, false, className, argc, argv); 359 | runtimeStart(runtime, isXposedLoaded ? XPOSED_CLASS_DOTS_TOOLS : "com.android.internal.os.RuntimeInit", args, zygote); 360 | } else { 361 | fprintf(stderr, "Error: no class name or --zygote supplied.\n"); 362 | app_usage(); 363 | LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied."); 364 | return 10; 365 | } 366 | } 367 | -------------------------------------------------------------------------------- /fd_utils-inl.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | #include 35 | #include "JNIHelp.h" 36 | #include "ScopedPrimitiveArray.h" 37 | 38 | static const char* kPathPrefixWhitelist[] = { 39 | "/data/app/", 40 | "/data/app-private/", 41 | "/system/app/", 42 | "/system/priv-app/", 43 | "/vendor/app/", 44 | "/vendor/priv-app/", 45 | }; 46 | 47 | static const char* kFdPath = "/proc/self/fd"; 48 | 49 | // Keeps track of all relevant information (flags, offset etc.) of an 50 | // open zygote file descriptor. 51 | class FileDescriptorInfo { 52 | public: 53 | // Create a FileDescriptorInfo for a given file descriptor. Returns 54 | // |NULL| if an error occurred. 55 | static FileDescriptorInfo* createFromFd(int fd) { 56 | struct stat f_stat; 57 | // This should never happen; the zygote should always have the right set 58 | // of permissions required to stat all its open files. 59 | if (TEMP_FAILURE_RETRY(fstat(fd, &f_stat)) == -1) { 60 | ALOGE("Unable to stat fd %d : %s", fd, strerror(errno)); 61 | return NULL; 62 | } 63 | 64 | if (S_ISSOCK(f_stat.st_mode)) { 65 | std::string socket_name; 66 | if (!GetSocketName(fd, &socket_name)) { 67 | return NULL; 68 | } 69 | 70 | if (!IsWhitelisted(socket_name)) { 71 | //ALOGE("Socket name not whitelisted : %s (fd=%d)", socket_name.c_str(), fd); 72 | return NULL; 73 | } 74 | 75 | return new FileDescriptorInfo(fd); 76 | } 77 | 78 | std::string file_path; 79 | if (!Readlink(fd, &file_path)) { 80 | return NULL; 81 | } 82 | 83 | if (!IsWhitelisted(file_path)) { 84 | //ALOGE("Not whitelisted : %s", file_path.c_str()); 85 | return NULL; 86 | } 87 | 88 | // We only handle whitelisted regular files and character devices. Whitelisted 89 | // character devices must provide a guarantee of sensible behaviour when 90 | // reopened. 91 | // 92 | // S_ISDIR : Not supported. (We could if we wanted to, but it's unused). 93 | // S_ISLINK : Not supported. 94 | // S_ISBLK : Not supported. 95 | // S_ISFIFO : Not supported. Note that the zygote uses pipes to communicate 96 | // with the child process across forks but those should have been closed 97 | // before we got to this point. 98 | if (!S_ISCHR(f_stat.st_mode) && !S_ISREG(f_stat.st_mode)) { 99 | ALOGE("Unsupported st_mode %d", f_stat.st_mode); 100 | return NULL; 101 | } 102 | 103 | // File descriptor flags : currently on FD_CLOEXEC. We can set these 104 | // using F_SETFD - we're single threaded at this point of execution so 105 | // there won't be any races. 106 | const int fd_flags = TEMP_FAILURE_RETRY(fcntl(fd, F_GETFD)); 107 | if (fd_flags == -1) { 108 | ALOGE("Failed fcntl(%d, F_GETFD) : %s", fd, strerror(errno)); 109 | return NULL; 110 | } 111 | 112 | // File status flags : 113 | // - File access mode : (O_RDONLY, O_WRONLY...) we'll pass these through 114 | // to the open() call. 115 | // 116 | // - File creation flags : (O_CREAT, O_EXCL...) - there's not much we can 117 | // do about these, since the file has already been created. We shall ignore 118 | // them here. 119 | // 120 | // - Other flags : We'll have to set these via F_SETFL. On linux, F_SETFL 121 | // can only set O_APPEND, O_ASYNC, O_DIRECT, O_NOATIME, and O_NONBLOCK. 122 | // In particular, it can't set O_SYNC and O_DSYNC. We'll have to test for 123 | // their presence and pass them in to open(). 124 | int fs_flags = TEMP_FAILURE_RETRY(fcntl(fd, F_GETFL)); 125 | if (fs_flags == -1) { 126 | ALOGE("Failed fcntl(%d, F_GETFL) : %s", fd, strerror(errno)); 127 | return NULL; 128 | } 129 | 130 | // File offset : Ignore the offset for non seekable files. 131 | const off_t offset = TEMP_FAILURE_RETRY(lseek64(fd, 0, SEEK_CUR)); 132 | 133 | // We pass the flags that open accepts to open, and use F_SETFL for 134 | // the rest of them. 135 | static const int kOpenFlags = (O_RDONLY | O_WRONLY | O_RDWR | O_DSYNC | O_SYNC); 136 | int open_flags = fs_flags & (kOpenFlags); 137 | fs_flags = fs_flags & (~(kOpenFlags)); 138 | 139 | return new FileDescriptorInfo(f_stat, file_path, fd, open_flags, fd_flags, fs_flags, offset); 140 | } 141 | 142 | bool Detach() const { 143 | const int dev_null_fd = open("/dev/null", O_RDWR); 144 | if (dev_null_fd < 0) { 145 | ALOGE("Failed to open /dev/null : %s", strerror(errno)); 146 | return false; 147 | } 148 | 149 | if (dup2(dev_null_fd, fd) == -1) { 150 | ALOGE("Failed dup2 on socket descriptor %d : %s", fd, strerror(errno)); 151 | return false; 152 | } 153 | 154 | if (close(dev_null_fd) == -1) { 155 | ALOGE("Failed close(%d) : %s", dev_null_fd, strerror(errno)); 156 | return false; 157 | } 158 | 159 | return true; 160 | } 161 | 162 | bool Reopen() const { 163 | if (is_sock) { 164 | return true; 165 | } 166 | 167 | // NOTE: This might happen if the file was unlinked after being opened. 168 | // It's a common pattern in the case of temporary files and the like but 169 | // we should not allow such usage from the zygote. 170 | const int new_fd = TEMP_FAILURE_RETRY(open(file_path.c_str(), open_flags)); 171 | 172 | if (new_fd == -1) { 173 | ALOGE("Failed open(%s, %d) : %s", file_path.c_str(), open_flags, strerror(errno)); 174 | return false; 175 | } 176 | 177 | if (TEMP_FAILURE_RETRY(fcntl(new_fd, F_SETFD, fd_flags)) == -1) { 178 | close(new_fd); 179 | ALOGE("Failed fcntl(%d, F_SETFD, %x) : %s", new_fd, fd_flags, strerror(errno)); 180 | return false; 181 | } 182 | 183 | if (TEMP_FAILURE_RETRY(fcntl(new_fd, F_SETFL, fs_flags)) == -1) { 184 | close(new_fd); 185 | ALOGE("Failed fcntl(%d, F_SETFL, %x) : %s", new_fd, fs_flags, strerror(errno)); 186 | return false; 187 | } 188 | 189 | if (offset != -1 && TEMP_FAILURE_RETRY(lseek64(new_fd, offset, SEEK_SET)) == -1) { 190 | close(new_fd); 191 | ALOGE("Failed lseek64(%d, SEEK_SET) : %s", new_fd, strerror(errno)); 192 | return false; 193 | } 194 | 195 | if (TEMP_FAILURE_RETRY(dup2(new_fd, fd)) == -1) { 196 | close(new_fd); 197 | ALOGE("Failed dup2(%d, %d) : %s", fd, new_fd, strerror(errno)); 198 | return false; 199 | } 200 | 201 | close(new_fd); 202 | 203 | return true; 204 | } 205 | 206 | const int fd; 207 | const struct stat stat; 208 | const std::string file_path; 209 | const int open_flags; 210 | const int fd_flags; 211 | const int fs_flags; 212 | const off_t offset; 213 | const bool is_sock; 214 | 215 | private: 216 | FileDescriptorInfo(int pfd) : 217 | fd(pfd), 218 | stat(), 219 | open_flags(0), 220 | fd_flags(0), 221 | fs_flags(0), 222 | offset(0), 223 | is_sock(true) { 224 | } 225 | 226 | FileDescriptorInfo(struct stat pstat, const std::string& pfile_path, int pfd, int popen_flags, 227 | int pfd_flags, int pfs_flags, off_t poffset) : 228 | fd(pfd), 229 | stat(pstat), 230 | file_path(pfile_path), 231 | open_flags(popen_flags), 232 | fd_flags(pfd_flags), 233 | fs_flags(pfs_flags), 234 | offset(poffset), 235 | is_sock(false) { 236 | } 237 | 238 | // Returns true iff. a given path is whitelisted. 239 | static bool IsWhitelisted(const std::string& path) { 240 | for (size_t i = 0; i < (sizeof(kPathPrefixWhitelist) / sizeof(kPathPrefixWhitelist[0])); ++i) { 241 | if (path.compare(0, strlen(kPathPrefixWhitelist[i]), kPathPrefixWhitelist[i]) == 0) { 242 | return true; 243 | } 244 | } 245 | return false; 246 | } 247 | 248 | // TODO: Call android::base::Readlink instead of copying the code here. 249 | static bool Readlink(const int fd, std::string* result) { 250 | char path[64]; 251 | snprintf(path, sizeof(path), "/proc/self/fd/%d", fd); 252 | 253 | // Code copied from android::base::Readlink starts here : 254 | 255 | // Annoyingly, the readlink system call returns EINVAL for a zero-sized buffer, 256 | // and truncates to whatever size you do supply, so it can't be used to query. 257 | // We could call lstat first, but that would introduce a race condition that 258 | // we couldn't detect. 259 | // ext2 and ext4 both have PAGE_SIZE limitations, so we assume that here. 260 | char* buf = new char[4096]; 261 | ssize_t len = readlink(path, buf, 4096); 262 | if (len == -1) { 263 | delete[] buf; 264 | return false; 265 | } 266 | 267 | result->assign(buf, len); 268 | delete[] buf; 269 | return true; 270 | } 271 | 272 | // Returns the locally-bound name of the socket |fd|. Returns true 273 | // iff. all of the following hold : 274 | // 275 | // - the socket's sa_family is AF_UNIX. 276 | // - the length of the path is greater than zero (i.e, not an unnamed socket). 277 | // - the first byte of the path isn't zero (i.e, not a socket with an abstract 278 | // address). 279 | static bool GetSocketName(const int fd, std::string* result) { 280 | sockaddr_storage ss; 281 | sockaddr* addr = reinterpret_cast(&ss); 282 | socklen_t addr_len = sizeof(ss); 283 | 284 | if (TEMP_FAILURE_RETRY(getsockname(fd, addr, &addr_len)) == -1) { 285 | ALOGE("Failed getsockname(%d) : %s", fd, strerror(errno)); 286 | return false; 287 | } 288 | 289 | #if PLATFORM_SDK_VERSION <= 23 290 | if (addr->sa_family == AF_NETLINK) { 291 | (*result) = "@netlink@"; 292 | return true; 293 | } 294 | #endif 295 | 296 | if (addr->sa_family != AF_UNIX) { 297 | //ALOGE("Unsupported socket (fd=%d) with family %d", fd, addr->sa_family); 298 | return false; 299 | } 300 | 301 | const sockaddr_un* unix_addr = reinterpret_cast(&ss); 302 | 303 | size_t path_len = addr_len - offsetof(struct sockaddr_un, sun_path); 304 | // This is an unnamed local socket, we do not accept it. 305 | if (path_len == 0) { 306 | //ALOGE("Unsupported AF_UNIX socket (fd=%d) with empty path.", fd); 307 | return false; 308 | } 309 | 310 | // This is a local socket with an abstract address, we do not accept it. 311 | if (unix_addr->sun_path[0] == '\0') { 312 | //ALOGE("Unsupported AF_UNIX socket (fd=%d) with abstract address.", fd); 313 | return false; 314 | } 315 | 316 | // If we're here, sun_path must refer to a null terminated filesystem 317 | // pathname (man 7 unix). Remove the terminator before assigning it to an 318 | // std::string. 319 | if (unix_addr->sun_path[path_len - 1] == '\0') { 320 | --path_len; 321 | } 322 | 323 | result->assign(unix_addr->sun_path, path_len); 324 | return true; 325 | } 326 | 327 | 328 | // DISALLOW_COPY_AND_ASSIGN(FileDescriptorInfo); 329 | FileDescriptorInfo(const FileDescriptorInfo&); 330 | void operator=(const FileDescriptorInfo&); 331 | }; 332 | 333 | // A FileDescriptorTable is a collection of FileDescriptorInfo objects 334 | // keyed by their FDs. 335 | class FileDescriptorTable { 336 | public: 337 | // Creates a new FileDescriptorTable. This function scans 338 | // /proc/self/fd for the list of open file descriptors and collects 339 | // information about them. Returns NULL if an error occurs. 340 | static FileDescriptorTable* Create() { 341 | DIR* d = opendir(kFdPath); 342 | if (d == NULL) { 343 | ALOGE("Unable to open directory %s: %s", kFdPath, strerror(errno)); 344 | return NULL; 345 | } 346 | int dir_fd = dirfd(d); 347 | dirent* e; 348 | 349 | std::unordered_map open_fd_map; 350 | while ((e = readdir(d)) != NULL) { 351 | const int fd = ParseFd(e, dir_fd); 352 | if (fd == -1) { 353 | continue; 354 | } 355 | 356 | FileDescriptorInfo* info = FileDescriptorInfo::createFromFd(fd); 357 | if (info == NULL) { 358 | continue; 359 | } 360 | info->Detach(); 361 | open_fd_map[fd] = info; 362 | } 363 | 364 | if (closedir(d) == -1) { 365 | ALOGE("Unable to close directory : %s", strerror(errno)); 366 | return NULL; 367 | } 368 | return new FileDescriptorTable(open_fd_map); 369 | } 370 | 371 | // Reopens all file descriptors that are contained in the table. 372 | void Reopen() { 373 | std::unordered_map::const_iterator it; 374 | for (it = open_fd_map_.begin(); it != open_fd_map_.end(); ++it) { 375 | const FileDescriptorInfo* info = it->second; 376 | if (info != NULL) { 377 | info->Reopen(); 378 | delete info; 379 | } 380 | } 381 | } 382 | 383 | private: 384 | FileDescriptorTable(const std::unordered_map& map) 385 | : open_fd_map_(map) { 386 | } 387 | 388 | static int ParseFd(dirent* e, int dir_fd) { 389 | char* end; 390 | const int fd = strtol(e->d_name, &end, 10); 391 | if ((*end) != '\0') { 392 | return -1; 393 | } 394 | 395 | // Don't bother with the standard input/output/error, they're handled 396 | // specially post-fork anyway. 397 | if (fd <= STDERR_FILENO || fd == dir_fd) { 398 | return -1; 399 | } 400 | 401 | return fd; 402 | } 403 | 404 | // Invariant: All values in this unordered_map are non-NULL. 405 | std::unordered_map open_fd_map_; 406 | 407 | // DISALLOW_COPY_AND_ASSIGN(FileDescriptorTable); 408 | FileDescriptorTable(const FileDescriptorTable&); 409 | void operator=(const FileDescriptorTable&); 410 | }; 411 | -------------------------------------------------------------------------------- /libxposed_art.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * This file includes functions specific to the ART runtime. 3 | */ 4 | 5 | #define LOG_TAG "Xposed" 6 | 7 | #include "xposed_shared.h" 8 | #include "libxposed_common.h" 9 | #if PLATFORM_SDK_VERSION >= 21 10 | #include "fd_utils-inl.h" 11 | #endif 12 | 13 | #include "thread.h" 14 | #include "common_throws.h" 15 | #if PLATFORM_SDK_VERSION >= 23 16 | #include "art_method-inl.h" 17 | #else 18 | #include "mirror/art_method-inl.h" 19 | #endif 20 | #include "mirror/object-inl.h" 21 | #include "mirror/throwable.h" 22 | #include "native/scoped_fast_native_object_access.h" 23 | #include "reflection.h" 24 | #include "scoped_thread_state_change.h" 25 | #include "well_known_classes.h" 26 | 27 | #if PLATFORM_SDK_VERSION >= 24 28 | #include "mirror/abstract_method.h" 29 | #include "thread_list.h" 30 | #endif 31 | 32 | using namespace art; 33 | 34 | #if PLATFORM_SDK_VERSION < 23 35 | using art::mirror::ArtMethod; 36 | #endif 37 | 38 | namespace xposed { 39 | 40 | 41 | //////////////////////////////////////////////////////////// 42 | // Library initialization 43 | //////////////////////////////////////////////////////////// 44 | 45 | /** Called by Xposed's app_process replacement. */ 46 | bool xposedInitLib(XposedShared* shared) { 47 | xposed = shared; 48 | xposed->onVmCreated = &onVmCreatedCommon; 49 | return true; 50 | } 51 | 52 | /** Called very early during VM startup. */ 53 | bool onVmCreated(JNIEnv*) { 54 | // TODO: Handle CLASS_MIUI_RESOURCES? 55 | ArtMethod::xposed_callback_class = classXposedBridge; 56 | ArtMethod::xposed_callback_method = methodXposedBridgeHandleHookedMethod; 57 | return true; 58 | } 59 | 60 | 61 | //////////////////////////////////////////////////////////// 62 | // Utility methods 63 | //////////////////////////////////////////////////////////// 64 | void logExceptionStackTrace() { 65 | Thread* self = Thread::Current(); 66 | ScopedObjectAccess soa(self); 67 | #if PLATFORM_SDK_VERSION >= 23 68 | XLOG(ERROR) << self->GetException()->Dump(); 69 | #else 70 | XLOG(ERROR) << self->GetException(nullptr)->Dump(); 71 | #endif 72 | } 73 | 74 | //////////////////////////////////////////////////////////// 75 | // JNI methods 76 | //////////////////////////////////////////////////////////// 77 | 78 | void XposedBridge_hookMethodNative(JNIEnv* env, jclass, jobject javaReflectedMethod, 79 | jobject, jint, jobject javaAdditionalInfo) { 80 | // Detect usage errors. 81 | ScopedObjectAccess soa(env); 82 | if (javaReflectedMethod == nullptr) { 83 | #if PLATFORM_SDK_VERSION >= 23 84 | ThrowIllegalArgumentException("method must not be null"); 85 | #else 86 | ThrowIllegalArgumentException(nullptr, "method must not be null"); 87 | #endif 88 | return; 89 | } 90 | 91 | // Get the ArtMethod of the method to be hooked. 92 | ArtMethod* artMethod = ArtMethod::FromReflectedMethod(soa, javaReflectedMethod); 93 | 94 | // Hook the method 95 | artMethod->EnableXposedHook(soa, javaAdditionalInfo); 96 | } 97 | 98 | jobject XposedBridge_invokeOriginalMethodNative(JNIEnv* env, jclass, jobject javaMethod, 99 | jint isResolved, jobjectArray, jclass, jobject javaReceiver, jobjectArray javaArgs) { 100 | ScopedFastNativeObjectAccess soa(env); 101 | if (UNLIKELY(!isResolved)) { 102 | ArtMethod* artMethod = ArtMethod::FromReflectedMethod(soa, javaMethod); 103 | if (LIKELY(artMethod->IsXposedHookedMethod())) { 104 | javaMethod = artMethod->GetXposedHookInfo()->reflected_method; 105 | } 106 | } 107 | #if PLATFORM_SDK_VERSION >= 23 108 | return InvokeMethod(soa, javaMethod, javaReceiver, javaArgs); 109 | #else 110 | return InvokeMethod(soa, javaMethod, javaReceiver, javaArgs, true); 111 | #endif 112 | } 113 | 114 | void XposedBridge_setObjectClassNative(JNIEnv* env, jclass, jobject javaObj, jclass javaClazz) { 115 | ScopedObjectAccess soa(env); 116 | StackHandleScope<3> hs(soa.Self()); 117 | Handle clazz(hs.NewHandle(soa.Decode(javaClazz))); 118 | #if PLATFORM_SDK_VERSION >= 23 119 | if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(soa.Self(), clazz, true, true)) { 120 | #else 121 | if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(clazz, true, true)) { 122 | #endif 123 | XLOG(ERROR) << "Could not initialize class " << PrettyClass(clazz.Get()); 124 | return; 125 | } 126 | Handle obj(hs.NewHandle(soa.Decode(javaObj))); 127 | Handle currentClass(hs.NewHandle(obj->GetClass())); 128 | if (clazz->GetObjectSize() != currentClass->GetObjectSize()) { 129 | std::string msg = StringPrintf("Different object sizes: %s (%d) vs. %s (%d)", 130 | PrettyClass(clazz.Get()).c_str(), clazz->GetObjectSize(), 131 | PrettyClass(currentClass.Get()).c_str(), currentClass->GetObjectSize()); 132 | #if PLATFORM_SDK_VERSION >= 23 133 | ThrowIllegalArgumentException(msg.c_str()); 134 | #else 135 | ThrowIllegalArgumentException(nullptr, msg.c_str()); 136 | #endif 137 | return; 138 | } 139 | obj->SetClass(clazz.Get()); 140 | } 141 | 142 | void XposedBridge_dumpObjectNative(JNIEnv*, jclass, jobject) { 143 | // TODO Can be useful for debugging 144 | UNIMPLEMENTED(ERROR|LOG_XPOSED); 145 | } 146 | 147 | jobject XposedBridge_cloneToSubclassNative(JNIEnv* env, jclass, jobject javaObject, jclass javaClazz) { 148 | ScopedObjectAccess soa(env); 149 | StackHandleScope<3> hs(soa.Self()); 150 | Handle obj(hs.NewHandle(soa.Decode(javaObject))); 151 | Handle clazz(hs.NewHandle(soa.Decode(javaClazz))); 152 | Handle dest(hs.NewHandle(obj->Clone(soa.Self(), clazz.Get()))); 153 | return soa.AddLocalReference(dest.Get()); 154 | } 155 | 156 | void XposedBridge_removeFinalFlagNative(JNIEnv* env, jclass, jclass javaClazz) { 157 | ScopedObjectAccess soa(env); 158 | StackHandleScope<1> hs(soa.Self()); 159 | Handle clazz(hs.NewHandle(soa.Decode(javaClazz))); 160 | uint32_t flags = clazz->GetAccessFlags(); 161 | if ((flags & kAccFinal) != 0) { 162 | clazz->SetAccessFlags(flags & ~kAccFinal); 163 | } 164 | } 165 | 166 | jint XposedBridge_getRuntime(JNIEnv*, jclass) { 167 | return 2; // RUNTIME_ART 168 | } 169 | 170 | #if PLATFORM_SDK_VERSION >= 21 171 | static FileDescriptorTable* gClosedFdTable = NULL; 172 | 173 | void XposedBridge_closeFilesBeforeForkNative(JNIEnv*, jclass) { 174 | gClosedFdTable = FileDescriptorTable::Create(); 175 | } 176 | 177 | void XposedBridge_reopenFilesAfterForkNative(JNIEnv*, jclass) { 178 | gClosedFdTable->Reopen(); 179 | delete gClosedFdTable; 180 | gClosedFdTable = NULL; 181 | } 182 | #endif 183 | 184 | #if PLATFORM_SDK_VERSION >= 24 185 | void XposedBridge_invalidateCallersNative(JNIEnv* env, jclass, jobjectArray javaMethods) { 186 | ScopedObjectAccess soa(env); 187 | auto* runtime = Runtime::Current(); 188 | auto* cl = runtime->GetClassLinker(); 189 | 190 | // Invalidate callers of the given methods. 191 | auto* abstract_methods = soa.Decode*>(javaMethods); 192 | size_t count = abstract_methods->GetLength(); 193 | for (size_t i = 0; i < count; i++) { 194 | auto* abstract_method = abstract_methods->Get(i); 195 | if (abstract_method == nullptr) { 196 | continue; 197 | } 198 | ArtMethod* method = abstract_method->GetArtMethod(); 199 | cl->InvalidateCallersForMethod(soa.Self(), method); 200 | } 201 | 202 | // Now instrument the stack to deoptimize methods which are being called right now. 203 | ScopedThreadSuspension sts(soa.Self(), kSuspended); 204 | ScopedSuspendAll ssa(__FUNCTION__); 205 | MutexLock mu(soa.Self(), *Locks::thread_list_lock_); 206 | runtime->GetThreadList()->ForEach([](Thread* thread, void*) SHARED_REQUIRES(Locks::mutator_lock_) { 207 | Runtime::Current()->GetInstrumentation()->InstrumentThreadStack(thread); 208 | }, nullptr); 209 | } 210 | #endif 211 | 212 | } // namespace xposed 213 | -------------------------------------------------------------------------------- /libxposed_common.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * This file includes functions shared by different runtimes. 3 | */ 4 | 5 | #define LOG_TAG "Xposed" 6 | 7 | #include "libxposed_common.h" 8 | #include "JNIHelp.h" 9 | #include 10 | 11 | #define private public 12 | #if PLATFORM_SDK_VERSION == 15 13 | #include 14 | #else 15 | #include 16 | #endif 17 | #undef private 18 | 19 | namespace xposed { 20 | 21 | //////////////////////////////////////////////////////////// 22 | // Variables 23 | //////////////////////////////////////////////////////////// 24 | 25 | bool xposedLoadedSuccessfully = false; 26 | xposed::XposedShared* xposed = NULL; 27 | jclass classXposedBridge = NULL; 28 | static jclass classXResources = NULL; 29 | static jclass classFileResult = NULL; 30 | 31 | jmethodID methodXposedBridgeHandleHookedMethod = NULL; 32 | static jmethodID methodXResourcesTranslateResId = NULL; 33 | static jmethodID methodXResourcesTranslateAttrId = NULL; 34 | static jmethodID constructorFileResult = NULL; 35 | 36 | 37 | //////////////////////////////////////////////////////////// 38 | // Forward declarations 39 | //////////////////////////////////////////////////////////// 40 | 41 | static int register_natives_XposedBridge(JNIEnv* env, jclass clazz); 42 | static int register_natives_XResources(JNIEnv* env, jclass clazz); 43 | static int register_natives_ZygoteService(JNIEnv* env, jclass clazz); 44 | 45 | 46 | //////////////////////////////////////////////////////////// 47 | // Utility methods 48 | //////////////////////////////////////////////////////////// 49 | 50 | /** Read an integer value from a configuration file. */ 51 | int readIntConfig(const char* fileName, int defaultValue) { 52 | FILE *fp = fopen(fileName, "r"); 53 | if (fp == NULL) 54 | return defaultValue; 55 | 56 | int result; 57 | int success = fscanf(fp, "%i", &result); 58 | fclose(fp); 59 | 60 | return (success >= 1) ? result : defaultValue; 61 | } 62 | 63 | 64 | //////////////////////////////////////////////////////////// 65 | // Library initialization 66 | //////////////////////////////////////////////////////////// 67 | 68 | bool initXposedBridge(JNIEnv* env) { 69 | classXposedBridge = env->FindClass(CLASS_XPOSED_BRIDGE); 70 | if (classXposedBridge == NULL) { 71 | ALOGE("Error while loading Xposed class '%s':", CLASS_XPOSED_BRIDGE); 72 | logExceptionStackTrace(); 73 | env->ExceptionClear(); 74 | return false; 75 | } 76 | classXposedBridge = reinterpret_cast(env->NewGlobalRef(classXposedBridge)); 77 | 78 | ALOGI("Found Xposed class '%s', now initializing", CLASS_XPOSED_BRIDGE); 79 | if (register_natives_XposedBridge(env, classXposedBridge) != JNI_OK) { 80 | ALOGE("Could not register natives for '%s'", CLASS_XPOSED_BRIDGE); 81 | logExceptionStackTrace(); 82 | env->ExceptionClear(); 83 | return false; 84 | } 85 | 86 | methodXposedBridgeHandleHookedMethod = env->GetStaticMethodID(classXposedBridge, "handleHookedMethod", 87 | "(Ljava/lang/reflect/Member;ILjava/lang/Object;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;"); 88 | if (methodXposedBridgeHandleHookedMethod == NULL) { 89 | ALOGE("ERROR: could not find method %s.handleHookedMethod(Member, int, Object, Object, Object[])", CLASS_XPOSED_BRIDGE); 90 | logExceptionStackTrace(); 91 | env->ExceptionClear(); 92 | return false; 93 | } 94 | 95 | return true; 96 | } 97 | 98 | bool initZygoteService(JNIEnv* env) { 99 | jclass zygoteServiceClass = env->FindClass(CLASS_ZYGOTE_SERVICE); 100 | if (zygoteServiceClass == NULL) { 101 | ALOGE("Error while loading ZygoteService class '%s':", CLASS_ZYGOTE_SERVICE); 102 | logExceptionStackTrace(); 103 | env->ExceptionClear(); 104 | return false; 105 | } 106 | if (register_natives_ZygoteService(env, zygoteServiceClass) != JNI_OK) { 107 | ALOGE("Could not register natives for '%s'", CLASS_ZYGOTE_SERVICE); 108 | env->ExceptionClear(); 109 | return false; 110 | } 111 | 112 | classFileResult = env->FindClass(CLASS_FILE_RESULT); 113 | if (classFileResult == NULL) { 114 | ALOGE("Error while loading FileResult class '%s':", CLASS_FILE_RESULT); 115 | logExceptionStackTrace(); 116 | env->ExceptionClear(); 117 | return false; 118 | } 119 | classFileResult = reinterpret_cast(env->NewGlobalRef(classFileResult)); 120 | 121 | constructorFileResult = env->GetMethodID(classFileResult, "", "(JJ)V"); 122 | if (constructorFileResult == NULL) { 123 | ALOGE("ERROR: could not find constructor %s(long, long)", CLASS_FILE_RESULT); 124 | logExceptionStackTrace(); 125 | env->ExceptionClear(); 126 | return false; 127 | } 128 | 129 | return true; 130 | } 131 | 132 | void onVmCreatedCommon(JNIEnv* env) { 133 | if (!initXposedBridge(env) || !initZygoteService(env)) { 134 | return; 135 | } 136 | 137 | if (!onVmCreated(env)) { 138 | return; 139 | } 140 | 141 | xposedLoadedSuccessfully = true; 142 | return; 143 | } 144 | 145 | 146 | //////////////////////////////////////////////////////////// 147 | // JNI methods 148 | //////////////////////////////////////////////////////////// 149 | 150 | jboolean XposedBridge_hadInitErrors(JNIEnv*, jclass) { 151 | return !xposedLoadedSuccessfully; 152 | } 153 | 154 | jobject XposedBridge_getStartClassName(JNIEnv* env, jclass) { 155 | return env->NewStringUTF(xposed->startClassName); 156 | } 157 | 158 | jboolean XposedBridge_startsSystemServer(JNIEnv*, jclass) { 159 | return xposed->startSystemServer; 160 | } 161 | 162 | jint XposedBridge_getXposedVersion(JNIEnv*, jclass) { 163 | return xposed->xposedVersionInt; 164 | } 165 | 166 | jboolean XposedBridge_initXResourcesNative(JNIEnv* env, jclass) { 167 | classXResources = env->FindClass(CLASS_XRESOURCES); 168 | if (classXResources == NULL) { 169 | ALOGE("Error while loading XResources class '%s':", CLASS_XRESOURCES); 170 | logExceptionStackTrace(); 171 | env->ExceptionClear(); 172 | return false; 173 | } 174 | classXResources = reinterpret_cast(env->NewGlobalRef(classXResources)); 175 | 176 | if (register_natives_XResources(env, classXResources) != JNI_OK) { 177 | ALOGE("Could not register natives for '%s'", CLASS_XRESOURCES); 178 | env->ExceptionClear(); 179 | return false; 180 | } 181 | 182 | methodXResourcesTranslateResId = env->GetStaticMethodID(classXResources, "translateResId", 183 | "(ILandroid/content/res/XResources;Landroid/content/res/Resources;)I"); 184 | if (methodXResourcesTranslateResId == NULL) { 185 | ALOGE("ERROR: could not find method %s.translateResId(int, XResources, Resources)", CLASS_XRESOURCES); 186 | logExceptionStackTrace(); 187 | env->ExceptionClear(); 188 | return false; 189 | } 190 | 191 | methodXResourcesTranslateAttrId = env->GetStaticMethodID(classXResources, "translateAttrId", 192 | "(Ljava/lang/String;Landroid/content/res/XResources;)I"); 193 | if (methodXResourcesTranslateAttrId == NULL) { 194 | ALOGE("ERROR: could not find method %s.findAttrId(String, XResources)", CLASS_XRESOURCES); 195 | logExceptionStackTrace(); 196 | env->ExceptionClear(); 197 | return false; 198 | } 199 | 200 | return true; 201 | } 202 | 203 | void XResources_rewriteXmlReferencesNative(JNIEnv* env, jclass, 204 | jlong parserPtr, jobject origRes, jobject repRes) { 205 | 206 | using namespace android; 207 | 208 | ResXMLParser* parser = (ResXMLParser*)parserPtr; 209 | const ResXMLTree& mTree = parser->mTree; 210 | uint32_t* mResIds = (uint32_t*)mTree.mResIds; 211 | ResXMLTree_attrExt* tag; 212 | int attrCount; 213 | 214 | if (parser == NULL) 215 | return; 216 | 217 | do { 218 | switch (parser->next()) { 219 | case ResXMLParser::START_TAG: 220 | tag = (ResXMLTree_attrExt*)parser->mCurExt; 221 | attrCount = dtohs(tag->attributeCount); 222 | for (int idx = 0; idx < attrCount; idx++) { 223 | ResXMLTree_attribute* attr = (ResXMLTree_attribute*) 224 | (((const uint8_t*)tag) 225 | + dtohs(tag->attributeStart) 226 | + (dtohs(tag->attributeSize)*idx)); 227 | 228 | // find resource IDs for attribute names 229 | int32_t attrNameID = parser->getAttributeNameID(idx); 230 | // only replace attribute name IDs for app packages 231 | if (attrNameID >= 0 && (size_t)attrNameID < mTree.mNumResIds && dtohl(mResIds[attrNameID]) >= 0x7f000000) { 232 | size_t attNameLen; 233 | const char16_t* attrName = mTree.mStrings.stringAt(attrNameID, &attNameLen); 234 | jint attrResID = env->CallStaticIntMethod(classXResources, methodXResourcesTranslateAttrId, 235 | env->NewString((const jchar*)attrName, attNameLen), origRes); 236 | if (env->ExceptionCheck()) 237 | goto leave; 238 | 239 | mResIds[attrNameID] = htodl(attrResID); 240 | } 241 | 242 | // find original resource IDs for reference values (app packages only) 243 | if (attr->typedValue.dataType != Res_value::TYPE_REFERENCE) 244 | continue; 245 | 246 | jint oldValue = dtohl(attr->typedValue.data); 247 | if (oldValue < 0x7f000000) 248 | continue; 249 | 250 | jint newValue = env->CallStaticIntMethod(classXResources, methodXResourcesTranslateResId, 251 | oldValue, origRes, repRes); 252 | if (env->ExceptionCheck()) 253 | goto leave; 254 | 255 | if (newValue != oldValue) 256 | attr->typedValue.data = htodl(newValue); 257 | } 258 | continue; 259 | case ResXMLParser::END_DOCUMENT: 260 | case ResXMLParser::BAD_DOCUMENT: 261 | goto leave; 262 | default: 263 | continue; 264 | } 265 | } while (true); 266 | 267 | leave: 268 | parser->restart(); 269 | } 270 | 271 | 272 | jboolean ZygoteService_checkFileAccess(JNIEnv* env, jclass, jstring filenameJ, jint mode) { 273 | #if XPOSED_WITH_SELINUX 274 | ScopedUtfChars filename(env, filenameJ); 275 | return xposed->zygoteservice_accessFile(filename.c_str(), mode) == 0; 276 | #else // XPOSED_WITH_SELINUX 277 | return false; 278 | #endif // XPOSED_WITH_SELINUX 279 | } 280 | 281 | jobject ZygoteService_statFile(JNIEnv* env, jclass, jstring filenameJ) { 282 | #if XPOSED_WITH_SELINUX 283 | ScopedUtfChars filename(env, filenameJ); 284 | 285 | struct stat st; 286 | int result = xposed->zygoteservice_statFile(filename.c_str(), &st); 287 | if (result != 0) { 288 | if (errno == ENOENT) { 289 | jniThrowExceptionFmt(env, "java/io/FileNotFoundException", "No such file or directory: %s", filename.c_str()); 290 | } else { 291 | jniThrowExceptionFmt(env, "java/io/IOException", "%s while reading %s", strerror(errno), filename.c_str()); 292 | } 293 | return NULL; 294 | } 295 | 296 | return env->NewObject(classFileResult, constructorFileResult, (jlong) st.st_size, (jlong) st.st_mtime); 297 | #else // XPOSED_WITH_SELINUX 298 | return NULL; 299 | #endif // XPOSED_WITH_SELINUX 300 | } 301 | 302 | jbyteArray ZygoteService_readFile(JNIEnv* env, jclass, jstring filenameJ) { 303 | #if XPOSED_WITH_SELINUX 304 | ScopedUtfChars filename(env, filenameJ); 305 | 306 | int bytesRead = 0; 307 | char* content = xposed->zygoteservice_readFile(filename.c_str(), &bytesRead); 308 | if (content == NULL) { 309 | if (errno == ENOENT) { 310 | jniThrowExceptionFmt(env, "java/io/FileNotFoundException", "No such file or directory: %s", filename.c_str()); 311 | } else { 312 | jniThrowExceptionFmt(env, "java/io/IOException", "%s while reading %s", strerror(errno), filename.c_str()); 313 | } 314 | return NULL; 315 | } 316 | 317 | jbyteArray ret = env->NewByteArray(bytesRead); 318 | if (ret != NULL) { 319 | jbyte* arrptr = (jbyte*)env->GetPrimitiveArrayCritical(ret, 0); 320 | if (arrptr) { 321 | memcpy(arrptr, content, bytesRead); 322 | env->ReleasePrimitiveArrayCritical(ret, arrptr, 0); 323 | } 324 | } 325 | 326 | free(content); 327 | return ret; 328 | #else // XPOSED_WITH_SELINUX 329 | return NULL; 330 | #endif // XPOSED_WITH_SELINUX 331 | } 332 | 333 | //////////////////////////////////////////////////////////// 334 | // JNI methods registrations 335 | //////////////////////////////////////////////////////////// 336 | 337 | int register_natives_XposedBridge(JNIEnv* env, jclass clazz) { 338 | const JNINativeMethod methods[] = { 339 | NATIVE_METHOD(XposedBridge, hadInitErrors, "()Z"), 340 | NATIVE_METHOD(XposedBridge, getStartClassName, "()Ljava/lang/String;"), 341 | NATIVE_METHOD(XposedBridge, getRuntime, "()I"), 342 | NATIVE_METHOD(XposedBridge, startsSystemServer, "()Z"), 343 | NATIVE_METHOD(XposedBridge, getXposedVersion, "()I"), 344 | NATIVE_METHOD(XposedBridge, initXResourcesNative, "()Z"), 345 | NATIVE_METHOD(XposedBridge, hookMethodNative, "(Ljava/lang/reflect/Member;Ljava/lang/Class;ILjava/lang/Object;)V"), 346 | NATIVE_METHOD(XposedBridge, setObjectClassNative, "(Ljava/lang/Object;Ljava/lang/Class;)V"), 347 | NATIVE_METHOD(XposedBridge, dumpObjectNative, "(Ljava/lang/Object;)V"), 348 | NATIVE_METHOD(XposedBridge, cloneToSubclassNative, "(Ljava/lang/Object;Ljava/lang/Class;)Ljava/lang/Object;"), 349 | NATIVE_METHOD(XposedBridge, removeFinalFlagNative, "(Ljava/lang/Class;)V"), 350 | #if PLATFORM_SDK_VERSION >= 21 351 | NATIVE_METHOD(XposedBridge, invokeOriginalMethodNative, 352 | "!(Ljava/lang/reflect/Member;I[Ljava/lang/Class;Ljava/lang/Class;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;"), 353 | NATIVE_METHOD(XposedBridge, closeFilesBeforeForkNative, "()V"), 354 | NATIVE_METHOD(XposedBridge, reopenFilesAfterForkNative, "()V"), 355 | #endif 356 | #if PLATFORM_SDK_VERSION >= 24 357 | NATIVE_METHOD(XposedBridge, invalidateCallersNative, "([Ljava/lang/reflect/Member;)V"), 358 | #endif 359 | }; 360 | return env->RegisterNatives(clazz, methods, NELEM(methods)); 361 | } 362 | 363 | int register_natives_XResources(JNIEnv* env, jclass clazz) { 364 | const JNINativeMethod methods[] = { 365 | NATIVE_METHOD(XResources, rewriteXmlReferencesNative, "(JLandroid/content/res/XResources;Landroid/content/res/Resources;)V"), 366 | }; 367 | return env->RegisterNatives(clazz, methods, NELEM(methods)); 368 | } 369 | 370 | int register_natives_ZygoteService(JNIEnv* env, jclass clazz) { 371 | const JNINativeMethod methods[] = { 372 | NATIVE_METHOD(ZygoteService, checkFileAccess, "(Ljava/lang/String;I)Z"), 373 | NATIVE_METHOD(ZygoteService, statFile, "(Ljava/lang/String;)L" CLASS_FILE_RESULT ";"), 374 | NATIVE_METHOD(ZygoteService, readFile, "(Ljava/lang/String;)[B"), 375 | }; 376 | return env->RegisterNatives(clazz, methods, NELEM(methods)); 377 | } 378 | 379 | } // namespace xposed 380 | -------------------------------------------------------------------------------- /libxposed_common.h: -------------------------------------------------------------------------------- 1 | #ifndef LIBXPOSED_COMMON_H_ 2 | #define LIBXPOSED_COMMON_H_ 3 | 4 | #include "xposed_shared.h" 5 | 6 | #ifndef NATIVE_METHOD 7 | #define NATIVE_METHOD(className, functionName, signature) \ 8 | { #functionName, signature, reinterpret_cast(className ## _ ## functionName) } 9 | #endif 10 | #define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0]))) 11 | 12 | namespace xposed { 13 | 14 | #define CLASS_XPOSED_BRIDGE "de/robv/android/xposed/XposedBridge" 15 | #define CLASS_XRESOURCES "android/content/res/XResources" 16 | #define CLASS_MIUI_RESOURCES "android/content/res/MiuiResources" 17 | #define CLASS_ZYGOTE_SERVICE "de/robv/android/xposed/services/ZygoteService" 18 | #define CLASS_FILE_RESULT "de/robv/android/xposed/services/FileResult" 19 | 20 | 21 | ///////////////////////////////////////////////////////////////// 22 | // Provided by common part, used by runtime-specific implementation 23 | ///////////////////////////////////////////////////////////////// 24 | extern jclass classXposedBridge; 25 | extern jmethodID methodXposedBridgeHandleHookedMethod; 26 | 27 | extern int readIntConfig(const char* fileName, int defaultValue); 28 | extern void onVmCreatedCommon(JNIEnv* env); 29 | 30 | 31 | ///////////////////////////////////////////////////////////////// 32 | // To be provided by runtime-specific implementation 33 | ///////////////////////////////////////////////////////////////// 34 | extern "C" bool xposedInitLib(xposed::XposedShared* shared); 35 | extern bool onVmCreated(JNIEnv* env); 36 | extern void prepareSubclassReplacement(JNIEnv* env, jclass clazz); 37 | extern void logExceptionStackTrace(); 38 | 39 | extern jint XposedBridge_getRuntime(JNIEnv* env, jclass clazz); 40 | extern void XposedBridge_hookMethodNative(JNIEnv* env, jclass clazz, jobject reflectedMethodIndirect, 41 | jobject declaredClassIndirect, jint slot, jobject additionalInfoIndirect); 42 | extern void XposedBridge_setObjectClassNative(JNIEnv* env, jclass clazz, jobject objIndirect, jclass clzIndirect); 43 | extern jobject XposedBridge_cloneToSubclassNative(JNIEnv* env, jclass clazz, jobject objIndirect, jclass clzIndirect); 44 | extern void XposedBridge_dumpObjectNative(JNIEnv* env, jclass clazz, jobject objIndirect); 45 | extern void XposedBridge_removeFinalFlagNative(JNIEnv* env, jclass clazz, jclass javaClazz); 46 | 47 | #if PLATFORM_SDK_VERSION >= 21 48 | extern jobject XposedBridge_invokeOriginalMethodNative(JNIEnv* env, jclass, jobject javaMethod, jint, jobjectArray, 49 | jclass, jobject javaReceiver, jobjectArray javaArgs); 50 | extern void XposedBridge_closeFilesBeforeForkNative(JNIEnv* env, jclass clazz); 51 | extern void XposedBridge_reopenFilesAfterForkNative(JNIEnv* env, jclass clazz); 52 | #endif 53 | #if PLATFORM_SDK_VERSION >= 24 54 | extern void XposedBridge_invalidateCallersNative(JNIEnv*, jclass, jobjectArray javaMethods); 55 | #endif 56 | 57 | } // namespace xposed 58 | 59 | #endif // LIBXPOSED_COMMON_H_ 60 | -------------------------------------------------------------------------------- /libxposed_dalvik.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * This file includes functions specific to the Dalvik runtime. 3 | */ 4 | 5 | #define LOG_TAG "Xposed" 6 | 7 | #include "libxposed_dalvik.h" 8 | #include "xposed_offsets.h" 9 | 10 | #include 11 | 12 | namespace xposed { 13 | 14 | //////////////////////////////////////////////////////////// 15 | // Forward declarations 16 | //////////////////////////////////////////////////////////// 17 | 18 | bool initMemberOffsets(JNIEnv* env); 19 | void hookedMethodCallback(const u4* args, JValue* pResult, const Method* method, ::Thread* self); 20 | void XposedBridge_invokeOriginalMethodNative(const u4* args, JValue* pResult, const Method* method, ::Thread* self); 21 | 22 | 23 | //////////////////////////////////////////////////////////// 24 | // Variables 25 | //////////////////////////////////////////////////////////// 26 | 27 | static ClassObject* objectArrayClass = NULL; 28 | static size_t arrayContentsOffset = 0; 29 | static void* PTR_gDvmJit = NULL; 30 | 31 | 32 | //////////////////////////////////////////////////////////// 33 | // Library initialization 34 | //////////////////////////////////////////////////////////// 35 | 36 | /** Called by Xposed's app_process replacement. */ 37 | bool xposedInitLib(xposed::XposedShared* shared) { 38 | xposed = shared; 39 | xposed->onVmCreated = &onVmCreatedCommon; 40 | return true; 41 | } 42 | 43 | /** Called very early during VM startup. */ 44 | bool onVmCreated(JNIEnv* env) { 45 | if (!initMemberOffsets(env)) 46 | return false; 47 | 48 | jclass classMiuiResources = env->FindClass(CLASS_MIUI_RESOURCES); 49 | if (classMiuiResources != NULL) { 50 | ClassObject* clazz = (ClassObject*)dvmDecodeIndirectRef(dvmThreadSelf(), classMiuiResources); 51 | if (dvmIsFinalClass(clazz)) { 52 | ALOGD("Removing final flag for class '%s'", CLASS_MIUI_RESOURCES); 53 | clazz->accessFlags &= ~ACC_FINAL; 54 | } 55 | } 56 | env->ExceptionClear(); 57 | 58 | Method* xposedInvokeOriginalMethodNative = (Method*) env->GetStaticMethodID(classXposedBridge, "invokeOriginalMethodNative", 59 | "(Ljava/lang/reflect/Member;I[Ljava/lang/Class;Ljava/lang/Class;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;"); 60 | if (xposedInvokeOriginalMethodNative == NULL) { 61 | ALOGE("ERROR: could not find method %s.invokeOriginalMethodNative(Member, int, Class[], Class, Object, Object[])", CLASS_XPOSED_BRIDGE); 62 | dvmLogExceptionStackTrace(); 63 | env->ExceptionClear(); 64 | return false; 65 | } 66 | dvmSetNativeFunc(xposedInvokeOriginalMethodNative, XposedBridge_invokeOriginalMethodNative, NULL); 67 | 68 | objectArrayClass = dvmFindArrayClass("[Ljava/lang/Object;", NULL); 69 | if (objectArrayClass == NULL) { 70 | ALOGE("Error while loading Object[] class"); 71 | dvmLogExceptionStackTrace(); 72 | env->ExceptionClear(); 73 | return false; 74 | } 75 | 76 | return true; 77 | } 78 | 79 | bool initMemberOffsets(JNIEnv* env) { 80 | PTR_gDvmJit = dlsym(RTLD_DEFAULT, "gDvmJit"); 81 | 82 | if (PTR_gDvmJit == NULL) { 83 | offsetMode = MEMBER_OFFSET_MODE_NO_JIT; 84 | } else { 85 | offsetMode = MEMBER_OFFSET_MODE_WITH_JIT; 86 | } 87 | ALOGD("Using structure member offsets for mode %s", xposedOffsetModesDesc[offsetMode]); 88 | 89 | MEMBER_OFFSET_COPY(DvmJitGlobals, codeCacheFull); 90 | 91 | int overrideCodeCacheFull = readIntConfig(XPOSED_OVERRIDE_JIT_RESET_OFFSET, -1); 92 | if (overrideCodeCacheFull > 0 && overrideCodeCacheFull < 0x400) { 93 | ALOGI("Offset for DvmJitGlobals.codeCacheFull is overridden, new value is 0x%x", overrideCodeCacheFull); 94 | MEMBER_OFFSET_VAR(DvmJitGlobals, codeCacheFull) = overrideCodeCacheFull; 95 | } 96 | 97 | // detect offset of ArrayObject->contents 98 | jintArray dummyArray = env->NewIntArray(1); 99 | if (dummyArray == NULL) { 100 | ALOGE("Could allocate int array for testing"); 101 | dvmLogExceptionStackTrace(); 102 | env->ExceptionClear(); 103 | return false; 104 | } 105 | 106 | jint* dummyArrayElements = env->GetIntArrayElements(dummyArray, NULL); 107 | arrayContentsOffset = (size_t)dummyArrayElements - (size_t)dvmDecodeIndirectRef(dvmThreadSelf(), dummyArray); 108 | env->ReleaseIntArrayElements(dummyArray,dummyArrayElements, 0); 109 | env->DeleteLocalRef(dummyArray); 110 | 111 | if (arrayContentsOffset < 12 || arrayContentsOffset > 128) { 112 | ALOGE("Detected strange offset %d of ArrayObject->contents", arrayContentsOffset); 113 | return false; 114 | } 115 | 116 | return true; 117 | } 118 | 119 | 120 | //////////////////////////////////////////////////////////// 121 | // Utility methods 122 | //////////////////////////////////////////////////////////// 123 | 124 | /** Portable clone of dvmSetObjectArrayElement() */ 125 | inline void setObjectArrayElement(const ArrayObject* obj, int index, Object* val) { 126 | uintptr_t arrayContents = (uintptr_t)obj + arrayContentsOffset; 127 | ((Object **)arrayContents)[index] = val; 128 | dvmWriteBarrierArray(obj, index, index + 1); 129 | } 130 | 131 | /** Wrapper used by the common part of the library. */ 132 | void logExceptionStackTrace() { 133 | dvmLogExceptionStackTrace(); 134 | } 135 | 136 | /** Check whether a method is already hooked. */ 137 | inline bool isMethodHooked(const Method* method) { 138 | return (method->nativeFunc == &hookedMethodCallback); 139 | } 140 | 141 | //////////////////////////////////////////////////////////// 142 | // JNI methods 143 | //////////////////////////////////////////////////////////// 144 | 145 | /** This is called when a hooked method is executed. */ 146 | void hookedMethodCallback(const u4* args, JValue* pResult, const Method* method, ::Thread* self) { 147 | if (!isMethodHooked(method)) { 148 | dvmThrowNoSuchMethodError("Could not find Xposed original method - how did you even get here?"); 149 | return; 150 | } 151 | 152 | XposedHookInfo* hookInfo = (XposedHookInfo*) method->insns; 153 | Method* original = (Method*) hookInfo; 154 | Object* originalReflected = hookInfo->reflectedMethod; 155 | Object* additionalInfo = hookInfo->additionalInfo; 156 | 157 | // convert/box arguments 158 | const char* desc = &method->shorty[1]; // [0] is the return type. 159 | Object* thisObject = NULL; 160 | size_t srcIndex = 0; 161 | size_t dstIndex = 0; 162 | 163 | // for non-static methods determine the "this" pointer 164 | if (!dvmIsStaticMethod(original)) { 165 | thisObject = (Object*) args[0]; 166 | srcIndex++; 167 | } 168 | 169 | ArrayObject* argsArray = dvmAllocArrayByClass(objectArrayClass, strlen(method->shorty) - 1, ALLOC_DEFAULT); 170 | if (argsArray == NULL) { 171 | return; 172 | } 173 | 174 | while (*desc != '\0') { 175 | char descChar = *(desc++); 176 | JValue value; 177 | Object* obj; 178 | 179 | switch (descChar) { 180 | case 'Z': 181 | case 'C': 182 | case 'F': 183 | case 'B': 184 | case 'S': 185 | case 'I': 186 | value.i = args[srcIndex++]; 187 | obj = (Object*) dvmBoxPrimitive(value, dvmFindPrimitiveClass(descChar)); 188 | dvmReleaseTrackedAlloc(obj, self); 189 | break; 190 | case 'D': 191 | case 'J': 192 | value.j = dvmGetArgLong(args, srcIndex); 193 | srcIndex += 2; 194 | obj = (Object*) dvmBoxPrimitive(value, dvmFindPrimitiveClass(descChar)); 195 | dvmReleaseTrackedAlloc(obj, self); 196 | break; 197 | case '[': 198 | case 'L': 199 | obj = (Object*) args[srcIndex++]; 200 | break; 201 | default: 202 | ALOGE("Unknown method signature description character: %c", descChar); 203 | obj = NULL; 204 | srcIndex++; 205 | } 206 | setObjectArrayElement(argsArray, dstIndex++, obj); 207 | } 208 | 209 | // call the Java handler function 210 | JValue result; 211 | dvmCallMethod(self, (Method*) methodXposedBridgeHandleHookedMethod, NULL, &result, 212 | originalReflected, (int) original, additionalInfo, thisObject, argsArray); 213 | 214 | dvmReleaseTrackedAlloc(argsArray, self); 215 | 216 | // exceptions are thrown to the caller 217 | if (dvmCheckException(self)) { 218 | return; 219 | } 220 | 221 | // return result with proper type 222 | ClassObject* returnType = dvmGetBoxedReturnType(method); 223 | if (returnType->primitiveType == PRIM_VOID) { 224 | // ignored 225 | } else if (result.l == NULL) { 226 | if (dvmIsPrimitiveClass(returnType)) { 227 | dvmThrowNullPointerException("null result when primitive expected"); 228 | } 229 | pResult->l = NULL; 230 | } else { 231 | if (!dvmUnboxPrimitive(result.l, returnType, pResult)) { 232 | dvmThrowClassCastException(result.l->clazz, returnType); 233 | } 234 | } 235 | } 236 | 237 | 238 | void XposedBridge_hookMethodNative(JNIEnv* env, jclass clazz, jobject reflectedMethodIndirect, 239 | jobject declaredClassIndirect, jint slot, jobject additionalInfoIndirect) { 240 | // Usage errors? 241 | if (declaredClassIndirect == NULL || reflectedMethodIndirect == NULL) { 242 | dvmThrowIllegalArgumentException("method and declaredClass must not be null"); 243 | return; 244 | } 245 | 246 | // Find the internal representation of the method 247 | ClassObject* declaredClass = (ClassObject*) dvmDecodeIndirectRef(dvmThreadSelf(), declaredClassIndirect); 248 | Method* method = dvmSlotToMethod(declaredClass, slot); 249 | if (method == NULL) { 250 | dvmThrowNoSuchMethodError("Could not get internal representation for method"); 251 | return; 252 | } 253 | 254 | if (isMethodHooked(method)) { 255 | // already hooked 256 | return; 257 | } 258 | 259 | // Save a copy of the original method and other hook info 260 | XposedHookInfo* hookInfo = (XposedHookInfo*) calloc(1, sizeof(XposedHookInfo)); 261 | memcpy(hookInfo, method, sizeof(hookInfo->originalMethodStruct)); 262 | hookInfo->reflectedMethod = dvmDecodeIndirectRef(dvmThreadSelf(), env->NewGlobalRef(reflectedMethodIndirect)); 263 | hookInfo->additionalInfo = dvmDecodeIndirectRef(dvmThreadSelf(), env->NewGlobalRef(additionalInfoIndirect)); 264 | 265 | // Replace method with our own code 266 | SET_METHOD_FLAG(method, ACC_NATIVE); 267 | method->nativeFunc = &hookedMethodCallback; 268 | method->insns = (const u2*) hookInfo; 269 | method->registersSize = method->insSize; 270 | method->outsSize = 0; 271 | 272 | if (PTR_gDvmJit != NULL) { 273 | // reset JIT cache 274 | char currentValue = *((char*)PTR_gDvmJit + MEMBER_OFFSET_VAR(DvmJitGlobals,codeCacheFull)); 275 | if (currentValue == 0 || currentValue == 1) { 276 | MEMBER_VAL(PTR_gDvmJit, DvmJitGlobals, codeCacheFull) = true; 277 | } else { 278 | ALOGE("Unexpected current value for codeCacheFull: %d", currentValue); 279 | } 280 | } 281 | } 282 | 283 | 284 | /** 285 | * Simplified copy of Method.invokeNative(), but calls the original (non-hooked) method 286 | * and has no access checks. Used to call the real implementation of hooked methods. 287 | */ 288 | void XposedBridge_invokeOriginalMethodNative(const u4* args, JValue* pResult, 289 | const Method* method, ::Thread* self) { 290 | Method* meth = (Method*) args[1]; 291 | if (meth == NULL) { 292 | meth = dvmGetMethodFromReflectObj((Object*) args[0]); 293 | if (isMethodHooked(meth)) { 294 | meth = (Method*) meth->insns; 295 | } 296 | } 297 | ArrayObject* params = (ArrayObject*) args[2]; 298 | ClassObject* returnType = (ClassObject*) args[3]; 299 | Object* thisObject = (Object*) args[4]; // null for static methods 300 | ArrayObject* argList = (ArrayObject*) args[5]; 301 | 302 | // invoke the method 303 | pResult->l = dvmInvokeMethod(thisObject, meth, argList, params, returnType, true); 304 | return; 305 | } 306 | 307 | void XposedBridge_setObjectClassNative(JNIEnv* env, jclass clazz, jobject objIndirect, jclass clzIndirect) { 308 | Object* obj = (Object*) dvmDecodeIndirectRef(dvmThreadSelf(), objIndirect); 309 | ClassObject* clz = (ClassObject*) dvmDecodeIndirectRef(dvmThreadSelf(), clzIndirect); 310 | if (clz->status < CLASS_INITIALIZED && !dvmInitClass(clz)) { 311 | ALOGE("Could not initialize class %s", clz->descriptor); 312 | return; 313 | } 314 | obj->clazz = clz; 315 | } 316 | 317 | void XposedBridge_dumpObjectNative(JNIEnv* env, jclass clazz, jobject objIndirect) { 318 | Object* obj = (Object*) dvmDecodeIndirectRef(dvmThreadSelf(), objIndirect); 319 | dvmDumpObject(obj); 320 | } 321 | 322 | jobject XposedBridge_cloneToSubclassNative(JNIEnv* env, jclass clazz, jobject objIndirect, jclass clzIndirect) { 323 | Object* obj = (Object*) dvmDecodeIndirectRef(dvmThreadSelf(), objIndirect); 324 | ClassObject* clz = (ClassObject*) dvmDecodeIndirectRef(dvmThreadSelf(), clzIndirect); 325 | 326 | jobject copyIndirect = env->AllocObject(clzIndirect); 327 | if (copyIndirect == NULL) 328 | return NULL; 329 | 330 | Object* copy = (Object*) dvmDecodeIndirectRef(dvmThreadSelf(), copyIndirect); 331 | size_t size = obj->clazz->objectSize; 332 | size_t offset = sizeof(Object); 333 | memcpy((char*)copy + offset, (char*)obj + offset, size - offset); 334 | 335 | if (IS_CLASS_FLAG_SET(clz, CLASS_ISFINALIZABLE)) 336 | dvmSetFinalizable(copy); 337 | 338 | return copyIndirect; 339 | } 340 | 341 | void XposedBridge_removeFinalFlagNative(JNIEnv* env, jclass, jclass javaClazz) { 342 | ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(dvmThreadSelf(), javaClazz); 343 | if (dvmIsFinalClass(clazz)) { 344 | clazz->accessFlags &= ~ACC_FINAL; 345 | } 346 | } 347 | 348 | jint XposedBridge_getRuntime(JNIEnv* env, jclass clazz) { 349 | return 1; // RUNTIME_DALVIK 350 | } 351 | 352 | } // namespace android 353 | -------------------------------------------------------------------------------- /libxposed_dalvik.h: -------------------------------------------------------------------------------- 1 | #ifndef LIBXPOSED_DALVIK_H_ 2 | #define LIBXPOSED_DALVIK_H_ 3 | 4 | #define ANDROID_SMP 1 5 | #include "Dalvik.h" 6 | 7 | #include "libxposed_common.h" 8 | 9 | namespace xposed { 10 | 11 | #define XPOSED_OVERRIDE_JIT_RESET_OFFSET XPOSED_DIR "conf/jit_reset_offset" 12 | 13 | struct XposedHookInfo { 14 | struct { 15 | Method originalMethod; 16 | // copy a few bytes more than defined for Method in AOSP 17 | // to accomodate for (rare) extensions by the target ROM 18 | int dummyForRomExtensions[4]; 19 | } originalMethodStruct; 20 | 21 | Object* reflectedMethod; 22 | Object* additionalInfo; 23 | }; 24 | 25 | } // namespace xposed 26 | 27 | #endif // LIBXPOSED_DALVIK_H_ 28 | -------------------------------------------------------------------------------- /xposed.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * This file includes functions called directly from app_main.cpp during startup. 3 | */ 4 | 5 | #define LOG_TAG "Xposed" 6 | 7 | #include "xposed.h" 8 | #include "xposed_logcat.h" 9 | #include "xposed_safemode.h" 10 | #include "xposed_service.h" 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #if PLATFORM_SDK_VERSION >= 18 25 | #include 26 | #else 27 | #include 28 | #endif 29 | 30 | namespace xposed { 31 | 32 | //////////////////////////////////////////////////////////// 33 | // Variables 34 | //////////////////////////////////////////////////////////// 35 | 36 | XposedShared* xposed = new XposedShared; 37 | static int sdkVersion = -1; 38 | static char* argBlockStart; 39 | static size_t argBlockLength; 40 | 41 | const char* xposedVersion = "unknown (invalid " XPOSED_PROP_FILE ")"; 42 | uint32_t xposedVersionInt = 0; 43 | 44 | //////////////////////////////////////////////////////////// 45 | // Functions 46 | //////////////////////////////////////////////////////////// 47 | 48 | /** Handle special command line options. */ 49 | bool handleOptions(int argc, char* const argv[]) { 50 | parseXposedProp(); 51 | 52 | if (argc == 2 && strcmp(argv[1], "--xposedversion") == 0) { 53 | printf("Xposed version: %s\n", xposedVersion); 54 | return true; 55 | } 56 | 57 | if (argc == 2 && strcmp(argv[1], "--xposedtestsafemode") == 0) { 58 | printf("Testing Xposed safemode trigger\n"); 59 | 60 | if (detectSafemodeTrigger(shouldSkipSafemodeDelay())) { 61 | printf("Safemode triggered\n"); 62 | } else { 63 | printf("Safemode not triggered\n"); 64 | } 65 | return true; 66 | } 67 | 68 | // From Lollipop coding, used to override the process name 69 | argBlockStart = argv[0]; 70 | uintptr_t start = reinterpret_cast(argv[0]); 71 | uintptr_t end = reinterpret_cast(argv[argc - 1]); 72 | end += strlen(argv[argc - 1]) + 1; 73 | argBlockLength = end - start; 74 | 75 | return false; 76 | } 77 | 78 | /** Initialize Xposed (unless it is disabled). */ 79 | bool initialize(bool zygote, bool startSystemServer, const char* className, int argc, char* const argv[]) { 80 | #if !defined(XPOSED_ENABLE_FOR_TOOLS) 81 | if (!zygote) 82 | return false; 83 | #endif 84 | 85 | if (isMinimalFramework()) { 86 | ALOGI("Not loading Xposed for minimal framework (encrypted device)"); 87 | return false; 88 | } 89 | 90 | xposed->zygote = zygote; 91 | xposed->startSystemServer = startSystemServer; 92 | xposed->startClassName = className; 93 | xposed->xposedVersionInt = xposedVersionInt; 94 | 95 | #if XPOSED_WITH_SELINUX 96 | xposed->isSELinuxEnabled = is_selinux_enabled() == 1; 97 | xposed->isSELinuxEnforcing = xposed->isSELinuxEnabled && security_getenforce() == 1; 98 | #else 99 | xposed->isSELinuxEnabled = false; 100 | xposed->isSELinuxEnforcing = false; 101 | #endif // XPOSED_WITH_SELINUX 102 | 103 | if (startSystemServer) { 104 | xposed::logcat::printStartupMarker(); 105 | } else if (zygote) { 106 | // TODO Find a better solution for this 107 | // Give the primary Zygote process a little time to start first. 108 | // This also makes the log easier to read, as logs for the two Zygotes are not mixed up. 109 | sleep(10); 110 | } 111 | 112 | printRomInfo(); 113 | 114 | if (startSystemServer) { 115 | if (!determineXposedInstallerUidGid() || !xposed::service::startAll()) { 116 | return false; 117 | } 118 | xposed::logcat::start(); 119 | #if XPOSED_WITH_SELINUX 120 | } else if (xposed->isSELinuxEnabled) { 121 | if (!xposed::service::startMembased()) { 122 | return false; 123 | } 124 | #endif // XPOSED_WITH_SELINUX 125 | } 126 | 127 | #if XPOSED_WITH_SELINUX 128 | // Don't let any further forks access the Zygote service 129 | if (xposed->isSELinuxEnabled) { 130 | xposed::service::membased::restrictMemoryInheritance(); 131 | } 132 | #endif // XPOSED_WITH_SELINUX 133 | 134 | // FIXME Zygote has no access to input devices, this would need to be check in system_server context 135 | if (zygote && !isSafemodeDisabled() && detectSafemodeTrigger(shouldSkipSafemodeDelay())) 136 | disableXposed(); 137 | 138 | if (isDisabled() || (!zygote && shouldIgnoreCommand(argc, argv))) 139 | return false; 140 | 141 | return addJarToClasspath(); 142 | } 143 | 144 | /** Print information about the used ROM into the log */ 145 | void printRomInfo() { 146 | char release[PROPERTY_VALUE_MAX]; 147 | char sdk[PROPERTY_VALUE_MAX]; 148 | char manufacturer[PROPERTY_VALUE_MAX]; 149 | char model[PROPERTY_VALUE_MAX]; 150 | char rom[PROPERTY_VALUE_MAX]; 151 | char fingerprint[PROPERTY_VALUE_MAX]; 152 | char platform[PROPERTY_VALUE_MAX]; 153 | #if defined(__LP64__) 154 | const int bit = 64; 155 | #else 156 | const int bit = 32; 157 | #endif 158 | 159 | property_get("ro.build.version.release", release, "n/a"); 160 | property_get("ro.build.version.sdk", sdk, "n/a"); 161 | property_get("ro.product.manufacturer", manufacturer, "n/a"); 162 | property_get("ro.product.model", model, "n/a"); 163 | property_get("ro.build.display.id", rom, "n/a"); 164 | property_get("ro.build.fingerprint", fingerprint, "n/a"); 165 | property_get("ro.product.cpu.abi", platform, "n/a"); 166 | 167 | ALOGI("-----------------"); 168 | ALOGI("Starting Xposed version %s, compiled for SDK %d", xposedVersion, PLATFORM_SDK_VERSION); 169 | ALOGI("Device: %s (%s), Android version %s (SDK %s)", model, manufacturer, release, sdk); 170 | ALOGI("ROM: %s", rom); 171 | ALOGI("Build fingerprint: %s", fingerprint); 172 | ALOGI("Platform: %s, %d-bit binary, system server: %s", platform, bit, xposed->startSystemServer ? "yes" : "no"); 173 | if (!xposed->zygote) { 174 | ALOGI("Class name: %s", xposed->startClassName); 175 | } 176 | ALOGI("SELinux enabled: %s, enforcing: %s", 177 | xposed->isSELinuxEnabled ? "yes" : "no", 178 | xposed->isSELinuxEnforcing ? "yes" : "no"); 179 | } 180 | 181 | /** Parses /system/xposed.prop and stores selected values in variables */ 182 | void parseXposedProp() { 183 | FILE *fp = fopen(XPOSED_PROP_FILE, "r"); 184 | if (fp == NULL) { 185 | ALOGE("Could not read %s: %s", XPOSED_PROP_FILE, strerror(errno)); 186 | return; 187 | } 188 | 189 | char buf[512]; 190 | while (fgets(buf, sizeof(buf), fp) != NULL) { 191 | char* key = buf; 192 | // Ignore leading spaces for the key 193 | while (isspace(*key)) key++; 194 | 195 | // Skip comments 196 | if (*key == '#') 197 | continue; 198 | 199 | // Find the key/value separator 200 | char* value = strchr(buf, '='); 201 | if (value == NULL) 202 | continue; 203 | 204 | // Ignore trailing spaces for the key 205 | char* tmp = value; 206 | do { *tmp = 0; tmp--; } while (isspace(*tmp)); 207 | 208 | // Ignore leading spaces for the value 209 | do { value++; } while (isspace(*value)); 210 | 211 | // Remove trailing newline 212 | tmp = strpbrk(value, "\n\r"); 213 | if (tmp != NULL) 214 | *tmp = 0; 215 | 216 | // Handle this entry 217 | if (!strcmp("version", key)) { 218 | int len = strlen(value); 219 | if (len == 0) 220 | continue; 221 | tmp = (char*) malloc(len + 1); 222 | strlcpy(tmp, value, len + 1); 223 | xposedVersion = tmp; 224 | xposedVersionInt = atoi(xposedVersion); 225 | } 226 | } 227 | fclose(fp); 228 | 229 | return; 230 | } 231 | 232 | /** Returns the SDK version of the system */ 233 | int getSdkVersion() { 234 | if (sdkVersion < 0) { 235 | char sdkString[PROPERTY_VALUE_MAX]; 236 | property_get("ro.build.version.sdk", sdkString, "0"); 237 | sdkVersion = atoi(sdkString); 238 | } 239 | return sdkVersion; 240 | } 241 | 242 | /** Check whether Xposed is disabled by a flag file */ 243 | bool isDisabled() { 244 | if (zygote_access(XPOSED_LOAD_BLOCKER, F_OK) == 0) { 245 | ALOGE("Found %s, not loading Xposed", XPOSED_LOAD_BLOCKER); 246 | return true; 247 | } 248 | return false; 249 | } 250 | 251 | /** Create a flag file to disable Xposed. */ 252 | void disableXposed() { 253 | int fd; 254 | // FIXME add a "touch" operation to xposed::service::membased 255 | fd = open(XPOSED_LOAD_BLOCKER, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR); 256 | if (fd >= 0) 257 | close(fd); 258 | } 259 | 260 | /** Check whether safemode is disabled. */ 261 | bool isSafemodeDisabled() { 262 | if (zygote_access(XPOSED_SAFEMODE_DISABLE, F_OK) == 0) 263 | return true; 264 | else 265 | return false; 266 | } 267 | 268 | /** Check whether the delay for safemode should be skipped. */ 269 | bool shouldSkipSafemodeDelay() { 270 | if (zygote_access(XPOSED_SAFEMODE_NODELAY, F_OK) == 0) 271 | return true; 272 | else 273 | return false; 274 | } 275 | 276 | /** Ignore the broadcasts by various Superuser implementations to avoid spamming the Xposed log. */ 277 | bool shouldIgnoreCommand(int argc, const char* const argv[]) { 278 | if (argc < 4 || strcmp(xposed->startClassName, "com.android.commands.am.Am") != 0) 279 | return false; 280 | 281 | if (strcmp(argv[2], "broadcast") != 0 && strcmp(argv[2], "start") != 0) 282 | return false; 283 | 284 | bool mightBeSuperuser = false; 285 | for (int i = 3; i < argc; i++) { 286 | if (strcmp(argv[i], "com.noshufou.android.su.RESULT") == 0 287 | || strcmp(argv[i], "eu.chainfire.supersu.NativeAccess") == 0) 288 | return true; 289 | 290 | if (mightBeSuperuser && strcmp(argv[i], "--user") == 0) 291 | return true; 292 | 293 | const char* lastComponent = strrchr(argv[i], '.'); 294 | if (!lastComponent) 295 | continue; 296 | 297 | if (strcmp(lastComponent, ".RequestActivity") == 0 298 | || strcmp(lastComponent, ".NotifyActivity") == 0 299 | || strcmp(lastComponent, ".SuReceiver") == 0) 300 | mightBeSuperuser = true; 301 | } 302 | 303 | return false; 304 | } 305 | 306 | /** Adds a path to the beginning of an environment variable. */ 307 | static bool addPathToEnv(const char* name, const char* path) { 308 | char* oldPath = getenv(name); 309 | if (oldPath == NULL) { 310 | setenv(name, path, 1); 311 | } else { 312 | char newPath[4096]; 313 | int neededLength = snprintf(newPath, sizeof(newPath), "%s:%s", path, oldPath); 314 | if (neededLength >= (int)sizeof(newPath)) { 315 | ALOGE("ERROR: %s would exceed %" PRIuPTR " characters", name, sizeof(newPath)); 316 | return false; 317 | } 318 | setenv(name, newPath, 1); 319 | } 320 | return true; 321 | } 322 | 323 | /** Add XposedBridge.jar to the Java classpath. */ 324 | bool addJarToClasspath() { 325 | ALOGI("-----------------"); 326 | 327 | // Do we have a new version and are (re)starting zygote? Then load it! 328 | /* 329 | FIXME if you can 330 | if (xposed->startSystemServer && access(XPOSED_JAR_NEWVERSION, R_OK) == 0) { 331 | ALOGI("Found new Xposed jar version, activating it"); 332 | if (rename(XPOSED_JAR_NEWVERSION, XPOSED_JAR) != 0) { 333 | ALOGE("Could not move %s to %s", XPOSED_JAR_NEWVERSION, XPOSED_JAR); 334 | return false; 335 | } 336 | } 337 | */ 338 | 339 | if (access(XPOSED_JAR, R_OK) == 0) { 340 | if (!addPathToEnv("CLASSPATH", XPOSED_JAR)) 341 | return false; 342 | 343 | ALOGI("Added Xposed (%s) to CLASSPATH", XPOSED_JAR); 344 | return true; 345 | } else { 346 | ALOGE("ERROR: Could not access Xposed jar '%s'", XPOSED_JAR); 347 | return false; 348 | } 349 | } 350 | 351 | /** Callback which checks the loaded shared libraries for libdvm/libart. */ 352 | static bool determineRuntime(const char** xposedLibPath) { 353 | FILE *fp = fopen("/proc/self/maps", "r"); 354 | if (fp == NULL) { 355 | ALOGE("Could not open /proc/self/maps: %s", strerror(errno)); 356 | return false; 357 | } 358 | 359 | bool success = false; 360 | char line[256]; 361 | while (fgets(line, sizeof(line), fp) != NULL) { 362 | char* libname = strrchr(line, '/'); 363 | if (!libname) 364 | continue; 365 | libname++; 366 | 367 | if (strcmp("libdvm.so\n", libname) == 0) { 368 | ALOGI("Detected Dalvik runtime"); 369 | *xposedLibPath = XPOSED_LIB_DALVIK; 370 | success = true; 371 | break; 372 | 373 | } else if (strcmp("libart.so\n", libname) == 0) { 374 | ALOGI("Detected ART runtime"); 375 | *xposedLibPath = XPOSED_LIB_ART; 376 | success = true; 377 | break; 378 | } 379 | } 380 | 381 | fclose(fp); 382 | return success; 383 | } 384 | 385 | /** Load the libxposed_*.so library for the currently active runtime. */ 386 | void onVmCreated(JNIEnv* env) { 387 | // Determine the currently active runtime 388 | const char* xposedLibPath = NULL; 389 | if (!determineRuntime(&xposedLibPath)) { 390 | ALOGE("Could not determine runtime, not loading Xposed"); 391 | return; 392 | } 393 | 394 | // Load the suitable libxposed_*.so for it 395 | void* xposedLibHandle = dlopen(xposedLibPath, RTLD_NOW); 396 | if (!xposedLibHandle) { 397 | ALOGE("Could not load libxposed: %s", dlerror()); 398 | return; 399 | } 400 | 401 | // Clear previous errors 402 | dlerror(); 403 | 404 | // Initialize the library 405 | bool (*xposedInitLib)(XposedShared* shared) = NULL; 406 | *(void **) (&xposedInitLib) = dlsym(xposedLibHandle, "xposedInitLib"); 407 | if (!xposedInitLib) { 408 | ALOGE("Could not find function xposedInitLib"); 409 | return; 410 | } 411 | 412 | #if XPOSED_WITH_SELINUX 413 | xposed->zygoteservice_accessFile = &service::membased::accessFile; 414 | xposed->zygoteservice_statFile = &service::membased::statFile; 415 | xposed->zygoteservice_readFile = &service::membased::readFile; 416 | #endif // XPOSED_WITH_SELINUX 417 | 418 | if (xposedInitLib(xposed)) { 419 | xposed->onVmCreated(env); 420 | } 421 | } 422 | 423 | /** Set the process name */ 424 | void setProcessName(const char* name) { 425 | memset(argBlockStart, 0, argBlockLength); 426 | strlcpy(argBlockStart, name, argBlockLength); 427 | set_process_name(name); 428 | } 429 | 430 | /** Determine the UID/GID of Xposed Installer. */ 431 | bool determineXposedInstallerUidGid() { 432 | if (xposed->isSELinuxEnabled) { 433 | struct stat* st = (struct stat*) mmap(NULL, sizeof(struct stat), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); 434 | if (st == MAP_FAILED) { 435 | ALOGE("Could not allocate memory in determineXposedInstallerUidGid(): %s", strerror(errno)); 436 | return false; 437 | } 438 | 439 | pid_t pid; 440 | if ((pid = fork()) < 0) { 441 | ALOGE("Fork in determineXposedInstallerUidGid() failed: %s", strerror(errno)); 442 | munmap(st, sizeof(struct stat)); 443 | return false; 444 | } else if (pid == 0) { 445 | // Child. 446 | #if XPOSED_WITH_SELINUX 447 | if (setcon(ctx_app) != 0) { 448 | ALOGE("Could not switch to %s context", ctx_app); 449 | exit(EXIT_FAILURE); 450 | } 451 | #endif // XPOSED_WITH_SELINUX 452 | 453 | if (TEMP_FAILURE_RETRY(stat(XPOSED_DIR, st)) != 0) { 454 | ALOGE("Could not stat %s: %s", XPOSED_DIR, strerror(errno)); 455 | exit(EXIT_FAILURE); 456 | } 457 | 458 | exit(EXIT_SUCCESS); 459 | } 460 | 461 | // Parent. 462 | int status; 463 | if (waitpid(pid, &status, 0) == -1 || !WIFEXITED(status) || WEXITSTATUS(status) != 0) { 464 | munmap(st, sizeof(struct stat)); 465 | return false; 466 | } 467 | 468 | xposed->installer_uid = st->st_uid; 469 | xposed->installer_gid = st->st_gid; 470 | munmap(st, sizeof(struct stat)); 471 | return true; 472 | } else { 473 | struct stat st; 474 | if (TEMP_FAILURE_RETRY(stat(XPOSED_DIR, &st)) != 0) { 475 | ALOGE("Could not stat %s: %s", XPOSED_DIR, strerror(errno)); 476 | return false; 477 | } 478 | 479 | xposed->installer_uid = st.st_uid; 480 | xposed->installer_gid = st.st_gid; 481 | return true; 482 | } 483 | } 484 | 485 | /** Switch UID/GID to the ones of Xposed Installer. */ 486 | bool switchToXposedInstallerUidGid() { 487 | if (setresgid(xposed->installer_gid, xposed->installer_gid, xposed->installer_gid) != 0) { 488 | ALOGE("Could not setgid(%d): %s", xposed->installer_gid, strerror(errno)); 489 | return false; 490 | } 491 | if (setresuid(xposed->installer_uid, xposed->installer_uid, xposed->installer_uid) != 0) { 492 | ALOGE("Could not setuid(%d): %s", xposed->installer_uid, strerror(errno)); 493 | return false; 494 | } 495 | return true; 496 | } 497 | 498 | /** Drop all capabilities except for the mentioned ones */ 499 | void dropCapabilities(int8_t keep[]) { 500 | struct __user_cap_header_struct header; 501 | struct __user_cap_data_struct cap[2]; 502 | memset(&header, 0, sizeof(header)); 503 | memset(&cap, 0, sizeof(cap)); 504 | header.version = _LINUX_CAPABILITY_VERSION_3; 505 | header.pid = 0; 506 | 507 | if (keep != NULL) { 508 | for (int i = 0; keep[i] >= 0; i++) { 509 | cap[CAP_TO_INDEX(keep[i])].permitted |= CAP_TO_MASK(keep[i]); 510 | } 511 | cap[0].effective = cap[0].inheritable = cap[0].permitted; 512 | cap[1].effective = cap[1].inheritable = cap[1].permitted; 513 | } 514 | 515 | capset(&header, &cap[0]); 516 | } 517 | 518 | /** 519 | * Checks whether the system is booting into a minimal Android framework. 520 | * This is the case when the device is encrypted with a password that 521 | * has to be entered on boot. /data is a tmpfs in that case, so we 522 | * can't load any modules anyway. 523 | * The system will reboot later with the full framework. 524 | */ 525 | bool isMinimalFramework() { 526 | char voldDecrypt[PROPERTY_VALUE_MAX]; 527 | property_get("vold.decrypt", voldDecrypt, ""); 528 | return ((strcmp(voldDecrypt, "trigger_restart_min_framework") == 0) || 529 | (strcmp(voldDecrypt, "1") == 0)); 530 | } 531 | 532 | } // namespace xposed 533 | -------------------------------------------------------------------------------- /xposed.h: -------------------------------------------------------------------------------- 1 | #ifndef XPOSED_H_ 2 | #define XPOSED_H_ 3 | 4 | #include "xposed_shared.h" 5 | 6 | #define XPOSED_PROP_FILE "/system/xposed.prop" 7 | 8 | #if defined(__LP64__) 9 | #define XPOSED_LIB_DIR "/system/lib64/" 10 | #else 11 | #define XPOSED_LIB_DIR "/system/lib/" 12 | #endif 13 | #define XPOSED_LIB_DALVIK XPOSED_LIB_DIR "libxposed_dalvik.so" 14 | #define XPOSED_LIB_ART XPOSED_LIB_DIR "libxposed_art.so" 15 | #define XPOSED_JAR "/system/framework/XposedBridge.jar" 16 | #define XPOSED_JAR_NEWVERSION XPOSED_DIR "bin/XposedBridge.jar.newversion" 17 | #define XPOSED_LOAD_BLOCKER XPOSED_DIR "conf/disabled" 18 | #define XPOSED_SAFEMODE_NODELAY XPOSED_DIR "conf/safemode_nodelay" 19 | #define XPOSED_SAFEMODE_DISABLE XPOSED_DIR "conf/safemode_disable" 20 | 21 | #define XPOSED_CLASS_DOTS_ZYGOTE "de.robv.android.xposed.XposedBridge" 22 | #define XPOSED_CLASS_DOTS_TOOLS "de.robv.android.xposed.XposedBridge$ToolEntryPoint" 23 | 24 | #if XPOSED_WITH_SELINUX 25 | #include 26 | #define ctx_system ((security_context_t) "u:r:system_server:s0") 27 | #if PLATFORM_SDK_VERSION >= 23 28 | #define ctx_app ((security_context_t) "u:r:untrusted_app:s0:c512,c768") 29 | #else 30 | #define ctx_app ((security_context_t) "u:r:untrusted_app:s0") 31 | #endif // PLATFORM_SDK_VERSION >= 23 32 | #endif // XPOSED_WITH_SELINUX 33 | 34 | namespace xposed { 35 | 36 | bool handleOptions(int argc, char* const argv[]); 37 | bool initialize(bool zygote, bool startSystemServer, const char* className, int argc, char* const argv[]); 38 | void printRomInfo(); 39 | void parseXposedProp(); 40 | int getSdkVersion(); 41 | bool isDisabled(); 42 | void disableXposed(); 43 | bool isSafemodeDisabled(); 44 | bool shouldSkipSafemodeDelay(); 45 | bool shouldIgnoreCommand(int argc, const char* const argv[]); 46 | bool addJarToClasspath(); 47 | void onVmCreated(JNIEnv* env); 48 | void setProcessName(const char* name); 49 | bool determineXposedInstallerUidGid(); 50 | bool switchToXposedInstallerUidGid(); 51 | void dropCapabilities(int8_t keep[] = NULL); 52 | bool isMinimalFramework(); 53 | 54 | } // namespace xposed 55 | 56 | #endif // XPOSED_H_ 57 | -------------------------------------------------------------------------------- /xposed_logcat.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * This file includes the Xposed service, which is especially used to work around SELinux restrictions. 3 | */ 4 | 5 | #define LOG_TAG "Xposed" 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "xposed.h" 15 | #include "xposed_service.h" 16 | #include "xposed_logcat.h" 17 | 18 | 19 | namespace xposed { 20 | namespace logcat { 21 | 22 | //////////////////////////////////////////////////////////// 23 | // Declarations 24 | //////////////////////////////////////////////////////////// 25 | 26 | #define AID_LOG 1007 27 | #define CAP_SYSLOG 34 28 | char marker[50]; 29 | 30 | 31 | //////////////////////////////////////////////////////////// 32 | // Functions 33 | //////////////////////////////////////////////////////////// 34 | 35 | static void execLogcat() { 36 | int8_t keep[] = { CAP_SYSLOG, -1 }; 37 | xposed::dropCapabilities(keep); 38 | 39 | // Execute a logcat command that will keep running in the background 40 | if (zygote_access(XPOSEDLOG_CONF_ALL, F_OK) == 0) { 41 | execl("/system/bin/logcat", "logcat", 42 | "-v", "time", // include timestamps in the log 43 | (char*) 0); 44 | } else { 45 | execl("/system/bin/logcat", "logcat", 46 | "-v", "time", // include timestamps in the log 47 | "-s", // be silent by default, except for the following tags 48 | "XposedStartupMarker:D", // marks the beginning of the current log 49 | "Xposed:I", // Xposed framework and default logging 50 | "appproc:I", // app_process 51 | "XposedInstaller:I", // Xposed Installer 52 | "art:F", // ART crashes 53 | (char*) 0); 54 | } 55 | 56 | // We only get here in case of errors 57 | ALOGE("Could not execute logcat: %s", strerror(errno)); 58 | exit(EXIT_FAILURE); 59 | } 60 | 61 | #ifndef dprintf 62 | static inline int dprintf(int fd, const char *format, ...) { 63 | char* message; 64 | va_list args; 65 | va_start(args, format); 66 | int size = vasprintf(&message, format, args); 67 | if (size > 0) { 68 | write(fd, message, size); 69 | free(message); 70 | } 71 | va_end(args); 72 | return size; 73 | } 74 | #endif 75 | 76 | static void runDaemon(int pipefd) { 77 | xposed::setProcessName("xposed_logcat"); 78 | xposed::dropCapabilities(); 79 | 80 | umask(0); 81 | int logfile = open(XPOSEDLOG, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); 82 | if (logfile < 0) { 83 | ALOGE("Could not open %s: %s", XPOSEDLOG, strerror(errno)); 84 | exit(EXIT_FAILURE); 85 | } 86 | 87 | FILE* pipe = fdopen(pipefd, "r"); 88 | if (pipe == NULL) { 89 | ALOGE("fdopen failed for pipe file descriptor %d: %s", pipefd, strerror(errno)); 90 | exit(EXIT_FAILURE); 91 | } 92 | 93 | char buf[512]; 94 | bool foundMarker = false; 95 | long totalSize = 0; 96 | while (fgets(buf, sizeof(buf), pipe) != NULL) { 97 | if (buf[0] == '-') 98 | continue; // beginning of 99 | 100 | if (!foundMarker) { 101 | if (strstr(buf, "XposedStartupMarker") != NULL && strstr(buf, marker) != NULL) { 102 | foundMarker = true; 103 | } 104 | continue; 105 | } 106 | 107 | int len = strlen(buf); 108 | write(logfile, buf, len); 109 | 110 | totalSize += len; 111 | if (totalSize > XPOSEDLOG_MAX_SIZE) { 112 | dprintf(logfile, "\nReached maximum log size (%'d kB), further lines won't be logged.\n", XPOSEDLOG_MAX_SIZE / 1024); 113 | exit(EXIT_FAILURE); 114 | } 115 | } 116 | 117 | ALOGE("Broken pipe to logcat: %s", strerror(ferror(pipe))); 118 | close(logfile); 119 | exit(EXIT_FAILURE); 120 | } 121 | 122 | void printStartupMarker() { 123 | sprintf(marker, "Current time: %d, PID: %d", (int) time(NULL), getpid()); 124 | ALOG(LOG_DEBUG, "XposedStartupMarker", marker, NULL); 125 | } 126 | 127 | void start() { 128 | // Fork to create a daemon 129 | pid_t pid; 130 | if ((pid = fork()) < 0) { 131 | ALOGE("Fork for Xposed logcat daemon failed: %s", strerror(errno)); 132 | return; 133 | } else if (pid != 0) { 134 | return; 135 | } 136 | 137 | // Ensure that we're allowed to read all log entries 138 | if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) < 0) { 139 | ALOGE("Failed to keep capabilities: %s", strerror(errno)); 140 | } 141 | const gid_t groups[] = { AID_LOG }; 142 | setgroups(1, groups); 143 | if (!xposed::switchToXposedInstallerUidGid()) { 144 | exit(EXIT_FAILURE); 145 | } 146 | 147 | #if XPOSED_WITH_SELINUX 148 | if (xposed->isSELinuxEnabled) { 149 | if (setcon(ctx_app) != 0) { 150 | ALOGE("Could not switch to %s context", ctx_app); 151 | exit(EXIT_FAILURE); 152 | } 153 | } 154 | #endif // XPOSED_WITH_SELINUX 155 | 156 | int err = rename(XPOSEDLOG, XPOSEDLOG_OLD); 157 | if (err < 0 && errno != ENOENT) { 158 | ALOGE("%s while renaming log file %s -> %s", strerror(errno), XPOSEDLOG, XPOSEDLOG_OLD); 159 | exit(EXIT_FAILURE); 160 | } 161 | 162 | int pipeFds[2]; 163 | if (pipe(pipeFds) < 0) { 164 | ALOGE("Could not allocate pipe for logcat output: %s", strerror(errno)); 165 | exit(EXIT_FAILURE); 166 | } 167 | fcntl(pipeFds[0], F_SETPIPE_SZ, 1048576); 168 | 169 | if ((pid = fork()) < 0) { 170 | ALOGE("Fork for logcat execution failed: %s", strerror(errno)); 171 | exit(EXIT_FAILURE); 172 | } else if (pid == 0) { 173 | close(pipeFds[0]); 174 | if (dup2(pipeFds[1], STDOUT_FILENO) == -1) { 175 | ALOGE("Could not redirect stdout: %s", strerror(errno)); 176 | exit(EXIT_FAILURE); 177 | } 178 | if (dup2(pipeFds[1], STDERR_FILENO) == -1) { 179 | ALOGE("Could not redirect stdout: %s", strerror(errno)); 180 | exit(EXIT_FAILURE); 181 | } 182 | execLogcat(); 183 | } else { 184 | close(pipeFds[1]); 185 | runDaemon(pipeFds[0]); 186 | } 187 | 188 | // Should never reach this point 189 | exit(EXIT_FAILURE); 190 | } 191 | 192 | } // namespace logcat 193 | } // namespace xposed 194 | -------------------------------------------------------------------------------- /xposed_logcat.h: -------------------------------------------------------------------------------- 1 | #ifndef XPOSED_LOGCAT_H_ 2 | #define XPOSED_LOGCAT_H_ 3 | 4 | #define XPOSEDLOG XPOSED_DIR "log/error.log" 5 | #define XPOSEDLOG_OLD XPOSEDLOG ".old" 6 | #define XPOSEDLOG_CONF_ALL XPOSED_DIR "conf/log_all" 7 | #define XPOSEDLOG_MAX_SIZE 5*1024*1024 8 | 9 | namespace xposed { 10 | namespace logcat { 11 | 12 | void printStartupMarker(); 13 | void start(); 14 | 15 | } // namespace logcat 16 | } // namespace xposed 17 | 18 | #endif /* XPOSED_LOGCAT_H_ */ 19 | -------------------------------------------------------------------------------- /xposed_offsets.h: -------------------------------------------------------------------------------- 1 | /* 2 | Certain compile time parameters result in different offsets 3 | for members in structures. This file defines the offsets for 4 | members which cannot be accessed otherwise and some macros 5 | to simplify accessing them. 6 | */ 7 | 8 | #define MEMBER_OFFSET_ARRAY(type,member) offsets_array_ ## type ## _ ## member 9 | #define MEMBER_OFFSET_VAR(type,member) offset_ ## type ## _ ## member 10 | #define MEMBER_TYPE(type,member) offset_type_ ## type ## _ ## member 11 | 12 | #define MEMBER_PTR(obj,type,member) \ 13 | ( (MEMBER_TYPE(type,member)*) ( (char*)(obj) + MEMBER_OFFSET_VAR(type,member) ) ) 14 | #define MEMBER_VAL(obj,type,member) *MEMBER_PTR(obj,type,member) 15 | 16 | #define MEMBER_OFFSET_DEFINE(type,member,offsets...) \ 17 | static int MEMBER_OFFSET_ARRAY(type,member)[] = { offsets }; \ 18 | static int MEMBER_OFFSET_VAR(type,member); 19 | #define MEMBER_OFFSET_COPY(type,member) MEMBER_OFFSET_VAR(type,member) = MEMBER_OFFSET_ARRAY(type,member)[offsetMode] 20 | 21 | 22 | // here are the definitions of the modes and offsets 23 | enum xposedOffsetModes { 24 | MEMBER_OFFSET_MODE_WITH_JIT, 25 | MEMBER_OFFSET_MODE_NO_JIT, 26 | }; 27 | static xposedOffsetModes offsetMode; 28 | const char* xposedOffsetModesDesc[] = { 29 | "WITH_JIT", 30 | "NO_JIT", 31 | }; 32 | 33 | MEMBER_OFFSET_DEFINE(DvmJitGlobals, codeCacheFull, 120, 0) 34 | #define offset_type_DvmJitGlobals_codeCacheFull bool 35 | 36 | 37 | 38 | // helper to determine the required values (compile with XPOSED_SHOW_OFFSET=true) 39 | #ifdef XPOSED_SHOW_OFFSETS 40 | template struct RESULT; 41 | #ifdef WITH_JIT 42 | #pragma message "WITH_JIT is defined" 43 | #else 44 | #pragma message "WITH_JIT is not defined" 45 | #endif 46 | RESULT SIZEOF_Method; 47 | RESULT SIZEOF_Thread; 48 | RESULT OFFSETOF_DvmJitGlobals_codeCacheFull; 49 | #endif 50 | 51 | 52 | -------------------------------------------------------------------------------- /xposed_safemode.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Detects input combinations for recovering from bootloops. 3 | * 4 | * The safemode trigger is detected if exactly one of the physical keys is pressed in 5 | * the first 2 seconds after detection startup (or already held down), and a total of 6 | * 5 consecutive presses of that same key are performed in the subsequent 5 seconds. 7 | * 8 | * 2 short vibrations are performed when the first key is pressed; an additional 9 | * vibration is performed for each subsequent press of the same key, and a final 10 | * long vibration is performed if the trigger was successful. 11 | * 12 | * The initial 2-second delay can be disabled through configuration; in that case, 13 | * one of the keys must already be pressed when the detection starts, otherwise 14 | * the detection fails and no delays are introduced. 15 | * 16 | * References: 17 | * /frameworks/base/services/input/EventHub.cpp (AOSP) 18 | * /include/uapi/linux/input.h (Linux) 19 | * Using the Input Subsystem, Linux Journal 20 | */ 21 | #include "xposed_safemode.h" 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #define INITIAL_DELAY 2 33 | #define DETECTION_TIMEOUT 5 34 | 35 | #define DETECTION_PRESSES 5 36 | 37 | #define VIBRATOR_CONTROL "/sys/class/timed_output/vibrator/enable" 38 | #define VIBRATION_SHORT 150 39 | #define VIBRATION_LONG 500 40 | #define VIBRATION_INTERVAL 200 41 | 42 | static const char *DEVICE_PATH = "/dev/input"; 43 | #define MAX_DEVICES 4 44 | 45 | #define test_bit(bit, array) (array[bit/8] & (1<<(bit%8))) 46 | 47 | static const int physical_keycodes[] = { KEY_VOLUMEDOWN, KEY_VOLUMEUP, KEY_POWER, 48 | KEY_HOME, KEY_BACK, KEY_MENU, KEY_CAMERA }; 49 | 50 | 51 | 52 | static void vibrate(int count, int duration_ms, int interval_ms) { 53 | int fd; 54 | int len; 55 | char value[30]; 56 | 57 | if ((fd = open(VIBRATOR_CONTROL, O_RDWR)) < 0) 58 | // Failed to open the control file, ignore it 59 | return; 60 | 61 | len = sprintf(value, "%d\n", duration_ms); 62 | for (int i = 0; i < count; i++) { 63 | if (i != 0) 64 | // Pause between the several vibrations 65 | usleep((duration_ms + interval_ms) * 1000); 66 | // Vibrate (asynchronously) 67 | write(fd, value, len); 68 | } 69 | close(fd); 70 | } 71 | 72 | 73 | /* 74 | * Enumerates the existing input devices and opens handles for the ones that 75 | * report the relevant keys. 76 | * 77 | * Arguments: 78 | * - *fds: is filled on output with the file handles for the opened devices 79 | * - max_fds: maximum available entries in the fds array 80 | * - *pressedKey: is filled on output with 81 | * 0 if no key was found being held down at this instant 82 | * -1 if more than one key was found being held down 83 | * id of the pressed key, if only a single one was being held down 84 | * Returns: 85 | * - the number of opened device handles, filled in the *fds output parameter 86 | * - 0 if no devices were opened 87 | */ 88 | static int openKeyDevices(int *fds, int max_fds, int *pressedKey) { 89 | char devname[PATH_MAX]; 90 | char *filename; 91 | DIR *dir; 92 | struct dirent *de; 93 | 94 | int count = 0; 95 | // No key was detected as pressed, for the moment 96 | *pressedKey = 0; 97 | 98 | dir = opendir(DEVICE_PATH); 99 | if(dir == NULL) 100 | return 0; 101 | 102 | strcpy(devname, DEVICE_PATH); 103 | filename = devname + strlen(devname); 104 | *filename++ = '/'; 105 | while (count < max_fds && (de = readdir(dir))) { 106 | // Skip '.' and '..' 107 | if(de->d_name[0] == '.' && 108 | (de->d_name[1] == '\0' || 109 | (de->d_name[1] == '.' && de->d_name[2] == '\0'))) 110 | continue; 111 | 112 | strcpy(filename, de->d_name); 113 | int fd = open(devname, O_RDWR | O_CLOEXEC); 114 | if(fd < 0) 115 | // Skip files that could not be opened 116 | continue; 117 | 118 | // Check if this device reports one of the relevant keys 119 | uint8_t keyBitmask[(KEY_MAX + 1) / 8]; 120 | memset(keyBitmask, 0, sizeof(keyBitmask)); 121 | ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(keyBitmask)), keyBitmask); 122 | bool reportsKeys = false; 123 | for (size_t i = 0; i < sizeof(physical_keycodes) / sizeof(physical_keycodes[0]); i++) { 124 | if (test_bit(physical_keycodes[i], keyBitmask)) { 125 | reportsKeys = true; 126 | break; 127 | } 128 | } 129 | if (!reportsKeys) { 130 | // This device doesn't report any of the relevant keys 131 | // Skip to the next one 132 | close(fd); 133 | continue; 134 | } 135 | 136 | fds[count++] = fd; 137 | 138 | // Check if one of the keys is currently pressed on this device, to report it to the caller 139 | memset(keyBitmask, 0, sizeof(keyBitmask)); 140 | ioctl(fd, EVIOCGKEY(sizeof(keyBitmask)), keyBitmask); 141 | for (size_t i = 0; i < sizeof(physical_keycodes) / sizeof(physical_keycodes[0]); i++) { 142 | if (test_bit(physical_keycodes[i], keyBitmask)) { 143 | // One of the relevant keys was detected as held down 144 | // We'll report it to be pressed, but only if there isn't more than one key being pressed 145 | if (*pressedKey == 0) { 146 | // No key was being pressed, this one will be reported 147 | *pressedKey = physical_keycodes[i]; 148 | } else { 149 | // Another key was already found to be pressed, report multiple keys to the caller 150 | *pressedKey = -1; 151 | break; 152 | } 153 | } 154 | } 155 | } 156 | 157 | closedir(dir); 158 | return count; 159 | } 160 | 161 | 162 | /* 163 | * Computes the remaining time, in ms, from the current time to the supplied expiration moment 164 | */ 165 | int getRemainingTime(struct timespec expiration) { 166 | struct timespec now; 167 | clock_gettime(CLOCK_MONOTONIC, &now); 168 | if (now.tv_sec > expiration.tv_sec) 169 | return 0; 170 | else 171 | return (expiration.tv_sec - now.tv_sec) * 1000 + (expiration.tv_nsec - now.tv_nsec) / 1000000; 172 | } 173 | 174 | 175 | 176 | namespace xposed { 177 | 178 | bool detectSafemodeTrigger(bool skipInitialDelay) { 179 | 180 | int efd = -1; 181 | int fds[MAX_DEVICES]; 182 | int deviceCount = 0; 183 | int pressedKey = 0; 184 | int triggerPresses = 0; 185 | bool result = false; 186 | 187 | // Open input devices that report one of the relevant physical keys 188 | deviceCount = openKeyDevices(fds, sizeof(fds) / sizeof(fds[0]), &pressedKey); 189 | if (deviceCount == 0) 190 | // No input devices found, abort detection 191 | goto leave; 192 | 193 | if (pressedKey < 0) 194 | // More than one key was initially pressed 195 | // Immediately report a negative detection, with no further delays 196 | goto leave; 197 | 198 | if (pressedKey == 0 && skipInitialDelay) 199 | // A single key wasn't held down and the initial delay is disabled 200 | // Immediately report a negative detection, with no further delays 201 | goto leave; 202 | 203 | // Prepare waiting mechanism for received events in all devices 204 | if ((efd = epoll_create(deviceCount)) < 0) 205 | // Failed to create the epoll handle, abort 206 | goto leave; 207 | 208 | // Register each device descriptor in the epoll handle 209 | struct epoll_event eventPollItems[MAX_DEVICES]; 210 | for (int i = 0; i < deviceCount; i++) { 211 | memset(&eventPollItems[i], 0, sizeof(eventPollItems[i])); 212 | eventPollItems[i].events = EPOLLIN; 213 | eventPollItems[i].data.fd = fds[i]; 214 | if (epoll_ctl(efd, EPOLL_CTL_ADD, fds[i], &eventPollItems[i])) 215 | // Failed to add device descriptor to the epoll handle, abort 216 | goto leave; 217 | } 218 | 219 | int timeout_ms; 220 | struct timespec expiration; 221 | clock_gettime(CLOCK_MONOTONIC, &expiration); 222 | expiration.tv_sec += INITIAL_DELAY; 223 | 224 | // Wait up to INITIAL_DELAY seconds for an initial keypress, it no key was initially down 225 | while (pressedKey == 0 && (timeout_ms = getRemainingTime(expiration)) > 0) { 226 | // Wait for next input event in one of the opened devices 227 | int pollResult = epoll_wait(efd, eventPollItems, sizeof(eventPollItems) / sizeof(eventPollItems[0]), timeout_ms); 228 | if (pollResult < 0) 229 | // Failed to wait for event, abort 230 | goto leave; 231 | 232 | // Loop through the opened devices where a new event is available 233 | for (int i = 0; i < pollResult; i++) { 234 | struct input_event evt; 235 | int32_t readSize = read(eventPollItems[i].data.fd, &evt, sizeof(evt)); 236 | if (readSize != sizeof(evt)) 237 | // Invalid size read, ignore 238 | continue; 239 | 240 | if (evt.type != EV_KEY) 241 | // Only consider key events 242 | continue; 243 | if (evt.value != 1) 244 | // Ignore key releases, we're monitoring presses 245 | continue; 246 | 247 | for (size_t j = 0; j < sizeof(physical_keycodes) / sizeof(physical_keycodes[0]); j++) { 248 | if (evt.code == physical_keycodes[j]) { 249 | // One of the keys was pressed, end the initial detection 250 | // No need to check for duplicate keys, as the events are reported sequentially 251 | // and multiple presses can't be reported at once 252 | pressedKey = evt.code; 253 | break; 254 | } 255 | } 256 | } 257 | } 258 | if (pressedKey == 0) 259 | // No key was pressed during the initial delay or upfront, so the detection has failed 260 | goto leave; 261 | 262 | // Notify the user that the safemode sequence has been started and we're waiting for 263 | // the remaining key presses 264 | vibrate(2, VIBRATION_SHORT, VIBRATION_INTERVAL); 265 | 266 | 267 | // Detection will wait at most DETECTION_TIMEOUT seconds 268 | clock_gettime(CLOCK_MONOTONIC, &expiration); 269 | expiration.tv_sec += DETECTION_TIMEOUT; 270 | 271 | // Initial key press is counted as well 272 | triggerPresses++; 273 | 274 | // Loop waiting for the same key to be pressed the appropriate number of times, a different key to 275 | // be pressed, or the timeout to be reached 276 | while (triggerPresses < DETECTION_PRESSES && (timeout_ms = getRemainingTime(expiration)) > 0) { 277 | // Wait for next input event 278 | int pollResult = epoll_wait(efd, eventPollItems, sizeof(eventPollItems) / sizeof(eventPollItems[0]), timeout_ms); 279 | if (pollResult < 0) 280 | // Failed to wait for event, abort 281 | goto leave; 282 | 283 | // Loop through the opened devices where a new event is available 284 | for (int i = 0; i < pollResult; i++) { 285 | struct input_event evt; 286 | int32_t readSize = read(eventPollItems[i].data.fd, &evt, sizeof(evt)); 287 | if (readSize != sizeof(evt)) 288 | // Invalid size read, ignore 289 | continue; 290 | 291 | if (evt.type != EV_KEY) 292 | // Only consider key events 293 | continue; 294 | if (evt.value != 1) 295 | // Ignore key releases, we're monitoring presses 296 | continue; 297 | 298 | for (size_t j = 0; j < sizeof(physical_keycodes) / sizeof(physical_keycodes[0]); j++) { 299 | if (evt.code == physical_keycodes[j]) { 300 | if (pressedKey == evt.code) { 301 | // The same key was pressed again, increment the counter and notify the user 302 | triggerPresses++; 303 | // The final key press will be confirmed with a long vibration later 304 | if (triggerPresses < DETECTION_PRESSES) 305 | vibrate(1, VIBRATION_SHORT, 0); 306 | } else { 307 | // A key was pressed other than the initial one 308 | // Abort the detection and avoid further delays 309 | goto leave; 310 | } 311 | break; 312 | } 313 | } 314 | } 315 | } 316 | 317 | // Was safemode successfully triggered? 318 | if (triggerPresses >= DETECTION_PRESSES) { 319 | vibrate(1, VIBRATION_LONG, 0); 320 | result = true; 321 | } 322 | 323 | leave: 324 | if (efd >= 0) 325 | close(efd); 326 | for (int i = 0; i < deviceCount; i++) 327 | close(fds[i]); 328 | 329 | return result; 330 | 331 | } 332 | 333 | } 334 | 335 | -------------------------------------------------------------------------------- /xposed_safemode.h: -------------------------------------------------------------------------------- 1 | #ifndef XPOSED_SAFEMODE_H_ 2 | #define XPOSED_SAFEMODE_H_ 3 | 4 | namespace xposed { 5 | 6 | bool detectSafemodeTrigger(bool skipInitialDelay); 7 | 8 | } 9 | 10 | #endif // XPOSED_SAFEMODE_H_ 11 | 12 | -------------------------------------------------------------------------------- /xposed_service.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * This file includes the Xposed services, which are especially used to work around SELinux restrictions. 3 | */ 4 | 5 | #define LOG_TAG "Xposed" 6 | 7 | #include "xposed.h" 8 | #include "xposed_service.h" 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #define __STDC_FORMAT_MACROS 19 | #include 20 | #include 21 | 22 | #define UID_SYSTEM 1000 23 | 24 | using namespace android; 25 | 26 | namespace xposed { 27 | namespace service { 28 | 29 | //////////////////////////////////////////////////////////// 30 | // Declarations 31 | //////////////////////////////////////////////////////////// 32 | 33 | bool running = false; 34 | 35 | 36 | //////////////////////////////////////////////////////////// 37 | // Memory-based communication (used by Zygote) 38 | //////////////////////////////////////////////////////////// 39 | 40 | namespace membased { 41 | 42 | enum State { 43 | STATE_NOT_RUNNING, 44 | STATE_IDLE, 45 | STATE_SERVICE_ACTION, 46 | STATE_SERVER_RESPONSE, 47 | }; 48 | 49 | enum Action { 50 | OP_NONE, 51 | OP_ACCESS_FILE, 52 | OP_STAT_FILE, 53 | OP_READ_FILE, 54 | }; 55 | 56 | struct AccessFileData { 57 | // in 58 | char path[PATH_MAX]; 59 | int mode; 60 | // out 61 | int result; 62 | }; 63 | 64 | struct StatFileData { 65 | // in 66 | char path[PATH_MAX]; 67 | // inout 68 | struct stat st; 69 | // out 70 | int result; 71 | }; 72 | 73 | struct ReadFileData { 74 | // in 75 | char path[PATH_MAX]; 76 | int offset; 77 | // out 78 | int totalSize; 79 | int bytesRead; 80 | bool eof; 81 | char content[32*1024]; 82 | }; 83 | 84 | struct MemBasedState { 85 | pthread_mutex_t workerMutex; 86 | pthread_cond_t workerCond; 87 | State state; 88 | Action action; 89 | int error; 90 | union { 91 | AccessFileData accessFile; 92 | StatFileData statFile; 93 | ReadFileData readFile; 94 | } data; 95 | }; 96 | 97 | MemBasedState* shared = NULL; 98 | pid_t zygotePid = 0; 99 | bool canAlwaysAccessService = false; 100 | 101 | inline static void initSharedMutex(pthread_mutex_t* mutex) { 102 | pthread_mutexattr_t attr; 103 | pthread_mutexattr_init(&attr); 104 | pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED); 105 | pthread_mutex_init(mutex, &attr); 106 | pthread_mutexattr_destroy(&attr); 107 | } 108 | 109 | inline static void initSharedCond(pthread_cond_t* cond) { 110 | pthread_condattr_t cattr; 111 | pthread_condattr_init(&cattr); 112 | pthread_condattr_setpshared(&cattr, PTHREAD_PROCESS_SHARED); 113 | pthread_cond_init(cond, &cattr); 114 | pthread_condattr_destroy(&cattr); 115 | } 116 | 117 | static bool init() { 118 | shared = (MemBasedState*) mmap(NULL, sizeof(MemBasedState), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); 119 | if (shared == MAP_FAILED) { 120 | ALOGE("Could not allocate memory for Zygote service: %s", strerror(errno)); 121 | shared = NULL; 122 | return false; 123 | } 124 | 125 | zygotePid = getpid(); 126 | canAlwaysAccessService = true; 127 | 128 | initSharedMutex(&shared->workerMutex); 129 | initSharedCond(&shared->workerCond); 130 | shared->state = STATE_NOT_RUNNING; 131 | shared->action = OP_NONE; 132 | shared->error = 0; 133 | return true; 134 | } 135 | 136 | void restrictMemoryInheritance() { 137 | madvise(shared, sizeof(MemBasedState), MADV_DONTFORK); 138 | canAlwaysAccessService = false; 139 | } 140 | 141 | static inline bool isServiceAccessible() { 142 | if (!canAlwaysAccessService && (shared == NULL || zygotePid != getpid())) { 143 | ALOGE("Zygote service is not accessible from PID %d, UID %d", getpid(), getuid()); 144 | shared = NULL; 145 | errno = EPERM; 146 | return false; 147 | } 148 | return true; 149 | } 150 | 151 | // Server implementation 152 | void* looper(void* unused __attribute__((unused))) { 153 | pthread_mutex_lock(&shared->workerMutex); 154 | shared->state = STATE_IDLE; 155 | pthread_cond_broadcast(&shared->workerCond); 156 | while (1) { 157 | while (shared->state != STATE_SERVICE_ACTION) { 158 | pthread_cond_wait(&shared->workerCond, &shared->workerMutex); 159 | } 160 | 161 | switch (shared->action) { 162 | case OP_ACCESS_FILE: { 163 | struct AccessFileData* data = &shared->data.accessFile; 164 | data->result = TEMP_FAILURE_RETRY(access(data->path, data->mode)); 165 | if (data->result != 0) { 166 | shared->error = errno; 167 | } 168 | } break; 169 | 170 | case OP_STAT_FILE: { 171 | struct StatFileData* data = &shared->data.statFile; 172 | data->result = TEMP_FAILURE_RETRY(stat(data->path, &data->st)); 173 | if (data->result != 0) { 174 | shared->error = errno; 175 | } 176 | } break; 177 | 178 | case OP_READ_FILE: { 179 | struct ReadFileData* data = &shared->data.readFile; 180 | struct stat st; 181 | 182 | if (stat(data->path, &st) != 0) { 183 | shared->error = errno; 184 | break; 185 | } 186 | 187 | data->totalSize = st.st_size; 188 | 189 | FILE *f = fopen(data->path, "r"); 190 | if (f == NULL) { 191 | shared->error = errno; 192 | break; 193 | } 194 | 195 | if (data->offset > 0 && fseek(f, data->offset, SEEK_SET) != 0) { 196 | shared->error = ferror(f); 197 | fclose(f); 198 | break; 199 | } 200 | 201 | data->bytesRead = fread(data->content, 1, sizeof(data->content), f); 202 | shared->error = ferror(f); 203 | data->eof = feof(f); 204 | 205 | fclose(f); 206 | } break; 207 | 208 | case OP_NONE: { 209 | ALOGE("No-op call to membased service"); 210 | break; 211 | } 212 | 213 | default: { 214 | ALOGE("Invalid action in call to membased service"); 215 | break; 216 | } 217 | } 218 | 219 | shared->state = STATE_SERVER_RESPONSE; 220 | pthread_cond_broadcast(&shared->workerCond); 221 | } 222 | 223 | pthread_mutex_unlock(&shared->workerMutex); 224 | return NULL; 225 | } 226 | 227 | // Client implementation 228 | static inline bool waitForRunning(int timeout) { 229 | if (shared == NULL || timeout < 0) 230 | return false; 231 | 232 | struct timespec ts; 233 | clock_gettime(CLOCK_REALTIME, &ts); 234 | ts.tv_sec += 5; 235 | int rc = 0; 236 | pthread_mutex_lock(&shared->workerMutex); 237 | while (shared->state == STATE_NOT_RUNNING && rc == 0) { 238 | rc = pthread_cond_timedwait(&shared->workerCond, &shared->workerMutex, &ts); 239 | } 240 | pthread_mutex_unlock(&shared->workerMutex); 241 | return rc == 0; 242 | } 243 | 244 | static inline void waitForIdle() { 245 | pthread_mutex_lock(&shared->workerMutex); 246 | while (shared->state != STATE_IDLE) { 247 | pthread_cond_wait(&shared->workerCond, &shared->workerMutex); 248 | } 249 | } 250 | 251 | static inline void callService(Action action) { 252 | shared->action = action; 253 | shared->state = STATE_SERVICE_ACTION; 254 | shared->error = 0; 255 | pthread_cond_broadcast(&shared->workerCond); 256 | 257 | while (shared->state != STATE_SERVER_RESPONSE) { 258 | pthread_cond_wait(&shared->workerCond, &shared->workerMutex); 259 | } 260 | } 261 | 262 | static inline void makeIdle() { 263 | shared->action = OP_NONE; 264 | shared->state = STATE_IDLE; 265 | pthread_cond_broadcast(&shared->workerCond); 266 | pthread_mutex_unlock(&shared->workerMutex); 267 | } 268 | 269 | int accessFile(const char* path, int mode) { 270 | if (!isServiceAccessible()) 271 | return -1; 272 | 273 | if (strlen(path) > sizeof(AccessFileData::path) - 1) { 274 | errno = ENAMETOOLONG; 275 | return -1; 276 | } 277 | 278 | waitForIdle(); 279 | 280 | struct AccessFileData* data = &shared->data.accessFile; 281 | strcpy(data->path, path); 282 | data->mode = mode; 283 | 284 | callService(OP_ACCESS_FILE); 285 | 286 | makeIdle(); 287 | errno = shared->error; 288 | return shared->error ? -1 : data->result; 289 | } 290 | 291 | int statFile(const char* path, struct stat* st) { 292 | if (!isServiceAccessible()) 293 | return -1; 294 | 295 | if (strlen(path) > sizeof(StatFileData::path) - 1) { 296 | errno = ENAMETOOLONG; 297 | return -1; 298 | } 299 | 300 | waitForIdle(); 301 | 302 | struct StatFileData* data = &shared->data.statFile; 303 | strcpy(data->path, path); 304 | 305 | callService(OP_STAT_FILE); 306 | 307 | memcpy(st, &data->st, sizeof(struct stat)); 308 | 309 | makeIdle(); 310 | errno = shared->error; 311 | return shared->error ? -1 : data->result; 312 | } 313 | 314 | char* readFile(const char* path, int* bytesRead) { 315 | if (!isServiceAccessible()) 316 | return NULL; 317 | 318 | char* result = NULL; 319 | int offset = 0, totalSize = 0; 320 | 321 | if (bytesRead) 322 | *bytesRead = 0; 323 | 324 | if (strlen(path) > sizeof(ReadFileData::path) - 1) { 325 | errno = ENAMETOOLONG; 326 | return NULL; 327 | } 328 | 329 | waitForIdle(); 330 | 331 | struct ReadFileData* data = &shared->data.readFile; 332 | strcpy(data->path, path); 333 | data->offset = 0; 334 | 335 | callService(OP_READ_FILE); 336 | if (shared->error) 337 | goto bail; 338 | 339 | totalSize = data->totalSize; 340 | result = (char*) malloc(totalSize + 1); 341 | result[totalSize] = 0; 342 | memcpy(result, data->content, data->bytesRead); 343 | 344 | while (!data->eof) { 345 | offset += data->bytesRead; 346 | data->offset = offset; 347 | 348 | callService(OP_READ_FILE); 349 | if (shared->error) 350 | goto bail; 351 | 352 | if (offset + data->bytesRead > totalSize) { 353 | shared->error = EBUSY; 354 | goto bail; 355 | } 356 | 357 | memcpy(result + offset, data->content, data->bytesRead); 358 | } 359 | 360 | if (bytesRead) 361 | *bytesRead = offset + data->bytesRead; 362 | 363 | bail: 364 | makeIdle(); 365 | if (shared->error && result) { 366 | free(result); 367 | result = NULL; 368 | } 369 | errno = shared->error; 370 | return result; 371 | } 372 | 373 | } // namespace membased 374 | 375 | 376 | //////////////////////////////////////////////////////////// 377 | // Binder service 378 | //////////////////////////////////////////////////////////// 379 | 380 | namespace binder { 381 | 382 | #define XPOSED_BINDER_SYSTEM_SERVICE_NAME "user.xposed.system" 383 | #define XPOSED_BINDER_APP_SERVICE_NAME "user.xposed.app" 384 | 385 | class IXposedService: public IInterface { 386 | public: 387 | DECLARE_META_INTERFACE(XposedService); 388 | virtual int test() const = 0; 389 | virtual status_t addService(const String16& name, 390 | const sp& service, 391 | bool allowIsolated = false) const = 0; 392 | virtual int accessFile(const String16& filename, 393 | int32_t mode) const = 0; 394 | virtual int statFile(const String16& filename, 395 | int64_t* size, 396 | int64_t* mtime) const = 0; 397 | virtual status_t readFile(const String16& filename, 398 | int32_t offset, 399 | int32_t length, 400 | int64_t* size, 401 | int64_t* mtime, 402 | uint8_t** buffer, 403 | int32_t* bytesRead, 404 | String16* errormsg) const = 0; 405 | 406 | enum { 407 | TEST_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION, 408 | ADD_SERVICE_TRANSACTION, 409 | ACCESS_FILE_TRANSACTION, 410 | STAT_FILE_TRANSACTION, 411 | READ_FILE_TRANSACTION, 412 | }; 413 | }; 414 | 415 | class BnXposedService: public BnInterface { 416 | public: 417 | virtual status_t onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0); 418 | }; 419 | 420 | class BpXposedService: public BpInterface { 421 | public: 422 | BpXposedService(const sp& impl) : BpInterface(impl) {} 423 | 424 | virtual int test() const { 425 | Parcel data, reply; 426 | data.writeInterfaceToken(IXposedService::getInterfaceDescriptor()); 427 | remote()->transact(TEST_TRANSACTION, data, &reply); 428 | if (reply.readExceptionCode() != 0) return -1; 429 | return reply.readInt32(); 430 | } 431 | 432 | virtual status_t addService(const String16& name, const sp& service, 433 | bool allowIsolated = false) const { 434 | Parcel data, reply; 435 | data.writeInterfaceToken(IXposedService::getInterfaceDescriptor()); 436 | data.writeString16(name); 437 | data.writeStrongBinder(service); 438 | data.writeInt32(allowIsolated ? 1 : 0); 439 | status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply); 440 | return err == NO_ERROR ? reply.readExceptionCode() : err; 441 | } 442 | 443 | virtual status_t accessFile(const String16& name, int32_t mode) const { 444 | Parcel data, reply; 445 | data.writeInterfaceToken(IXposedService::getInterfaceDescriptor()); 446 | data.writeString16(name); 447 | data.writeInt32(mode); 448 | 449 | remote()->transact(ACCESS_FILE_TRANSACTION, data, &reply); 450 | if (reply.readExceptionCode() != 0) return -1; 451 | 452 | errno = reply.readInt32(); 453 | return (errno == 0) ? 0 : -1; 454 | } 455 | 456 | virtual status_t statFile(const String16& name, int64_t* size, int64_t* mtime) const { 457 | Parcel data, reply; 458 | data.writeInterfaceToken(IXposedService::getInterfaceDescriptor()); 459 | data.writeString16(name); 460 | 461 | remote()->transact(STAT_FILE_TRANSACTION, data, &reply); 462 | if (reply.readExceptionCode() != 0) return -1; 463 | 464 | errno = reply.readInt32(); 465 | if (errno != 0) return -1; 466 | 467 | int64_t size1 = reply.readInt64(); 468 | int64_t mtime1 = reply.readInt64(); 469 | if (size != NULL) *size = size1; 470 | if (mtime != NULL) *mtime = mtime1; 471 | return 0; 472 | } 473 | 474 | virtual status_t readFile(const String16& filename, int32_t offset, int32_t length, 475 | int64_t* size, int64_t* mtime, uint8_t** buffer, int32_t* bytesRead, String16* errormsg) const { 476 | Parcel data, reply; 477 | data.writeInterfaceToken(IXposedService::getInterfaceDescriptor()); 478 | data.writeString16(filename); 479 | data.writeInt32(offset); 480 | data.writeInt32(length); 481 | int64_t size1 = 0; 482 | int64_t mtime1 = 0; 483 | if (size != NULL) size1 = *size; 484 | if (mtime != NULL) mtime1 = *mtime; 485 | data.writeInt64(size1); 486 | data.writeInt64(mtime1); 487 | 488 | remote()->transact(READ_FILE_TRANSACTION, data, &reply); 489 | if (reply.readExceptionCode() != 0) return -1; 490 | 491 | status_t err = reply.readInt32(); 492 | const String16& errormsg1(reply.readString16()); 493 | size1 = reply.readInt64(); 494 | mtime1 = reply.readInt64(); 495 | int32_t bytesRead1 = reply.readInt32(); 496 | if (size != NULL) *size = size1; 497 | if (mtime != NULL) *mtime = mtime1; 498 | if (bytesRead != NULL) *bytesRead = bytesRead1; 499 | if (errormsg) *errormsg = errormsg1; 500 | 501 | if (bytesRead1 > 0 && bytesRead1 <= (int32_t)reply.dataAvail()) { 502 | *buffer = (uint8_t*) malloc(bytesRead1 + 1); 503 | *buffer[bytesRead1] = 0; 504 | reply.read(*buffer, bytesRead1); 505 | } else { 506 | *buffer = NULL; 507 | } 508 | 509 | errno = err; 510 | return (errno == 0) ? 0 : -1; 511 | } 512 | }; 513 | 514 | IMPLEMENT_META_INTERFACE(XposedService, "de.robv.android.xposed.IXposedService"); 515 | 516 | status_t BnXposedService::onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { 517 | switch (code) { 518 | case TEST_TRANSACTION: { 519 | CHECK_INTERFACE(IXposedService, data, reply); 520 | reply->writeNoException(); 521 | reply->writeInt32(test()); 522 | return NO_ERROR; 523 | } break; 524 | 525 | case ADD_SERVICE_TRANSACTION: { 526 | CHECK_INTERFACE(IXposedService, data, reply); 527 | String16 which = data.readString16(); 528 | sp b = data.readStrongBinder(); 529 | bool allowIsolated = (data.readInt32() != 0); 530 | reply->writeInt32(addService(which, b, allowIsolated)); 531 | return NO_ERROR; 532 | } break; 533 | 534 | case ACCESS_FILE_TRANSACTION: { 535 | CHECK_INTERFACE(IXposedService, data, reply); 536 | String16 filename = data.readString16(); 537 | int32_t mode = data.readInt32(); 538 | status_t result = accessFile(filename, mode); 539 | int err = errno; 540 | reply->writeNoException(); 541 | reply->writeInt32(result == 0 ? 0 : err); 542 | return NO_ERROR; 543 | } break; 544 | 545 | case STAT_FILE_TRANSACTION: { 546 | CHECK_INTERFACE(IXposedService, data, reply); 547 | String16 filename = data.readString16(); 548 | int64_t size, time; 549 | status_t result = statFile(filename, &size, &time); 550 | int err = errno; 551 | reply->writeNoException(); 552 | if (result == 0) { 553 | reply->writeInt32(0); 554 | reply->writeInt64(size); 555 | reply->writeInt64(time); 556 | } else { 557 | reply->writeInt32(err); 558 | } 559 | return NO_ERROR; 560 | } break; 561 | 562 | case READ_FILE_TRANSACTION: { 563 | CHECK_INTERFACE(IXposedService, data, reply); 564 | String16 filename = data.readString16(); 565 | int32_t offset = data.readInt32(); 566 | int32_t length = data.readInt32(); 567 | int64_t size = data.readInt64(); 568 | int64_t mtime = data.readInt64(); 569 | uint8_t* buffer = NULL; 570 | int32_t bytesRead = -1; 571 | String16 errormsg; 572 | 573 | status_t err = readFile(filename, offset, length, &size, &mtime, &buffer, &bytesRead, &errormsg); 574 | 575 | reply->writeNoException(); 576 | reply->writeInt32(err); 577 | reply->writeString16(errormsg); 578 | reply->writeInt64(size); 579 | reply->writeInt64(mtime); 580 | if (bytesRead > 0) { 581 | reply->writeInt32(bytesRead); 582 | reply->write(buffer, bytesRead); 583 | free(buffer); 584 | } else { 585 | reply->writeInt32(bytesRead); // empty array (0) or null (-1) 586 | } 587 | return NO_ERROR; 588 | } break; 589 | 590 | default: 591 | return BBinder::onTransact(code, data, reply, flags); 592 | } 593 | } 594 | 595 | class XposedService : public BnXposedService { 596 | public: 597 | XposedService(bool system); 598 | 599 | virtual int test() const; 600 | virtual status_t addService(const String16& name, 601 | const sp& service, 602 | bool allowIsolated = false) const; 603 | virtual status_t accessFile(const String16& filename16, 604 | int32_t mode) const; 605 | virtual status_t statFile(const String16& filename, 606 | int64_t* size, 607 | int64_t* mtime) const; 608 | virtual status_t readFile(const String16& filename16, 609 | int32_t offset, 610 | int32_t length, 611 | int64_t* size, 612 | int64_t* mtime, 613 | uint8_t** buffer, 614 | int32_t* bytesRead, 615 | String16* errormsg) const; 616 | 617 | private: 618 | bool isSystem; 619 | }; 620 | 621 | static String16 formatToString16(const char* fmt, ...) { 622 | char* message; 623 | va_list args; 624 | va_start(args, fmt); 625 | int size = vasprintf(&message, fmt, args); 626 | String16 result(message, size); 627 | free(message); 628 | va_end(args); 629 | return result; 630 | } 631 | 632 | XposedService::XposedService(bool system) 633 | : isSystem(system) {} 634 | 635 | int XposedService::test() const { 636 | pid_t pid = IPCThreadState::self()->getCallingPid(); 637 | ALOGD("This is PID %d, test method was called from PID %d", getpid(), pid); 638 | return getpid(); 639 | } 640 | 641 | status_t XposedService::addService(const String16& name, const sp& service, 642 | bool allowIsolated) const { 643 | uid_t uid = IPCThreadState::self()->getCallingUid(); 644 | if (!isSystem || (uid != xposed->installer_uid)) { 645 | ALOGE("Permission denied, not adding service %s", String8(name).string()); 646 | errno = EPERM; 647 | return -1; 648 | } 649 | sp sm = defaultServiceManager(); 650 | #if PLATFORM_SDK_VERSION >= 16 651 | return sm->addService(name, service, allowIsolated); 652 | #else 653 | return sm->addService(name, service); 654 | #endif 655 | } 656 | 657 | status_t XposedService::accessFile(const String16& filename16, int32_t mode) const { 658 | uid_t caller = IPCThreadState::self()->getCallingUid(); 659 | if (caller != UID_SYSTEM) { 660 | ALOGE("UID %d is not allowed to use the Xposed service", caller); 661 | errno = EPERM; 662 | return -1; 663 | } 664 | const char* filename = String8(filename16).string(); 665 | return TEMP_FAILURE_RETRY(access(filename, mode)); 666 | } 667 | 668 | status_t XposedService::statFile(const String16& filename16, int64_t* size, int64_t* time) const { 669 | uid_t caller = IPCThreadState::self()->getCallingUid(); 670 | if (caller != UID_SYSTEM) { 671 | ALOGE("UID %d is not allowed to use the Xposed service", caller); 672 | errno = EPERM; 673 | return -1; 674 | } 675 | const char* filename = String8(filename16).string(); 676 | struct stat st; 677 | status_t result = TEMP_FAILURE_RETRY(stat(filename, &st)); 678 | if (result == 0) { 679 | *size = st.st_size; 680 | *time = st.st_mtime; 681 | } 682 | return result; 683 | } 684 | 685 | status_t XposedService::readFile(const String16& filename16, int32_t offset, int32_t length, 686 | int64_t* size, int64_t* mtime, uint8_t** buffer, int32_t* bytesRead, String16* errormsg) const { 687 | 688 | uid_t caller = IPCThreadState::self()->getCallingUid(); 689 | if (caller != UID_SYSTEM) { 690 | ALOGE("UID %d is not allowed to use the Xposed service", caller); 691 | return EPERM; 692 | } 693 | 694 | *buffer = NULL; 695 | *bytesRead = -1; 696 | 697 | // Get file metadata 698 | const char* filename = String8(filename16).string(); 699 | struct stat st; 700 | if (stat(filename, &st) != 0) { 701 | status_t err = errno; 702 | if (errormsg) *errormsg = formatToString16("%s during stat() on %s", strerror(err), filename); 703 | return err; 704 | } 705 | 706 | if (S_ISDIR(st.st_mode)) { 707 | if (errormsg) *errormsg = formatToString16("%s is a directory", filename); 708 | return EISDIR; 709 | } 710 | 711 | // Don't load again if file is unchanged 712 | if (*size == st.st_size && *mtime == (int32_t)st.st_mtime) { 713 | return 0; 714 | } 715 | 716 | *size = st.st_size; 717 | *mtime = st.st_mtime; 718 | 719 | // Check range 720 | if (offset > 0 && offset >= *size) { 721 | if (errormsg) *errormsg = formatToString16("offset %d >= size %" PRId64 " for %s", offset, *size, filename); 722 | return EINVAL; 723 | } else if (offset < 0) { 724 | offset = 0; 725 | } 726 | 727 | if (length > 0 && (offset + length) > *size) { 728 | if (errormsg) *errormsg = formatToString16("offset %d + length %d > size %" PRId64 " for %s", offset, length, *size, filename); 729 | return EINVAL; 730 | } else if (*size == 0) { 731 | *bytesRead = 0; 732 | return 0; 733 | } else if (length <= 0) { 734 | length = *size - offset; 735 | } 736 | 737 | // Allocate buffer 738 | *buffer = (uint8_t*) malloc(length + 1); 739 | if (*buffer == NULL) { 740 | if (errormsg) *errormsg = formatToString16("allocating buffer with %d bytes failed", length + 1); 741 | return ENOMEM; 742 | } 743 | (*buffer)[length] = 0; 744 | 745 | // Open file 746 | FILE *f = fopen(filename, "r"); 747 | if (f == NULL) { 748 | status_t err = errno; 749 | free(*buffer); 750 | *buffer = NULL; 751 | if (errormsg) *errormsg = formatToString16("%s during fopen() on %s", strerror(err), filename); 752 | return err; 753 | } 754 | 755 | // Seek to correct offset 756 | if (offset > 0 && fseek(f, offset, SEEK_SET) != 0) { 757 | free(*buffer); 758 | *buffer = NULL; 759 | status_t err = ferror(f); 760 | fclose(f); 761 | if (errormsg) *errormsg = formatToString16("%s during fseek() to offset %d for %s", strerror(err), offset, filename); 762 | return err; 763 | } 764 | 765 | // Read the file 766 | *bytesRead = fread(*buffer, 1, length, f); 767 | status_t err = ferror(f); 768 | if (err != 0) { 769 | free(*buffer); 770 | *buffer = NULL; 771 | *bytesRead = -1; 772 | if (errormsg) *errormsg = formatToString16("%s during fread(), read %d bytes for %s", strerror(err), *bytesRead, filename); 773 | } 774 | 775 | // Close the file 776 | fclose(f); 777 | 778 | return err; 779 | } 780 | 781 | } // namespace binder 782 | 783 | 784 | //////////////////////////////////////////////////////////// 785 | // General 786 | //////////////////////////////////////////////////////////// 787 | 788 | static void systemService() { 789 | xposed::setProcessName("xposed_service_system"); 790 | xposed::dropCapabilities(); 791 | 792 | #if XPOSED_WITH_SELINUX 793 | if (xposed->isSELinuxEnabled) { 794 | if (setcon(ctx_system) != 0) { 795 | ALOGE("Could not switch to %s context", ctx_system); 796 | exit(EXIT_FAILURE); 797 | } 798 | } 799 | #endif // XPOSED_WITH_SELINUX 800 | 801 | // Initialize the system service 802 | sp sm(defaultServiceManager()); 803 | #if PLATFORM_SDK_VERSION >= 16 804 | status_t err = sm->addService(String16(XPOSED_BINDER_SYSTEM_SERVICE_NAME), new binder::XposedService(true), true); 805 | #else 806 | status_t err = sm->addService(String16(XPOSED_BINDER_SYSTEM_SERVICE_NAME), new binder::XposedService(true)); 807 | #endif 808 | if (err != NO_ERROR) { 809 | ALOGE("Error %d while adding system service %s", err, XPOSED_BINDER_SYSTEM_SERVICE_NAME); 810 | exit(EXIT_FAILURE); 811 | } 812 | 813 | sp ps(ProcessState::self()); 814 | ps->startThreadPool(); 815 | #if PLATFORM_SDK_VERSION >= 18 816 | ps->giveThreadPoolName(); 817 | #endif 818 | IPCThreadState::self()->joinThreadPool(); 819 | } 820 | 821 | static void appService() { 822 | xposed::setProcessName("xposed_service_app"); 823 | if (!xposed::switchToXposedInstallerUidGid()) { 824 | exit(EXIT_FAILURE); 825 | } 826 | xposed::dropCapabilities(); 827 | 828 | #if XPOSED_WITH_SELINUX 829 | if (xposed->isSELinuxEnabled) { 830 | if (setcon(ctx_app) != 0) { 831 | ALOGE("Could not switch to %s context", ctx_app); 832 | exit(EXIT_FAILURE); 833 | } 834 | } 835 | #endif // XPOSED_WITH_SELINUX 836 | 837 | // We have to register the app service by using the already running system service as a proxy 838 | sp sm(defaultServiceManager()); 839 | sp systemBinder = sm->getService(String16(XPOSED_BINDER_SYSTEM_SERVICE_NAME)); 840 | sp xposedSystemService = interface_cast(systemBinder); 841 | status_t err = xposedSystemService->addService(String16(XPOSED_BINDER_APP_SERVICE_NAME), new binder::XposedService(false), true); 842 | 843 | // Check result for the app service registration 844 | if (err != NO_ERROR) { 845 | ALOGE("Error %d while adding app service %s", err, XPOSED_BINDER_APP_SERVICE_NAME); 846 | exit(EXIT_FAILURE); 847 | } 848 | 849 | #if XPOSED_WITH_SELINUX 850 | // Initialize the memory-based Zygote service 851 | if (xposed->isSELinuxEnabled) { 852 | pthread_t thMemBased; 853 | if (pthread_create(&thMemBased, NULL, &membased::looper, NULL) != 0) { 854 | ALOGE("Could not create thread for memory-based service: %s", strerror(errno)); 855 | exit(EXIT_FAILURE); 856 | } 857 | } 858 | #endif // XPOSED_WITH_SELINUX 859 | 860 | sp ps(ProcessState::self()); 861 | ps->startThreadPool(); 862 | #if PLATFORM_SDK_VERSION >= 18 863 | ps->giveThreadPoolName(); 864 | #endif 865 | IPCThreadState::self()->joinThreadPool(); 866 | } 867 | 868 | bool checkMembasedRunning() { 869 | // Ensure that the memory based service is running 870 | if (!membased::waitForRunning(5)) { 871 | ALOGE("Xposed's Zygote service is not running, cannot work without it"); 872 | return false; 873 | } 874 | 875 | return true; 876 | } 877 | 878 | bool startAll() { 879 | if (xposed->isSELinuxEnabled && !membased::init()) { 880 | return false; 881 | } 882 | 883 | // system context service 884 | pid_t pid; 885 | if ((pid = fork()) < 0) { 886 | ALOGE("Fork for Xposed service in system context failed: %s", strerror(errno)); 887 | return false; 888 | } else if (pid == 0) { 889 | systemService(); 890 | // Should never reach this point 891 | exit(EXIT_FAILURE); 892 | } 893 | 894 | // app context service 895 | if ((pid = fork()) < 0) { 896 | ALOGE("Fork for Xposed service in app context failed: %s", strerror(errno)); 897 | return false; 898 | } else if (pid == 0) { 899 | appService(); 900 | // Should never reach this point 901 | exit(EXIT_FAILURE); 902 | } 903 | 904 | if (xposed->isSELinuxEnabled && !checkMembasedRunning()) { 905 | return false; 906 | } 907 | 908 | return true; 909 | } 910 | 911 | #if XPOSED_WITH_SELINUX 912 | bool startMembased() { 913 | if (!xposed->isSELinuxEnabled) { 914 | return true; 915 | } 916 | 917 | if (!membased::init()) { 918 | return false; 919 | } 920 | 921 | pid_t pid; 922 | if ((pid = fork()) < 0) { 923 | ALOGE("Fork for Xposed Zygote service failed: %s", strerror(errno)); 924 | return false; 925 | } else if (pid == 0) { 926 | xposed::setProcessName("xposed_zygote_service"); 927 | if (!xposed::switchToXposedInstallerUidGid()) { 928 | exit(EXIT_FAILURE); 929 | } 930 | xposed::dropCapabilities(); 931 | if (setcon(ctx_app) != 0) { 932 | ALOGE("Could not switch to %s context", ctx_app); 933 | exit(EXIT_FAILURE); 934 | } 935 | membased::looper(NULL); 936 | // Should never reach this point 937 | exit(EXIT_FAILURE); 938 | } 939 | 940 | return checkMembasedRunning(); 941 | } 942 | #endif // XPOSED_WITH_SELINUX 943 | 944 | } // namespace service 945 | } // namespace xposed 946 | -------------------------------------------------------------------------------- /xposed_service.h: -------------------------------------------------------------------------------- 1 | #ifndef XPOSED_SERVICE_H_ 2 | #define XPOSED_SERVICE_H_ 3 | 4 | #include 5 | #include 6 | 7 | namespace xposed { 8 | namespace service { 9 | bool startAll(); 10 | 11 | #if XPOSED_WITH_SELINUX 12 | bool startMembased(); 13 | 14 | namespace membased { 15 | int accessFile(const char* path, int mode); 16 | int statFile(const char* path, struct stat* stat); 17 | char* readFile(const char* path, int* bytesRead); 18 | void restrictMemoryInheritance(); 19 | } // namespace membased 20 | #endif // XPOSED_WITH_SELINUX 21 | 22 | } // namespace service 23 | 24 | static inline int zygote_access(const char *pathname, int mode) { 25 | #if XPOSED_WITH_SELINUX 26 | if (xposed->isSELinuxEnabled) 27 | return xposed::service::membased::accessFile(pathname, mode); 28 | #endif // XPOSED_WITH_SELINUX 29 | 30 | return access(pathname, mode); 31 | } 32 | 33 | } // namespace xposed 34 | 35 | #endif /* XPOSED_SERVICE_H_ */ 36 | -------------------------------------------------------------------------------- /xposed_shared.h: -------------------------------------------------------------------------------- 1 | /** 2 | * These declarations are needed for both app_process and the libraries. 3 | */ 4 | 5 | #ifndef XPOSED_SHARED_H_ 6 | #define XPOSED_SHARED_H_ 7 | 8 | #include 9 | 10 | #include "cutils/log.h" 11 | #include "jni.h" 12 | 13 | #ifndef ALOG 14 | #define ALOG LOG 15 | #define ALOGD LOGD 16 | #define ALOGD LOGD 17 | #define ALOGE LOGE 18 | #define ALOGI LOGI 19 | #define ALOGV LOGV 20 | #endif 21 | 22 | #if PLATFORM_SDK_VERSION >= 24 23 | #define XPOSED_DIR "/data/user_de/0/de.robv.android.xposed.installer/" 24 | #else 25 | #define XPOSED_DIR "/data/data/de.robv.android.xposed.installer/" 26 | #endif 27 | 28 | namespace xposed { 29 | 30 | struct XposedShared { 31 | // Global variables 32 | bool zygote; 33 | bool startSystemServer; 34 | const char* startClassName; 35 | uint32_t xposedVersionInt; 36 | bool isSELinuxEnabled; 37 | bool isSELinuxEnforcing; 38 | uid_t installer_uid; 39 | gid_t installer_gid; 40 | 41 | // Provided by runtime-specific library, used by executable 42 | void (*onVmCreated)(JNIEnv* env); 43 | 44 | #if XPOSED_WITH_SELINUX 45 | // Provided by the executable, used by runtime-specific library 46 | int (*zygoteservice_accessFile)(const char* path, int mode); 47 | int (*zygoteservice_statFile)(const char* path, struct stat* st); 48 | char* (*zygoteservice_readFile)(const char* path, int* bytesRead); 49 | #endif 50 | }; 51 | 52 | extern XposedShared* xposed; 53 | 54 | } // namespace xposed 55 | 56 | #endif // XPOSED_SHARED_H_ 57 | --------------------------------------------------------------------------------