diff --git a/edxp-common/src/main/java/com/elderdrivers/riru/edxp/art/ClassLinker.java b/edxp-common/src/main/java/com/elderdrivers/riru/edxp/art/ClassLinker.java new file mode 100644 index 00000000..f566c64d --- /dev/null +++ b/edxp-common/src/main/java/com/elderdrivers/riru/edxp/art/ClassLinker.java @@ -0,0 +1,9 @@ +package com.elderdrivers.riru.edxp.art; + +import java.lang.reflect.Member; + +public class ClassLinker { + + public static native void setEntryPointsToInterpreter(Member method); + +} diff --git a/edxp-common/src/main/java/com/elderdrivers/riru/edxp/art/Heap.java b/edxp-common/src/main/java/com/elderdrivers/riru/edxp/art/Heap.java new file mode 100644 index 00000000..8cff4275 --- /dev/null +++ b/edxp-common/src/main/java/com/elderdrivers/riru/edxp/art/Heap.java @@ -0,0 +1,9 @@ +package com.elderdrivers.riru.edxp.art; + +import java.lang.reflect.Member; + +public class Heap { + + public static native int waitForGcToComplete(long thread); + +} diff --git a/edxp-common/src/main/java/com/elderdrivers/riru/edxp/config/ConfigManager.java b/edxp-common/src/main/java/com/elderdrivers/riru/edxp/config/ConfigManager.java index bfee37ed..013b14b0 100644 --- a/edxp-common/src/main/java/com/elderdrivers/riru/edxp/config/ConfigManager.java +++ b/edxp-common/src/main/java/com/elderdrivers/riru/edxp/config/ConfigManager.java @@ -18,17 +18,6 @@ public class ConfigManager { private static final String DYNAMIC_MODULES = INSTALLER_DATA_BASE_DIR + "conf/dynamicmodules"; private static final Set WHITE_LIST = Collections.singleton(INSTALLER_PACKAGE_NAME); private static final HashMap compatModeCache = new HashMap<>(); - private static volatile boolean IS_DYNAMIC_MODULES = false; - - public static boolean isDynamicModulesMode() { - return IS_DYNAMIC_MODULES; - } - - public static synchronized void setDynamicModulesMode(boolean isDynamicModulesMode) { - if (isDynamicModulesMode != IS_DYNAMIC_MODULES) { - IS_DYNAMIC_MODULES = isDynamicModulesMode; - } - } public static boolean shouldUseWhitelist() { return isFileExists(USE_WHITE_LIST); @@ -59,4 +48,16 @@ public class ConfigManager { private static boolean isFileExists(String path) { return SELinuxHelper.getAppDataFileService().checkFileExists(path); } + + public static native boolean isBlackWhiteListEnabled(); + + public static native boolean isDynamicModulesEnabled(); + + public static native boolean isResourcesHookEnabled(); + + public static native boolean isDeoptBootImageEnabled(); + + public static native String getInstallerPackageName(); + + public static native boolean isAppNeedHook(String appDataDir); } diff --git a/edxp-common/src/main/java/com/elderdrivers/riru/edxp/core/ResourcesHook.java b/edxp-common/src/main/java/com/elderdrivers/riru/edxp/core/ResourcesHook.java new file mode 100644 index 00000000..684a426b --- /dev/null +++ b/edxp-common/src/main/java/com/elderdrivers/riru/edxp/core/ResourcesHook.java @@ -0,0 +1,9 @@ +package com.elderdrivers.riru.edxp.core; + +public class ResourcesHook { + + public static native boolean initXResourcesNative(); + + public static native boolean removeFinalFlagNative(Class clazz); + +} diff --git a/edxp-common/src/main/java/com/elderdrivers/riru/edxp/core/Yahfa.java b/edxp-common/src/main/java/com/elderdrivers/riru/edxp/core/Yahfa.java new file mode 100644 index 00000000..f9e3cce6 --- /dev/null +++ b/edxp-common/src/main/java/com/elderdrivers/riru/edxp/core/Yahfa.java @@ -0,0 +1,18 @@ +package com.elderdrivers.riru.edxp.core; + +import java.lang.reflect.Member; +import java.lang.reflect.Method; + +public class Yahfa { + + public static native boolean backupAndHookNative(Object target, Method hook, Method backup); + + public static native void ensureMethodCached(Method hook, Method backup); + + // JNI.ToReflectedMethod() could return either Method or Constructor + public static native Object findMethodNative(Class targetClass, String methodName, String methodSig); + + public static native void init(int SDK_version); + + public static native void setMethodNonCompilable(Member member); +} diff --git a/edxp-common/src/main/java/com/elderdrivers/riru/edxp/framework/Zygote.java b/edxp-common/src/main/java/com/elderdrivers/riru/edxp/framework/Zygote.java new file mode 100644 index 00000000..8aa08002 --- /dev/null +++ b/edxp-common/src/main/java/com/elderdrivers/riru/edxp/framework/Zygote.java @@ -0,0 +1,11 @@ +package com.elderdrivers.riru.edxp.framework; + +public class Zygote { + + // prevent from fatal error caused by holding not whitelisted file descriptors when forking zygote + // https://github.com/rovo89/Xposed/commit/b3ba245ad04cd485699fb1d2ebde7117e58214ff + public static native void closeFilesBeforeFork(); + + public static native void reopenFilesAfterFork(); + +} diff --git a/edxp-core/build.gradle b/edxp-core/build.gradle index edb982f5..28a8bb5f 100644 --- a/edxp-core/build.gradle +++ b/edxp-core/build.gradle @@ -3,10 +3,10 @@ import org.gradle.internal.os.OperatingSystem apply plugin: 'com.android.library' -version "v0.4.2.3_alpha" +version "v0.4.3.0_alpha" ext { - versionCode = "4230" + versionCode = "4300" module_name = "EdXposed" jar_dest_dir = "${projectDir}/template_override/system/framework/" is_windows = OperatingSystem.current().isWindows() @@ -31,7 +31,7 @@ android { externalNativeBuild { cmake { abiFilters 'arm64-v8a', 'armeabi-v7a', 'x86', 'x86_64' - cppFlags "-std=c++11 -ffixed-x18 -Qunused-arguments -frtti" + cppFlags "-std=c++17 -ffixed-x18 -Qunused-arguments -frtti" cFlags "-std=gnu99 -ffixed-x18 -Qunused-arguments -frtti" } } @@ -49,8 +49,8 @@ android { release { externalNativeBuild { cmake { - cppFlags "-fvisibility=hidden -fvisibility-inlines-hidden -O2 -s" - cFlags "-fvisibility=hidden -fvisibility-inlines-hidden -O2 -s" + cppFlags "-fvisibility=hidden -fvisibility-inlines-hidden -O2 -s -Wno-unused-value" + cFlags "-fvisibility=hidden -fvisibility-inlines-hidden -O2 -s -Wno-unused-value" } } minifyEnabled true diff --git a/edxp-core/src/main/cpp/external/CMakeLists.txt b/edxp-core/src/main/cpp/external/CMakeLists.txt index 1d3971a1..79df4435 100644 --- a/edxp-core/src/main/cpp/external/CMakeLists.txt +++ b/edxp-core/src/main/cpp/external/CMakeLists.txt @@ -3,4 +3,5 @@ cmake_minimum_required(VERSION 3.4.1) add_subdirectory(xhook) add_subdirectory(riru) add_subdirectory(yahfa) -add_subdirectory(substrate) \ No newline at end of file +add_subdirectory(substrate) +add_subdirectory(android) \ No newline at end of file diff --git a/edxp-core/src/main/cpp/external/android/CMakeLists.txt b/edxp-core/src/main/cpp/external/android/CMakeLists.txt new file mode 100644 index 00000000..bc215090 --- /dev/null +++ b/edxp-core/src/main/cpp/external/android/CMakeLists.txt @@ -0,0 +1,11 @@ +cmake_minimum_required(VERSION 3.4.1) + +set(SRC_LIST + android-base/logging.cpp + android-base/file.cpp + android-base/threads.cpp + android-base/strings.cpp + ) +add_library(android STATIC ${SRC_LIST}) + +target_include_directories(android PUBLIC .) \ No newline at end of file diff --git a/edxp-core/src/main/cpp/external/android/android-base/file.cpp b/edxp-core/src/main/cpp/external/android/android-base/file.cpp new file mode 100644 index 00000000..95120fa3 --- /dev/null +++ b/edxp-core/src/main/cpp/external/android/android-base/file.cpp @@ -0,0 +1,427 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "android-base/file.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if defined(__APPLE__) +#include +#endif +#if defined(_WIN32) +#include +#include +#define O_NOFOLLOW 0 +#define OS_PATH_SEPARATOR '\\' +#else +#define OS_PATH_SEPARATOR '/' +#endif +#include "android-base/logging.h" // and must be after windows.h for ERROR +#include "android-base/macros.h" // For TEMP_FAILURE_RETRY on Darwin. +#include "android-base/unique_fd.h" +#include "android-base/utf8.h" +#ifdef _WIN32 +int mkstemp(char* template_name) { + if (_mktemp(template_name) == nullptr) { + return -1; + } + // Use open() to match the close() that TemporaryFile's destructor does. + // Use O_BINARY to match base file APIs. + return open(template_name, O_CREAT | O_EXCL | O_RDWR | O_BINARY, S_IRUSR | S_IWUSR); +} +char* mkdtemp(char* template_name) { + if (_mktemp(template_name) == nullptr) { + return nullptr; + } + if (_mkdir(template_name) == -1) { + return nullptr; + } + return template_name; +} +#endif +namespace { + std::string GetSystemTempDir() { +#if defined(__ANDROID__) + const auto* tmpdir = getenv("TMPDIR"); + if (tmpdir == nullptr) tmpdir = "/data/local/tmp"; + if (access(tmpdir, R_OK | W_OK | X_OK) == 0) { + return tmpdir; + } + // Tests running in app context can't access /data/local/tmp, + // so try current directory if /data/local/tmp is not accessible. + return "."; +#elif defined(_WIN32) + char tmp_dir[MAX_PATH]; + DWORD result = GetTempPathA(sizeof(tmp_dir), tmp_dir); // checks TMP env + CHECK_NE(result, 0ul) << "GetTempPathA failed, error: " << GetLastError(); + CHECK_LT(result, sizeof(tmp_dir)) << "path truncated to: " << result; + // GetTempPath() returns a path with a trailing slash, but init() + // does not expect that, so remove it. + CHECK_EQ(tmp_dir[result - 1], '\\'); + tmp_dir[result - 1] = '\0'; + return tmp_dir; +#else + const auto* tmpdir = getenv("TMPDIR"); + if (tmpdir == nullptr) tmpdir = "/tmp"; + return tmpdir; +#endif + } +} // namespace +TemporaryFile::TemporaryFile() { + init(GetSystemTempDir()); +} +TemporaryFile::TemporaryFile(const std::string& tmp_dir) { + init(tmp_dir); +} +TemporaryFile::~TemporaryFile() { + if (fd != -1) { + close(fd); + } + if (remove_file_) { + unlink(path); + } +} +int TemporaryFile::release() { + int result = fd; + fd = -1; + return result; +} +void TemporaryFile::init(const std::string& tmp_dir) { + snprintf(path, sizeof(path), "%s%cTemporaryFile-XXXXXX", tmp_dir.c_str(), OS_PATH_SEPARATOR); + fd = mkstemp(path); +} +TemporaryDir::TemporaryDir() { + init(GetSystemTempDir()); +} +TemporaryDir::~TemporaryDir() { + if (!remove_dir_and_contents_) return; + auto callback = [](const char* child, const struct stat*, int file_type, struct FTW*) -> int { + switch (file_type) { + case FTW_D: + case FTW_DP: + case FTW_DNR: + if (rmdir(child) == -1) { + PLOG(ERROR) << "rmdir " << child; + } + break; + case FTW_NS: + default: + if (rmdir(child) != -1) break; + // FALLTHRU (for gcc, lint, pcc, etc; and following for clang) + FALLTHROUGH_INTENDED; + case FTW_F: + case FTW_SL: + case FTW_SLN: + if (unlink(child) == -1) { + PLOG(ERROR) << "unlink " << child; + } + break; + } + return 0; + }; + nftw(path, callback, 128, FTW_DEPTH | FTW_MOUNT | FTW_PHYS); +} +bool TemporaryDir::init(const std::string& tmp_dir) { + snprintf(path, sizeof(path), "%s%cTemporaryDir-XXXXXX", tmp_dir.c_str(), OS_PATH_SEPARATOR); + return (mkdtemp(path) != nullptr); +} +namespace android { + namespace base { +// Versions of standard library APIs that support UTF-8 strings. + using namespace android::base::utf8; + bool ReadFdToString(borrowed_fd fd, std::string* content) { + content->clear(); + // Although original we had small files in mind, this code gets used for + // very large files too, where the std::string growth heuristics might not + // be suitable. https://code.google.com/p/android/issues/detail?id=258500. + struct stat sb; + if (fstat(fd.get(), &sb) != -1 && sb.st_size > 0) { + content->reserve(sb.st_size); + } + char buf[BUFSIZ]; + ssize_t n; + while ((n = TEMP_FAILURE_RETRY(read(fd.get(), &buf[0], sizeof(buf)))) > 0) { + content->append(buf, n); + } + return (n == 0) ? true : false; + } + bool ReadFileToString(const std::string& path, std::string* content, bool follow_symlinks) { + content->clear(); + int flags = O_RDONLY | O_CLOEXEC | O_BINARY | (follow_symlinks ? 0 : O_NOFOLLOW); + android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), flags))); + if (fd == -1) { + return false; + } + return ReadFdToString(fd, content); + } + bool WriteStringToFd(const std::string& content, borrowed_fd fd) { + const char* p = content.data(); + size_t left = content.size(); + while (left > 0) { + ssize_t n = TEMP_FAILURE_RETRY(write(fd.get(), p, left)); + if (n == -1) { + return false; + } + p += n; + left -= n; + } + return true; + } + static bool CleanUpAfterFailedWrite(const std::string& path) { + // Something went wrong. Let's not leave a corrupt file lying around. + int saved_errno = errno; + unlink(path.c_str()); + errno = saved_errno; + return false; + } +#if !defined(_WIN32) + bool WriteStringToFile(const std::string& content, const std::string& path, + mode_t mode, uid_t owner, gid_t group, + bool follow_symlinks) { + int flags = O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_BINARY | + (follow_symlinks ? 0 : O_NOFOLLOW); + android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), flags, mode))); + if (fd == -1) { + PLOG(ERROR) << "android::WriteStringToFile open failed"; + return false; + } + // We do an explicit fchmod here because we assume that the caller really + // meant what they said and doesn't want the umask-influenced mode. + if (fchmod(fd, mode) == -1) { + PLOG(ERROR) << "android::WriteStringToFile fchmod failed"; + return CleanUpAfterFailedWrite(path); + } + if (fchown(fd, owner, group) == -1) { + PLOG(ERROR) << "android::WriteStringToFile fchown failed"; + return CleanUpAfterFailedWrite(path); + } + if (!WriteStringToFd(content, fd)) { + PLOG(ERROR) << "android::WriteStringToFile write failed"; + return CleanUpAfterFailedWrite(path); + } + return true; + } +#endif + bool WriteStringToFile(const std::string& content, const std::string& path, + bool follow_symlinks) { + int flags = O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_BINARY | + (follow_symlinks ? 0 : O_NOFOLLOW); + android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), flags, 0666))); + if (fd == -1) { + return false; + } + return WriteStringToFd(content, fd) || CleanUpAfterFailedWrite(path); + } + bool ReadFully(borrowed_fd fd, void* data, size_t byte_count) { + uint8_t* p = reinterpret_cast(data); + size_t remaining = byte_count; + while (remaining > 0) { + ssize_t n = TEMP_FAILURE_RETRY(read(fd.get(), p, remaining)); + if (n <= 0) return false; + p += n; + remaining -= n; + } + return true; + } +#if defined(_WIN32) + // Windows implementation of pread. Note that this DOES move the file descriptors read position, +// but it does so atomically. +static ssize_t pread(borrowed_fd fd, void* data, size_t byte_count, off64_t offset) { + DWORD bytes_read; + OVERLAPPED overlapped; + memset(&overlapped, 0, sizeof(OVERLAPPED)); + overlapped.Offset = static_cast(offset); + overlapped.OffsetHigh = static_cast(offset >> 32); + if (!ReadFile(reinterpret_cast(_get_osfhandle(fd.get())), data, + static_cast(byte_count), &bytes_read, &overlapped)) { + // In case someone tries to read errno (since this is masquerading as a POSIX call) + errno = EIO; + return -1; + } + return static_cast(bytes_read); +} +#endif + bool ReadFullyAtOffset(borrowed_fd fd, void* data, size_t byte_count, off64_t offset) { + uint8_t* p = reinterpret_cast(data); + while (byte_count > 0) { + ssize_t n = TEMP_FAILURE_RETRY(pread(fd.get(), p, byte_count, offset)); + if (n <= 0) return false; + p += n; + byte_count -= n; + offset += n; + } + return true; + } + bool WriteFully(borrowed_fd fd, const void* data, size_t byte_count) { + const uint8_t* p = reinterpret_cast(data); + size_t remaining = byte_count; + while (remaining > 0) { + ssize_t n = TEMP_FAILURE_RETRY(write(fd.get(), p, remaining)); + if (n == -1) return false; + p += n; + remaining -= n; + } + return true; + } + bool RemoveFileIfExists(const std::string& path, std::string* err) { + struct stat st; +#if defined(_WIN32) + // TODO: Windows version can't handle symbolic links correctly. + int result = stat(path.c_str(), &st); + bool file_type_removable = (result == 0 && S_ISREG(st.st_mode)); +#else + int result = lstat(path.c_str(), &st); + bool file_type_removable = (result == 0 && (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode))); +#endif + if (result == -1) { + if (errno == ENOENT || errno == ENOTDIR) return true; + if (err != nullptr) *err = strerror(errno); + return false; + } + if (result == 0) { + if (!file_type_removable) { + if (err != nullptr) { + *err = "is not a regular file or symbolic link"; + } + return false; + } + if (unlink(path.c_str()) == -1) { + if (err != nullptr) { + *err = strerror(errno); + } + return false; + } + } + return true; + } +#if !defined(_WIN32) + bool Readlink(const std::string& path, std::string* result) { + result->clear(); + // Most Linux file systems (ext2 and ext4, say) limit symbolic links to + // 4095 bytes. Since we'll copy out into the string anyway, it doesn't + // waste memory to just start there. We add 1 so that we can recognize + // whether it actually fit (rather than being truncated to 4095). + std::vector buf(4095 + 1); + while (true) { + ssize_t size = readlink(path.c_str(), &buf[0], buf.size()); + // Unrecoverable error? + if (size == -1) return false; + // It fit! (If size == buf.size(), it may have been truncated.) + if (static_cast(size) < buf.size()) { + result->assign(&buf[0], size); + return true; + } + // Double our buffer and try again. + buf.resize(buf.size() * 2); + } + } +#endif +#if !defined(_WIN32) + bool Realpath(const std::string& path, std::string* result) { + result->clear(); + // realpath may exit with EINTR. Retry if so. + char* realpath_buf = nullptr; + do { + realpath_buf = realpath(path.c_str(), nullptr); + } while (realpath_buf == nullptr && errno == EINTR); + if (realpath_buf == nullptr) { + return false; + } + result->assign(realpath_buf); + free(realpath_buf); + return true; + } +#endif + std::string GetExecutablePath() { +#if defined(__linux__) + std::string path; + android::base::Readlink("/proc/self/exe", &path); + return path; +#elif defined(__APPLE__) + char path[PATH_MAX + 1]; + uint32_t path_len = sizeof(path); + int rc = _NSGetExecutablePath(path, &path_len); + if (rc < 0) { + std::unique_ptr path_buf(new char[path_len]); + _NSGetExecutablePath(path_buf.get(), &path_len); + return path_buf.get(); + } + return path; +#elif defined(_WIN32) + char path[PATH_MAX + 1]; + DWORD result = GetModuleFileName(NULL, path, sizeof(path) - 1); + if (result == 0 || result == sizeof(path) - 1) return ""; + path[PATH_MAX - 1] = 0; + return path; +#else +#error unknown OS +#endif + } + std::string GetExecutableDirectory() { + return Dirname(GetExecutablePath()); + } + std::string Basename(const std::string& path) { + // Copy path because basename may modify the string passed in. + std::string result(path); +#if !defined(__BIONIC__) + // Use lock because basename() may write to a process global and return a + // pointer to that. Note that this locking strategy only works if all other + // callers to basename in the process also grab this same lock, but its + // better than nothing. Bionic's basename returns a thread-local buffer. + static std::mutex& basename_lock = *new std::mutex(); + std::lock_guard lock(basename_lock); +#endif + // Note that if std::string uses copy-on-write strings, &str[0] will cause + // the copy to be made, so there is no chance of us accidentally writing to + // the storage for 'path'. + char* name = basename(&result[0]); + // In case basename returned a pointer to a process global, copy that string + // before leaving the lock. + result.assign(name); + return result; + } + std::string Dirname(const std::string& path) { + // Copy path because dirname may modify the string passed in. + std::string result(path); +#if !defined(__BIONIC__) + // Use lock because dirname() may write to a process global and return a + // pointer to that. Note that this locking strategy only works if all other + // callers to dirname in the process also grab this same lock, but its + // better than nothing. Bionic's dirname returns a thread-local buffer. + static std::mutex& dirname_lock = *new std::mutex(); + std::lock_guard lock(dirname_lock); +#endif + // Note that if std::string uses copy-on-write strings, &str[0] will cause + // the copy to be made, so there is no chance of us accidentally writing to + // the storage for 'path'. + char* parent = dirname(&result[0]); + // In case dirname returned a pointer to a process global, copy that string + // before leaving the lock. + result.assign(parent); + return result; + } + } // namespace base +} // namespace android \ No newline at end of file diff --git a/edxp-core/src/main/cpp/external/android/android-base/file.h b/edxp-core/src/main/cpp/external/android/android-base/file.h new file mode 100644 index 00000000..f6a1cedb --- /dev/null +++ b/edxp-core/src/main/cpp/external/android/android-base/file.h @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once +#include +#include +#include +#include "android-base/macros.h" +#include "android-base/off64_t.h" +#include "android-base/unique_fd.h" +#if !defined(_WIN32) && !defined(O_BINARY) +/** Windows needs O_BINARY, but Unix never mangles line endings. */ +#define O_BINARY 0 +#endif +#if defined(_WIN32) && !defined(O_CLOEXEC) +/** Windows has O_CLOEXEC but calls it O_NOINHERIT for some reason. */ +#define O_CLOEXEC O_NOINHERIT +#endif +class TemporaryFile { +public: + TemporaryFile(); + explicit TemporaryFile(const std::string& tmp_dir); + ~TemporaryFile(); + // Release the ownership of fd, caller is reponsible for closing the + // fd or stream properly. + int release(); + // Don't remove the temporary file in the destructor. + void DoNotRemove() { remove_file_ = false; } + int fd; + char path[1024]; +private: + void init(const std::string& tmp_dir); + bool remove_file_ = true; + DISALLOW_COPY_AND_ASSIGN(TemporaryFile); +}; +class TemporaryDir { +public: + TemporaryDir(); + ~TemporaryDir(); + // Don't remove the temporary dir in the destructor. + void DoNotRemove() { remove_dir_and_contents_ = false; } + char path[1024]; +private: + bool init(const std::string& tmp_dir); + bool remove_dir_and_contents_ = true; + DISALLOW_COPY_AND_ASSIGN(TemporaryDir); +}; +namespace android { + namespace base { + bool ReadFdToString(borrowed_fd fd, std::string* content); + bool ReadFileToString(const std::string& path, std::string* content, + bool follow_symlinks = false); + bool WriteStringToFile(const std::string& content, const std::string& path, + bool follow_symlinks = false); + bool WriteStringToFd(const std::string& content, borrowed_fd fd); +#if !defined(_WIN32) + bool WriteStringToFile(const std::string& content, const std::string& path, + mode_t mode, uid_t owner, gid_t group, + bool follow_symlinks = false); +#endif + bool ReadFully(borrowed_fd fd, void* data, size_t byte_count); +// Reads `byte_count` bytes from the file descriptor at the specified offset. +// Returns false if there was an IO error or EOF was reached before reading `byte_count` bytes. +// +// NOTE: On Linux/Mac, this function wraps pread, which provides atomic read support without +// modifying the read pointer of the file descriptor. On Windows, however, the read pointer does +// get modified. This means that ReadFullyAtOffset can be used concurrently with other calls to the +// same function, but concurrently seeking or reading incrementally can lead to unexpected +// behavior. + bool ReadFullyAtOffset(borrowed_fd fd, void* data, size_t byte_count, off64_t offset); + bool WriteFully(borrowed_fd fd, const void* data, size_t byte_count); + bool RemoveFileIfExists(const std::string& path, std::string* err = nullptr); +#if !defined(_WIN32) + bool Realpath(const std::string& path, std::string* result); + bool Readlink(const std::string& path, std::string* result); +#endif + std::string GetExecutablePath(); + std::string GetExecutableDirectory(); +// Like the regular basename and dirname, but thread-safe on all +// platforms and capable of correctly handling exotic Windows paths. + std::string Basename(const std::string& path); + std::string Dirname(const std::string& path); + } // namespace base +} // namespace android \ No newline at end of file diff --git a/edxp-core/src/main/cpp/external/android/android-base/logging.cpp b/edxp-core/src/main/cpp/external/android/android-base/logging.cpp new file mode 100644 index 00000000..e5a6ecef --- /dev/null +++ b/edxp-core/src/main/cpp/external/android/android-base/logging.cpp @@ -0,0 +1,423 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#if defined(_WIN32) +#include +#endif +#include "android-base/logging.h" +#include +#include +#include +#include +// For getprogname(3) or program_invocation_short_name. +#if defined(__ANDROID__) || defined(__APPLE__) +#include +#elif defined(__GLIBC__) +#include +#endif +#if defined(__linux__) +#include +#endif +#include +#include +#include +#include +#include +#include +#include +// Headers for LogMessage::LogLine. +#ifdef __ANDROID__ +#include +#include +#else +#include +#include +#endif +#include +#include +#include +#include +#include +namespace android { + namespace base { +// BSD-based systems like Android/macOS have getprogname(). Others need us to provide one. +#if defined(__GLIBC__) || defined(_WIN32) + static const char* getprogname() { +#if defined(__GLIBC__) + return program_invocation_short_name; +#elif defined(_WIN32) + static bool first = true; + static char progname[MAX_PATH] = {}; + if (first) { + snprintf(progname, sizeof(progname), "%s", + android::base::Basename(android::base::GetExecutablePath()).c_str()); + first = false; + } + return progname; +#endif +} +#endif + static const char* GetFileBasename(const char* file) { + // We can't use basename(3) even on Unix because the Mac doesn't + // have a non-modifying basename. + const char* last_slash = strrchr(file, '/'); + if (last_slash != nullptr) { + return last_slash + 1; + } +#if defined(_WIN32) + const char* last_backslash = strrchr(file, '\\'); + if (last_backslash != nullptr) { + return last_backslash + 1; + } +#endif + return file; + } +#if defined(__linux__) + static int OpenKmsg() { +#if defined(__ANDROID__) + // pick up 'file w /dev/kmsg' environment from daemon's init rc file + const auto val = getenv("ANDROID_FILE__dev_kmsg"); + if (val != nullptr) { + int fd; + if (android::base::ParseInt(val, &fd, 0)) { + auto flags = fcntl(fd, F_GETFL); + if ((flags != -1) && ((flags & O_ACCMODE) == O_WRONLY)) return fd; + } + } +#endif + return TEMP_FAILURE_RETRY(open("/dev/kmsg", O_WRONLY | O_CLOEXEC)); +} +#endif + static std::mutex& LoggingLock() { + static auto& logging_lock = *new std::mutex(); + return logging_lock; + } + static LogFunction& Logger() { +#ifdef __ANDROID__ + static auto& logger = *new LogFunction(LogdLogger()); +#else + static auto& logger = *new LogFunction(StderrLogger); +#endif + return logger; + } + static AbortFunction& Aborter() { + static auto& aborter = *new AbortFunction(DefaultAborter); + return aborter; + } + static std::recursive_mutex& TagLock() { + static auto& tag_lock = *new std::recursive_mutex(); + return tag_lock; + } + static std::string* gDefaultTag; + std::string GetDefaultTag() { + std::lock_guard lock(TagLock()); + if (gDefaultTag == nullptr) { + return ""; + } + return *gDefaultTag; + } + void SetDefaultTag(const std::string& tag) { + std::lock_guard lock(TagLock()); + if (gDefaultTag != nullptr) { + delete gDefaultTag; + gDefaultTag = nullptr; + } + if (!tag.empty()) { + gDefaultTag = new std::string(tag); + } + } + static bool gInitialized = false; + static LogSeverity gMinimumLogSeverity = INFO; +#if defined(__linux__) + void KernelLogger(android::base::LogId, android::base::LogSeverity severity, + const char* tag, const char*, unsigned int, const char* msg) { + // clang-format off + static constexpr int kLogSeverityToKernelLogLevel[] = { + [android::base::VERBOSE] = 7, // KERN_DEBUG (there is no verbose kernel log + // level) + [android::base::DEBUG] = 7, // KERN_DEBUG + [android::base::INFO] = 6, // KERN_INFO + [android::base::WARNING] = 4, // KERN_WARNING + [android::base::ERROR] = 3, // KERN_ERROR + [android::base::FATAL_WITHOUT_ABORT] = 2, // KERN_CRIT + [android::base::FATAL] = 2, // KERN_CRIT + }; + // clang-format on + static_assert(arraysize(kLogSeverityToKernelLogLevel) == android::base::FATAL + 1, + "Mismatch in size of kLogSeverityToKernelLogLevel and values in LogSeverity"); + static int klog_fd = OpenKmsg(); + if (klog_fd == -1) return; + int level = kLogSeverityToKernelLogLevel[severity]; + // The kernel's printk buffer is only 1024 bytes. + // TODO: should we automatically break up long lines into multiple lines? + // Or we could log but with something like "..." at the end? + char buf[1024]; + size_t size = snprintf(buf, sizeof(buf), "<%d>%s: %s\n", level, tag, msg); + if (size > sizeof(buf)) { + size = snprintf(buf, sizeof(buf), "<%d>%s: %zu-byte message too long for printk\n", + level, tag, size); + } + iovec iov[1]; + iov[0].iov_base = buf; + iov[0].iov_len = size; + TEMP_FAILURE_RETRY(writev(klog_fd, iov, 1)); +} +#endif + void StderrLogger(LogId, LogSeverity severity, const char* tag, const char* file, unsigned int line, + const char* message) { + struct tm now; + time_t t = time(nullptr); +#if defined(_WIN32) + localtime_s(&now, &t); +#else + localtime_r(&t, &now); +#endif + char timestamp[32]; + strftime(timestamp, sizeof(timestamp), "%m-%d %H:%M:%S", &now); + static const char log_characters[] = "VDIWEFF"; + static_assert(arraysize(log_characters) - 1 == FATAL + 1, + "Mismatch in size of log_characters and values in LogSeverity"); + char severity_char = log_characters[severity]; + fprintf(stderr, "%s %c %s %5d %5" PRIu64 " %s:%u] %s\n", tag ? tag : "nullptr", severity_char, + timestamp, getpid(), GetThreadId(), file, line, message); + } + void StdioLogger(LogId, LogSeverity severity, const char* /*tag*/, const char* /*file*/, + unsigned int /*line*/, const char* message) { + if (severity >= WARNING) { + fflush(stdout); + fprintf(stderr, "%s: %s\n", GetFileBasename(getprogname()), message); + } else { + fprintf(stdout, "%s\n", message); + } + } + void DefaultAborter(const char* abort_message) { +#ifdef __ANDROID__ + android_set_abort_message(abort_message); +#else + UNUSED(abort_message); +#endif + abort(); + } +#ifdef __ANDROID__ + LogdLogger::LogdLogger(LogId default_log_id) : default_log_id_(default_log_id) { +} +void LogdLogger::operator()(LogId id, LogSeverity severity, const char* tag, + const char* file, unsigned int line, + const char* message) { + static constexpr android_LogPriority kLogSeverityToAndroidLogPriority[] = { + ANDROID_LOG_VERBOSE, ANDROID_LOG_DEBUG, ANDROID_LOG_INFO, + ANDROID_LOG_WARN, ANDROID_LOG_ERROR, ANDROID_LOG_FATAL, + ANDROID_LOG_FATAL, + }; + static_assert(arraysize(kLogSeverityToAndroidLogPriority) == FATAL + 1, + "Mismatch in size of kLogSeverityToAndroidLogPriority and values in LogSeverity"); + int priority = kLogSeverityToAndroidLogPriority[severity]; + if (id == DEFAULT) { + id = default_log_id_; + } + static constexpr log_id kLogIdToAndroidLogId[] = { + LOG_ID_MAX, LOG_ID_MAIN, LOG_ID_SYSTEM, + }; + static_assert(arraysize(kLogIdToAndroidLogId) == SYSTEM + 1, + "Mismatch in size of kLogIdToAndroidLogId and values in LogId"); + log_id lg_id = kLogIdToAndroidLogId[id]; + if (priority == ANDROID_LOG_FATAL) { + __android_log_buf_print(lg_id, priority, tag, "%s:%u] %s", file, line, + message); + } else { + __android_log_buf_print(lg_id, priority, tag, "%s", message); + } +} +#endif + void InitLogging(char* argv[], LogFunction&& logger, AbortFunction&& aborter) { + SetLogger(std::forward(logger)); + SetAborter(std::forward(aborter)); + if (gInitialized) { + return; + } + gInitialized = true; + // Stash the command line for later use. We can use /proc/self/cmdline on + // Linux to recover this, but we don't have that luxury on the Mac/Windows, + // and there are a couple of argv[0] variants that are commonly used. + if (argv != nullptr) { + SetDefaultTag(basename(argv[0])); + } + const char* tags = getenv("ANDROID_LOG_TAGS"); + if (tags == nullptr) { + return; + } + std::vector specs = Split(tags, " "); + for (size_t i = 0; i < specs.size(); ++i) { + // "tag-pattern:[vdiwefs]" + std::string spec(specs[i]); + if (spec.size() == 3 && StartsWith(spec, "*:")) { + switch (spec[2]) { + case 'v': + gMinimumLogSeverity = VERBOSE; + continue; + case 'd': + gMinimumLogSeverity = DEBUG; + continue; + case 'i': + gMinimumLogSeverity = INFO; + continue; + case 'w': + gMinimumLogSeverity = WARNING; + continue; + case 'e': + gMinimumLogSeverity = ERROR; + continue; + case 'f': + gMinimumLogSeverity = FATAL_WITHOUT_ABORT; + continue; + // liblog will even suppress FATAL if you say 's' for silent, but that's + // crazy! + case 's': + gMinimumLogSeverity = FATAL_WITHOUT_ABORT; + continue; + } + } + LOG(FATAL) << "unsupported '" << spec << "' in ANDROID_LOG_TAGS (" << tags + << ")"; + } + } + void SetLogger(LogFunction&& logger) { + std::lock_guard lock(LoggingLock()); + Logger() = std::move(logger); + } + void SetAborter(AbortFunction&& aborter) { + std::lock_guard lock(LoggingLock()); + Aborter() = std::move(aborter); + } +// This indirection greatly reduces the stack impact of having lots of +// checks/logging in a function. + class LogMessageData { + public: + LogMessageData(const char* file, unsigned int line, LogId id, LogSeverity severity, + const char* tag, int error) + : file_(GetFileBasename(file)), + line_number_(line), + id_(id), + severity_(severity), + tag_(tag), + error_(error) {} + const char* GetFile() const { + return file_; + } + unsigned int GetLineNumber() const { + return line_number_; + } + LogSeverity GetSeverity() const { + return severity_; + } + const char* GetTag() const { return tag_; } + LogId GetId() const { + return id_; + } + int GetError() const { + return error_; + } + std::ostream& GetBuffer() { + return buffer_; + } + std::string ToString() const { + return buffer_.str(); + } + private: + std::ostringstream buffer_; + const char* const file_; + const unsigned int line_number_; + const LogId id_; + const LogSeverity severity_; + const char* const tag_; + const int error_; + DISALLOW_COPY_AND_ASSIGN(LogMessageData); + }; + LogMessage::LogMessage(const char* file, unsigned int line, LogId id, LogSeverity severity, + const char* tag, int error) + : data_(new LogMessageData(file, line, id, severity, tag, error)) {} + LogMessage::~LogMessage() { + // Check severity again. This is duplicate work wrt/ LOG macros, but not LOG_STREAM. + if (!WOULD_LOG(data_->GetSeverity())) { + return; + } + // Finish constructing the message. + if (data_->GetError() != -1) { + data_->GetBuffer() << ": " << strerror(data_->GetError()); + } + std::string msg(data_->ToString()); + if (data_->GetSeverity() == FATAL) { +#ifdef __ANDROID__ + // Set the bionic abort message early to avoid liblog doing it + // with the individual lines, so that we get the whole message. + android_set_abort_message(msg.c_str()); +#endif + } + { + // Do the actual logging with the lock held. + std::lock_guard lock(LoggingLock()); + if (msg.find('\n') == std::string::npos) { + LogLine(data_->GetFile(), data_->GetLineNumber(), data_->GetId(), data_->GetSeverity(), + data_->GetTag(), msg.c_str()); + } else { + msg += '\n'; + size_t i = 0; + while (i < msg.size()) { + size_t nl = msg.find('\n', i); + msg[nl] = '\0'; + LogLine(data_->GetFile(), data_->GetLineNumber(), data_->GetId(), data_->GetSeverity(), + data_->GetTag(), &msg[i]); + // Undo the zero-termination so we can give the complete message to the aborter. + msg[nl] = '\n'; + i = nl + 1; + } + } + } + // Abort if necessary. + if (data_->GetSeverity() == FATAL) { + Aborter()(msg.c_str()); + } + } + std::ostream& LogMessage::stream() { + return data_->GetBuffer(); + } + void LogMessage::LogLine(const char* file, unsigned int line, LogId id, LogSeverity severity, + const char* tag, const char* message) { + if (tag == nullptr) { + std::lock_guard lock(TagLock()); + if (gDefaultTag == nullptr) { + gDefaultTag = new std::string(getprogname()); + } + Logger()(id, severity, gDefaultTag->c_str(), file, line, message); + } else { + Logger()(id, severity, tag, file, line, message); + } + } + LogSeverity GetMinimumLogSeverity() { + return gMinimumLogSeverity; + } + LogSeverity SetMinimumLogSeverity(LogSeverity new_severity) { + LogSeverity old_severity = gMinimumLogSeverity; + gMinimumLogSeverity = new_severity; + return old_severity; + } + ScopedLogSeverity::ScopedLogSeverity(LogSeverity new_severity) { + old_ = SetMinimumLogSeverity(new_severity); + } + ScopedLogSeverity::~ScopedLogSeverity() { + SetMinimumLogSeverity(old_); + } + } // namespace base +} // namespace android \ No newline at end of file diff --git a/edxp-core/src/main/cpp/external/android/android-base/logging.h b/edxp-core/src/main/cpp/external/android/android-base/logging.h new file mode 100644 index 00000000..f6a56f3b --- /dev/null +++ b/edxp-core/src/main/cpp/external/android/android-base/logging.h @@ -0,0 +1,429 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once +// +// Google-style C++ logging. +// +// This header provides a C++ stream interface to logging. +// +// To log: +// +// LOG(INFO) << "Some text; " << some_value; +// +// Replace `INFO` with any severity from `enum LogSeverity`. +// +// To log the result of a failed function and include the string +// representation of `errno` at the end: +// +// PLOG(ERROR) << "Write failed"; +// +// The output will be something like `Write failed: I/O error`. +// Remember this as 'P' as in perror(3). +// +// To output your own types, simply implement operator<< as normal. +// +// By default, output goes to logcat on Android and stderr on the host. +// A process can use `SetLogger` to decide where all logging goes. +// Implementations are provided for logcat, stderr, and dmesg. +// +// By default, the process' name is used as the log tag. +// Code can choose a specific log tag by defining LOG_TAG +// before including this header. +// This header also provides assertions: +// +// CHECK(must_be_true); +// CHECK_EQ(a, b) << z_is_interesting_too; +// NOTE: For Windows, you must include logging.h after windows.h to allow the +// following code to suppress the evil ERROR macro: +#ifdef _WIN32 +// windows.h includes wingdi.h which defines an evil macro ERROR. +#ifdef ERROR +#undef ERROR +#endif +#endif +#include +#include +#include +#include "macros.h" +// Note: DO NOT USE DIRECTLY. Use LOG_TAG instead. +#ifdef _LOG_TAG_INTERNAL +#error "_LOG_TAG_INTERNAL must not be defined" +#endif +#ifdef LOG_TAG +#define _LOG_TAG_INTERNAL LOG_TAG +#else +#define _LOG_TAG_INTERNAL "EdXposed" +#endif +namespace android { + namespace base { + enum LogSeverity { + VERBOSE, + DEBUG, + INFO, + WARNING, + ERROR, + FATAL_WITHOUT_ABORT, + FATAL, + }; + enum LogId { + DEFAULT, + MAIN, + SYSTEM, + }; + using LogFunction = std::function; + using AbortFunction = std::function; +// Loggers for use with InitLogging/SetLogger. +// Log to the kernel log (dmesg). + void KernelLogger(LogId, LogSeverity, const char*, const char*, unsigned int, const char*); +// Log to stderr in the full logcat format (with pid/tid/time/tag details). + void StderrLogger(LogId, LogSeverity, const char*, const char*, unsigned int, const char*); +// Log just the message to stdout/stderr (without pid/tid/time/tag details). +// The choice of stdout versus stderr is based on the severity. +// Errors are also prefixed by the program name (as with err(3)/error(3)). +// Useful for replacing printf(3)/perror(3)/err(3)/error(3) in command-line tools. + void StdioLogger(LogId, LogSeverity, const char*, const char*, unsigned int, const char*); + void DefaultAborter(const char* abort_message); + std::string GetDefaultTag(); + void SetDefaultTag(const std::string& tag); +#ifdef __ANDROID__ + // We expose this even though it is the default because a user that wants to +// override the default log buffer will have to construct this themselves. +class LogdLogger { + public: + explicit LogdLogger(LogId default_log_id = android::base::MAIN); + void operator()(LogId, LogSeverity, const char* tag, const char* file, + unsigned int line, const char* message); + private: + LogId default_log_id_; +}; +#endif +// Configure logging based on ANDROID_LOG_TAGS environment variable. +// We need to parse a string that looks like +// +// *:v jdwp:d dalvikvm:d dalvikvm-gc:i dalvikvmi:i +// +// The tag (or '*' for the global level) comes first, followed by a colon and a +// letter indicating the minimum priority level we're expected to log. This can +// be used to reveal or conceal logs with specific tags. +#ifdef __ANDROID__ +#define INIT_LOGGING_DEFAULT_LOGGER LogdLogger() +#else +#define INIT_LOGGING_DEFAULT_LOGGER StderrLogger +#endif + void InitLogging(char* argv[], + LogFunction&& logger = INIT_LOGGING_DEFAULT_LOGGER, + AbortFunction&& aborter = DefaultAborter); +#undef INIT_LOGGING_DEFAULT_LOGGER +// Replace the current logger. + void SetLogger(LogFunction&& logger); +// Replace the current aborter. + void SetAborter(AbortFunction&& aborter); + class ErrnoRestorer { + public: + ErrnoRestorer() + : saved_errno_(errno) { + } + ~ErrnoRestorer() { + errno = saved_errno_; + } + // Allow this object to be used as part of && operation. + operator bool() const { + return true; + } + private: + const int saved_errno_; + DISALLOW_COPY_AND_ASSIGN(ErrnoRestorer); + }; +// A helper macro that produces an expression that accepts both a qualified name and an +// unqualified name for a LogSeverity, and returns a LogSeverity value. +// Note: DO NOT USE DIRECTLY. This is an implementation detail. +#define SEVERITY_LAMBDA(severity) ([&]() { \ + using ::android::base::VERBOSE; \ + using ::android::base::DEBUG; \ + using ::android::base::INFO; \ + using ::android::base::WARNING; \ + using ::android::base::ERROR; \ + using ::android::base::FATAL_WITHOUT_ABORT; \ + using ::android::base::FATAL; \ + return (severity); }()) +#ifdef __clang_analyzer__ + // Clang's static analyzer does not see the conditional statement inside +// LogMessage's destructor that will abort on FATAL severity. +#define ABORT_AFTER_LOG_FATAL for (;; abort()) +struct LogAbortAfterFullExpr { + ~LogAbortAfterFullExpr() __attribute__((noreturn)) { abort(); } + explicit operator bool() const { return false; } +}; +// Provides an expression that evaluates to the truthiness of `x`, automatically +// aborting if `c` is true. +#define ABORT_AFTER_LOG_EXPR_IF(c, x) (((c) && ::android::base::LogAbortAfterFullExpr()) || (x)) +// Note to the static analyzer that we always execute FATAL logs in practice. +#define MUST_LOG_MESSAGE(severity) (SEVERITY_LAMBDA(severity) == ::android::base::FATAL) +#else +#define ABORT_AFTER_LOG_FATAL +#define ABORT_AFTER_LOG_EXPR_IF(c, x) (x) +#define MUST_LOG_MESSAGE(severity) false +#endif +#define ABORT_AFTER_LOG_FATAL_EXPR(x) ABORT_AFTER_LOG_EXPR_IF(true, x) +// Defines whether the given severity will be logged or silently swallowed. +#define WOULD_LOG(severity) \ + (UNLIKELY((SEVERITY_LAMBDA(severity)) >= ::android::base::GetMinimumLogSeverity()) || \ + MUST_LOG_MESSAGE(severity)) +// Get an ostream that can be used for logging at the given severity and to the default +// destination. +// +// Notes: +// 1) This will not check whether the severity is high enough. One should use WOULD_LOG to filter +// usage manually. +// 2) This does not save and restore errno. +#define LOG_STREAM(severity) LOG_STREAM_TO(DEFAULT, severity) +// Get an ostream that can be used for logging at the given severity and to the +// given destination. The same notes as for LOG_STREAM apply. +#define LOG_STREAM_TO(dest, severity) \ + ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::dest, \ + SEVERITY_LAMBDA(severity), _LOG_TAG_INTERNAL, -1) \ + .stream() +// Logs a message to logcat on Android otherwise to stderr. If the severity is +// FATAL it also causes an abort. For example: +// +// LOG(FATAL) << "We didn't expect to reach here"; +#define LOG(severity) LOG_TO(DEFAULT, severity) +// Checks if we want to log something, and sets up appropriate RAII objects if +// so. +// Note: DO NOT USE DIRECTLY. This is an implementation detail. +#define LOGGING_PREAMBLE(severity) \ + (WOULD_LOG(severity) && \ + ABORT_AFTER_LOG_EXPR_IF((SEVERITY_LAMBDA(severity)) == ::android::base::FATAL, true) && \ + ::android::base::ErrnoRestorer()) +// Logs a message to logcat with the specified log ID on Android otherwise to +// stderr. If the severity is FATAL it also causes an abort. +// Use an expression here so we can support the << operator following the macro, +// like "LOG(DEBUG) << xxx;". +#define LOG_TO(dest, severity) LOGGING_PREAMBLE(severity) && LOG_STREAM_TO(dest, severity) +// A variant of LOG that also logs the current errno value. To be used when +// library calls fail. +#define PLOG(severity) PLOG_TO(DEFAULT, severity) +// Behaves like PLOG, but logs to the specified log ID. +#define PLOG_TO(dest, severity) \ + LOGGING_PREAMBLE(severity) && \ + ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::dest, \ + SEVERITY_LAMBDA(severity), _LOG_TAG_INTERNAL, errno) \ + .stream() +// Marker that code is yet to be implemented. +#define UNIMPLEMENTED(level) \ + LOG(level) << __PRETTY_FUNCTION__ << " unimplemented " +// Check whether condition x holds and LOG(FATAL) if not. The value of the +// expression x is only evaluated once. Extra logging can be appended using << +// after. For example: +// +// CHECK(false == true) results in a log message of +// "Check failed: false == true". +#define CHECK(x) \ + LIKELY((x)) || ABORT_AFTER_LOG_FATAL_EXPR(false) || \ + ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::DEFAULT, \ + ::android::base::FATAL, _LOG_TAG_INTERNAL, -1) \ + .stream() \ + << "Check failed: " #x << " " +// clang-format off +// Helper for CHECK_xx(x,y) macros. +#define CHECK_OP(LHS, RHS, OP) \ + for (auto _values = ::android::base::MakeEagerEvaluator(LHS, RHS); \ + UNLIKELY(!(_values.lhs OP _values.rhs)); \ + /* empty */) \ + ABORT_AFTER_LOG_FATAL \ + ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::DEFAULT, \ + ::android::base::FATAL, _LOG_TAG_INTERNAL, -1) \ + .stream() \ + << "Check failed: " << #LHS << " " << #OP << " " << #RHS << " (" #LHS "=" << _values.lhs \ + << ", " #RHS "=" << _values.rhs << ") " +// clang-format on +// Check whether a condition holds between x and y, LOG(FATAL) if not. The value +// of the expressions x and y is evaluated once. Extra logging can be appended +// using << after. For example: +// +// CHECK_NE(0 == 1, false) results in +// "Check failed: false != false (0==1=false, false=false) ". +#define CHECK_EQ(x, y) CHECK_OP(x, y, == ) +#define CHECK_NE(x, y) CHECK_OP(x, y, != ) +#define CHECK_LE(x, y) CHECK_OP(x, y, <= ) +#define CHECK_LT(x, y) CHECK_OP(x, y, < ) +#define CHECK_GE(x, y) CHECK_OP(x, y, >= ) +#define CHECK_GT(x, y) CHECK_OP(x, y, > ) +// clang-format off +// Helper for CHECK_STRxx(s1,s2) macros. +#define CHECK_STROP(s1, s2, sense) \ + while (UNLIKELY((strcmp(s1, s2) == 0) != (sense))) \ + ABORT_AFTER_LOG_FATAL \ + ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::DEFAULT, \ + ::android::base::FATAL, _LOG_TAG_INTERNAL, -1) \ + .stream() \ + << "Check failed: " << "\"" << (s1) << "\"" \ + << ((sense) ? " == " : " != ") << "\"" << (s2) << "\"" +// clang-format on +// Check for string (const char*) equality between s1 and s2, LOG(FATAL) if not. +#define CHECK_STREQ(s1, s2) CHECK_STROP(s1, s2, true) +#define CHECK_STRNE(s1, s2) CHECK_STROP(s1, s2, false) +// Perform the pthread function call(args), LOG(FATAL) on error. +#define CHECK_PTHREAD_CALL(call, args, what) \ + do { \ + int rc = call args; \ + if (rc != 0) { \ + errno = rc; \ + ABORT_AFTER_LOG_FATAL \ + PLOG(FATAL) << #call << " failed for " << (what); \ + } \ + } while (false) +// CHECK that can be used in a constexpr function. For example: +// +// constexpr int half(int n) { +// return +// DCHECK_CONSTEXPR(n >= 0, , 0) +// CHECK_CONSTEXPR((n & 1) == 0), +// << "Extra debugging output: n = " << n, 0) +// n / 2; +// } +#define CHECK_CONSTEXPR(x, out, dummy) \ + (UNLIKELY(!(x))) \ + ? (LOG(FATAL) << "Check failed: " << #x out, dummy) \ + : +// DCHECKs are debug variants of CHECKs only enabled in debug builds. Generally +// CHECK should be used unless profiling identifies a CHECK as being in +// performance critical code. +#if defined(NDEBUG) && !defined(__clang_analyzer__) + static constexpr bool kEnableDChecks = false; +#else + static constexpr bool kEnableDChecks = true; +#endif +#define DCHECK(x) \ + if (::android::base::kEnableDChecks) CHECK(x) +#define DCHECK_EQ(x, y) \ + if (::android::base::kEnableDChecks) CHECK_EQ(x, y) +#define DCHECK_NE(x, y) \ + if (::android::base::kEnableDChecks) CHECK_NE(x, y) +#define DCHECK_LE(x, y) \ + if (::android::base::kEnableDChecks) CHECK_LE(x, y) +#define DCHECK_LT(x, y) \ + if (::android::base::kEnableDChecks) CHECK_LT(x, y) +#define DCHECK_GE(x, y) \ + if (::android::base::kEnableDChecks) CHECK_GE(x, y) +#define DCHECK_GT(x, y) \ + if (::android::base::kEnableDChecks) CHECK_GT(x, y) +#define DCHECK_STREQ(s1, s2) \ + if (::android::base::kEnableDChecks) CHECK_STREQ(s1, s2) +#define DCHECK_STRNE(s1, s2) \ + if (::android::base::kEnableDChecks) CHECK_STRNE(s1, s2) +#if defined(NDEBUG) && !defined(__clang_analyzer__) +#define DCHECK_CONSTEXPR(x, out, dummy) +#else +#define DCHECK_CONSTEXPR(x, out, dummy) CHECK_CONSTEXPR(x, out, dummy) +#endif +// Temporary class created to evaluate the LHS and RHS, used with +// MakeEagerEvaluator to infer the types of LHS and RHS. + template + struct EagerEvaluator { + constexpr EagerEvaluator(LHS l, RHS r) : lhs(l), rhs(r) { + } + LHS lhs; + RHS rhs; + }; +// Helper function for CHECK_xx. + template + constexpr EagerEvaluator MakeEagerEvaluator(LHS lhs, RHS rhs) { + return EagerEvaluator(lhs, rhs); + } +// Explicitly instantiate EagerEvalue for pointers so that char*s aren't treated +// as strings. To compare strings use CHECK_STREQ and CHECK_STRNE. We rely on +// signed/unsigned warnings to protect you against combinations not explicitly +// listed below. +#define EAGER_PTR_EVALUATOR(T1, T2) \ + template <> \ + struct EagerEvaluator { \ + EagerEvaluator(T1 l, T2 r) \ + : lhs(reinterpret_cast(l)), \ + rhs(reinterpret_cast(r)) { \ + } \ + const void* lhs; \ + const void* rhs; \ + } + EAGER_PTR_EVALUATOR(const char*, const char*); + EAGER_PTR_EVALUATOR(const char*, char*); + EAGER_PTR_EVALUATOR(char*, const char*); + EAGER_PTR_EVALUATOR(char*, char*); + EAGER_PTR_EVALUATOR(const unsigned char*, const unsigned char*); + EAGER_PTR_EVALUATOR(const unsigned char*, unsigned char*); + EAGER_PTR_EVALUATOR(unsigned char*, const unsigned char*); + EAGER_PTR_EVALUATOR(unsigned char*, unsigned char*); + EAGER_PTR_EVALUATOR(const signed char*, const signed char*); + EAGER_PTR_EVALUATOR(const signed char*, signed char*); + EAGER_PTR_EVALUATOR(signed char*, const signed char*); + EAGER_PTR_EVALUATOR(signed char*, signed char*); +// Data for the log message, not stored in LogMessage to avoid increasing the +// stack size. + class LogMessageData; +// A LogMessage is a temporarily scoped object used by LOG and the unlikely part +// of a CHECK. The destructor will abort if the severity is FATAL. + class LogMessage { + public: + LogMessage(const char* file, unsigned int line, LogId id, LogSeverity severity, const char* tag, + int error); + ~LogMessage(); + // Returns the stream associated with the message, the LogMessage performs + // output when it goes out of scope. + std::ostream& stream(); + // The routine that performs the actual logging. + static void LogLine(const char* file, unsigned int line, LogId id, LogSeverity severity, + const char* tag, const char* msg); + private: + const std::unique_ptr data_; + DISALLOW_COPY_AND_ASSIGN(LogMessage); + }; +// Get the minimum severity level for logging. + LogSeverity GetMinimumLogSeverity(); +// Set the minimum severity level for logging, returning the old severity. + LogSeverity SetMinimumLogSeverity(LogSeverity new_severity); +// Allows to temporarily change the minimum severity level for logging. + class ScopedLogSeverity { + public: + explicit ScopedLogSeverity(LogSeverity level); + ~ScopedLogSeverity(); + private: + LogSeverity old_; + }; + } // namespace base +} // namespace android +namespace std { +// Emit a warning of ostream<< with std::string*. The intention was most likely to print *string. +// +// Note: for this to work, we need to have this in a namespace. +// Note: lots of ifdef magic to make this work with Clang (platform) vs GCC (windows tools) +// Note: using diagnose_if(true) under Clang and nothing under GCC/mingw as there is no common +// attribute support. +// Note: using a pragma because "-Wgcc-compat" (included in "-Weverything") complains about +// diagnose_if. +// Note: to print the pointer, use "<< static_cast(string_pointer)" instead. +// Note: a not-recommended alternative is to let Clang ignore the warning by adding +// -Wno-user-defined-warnings to CPPFLAGS. +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wgcc-compat" +#define OSTREAM_STRING_POINTER_USAGE_WARNING \ + __attribute__((diagnose_if(true, "Unexpected logging of string pointer", "warning"))) + inline std::ostream& operator<<(std::ostream& stream, const std::string* string_pointer) + OSTREAM_STRING_POINTER_USAGE_WARNING { + return stream << static_cast(string_pointer); + } +#pragma clang diagnostic pop +} // namespace std \ No newline at end of file diff --git a/edxp-core/src/main/cpp/external/android/android-base/macros.h b/edxp-core/src/main/cpp/external/android/android-base/macros.h new file mode 100644 index 00000000..226ce281 --- /dev/null +++ b/edxp-core/src/main/cpp/external/android/android-base/macros.h @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once +#include // for size_t +#include // for TEMP_FAILURE_RETRY +#include +// bionic and glibc both have TEMP_FAILURE_RETRY, but eg Mac OS' libc doesn't. +#ifndef TEMP_FAILURE_RETRY +#define TEMP_FAILURE_RETRY(exp) \ + ({ \ + decltype(exp) _rc; \ + do { \ + _rc = (exp); \ + } while (_rc == -1 && errno == EINTR); \ + _rc; \ + }) +#endif +// A macro to disallow the copy constructor and operator= functions +// This must be placed in the private: declarations for a class. +// +// For disallowing only assign or copy, delete the relevant operator or +// constructor, for example: +// void operator=(const TypeName&) = delete; +// Note, that most uses of DISALLOW_ASSIGN and DISALLOW_COPY are broken +// semantically, one should either use disallow both or neither. Try to +// avoid these in new code. +#define DISALLOW_COPY_AND_ASSIGN(TypeName) \ + TypeName(const TypeName&) = delete; \ + void operator=(const TypeName&) = delete +// A macro to disallow all the implicit constructors, namely the +// default constructor, copy constructor and operator= functions. +// +// This should be used in the private: declarations for a class +// that wants to prevent anyone from instantiating it. This is +// especially useful for classes containing only static methods. +#define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \ + TypeName() = delete; \ + DISALLOW_COPY_AND_ASSIGN(TypeName) +// The arraysize(arr) macro returns the # of elements in an array arr. +// The expression is a compile-time constant, and therefore can be +// used in defining new arrays, for example. If you use arraysize on +// a pointer by mistake, you will get a compile-time error. +// +// One caveat is that arraysize() doesn't accept any array of an +// anonymous type or a type defined inside a function. In these rare +// cases, you have to use the unsafe ARRAYSIZE_UNSAFE() macro below. This is +// due to a limitation in C++'s template system. The limitation might +// eventually be removed, but it hasn't happened yet. +// This template function declaration is used in defining arraysize. +// Note that the function doesn't need an implementation, as we only +// use its type. +template +char(&ArraySizeHelper(T(&array)[N]))[N]; // NOLINT(readability/casting) +#define arraysize(array) (sizeof(ArraySizeHelper(array))) +#define SIZEOF_MEMBER(t, f) sizeof(std::declval().f) +// Changing this definition will cause you a lot of pain. A majority of +// vendor code defines LIKELY and UNLIKELY this way, and includes +// this header through an indirect path. +#define LIKELY( exp ) (__builtin_expect( (exp) != 0, true )) +#define UNLIKELY( exp ) (__builtin_expect( (exp) != 0, false )) +#define WARN_UNUSED __attribute__((warn_unused_result)) +// A deprecated function to call to create a false use of the parameter, for +// example: +// int foo(int x) { UNUSED(x); return 10; } +// to avoid compiler warnings. Going forward we prefer ATTRIBUTE_UNUSED. +template +void UNUSED(const T&...) { +} +// An attribute to place on a parameter to a function, for example: +// int foo(int x ATTRIBUTE_UNUSED) { return 10; } +// to avoid compiler warnings. +#define ATTRIBUTE_UNUSED __attribute__((__unused__)) +// The FALLTHROUGH_INTENDED macro can be used to annotate implicit fall-through +// between switch labels: +// switch (x) { +// case 40: +// case 41: +// if (truth_is_out_there) { +// ++x; +// FALLTHROUGH_INTENDED; // Use instead of/along with annotations in +// // comments. +// } else { +// return x; +// } +// case 42: +// ... +// +// As shown in the example above, the FALLTHROUGH_INTENDED macro should be +// followed by a semicolon. It is designed to mimic control-flow statements +// like 'break;', so it can be placed in most places where 'break;' can, but +// only if there are no statements on the execution path between it and the +// next switch label. +// +// When compiled with clang, the FALLTHROUGH_INTENDED macro is expanded to +// [[clang::fallthrough]] attribute, which is analysed when performing switch +// labels fall-through diagnostic ('-Wimplicit-fallthrough'). See clang +// documentation on language extensions for details: +// http://clang.llvm.org/docs/LanguageExtensions.html#clang__fallthrough +// +// When used with unsupported compilers, the FALLTHROUGH_INTENDED macro has no +// effect on diagnostics. +// +// In either case this macro has no effect on runtime behavior and performance +// of code. +#ifndef FALLTHROUGH_INTENDED +#define FALLTHROUGH_INTENDED [[clang::fallthrough]] // NOLINT +#endif +// Current ABI string +#if defined(__arm__) +#define ABI_STRING "arm" +#elif defined(__aarch64__) +#define ABI_STRING "arm64" +#elif defined(__i386__) +#define ABI_STRING "x86" +#elif defined(__x86_64__) +#define ABI_STRING "x86_64" +#elif defined(__mips__) && !defined(__LP64__) +#define ABI_STRING "mips" +#elif defined(__mips__) && defined(__LP64__) +#define ABI_STRING "mips64" +#endif \ No newline at end of file diff --git a/edxp-core/src/main/cpp/external/android/android-base/off64_t.h b/edxp-core/src/main/cpp/external/android/android-base/off64_t.h new file mode 100644 index 00000000..b44d9d3d --- /dev/null +++ b/edxp-core/src/main/cpp/external/android/android-base/off64_t.h @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once +#if defined(__APPLE__) +/** Mac OS has always had a 64-bit off_t, so it doesn't have off64_t. */ +typedef off_t off64_t; +#endif \ No newline at end of file diff --git a/edxp-core/src/main/cpp/external/android/android-base/parseint.h b/edxp-core/src/main/cpp/external/android/android-base/parseint.h new file mode 100644 index 00000000..9c87330d --- /dev/null +++ b/edxp-core/src/main/cpp/external/android/android-base/parseint.h @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once +#include +#include +#include +#include +#include +#include +namespace android { + namespace base { +// Parses the unsigned decimal or hexadecimal integer in the string 's' and sets +// 'out' to that value if it is specified. Optionally allows the caller to define +// a 'max' beyond which otherwise valid values will be rejected. Returns boolean +// success; 'out' is untouched if parsing fails. + template + bool ParseUint(const char* s, T* out, T max = std::numeric_limits::max(), + bool allow_suffixes = false) { + static_assert(std::is_unsigned::value, "ParseUint can only be used with unsigned types"); + while (isspace(*s)) { + s++; + } + if (s[0] == '-') { + errno = EINVAL; + return false; + } + int base = (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) ? 16 : 10; + errno = 0; + char* end; + unsigned long long int result = strtoull(s, &end, base); + if (errno != 0) return false; + if (end == s) { + errno = EINVAL; + return false; + } + if (*end != '\0') { + const char* suffixes = "bkmgtpe"; + const char* suffix; + if ((!allow_suffixes || (suffix = strchr(suffixes, tolower(*end))) == nullptr) || + __builtin_mul_overflow(result, 1ULL << (10 * (suffix - suffixes)), &result)) { + errno = EINVAL; + return false; + } + } + if (max < result) { + errno = ERANGE; + return false; + } + if (out != nullptr) { + *out = static_cast(result); + } + return true; + } +// TODO: string_view + template + bool ParseUint(const std::string& s, T* out, T max = std::numeric_limits::max(), + bool allow_suffixes = false) { + return ParseUint(s.c_str(), out, max, allow_suffixes); + } + template + bool ParseByteCount(const char* s, T* out, T max = std::numeric_limits::max()) { + return ParseUint(s, out, max, true); + } +// TODO: string_view + template + bool ParseByteCount(const std::string& s, T* out, T max = std::numeric_limits::max()) { + return ParseByteCount(s.c_str(), out, max); + } +// Parses the signed decimal or hexadecimal integer in the string 's' and sets +// 'out' to that value if it is specified. Optionally allows the caller to define +// a 'min' and 'max' beyond which otherwise valid values will be rejected. Returns +// boolean success; 'out' is untouched if parsing fails. + template + bool ParseInt(const char* s, T* out, + T min = std::numeric_limits::min(), + T max = std::numeric_limits::max()) { + static_assert(std::is_signed::value, "ParseInt can only be used with signed types"); + while (isspace(*s)) { + s++; + } + int base = (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) ? 16 : 10; + errno = 0; + char* end; + long long int result = strtoll(s, &end, base); + if (errno != 0) { + return false; + } + if (s == end || *end != '\0') { + errno = EINVAL; + return false; + } + if (result < min || max < result) { + errno = ERANGE; + return false; + } + if (out != nullptr) { + *out = static_cast(result); + } + return true; + } +// TODO: string_view + template + bool ParseInt(const std::string& s, T* out, + T min = std::numeric_limits::min(), + T max = std::numeric_limits::max()) { + return ParseInt(s.c_str(), out, min, max); + } + } // namespace base +} // namespace android \ No newline at end of file diff --git a/edxp-core/src/main/cpp/external/android/android-base/strings.cpp b/edxp-core/src/main/cpp/external/android/android-base/strings.cpp new file mode 100644 index 00000000..50d6364b --- /dev/null +++ b/edxp-core/src/main/cpp/external/android/android-base/strings.cpp @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "android-base/strings.h" +#include +#include +#include +#include +namespace android { + namespace base { +#define CHECK_NE(a, b) \ + if ((a) == (b)) abort(); + std::vector Split(const std::string& s, + const std::string& delimiters) { + CHECK_NE(delimiters.size(), 0U); + std::vector result; + size_t base = 0; + size_t found; + while (true) { + found = s.find_first_of(delimiters, base); + result.push_back(s.substr(base, found - base)); + if (found == s.npos) break; + base = found + 1; + } + return result; + } + std::string Trim(const std::string& s) { + std::string result; + if (s.size() == 0) { + return result; + } + size_t start_index = 0; + size_t end_index = s.size() - 1; + // Skip initial whitespace. + while (start_index < s.size()) { + if (!isspace(s[start_index])) { + break; + } + start_index++; + } + // Skip terminating whitespace. + while (end_index >= start_index) { + if (!isspace(s[end_index])) { + break; + } + end_index--; + } + // All spaces, no beef. + if (end_index < start_index) { + return ""; + } + // Start_index is the first non-space, end_index is the last one. + return s.substr(start_index, end_index - start_index + 1); + } +// These cases are probably the norm, so we mark them extern in the header to +// aid compile time and binary size. + template std::string Join(const std::vector&, char); + template std::string Join(const std::vector&, char); + template std::string Join(const std::vector&, const std::string&); + template std::string Join(const std::vector&, const std::string&); + bool StartsWith(std::string_view s, std::string_view prefix) { + return s.substr(0, prefix.size()) == prefix; + } + bool StartsWith(std::string_view s, char prefix) { + return !s.empty() && s.front() == prefix; + } + bool StartsWithIgnoreCase(std::string_view s, std::string_view prefix) { + return s.size() >= prefix.size() && strncasecmp(s.data(), prefix.data(), prefix.size()) == 0; + } + bool EndsWith(std::string_view s, std::string_view suffix) { + return s.size() >= suffix.size() && s.substr(s.size() - suffix.size(), suffix.size()) == suffix; + } + bool EndsWith(std::string_view s, char suffix) { + return !s.empty() && s.back() == suffix; + } + bool EndsWithIgnoreCase(std::string_view s, std::string_view suffix) { + return s.size() >= suffix.size() && + strncasecmp(s.data() + (s.size() - suffix.size()), suffix.data(), suffix.size()) == 0; + } + bool EqualsIgnoreCase(std::string_view lhs, std::string_view rhs) { + return lhs.size() == rhs.size() && strncasecmp(lhs.data(), rhs.data(), lhs.size()) == 0; + } + } // namespace base +} // namespace android \ No newline at end of file diff --git a/edxp-core/src/main/cpp/external/android/android-base/strings.h b/edxp-core/src/main/cpp/external/android/android-base/strings.h new file mode 100644 index 00000000..b3fac66c --- /dev/null +++ b/edxp-core/src/main/cpp/external/android/android-base/strings.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once +#include +#include +#include +#include +namespace android { + namespace base { +// Splits a string into a vector of strings. +// +// The string is split at each occurrence of a character in delimiters. +// +// The empty string is not a valid delimiter list. + std::vector Split(const std::string& s, + const std::string& delimiters); +// Trims whitespace off both ends of the given string. + std::string Trim(const std::string& s); +// Joins a container of things into a single string, using the given separator. + template + std::string Join(const ContainerT& things, SeparatorT separator) { + if (things.empty()) { + return ""; + } + std::ostringstream result; + result << *things.begin(); + for (auto it = std::next(things.begin()); it != things.end(); ++it) { + result << separator << *it; + } + return result.str(); + } +// We instantiate the common cases in strings.cpp. + extern template std::string Join(const std::vector&, char); + extern template std::string Join(const std::vector&, char); + extern template std::string Join(const std::vector&, const std::string&); + extern template std::string Join(const std::vector&, const std::string&); +// Tests whether 's' starts with 'prefix'. + bool StartsWith(std::string_view s, std::string_view prefix); + bool StartsWith(std::string_view s, char prefix); + bool StartsWithIgnoreCase(std::string_view s, std::string_view prefix); +// Tests whether 's' ends with 'suffix'. + bool EndsWith(std::string_view s, std::string_view suffix); + bool EndsWith(std::string_view s, char suffix); + bool EndsWithIgnoreCase(std::string_view s, std::string_view suffix); +// Tests whether 'lhs' equals 'rhs', ignoring case. + bool EqualsIgnoreCase(std::string_view lhs, std::string_view rhs); +// Removes `prefix` from the start of the given string and returns true (if +// it was present), false otherwise. + inline bool ConsumePrefix(std::string_view* s, std::string_view prefix) { + if (!StartsWith(*s, prefix)) return false; + s->remove_prefix(prefix.size()); + return true; + } +// Removes `suffix` from the end of the given string and returns true (if +// it was present), false otherwise. + inline bool ConsumeSuffix(std::string_view* s, std::string_view suffix) { + if (!EndsWith(*s, suffix)) return false; + s->remove_suffix(suffix.size()); + return true; + } + } // namespace base +} // namespace android \ No newline at end of file diff --git a/edxp-core/src/main/cpp/external/android/android-base/thread_annotations.h b/edxp-core/src/main/cpp/external/android/android-base/thread_annotations.h new file mode 100644 index 00000000..5491be23 --- /dev/null +++ b/edxp-core/src/main/cpp/external/android/android-base/thread_annotations.h @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once +#include +#define THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x)) +#define CAPABILITY(x) \ + THREAD_ANNOTATION_ATTRIBUTE__(capability(x)) +#define SCOPED_CAPABILITY \ + THREAD_ANNOTATION_ATTRIBUTE__(scoped_lockable) +#define SHARED_CAPABILITY(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(shared_capability(__VA_ARGS__)) +#define GUARDED_BY(x) \ + THREAD_ANNOTATION_ATTRIBUTE__(guarded_by(x)) +#define PT_GUARDED_BY(x) \ + THREAD_ANNOTATION_ATTRIBUTE__(pt_guarded_by(x)) +#define EXCLUSIVE_LOCKS_REQUIRED(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(exclusive_locks_required(__VA_ARGS__)) +#define SHARED_LOCKS_REQUIRED(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(shared_locks_required(__VA_ARGS__)) +#define ACQUIRED_BEFORE(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(acquired_before(__VA_ARGS__)) +#define ACQUIRED_AFTER(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(acquired_after(__VA_ARGS__)) +#define REQUIRES(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(requires_capability(__VA_ARGS__)) +#define REQUIRES_SHARED(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(requires_shared_capability(__VA_ARGS__)) +#define ACQUIRE(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(acquire_capability(__VA_ARGS__)) +#define ACQUIRE_SHARED(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(acquire_shared_capability(__VA_ARGS__)) +#define RELEASE(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(release_capability(__VA_ARGS__)) +#define RELEASE_SHARED(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(release_shared_capability(__VA_ARGS__)) +#define TRY_ACQUIRE(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(try_acquire_capability(__VA_ARGS__)) +#define TRY_ACQUIRE_SHARED(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(try_acquire_shared_capability(__VA_ARGS__)) +#define EXCLUDES(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(locks_excluded(__VA_ARGS__)) +#define ASSERT_CAPABILITY(x) \ + THREAD_ANNOTATION_ATTRIBUTE__(assert_capability(x)) +#define ASSERT_SHARED_CAPABILITY(x) \ + THREAD_ANNOTATION_ATTRIBUTE__(assert_shared_capability(x)) +#define RETURN_CAPABILITY(x) \ + THREAD_ANNOTATION_ATTRIBUTE__(lock_returned(x)) +#define EXCLUSIVE_LOCK_FUNCTION(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(exclusive_lock_function(__VA_ARGS__)) +#define EXCLUSIVE_TRYLOCK_FUNCTION(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(exclusive_trylock_function(__VA_ARGS__)) +#define SHARED_LOCK_FUNCTION(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(shared_lock_function(__VA_ARGS__)) +#define SHARED_TRYLOCK_FUNCTION(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(shared_trylock_function(__VA_ARGS__)) +#define UNLOCK_FUNCTION(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(unlock_function(__VA_ARGS__)) +#define SCOPED_LOCKABLE \ + THREAD_ANNOTATION_ATTRIBUTE__(scoped_lockable) +#define LOCK_RETURNED(x) \ + THREAD_ANNOTATION_ATTRIBUTE__(lock_returned(x)) +#define NO_THREAD_SAFETY_ANALYSIS \ + THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis) +namespace android { + namespace base { +// A class to help thread safety analysis deal with std::unique_lock and condition_variable. +// +// Clang's thread safety analysis currently doesn't perform alias analysis, so movable types +// like std::unique_lock can't be marked with thread safety annotations. This helper allows +// for manual assertion of lock state in a scope. +// +// For example: +// +// std::mutex mutex; +// std::condition_variable cv; +// std::vector vec GUARDED_BY(mutex); +// +// int pop() { +// std::unique_lock lock(mutex); +// ScopedLockAssertion lock_assertion(mutex); +// cv.wait(lock, []() { +// ScopedLockAssertion lock_assertion(mutex); +// return !vec.empty(); +// }); +// +// int result = vec.back(); +// vec.pop_back(); +// return result; +// } + class SCOPED_CAPABILITY ScopedLockAssertion { + public: + ScopedLockAssertion(std::mutex& mutex) ACQUIRE(mutex) {} + ~ScopedLockAssertion() RELEASE() {} + }; + } // namespace base +} // namespace android \ No newline at end of file diff --git a/edxp-core/src/main/cpp/external/android/android-base/threads.cpp b/edxp-core/src/main/cpp/external/android/android-base/threads.cpp new file mode 100644 index 00000000..b9da6178 --- /dev/null +++ b/edxp-core/src/main/cpp/external/android/android-base/threads.cpp @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include +#if defined(__APPLE__) +#include +#elif defined(__linux__) && !defined(__ANDROID__) +#include +#elif defined(_WIN32) +#include +#endif +namespace android { + namespace base { + uint64_t GetThreadId() { +#if defined(__BIONIC__) + return gettid(); +#elif defined(__APPLE__) + uint64_t tid; + pthread_threadid_np(NULL, &tid); + return tid; +#elif defined(__linux__) + return syscall(__NR_gettid); +#elif defined(_WIN32) + return GetCurrentThreadId(); +#endif + } + } // namespace base +} // namespace android +#if defined(__GLIBC__) +int tgkill(int tgid, int tid, int sig) { + return syscall(__NR_tgkill, tgid, tid, sig); +} +#endif \ No newline at end of file diff --git a/edxp-core/src/main/cpp/external/android/android-base/threads.h b/edxp-core/src/main/cpp/external/android/android-base/threads.h new file mode 100644 index 00000000..212e127e --- /dev/null +++ b/edxp-core/src/main/cpp/external/android/android-base/threads.h @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once +#include +namespace android { + namespace base { + uint64_t GetThreadId(); + } +} // namespace android +#if defined(__GLIBC__) +// bionic has this Linux-specifix call, but glibc doesn't. +extern "C" int tgkill(int tgid, int tid, int sig); +#endif \ No newline at end of file diff --git a/edxp-core/src/main/cpp/external/android/android-base/unique_fd.h b/edxp-core/src/main/cpp/external/android/android-base/unique_fd.h new file mode 100644 index 00000000..4fcf051b --- /dev/null +++ b/edxp-core/src/main/cpp/external/android/android-base/unique_fd.h @@ -0,0 +1,244 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once +#include +#include +#include +#if !defined(_WIN32) +#include +#endif +#include +#include +#include +// DO NOT INCLUDE OTHER LIBBASE HEADERS! +// This file gets used in libbinder, and libbinder is used everywhere. +// Including other headers from libbase frequently results in inclusion of +// android-base/macros.h, which causes macro collisions. +// Container for a file descriptor that automatically closes the descriptor as +// it goes out of scope. +// +// unique_fd ufd(open("/some/path", "r")); +// if (ufd.get() == -1) return error; +// +// // Do something useful, possibly including 'return'. +// +// return 0; // Descriptor is closed for you. +// +// unique_fd is also known as ScopedFd/ScopedFD/scoped_fd; mentioned here to help +// you find this class if you're searching for one of those names. +#if defined(__BIONIC__) +#include +#endif +namespace android { + namespace base { + struct DefaultCloser { +#if defined(__BIONIC__) + static void Tag(int fd, void* old_addr, void* new_addr) { + if (android_fdsan_exchange_owner_tag) { + uint64_t old_tag = android_fdsan_create_owner_tag(ANDROID_FDSAN_OWNER_TYPE_UNIQUE_FD, + reinterpret_cast(old_addr)); + uint64_t new_tag = android_fdsan_create_owner_tag(ANDROID_FDSAN_OWNER_TYPE_UNIQUE_FD, + reinterpret_cast(new_addr)); + android_fdsan_exchange_owner_tag(fd, old_tag, new_tag); + } + } + static void Close(int fd, void* addr) { + if (android_fdsan_close_with_tag) { + uint64_t tag = android_fdsan_create_owner_tag(ANDROID_FDSAN_OWNER_TYPE_UNIQUE_FD, + reinterpret_cast(addr)); + android_fdsan_close_with_tag(fd, tag); + } else { + close(fd); + } + } +#else + static void Close(int fd) { + // Even if close(2) fails with EINTR, the fd will have been closed. + // Using TEMP_FAILURE_RETRY will either lead to EBADF or closing someone + // else's fd. + // http://lkml.indiana.edu/hypermail/linux/kernel/0509.1/0877.html + ::close(fd); + } +#endif + }; + template + class unique_fd_impl final { + public: + unique_fd_impl() {} + explicit unique_fd_impl(int fd) { reset(fd); } + ~unique_fd_impl() { reset(); } + unique_fd_impl(unique_fd_impl&& other) noexcept { reset(other.release()); } + unique_fd_impl& operator=(unique_fd_impl&& s) noexcept { + int fd = s.fd_; + s.fd_ = -1; + reset(fd, &s); + return *this; + } + void reset(int new_value = -1) { reset(new_value, nullptr); } + int get() const { return fd_; } +#if !defined(ANDROID_BASE_UNIQUE_FD_DISABLE_IMPLICIT_CONVERSION) + // unique_fd's operator int is dangerous, but we have way too much code that + // depends on it, so make this opt-in at first. + operator int() const { return get(); } // NOLINT +#endif + bool operator>=(int rhs) const { return get() >= rhs; } + bool operator<(int rhs) const { return get() < rhs; } + bool operator==(int rhs) const { return get() == rhs; } + bool operator!=(int rhs) const { return get() != rhs; } + // Catch bogus error checks (i.e.: "!fd" instead of "fd != -1"). + bool operator!() const = delete; + int release() __attribute__((warn_unused_result)) { + tag(fd_, this, nullptr); + int ret = fd_; + fd_ = -1; + return ret; + } + private: + void reset(int new_value, void* previous_tag) { + int previous_errno = errno; + if (fd_ != -1) { + close(fd_, this); + } + fd_ = new_value; + if (new_value != -1) { + tag(new_value, previous_tag, this); + } + errno = previous_errno; + } + int fd_ = -1; + // Template magic to use Closer::Tag if available, and do nothing if not. + // If Closer::Tag exists, this implementation is preferred, because int is a better match. + // If not, this implementation is SFINAEd away, and the no-op below is the only one that exists. + template + static auto tag(int fd, void* old_tag, void* new_tag) + -> decltype(T::Tag(fd, old_tag, new_tag), void()) { + T::Tag(fd, old_tag, new_tag); + } + template + static void tag(long, void*, void*) { + // No-op. + } + // Same as above, to select between Closer::Close(int) and Closer::Close(int, void*). + template + static auto close(int fd, void* tag_value) -> decltype(T::Close(fd, tag_value), void()) { + T::Close(fd, tag_value); + } + template + static auto close(int fd, void*) -> decltype(T::Close(fd), void()) { + T::Close(fd); + } + unique_fd_impl(const unique_fd_impl&); + void operator=(const unique_fd_impl&); + }; + using unique_fd = unique_fd_impl; +#if !defined(_WIN32) +// Inline functions, so that they can be used header-only. + template + inline bool Pipe(unique_fd_impl* read, unique_fd_impl* write, + int flags = O_CLOEXEC) { + int pipefd[2]; +#if defined(__linux__) + if (pipe2(pipefd, flags) != 0) { + return false; + } +#else // defined(__APPLE__) + if (flags & ~(O_CLOEXEC | O_NONBLOCK)) { + return false; + } + if (pipe(pipefd) != 0) { + return false; + } + if (flags & O_CLOEXEC) { + if (fcntl(pipefd[0], F_SETFD, FD_CLOEXEC) != 0 || fcntl(pipefd[1], F_SETFD, FD_CLOEXEC) != 0) { + close(pipefd[0]); + close(pipefd[1]); + return false; + } + } + if (flags & O_NONBLOCK) { + if (fcntl(pipefd[0], F_SETFL, O_NONBLOCK) != 0 || fcntl(pipefd[1], F_SETFL, O_NONBLOCK) != 0) { + close(pipefd[0]); + close(pipefd[1]); + return false; + } + } +#endif + read->reset(pipefd[0]); + write->reset(pipefd[1]); + return true; + } + template + inline bool Socketpair(int domain, int type, int protocol, unique_fd_impl* left, + unique_fd_impl* right) { + int sockfd[2]; + if (socketpair(domain, type, protocol, sockfd) != 0) { + return false; + } + left->reset(sockfd[0]); + right->reset(sockfd[1]); + return true; + } + template + inline bool Socketpair(int type, unique_fd_impl* left, unique_fd_impl* right) { + return Socketpair(AF_UNIX, type, 0, left, right); + } +// Using fdopen with unique_fd correctly is more annoying than it should be, +// because fdopen doesn't close the file descriptor received upon failure. + inline FILE* Fdopen(unique_fd&& ufd, const char* mode) { + int fd = ufd.release(); + FILE* file = fdopen(fd, mode); + if (!file) { + close(fd); + } + return file; + } +// Using fdopendir with unique_fd correctly is more annoying than it should be, +// because fdopen doesn't close the file descriptor received upon failure. + inline DIR* Fdopendir(unique_fd&& ufd) { + int fd = ufd.release(); + DIR* dir = fdopendir(fd); + if (dir == nullptr) { + close(fd); + } + return dir; + } +#endif // !defined(_WIN32) +// A wrapper type that can be implicitly constructed from either int or unique_fd. + struct borrowed_fd { + /* implicit */ borrowed_fd(int fd) : fd_(fd) {} + template + /* implicit */ borrowed_fd(const unique_fd_impl& ufd) : fd_(ufd.get()) {} + int get() const { return fd_; } + bool operator>=(int rhs) const { return get() >= rhs; } + bool operator<(int rhs) const { return get() < rhs; } + bool operator==(int rhs) const { return get() == rhs; } + bool operator!=(int rhs) const { return get() != rhs; } + private: + int fd_ = -1; + }; + } // namespace base +} // namespace android +template +int close(const android::base::unique_fd_impl&) +__attribute__((__unavailable__("close called on unique_fd"))); +template +FILE* fdopen(const android::base::unique_fd_impl&, const char* mode) +__attribute__((__unavailable__("fdopen takes ownership of the fd passed in; either dup the " +"unique_fd, or use android::base::Fdopen to pass ownership"))); +template +DIR* fdopendir(const android::base::unique_fd_impl&) __attribute__(( +__unavailable__("fdopendir takes ownership of the fd passed in; either dup the " +"unique_fd, or use android::base::Fdopendir to pass ownership"))); \ No newline at end of file diff --git a/edxp-core/src/main/cpp/external/android/android-base/utf8.h b/edxp-core/src/main/cpp/external/android/android-base/utf8.h new file mode 100644 index 00000000..d85560b1 --- /dev/null +++ b/edxp-core/src/main/cpp/external/android/android-base/utf8.h @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once +#ifdef _WIN32 +#include +#include +#else +// Bring in prototypes for standard APIs so that we can import them into the utf8 namespace. +#include // open +#include // fopen +#include // mkdir +#include // unlink +#endif +namespace android { + namespace base { +// Only available on Windows because this is only needed on Windows. +#ifdef _WIN32 + // Convert size number of UTF-16 wchar_t's to UTF-8. Returns whether the +// conversion was done successfully. +bool WideToUTF8(const wchar_t* utf16, const size_t size, std::string* utf8); +// Convert a NULL-terminated string of UTF-16 characters to UTF-8. Returns +// whether the conversion was done successfully. +bool WideToUTF8(const wchar_t* utf16, std::string* utf8); +// Convert a UTF-16 std::wstring (including any embedded NULL characters) to +// UTF-8. Returns whether the conversion was done successfully. +bool WideToUTF8(const std::wstring& utf16, std::string* utf8); +// Convert size number of UTF-8 char's to UTF-16. Returns whether the conversion +// was done successfully. +bool UTF8ToWide(const char* utf8, const size_t size, std::wstring* utf16); +// Convert a NULL-terminated string of UTF-8 characters to UTF-16. Returns +// whether the conversion was done successfully. +bool UTF8ToWide(const char* utf8, std::wstring* utf16); +// Convert a UTF-8 std::string (including any embedded NULL characters) to +// UTF-16. Returns whether the conversion was done successfully. +bool UTF8ToWide(const std::string& utf8, std::wstring* utf16); +// Convert a file system path, represented as a NULL-terminated string of +// UTF-8 characters, to a UTF-16 string representing the same file system +// path using the Windows extended-lengh path representation. +// +// See https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx#MAXPATH: +// ```The Windows API has many functions that also have Unicode versions to +// permit an extended-length path for a maximum total path length of 32,767 +// characters. To specify an extended-length path, use the "\\?\" prefix. +// For example, "\\?\D:\very long path".``` +// +// Returns whether the conversion was done successfully. +bool UTF8PathToWindowsLongPath(const char* utf8, std::wstring* utf16); +#endif +// The functions in the utf8 namespace take UTF-8 strings. For Windows, these +// are wrappers, for non-Windows these just expose existing APIs. To call these +// functions, use: +// +// // anonymous namespace to avoid conflict with existing open(), unlink(), etc. +// namespace { +// // Import functions into anonymous namespace. +// using namespace android::base::utf8; +// +// void SomeFunction(const char* name) { +// int fd = open(name, ...); // Calls android::base::utf8::open(). +// ... +// unlink(name); // Calls android::base::utf8::unlink(). +// } +// } + namespace utf8 { +#ifdef _WIN32 + FILE* fopen(const char* name, const char* mode); +int mkdir(const char* name, mode_t mode); +int open(const char* name, int flags, ...); +int unlink(const char* name); +#else + using ::fopen; + using ::mkdir; + using ::open; + using ::unlink; +#endif + } // namespace utf8 + } // namespace base +} // namespace android \ No newline at end of file diff --git a/edxp-core/src/main/cpp/external/android/android/fdsan.h b/edxp-core/src/main/cpp/external/android/android/fdsan.h new file mode 100644 index 00000000..a1376a3d --- /dev/null +++ b/edxp-core/src/main/cpp/external/android/android/fdsan.h @@ -0,0 +1,170 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#pragma once +#include +#include +#include +__BEGIN_DECLS +/* + * Error checking for close(2). + * + * Mishandling of file descriptor ownership is a common source of errors that + * can be extremely difficult to diagnose. Mistakes like the following can + * result in seemingly 'impossible' failures showing up on other threads that + * happened to try to open a file descriptor between the buggy code's close and + * fclose: + * + * int print(int fd) { + * int rc; + * char buf[128]; + * while ((rc = read(fd, buf, sizeof(buf))) > 0) { + * printf("%.*s", rc); + * } + * close(fd); + * } + * + * int bug() { + * FILE* f = fopen("foo", "r"); + * print(fileno(f)); + * fclose(f); + * } + * + * To make it easier to find this class of bugs, bionic provides a method to + * require that file descriptors are closed by their owners. File descriptors + * can be associated with tags with which they must be closed. This allows + * objects that conceptually own an fd (FILE*, unique_fd, etc.) to use their + * own address at the tag, to enforce that closure of the fd must come as a + * result of their own destruction (fclose, ~unique_fd, etc.) + * + * By default, a file descriptor's tag is 0, and close(fd) is equivalent to + * closing fd with the tag 0. + */ +/* + * For improved diagnostics, the type of a file descriptors owner can be + * encoded in the most significant byte of the owner tag. Values of 0 and 0xff + * are ignored, which allows for raw pointers to be used as owner tags without + * modification. + */ +enum android_fdsan_owner_type { + /* + * Generic Java or native owners. + * + * Generic Java objects always use 255 as their type, using identityHashCode + * as the value of the tag, leaving bits 33-56 unset. Native pointers are sign + * extended from 48-bits of virtual address space, and so can have the MSB + * set to 255 as well. Use the value of bits 49-56 to distinguish between + * these cases. + */ + ANDROID_FDSAN_OWNER_TYPE_GENERIC_00 = 0, + ANDROID_FDSAN_OWNER_TYPE_GENERIC_FF = 255, + /* FILE* */ + ANDROID_FDSAN_OWNER_TYPE_FILE = 1, + /* DIR* */ + ANDROID_FDSAN_OWNER_TYPE_DIR = 2, + /* android::base::unique_fd */ + ANDROID_FDSAN_OWNER_TYPE_UNIQUE_FD = 3, + /* sqlite-owned file descriptors */ + ANDROID_FDSAN_OWNER_TYPE_SQLITE = 4, + /* java.io.FileInputStream */ + ANDROID_FDSAN_OWNER_TYPE_FILEINPUTSTREAM = 5, + /* java.io.FileOutputStream */ + ANDROID_FDSAN_OWNER_TYPE_FILEOUTPUTSTREAM = 6, + /* java.io.RandomAccessFile */ + ANDROID_FDSAN_OWNER_TYPE_RANDOMACCESSFILE = 7, + /* android.os.ParcelFileDescriptor */ + ANDROID_FDSAN_OWNER_TYPE_PARCELFILEDESCRIPTOR = 8, + /* ART FdFile */ + ANDROID_FDSAN_OWNER_TYPE_ART_FDFILE = 9, + /* java.net.DatagramSocketImpl */ + ANDROID_FDSAN_OWNER_TYPE_DATAGRAMSOCKETIMPL = 10, + /* java.net.SocketImpl */ + ANDROID_FDSAN_OWNER_TYPE_SOCKETIMPL = 11, + /* libziparchive's ZipArchive */ + ANDROID_FDSAN_OWNER_TYPE_ZIPARCHIVE = 12, +}; +/* + * Create an owner tag with the specified type and least significant 56 bits of tag. + */ +uint64_t android_fdsan_create_owner_tag(enum android_fdsan_owner_type type, uint64_t tag) __INTRODUCED_IN(29) __attribute__((__weak__)); +/* + * Exchange a file descriptor's tag. + * + * Logs and aborts if the fd's tag does not match expected_tag. + */ +void android_fdsan_exchange_owner_tag(int fd, uint64_t expected_tag, uint64_t new_tag) __INTRODUCED_IN(29) __attribute__((__weak__)); +/* + * Close a file descriptor with a tag, and resets the tag to 0. + * + * Logs and aborts if the tag is incorrect. + */ +int android_fdsan_close_with_tag(int fd, uint64_t tag) __INTRODUCED_IN(29) __attribute__((__weak__)); +/* + * Get a file descriptor's current owner tag. + * + * Returns 0 for untagged and invalid file descriptors. + */ +uint64_t android_fdsan_get_owner_tag(int fd) __INTRODUCED_IN(29); +/* + * Get an owner tag's string representation. + * + * The return value points to memory with static lifetime, do not attempt to modify it. + */ +const char* android_fdsan_get_tag_type(uint64_t tag) __INTRODUCED_IN(29); +/* + * Get an owner tag's value, with the type masked off. + */ +uint64_t android_fdsan_get_tag_value(uint64_t tag) __INTRODUCED_IN(29); +enum android_fdsan_error_level { + // No errors. + ANDROID_FDSAN_ERROR_LEVEL_DISABLED, + // Warn once(ish) on error, and then downgrade to ANDROID_FDSAN_ERROR_LEVEL_DISABLED. + ANDROID_FDSAN_ERROR_LEVEL_WARN_ONCE, + // Warn always on error. + ANDROID_FDSAN_ERROR_LEVEL_WARN_ALWAYS, + // Abort on error. + ANDROID_FDSAN_ERROR_LEVEL_FATAL, +}; +/* + * Get the error level. + */ +enum android_fdsan_error_level android_fdsan_get_error_level() __INTRODUCED_IN(29) __attribute__((__weak__)); +/* + * Set the error level and return the previous state. + * + * Error checking is automatically disabled in the child of a fork, to maintain + * compatibility with code that forks, blindly closes FDs, and then execs. + * + * In cases such as the zygote, where the child has no intention of calling + * exec, call this function to reenable fdsan checks. + * + * This function is not thread-safe and does not synchronize with checks of the + * value, and so should probably only be called in single-threaded contexts + * (e.g. postfork). + */ +enum android_fdsan_error_level android_fdsan_set_error_level(enum android_fdsan_error_level new_level) __INTRODUCED_IN(29) __attribute__((__weak__)); +__END_DECLS \ No newline at end of file diff --git a/edxp-core/src/main/cpp/external/android/art/base/macros.h b/edxp-core/src/main/cpp/external/android/art/base/macros.h new file mode 100644 index 00000000..ebb5abb5 --- /dev/null +++ b/edxp-core/src/main/cpp/external/android/art/base/macros.h @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once +#include // for size_t +#include // for TEMP_FAILURE_RETRY +#include "android-base/macros.h" +#include "android-base/thread_annotations.h" +// Declare a friend relationship in a class with a test. Used rather that FRIEND_TEST to avoid +// globally importing gtest/gtest.h into the main ART header files. +#define ART_FRIEND_TEST(test_set_name, individual_test)\ +friend class test_set_name##_##individual_test##_Test +// Declare a friend relationship in a class with a typed test. +#define ART_FRIEND_TYPED_TEST(test_set_name, individual_test)\ +template ART_FRIEND_TEST(test_set_name, individual_test) +// A macro to disallow new and delete operators for a class. It goes in the private: declarations. +// NOTE: Providing placement new (and matching delete) for constructing container elements. +#define DISALLOW_ALLOCATION() \ + public: \ + NO_RETURN ALWAYS_INLINE void operator delete(void*, size_t) { UNREACHABLE(); } \ + ALWAYS_INLINE void* operator new(size_t, void* ptr) noexcept { return ptr; } \ + ALWAYS_INLINE void operator delete(void*, void*) noexcept { } \ + private: \ + void* operator new(size_t) = delete // NOLINT +// offsetof is not defined by the spec on types with non-standard layout, +// however it is implemented by compilers in practice. +// (note that reinterpret_cast is not valid constexpr) +// +// Alternative approach would be something like: +// #define OFFSETOF_HELPER(t, f) \ +// (reinterpret_cast(&reinterpret_cast(16)->f) - static_cast(16u)) +// #define OFFSETOF_MEMBER(t, f) \ +// (__builtin_constant_p(OFFSETOF_HELPER(t,f)) ? OFFSETOF_HELPER(t,f) : OFFSETOF_HELPER(t,f)) +#define OFFSETOF_MEMBER(t, f) offsetof(t, f) +#define OFFSETOF_MEMBERPTR(t, f) \ + (reinterpret_cast(&(reinterpret_cast(16)->*f)) - static_cast(16)) // NOLINT +#define ALIGNED(x) __attribute__ ((__aligned__(x))) +#define PACKED(x) __attribute__ ((__aligned__(x), __packed__)) +// Stringify the argument. +#define QUOTE(x) #x +#define STRINGIFY(x) QUOTE(x) +// Append tokens after evaluating. +#define APPEND_TOKENS_AFTER_EVAL_2(a, b) a ## b +#define APPEND_TOKENS_AFTER_EVAL(a, b) APPEND_TOKENS_AFTER_EVAL_2(a, b) +#ifndef NDEBUG +#define ALWAYS_INLINE +#else +#define ALWAYS_INLINE __attribute__ ((always_inline)) +#endif +// clang doesn't like attributes on lambda functions. It would be nice to say: +// #define ALWAYS_INLINE_LAMBDA ALWAYS_INLINE +#define ALWAYS_INLINE_LAMBDA +#define NO_INLINE __attribute__ ((noinline)) +#if defined (__APPLE__) +#define HOT_ATTR +#define COLD_ATTR +#else +#define HOT_ATTR __attribute__ ((hot)) +#define COLD_ATTR __attribute__ ((cold)) +#endif +#define PURE __attribute__ ((__pure__)) +// Define that a position within code is unreachable, for example: +// int foo () { LOG(FATAL) << "Don't call me"; UNREACHABLE(); } +// without the UNREACHABLE a return statement would be necessary. +#define UNREACHABLE __builtin_unreachable +// Add the C++11 noreturn attribute. +#define NO_RETURN [[ noreturn ]] // NOLINT[whitespace/braces] [5] +// Annotalysis thread-safety analysis support. Things that are not in base. +#define LOCKABLE CAPABILITY("mutex") +#define SHARED_LOCKABLE SHARED_CAPABILITY("mutex") \ No newline at end of file diff --git a/edxp-core/src/main/cpp/external/android/art/runtime/native/native_util.h b/edxp-core/src/main/cpp/external/android/art/runtime/native/native_util.h new file mode 100644 index 00000000..63ab54d4 --- /dev/null +++ b/edxp-core/src/main/cpp/external/android/art/runtime/native/native_util.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include "android-base/logging.h" +#include "nativehelper/scoped_local_ref.h" +#include "art/base/macros.h" + +namespace art { + ALWAYS_INLINE inline void RegisterNativeMethodsWithClass(JNIEnv* env, + jclass clazz, + const JNINativeMethod* methods, + jint method_count) { + ScopedLocalRef c(env, clazz); + if (clazz == nullptr) { + LOG(ERROR) << "clazz is null"; + return; + } + jint jni_result = env->RegisterNatives(c.get(), methods, method_count); + CHECK_EQ(JNI_OK, jni_result); + } + ALWAYS_INLINE inline void RegisterNativeMethodsWithName(JNIEnv* env, + const char* jni_class_name, + const JNINativeMethod* methods, + jint method_count) { + ScopedLocalRef clazz(env, env->FindClass(jni_class_name)); + if (clazz.get() == nullptr) { + LOG(FATAL) << "Couldn't find class: " << jni_class_name; + return; + } + RegisterNativeMethodsWithClass(env, clazz.get(), methods, method_count); + } +#define REGISTER_NATIVE_METHODS(jni_class_name) \ + RegisterNativeMethodsWithName(env, (jni_class_name), gMethods, arraysize(gMethods)) + +#define REGISTER_NATIVE_METHODS_WITH_CLASS(clazz) \ + RegisterNativeMethodsWithClass(env, (clazz), gMethods, arraysize(gMethods)) +} // namespace art \ No newline at end of file diff --git a/edxp-core/src/main/cpp/external/android/nativehelper/jni_macros.h b/edxp-core/src/main/cpp/external/android/nativehelper/jni_macros.h new file mode 100644 index 00000000..f2168c9f --- /dev/null +++ b/edxp-core/src/main/cpp/external/android/nativehelper/jni_macros.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * JNI helper macros. + * + * Only intended to be used in the platform. + */ +#pragma once +// Intended to construct a JNINativeMethod. +// (Assumes the C name is the ClassName_JavaMethodName). +#ifndef NATIVE_METHOD +#define NATIVE_METHOD(className, functionName, signature) \ + { #functionName, \ + signature, \ + _NATIVEHELPER_JNI_MACRO_CAST(void*) (className ## _ ## functionName) \ + } +#endif +// Intended to construct a JNINativeMethod (when the C name doesn't match the Java name). +// (Assumes the C name is the ClassName_Identifier). +#ifndef OVERLOADED_NATIVE_METHOD +#define OVERLOADED_NATIVE_METHOD(className, functionName, signature, identifier) \ + { #functionName, \ + signature, \ + _NATIVEHELPER_JNI_MACRO_CAST(void*) (className ## _ ## identifier) \ + } +#endif +// Used for methods that are annotated with @FastNative on the managed side. +// See NATIVE_METHOD for usage. +#ifndef FAST_NATIVE_METHOD +#define FAST_NATIVE_METHOD(className, functionName, signature) \ + { #functionName, \ + signature, \ + _NATIVEHELPER_JNI_MACRO_CAST(void*) (className ## _ ## functionName) \ + } +#endif +// Used for methods that are annotated with @FastNative on the managed side, +// and when the C-name doesn't match the Java-name. +// +// See OVERLOADED_NATIVE_METHOD for usage. +#ifndef OVERLOADED_FAST_NATIVE_METHOD +#define OVERLOADED_FAST_NATIVE_METHOD(className, functionName, signature, identifier) \ + { #functionName, \ + signature, \ + _NATIVEHELPER_JNI_MACRO_CAST(void*) (className ## _ ## identifier) \ + } +#endif +//////////////////////////////////////////////////////// +// IMPLEMENTATION ONLY. +// DO NOT USE DIRECTLY. +//////////////////////////////////////////////////////// +// C-style cast for C, C++-style cast for C++ to avoid warnings/errors. +#if defined(__cplusplus) +#define _NATIVEHELPER_JNI_MACRO_CAST(to) \ + reinterpret_cast +#else +#define _NATIVEHELPER_JNI_MACRO_CAST(to) \ + (to) +#endif \ No newline at end of file diff --git a/edxp-core/src/main/cpp/external/android/nativehelper/module_api.h b/edxp-core/src/main/cpp/external/android/nativehelper/module_api.h new file mode 100644 index 00000000..1c8ca92e --- /dev/null +++ b/edxp-core/src/main/cpp/external/android/nativehelper/module_api.h @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once +#ifdef __cplusplus +#define MODULE_API extern "C" +#else +#define MODULE_API +#endif // __cplusplus diff --git a/edxp-core/src/main/cpp/external/android/nativehelper/nativehelper_utils.h b/edxp-core/src/main/cpp/external/android/nativehelper/nativehelper_utils.h new file mode 100644 index 00000000..9124bfa4 --- /dev/null +++ b/edxp-core/src/main/cpp/external/android/nativehelper/nativehelper_utils.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma +#if defined(__cplusplus) +#if !defined(DISALLOW_COPY_AND_ASSIGN) +// DISALLOW_COPY_AND_ASSIGN disallows the copy and operator= functions. It goes in the private: +// declarations in a class. +#define DISALLOW_COPY_AND_ASSIGN(TypeName) \ + TypeName(const TypeName&) = delete; \ + void operator=(const TypeName&) = delete +#endif // !defined(DISALLOW_COPY_AND_ASSIGN) +#ifndef NATIVEHELPER_JNIHELP_H_ +// This seems a header-only include. Provide NPE throwing. +static inline int jniThrowNullPointerException(JNIEnv* env, const char* msg) { + if (env->ExceptionCheck()) { + // Drop any pending exception. + env->ExceptionClear(); + } + jclass e_class = env->FindClass("java/lang/NullPointerException"); + if (e_class == nullptr) { + return -1; + } + if (env->ThrowNew(e_class, msg) != JNI_OK) { + env->DeleteLocalRef(e_class); + return -1; + } + env->DeleteLocalRef(e_class); + return 0; +} +#endif // NATIVEHELPER_JNIHELP_H_ +#endif // defined(__cplusplus) \ No newline at end of file diff --git a/edxp-core/src/main/cpp/external/android/nativehelper/scoped_local_ref.h b/edxp-core/src/main/cpp/external/android/nativehelper/scoped_local_ref.h new file mode 100644 index 00000000..151dea51 --- /dev/null +++ b/edxp-core/src/main/cpp/external/android/nativehelper/scoped_local_ref.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once +#include +#include "jni.h" +#include "nativehelper_utils.h" +// A smart pointer that deletes a JNI local reference when it goes out of scope. +template +class ScopedLocalRef { +public: + ScopedLocalRef(JNIEnv* env, T localRef) : mEnv(env), mLocalRef(localRef) { + } + ScopedLocalRef(ScopedLocalRef&& s) noexcept : mEnv(s.mEnv), mLocalRef(s.release()) { + } + explicit ScopedLocalRef(JNIEnv* env) : mEnv(env), mLocalRef(nullptr) { + } + ~ScopedLocalRef() { + reset(); + } + void reset(T ptr = NULL) { + if (ptr != mLocalRef) { + if (mLocalRef != NULL) { + mEnv->DeleteLocalRef(mLocalRef); + } + mLocalRef = ptr; + } + } + T release() __attribute__((warn_unused_result)) { + T localRef = mLocalRef; + mLocalRef = NULL; + return localRef; + } + T get() const { + return mLocalRef; + } + // We do not expose an empty constructor as it can easily lead to errors + // using common idioms, e.g.: + // ScopedLocalRef<...> ref; + // ref.reset(...); + // Move assignment operator. + ScopedLocalRef& operator=(ScopedLocalRef&& s) noexcept { + reset(s.release()); + mEnv = s.mEnv; + return *this; + } + // Allows "if (scoped_ref == nullptr)" + bool operator==(std::nullptr_t) const { + return mLocalRef == nullptr; + } + // Allows "if (scoped_ref != nullptr)" + bool operator!=(std::nullptr_t) const { + return mLocalRef != nullptr; + } +private: + JNIEnv* mEnv; + T mLocalRef; + DISALLOW_COPY_AND_ASSIGN(ScopedLocalRef); +}; \ No newline at end of file diff --git a/edxp-core/src/main/cpp/main/CMakeLists.txt b/edxp-core/src/main/cpp/main/CMakeLists.txt index 9d9994c0..71a612cb 100644 --- a/edxp-core/src/main/cpp/main/CMakeLists.txt +++ b/edxp-core/src/main/cpp/main/CMakeLists.txt @@ -2,17 +2,10 @@ cmake_minimum_required(VERSION 3.4.1) set(CMAKE_ANDROID_STL_TYPE c++_static) -set(SRC_LIST - main.cpp - native_hook/native_hook.cpp - native_hook/resource_hook.cpp - native_hook/riru_hook.cpp - java_hook/java_hook.cpp - inject/framework_hook.cpp - inject/config_manager.cpp - ) -include_directories(include .) -add_library(riru_edxp SHARED ${SRC_LIST}) +aux_source_directory(src SRC_LIST) +aux_source_directory(src/jni SRC_JNI_LIST) +include_directories(include src) +add_library(riru_edxp SHARED ${SRC_LIST} ${SRC_JNI_LIST}) find_library(log-lib log) -target_link_libraries(riru_edxp yahfa riru xhook substrate ${log-lib}) \ No newline at end of file +target_link_libraries(riru_edxp yahfa riru xhook substrate android ${log-lib}) \ No newline at end of file diff --git a/edxp-core/src/main/cpp/main/include/JNIHelper.h b/edxp-core/src/main/cpp/main/include/JNIHelper.h index 2436538f..a28708a0 100644 --- a/edxp-core/src/main/cpp/main/include/JNIHelper.h +++ b/edxp-core/src/main/cpp/main/include/JNIHelper.h @@ -1,10 +1,35 @@ -#ifndef JNIHELPER_H -#define JNIHELPER_H +#pragma once #include +#include #include "logging.h" -int ClearException(JNIEnv *env) { +#define JNI_START JNIEnv *env, jclass clazz + +ALWAYS_INLINE static void JNIExceptionClear(JNIEnv *env) { + if (env->ExceptionCheck()) { + env->ExceptionClear(); + } +} + +ALWAYS_INLINE static bool JNIExceptionCheck(JNIEnv *env) { + if (env->ExceptionCheck()) { + jthrowable e = env->ExceptionOccurred(); + env->Throw(e); + env->DeleteLocalRef(e); + return true; + } + return false; +} + +ALWAYS_INLINE static void JNIExceptionClearAndDescribe(JNIEnv *env) { + if (env->ExceptionCheck()) { + env->ExceptionDescribe(); + env->ExceptionClear(); + } +} + +ALWAYS_INLINE static int ClearException(JNIEnv *env) { jthrowable exception = env->ExceptionOccurred(); if (exception != nullptr) { env->ExceptionDescribe(); @@ -58,6 +83,10 @@ int ClearException(JNIEnv *env) { env->CallStaticVoidMethod(obj, __VA_ARGS__); \ if (ClearException(env)) LOGE("CallStaticVoidMethod " #obj " " #__VA_ARGS__); +#define JNI_CallStaticObjectMethod(env, obj, ...) \ + env->CallStaticObjectMethod(obj, __VA_ARGS__); \ + if (ClearException(env)) LOGE("CallStaticVoidMethod " #obj " " #__VA_ARGS__); + #define JNI_GetArrayLength(env, array) \ env->GetArrayLength(array); \ if (ClearException(env)) LOGE("GetArrayLength " #array); @@ -70,4 +99,3 @@ int ClearException(JNIEnv *env) { env->RegisterNatives(class, methods, size); \ if (ClearException(env)) LOGE("RegisterNatives " #class); -#endif // JNIHELPER_H diff --git a/edxp-core/src/main/cpp/main/include/android_build.h b/edxp-core/src/main/cpp/main/include/android_build.h index 5d20d6ae..4507c49b 100644 --- a/edxp-core/src/main/cpp/main/include/android_build.h +++ b/edxp-core/src/main/cpp/main/include/android_build.h @@ -20,6 +20,7 @@ #define ANDROID_O 26 #define ANDROID_O_MR1 27 #define ANDROID_P 28 +#define ANDROID_Q 29 static inline int32_t GetAndroidApiLevel() { char prop_value[PROP_VALUE_MAX]; diff --git a/edxp-core/src/main/cpp/main/include/art/runtime/class_linker.h b/edxp-core/src/main/cpp/main/include/art/runtime/class_linker.h new file mode 100644 index 00000000..e6c71b48 --- /dev/null +++ b/edxp-core/src/main/cpp/main/include/art/runtime/class_linker.h @@ -0,0 +1,46 @@ + +#pragma once + +#include +#include + +namespace art { + + class ClassLinker : public edxp::HookedObject { + + private: + inline static ClassLinker *instance_; + + CREATE_FUNC_SYMBOL_ENTRY(void, SetEntryPointsToInterpreter, void *thiz, void *art_method) { + if (LIKELY(SetEntryPointsToInterpreterSym)) + SetEntryPointsToInterpreterSym(thiz, art_method); + } + + CREATE_HOOK_STUB_ENTRIES(void *, Constructor, void *thiz, void *intern_table) { + if (LIKELY(instance_)) + instance_->Reset(thiz); + else + instance_ = new ClassLinker(thiz); + return ConstructorBackup(thiz, intern_table); + } + + public: + ClassLinker(void *thiz) : HookedObject(thiz) {} + + static ClassLinker *Current() { + return instance_; + } + + static void Setup(void *handle, HookFunType hook_func) { + HOOK_FUNC(Constructor, "_ZN3art11ClassLinkerC2EPNS_11InternTableE"); + RETRIEVE_FUNC_SYMBOL(SetEntryPointsToInterpreter, + "_ZNK3art11ClassLinker27SetEntryPointsToInterpreterEPNS_9ArtMethodE"); + } + + ALWAYS_INLINE void SetEntryPointsToInterpreter(void *art_method) const { + if (LIKELY(thiz_)) + SetEntryPointsToInterpreter(thiz_, art_method); + } + + }; +} diff --git a/edxp-core/src/main/cpp/main/include/art/runtime/gc/collector/gc_type.h b/edxp-core/src/main/cpp/main/include/art/runtime/gc/collector/gc_type.h new file mode 100644 index 00000000..82bc1d67 --- /dev/null +++ b/edxp-core/src/main/cpp/main/include/art/runtime/gc/collector/gc_type.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef ART_RUNTIME_GC_COLLECTOR_GC_TYPE_H_ +#define ART_RUNTIME_GC_COLLECTOR_GC_TYPE_H_ + +#include + +namespace art { + namespace gc { + namespace collector { +// The type of collection to be performed. The ordering of the enum matters, it is used to +// determine which GCs are run first. + enum GcType { + // Placeholder for when no GC has been performed. + kGcTypeNone, + // Sticky mark bits GC that attempts to only free objects allocated since the last GC. + kGcTypeSticky, + // Partial GC that marks the application heap but not the Zygote. + kGcTypePartial, + // Full GC that marks and frees in both the application and Zygote heap. + kGcTypeFull, + // Number of different GC types. + kGcTypeMax, + }; + + std::ostream &operator<<(std::ostream &os, const GcType &policy); + } // namespace collector + } // namespace gc +} // namespace art +#endif // ART_RUNTIME_GC_COLLECTOR_GC_TYPE_H_ \ No newline at end of file diff --git a/edxp-core/src/main/cpp/main/include/art/runtime/gc/gc_cause.h b/edxp-core/src/main/cpp/main/include/art/runtime/gc/gc_cause.h new file mode 100644 index 00000000..a92e162e --- /dev/null +++ b/edxp-core/src/main/cpp/main/include/art/runtime/gc/gc_cause.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef ART_RUNTIME_GC_GC_CAUSE_H_ +#define ART_RUNTIME_GC_GC_CAUSE_H_ + +#include + +namespace art { + namespace gc { +// What caused the GC? + enum GcCause { + // Invalid GC cause used as a placeholder. + kGcCauseNone, + // GC triggered by a failed allocation. Thread doing allocation is blocked waiting for GC before + // retrying allocation. + kGcCauseForAlloc, + // A background GC trying to ensure there is free memory ahead of allocations. + kGcCauseBackground, + // An explicit System.gc() call. + kGcCauseExplicit, + // GC triggered for a native allocation when NativeAllocationGcWatermark is exceeded. + // (This may be a blocking GC depending on whether we run a non-concurrent collector). + kGcCauseForNativeAlloc, + // GC triggered for a collector transition. + kGcCauseCollectorTransition, + // Not a real GC cause, used when we disable moving GC (currently for GetPrimitiveArrayCritical). + kGcCauseDisableMovingGc, + // Not a real GC cause, used when we trim the heap. + kGcCauseTrim, + // Not a real GC cause, used to implement exclusion between GC and instrumentation. + kGcCauseInstrumentation, + // Not a real GC cause, used to add or remove app image spaces. + kGcCauseAddRemoveAppImageSpace, + // Not a real GC cause, used to implement exclusion between GC and debugger. + kGcCauseDebugger, + // GC triggered for background transition when both foreground and background collector are CMS. + kGcCauseHomogeneousSpaceCompact, + // Class linker cause, used to guard filling art methods with special values. + kGcCauseClassLinker, + // Not a real GC cause, used to implement exclusion between code cache metadata and GC. + kGcCauseJitCodeCache, + // Not a real GC cause, used to add or remove system-weak holders. + kGcCauseAddRemoveSystemWeakHolder, + // Not a real GC cause, used to prevent hprof running in the middle of GC. + kGcCauseHprof, + // Not a real GC cause, used to prevent GetObjectsAllocated running in the middle of GC. + kGcCauseGetObjectsAllocated, + // GC cause for the profile saver. + kGcCauseProfileSaver, + }; + } // namespace gc +} // namespace art +#endif // ART_RUNTIME_GC_GC_CAUSE_H_ \ No newline at end of file diff --git a/edxp-core/src/main/cpp/main/include/art/runtime/gc/heap.h b/edxp-core/src/main/cpp/main/include/art/runtime/gc/heap.h new file mode 100644 index 00000000..dde6e854 --- /dev/null +++ b/edxp-core/src/main/cpp/main/include/art/runtime/gc/heap.h @@ -0,0 +1,55 @@ + +#pragma once + +#include +#include "collector/gc_type.h" +#include "gc_cause.h" +#include "../thread.h" + +namespace art { + + namespace gc { + + class Heap : public edxp::HookedObject { + + private: + inline static Heap *instance_; + + CREATE_FUNC_SYMBOL_ENTRY(collector::GcType, WaitForGcToComplete, + void *thiz, GcCause cause, void *threadSelf) { + if (LIKELY(WaitForGcToCompleteSym)) + return WaitForGcToCompleteSym(thiz, cause, threadSelf); + return art::gc::collector::GcType::kGcTypeNone; + } + + CREATE_HOOK_STUB_ENTRIES(void, PreZygoteFork, void *thiz) { + if (instance_) + instance_->Reset(thiz); + else + instance_ = new Heap(thiz); + PreZygoteForkBackup(thiz); + } + + public: + Heap(void *thiz) : HookedObject(thiz) {} + + static Heap *Current() { + return instance_; + } + + static void Setup(void *handle, HookFunType hook_func) { + HOOK_FUNC(PreZygoteFork, "_ZN3art2gc4Heap13PreZygoteForkEv"); + RETRIEVE_FUNC_SYMBOL(WaitForGcToComplete, + "_ZN3art2gc4Heap19WaitForGcToCompleteENS0_7GcCauseEPNS_6ThreadE"); + } + + ALWAYS_INLINE collector::GcType + WaitForGcToComplete(GcCause cause, void *threadSelf) const { + if (LIKELY(thiz_)) + return WaitForGcToComplete(thiz_, cause, threadSelf); + return art::gc::collector::GcType::kGcTypeNone; + } + + }; + } // namespace gc +} // namespace art diff --git a/edxp-core/src/main/cpp/main/include/art/runtime/hidden_api.h b/edxp-core/src/main/cpp/main/include/art/runtime/hidden_api.h new file mode 100644 index 00000000..ae6eddbf --- /dev/null +++ b/edxp-core/src/main/cpp/main/include/art/runtime/hidden_api.h @@ -0,0 +1,39 @@ + +#pragma once + +#include "base/object.h" + +namespace art { + + namespace hidden_api { + + enum Action { + kAllow, + kAllowButWarn, + kAllowButWarnAndToast, + kDeny + }; + + CREATE_HOOK_STUB_ENTRIES(Action, GetMethodActionImpl) { + return Action::kAllow; + } + + CREATE_HOOK_STUB_ENTRIES(Action, GetFieldActionImpl) { + return Action::kAllow; + } + + static void DisableHiddenApi(void *handle, HookFunType hook_func) { + if (GetAndroidApiLevel() < ANDROID_P) { + return; + } + HOOK_FUNC(GetMethodActionImpl, + "_ZN3art9hiddenapi6detail19GetMemberActionImplINS_9ArtMethodEEENS0_" + "6ActionEPT_NS_20HiddenApiAccessFlags7ApiListES4_NS0_12AccessMethodE"); + HOOK_FUNC(GetFieldActionImpl, + "_ZN3art9hiddenapi6detail19GetMemberActionImplINS_8ArtFieldEEENS0_" + "6ActionEPT_NS_20HiddenApiAccessFlags7ApiListES4_NS0_12AccessMethodE"); + }; + + } + +} diff --git a/edxp-core/src/main/cpp/main/include/art/runtime/mirror/class.h b/edxp-core/src/main/cpp/main/include/art/runtime/mirror/class.h new file mode 100644 index 00000000..cca55aca --- /dev/null +++ b/edxp-core/src/main/cpp/main/include/art/runtime/mirror/class.h @@ -0,0 +1,67 @@ + +#pragma once + +#include "base/object.h" + +namespace art { + namespace mirror { + + using namespace std; + using namespace edxp; + + class Class : public HookedObject { + + private: + + CREATE_FUNC_SYMBOL_ENTRY(const char *, GetDescriptor, void *thiz, + std::string *storage) { + return GetDescriptorSym(thiz, storage); + } + + + CREATE_ORIGINAL_ENTRY(bool, IsInSamePackage, void *thiz, void *that) { + if (IsInSamePackageBackup) { + return IsInSamePackageBackup(thiz, that); + } + return false; + } + + CREATE_HOOK_STUB_ENTRIES(bool, IsInSamePackage, void *thiz, void *that) { + std::string storage1, storage2; + const char *thisDesc = GetDescriptor(thiz, &storage1); + const char *thatDesc = GetDescriptor(that, &storage2); + // Note: these identifiers should be consistent with those in Java layer + if (strstr(thisDesc, "EdHooker_") != nullptr + || strstr(thatDesc, "EdHooker_") != nullptr + || strstr(thisDesc, "com/elderdrivers/riru/") != nullptr + || strstr(thatDesc, "com/elderdrivers/riru/") != nullptr) { + return true; + } + // for MIUI resources hooking + if (strstr(thisDesc, "android/content/res/MiuiTypedArray") != nullptr + || strstr(thatDesc, "android/content/res/MiuiTypedArray") != nullptr + || strstr(thisDesc, "android/content/res/XResources$XTypedArray") != nullptr + || strstr(thatDesc, "android/content/res/XResources$XTypedArray") != nullptr) { + return true; + } + return IsInSamePackage(thiz, that); + } + + public: + Class(void *thiz) : HookedObject(thiz) {} + + static void Setup(void *handle, HookFunType hook_func) { + RETRIEVE_FUNC_SYMBOL(GetDescriptor, "_ZN3art6mirror5Class13GetDescriptorEPNSt3__112" + "basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEE"); + +// RETRIEVE_FIELD_SYMBOL(mutator_lock_, "_ZN3art5Locks13mutator_lock_E"); +// LOGE("mutator_lock_: %p", mutator_lock_); + + HOOK_FUNC(IsInSamePackage, + "_ZN3art6mirror5Class15IsInSamePackageENS_6ObjPtrIS1_EE", //8.0- + "_ZN3art6mirror5Class15IsInSamePackageEPS1_"); //5.0-7.1 + } + }; + + } // namespace mirror +} // namespace art diff --git a/edxp-core/src/main/cpp/main/include/art/runtime/runtime.h b/edxp-core/src/main/cpp/main/include/art/runtime/runtime.h new file mode 100644 index 00000000..bf9ec09c --- /dev/null +++ b/edxp-core/src/main/cpp/main/include/art/runtime/runtime.h @@ -0,0 +1,51 @@ + +#pragma once + +#include + +namespace art { + + class Runtime : public edxp::HookedObject { + + private: + inline static Runtime *instance_; + + CREATE_FUNC_SYMBOL_ENTRY(void, DeoptimizeBootImage, void *thiz) { + if (LIKELY(DeoptimizeBootImageSym)) + DeoptimizeBootImageSym(thiz); + } + + CREATE_HOOK_STUB_ENTRIES(bool, Init, void *thiz, void *runtime_options) { + if (LIKELY(instance_)) + instance_->Reset(thiz); + else + instance_ = new Runtime(thiz); + bool success = InitBackup(thiz, runtime_options); + if (edxp::ConfigManager::GetInstance()->IsDeoptBootImageEnabled()) { + DeoptimizeBootImage(thiz); + LOGI("DeoptimizeBootImage done"); + } + return success; + } + + public: + Runtime(void *thiz) : HookedObject(thiz) {} + + static Runtime *Current() { + return instance_; + } + + static void Setup(void *handle, HookFunType hook_func) { + HOOK_FUNC(Init, "_ZN3art7Runtime4InitEONS_18RuntimeArgumentMapE"); + RETRIEVE_FUNC_SYMBOL(DeoptimizeBootImage, + "_ZN3art7Runtime19DeoptimizeBootImageEv"); + } + + ALWAYS_INLINE void DeoptimizeBootImage() const { + if (LIKELY(thiz_)) + DeoptimizeBootImage(thiz_); + } + + }; + +} diff --git a/edxp-core/src/main/cpp/main/include/art/runtime/thread.h b/edxp-core/src/main/cpp/main/include/art/runtime/thread.h new file mode 100644 index 00000000..136914f6 --- /dev/null +++ b/edxp-core/src/main/cpp/main/include/art/runtime/thread.h @@ -0,0 +1,14 @@ + +#pragma once + +#include + +namespace art { + +class Thread : public edxp::HookedObject { + + public: + Thread(void *thiz) : HookedObject(thiz) {} + + }; +} diff --git a/edxp-core/src/main/cpp/main/include/base/object.h b/edxp-core/src/main/cpp/main/include/base/object.h new file mode 100644 index 00000000..e4fd8d98 --- /dev/null +++ b/edxp-core/src/main/cpp/main/include/base/object.h @@ -0,0 +1,115 @@ + +#pragma once + +#include +#include + +typedef void (*HookFunType)(void *, void *, void **); + +#define HOOK_FUNC(func, ...) \ + edxp::HookSyms(handle, hook_func, \ + reinterpret_cast(func##Replace), \ + reinterpret_cast(&func##Backup), \ + __VA_ARGS__) + +#define CREATE_HOOK_STUB_ENTRIES(ret, func, ...) \ + inline static ret (*func##Backup)(__VA_ARGS__); \ + static ret func##Replace(__VA_ARGS__) + +#define CREATE_ORIGINAL_ENTRY(ret, func, ...) \ + static ret func(__VA_ARGS__) + +#define RETRIEVE_FUNC_SYMBOL(name, ...) \ + name##Sym = reinterpret_cast( \ + edxp::Dlsym(handle, __VA_ARGS__)) + +#define RETRIEVE_FIELD_SYMBOL(name, ...) \ + void *name = edxp::Dlsym(handle, __VA_ARGS__) + +#define CREATE_FUNC_SYMBOL_ENTRY(ret, func, ...) \ + typedef ret (*func##Type)(__VA_ARGS__); \ + inline static ret (*func##Sym)(__VA_ARGS__); \ + ALWAYS_INLINE static ret func(__VA_ARGS__) + +namespace edxp { + + class ShadowObject { + + public: + ShadowObject(void *thiz) : thiz_(thiz) { + } + + ALWAYS_INLINE inline void *Get() { + return thiz_; + } + + ALWAYS_INLINE inline void Reset(void *thiz) { + thiz_ = thiz; + } + + ALWAYS_INLINE inline operator bool() const { + return thiz_ != nullptr; + } + + protected: + void *thiz_; + }; + + class HookedObject : public ShadowObject { + + public: + + HookedObject(void *thiz) : ShadowObject(thiz) {} + + static void SetupSymbols(void *handle) { + + } + + static void SetupHooks(void *handle, HookFunType hook_fun) { + + } + }; + + ALWAYS_INLINE static void *Dlsym(void *handle, const char *name) { + return dlsym(handle, name); + } + + template + static void *Dlsym(void *handle, T first, Args... last) { + auto ret = Dlsym(handle, first); + if (ret) { + return ret; + } + return Dlsym(handle, last...); + } + + ALWAYS_INLINE inline static void HookFunction(HookFunType hook_fun, void *original, + void *replace, void **backup) { + hook_fun(original, replace, backup); + } + + inline static void *HookSym(void *handle, HookFunType hook_fun, const char *sym, + void *replace, void **backup) { + auto original = Dlsym(handle, sym); + if (original) { + HookFunction(hook_fun, original, replace, backup); + } else { + LOGW("%s not found", sym); + } + return original; + } + + template + inline static void *HookSyms(void *handle, HookFunType hook_fun, + void *replace, void **backup, T first, Args... last) { + auto original = Dlsym(handle, first, last...); + if (original) { + HookFunction(hook_fun, original, replace, backup); + return original; + } else { + LOGW("%s not found", first); + return nullptr; + } + } + +} // namespace edxp diff --git a/edxp-core/src/main/cpp/main/include/config.h b/edxp-core/src/main/cpp/main/include/config.h index 4efe8778..abf85568 100644 --- a/edxp-core/src/main/cpp/main/include/config.h +++ b/edxp-core/src/main/cpp/main/include/config.h @@ -1,19 +1,36 @@ + +#pragma once + #include #include +#include +#include "art/base/macros.h" -#ifndef CONFIG_H -#define CONFIG_H +namespace edxp { //#define LOG_DISABLED //#define DEBUG -#define INJECT_DEX_PATH \ -"/system/framework/edxp.jar:/system/framework/eddalvikdx.jar:/system/framework/eddexmaker.jar" +#if defined(__LP64__) +# define LP_SELECT(lp32, lp64) (lp64) +#else +# define LP_SELECT(lp32, lp64) (lp32) +#endif -#define ENTRY_CLASS_NAME "com.elderdrivers.riru.edxp.Main" + static constexpr const char *kInjectDexPath = "/system/framework/edxp.jar:" + "/system/framework/eddalvikdx.jar:" + "/system/framework/eddexmaker.jar"; + static constexpr const char *kEntryClassName = "com.elderdrivers.riru.edxp.Main"; + static constexpr const char *kSandHookClassName = "com.swift.sandhook.SandHook"; + static constexpr const char *kSandHookNeverCallClassName = "com.swift.sandhook.ClassNeverCall"; -#define CLASS_SAND_HOOK "com.swift.sandhook.SandHook" + static const std::string kLibBasePath = LP_SELECT("/system/lib/", "/system/lib64/"); + static const std::string kLibArtPath = kLibBasePath + "libart.so"; + static const std::string kLibWhalePath = kLibBasePath + "libwhale.edxp.so"; + static const std::string kLibSandHookPath = kLibBasePath + "libsandhook.edxp.so"; + static const std::string kLibFwPath = kLibBasePath + "libandroidfw.so"; -#define CLASS_NEVER_CALL "com.swift.sandhook.ClassNeverCall" - -#endif //CONFIG_H \ No newline at end of file + inline const char *const BoolToString(bool b) { + return b ? "true" : "false"; + } +} \ No newline at end of file diff --git a/edxp-core/src/main/cpp/main/include/dl_util.h b/edxp-core/src/main/cpp/main/include/dl_util.h new file mode 100644 index 00000000..74c9bb15 --- /dev/null +++ b/edxp-core/src/main/cpp/main/include/dl_util.h @@ -0,0 +1,58 @@ + +#pragma once + +#include "logging.h" + +namespace edxp { + + inline static void *DlOpen(const char *file) { + void *handle = dlopen(file, RTLD_LAZY | RTLD_GLOBAL); + if (!handle) { + LOGE("dlopen(%s) failed: %s", file, dlerror()); + } + return handle; + } + + template + inline static T DlSym(void *handle, const char *sym_name) { + if (!handle) { + LOGE("dlsym(%s) failed: handle is null", sym_name); + } + T symbol = reinterpret_cast(dlsym(handle, sym_name)); + if (!symbol) { + LOGE("dlsym(%s) failed: %s", sym_name, dlerror()); + } + return symbol; + } + + class ScopedDlHandle { + + public: + ScopedDlHandle(const char *file) { + handle_ = DlOpen(file); + } + + ~ScopedDlHandle() { + if (handle_) { + dlclose(handle_); + } + } + + void *Get() const { + return handle_; + } + + template + T DlSym(const char *sym_name) const { + return edxp::DlSym(handle_, sym_name); + } + + bool IsValid() const { + return handle_ != nullptr; + } + + private: + void *handle_; + }; + +} diff --git a/edxp-core/src/main/cpp/main/include/framework/androidfw/ResourceTypes.h b/edxp-core/src/main/cpp/main/include/framework/androidfw/ResourceTypes.h new file mode 100644 index 00000000..de0a2907 --- /dev/null +++ b/edxp-core/src/main/cpp/main/include/framework/androidfw/ResourceTypes.h @@ -0,0 +1,263 @@ + +#pragma once + +#include + +namespace android { + + typedef int32_t status_t; + + enum { + RES_NULL_TYPE = 0x0000, + RES_STRING_POOL_TYPE = 0x0001, + RES_TABLE_TYPE = 0x0002, + RES_XML_TYPE = 0x0003, + // Chunk types in RES_XML_TYPE + RES_XML_FIRST_CHUNK_TYPE = 0x0100, + RES_XML_START_NAMESPACE_TYPE = 0x0100, + RES_XML_END_NAMESPACE_TYPE = 0x0101, + RES_XML_START_ELEMENT_TYPE = 0x0102, + RES_XML_END_ELEMENT_TYPE = 0x0103, + RES_XML_CDATA_TYPE = 0x0104, + RES_XML_LAST_CHUNK_TYPE = 0x017f, + // This contains a uint32_t array mapping strings in the string + // pool back to resource identifiers. It is optional. + RES_XML_RESOURCE_MAP_TYPE = 0x0180, + // Chunk types in RES_TABLE_TYPE + RES_TABLE_PACKAGE_TYPE = 0x0200, + RES_TABLE_TYPE_TYPE = 0x0201, + RES_TABLE_TYPE_SPEC_TYPE = 0x0202, + RES_TABLE_LIBRARY_TYPE = 0x0203 + }; + + struct ResXMLTree_node { + void *header; + // Line number in original source file at which this element appeared. + uint32_t lineNumber; + // Optional XML comment that was associated with this element; -1 if none. + void *comment; + }; + + class ResXMLTree; + + class ResXMLParser { + + public: + enum event_code_t { + BAD_DOCUMENT = -1, + START_DOCUMENT = 0, + END_DOCUMENT = 1, + + FIRST_CHUNK_CODE = RES_XML_FIRST_CHUNK_TYPE, + + START_NAMESPACE = RES_XML_START_NAMESPACE_TYPE, + END_NAMESPACE = RES_XML_END_NAMESPACE_TYPE, + START_TAG = RES_XML_START_ELEMENT_TYPE, + END_TAG = RES_XML_END_ELEMENT_TYPE, + TEXT = RES_XML_CDATA_TYPE + }; + + const ResXMLTree &mTree; + event_code_t mEventCode; + const ResXMLTree_node *mCurNode; + const void *mCurExt; + }; + + class ResStringPool { + + public: + status_t mError; + void *mOwnedData; + const void *mHeader; + size_t mSize; + mutable pthread_mutex_t mDecodeLock; + const uint32_t *mEntries; + const uint32_t *mEntryStyles; + const void *mStrings; + char16_t mutable **mCache; + uint32_t mStringPoolSize; // number of uint16_t + const uint32_t *mStyles; + uint32_t mStylePoolSize; // number of uint32_t + }; + + + class ResXMLTree : public ResXMLParser { + + public: + void *mDynamicRefTable; + status_t mError; + void *mOwnedData; + const void *mHeader; + size_t mSize; + const uint8_t *mDataEnd; + ResStringPool mStrings; + const uint32_t *mResIds; + size_t mNumResIds; + const ResXMLTree_node *mRootNode; + const void *mRootExt; + event_code_t mRootCode; + }; + + struct ResStringPool_ref { + + // Index into the string pool table (uint32_t-offset from the indices + // immediately after ResStringPool_header) at which to find the location + // of the string data in the pool. + uint32_t index; + }; + + struct ResXMLTree_attrExt { + + // String of the full namespace of this element. + struct ResStringPool_ref ns; + + // String name of this node if it is an ELEMENT; the raw + // character data if this is a CDATA node. + struct ResStringPool_ref name; + + // Byte offset from the start of this structure where the attributes start. + uint16_t attributeStart; + + // Size of the ResXMLTree_attribute structures that follow. + uint16_t attributeSize; + + // Number of attributes associated with an ELEMENT. These are + // available as an array of ResXMLTree_attribute structures + // immediately following this node. + uint16_t attributeCount; + + // Index (1-based) of the "id" attribute. 0 if none. + uint16_t idIndex; + + // Index (1-based) of the "class" attribute. 0 if none. + uint16_t classIndex; + + // Index (1-based) of the "style" attribute. 0 if none. + uint16_t styleIndex; + }; + + struct Res_value { + + // Number of bytes in this structure. + uint16_t size; + // Always set to 0. + uint8_t res0; + + // Type of the data value. + enum : uint8_t { + // The 'data' is either 0 or 1, specifying this resource is either + // undefined or empty, respectively. + TYPE_NULL = 0x00, + // The 'data' holds a ResTable_ref, a reference to another resource + // table entry. + TYPE_REFERENCE = 0x01, + // The 'data' holds an attribute resource identifier. + TYPE_ATTRIBUTE = 0x02, + // The 'data' holds an index into the containing resource table's + // global value string pool. + TYPE_STRING = 0x03, + // The 'data' holds a single-precision floating point number. + TYPE_FLOAT = 0x04, + // The 'data' holds a complex number encoding a dimension value, + // such as "100in". + TYPE_DIMENSION = 0x05, + // The 'data' holds a complex number encoding a fraction of a + // container. + TYPE_FRACTION = 0x06, + // The 'data' holds a dynamic ResTable_ref, which needs to be + // resolved before it can be used like a TYPE_REFERENCE. + TYPE_DYNAMIC_REFERENCE = 0x07, + // The 'data' holds an attribute resource identifier, which needs to be resolved + // before it can be used like a TYPE_ATTRIBUTE. + TYPE_DYNAMIC_ATTRIBUTE = 0x08, + // Beginning of integer flavors... + TYPE_FIRST_INT = 0x10, + // The 'data' is a raw integer value of the form n..n. + TYPE_INT_DEC = 0x10, + // The 'data' is a raw integer value of the form 0xn..n. + TYPE_INT_HEX = 0x11, + // The 'data' is either 0 or 1, for input "false" or "true" respectively. + TYPE_INT_BOOLEAN = 0x12, + // Beginning of color integer flavors... + TYPE_FIRST_COLOR_INT = 0x1c, + // The 'data' is a raw integer value of the form #aarrggbb. + TYPE_INT_COLOR_ARGB8 = 0x1c, + // The 'data' is a raw integer value of the form #rrggbb. + TYPE_INT_COLOR_RGB8 = 0x1d, + // The 'data' is a raw integer value of the form #argb. + TYPE_INT_COLOR_ARGB4 = 0x1e, + // The 'data' is a raw integer value of the form #rgb. + TYPE_INT_COLOR_RGB4 = 0x1f, + // ...end of integer flavors. + TYPE_LAST_COLOR_INT = 0x1f, + // ...end of integer flavors. + TYPE_LAST_INT = 0x1f + }; + uint8_t dataType; + // Structure of complex data values (TYPE_UNIT and TYPE_FRACTION) + enum { + // Where the unit type information is. This gives us 16 possible + // types, as defined below. + COMPLEX_UNIT_SHIFT = 0, + COMPLEX_UNIT_MASK = 0xf, + // TYPE_DIMENSION: Value is raw pixels. + COMPLEX_UNIT_PX = 0, + // TYPE_DIMENSION: Value is Device Independent Pixels. + COMPLEX_UNIT_DIP = 1, + // TYPE_DIMENSION: Value is a Scaled device independent Pixels. + COMPLEX_UNIT_SP = 2, + // TYPE_DIMENSION: Value is in points. + COMPLEX_UNIT_PT = 3, + // TYPE_DIMENSION: Value is in inches. + COMPLEX_UNIT_IN = 4, + // TYPE_DIMENSION: Value is in millimeters. + COMPLEX_UNIT_MM = 5, + // TYPE_FRACTION: A basic fraction of the overall size. + COMPLEX_UNIT_FRACTION = 0, + // TYPE_FRACTION: A fraction of the parent size. + COMPLEX_UNIT_FRACTION_PARENT = 1, + // Where the radix information is, telling where the decimal place + // appears in the mantissa. This give us 4 possible fixed point + // representations as defined below. + COMPLEX_RADIX_SHIFT = 4, + COMPLEX_RADIX_MASK = 0x3, + // The mantissa is an integral number -- i.e., 0xnnnnnn.0 + COMPLEX_RADIX_23p0 = 0, + // The mantissa magnitude is 16 bits -- i.e, 0xnnnn.nn + COMPLEX_RADIX_16p7 = 1, + // The mantissa magnitude is 8 bits -- i.e, 0xnn.nnnn + COMPLEX_RADIX_8p15 = 2, + // The mantissa magnitude is 0 bits -- i.e, 0x0.nnnnnn + COMPLEX_RADIX_0p23 = 3, + // Where the actual value is. This gives us 23 bits of + // precision. The top bit is the sign. + COMPLEX_MANTISSA_SHIFT = 8, + COMPLEX_MANTISSA_MASK = 0xffffff + }; + // Possible data values for TYPE_NULL. + enum { + // The value is not defined. + DATA_NULL_UNDEFINED = 0, + // The value is explicitly defined as empty. + DATA_NULL_EMPTY = 1 + }; + // The data for this item, as interpreted according to dataType. + typedef uint32_t data_type; + data_type data; + }; + + struct ResXMLTree_attribute { + // Namespace of this attribute. + struct ResStringPool_ref ns; + + // Name of this attribute. + struct ResStringPool_ref name; + + // The original raw string value of this attribute. + struct ResStringPool_ref rawValue; + + // Processesd typed value of this attribute. + struct Res_value typedValue; + }; + +} \ No newline at end of file diff --git a/edxp-core/src/main/cpp/main/include/fd_utils-inl.h b/edxp-core/src/main/cpp/main/include/framework/fd_utils-inl.h similarity index 88% rename from edxp-core/src/main/cpp/main/include/fd_utils-inl.h rename to edxp-core/src/main/cpp/main/include/framework/fd_utils-inl.h index 34368ef6..4bb2bc52 100644 --- a/edxp-core/src/main/cpp/main/include/fd_utils-inl.h +++ b/edxp-core/src/main/cpp/main/include/framework/fd_utils-inl.h @@ -14,6 +14,8 @@ * limitations under the License. */ +#pragma once + #include #include #include @@ -31,20 +33,21 @@ #include #include -static const char* kPathPrefixWhitelist[] = { +static const char *kPathPrefixWhitelist[] = { "/data/app/", "/data/app-private/" }; -static const char* kFdPath = "/proc/self/fd"; +static const char *kFdPath = "/proc/self/fd"; +// todo stay up to date // Keeps track of all relevant information (flags, offset etc.) of an // open zygote file descriptor. class FileDescriptorInfo { public: // Create a FileDescriptorInfo for a given file descriptor. Returns // |NULL| if an error occurred. - static FileDescriptorInfo* createFromFd(int fd) { + static FileDescriptorInfo *createFromFd(int fd) { struct stat f_stat; // This should never happen; the zygote should always have the right set // of permissions required to stat all its open files. @@ -128,7 +131,8 @@ public: int open_flags = fs_flags & (kOpenFlags); fs_flags = fs_flags & (~(kOpenFlags)); - return new FileDescriptorInfo(f_stat, file_path, fd, open_flags, fd_flags, fs_flags, offset); + return new FileDescriptorInfo(f_stat, file_path, fd, open_flags, fd_flags, fs_flags, + offset); } bool Detach() const { @@ -215,7 +219,7 @@ private: is_sock(true) { } - FileDescriptorInfo(struct stat pstat, const std::string& pfile_path, int pfd, int popen_flags, + FileDescriptorInfo(struct stat pstat, const std::string &pfile_path, int pfd, int popen_flags, int pfd_flags, int pfs_flags, off_t poffset) : fd(pfd), stat(pstat), @@ -228,8 +232,9 @@ private: } // Returns true iff. a given path is whitelisted. - static bool IsWhitelisted(const std::string& path) { - for (size_t i = 0; i < (sizeof(kPathPrefixWhitelist) / sizeof(kPathPrefixWhitelist[0])); ++i) { + static bool IsWhitelisted(const std::string &path) { + for (size_t i = 0; + i < (sizeof(kPathPrefixWhitelist) / sizeof(kPathPrefixWhitelist[0])); ++i) { if (path.compare(0, strlen(kPathPrefixWhitelist[i]), kPathPrefixWhitelist[i]) == 0) { return true; } @@ -238,7 +243,7 @@ private: } // TODO: Call android::base::Readlink instead of copying the code here. - static bool Readlink(const int fd, std::string* result) { + static bool Readlink(const int fd, std::string *result) { char path[64]; snprintf(path, sizeof(path), "/proc/self/fd/%d", fd); @@ -249,7 +254,7 @@ private: // We could call lstat first, but that would introduce a race condition that // we couldn't detect. // ext2 and ext4 both have PAGE_SIZE limitations, so we assume that here. - char* buf = new char[4096]; + char *buf = new char[4096]; ssize_t len = readlink(path, buf, 4096); if (len == -1) { delete[] buf; @@ -268,9 +273,9 @@ private: // - the length of the path is greater than zero (i.e, not an unnamed socket). // - the first byte of the path isn't zero (i.e, not a socket with an abstract // address). - static bool GetSocketName(const int fd, std::string* result) { + static bool GetSocketName(const int fd, std::string *result) { sockaddr_storage ss; - sockaddr* addr = reinterpret_cast(&ss); + sockaddr *addr = reinterpret_cast(&ss); socklen_t addr_len = sizeof(ss); if (TEMP_FAILURE_RETRY(getsockname(fd, addr, &addr_len)) == -1) { @@ -290,7 +295,7 @@ private: return false; } - const sockaddr_un* unix_addr = reinterpret_cast(&ss); + const sockaddr_un *unix_addr = reinterpret_cast(&ss); size_t path_len = addr_len - offsetof(struct sockaddr_un, sun_path); // This is an unnamed local socket, we do not accept it. @@ -308,7 +313,7 @@ private: // If we're here, sun_path must refer to a null terminated filesystem // pathname (man 7 unix). Remove the terminator before assigning it to an // std::string. - if (unix_addr->sun_path[path_len - 1] == '\0') { + if (unix_addr->sun_path[path_len - 1] == '\0') { --path_len; } @@ -318,8 +323,9 @@ private: // DISALLOW_COPY_AND_ASSIGN(FileDescriptorInfo); - FileDescriptorInfo(const FileDescriptorInfo&); - void operator=(const FileDescriptorInfo&); + FileDescriptorInfo(const FileDescriptorInfo &); + + void operator=(const FileDescriptorInfo &); }; // A FileDescriptorTable is a collection of FileDescriptorInfo objects @@ -329,23 +335,23 @@ public: // Creates a new FileDescriptorTable. This function scans // /proc/self/fd for the list of open file descriptors and collects // information about them. Returns NULL if an error occurs. - static FileDescriptorTable* Create() { - DIR* d = opendir(kFdPath); + static FileDescriptorTable *Create() { + DIR *d = opendir(kFdPath); if (d == NULL) { LOGE("Unable to open directory %s: %s", kFdPath, strerror(errno)); return NULL; } int dir_fd = dirfd(d); - dirent* e; + dirent *e; - std::unordered_map open_fd_map; + std::unordered_map open_fd_map; while ((e = readdir(d)) != NULL) { const int fd = ParseFd(e, dir_fd); if (fd == -1) { continue; } - FileDescriptorInfo* info = FileDescriptorInfo::createFromFd(fd); + FileDescriptorInfo *info = FileDescriptorInfo::createFromFd(fd); if (info == NULL) { continue; } @@ -362,9 +368,9 @@ public: // Reopens all file descriptors that are contained in the table. void Reopen() { - std::unordered_map::const_iterator it; + std::unordered_map::const_iterator it; for (it = open_fd_map_.begin(); it != open_fd_map_.end(); ++it) { - const FileDescriptorInfo* info = it->second; + const FileDescriptorInfo *info = it->second; if (info != NULL) { info->Reopen(); delete info; @@ -373,12 +379,12 @@ public: } private: - FileDescriptorTable(const std::unordered_map& map) + FileDescriptorTable(const std::unordered_map &map) : open_fd_map_(map) { } - static int ParseFd(dirent* e, int dir_fd) { - char* end; + static int ParseFd(dirent *e, int dir_fd) { + char *end; const int fd = strtol(e->d_name, &end, 10); if ((*end) != '\0') { return -1; @@ -394,9 +400,10 @@ private: } // Invariant: All values in this unordered_map are non-NULL. - std::unordered_map open_fd_map_; + std::unordered_map open_fd_map_; // DISALLOW_COPY_AND_ASSIGN(FileDescriptorTable); - FileDescriptorTable(const FileDescriptorTable&); - void operator=(const FileDescriptorTable&); + FileDescriptorTable(const FileDescriptorTable &); + + void operator=(const FileDescriptorTable &); }; \ No newline at end of file diff --git a/edxp-core/src/main/cpp/main/include/logging.h b/edxp-core/src/main/cpp/main/include/logging.h index 0a1eb5c6..4a009af2 100644 --- a/edxp-core/src/main/cpp/main/include/logging.h +++ b/edxp-core/src/main/cpp/main/include/logging.h @@ -5,7 +5,7 @@ #include "android/log.h" #ifndef LOG_TAG -#define LOG_TAG "EdXposed-Core-Native" +#define LOG_TAG "EdXposed" #endif #include "config.h" diff --git a/edxp-core/src/main/cpp/main/include/misc.cpp b/edxp-core/src/main/cpp/main/include/misc.cpp deleted file mode 100644 index 04e9055a..00000000 --- a/edxp-core/src/main/cpp/main/include/misc.cpp +++ /dev/null @@ -1,91 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "java_hook/java_hook.h" -#include "include/logging.h" -#include "misc.h" - -ssize_t fdgets(char *buf, const size_t size, int fd) { - ssize_t len = 0; - buf[0] = '\0'; - while (len < size - 1) { - ssize_t ret = read(fd, buf + len, 1); - if (ret < 0) - return -1; - if (ret == 0) - break; - if (buf[len] == '\0' || buf[len++] == '\n') { - break; - } - } - buf[len] = '\0'; - buf[size - 1] = '\0'; - return len; -} - -char *get_cmdline_from_pid(pid_t pid, char *buf, size_t len) { - char filename[32]; - if (pid < 1 || buf == NULL) { - printf("%s: illegal para\n", __func__); - return NULL; - } - - snprintf(filename, 32, "/proc/%d/cmdline", pid); - int read_ret = read_to_buf(filename, buf, len); - if (read_ret < 0) - return NULL; - - if (buf[read_ret - 1] == '\n') - buf[--read_ret] = 0; - - char *name = buf; - while (read_ret) { - if (((unsigned char) *name) < ' ') - *name = ' '; - name++; - read_ret--; - } - *name = 0; - name = NULL; - - LOGV("cmdline:%s\n", buf); - return buf; -} - -int read_to_buf(const char *filename, void *buf, size_t len) { - int fd; - int ret; - if (buf == NULL) { - printf("%s: illegal para\n", __func__); - return -1; - } - memset(buf, 0, len); - fd = open(filename, O_RDONLY); - if (fd < 0) { - perror("open"); - return -1; - } - ret = (int) read(fd, buf, len); - close(fd); - return ret; -} - -char *getAppId(char *application_id, size_t size) { - pid_t pid = getpid(); -// LOGV("process new id %d", pid); - char path[64] = {0}; - sprintf(path, "/proc/%d/cmdline", pid); - FILE *cmdline = fopen(path, "r"); - if (cmdline) { - fread(application_id, size, 1, cmdline); -// LOGV("application id %s", application_id); - fclose(cmdline); - } - return application_id; -} \ No newline at end of file diff --git a/edxp-core/src/main/cpp/main/include/misc.h b/edxp-core/src/main/cpp/main/include/misc.h deleted file mode 100644 index d5fa4ee2..00000000 --- a/edxp-core/src/main/cpp/main/include/misc.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef MISC_H -#define MISC_H - -#include - -ssize_t fdgets(char *buf, const size_t size, int fd); - -char *get_cmdline_from_pid(pid_t pid, char *buf, size_t len); - -int read_to_buf(const char *filename, void *buf, size_t len); - -char *getAppId(char *application_id, size_t size); - -#endif // MISC_H diff --git a/edxp-core/src/main/cpp/main/include/native_util.h b/edxp-core/src/main/cpp/main/include/native_util.h new file mode 100644 index 00000000..35ac1eb0 --- /dev/null +++ b/edxp-core/src/main/cpp/main/include/native_util.h @@ -0,0 +1,34 @@ + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-value" +#pragma once + +#include +#include +#include +#include +#include "JNIHelper.h" + +namespace edxp { + + ALWAYS_INLINE inline void RegisterNativeMethodsInternal(JNIEnv *env, + const char *class_name, + const JNINativeMethod *methods, + jint method_count) { + + ScopedLocalRef clazz(env, + Context::GetInstance()->FindClassFromLoader(env, class_name)); + if (clazz.get() == nullptr) { + LOG(FATAL) << "Couldn't find class: " << class_name; + return; + } + jint jni_result = JNI_RegisterNatives(env, clazz.get(), methods, method_count); + CHECK_EQ(JNI_OK, jni_result); + } + +#define REGISTER_EDXP_NATIVE_METHODS(class_name) \ + RegisterNativeMethodsInternal(env, (class_name), gMethods, arraysize(gMethods)) + +} // namespace edxp + +#pragma clang diagnostic pop \ No newline at end of file diff --git a/edxp-core/src/main/cpp/main/inject/config_manager.cpp b/edxp-core/src/main/cpp/main/inject/config_manager.cpp deleted file mode 100644 index 21fac981..00000000 --- a/edxp-core/src/main/cpp/main/inject/config_manager.cpp +++ /dev/null @@ -1,222 +0,0 @@ -// -// Created by Solo on 2019/1/27. -// - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include "config_manager.h" - -using namespace std; - -#define PRIMARY_INSTALLER_PKG_NAME "com.solohsu.android.edxp.manager" -#define SECONDARY_INSTALLER_PKG_NAME "org.meowcat.edxposed.manager" -#define LEGACY_INSTALLER_PKG_NAME "de.robv.android.xposed.installer" - -static bool use_prot_storage = GetAndroidApiLevel() >= ANDROID_N; -static const char *data_path_prefix = use_prot_storage ? "/data/user_de/0/" : "/data/user/0/"; -static const char *config_path_tpl = "%s/%s/conf/%s"; -static char data_test_path[PATH_MAX]; -static char base_config_path[PATH_MAX]; -static char blacklist_path[PATH_MAX]; -static char whitelist_path[PATH_MAX]; -static char use_whitelist_path[PATH_MAX]; -static char black_white_list_path[PATH_MAX]; -static char dynamic_modules_path[PATH_MAX]; -static char deopt_boot_image_path[PATH_MAX]; -static char resources_hook_disable_path[PATH_MAX]; - -static const char *installer_package_name; -static bool black_white_list_enabled = false; -static bool dynamic_modules_enabled = false; -static bool deopt_boot_image_enabled = false; -static bool resources_hook_enabled = true; -static bool inited = false; -// snapshot at boot -static bool use_white_list_snapshot = false; -static vector white_list_default; -static vector black_list_default; - -static const char *get_installer_package_name() { - snprintf(data_test_path, PATH_MAX, config_path_tpl, data_path_prefix, - PRIMARY_INSTALLER_PKG_NAME, "/"); - if (access(data_test_path, F_OK) == 0) { - LOGI("using installer " - PRIMARY_INSTALLER_PKG_NAME); - return PRIMARY_INSTALLER_PKG_NAME; - } - snprintf(data_test_path, PATH_MAX, config_path_tpl, data_path_prefix, - SECONDARY_INSTALLER_PKG_NAME, "/"); - if (access(data_test_path, F_OK) == 0) { - LOGI("using installer " - SECONDARY_INSTALLER_PKG_NAME); - return SECONDARY_INSTALLER_PKG_NAME; - } - snprintf(data_test_path, PATH_MAX, config_path_tpl, data_path_prefix, - LEGACY_INSTALLER_PKG_NAME, "/"); - if (access(data_test_path, F_OK) == 0) { - LOGI("using installer " - LEGACY_INSTALLER_PKG_NAME); - return LEGACY_INSTALLER_PKG_NAME; - } - LOGE("no supported installer app found, using primary as default"); - return PRIMARY_INSTALLER_PKG_NAME; -} - -static void snapshotBlackWhiteList() { - DIR *dir; - struct dirent *dent; - dir = opendir(whitelist_path); - if (dir != nullptr) { - while ((dent = readdir(dir)) != nullptr) { - if (dent->d_type == DT_REG) { - const char *fileName = dent->d_name; - LOGI("whitelist: %s", fileName); - white_list_default.emplace_back(fileName); - } - } - closedir(dir); - } - dir = opendir(blacklist_path); - if (dir != nullptr) { - while ((dent = readdir(dir)) != nullptr) { - if (dent->d_type == DT_REG) { - const char *fileName = dent->d_name; - LOGI("blacklist: %s", fileName); - black_list_default.emplace_back(fileName); - } - } - closedir(dir); - } -} - -static void init_once() { - if (!inited) { - installer_package_name = get_installer_package_name(); - snprintf(base_config_path, PATH_MAX, config_path_tpl, data_path_prefix, - installer_package_name, ""); - snprintf(blacklist_path, PATH_MAX, config_path_tpl, data_path_prefix, - installer_package_name, "blacklist/"); - snprintf(whitelist_path, PATH_MAX, config_path_tpl, data_path_prefix, - installer_package_name, "whitelist/"); - snprintf(use_whitelist_path, PATH_MAX, config_path_tpl, data_path_prefix, - installer_package_name, "usewhitelist"); - snprintf(black_white_list_path, PATH_MAX, config_path_tpl, data_path_prefix, - installer_package_name, "blackwhitelist"); - snprintf(dynamic_modules_path, PATH_MAX, config_path_tpl, data_path_prefix, - installer_package_name, "dynamicmodules"); - snprintf(deopt_boot_image_path, PATH_MAX, config_path_tpl, data_path_prefix, - installer_package_name, "deoptbootimage"); - snprintf(resources_hook_disable_path, PATH_MAX, config_path_tpl, data_path_prefix, - installer_package_name, "disable_resources"); - dynamic_modules_enabled = access(dynamic_modules_path, F_OK) == 0; - black_white_list_enabled = access(black_white_list_path, F_OK) == 0; - // use_white_list snapshot - use_white_list_snapshot = access(use_whitelist_path, F_OK) == 0; - deopt_boot_image_enabled = access(deopt_boot_image_path, F_OK) == 0; - resources_hook_enabled = access(resources_hook_disable_path, F_OK) != 0; - LOGI("black/white list mode: %d, using whitelist: %d", black_white_list_enabled, - use_white_list_snapshot); - LOGI("dynamic modules mode: %d", dynamic_modules_enabled); - LOGI("resources hook: %d", resources_hook_enabled); - if (black_white_list_enabled) { - snapshotBlackWhiteList(); - } - inited = true; - } -} - -static char package_name[256]; - -bool is_app_need_hook(JNIEnv *env, jclass, jstring appDataDir) { - init_once(); - if (!black_white_list_enabled) { - return true; - } - - const char *app_data_dir = env->GetStringUTFChars(appDataDir, nullptr); - bool can_access_app_data = access(base_config_path, F_OK) == 0; - bool use_white_list; - if (can_access_app_data) { - use_white_list = access(use_whitelist_path, F_OK) == 0; - } else { - LOGE("can't access config path, using snapshot use_white_list: %s", app_data_dir); - use_white_list = use_white_list_snapshot; - } - int user = 0; - if (sscanf(app_data_dir, "/data/%*[^/]/%d/%s", &user, package_name) != 2) { - if (sscanf(app_data_dir, "/data/%*[^/]/%s", package_name) != 1) { - package_name[0] = '\0'; - LOGE("can't parse %s", app_data_dir); - env->ReleaseStringUTFChars(appDataDir, app_data_dir); - return !use_white_list; - } - } - if (strcmp(package_name, "com.solohsu.android.edxp.manager") == 0 - || strcmp(package_name, "org.meowcat.edxposed.manager") == 0 - || strcmp(package_name, "de.robv.android.xposed.installer") == 0) { - // always hook installer apps - env->ReleaseStringUTFChars(appDataDir, app_data_dir); - return true; - } - if (use_white_list) { - if (!can_access_app_data) { - LOGE("can't access config path, using snapshot white list: %s", app_data_dir); - return find(white_list_default.begin(), white_list_default.end(), package_name) != - white_list_default.end(); - } - char path[PATH_MAX]; - snprintf(path, PATH_MAX, "%s%s", whitelist_path, package_name); - bool res = access(path, F_OK) == 0; - LOGD("using whitelist, %s -> %d", app_data_dir, res); - env->ReleaseStringUTFChars(appDataDir, app_data_dir); - return res; - } else { - if (!can_access_app_data) { - LOGE("can't access config path, using snapshot black list: %s", app_data_dir); - return !(find(black_list_default.begin(), black_list_default.end(), package_name) != - black_list_default.end()); - } - char path[PATH_MAX]; - snprintf(path, PATH_MAX, "%s%s", blacklist_path, package_name); - bool res = access(path, F_OK) != 0; - LOGD("using blacklist, %s -> %d", app_data_dir, res); - env->ReleaseStringUTFChars(appDataDir, app_data_dir); - return res; - } -} - -bool is_black_white_list_enabled() { - init_once(); - return black_white_list_enabled; -} - -bool is_dynamic_modules_enabled() { - init_once(); - return dynamic_modules_enabled; -} - -bool is_resources_hook_enabled() { - init_once(); - return resources_hook_enabled; -} - -bool is_deopt_boot_image_enabled() { - init_once(); - return deopt_boot_image_enabled; -} - -jstring get_installer_pkg_name(JNIEnv *env, jclass) { - init_once(); - return env->NewStringUTF(installer_package_name); -} diff --git a/edxp-core/src/main/cpp/main/inject/config_manager.h b/edxp-core/src/main/cpp/main/inject/config_manager.h deleted file mode 100644 index 7cf72141..00000000 --- a/edxp-core/src/main/cpp/main/inject/config_manager.h +++ /dev/null @@ -1,22 +0,0 @@ -// -// Created by Solo on 2019/1/27. -// - -#ifndef EDXPOSED_CONFIG_MANAGER_H -#define EDXPOSED_CONFIG_MANAGER_H - -#include - -bool is_app_need_hook(JNIEnv *env, jclass, jstring appDataDir); - -bool is_black_white_list_enabled(); - -bool is_dynamic_modules_enabled(); - -bool is_resources_hook_enabled(); - -bool is_deopt_boot_image_enabled(); - -jstring get_installer_pkg_name(JNIEnv *env, jclass clazz); - -#endif //EDXPOSED_CONFIG_MANAGER_H diff --git a/edxp-core/src/main/cpp/main/inject/framework_hook.cpp b/edxp-core/src/main/cpp/main/inject/framework_hook.cpp deleted file mode 100644 index 5fd8eb6f..00000000 --- a/edxp-core/src/main/cpp/main/inject/framework_hook.cpp +++ /dev/null @@ -1,109 +0,0 @@ - - -#include -#include -#include -#include "framework_hook.h" -#include "include/misc.h" -#include "config_manager.h" - -#define SYSTEM_SERVER_DATA_DIR "/data/user/0/android" - -static jclass sEntryClass; -static jstring sAppDataDir; -static jstring sNiceName; - -void prepareJavaEnv(JNIEnv *env) { - loadDexAndInit(env, INJECT_DEX_PATH); - sEntryClass = findClassFromLoader(env, gInjectDexClassLoader, ENTRY_CLASS_NAME); -} - -void findAndCall(JNIEnv *env, const char *methodName, const char *methodSig, ...) { - if (!sEntryClass) { - LOGE("cannot call method %s, entry class is null", methodName); - return; - } - jmethodID mid = env->GetStaticMethodID(sEntryClass, methodName, methodSig); - if (env->ExceptionOccurred()) { - env->ExceptionClear(); - LOGE("method %s not found in entry class", methodName); - mid = NULL; - } - if (mid) { - va_list args; - va_start(args, methodSig); - env->functions->CallStaticVoidMethodV(env, sEntryClass, mid, args); - va_end(args); - } else { - LOGE("method %s id is null", methodName); - } -} - -void onNativeForkSystemServerPre(JNIEnv *env, jclass clazz, uid_t uid, gid_t gid, jintArray gids, - jint runtime_flags, jobjectArray rlimits, - jlong permittedCapabilities, jlong effectiveCapabilities) { - sAppDataDir = env->NewStringUTF(SYSTEM_SERVER_DATA_DIR); - bool is_black_white_list_mode = is_black_white_list_enabled(); - bool is_dynamic_modules_mode = is_dynamic_modules_enabled(); - if (is_black_white_list_mode && is_dynamic_modules_mode) { - // when black/white list is on, never inject into zygote if dynamic modules mode is on - return; - } - prepareJavaEnv(env); - // jump to java code - findAndCall(env, "forkSystemServerPre", "(II[II[[IJJ)V", uid, gid, gids, runtime_flags, - rlimits, permittedCapabilities, effectiveCapabilities); -} - - -int onNativeForkSystemServerPost(JNIEnv *env, jclass clazz, jint res) { - if (res == 0) { - prepareJavaEnv(env); - // only do work in child since findAndCall would print log - findAndCall(env, "forkSystemServerPost", "(I)V", res); - } else { - // in zygote process, res is child zygote pid - // don't print log here, see https://github.com/RikkaApps/Riru/blob/77adfd6a4a6a81bfd20569c910bc4854f2f84f5e/riru-core/jni/main/jni_native_method.cpp#L55-L66 - } - return 0; -} - -void onNativeForkAndSpecializePre(JNIEnv *env, jclass clazz, - jint uid, jint gid, - jintArray gids, - jint runtime_flags, - jobjectArray rlimits, - jint _mount_external, - jstring se_info, - jstring se_name, - jintArray fdsToClose, - jintArray fdsToIgnore, - jboolean is_child_zygote, - jstring instructionSet, - jstring appDataDir) { - sAppDataDir = appDataDir; - sNiceName = se_name; - bool is_black_white_list_mode = is_black_white_list_enabled(); - bool is_dynamic_modules_mode = is_dynamic_modules_enabled(); - if (is_black_white_list_mode && is_dynamic_modules_mode) { - // when black/white list is on, never inject into zygote if dynamic modules mode is on - return; - } - prepareJavaEnv(env); - findAndCall(env, "forkAndSpecializePre", - "(II[II[[IILjava/lang/String;Ljava/lang/String;[I[IZLjava/lang/String;Ljava/lang/String;)V", - uid, gid, gids, runtime_flags, rlimits, - _mount_external, se_info, se_name, fdsToClose, fdsToIgnore, - is_child_zygote, instructionSet, appDataDir); -} - -int onNativeForkAndSpecializePost(JNIEnv *env, jclass clazz, jint res) { - if (res == 0) { - prepareJavaEnv(env); - findAndCall(env, "forkAndSpecializePost", "(ILjava/lang/String;Ljava/lang/String;)V", res, sAppDataDir, sNiceName); - } else { - // in zygote process, res is child zygote pid - // don't print log here, see https://github.com/RikkaApps/Riru/blob/77adfd6a4a6a81bfd20569c910bc4854f2f84f5e/riru-core/jni/main/jni_native_method.cpp#L55-L66 - } - return 0; -} \ No newline at end of file diff --git a/edxp-core/src/main/cpp/main/inject/framework_hook.h b/edxp-core/src/main/cpp/main/inject/framework_hook.h deleted file mode 100644 index 6e2cffab..00000000 --- a/edxp-core/src/main/cpp/main/inject/framework_hook.h +++ /dev/null @@ -1,30 +0,0 @@ - -#ifndef RIRU_FRAMEWORK_HOOK_H -#define RIRU_FRAMEWORK_HOOK_H - - -#include - -void onNativeForkSystemServerPre(JNIEnv *env, jclass clazz, uid_t uid, gid_t gid, jintArray gids, - jint runtime_flags, jobjectArray rlimits, - jlong permittedCapabilities, jlong effectiveCapabilities); - -int onNativeForkSystemServerPost(JNIEnv *env, jclass clazz, jint res); - -void onNativeForkAndSpecializePre(JNIEnv *env, jclass clazz, - jint _uid, jint gid, - jintArray gids, - jint runtime_flags, - jobjectArray rlimits, - jint _mount_external, - jstring se_info, - jstring se_name, - jintArray fdsToClose, - jintArray fdsToIgnore, - jboolean is_child_zygote, - jstring instructionSet, - jstring appDataDir); - -int onNativeForkAndSpecializePost(JNIEnv *env, jclass clazz, jint res); - -#endif //RIRU_FRAMEWORK_HOOK_H diff --git a/edxp-core/src/main/cpp/main/java_hook/java_hook.cpp b/edxp-core/src/main/cpp/main/java_hook/java_hook.cpp deleted file mode 100644 index d087cf85..00000000 --- a/edxp-core/src/main/cpp/main/java_hook/java_hook.cpp +++ /dev/null @@ -1,274 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "java_hook/java_hook.h" -#include "include/logging.h" -#include "include/fd_utils-inl.h" - -extern "C" -{ -#include "HookMain.h" -} - -jobject gInjectDexClassLoader; - -static bool isInited = false; - -static FileDescriptorTable *gClosedFdTable = nullptr; - -void closeFilesBeforeForkNative(JNIEnv *, jclass) { - // FIXME what if gClosedFdTable is not null - gClosedFdTable = FileDescriptorTable::Create(); -} - -void reopenFilesAfterForkNative(JNIEnv *, jclass) { - if (!gClosedFdTable) { - LOGE("gClosedFdTable is null when reopening files"); - return; - } - gClosedFdTable->Reopen(); - delete gClosedFdTable; - gClosedFdTable = nullptr; -} - -jlong suspendAllThreads(JNIEnv *, jclass) { - if (!suspendAll) { - return 0; - } - ScopedSuspendAll *suspendAllObj = (ScopedSuspendAll *) malloc(sizeof(ScopedSuspendAll)); - suspendAll(suspendAllObj, "edxp_stop_gc", false); - return reinterpret_cast(suspendAllObj); -} - -void resumeAllThreads(JNIEnv *, jclass, jlong obj) { - if (!resumeAll) { - return; - } - resumeAll(reinterpret_cast(obj)); -} - -int waitForGcToComplete(JNIEnv *, jclass, jlong thread) { - // if waitGc succeeded, it should return one of enum collector::GcType - int gcType = waitGc(0, reinterpret_cast(thread)); - return gcType; -} - -void setMethodNonCompilable(JNIEnv *env, jclass, jobject member) { - if (!member) { - LOGE("setNonCompilableNative: member is null"); - return; - } - void *artMethod = env->FromReflectedMethod(member); - if (!artMethod) { - LOGE("setNonCompilableNative: artMethod is null"); - return; - } - setNonCompilable(artMethod); -} - -static constexpr uint32_t kAccFinal = 0x0010; - -jboolean removeFinalFlag(JNIEnv *env, jclass, jclass clazz) { - if (clazz) { - jfieldID java_lang_Class_accessFlags = env->GetFieldID( - env->FindClass("java/lang/Class"), "accessFlags", "I"); - jint access_flags = env->GetIntField(clazz, java_lang_Class_accessFlags); - env->SetIntField(clazz, java_lang_Class_accessFlags, access_flags & ~kAccFinal); - return true; - } - return false; -} - -static JNINativeMethod hookMethods[] = { - { - "init", - "(I)V", - (void *) Java_lab_galaxy_yahfa_HookMain_init - }, - { - "backupAndHookNative", - "(Ljava/lang/Object;Ljava/lang/reflect/Method;Ljava/lang/reflect/Method;)Z", - (void *) Java_lab_galaxy_yahfa_HookMain_backupAndHookNative - }, - { - "setMethodNonCompilable", "(Ljava/lang/Object;)V", (void *) setMethodNonCompilable - }, - { - "findMethodNative", - "(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object;", - (void *) Java_lab_galaxy_yahfa_HookMain_findMethodNative - }, - { - "ensureMethodCached", - "(Ljava/lang/reflect/Method;Ljava/lang/reflect/Method;)V", - (void *) Java_lab_galaxy_yahfa_HookMain_ensureMethodCached - }, - { - "isBlackWhiteListEnabled", "()Z", (void *) is_black_white_list_enabled - }, - { - "isDynamicModulesEnabled", "()Z", (void *) is_dynamic_modules_enabled - }, - { - "isResourcesHookEnabled", "()Z", (void *) is_resources_hook_enabled - }, - { - "isAppNeedHook", "(Ljava/lang/String;)Z", (void *) is_app_need_hook - }, - { - "getInstallerPkgName", "()Ljava/lang/String;", (void *) get_installer_pkg_name - }, - { - "closeFilesBeforeForkNative", "()V", (void *) closeFilesBeforeForkNative - }, - { - "reopenFilesAfterForkNative", "()V", (void *) reopenFilesAfterForkNative - }, - { - "deoptMethodNative", "(Ljava/lang/Object;)V", (void *) deoptimize_method - }, - { - "suspendAllThreads", "()J", (void *) suspendAllThreads - }, - { - "resumeAllThreads", "(J)V", (void *) resumeAllThreads - }, - { - "waitForGcToComplete", "(J)I", (void *) waitForGcToComplete - }, - { - "initXResourcesNative", "()Z", (void *) XposedBridge_initXResourcesNative - }, - { - "removeFinalFlagNative", "(Ljava/lang/Class;)Z", (void *) removeFinalFlag - } -}; - -void loadDexAndInit(JNIEnv *env, const char *dexPath) { - if (isInited) { - return; - } - jclass clzClassLoader = env->FindClass("java/lang/ClassLoader"); - LOGD("java/lang/ClassLoader: %p", clzClassLoader); - jmethodID mdgetSystemClassLoader = env->GetStaticMethodID(clzClassLoader, - "getSystemClassLoader", - "()Ljava/lang/ClassLoader;"); - LOGD("java/lang/ClassLoader.getSystemClassLoader method: %p", mdgetSystemClassLoader); - jobject systemClassLoader = env->CallStaticObjectMethod(clzClassLoader, mdgetSystemClassLoader); - LOGD("java/lang/ClassLoader.getSystemClassLoader method result: %p", systemClassLoader); - if (NULL == systemClassLoader) { - LOGE("getSystemClassLoader failed!!!"); - return; - } - jclass clzPathClassLoader = env->FindClass("dalvik/system/PathClassLoader"); - LOGD("dalvik/system/PathClassLoader: %p", clzClassLoader); - jmethodID mdinitPathCL = env->GetMethodID(clzPathClassLoader, "", - "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/ClassLoader;)V"); - LOGD("dalvik/system/PathClassLoader.: %p", clzClassLoader); - jstring jarpath_str = env->NewStringUTF(dexPath); - jobject myClassLoader = env->NewObject(clzPathClassLoader, mdinitPathCL, - jarpath_str, NULL, systemClassLoader); - if (NULL == myClassLoader) { - LOGE("PathClassLoader creation failed!!!"); - return; - } - gInjectDexClassLoader = env->NewGlobalRef(myClassLoader); - LOGD("PathClassLoader created: %p", myClassLoader); - LOGD("PathClassLoader loading dexPath[%s]\n", dexPath); - jclass entry_class = findClassFromLoader(env, myClassLoader, ENTRY_CLASS_NAME); - if (NULL != entry_class) { - LOGD("HookEntry Class %p", entry_class); - env->RegisterNatives(entry_class, hookMethods, NELEM(hookMethods)); - isInited = true; - LOGD("RegisterNatives succeed for HookEntry."); - } else { - LOGE("HookEntry class is null. %d", getpid()); - } - - //load lib sandhook - void* lib_sandhook; - if (sizeof(void*) == 8) { - lib_sandhook = dlopen("/system/lib64/libsandhook.edxp.so", RTLD_NOW); - } else { - lib_sandhook = dlopen("/system/lib/libsandhook.edxp.so", RTLD_NOW); - } - if (!lib_sandhook) { - LOGW("libsandhook open failed. %s", dlerror()); - return; - } - bool* (*jni_load)(JNIEnv*, jclass, jclass) = reinterpret_cast(dlsym(lib_sandhook, "JNI_Load_Ex")); - - jclass sandhook_class = findClassFromLoader(env, myClassLoader, CLASS_SAND_HOOK); - jclass nevercall_class = findClassFromLoader(env, myClassLoader, CLASS_NEVER_CALL); - if (!sandhook_class || !nevercall_class) { // fail-fast - return; - } - if (!jni_load(env, sandhook_class, nevercall_class)) { - LOGE("SandHook: HookEntry class error. %d", getpid()); - } -} - -jstring getThrowableMessage(JNIEnv *env, jobject throwable) { - if (!throwable) { - LOGE("throwable is null."); - return NULL; - } - jclass jthrowableClass = env->GetObjectClass(throwable); - jmethodID getMsgMid = env->GetMethodID(jthrowableClass, "getMessage", "()Ljava/lang/String;"); - if (getMsgMid == 0) { - LOGE("get Throwable.getMessage method id failed."); - return NULL; - } - return (jstring) env->CallObjectMethod(throwable, getMsgMid); -} - -jclass findClassFromLoader(JNIEnv *env, jobject classLoader, const char *className) { - jclass clz = env->GetObjectClass(classLoader); - jmethodID mid = env->GetMethodID(clz, "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;"); - if (env->ExceptionOccurred()) { - LOGE("loadClass method not found"); - env->ExceptionClear(); - } else { - LOGD("loadClass method %p", mid); - } - jclass ret = NULL; - if (!mid) { - mid = env->GetMethodID(clz, "findClass", "(Ljava/lang/String;)Ljava/lang/Class;"); - if (env->ExceptionOccurred()) { - LOGE("findClass method not found"); - env->ExceptionClear(); - } else { - LOGD("findClass method %p", mid); - } - } - if (mid) { - jstring className_str = env->NewStringUTF(className); - jobject tmp = env->CallObjectMethod(classLoader, mid, className_str); - jthrowable exception = env->ExceptionOccurred(); - if (exception) { - jstring message = getThrowableMessage(env, exception); - const char *message_char = env->GetStringUTFChars(message, JNI_FALSE); - LOGE("Error when findClass %s: %s", className, message_char); - env->ReleaseStringUTFChars(message, message_char); - env->ExceptionClear(); - } - if (NULL != tmp) { - LOGD("findClassFromLoader %p", tmp); - ret = (jclass) tmp; - } - } else { - LOGE("no method found"); - } - if (ret == NULL) { - LOGE("class %s not found.", className); - } - return ret; -} \ No newline at end of file diff --git a/edxp-core/src/main/cpp/main/java_hook/java_hook.h b/edxp-core/src/main/cpp/main/java_hook/java_hook.h deleted file mode 100644 index 9e678baf..00000000 --- a/edxp-core/src/main/cpp/main/java_hook/java_hook.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef _JAVAHOOK_H -#define _JAVAHOOK_H - -#include -#include - -#define NELEM(x) (sizeof(x)/sizeof((x)[0])) - -extern jobject gInjectDexClassLoader; - -void loadDexAndInit(JNIEnv *env, const char *dexPath); - -jclass findClassFromLoader(JNIEnv *env, jobject classLoader, const char *className); - -#endif // _JAVAHOOK_H \ No newline at end of file diff --git a/edxp-core/src/main/cpp/main/main.cpp b/edxp-core/src/main/cpp/main/main.cpp deleted file mode 100644 index 8318ff2f..00000000 --- a/edxp-core/src/main/cpp/main/main.cpp +++ /dev/null @@ -1,79 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "inject/framework_hook.h" -#include "native_hook/native_hook.h" - -#include "include/logging.h" -#include "include/misc.h" - -#include "include/config.h" - -extern "C" { - -__attribute__((visibility("default"))) void onModuleLoaded() { - LOGI("onModuleLoaded: welcome to EdXposed!"); - install_inline_hooks(); -} - -__attribute__((visibility("default"))) int shouldSkipUid(int uid) { - return 0; -} - -__attribute__((visibility("default"))) void -nativeForkAndSpecializePre(JNIEnv *env, jclass clazz, jint *_uid, jint *gid, jintArray *gids, - jint *runtime_flags, - jobjectArray *rlimits, jint *_mount_external, jstring *se_info, - jstring *se_name, - jintArray *fdsToClose, jintArray *fdsToIgnore, jboolean *is_child_zygote, - jstring *instructionSet, jstring *appDataDir, jstring *packageName, - jobjectArray *packagesForUID, jstring *sandboxId) { - onNativeForkAndSpecializePre(env, clazz, *_uid, *gid, *gids, *runtime_flags, *rlimits, - *_mount_external, *se_info, *se_name, *fdsToClose, *fdsToIgnore, - *is_child_zygote, *instructionSet, *appDataDir); -} - -__attribute__((visibility("default"))) int nativeForkAndSpecializePost(JNIEnv *env, jclass clazz, - jint res) { - return onNativeForkAndSpecializePost(env, clazz, res); -} - -__attribute__((visibility("default"))) -void nativeForkSystemServerPre(JNIEnv *env, jclass clazz, uid_t *uid, gid_t *gid, jintArray *gids, - jint *runtime_flags, - jobjectArray *rlimits, jlong *permittedCapabilities, - jlong *effectiveCapabilities) { - onNativeForkSystemServerPre(env, clazz, *uid, *gid, *gids, *runtime_flags, *rlimits, - *permittedCapabilities, *effectiveCapabilities); -} - -__attribute__((visibility("default"))) -int nativeForkSystemServerPost(JNIEnv *env, jclass clazz, jint res) { - return onNativeForkSystemServerPost(env, clazz, res); -} - -__attribute__((visibility("default"))) void specializeAppProcessPre( - JNIEnv *env, jclass clazz, jint *_uid, jint *gid, jintArray *gids, jint *runtimeFlags, - jobjectArray *rlimits, jint *mountExternal, jstring *seInfo, jstring *niceName, - jboolean *startChildZygote, jstring *instructionSet, jstring *appDataDir, - jstring *packageName, jobjectArray *packagesForUID, jstring *sandboxId) { - onNativeForkAndSpecializePre(env, clazz, *_uid, *gid, *gids, *runtimeFlags, *rlimits, - *mountExternal, *seInfo, *niceName, nullptr, nullptr, - *startChildZygote, *instructionSet, *appDataDir); -} - -__attribute__((visibility("default"))) int specializeAppProcessPost( - JNIEnv *env, jclass clazz) { - return onNativeForkAndSpecializePost(env, clazz, 0); -} - -} diff --git a/edxp-core/src/main/cpp/main/native_hook/native_hook.cpp b/edxp-core/src/main/cpp/main/native_hook/native_hook.cpp deleted file mode 100644 index 51403971..00000000 --- a/edxp-core/src/main/cpp/main/native_hook/native_hook.cpp +++ /dev/null @@ -1,323 +0,0 @@ - -#include -#include -#include -#include -#include -#include - -#include "include/logging.h" -#include "native_hook.h" -#include "riru_hook.h" - -static bool inlineHooksInstalled = false; - -static const char *(*getDesc)(void *, std::string *); - -static bool (*isInSamePackageBackup)(void *, void *) = nullptr; - -// runtime -void *runtime_ = nullptr; - -void (*deoptBootImage)(void *runtime) = nullptr; - -bool (*runtimeInitBackup)(void *runtime, void *mapAddr) = nullptr; - -void *class_linker_ = nullptr; - -static void *(*classLinkerCstBackup)(void *, void *) = nullptr; - -void (*deoptMethod)(void *, void *) = nullptr; - -static void (*heapPreForkBackup)(void *) = nullptr; - -bool my_runtimeInit(void *runtime, void *mapAddr) { - if (!runtimeInitBackup) { - LOGE("runtimeInitBackup is null"); - return false; - } - LOGI("runtimeInit starts"); - bool result = runtimeInitBackup(runtime, mapAddr); - if (!deoptBootImage) { - LOGE("deoptBootImageSym is null, skip deoptBootImage"); - } else { - LOGI("deoptBootImage starts"); - deoptBootImage(runtime); - LOGI("deoptBootImage finishes"); - } - LOGI("runtimeInit finishes"); - return result; -} - -static bool onIsInSamePackageCalled(void *thiz, void *that) { - std::string storage1, storage2; - const char *thisDesc = getDesc(thiz, &storage1); - const char *thatDesc = getDesc(that, &storage2); - // Note: these identifiers should be consistent with those in Java layer - if (strstr(thisDesc, "EdHooker_") != nullptr - || strstr(thatDesc, "EdHooker_") != nullptr - || strstr(thisDesc, "com/elderdrivers/riru/") != nullptr - || strstr(thatDesc, "com/elderdrivers/riru/") != nullptr) { - return true; - } - // for MIUI resources hooking - if (strstr(thisDesc, "android/content/res/MiuiTypedArray") != nullptr - || strstr(thatDesc, "android/content/res/MiuiTypedArray") != nullptr - || strstr(thisDesc, "android/content/res/XResources$XTypedArray") != nullptr - || strstr(thatDesc, "android/content/res/XResources$XTypedArray") != nullptr) { - return true; - } - return (*isInSamePackageBackup)(thiz, that); -} - -static bool onInvokeHiddenAPI() { - return false; -} - -/** - * NOTICE: - * After Android Q(10.0), GetMemberActionImpl has been renamed to ShouldDenyAccessToMemberImpl, - * But we don't know the symbols until it's published. - * @author asLody - */ -static bool disableHiddenAPIPolicyImpl(int api_level, void *artHandle, - void (*hookFun)(void *, void *, void **)) { - if (api_level < ANDROID_P) { - return true; - } - void *symbol = nullptr; - // Android P : Preview 1 ~ 4 version - symbol = dlsym(artHandle, - "_ZN3art9hiddenapi25ShouldBlockAccessToMemberINS_8ArtFieldEEEbPT_PNS_6ThreadENSt3__18functionIFbS6_EEENS0_12AccessMethodE"); - if (symbol) { - hookFun(symbol, reinterpret_cast(onInvokeHiddenAPI), nullptr); - } - symbol = dlsym(artHandle, - "_ZN3art9hiddenapi25ShouldBlockAccessToMemberINS_9ArtMethodEEEbPT_PNS_6ThreadENSt3__18functionIFbS6_EEENS0_12AccessMethodE" - ); - - if (symbol) { - hookFun(symbol, reinterpret_cast(onInvokeHiddenAPI), nullptr); - return true; - } - // Android P : Release version - symbol = dlsym(artHandle, - "_ZN3art9hiddenapi6detail19GetMemberActionImplINS_8ArtFieldEEENS0_6ActionEPT_NS_20HiddenApiAccessFlags7ApiListES4_NS0_12AccessMethodE" - ); - if (symbol) { - hookFun(symbol, reinterpret_cast(onInvokeHiddenAPI), nullptr); - } - symbol = dlsym(artHandle, - "_ZN3art9hiddenapi6detail19GetMemberActionImplINS_9ArtMethodEEENS0_6ActionEPT_NS_20HiddenApiAccessFlags7ApiListES4_NS0_12AccessMethodE" - ); - if (symbol) { - hookFun(symbol, reinterpret_cast(onInvokeHiddenAPI), nullptr); - } - return symbol != nullptr; -} - -static void hookIsInSamePackage(int api_level, void *artHandle, - void (*hookFun)(void *, void *, void **)) { - // 5.0 - 7.1 - const char *isInSamePackageSym = "_ZN3art6mirror5Class15IsInSamePackageEPS1_"; - const char *getDescriptorSym = "_ZN3art6mirror5Class13GetDescriptorEPNSt3__112basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEE"; - if (api_level >= ANDROID_O) { - // 8.0 and later - isInSamePackageSym = "_ZN3art6mirror5Class15IsInSamePackageENS_6ObjPtrIS1_EE"; - } - void *original = dlsym(artHandle, isInSamePackageSym); - if (!original) { - LOGE("can't get isInSamePackageSym: %s", dlerror()); - return; - } - void *getDescSym = dlsym(artHandle, getDescriptorSym); - if (!getDescSym) { - LOGE("can't get GetDescriptorSym: %s", dlerror()); - return; - } - getDesc = reinterpret_cast(getDescSym); - hookFun(original, reinterpret_cast(onIsInSamePackageCalled), - reinterpret_cast(&isInSamePackageBackup)); -} - -void *my_classLinkerCst(void *classLinker, void *internTable) { - LOGI("classLinkerCst starts"); - void *result = classLinkerCstBackup(classLinker, internTable); - if (class_linker_ != classLinker) { - LOGI("class_linker_ changed from %p to %p", class_linker_, classLinker); - class_linker_ = classLinker; - } - LOGI("classLinkerCst finishes"); - return result; -} - -void hookInstrumentation(int api_level, void *artHandle, void (*hookFun)(void *, void *, void **)) { - if (api_level < ANDROID_M) { - // 5.x not supported - return; - } - void *classLinkerCstSym = dlsym(artHandle, - "_ZN3art11ClassLinkerC2EPNS_11InternTableE"); - if (!classLinkerCstSym) { - LOGE("can't get classLinkerCstSym: %s", dlerror()); - return; - } - deoptMethod = reinterpret_cast( - dlsym(artHandle, - "_ZNK3art11ClassLinker27SetEntryPointsToInterpreterEPNS_9ArtMethodE")); - if (!deoptMethod) { - LOGE("can't get deoptMethodSym: %s", dlerror()); - return; - } - hookFun(classLinkerCstSym, reinterpret_cast(my_classLinkerCst), - reinterpret_cast(&classLinkerCstBackup)); - LOGI("classLinkerCst hooked"); -} - -std::vector deoptedMethods; - -void deoptimize_method(JNIEnv *env, jclass clazz, jobject method) { - if (!deoptMethod) { - LOGE("deoptMethodSym is null, skip deopt"); - return; - } - if (!class_linker_) { - LOGE("class_linker_ is null, skip deopt"); - return; - } - void *reflected_method = env->FromReflectedMethod(method); - if (std::find(deoptedMethods.begin(), deoptedMethods.end(), reflected_method) != - deoptedMethods.end()) { - LOGD("method %p has been deopted before, skip...", reflected_method); - return; - } - LOGD("deoptimizing method: %p", reflected_method); - deoptMethod(class_linker_, reflected_method); - deoptedMethods.push_back(reflected_method); - LOGD("method deoptimized: %p", reflected_method); -} - -void hookRuntime(int api_level, void *artHandle, void (*hookFun)(void *, void *, void **)) { - if (!is_deopt_boot_image_enabled()) { - return; - } - void *runtimeInitSym = nullptr; - if (api_level >= ANDROID_O) { - // only oreo has deoptBootImageSym in Runtime - runtime_ = dlsym(artHandle, "_ZN3art7Runtime9instance_E"); - if (!runtime_) { LOGW("runtime instance not found"); } - runtimeInitSym = dlsym(artHandle, "_ZN3art7Runtime4InitEONS_18RuntimeArgumentMapE"); - if (!runtimeInitSym) { - LOGE("can't find runtimeInitSym: %s", dlerror()); - return; - } - deoptBootImage = reinterpret_cast(dlsym(artHandle, - "_ZN3art7Runtime19DeoptimizeBootImageEv")); - if (!deoptBootImage) { - LOGE("can't find deoptBootImageSym: %s", dlerror()); - return; - } - LOGI("start to hook runtimeInitSym"); - hookFun(runtimeInitSym, reinterpret_cast(my_runtimeInit), - reinterpret_cast(&runtimeInitBackup)); - LOGI("runtimeInitSym hooked"); - } else { - // TODO support deoptBootImage for Android 7.1 and before? - LOGI("hooking Runtime skipped"); - } -} - -void (*suspendAll)(ScopedSuspendAll *, const char *, bool) = nullptr; - -void (*resumeAll)(ScopedSuspendAll *) = nullptr; - -int (*waitGcInternal)(void *, int, void *) = nullptr; - -void *heap_ = nullptr; - -int waitGc(int gcCause, void *thread) { - if (!heap_) { - LOGE("heap_ is null"); - return -1; - } - return waitGcInternal(heap_, gcCause, thread); -} - -static void myHeapPreFork(void *heap) { - heap_ = heap; - heapPreForkBackup(heap); -} - -void getSuspendSyms(int api_level, void *artHandle, void (*hookFun)(void *, void *, void **)) { - if (api_level >= ANDROID_LOLLIPOP) { - waitGcInternal = reinterpret_cast(dlsym(artHandle, - "_ZN3art2gc4Heap19WaitForGcToCompleteENS0_7GcCauseEPNS_6ThreadE")); - void *heapPreFork = dlsym(artHandle, "_ZN3art2gc4Heap13PreZygoteForkEv"); - if (!heapPreFork) { - LOGE("can't find heapPreFork: %s", dlerror()); - } else { - // a chance to get pointer of the heap - hookFun(heapPreFork, reinterpret_cast(myHeapPreFork), - reinterpret_cast(&heapPreForkBackup)); - LOGI("heapPreFork hooked."); - } - } - if (api_level >= ANDROID_N) { - suspendAll = reinterpret_cast(dlsym( - artHandle, - "_ZN3art16ScopedSuspendAllC2EPKcb")); - resumeAll = reinterpret_cast(dlsym(artHandle, - "_ZN3art16ScopedSuspendAllD2Ev")); - } -} - -void install_inline_hooks() { - if (inlineHooksInstalled) { - LOGI("inline hooks installed, skip"); - return; - } - LOGI("start to install inline hooks"); - int api_level = GetAndroidApiLevel(); - if (api_level < ANDROID_LOLLIPOP) { - LOGE("api level not supported: %d, skip", api_level); - return; - } - install_riru_hooks(); - LOGI("using api level %d", api_level); -#ifdef __LP64__ - void *whaleHandle = dlopen(kLibWhalePath, RTLD_LAZY | RTLD_GLOBAL); - if (!whaleHandle) { - LOGE("can't open libwhale: %s", dlerror()); - return; - } - void *hookFunSym = dlsym(whaleHandle, "WInlineHookFunction"); -#else - void *hookFunSym = (void *)(MSHookFunction); -#endif - if (!hookFunSym) { - LOGE("can't get WInlineHookFunction: %s", dlerror()); - return; - } - void (*hookFun)(void *, void *, void **) = reinterpret_cast(hookFunSym); - void *artHandle = dlopen(kLibArtPath, RTLD_LAZY | RTLD_GLOBAL); - if (!artHandle) { - LOGE("can't open libart: %s", dlerror()); - return; - } - hookRuntime(api_level, artHandle, hookFun); - hookInstrumentation(api_level, artHandle, hookFun); - getSuspendSyms(api_level, artHandle, hookFun); - hookIsInSamePackage(api_level, artHandle, hookFun); - if (disableHiddenAPIPolicyImpl(api_level, artHandle, hookFun)) { - LOGI("disableHiddenAPIPolicyImpl done."); - } else { - LOGE("disableHiddenAPIPolicyImpl failed."); - } -#ifdef __LP64__ - dlclose(whaleHandle); -#endif - dlclose(artHandle); - LOGI("install inline hooks done"); -} - diff --git a/edxp-core/src/main/cpp/main/native_hook/native_hook.h b/edxp-core/src/main/cpp/main/native_hook/native_hook.h deleted file mode 100644 index 291bafc4..00000000 --- a/edxp-core/src/main/cpp/main/native_hook/native_hook.h +++ /dev/null @@ -1,35 +0,0 @@ -#ifndef HOOK_H -#define HOOK_H - -#include - -#if defined(__LP64__) -static constexpr const char *kLibArtPath = "/system/lib64/libart.so"; -static constexpr const char *kLibWhalePath = "/system/lib64/libwhale.edxp.so"; -#else -static constexpr const char *kLibArtPath = "/system/lib/libart.so"; -static constexpr const char *kLibWhalePath = "/system/lib/libwhale.edxp.so"; -#endif - -#define XHOOK_REGISTER(NAME) \ - if (xhook_register(".*", #NAME, (void*) new_##NAME, (void **) &old_##NAME) != 0) \ - LOGE("failed to register hook " #NAME "."); \ - -#define NEW_FUNC_DEF(ret, func, ...) \ - static ret (*old_##func)(__VA_ARGS__); \ - static ret new_##func(__VA_ARGS__) - -class ScopedSuspendAll { -}; - -extern void (*suspendAll)(ScopedSuspendAll *, const char *, bool); - -extern void (*resumeAll)(ScopedSuspendAll *); - -extern int waitGc(int, void *); - -void install_inline_hooks(); - -void deoptimize_method(JNIEnv *env, jclass clazz, jobject method); - -#endif // HOOK_H diff --git a/edxp-core/src/main/cpp/main/native_hook/resource_hook.cpp b/edxp-core/src/main/cpp/main/native_hook/resource_hook.cpp deleted file mode 100644 index 56e37865..00000000 --- a/edxp-core/src/main/cpp/main/native_hook/resource_hook.cpp +++ /dev/null @@ -1,185 +0,0 @@ -// -// Created by solo on 2019/3/24. -// - -#include -#include -#include -#include -#include -#include "resource_hook.h" - -#define CLASS_XRESOURCES "android/content/res/XResources" - -jclass classXResources; -jmethodID methodXResourcesTranslateAttrId; -jmethodID methodXResourcesTranslateResId; - -int32_t (*ResXMLParser_next)(void *); - -void (*ResXMLParser_restart)(void *); - -int32_t (*ResXMLParser_getAttributeNameID)(void *, int); - -char16_t *(*ResStringPool_stringAt)(const void *, int32_t, size_t *); - -bool prepareSymbols() { - void *fwHandle = dlopen(kLibFwPath, RTLD_LAZY | RTLD_GLOBAL); - if (!fwHandle) { - LOGE("can't open libandroidfw: %s", dlerror()); - return false; - } - ResXMLParser_next = reinterpret_cast(dlsym(fwHandle, - "_ZN7android12ResXMLParser4nextEv")); - if (!ResXMLParser_next) { - LOGE("can't get ResXMLParser_next: %s", dlerror()); - return false; - } - ResXMLParser_restart = reinterpret_cast(dlsym(fwHandle, - "_ZN7android12ResXMLParser7restartEv")); - if (!ResXMLParser_restart) { - LOGE("can't get ResXMLParser_restart: %s", dlerror()); - return false; - } - ResXMLParser_getAttributeNameID = reinterpret_cast(dlsym(fwHandle, -#if defined(__LP64__) - "_ZNK7android12ResXMLParser18getAttributeNameIDEm" -#else - "_ZNK7android12ResXMLParser18getAttributeNameIDEj" -#endif - )); - if (!ResXMLParser_getAttributeNameID) { - LOGE("can't get ResXMLParser_getAttributeNameID: %s", dlerror()); - return false; - } - ResStringPool_stringAt = reinterpret_cast(dlsym( - fwHandle, -#if defined(__LP64__) - "_ZNK7android13ResStringPool8stringAtEmPm" -#else - "_ZNK7android13ResStringPool8stringAtEjPj" -#endif - )); - if (!ResStringPool_stringAt) { - LOGE("can't get ResStringPool_stringAt: %s", dlerror()); - return false; - } - return true; -} - -int register_natives_XResources(JNIEnv *env, jclass clazz) { - const JNINativeMethod methods[] = { - {"rewriteXmlReferencesNative", - "(JLandroid/content/res/XResources;Landroid/content/res/Resources;)V", - (void *) XResources_rewriteXmlReferencesNative}, - }; - return env->RegisterNatives(clazz, methods, NELEM(methods)); -} - -jboolean XposedBridge_initXResourcesNative(JNIEnv *env, jclass) { - classXResources = env->FindClass(CLASS_XRESOURCES); - if (classXResources == NULL) { - LOGE("Error while loading XResources class '%s':", CLASS_XRESOURCES); - env->ExceptionClear(); - return false; - } - classXResources = reinterpret_cast(env->NewGlobalRef(classXResources)); - - if (register_natives_XResources(env, classXResources) != JNI_OK) { - LOGE("Could not register natives for '%s'", CLASS_XRESOURCES); - env->ExceptionClear(); - return false; - } - - methodXResourcesTranslateResId = env->GetStaticMethodID(classXResources, "translateResId", - "(ILandroid/content/res/XResources;Landroid/content/res/Resources;)I"); - if (methodXResourcesTranslateResId == NULL) { - LOGE("ERROR: could not find method %s.translateResId(int, XResources, Resources)", - CLASS_XRESOURCES); - env->ExceptionClear(); - return false; - } - - methodXResourcesTranslateAttrId = env->GetStaticMethodID(classXResources, "translateAttrId", - "(Ljava/lang/String;Landroid/content/res/XResources;)I"); - if (methodXResourcesTranslateAttrId == NULL) { - LOGE("ERROR: could not find method %s.findAttrId(String, XResources)", CLASS_XRESOURCES); - env->ExceptionClear(); - return false; - } - - return prepareSymbols(); -} - -void XResources_rewriteXmlReferencesNative(JNIEnv *env, jclass, - jlong parserPtr, jobject origRes, jobject repRes) { - - ResXMLParser *parser = (ResXMLParser *) parserPtr; - - if (parser == nullptr) - return; - - const ResXMLTree &mTree = parser->mTree; - uint32_t *mResIds = (uint32_t *) mTree.mResIds; - ResXMLTree_attrExt *tag; - int attrCount; - - do { - switch (ResXMLParser_next(parser)) { - case ResXMLParser::START_TAG: - tag = (ResXMLTree_attrExt *) parser->mCurExt; - attrCount = dtohs(tag->attributeCount); - for (int idx = 0; idx < attrCount; idx++) { - ResXMLTree_attribute *attr = (ResXMLTree_attribute *) - (((const uint8_t *) tag) - + dtohs(tag->attributeStart) - + (dtohs(tag->attributeSize) * idx)); - - // find resource IDs for attribute names - int32_t attrNameID = ResXMLParser_getAttributeNameID(parser, idx); - // only replace attribute name IDs for app packages - if (attrNameID >= 0 && (size_t) attrNameID < mTree.mNumResIds && - dtohl(mResIds[attrNameID]) >= 0x7f000000) { - size_t attNameLen; - const char16_t *attrName = ResStringPool_stringAt(&(mTree.mStrings), - attrNameID, &attNameLen); - jint attrResID = env->CallStaticIntMethod(classXResources, - methodXResourcesTranslateAttrId, - env->NewString( - (const jchar *) attrName, - attNameLen), origRes); - if (env->ExceptionCheck()) - goto leave; - - mResIds[attrNameID] = htodl(attrResID); - } - - // find original resource IDs for reference values (app packages only) - if (attr->typedValue.dataType != Res_value::TYPE_REFERENCE) - continue; - - jint oldValue = dtohl(attr->typedValue.data); - if (oldValue < 0x7f000000) - continue; - - jint newValue = env->CallStaticIntMethod(classXResources, - methodXResourcesTranslateResId, - oldValue, origRes, repRes); - if (env->ExceptionCheck()) - goto leave; - - if (newValue != oldValue) - attr->typedValue.data = htodl(newValue); - } - continue; - case ResXMLParser::END_DOCUMENT: - case ResXMLParser::BAD_DOCUMENT: - goto leave; - default: - continue; - } - } while (true); - - leave: - ResXMLParser_restart(parser); -} \ No newline at end of file diff --git a/edxp-core/src/main/cpp/main/native_hook/resource_hook.h b/edxp-core/src/main/cpp/main/native_hook/resource_hook.h deleted file mode 100644 index 8208f302..00000000 --- a/edxp-core/src/main/cpp/main/native_hook/resource_hook.h +++ /dev/null @@ -1,276 +0,0 @@ -// -// Created by solo on 2019/3/24. -// - -#ifndef EDXPOSED_TEMP_RESOURCE_HOOK_H -#define EDXPOSED_TEMP_RESOURCE_HOOK_H - -#include - -#if defined(__LP64__) -static constexpr const char *kLibFwPath = "/system/lib64/libandroidfw.so"; -#else -static constexpr const char *kLibFwPath = "/system/lib/libandroidfw.so"; -#endif - -jboolean XposedBridge_initXResourcesNative(JNIEnv *env, jclass); - -void XResources_rewriteXmlReferencesNative(JNIEnv *env, jclass, - jlong parserPtr, jobject origRes, jobject repRes); - -typedef int32_t status_t; - -enum { - RES_NULL_TYPE = 0x0000, - RES_STRING_POOL_TYPE = 0x0001, - RES_TABLE_TYPE = 0x0002, - RES_XML_TYPE = 0x0003, - // Chunk types in RES_XML_TYPE - RES_XML_FIRST_CHUNK_TYPE = 0x0100, - RES_XML_START_NAMESPACE_TYPE = 0x0100, - RES_XML_END_NAMESPACE_TYPE = 0x0101, - RES_XML_START_ELEMENT_TYPE = 0x0102, - RES_XML_END_ELEMENT_TYPE = 0x0103, - RES_XML_CDATA_TYPE = 0x0104, - RES_XML_LAST_CHUNK_TYPE = 0x017f, - // This contains a uint32_t array mapping strings in the string - // pool back to resource identifiers. It is optional. - RES_XML_RESOURCE_MAP_TYPE = 0x0180, - // Chunk types in RES_TABLE_TYPE - RES_TABLE_PACKAGE_TYPE = 0x0200, - RES_TABLE_TYPE_TYPE = 0x0201, - RES_TABLE_TYPE_SPEC_TYPE = 0x0202, - RES_TABLE_LIBRARY_TYPE = 0x0203 -}; - -struct ResXMLTree_node { - void *header; - // Line number in original source file at which this element appeared. - uint32_t lineNumber; - // Optional XML comment that was associated with this element; -1 if none. - void *comment; -}; - -class ResXMLTree; - -class ResXMLParser { - -public: - enum event_code_t { - BAD_DOCUMENT = -1, - START_DOCUMENT = 0, - END_DOCUMENT = 1, - - FIRST_CHUNK_CODE = RES_XML_FIRST_CHUNK_TYPE, - - START_NAMESPACE = RES_XML_START_NAMESPACE_TYPE, - END_NAMESPACE = RES_XML_END_NAMESPACE_TYPE, - START_TAG = RES_XML_START_ELEMENT_TYPE, - END_TAG = RES_XML_END_ELEMENT_TYPE, - TEXT = RES_XML_CDATA_TYPE - }; - - const ResXMLTree &mTree; - event_code_t mEventCode; - const ResXMLTree_node *mCurNode; - const void *mCurExt; -}; - -class ResStringPool { - -public: - status_t mError; - void *mOwnedData; - const void *mHeader; - size_t mSize; - mutable pthread_mutex_t mDecodeLock; - const uint32_t *mEntries; - const uint32_t *mEntryStyles; - const void *mStrings; - char16_t mutable **mCache; - uint32_t mStringPoolSize; // number of uint16_t - const uint32_t *mStyles; - uint32_t mStylePoolSize; // number of uint32_t -}; - - -class ResXMLTree : public ResXMLParser { - -public: - void *mDynamicRefTable; - status_t mError; - void *mOwnedData; - const void *mHeader; - size_t mSize; - const uint8_t *mDataEnd; - ResStringPool mStrings; - const uint32_t *mResIds; - size_t mNumResIds; - const ResXMLTree_node *mRootNode; - const void *mRootExt; - event_code_t mRootCode; -}; - -struct ResStringPool_ref { - - // Index into the string pool table (uint32_t-offset from the indices - // immediately after ResStringPool_header) at which to find the location - // of the string data in the pool. - uint32_t index; -}; - -struct ResXMLTree_attrExt { - - // String of the full namespace of this element. - struct ResStringPool_ref ns; - - // String name of this node if it is an ELEMENT; the raw - // character data if this is a CDATA node. - struct ResStringPool_ref name; - - // Byte offset from the start of this structure where the attributes start. - uint16_t attributeStart; - - // Size of the ResXMLTree_attribute structures that follow. - uint16_t attributeSize; - - // Number of attributes associated with an ELEMENT. These are - // available as an array of ResXMLTree_attribute structures - // immediately following this node. - uint16_t attributeCount; - - // Index (1-based) of the "id" attribute. 0 if none. - uint16_t idIndex; - - // Index (1-based) of the "class" attribute. 0 if none. - uint16_t classIndex; - - // Index (1-based) of the "style" attribute. 0 if none. - uint16_t styleIndex; -}; - -struct Res_value { - - // Number of bytes in this structure. - uint16_t size; - // Always set to 0. - uint8_t res0; - - // Type of the data value. - enum : uint8_t { - // The 'data' is either 0 or 1, specifying this resource is either - // undefined or empty, respectively. - TYPE_NULL = 0x00, - // The 'data' holds a ResTable_ref, a reference to another resource - // table entry. - TYPE_REFERENCE = 0x01, - // The 'data' holds an attribute resource identifier. - TYPE_ATTRIBUTE = 0x02, - // The 'data' holds an index into the containing resource table's - // global value string pool. - TYPE_STRING = 0x03, - // The 'data' holds a single-precision floating point number. - TYPE_FLOAT = 0x04, - // The 'data' holds a complex number encoding a dimension value, - // such as "100in". - TYPE_DIMENSION = 0x05, - // The 'data' holds a complex number encoding a fraction of a - // container. - TYPE_FRACTION = 0x06, - // The 'data' holds a dynamic ResTable_ref, which needs to be - // resolved before it can be used like a TYPE_REFERENCE. - TYPE_DYNAMIC_REFERENCE = 0x07, - // The 'data' holds an attribute resource identifier, which needs to be resolved - // before it can be used like a TYPE_ATTRIBUTE. - TYPE_DYNAMIC_ATTRIBUTE = 0x08, - // Beginning of integer flavors... - TYPE_FIRST_INT = 0x10, - // The 'data' is a raw integer value of the form n..n. - TYPE_INT_DEC = 0x10, - // The 'data' is a raw integer value of the form 0xn..n. - TYPE_INT_HEX = 0x11, - // The 'data' is either 0 or 1, for input "false" or "true" respectively. - TYPE_INT_BOOLEAN = 0x12, - // Beginning of color integer flavors... - TYPE_FIRST_COLOR_INT = 0x1c, - // The 'data' is a raw integer value of the form #aarrggbb. - TYPE_INT_COLOR_ARGB8 = 0x1c, - // The 'data' is a raw integer value of the form #rrggbb. - TYPE_INT_COLOR_RGB8 = 0x1d, - // The 'data' is a raw integer value of the form #argb. - TYPE_INT_COLOR_ARGB4 = 0x1e, - // The 'data' is a raw integer value of the form #rgb. - TYPE_INT_COLOR_RGB4 = 0x1f, - // ...end of integer flavors. - TYPE_LAST_COLOR_INT = 0x1f, - // ...end of integer flavors. - TYPE_LAST_INT = 0x1f - }; - uint8_t dataType; - // Structure of complex data values (TYPE_UNIT and TYPE_FRACTION) - enum { - // Where the unit type information is. This gives us 16 possible - // types, as defined below. - COMPLEX_UNIT_SHIFT = 0, - COMPLEX_UNIT_MASK = 0xf, - // TYPE_DIMENSION: Value is raw pixels. - COMPLEX_UNIT_PX = 0, - // TYPE_DIMENSION: Value is Device Independent Pixels. - COMPLEX_UNIT_DIP = 1, - // TYPE_DIMENSION: Value is a Scaled device independent Pixels. - COMPLEX_UNIT_SP = 2, - // TYPE_DIMENSION: Value is in points. - COMPLEX_UNIT_PT = 3, - // TYPE_DIMENSION: Value is in inches. - COMPLEX_UNIT_IN = 4, - // TYPE_DIMENSION: Value is in millimeters. - COMPLEX_UNIT_MM = 5, - // TYPE_FRACTION: A basic fraction of the overall size. - COMPLEX_UNIT_FRACTION = 0, - // TYPE_FRACTION: A fraction of the parent size. - COMPLEX_UNIT_FRACTION_PARENT = 1, - // Where the radix information is, telling where the decimal place - // appears in the mantissa. This give us 4 possible fixed point - // representations as defined below. - COMPLEX_RADIX_SHIFT = 4, - COMPLEX_RADIX_MASK = 0x3, - // The mantissa is an integral number -- i.e., 0xnnnnnn.0 - COMPLEX_RADIX_23p0 = 0, - // The mantissa magnitude is 16 bits -- i.e, 0xnnnn.nn - COMPLEX_RADIX_16p7 = 1, - // The mantissa magnitude is 8 bits -- i.e, 0xnn.nnnn - COMPLEX_RADIX_8p15 = 2, - // The mantissa magnitude is 0 bits -- i.e, 0x0.nnnnnn - COMPLEX_RADIX_0p23 = 3, - // Where the actual value is. This gives us 23 bits of - // precision. The top bit is the sign. - COMPLEX_MANTISSA_SHIFT = 8, - COMPLEX_MANTISSA_MASK = 0xffffff - }; - // Possible data values for TYPE_NULL. - enum { - // The value is not defined. - DATA_NULL_UNDEFINED = 0, - // The value is explicitly defined as empty. - DATA_NULL_EMPTY = 1 - }; - // The data for this item, as interpreted according to dataType. - typedef uint32_t data_type; - data_type data; -}; - -struct ResXMLTree_attribute { - // Namespace of this attribute. - struct ResStringPool_ref ns; - - // Name of this attribute. - struct ResStringPool_ref name; - - // The original raw string value of this attribute. - struct ResStringPool_ref rawValue; - - // Processesd typed value of this attribute. - struct Res_value typedValue; -}; - -#endif //EDXPOSED_TEMP_RESOURCE_HOOK_H diff --git a/edxp-core/src/main/cpp/main/native_hook/riru_hook.cpp b/edxp-core/src/main/cpp/main/native_hook/riru_hook.cpp deleted file mode 100644 index 59aa3232..00000000 --- a/edxp-core/src/main/cpp/main/native_hook/riru_hook.cpp +++ /dev/null @@ -1,112 +0,0 @@ -// -// Created by solo on 2019/3/16. -// - -#include -#include -#include -#include -#include -#include -#include -#include "riru_hook.h" - -int api_level = 0; - -#define PROP_KEY_COMPILER_FILTER "dalvik.vm.dex2oat-filter" -#define PROP_KEY_COMPILER_FLAGS "dalvik.vm.dex2oat-flags" -#define PROP_KEY_USEJITPROFILES "dalvik.vm.usejitprofiles" -#define PROP_KEY_PM_BG_DEXOPT "pm.dexopt.bg-dexopt" -#define PROP_VALUE_COMPILER_FILTER "quicken" -#define PROP_VALUE_COMPILER_FLAGS "--inline-max-code-units=0" -#define PROP_VALUE_PM_BG_DEXOPT "speed" - -#define XHOOK_REGISTER(NAME) \ - if (xhook_register(".*", #NAME, (void*) new_##NAME, (void **) &old_##NAME) == 0) { \ - if (riru_get_version() >= 8) { \ - void *f = riru_get_func(#NAME); \ - if (f != nullptr) { \ - memcpy(&old_##NAME, &f, sizeof(void *)); \ - } \ - riru_set_func(#NAME, (void *) new_##NAME); \ - } \ - } else { \ - LOGE("failed to register riru hook " #NAME "."); \ - } - -#define NEW_FUNC_DEF(ret, func, ...) \ - static ret (*old_##func)(__VA_ARGS__); \ - static ret new_##func(__VA_ARGS__) - -NEW_FUNC_DEF(int, __system_property_get, const char *key, char *value) { - int res = old___system_property_get(key, value); - if (key) { - if (strcmp(PROP_KEY_COMPILER_FILTER, key) == 0) { - strcpy(value, PROP_VALUE_COMPILER_FILTER); - LOGI("system_property_get: %s -> %s", key, value); - } else if (strcmp(PROP_KEY_COMPILER_FLAGS, key) == 0) { - strcpy(value, PROP_VALUE_COMPILER_FLAGS); - LOGI("system_property_get: %s -> %s", key, value); - } - if (api_level == ANDROID_O_MR1) { - // https://android.googlesource.com/platform/art/+/f5516d38736fb97bfd0435ad03bbab17ddabbe4e - // Android 8.1 add a fatal check for debugging (removed in Android 9.0), - // which will be triggered by EdXposed in cases where target method is hooked - // (native flag set) after it has been called several times(getCounter() return positive number) - if (strcmp(PROP_KEY_USEJITPROFILES, key) == 0) { - strcpy(value, "false"); - } else if (strcmp(PROP_KEY_PM_BG_DEXOPT, key) == 0) { - // use speed as bg-dexopt filter since that speed-profile won't work after - // jit profiles is disabled - strcpy(value, PROP_VALUE_PM_BG_DEXOPT); - } - LOGD("system_property_get: %s -> %s", key, value); - } - } - return res; -} - -NEW_FUNC_DEF(std::string, - _ZN7android4base11GetPropertyERKNSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEES9_, - const std::string &key, const std::string &default_value) { - std::string res = old__ZN7android4base11GetPropertyERKNSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEES9_( - key, default_value); - if (strcmp(PROP_KEY_COMPILER_FILTER, key.c_str()) == 0) { - res = PROP_VALUE_COMPILER_FILTER; - LOGI("android::base::GetProperty: %s -> %s", key.c_str(), res.c_str()); - } else if (strcmp(PROP_KEY_COMPILER_FLAGS, key.c_str()) == 0) { - res = PROP_VALUE_COMPILER_FLAGS; - LOGI("android::base::GetProperty: %s -> %s", key.c_str(), res.c_str()); - } - if (api_level == ANDROID_O_MR1) { - // see __system_property_get hook above for explanations - if (strcmp(PROP_KEY_USEJITPROFILES, key.c_str()) == 0) { - res = "false"; - } else if (strcmp(PROP_KEY_PM_BG_DEXOPT, key.c_str()) == 0) { - res = PROP_VALUE_PM_BG_DEXOPT; - } - LOGD("android::base::GetProperty: %s -> %s", key.c_str(), res.c_str()); - } - return res; -} - -void install_riru_hooks() { - - LOGI("install riru hook"); - - api_level = GetAndroidApiLevel(); - - XHOOK_REGISTER(__system_property_get); - - if (GetAndroidApiLevel() >= ANDROID_P) { - XHOOK_REGISTER( - _ZN7android4base11GetPropertyERKNSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEES9_); - } - - if (xhook_refresh(0) == 0) { - xhook_clear(); - LOGI("riru hooks installed"); - } else { - LOGE("failed to install riru hooks"); - } -} \ No newline at end of file diff --git a/edxp-core/src/main/cpp/main/native_hook/riru_hook.h b/edxp-core/src/main/cpp/main/native_hook/riru_hook.h deleted file mode 100644 index 29385cd4..00000000 --- a/edxp-core/src/main/cpp/main/native_hook/riru_hook.h +++ /dev/null @@ -1,10 +0,0 @@ -// -// Created by solo on 2019/3/16. -// - -#ifndef EDXPOSED_RIRU_HOOK_H -#define EDXPOSED_RIRU_HOOK_H - -void install_riru_hooks(); - -#endif //EDXPOSED_RIRU_HOOK_H diff --git a/edxp-core/src/main/cpp/main/src/config_manager.cpp b/edxp-core/src/main/cpp/main/src/config_manager.cpp new file mode 100644 index 00000000..1595955f --- /dev/null +++ b/edxp-core/src/main/cpp/main/src/config_manager.cpp @@ -0,0 +1,191 @@ +// +// Created by solo on 2019/5/31. +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include "art/runtime/native/native_util.h" +#include "config_manager.h" + +using namespace std; +using namespace art; + +namespace edxp { + + std::string ConfigManager::RetrieveInstallerPkgName() const { + std::string data_test_path = data_path_prefix_ + kPrimaryInstallerPkgName; + if (access(data_test_path.c_str(), F_OK) == 0) { + LOGI("using installer %s", kPrimaryInstallerPkgName); + return kPrimaryInstallerPkgName; + } + data_test_path = data_path_prefix_ + kSecondaryInstallerPkgName; + if (access(data_test_path.c_str(), F_OK) == 0) { + LOGI("using installer %s", kSecondaryInstallerPkgName); + return kSecondaryInstallerPkgName; + } + data_test_path = data_path_prefix_ + kLegacyInstallerPkgName; + if (access(data_test_path.c_str(), F_OK) == 0) { + LOGI("using installer %s", kLegacyInstallerPkgName); + return kLegacyInstallerPkgName; + } + LOGE("no supported installer app found, using primary as default %s", + kPrimaryInstallerPkgName); + return kPrimaryInstallerPkgName; + } + + void ConfigManager::SnapshotBlackWhiteList() { + DIR *dir; + struct dirent *dent; + dir = opendir(whitelist_path_.c_str()); + if (dir != nullptr) { + while ((dent = readdir(dir)) != nullptr) { + if (dent->d_type == DT_REG) { + const char *fileName = dent->d_name; + LOGI("whitelist: %s", fileName); + white_list_default_.emplace_back(fileName); + } + } + closedir(dir); + } + dir = opendir(blacklist_path_.c_str()); + if (dir != nullptr) { + while ((dent = readdir(dir)) != nullptr) { + if (dent->d_type == DT_REG) { + const char *fileName = dent->d_name; + LOGI("blacklist: %s", fileName); + black_list_default_.emplace_back(fileName); + } + } + closedir(dir); + } + } + + void ConfigManager::InitOnce() { + if (!initialized_) { + use_prot_storage_ = GetAndroidApiLevel() >= ANDROID_N; + data_path_prefix_ = use_prot_storage_ ? "/data/user_de/0/" : "/data/user/0/"; + + installer_pkg_name_ = RetrieveInstallerPkgName(); + base_config_path_ = GetConfigPath(""); + blacklist_path_ = GetConfigPath("blacklist/"); + whitelist_path_ = GetConfigPath("whitelist/"); + use_whitelist_path_ = GetConfigPath("usewhitelist"); + + dynamic_modules_enabled_ = access(GetConfigPath("dynamicmodules").c_str(), F_OK) == 0; + black_white_list_enabled_ = access(GetConfigPath("blackwhitelist").c_str(), F_OK) == 0; + deopt_boot_image_enabled_ = access(GetConfigPath("deoptbootimage").c_str(), F_OK) == 0; + resources_hook_enabled_ = access(GetConfigPath("disable_resources").c_str(), F_OK) != 0; + + // use_white_list snapshot + use_white_list_snapshot_ = access(use_whitelist_path_.c_str(), F_OK) == 0; + LOGI("black/white list mode: %s, using whitelist: %s", + BoolToString(black_white_list_enabled_), BoolToString(use_white_list_snapshot_)); + LOGI("dynamic modules mode: %s", BoolToString(dynamic_modules_enabled_)); + LOGI("resources hook: %s", BoolToString(resources_hook_enabled_)); + LOGI("deopt boot image: %s", BoolToString(deopt_boot_image_enabled_)); + if (black_white_list_enabled_) { + SnapshotBlackWhiteList(); + } + initialized_ = true; + } + } + + bool ConfigManager::IsAppNeedHook(const std::string &app_data_dir) const { + if (!black_white_list_enabled_) { + return true; + } + bool can_access_app_data = access(base_config_path_.c_str(), F_OK) == 0; + bool use_white_list; + if (can_access_app_data) { + use_white_list = access(use_whitelist_path_.c_str(), F_OK) == 0; + } else { + LOGE("can't access config path, using snapshot use_white_list: %s", + app_data_dir.c_str()); + use_white_list = use_white_list_snapshot_; + } + int user = 0; + char package_name[PATH_MAX]; + if (sscanf(app_data_dir.c_str(), "/data/%*[^/]/%d/%s", &user, package_name) != 2) { + if (sscanf(app_data_dir.c_str(), "/data/%*[^/]/%s", package_name) != 1) { + package_name[0] = '\0'; + LOGE("can't parse %s", app_data_dir.c_str()); + return !use_white_list; + } + } + if (strcmp(package_name, kPrimaryInstallerPkgName) == 0 + || strcmp(package_name, kSecondaryInstallerPkgName) == 0 + || strcmp(package_name, kLegacyInstallerPkgName) == 0) { + // always hook installer apps + return true; + } + if (use_white_list) { + if (!can_access_app_data) { + LOGE("can't access config path, using snapshot white list: %s", + app_data_dir.c_str()); + return !(find(white_list_default_.begin(), white_list_default_.end(), + package_name) == + white_list_default_.end()); + } + std::string target_path = whitelist_path_ + package_name; + bool res = access(target_path.c_str(), F_OK) == 0; + LOGD("using whitelist, %s -> %d", app_data_dir.c_str(), res); + return res; + } else { + if (!can_access_app_data) { + LOGE("can't access config path, using snapshot black list: %s", + app_data_dir.c_str()); + return find(black_list_default_.begin(), black_list_default_.end(), package_name) == + black_list_default_.end(); + } + std::string target_path = blacklist_path_ + package_name; + bool res = access(target_path.c_str(), F_OK) != 0; + LOGD("using blacklist, %s -> %d", app_data_dir.c_str(), res); + return res; + } + } + + ALWAYS_INLINE bool ConfigManager::IsBlackWhiteListEnabled() const { + return black_white_list_enabled_; + } + + ALWAYS_INLINE bool ConfigManager::IsDynamicModulesEnabled() const { + return dynamic_modules_enabled_; + } + + ALWAYS_INLINE bool ConfigManager::IsResourcesHookEnabled() const { + return resources_hook_enabled_; + } + + ALWAYS_INLINE bool ConfigManager::IsDeoptBootImageEnabled() const { + return deopt_boot_image_enabled_; + } + + ALWAYS_INLINE std::string ConfigManager::GetInstallerPkgName() const { + return installer_pkg_name_; + } + + ALWAYS_INLINE std::string ConfigManager::GetConfigPath(const std::string &suffix) const { + return data_path_prefix_ + installer_pkg_name_ + "/conf/" + suffix; + }; + + ConfigManager::ConfigManager() { + InitOnce(); + } + + ConfigManager::~ConfigManager() { + initialized_ = false; + } + +} \ No newline at end of file diff --git a/edxp-core/src/main/cpp/main/src/config_manager.h b/edxp-core/src/main/cpp/main/src/config_manager.h new file mode 100644 index 00000000..fb97f63a --- /dev/null +++ b/edxp-core/src/main/cpp/main/src/config_manager.h @@ -0,0 +1,71 @@ + +#pragma once + +#include +#include +#include +#include + +namespace edxp { + + static constexpr const char *kPrimaryInstallerPkgName = "com.solohsu.android.edxp.manager"; + static constexpr const char *kSecondaryInstallerPkgName = "org.meowcat.edxposed.manager"; + static constexpr const char *kLegacyInstallerPkgName = "de.robv.android.xposed.installer"; + + class ConfigManager { + public: + + static ConfigManager *GetInstance() { + if (instance_ == 0) { + instance_ = new ConfigManager(); + } + return instance_; + } + + bool IsBlackWhiteListEnabled() const; + + bool IsDynamicModulesEnabled() const; + + bool IsResourcesHookEnabled() const; + + bool IsDeoptBootImageEnabled() const; + + std::string GetInstallerPkgName() const; + + bool IsAppNeedHook(const std::string &app_data_dir) const; + + private: + inline static ConfigManager *instance_; + bool initialized_ = false; + bool use_prot_storage_ = true; + std::string data_path_prefix_; + std::string installer_pkg_name_; + std::string base_config_path_; + std::string blacklist_path_; + std::string whitelist_path_; + std::string use_whitelist_path_; + bool black_white_list_enabled_ = false; + bool dynamic_modules_enabled_ = false; + bool deopt_boot_image_enabled_ = false; + bool resources_hook_enabled_ = true; + // snapshot at boot + bool use_white_list_snapshot_ = false; + std::vector white_list_default_; + std::vector black_list_default_; + + ConfigManager(); + + ~ConfigManager(); + + void InitOnce(); + + void SnapshotBlackWhiteList(); + + std::string RetrieveInstallerPkgName() const; + + std::string GetConfigPath(const std::string &suffix) const; + }; + + +} // namespace edxp + diff --git a/edxp-core/src/main/cpp/main/src/edxp_context.cpp b/edxp-core/src/main/cpp/main/src/edxp_context.cpp new file mode 100644 index 00000000..3e210630 --- /dev/null +++ b/edxp-core/src/main/cpp/main/src/edxp_context.cpp @@ -0,0 +1,225 @@ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "edxp_context.h" +#include "config_manager.h" + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-value" + +namespace edxp { + + Context *Context::instance_ = nullptr; + + Context *Context::GetInstance() { + if (instance_ == nullptr) { + instance_ = new Context(); + } + return instance_; + } + + ALWAYS_INLINE inline jobject Context::GetCurrentClassLoader() const { + return inject_class_loader_; + } + + void Context::LoadDexAndInit(JNIEnv *env, const char *dex_path) { + if (LIKELY(initialized_)) { + return; + } + jclass classloader = JNI_FindClass(env, "java/lang/ClassLoader"); + jmethodID getsyscl_mid = JNI_GetStaticMethodID( + env, classloader, "getSystemClassLoader", "()Ljava/lang/ClassLoader;"); + jobject sys_classloader = JNI_CallStaticObjectMethod(env, classloader, getsyscl_mid); + if (UNLIKELY(!sys_classloader)) { + LOG(ERROR) << "getSystemClassLoader failed!!!"; + return; + } + jclass path_classloader = JNI_FindClass(env, "dalvik/system/PathClassLoader"); + jmethodID initMid = JNI_GetMethodID(env, path_classloader, "", + "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/ClassLoader;)V"); + jobject my_cl = JNI_NewObject(env, path_classloader, initMid, env->NewStringUTF(dex_path), + nullptr, sys_classloader); + if (UNLIKELY(!my_cl)) { + LOG(ERROR) << "PathClassLoader creation failed!!!"; + return; + } + inject_class_loader_ = env->NewGlobalRef(my_cl); + entry_class_ = (jclass) (env->NewGlobalRef( + FindClassFromLoader(env, GetCurrentClassLoader(), kEntryClassName))); + + RegisterEdxpResourcesHook(env); + RegisterFrameworkZygote(env); + RegisterConfigManagerMethods(env); + RegisterArtClassLinker(env); + RegisterArtHeap(env); + RegisterEdxpYahfa(env); + + initialized_ = true; + + //for SandHook variant + ScopedDlHandle sandhook_handle(kLibSandHookPath.c_str()); + if (!sandhook_handle.IsValid()) { + return; + } + typedef bool *(*TYPE_JNI_LOAD)(JNIEnv *, jclass, jclass); + auto jni_load = sandhook_handle.DlSym("JNI_Load_Ex"); + jclass sandhook_class = FindClassFromLoader(env, kSandHookClassName); + jclass nevercall_class = FindClassFromLoader(env, kSandHookNeverCallClassName); + if (!sandhook_class || !nevercall_class) { // fail-fast + return; + } + if (!jni_load(env, sandhook_class, nevercall_class)) { + LOGE("SandHook: HookEntry class error. %d", getpid()); + } + } + + jclass + Context::FindClassFromLoader(JNIEnv *env, jobject class_loader, const char *class_name) const { + jclass clz = JNI_GetObjectClass(env, class_loader); + jmethodID mid = JNI_GetMethodID(env, clz, "loadClass", + "(Ljava/lang/String;)Ljava/lang/Class;"); + jclass ret = nullptr; + if (!mid) { + mid = JNI_GetMethodID(env, clz, "findClass", "(Ljava/lang/String;)Ljava/lang/Class;"); + } + if (LIKELY(mid)) { + jobject target = JNI_CallObjectMethod(env, class_loader, mid, + env->NewStringUTF(class_name)); + if (target) { + return (jclass) target; + } + } else { + LOG(ERROR) << "No loadClass/findClass method found"; + } + LOG(ERROR) << "Class %s not found: " << class_name; + return ret; + } + + jclass Context::FindClassFromLoader(JNIEnv *env, const char *className) const { + return FindClassFromLoader(env, GetCurrentClassLoader(), className); + } + + inline void Context::PrepareJavaEnv(JNIEnv *env) { + LoadDexAndInit(env, kInjectDexPath); + } + + inline void Context::FindAndCall(JNIEnv *env, const char *method_name, + const char *method_sig, ...) const { + if (!entry_class_) { + LOGE("cannot call method %s, entry class is null", method_name); + return; + } + jmethodID mid = JNI_GetStaticMethodID(env, entry_class_, method_name, method_sig); + if (LIKELY(mid)) { + va_list args; + va_start(args, method_sig); + env->functions->CallStaticVoidMethodV(env, entry_class_, mid, args); + va_end(args); + } else { + LOGE("method %s id is null", method_name); + } + } + + ALWAYS_INLINE void Context::SetAppDataDir(jstring app_data_dir) { + app_data_dir_ = app_data_dir; + } + + ALWAYS_INLINE jstring Context::GetAppDataDir() const { + return app_data_dir_; + } + + ALWAYS_INLINE void Context::SetNiceName(jstring nice_name) { + nice_name_ = nice_name; + } + + ALWAYS_INLINE jstring Context::GetNiceName() const { + return nice_name_; + } + + void + Context::OnNativeForkSystemServerPre(JNIEnv *env, jclass clazz, uid_t uid, gid_t gid, + jintArray gids, + jint runtime_flags, jobjectArray rlimits, + jlong permitted_capabilities, + jlong effective_capabilities) { + app_data_dir_ = env->NewStringUTF(SYSTEM_SERVER_DATA_DIR); + bool is_black_white_list_mode = ConfigManager::GetInstance()->IsBlackWhiteListEnabled(); + bool is_dynamic_modules_mode = ConfigManager::GetInstance()->IsDynamicModulesEnabled(); + if (is_black_white_list_mode && is_dynamic_modules_mode) { + // when black/white list is on, never inject into zygote if dynamic modules mode is on + return; + } + PrepareJavaEnv(env); + // jump to java code + FindAndCall(env, "forkSystemServerPre", "(II[II[[IJJ)V", uid, gid, gids, runtime_flags, + rlimits, permitted_capabilities, effective_capabilities); + } + + + int Context::OnNativeForkSystemServerPost(JNIEnv *env, jclass clazz, jint res) { + if (res == 0) { + PrepareJavaEnv(env); + // only do work in child since FindAndCall would print log + FindAndCall(env, "forkSystemServerPost", "(I)V", res); + } else { + // in zygote process, res is child zygote pid + // don't print log here, see https://github.com/RikkaApps/Riru/blob/77adfd6a4a6a81bfd20569c910bc4854f2f84f5e/riru-core/jni/main/jni_native_method.cpp#L55-L66 + } + return 0; + } + + void Context::OnNativeForkAndSpecializePre(JNIEnv *env, jclass clazz, + jint uid, jint gid, + jintArray gids, + jint runtime_flags, + jobjectArray rlimits, + jint mount_external, + jstring se_info, + jstring se_name, + jintArray fds_to_close, + jintArray fds_to_ignore, + jboolean is_child_zygote, + jstring instruction_set, + jstring app_data_dir) { + app_data_dir_ = app_data_dir; + nice_name_ = se_name; + if (ConfigManager::GetInstance()->IsBlackWhiteListEnabled() && + ConfigManager::GetInstance()->IsDynamicModulesEnabled()) { + // when black/white list is on, never inject into zygote if dynamic modules mode is on + return; + } + PrepareJavaEnv(env); + FindAndCall(env, "forkAndSpecializePre", + "(II[II[[IILjava/lang/String;Ljava/lang/String;[I[IZLjava/lang/String;Ljava/lang/String;)V", + uid, gid, gids, runtime_flags, rlimits, + mount_external, se_info, se_name, fds_to_close, fds_to_ignore, + is_child_zygote, instruction_set, app_data_dir); + } + + int Context::OnNativeForkAndSpecializePost(JNIEnv *env, jclass clazz, jint res) { + if (res == 0) { + PrepareJavaEnv(env); + FindAndCall(env, "forkAndSpecializePost", "(ILjava/lang/String;Ljava/lang/String;)V", + res, + app_data_dir_, nice_name_); + } else { + // in zygote process, res is child zygote pid + // don't print log here, see https://github.com/RikkaApps/Riru/blob/77adfd6a4a6a81bfd20569c910bc4854f2f84f5e/riru-core/jni/main/jni_native_method.cpp#L55-L66 + } + return 0; + } + +} + +#pragma clang diagnostic pop \ No newline at end of file diff --git a/edxp-core/src/main/cpp/main/src/edxp_context.h b/edxp-core/src/main/cpp/main/src/edxp_context.h new file mode 100644 index 00000000..8b00f2a0 --- /dev/null +++ b/edxp-core/src/main/cpp/main/src/edxp_context.h @@ -0,0 +1,65 @@ + +#pragma once + +#include + +namespace edxp { + +#define SYSTEM_SERVER_DATA_DIR "/data/user/0/android" + + class Context { + + public: + static Context *GetInstance(); + + jobject GetCurrentClassLoader() const; + + void PrepareJavaEnv(JNIEnv *env); + + void FindAndCall(JNIEnv *env, const char *method_name, const char *method_sig, ...) const; + + void SetAppDataDir(jstring app_data_dir); + + void SetNiceName(jstring nice_name); + + jstring GetAppDataDir() const; + + jstring GetNiceName() const; + + jclass FindClassFromLoader(JNIEnv *env, const char *className) const; + + void OnNativeForkAndSpecializePre(JNIEnv *env, jclass clazz, jint uid, jint gid, + jintArray gids, jint runtime_flags, jobjectArray rlimits, + jint mount_external, + jstring se_info, jstring se_name, jintArray fds_to_close, + jintArray fds_to_ignore, jboolean is_child_zygote, + jstring instruction_set, jstring app_data_dir); + + int OnNativeForkAndSpecializePost(JNIEnv *env, jclass clazz, jint res); + + int OnNativeForkSystemServerPost(JNIEnv *env, jclass clazz, jint res); + + void OnNativeForkSystemServerPre(JNIEnv *env, jclass clazz, uid_t uid, gid_t gid, + jintArray gids, jint runtime_flags, jobjectArray rlimits, + jlong permitted_capabilities, + jlong effective_capabilities); + + private: + static Context *instance_; + bool initialized_ = false; + jobject inject_class_loader_ = nullptr; + jclass entry_class_ = nullptr; + jstring app_data_dir_ = nullptr; + jstring nice_name_ = nullptr; + + Context() {} + + ~Context() {} + + void LoadDexAndInit(JNIEnv *env, const char *dex_path); + + jclass FindClassFromLoader(JNIEnv *env, jobject class_loader, const char *class_name) const; + + }; + +} diff --git a/edxp-core/src/main/cpp/main/src/jni/art_class_linker.cpp b/edxp-core/src/main/cpp/main/src/jni/art_class_linker.cpp new file mode 100644 index 00000000..0b9f4231 --- /dev/null +++ b/edxp-core/src/main/cpp/main/src/jni/art_class_linker.cpp @@ -0,0 +1,34 @@ + +#include +#include +#include +#include +#include +#include "art_class_linker.h" + +namespace edxp { + + static std::vector deopted_methods; + + static void ClassLinker_setEntryPointsToInterpreter(JNI_START, jobject method) { + void *reflected_method = env->FromReflectedMethod(method); + if (std::find(deopted_methods.begin(), deopted_methods.end(), reflected_method) != + deopted_methods.end()) { + LOGD("method %p has been deopted before, skip...", reflected_method); + return; + } + LOGD("deoptimizing method: %p", reflected_method); + art::ClassLinker::Current()->SetEntryPointsToInterpreter(reflected_method); + deopted_methods.push_back(reflected_method); + LOGD("method deoptimized: %p", reflected_method); + } + + static JNINativeMethod gMethods[] = { + NATIVE_METHOD(ClassLinker, setEntryPointsToInterpreter, "(Ljava/lang/reflect/Member;)V") + }; + + void RegisterArtClassLinker(JNIEnv *env) { + REGISTER_EDXP_NATIVE_METHODS("com.elderdrivers.riru.edxp.art.ClassLinker"); + } + +} \ No newline at end of file diff --git a/edxp-core/src/main/cpp/main/src/jni/art_class_linker.h b/edxp-core/src/main/cpp/main/src/jni/art_class_linker.h new file mode 100644 index 00000000..3e3b013c --- /dev/null +++ b/edxp-core/src/main/cpp/main/src/jni/art_class_linker.h @@ -0,0 +1,8 @@ + +#pragma once + +namespace edxp { + + void RegisterArtClassLinker(JNIEnv *); + +} // namespace edxp diff --git a/edxp-core/src/main/cpp/main/src/jni/art_heap.cpp b/edxp-core/src/main/cpp/main/src/jni/art_heap.cpp new file mode 100644 index 00000000..30e91a21 --- /dev/null +++ b/edxp-core/src/main/cpp/main/src/jni/art_heap.cpp @@ -0,0 +1,26 @@ + +#include +#include +#include +#include +#include +#include "art_heap.h" + +namespace edxp { + + + static jint Heap_waitForGcToComplete(JNI_START, jlong thread) { + art::gc::collector::GcType gcType = art::gc::Heap::Current()->WaitForGcToComplete( + art::gc::GcCause::kGcCauseNone, reinterpret_cast(thread)); + return gcType; + } + + static JNINativeMethod gMethods[] = { + NATIVE_METHOD(Heap, waitForGcToComplete, "(J)I") + }; + + void RegisterArtHeap(JNIEnv *env) { + REGISTER_EDXP_NATIVE_METHODS("com.elderdrivers.riru.edxp.art.Heap"); + } + +} \ No newline at end of file diff --git a/edxp-core/src/main/cpp/main/src/jni/art_heap.h b/edxp-core/src/main/cpp/main/src/jni/art_heap.h new file mode 100644 index 00000000..1bb69ab1 --- /dev/null +++ b/edxp-core/src/main/cpp/main/src/jni/art_heap.h @@ -0,0 +1,8 @@ + +#pragma once + +namespace edxp { + + void RegisterArtHeap(JNIEnv *); + +} // namespace edxp diff --git a/edxp-core/src/main/cpp/main/src/jni/edxp_config_manager.cpp b/edxp-core/src/main/cpp/main/src/jni/edxp_config_manager.cpp new file mode 100644 index 00000000..f62dfbd7 --- /dev/null +++ b/edxp-core/src/main/cpp/main/src/jni/edxp_config_manager.cpp @@ -0,0 +1,49 @@ + +#include +#include +#include +#include "edxp_config_manager.h" + +namespace edxp { + + static jboolean ConfigManager_isBlackWhiteListEnabled(JNI_START) { + return (jboolean) ConfigManager::GetInstance()->IsBlackWhiteListEnabled(); + } + + static jboolean ConfigManager_isDynamicModulesEnabled(JNI_START) { + return (jboolean) ConfigManager::GetInstance()->IsDynamicModulesEnabled(); + } + + static jboolean ConfigManager_isResourcesHookEnabled(JNI_START) { + return (jboolean) ConfigManager::GetInstance()->IsResourcesHookEnabled(); + } + + static jboolean ConfigManager_isDeoptBootImageEnabled(JNI_START) { + return (jboolean) ConfigManager::GetInstance()->IsDeoptBootImageEnabled(); + } + + static jstring ConfigManager_getInstallerPackageName(JNI_START) { + return env->NewStringUTF(ConfigManager::GetInstance()->GetInstallerPkgName().c_str()); + } + + static jboolean ConfigManager_isAppNeedHook(JNI_START, jstring appDataDir) { + const char *app_data_dir = env->GetStringUTFChars(appDataDir, JNI_FALSE); + auto result = (jboolean) ConfigManager::GetInstance()->IsAppNeedHook(app_data_dir); + env->ReleaseStringUTFChars(appDataDir, app_data_dir); + return result; + } + + static JNINativeMethod gMethods[] = { + NATIVE_METHOD(ConfigManager, isBlackWhiteListEnabled, "()Z"), + NATIVE_METHOD(ConfigManager, isDynamicModulesEnabled, "()Z"), + NATIVE_METHOD(ConfigManager, isResourcesHookEnabled, "()Z"), + NATIVE_METHOD(ConfigManager, isDeoptBootImageEnabled, "()Z"), + NATIVE_METHOD(ConfigManager, getInstallerPackageName, "()Ljava/lang/String;"), + NATIVE_METHOD(ConfigManager, isAppNeedHook, "(Ljava/lang/String;)Z"), + }; + + void RegisterConfigManagerMethods(JNIEnv *env) { + REGISTER_EDXP_NATIVE_METHODS("com.elderdrivers.riru.edxp.config.ConfigManager"); + } + +} \ No newline at end of file diff --git a/edxp-core/src/main/cpp/main/src/jni/edxp_config_manager.h b/edxp-core/src/main/cpp/main/src/jni/edxp_config_manager.h new file mode 100644 index 00000000..54912336 --- /dev/null +++ b/edxp-core/src/main/cpp/main/src/jni/edxp_config_manager.h @@ -0,0 +1,10 @@ + +#pragma once + +#include "jni.h" + +namespace edxp { + + void RegisterConfigManagerMethods(JNIEnv *env); + +} diff --git a/edxp-core/src/main/cpp/main/src/jni/edxp_resources_hook.cpp b/edxp-core/src/main/cpp/main/src/jni/edxp_resources_hook.cpp new file mode 100644 index 00000000..ab834d87 --- /dev/null +++ b/edxp-core/src/main/cpp/main/src/jni/edxp_resources_hook.cpp @@ -0,0 +1,35 @@ + +#include +#include +#include +#include +#include "edxp_resources_hook.h" + +namespace edxp { + + static jboolean ResourcesHook_initXResourcesNative(JNI_START) { + return XposedBridge_initXResourcesNative(env, clazz); + } + + static jboolean ResourcesHook_removeFinalFlagNative(JNI_START, jclass target_class) { + if (target_class) { + jclass class_clazz = JNI_FindClass(env, "java/lang/Class"); + jfieldID java_lang_Class_accessFlags = JNI_GetFieldID( + env, class_clazz, "accessFlags", "I"); + jint access_flags = env->GetIntField(clazz, java_lang_Class_accessFlags); + env->SetIntField(clazz, java_lang_Class_accessFlags, access_flags & ~kAccFinal); + return JNI_TRUE; + } + return JNI_FALSE; + } + + static JNINativeMethod gMethods[] = { + NATIVE_METHOD(ResourcesHook, initXResourcesNative, "()Z"), + NATIVE_METHOD(ResourcesHook, removeFinalFlagNative, "(Ljava/lang/Class;)Z"), + }; + + void RegisterEdxpResourcesHook(JNIEnv *env) { + REGISTER_EDXP_NATIVE_METHODS("com.elderdrivers.riru.edxp.core.ResourcesHook"); + } + +} \ No newline at end of file diff --git a/edxp-core/src/main/cpp/main/src/jni/edxp_resources_hook.h b/edxp-core/src/main/cpp/main/src/jni/edxp_resources_hook.h new file mode 100644 index 00000000..74461c9b --- /dev/null +++ b/edxp-core/src/main/cpp/main/src/jni/edxp_resources_hook.h @@ -0,0 +1,12 @@ + +#pragma once + +#include "jni.h" + +namespace edxp { + + static constexpr uint32_t kAccFinal = 0x0010; + + void RegisterEdxpResourcesHook(JNIEnv *); + +} // namespace edxp diff --git a/edxp-core/src/main/cpp/main/src/jni/edxp_yahfa.cpp b/edxp-core/src/main/cpp/main/src/jni/edxp_yahfa.cpp new file mode 100644 index 00000000..fa3b623f --- /dev/null +++ b/edxp-core/src/main/cpp/main/src/jni/edxp_yahfa.cpp @@ -0,0 +1,61 @@ + +extern "C" +{ +#include "HookMain.h" +} + +#include +#include "jni.h" +#include "native_util.h" +#include "edxp_yahfa.h" + +namespace edxp { + + static void Yahfa_init(JNI_START, jint sdkVersion) { + Java_lab_galaxy_yahfa_HookMain_init(env, clazz, sdkVersion); + } + + static jobject Yahfa_findMethodNative(JNI_START, jclass targetClass, + jstring methodName, jstring methodSig) { + return Java_lab_galaxy_yahfa_HookMain_findMethodNative(env, clazz, targetClass, methodName, + methodSig); + } + + static jboolean Yahfa_backupAndHookNative(JNI_START, jobject target, + jobject hook, jobject backup) { + return Java_lab_galaxy_yahfa_HookMain_backupAndHookNative(env, clazz, target, hook, backup); + } + + static void Yahfa_ensureMethodCached(JNI_START, jobject hook, jobject backup) { + Java_lab_galaxy_yahfa_HookMain_ensureMethodCached(env, clazz, hook, backup); + } + + static void Yahfa_setMethodNonCompilable(JNI_START, jobject member) { + if (!member) { + LOGE("setNonCompilableNative: member is null"); + return; + } + void *art_method = env->FromReflectedMethod(member); + if (!art_method) { + LOGE("setNonCompilableNative: art_method is null"); + return; + } + setNonCompilable(art_method); + } + + static JNINativeMethod gMethods[] = { + NATIVE_METHOD(Yahfa, init, "(I)V"), + NATIVE_METHOD(Yahfa, findMethodNative, + "(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object;"), + NATIVE_METHOD(Yahfa, backupAndHookNative, + "(Ljava/lang/Object;Ljava/lang/reflect/Method;Ljava/lang/reflect/Method;)Z"), + NATIVE_METHOD(Yahfa, ensureMethodCached, + "(Ljava/lang/reflect/Method;Ljava/lang/reflect/Method;)V"), + NATIVE_METHOD(Yahfa, setMethodNonCompilable, "(Ljava/lang/reflect/Member;)V"), + }; + + void RegisterEdxpYahfa(JNIEnv *env) { + REGISTER_EDXP_NATIVE_METHODS("com.elderdrivers.riru.edxp.core.Yahfa"); + } + +} \ No newline at end of file diff --git a/edxp-core/src/main/cpp/main/src/jni/edxp_yahfa.h b/edxp-core/src/main/cpp/main/src/jni/edxp_yahfa.h new file mode 100644 index 00000000..10f7252d --- /dev/null +++ b/edxp-core/src/main/cpp/main/src/jni/edxp_yahfa.h @@ -0,0 +1,10 @@ + +#pragma once + +#include "jni.h" + +namespace edxp { + + void RegisterEdxpYahfa(JNIEnv *); + +} // namespace edxp diff --git a/edxp-core/src/main/cpp/main/src/jni/framework_zygote.cpp b/edxp-core/src/main/cpp/main/src/jni/framework_zygote.cpp new file mode 100644 index 00000000..fbafd933 --- /dev/null +++ b/edxp-core/src/main/cpp/main/src/jni/framework_zygote.cpp @@ -0,0 +1,36 @@ + +#include +#include +#include +#include +#include "framework_zygote.h" + +namespace edxp { + + static FileDescriptorTable *gClosedFdTable = nullptr; + + static void Zygote_closeFilesBeforeFork(JNI_START) { + // FIXME what if gClosedFdTable is not null + gClosedFdTable = FileDescriptorTable::Create(); + } + + static void Zygote_reopenFilesAfterFork(JNI_START) { + if (!gClosedFdTable) { + LOGE("gClosedFdTable is null when reopening files"); + return; + } + gClosedFdTable->Reopen(); + delete gClosedFdTable; + gClosedFdTable = nullptr; + } + + static JNINativeMethod gMethods[] = { + NATIVE_METHOD(Zygote, closeFilesBeforeFork, "()V"), + NATIVE_METHOD(Zygote, reopenFilesAfterFork, "()V") + }; + + void RegisterFrameworkZygote(JNIEnv *env) { + REGISTER_EDXP_NATIVE_METHODS("com.elderdrivers.riru.edxp.framework.Zygote"); + } + +} \ No newline at end of file diff --git a/edxp-core/src/main/cpp/main/src/jni/framework_zygote.h b/edxp-core/src/main/cpp/main/src/jni/framework_zygote.h new file mode 100644 index 00000000..ab15e267 --- /dev/null +++ b/edxp-core/src/main/cpp/main/src/jni/framework_zygote.h @@ -0,0 +1,10 @@ + +#pragma once + +#include "jni.h" + +namespace edxp { + + void RegisterFrameworkZygote(JNIEnv *); + +} // namespace edxp diff --git a/edxp-core/src/main/cpp/main/src/main.cpp b/edxp-core/src/main/cpp/main/src/main.cpp new file mode 100644 index 00000000..66026e43 --- /dev/null +++ b/edxp-core/src/main/cpp/main/src/main.cpp @@ -0,0 +1,99 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "native_hook.h" +#include "logging.h" +#include "config.h" +#include "edxp_context.h" + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-value" + +namespace edxp { + + extern "C" { + + __attribute__((visibility("default"))) void onModuleLoaded() { + LOG(INFO) << "onModuleLoaded: welcome to EdXposed!"; + InstallInlineHooks(); + } + + __attribute__((visibility("default"))) int shouldSkipUid(int uid) { + return 0; + } + + __attribute__((visibility("default"))) void + nativeForkAndSpecializePre(JNIEnv *env, jclass clazz, jint *_uid, jint *gid, jintArray *gids, + jint *runtime_flags, + jobjectArray *rlimits, jint *mount_external, jstring *se_info, + jstring *se_name, + jintArray *fds_to_close, jintArray *fds_to_ignore, + jboolean *is_child_zygote, + jstring *instruction_set, jstring *app_data_dir, + jstring *package_name, + jobjectArray *packages_for_uid, jstring *sandbox_id) { + Context::GetInstance()->OnNativeForkAndSpecializePre(env, clazz, *_uid, *gid, *gids, + *runtime_flags, *rlimits, + *mount_external, *se_info, *se_name, + *fds_to_close, + *fds_to_ignore, + *is_child_zygote, *instruction_set, + *app_data_dir); + } + + __attribute__((visibility("default"))) int + nativeForkAndSpecializePost(JNIEnv *env, jclass clazz, + jint res) { + return Context::GetInstance()->OnNativeForkAndSpecializePost(env, clazz, res); + } + + __attribute__((visibility("default"))) + void + nativeForkSystemServerPre(JNIEnv *env, jclass clazz, uid_t *uid, gid_t *gid, jintArray *gids, + jint *runtime_flags, + jobjectArray *rlimits, jlong *permitted_capabilities, + jlong *effective_capabilities) { + Context::GetInstance()->OnNativeForkSystemServerPre(env, clazz, *uid, *gid, *gids, + *runtime_flags, *rlimits, + *permitted_capabilities, + *effective_capabilities); + } + + __attribute__((visibility("default"))) + int nativeForkSystemServerPost(JNIEnv *env, jclass clazz, jint res) { + return Context::GetInstance()->OnNativeForkSystemServerPost(env, clazz, res); + } + + __attribute__((visibility("default"))) void specializeAppProcessPre( + JNIEnv *env, jclass clazz, jint *_uid, jint *gid, jintArray *gids, jint *runtime_flags, + jobjectArray *rlimits, jint *mount_external, jstring *se_info, jstring *nice_name, + jboolean *start_child_zygote, jstring *instruction_set, jstring *app_data_dir, + jstring *package_name, jobjectArray *packages_for_uid, jstring *sandbox_id) { + Context::GetInstance()->OnNativeForkAndSpecializePre(env, clazz, *_uid, *gid, *gids, + *runtime_flags, *rlimits, + *mount_external, *se_info, *nice_name, + nullptr, nullptr, + *start_child_zygote, *instruction_set, + *app_data_dir); + } + + __attribute__((visibility("default"))) int specializeAppProcessPost( + JNIEnv *env, jclass clazz) { + return Context::GetInstance()->OnNativeForkAndSpecializePost(env, clazz, 0); + } + + } + +} + +#pragma clang diagnostic pop \ No newline at end of file diff --git a/edxp-core/src/main/cpp/main/src/native_hook.cpp b/edxp-core/src/main/cpp/main/src/native_hook.cpp new file mode 100644 index 00000000..d0c6166b --- /dev/null +++ b/edxp-core/src/main/cpp/main/src/native_hook.cpp @@ -0,0 +1,62 @@ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "logging.h" +#include "native_hook.h" +#include "riru_hook.h" +#include "art/runtime/mirror/class.h" +#include "art/runtime/class_linker.h" +#include "art/runtime/gc/heap.h" +#include "art/runtime/hidden_api.h" + +namespace edxp { + + static bool installed = false; + + void InstallInlineHooks() { + if (installed) { + LOGI("Inline hooks have been installed, skip"); + return; + } + LOGI("Start to install inline hooks"); + int api_level = GetAndroidApiLevel(); + if (UNLIKELY(api_level < ANDROID_LOLLIPOP)) { + LOGE("API level not supported: %d, skip inline hooks", api_level); + return; + } + LOGI("Using api level %d", api_level); + InstallRiruHooks(); +#ifdef __LP64__ + ScopedDlHandle whale_handle(kLibWhalePath.c_str()); + if (!whale_handle.IsValid()) { + return; + } + void *hook_func_symbol = whale_handle.DlSym("WInlineHookFunction"); +#else + void *hook_func_symbol = (void *) MSHookFunction; +#endif + if (!hook_func_symbol) { + return; + } + auto hook_func = reinterpret_cast(hook_func_symbol); + ScopedDlHandle art_handle(kLibArtPath.c_str()); + if (!art_handle.IsValid()) { + return; + } + art::hidden_api::DisableHiddenApi(art_handle.Get(), hook_func); + art::Runtime::Setup(art_handle.Get(), hook_func); + art::gc::Heap::Setup(art_handle.Get(), hook_func); + art::ClassLinker::Setup(art_handle.Get(), hook_func); + art::mirror::Class::Setup(art_handle.Get(), hook_func); + LOGI("Inline hooks installed"); + } + +} + diff --git a/edxp-core/src/main/cpp/main/src/native_hook.h b/edxp-core/src/main/cpp/main/src/native_hook.h new file mode 100644 index 00000000..0f8747e3 --- /dev/null +++ b/edxp-core/src/main/cpp/main/src/native_hook.h @@ -0,0 +1,10 @@ + +#pragma once + +#include + +namespace edxp { + + void InstallInlineHooks(); + +} diff --git a/edxp-core/src/main/cpp/main/src/resource_hook.cpp b/edxp-core/src/main/cpp/main/src/resource_hook.cpp new file mode 100644 index 00000000..1a5563d2 --- /dev/null +++ b/edxp-core/src/main/cpp/main/src/resource_hook.cpp @@ -0,0 +1,174 @@ +// +// Created by solo on 2019/3/24. +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "framework/androidfw/ResourceTypes.h" +#include "resource_hook.h" +#include "dl_util.h" + +namespace edxp { + + static constexpr const char *kXResourcesClassName = "android/content/res/XResources"; + + typedef int32_t (*TYPE_GET_ATTR_NAME_ID)(void *, int); + + typedef char16_t *(*TYPE_STRING_AT)(const void *, int32_t, size_t *); + + typedef void(*TYPE_RESTART)(void *); + + typedef int32_t (*TYPE_NEXT)(void *); + + static jclass classXResources; + static jmethodID methodXResourcesTranslateAttrId; + static jmethodID methodXResourcesTranslateResId; + + static TYPE_NEXT ResXMLParser_next = nullptr; + static TYPE_RESTART ResXMLParser_restart = nullptr; + static TYPE_GET_ATTR_NAME_ID ResXMLParser_getAttributeNameID = nullptr; + static TYPE_STRING_AT ResStringPool_stringAt = nullptr; + + static JNINativeMethod gMethods[] = { + NATIVE_METHOD(XResources, rewriteXmlReferencesNative, + "(JLandroid/content/res/XResources;Landroid/content/res/Resources;)V") + }; + + static bool register_natives_XResources(JNIEnv *env, jclass clazz) { + jint result = JNI_RegisterNatives(env, clazz, gMethods, arraysize(gMethods)); + return result == JNI_OK; + } + + static bool PrepareSymbols() { + ScopedDlHandle fw_handle(kLibFwPath.c_str()); + if (!fw_handle.IsValid()) { + return false; + }; + if (!(ResXMLParser_next = fw_handle.DlSym( + "_ZN7android12ResXMLParser4nextEv"))) { + return false; + } + if (!(ResXMLParser_restart = fw_handle.DlSym( + "_ZN7android12ResXMLParser7restartEv"))) { + return false; + }; + if (!(ResXMLParser_getAttributeNameID = fw_handle.DlSym( + LP_SELECT("_ZNK7android12ResXMLParser18getAttributeNameIDEj", + "_ZNK7android12ResXMLParser18getAttributeNameIDEm")))) { + return false; + } + return (ResStringPool_stringAt = fw_handle.DlSym( + LP_SELECT("_ZNK7android13ResStringPool8stringAtEjPj", + "_ZNK7android13ResStringPool8stringAtEmPm"))) != nullptr; + } + + jboolean XposedBridge_initXResourcesNative(JNIEnv *env, jclass) { + classXResources = Context::GetInstance()->FindClassFromLoader(env, kXResourcesClassName); + if (!classXResources) { + LOGE("Error while loading XResources class '%s':", kXResourcesClassName); + return JNI_FALSE; + } + if (!register_natives_XResources(env, classXResources)) { + return JNI_FALSE; + } + methodXResourcesTranslateResId = JNI_GetStaticMethodID( + env, classXResources, "translateResId", + "(ILandroid/content/res/XResources;Landroid/content/res/Resources;)I"); + if (!methodXResourcesTranslateResId) { + return JNI_FALSE; + } + methodXResourcesTranslateAttrId = JNI_GetStaticMethodID( + env, classXResources, "translateAttrId", + "(Ljava/lang/String;Landroid/content/res/XResources;)I"); + if (!methodXResourcesTranslateAttrId) { + return JNI_FALSE; + } + if (!PrepareSymbols()) { + return JNI_FALSE; + } + classXResources = reinterpret_cast(env->NewGlobalRef(classXResources)); + return JNI_TRUE; + } + + void XResources_rewriteXmlReferencesNative(JNIEnv *env, jclass, + jlong parserPtr, jobject origRes, jobject repRes) { + + auto parser = (android::ResXMLParser *) parserPtr; + + if (parser == nullptr) + return; + + const android::ResXMLTree &mTree = parser->mTree; + auto mResIds = (uint32_t *) mTree.mResIds; + android::ResXMLTree_attrExt *tag; + int attrCount; + + do { + switch (ResXMLParser_next(parser)) { + case android::ResXMLParser::START_TAG: + tag = (android::ResXMLTree_attrExt *) parser->mCurExt; + attrCount = dtohs(tag->attributeCount); + for (int idx = 0; idx < attrCount; idx++) { + auto attr = (android::ResXMLTree_attribute *) + (((const uint8_t *) tag) + + dtohs(tag->attributeStart) + + (dtohs(tag->attributeSize) * idx)); + + // find resource IDs for attribute names + int32_t attrNameID = ResXMLParser_getAttributeNameID(parser, idx); + // only replace attribute name IDs for app packages + if (attrNameID >= 0 && (size_t) attrNameID < mTree.mNumResIds && + dtohl(mResIds[attrNameID]) >= 0x7f000000) { + size_t attNameLen; + const char16_t *attrName = ResStringPool_stringAt(&(mTree.mStrings), + attrNameID, + &attNameLen); + jint attrResID = env->CallStaticIntMethod(classXResources, + methodXResourcesTranslateAttrId, + env->NewString( + (const jchar *) attrName, + attNameLen), origRes); + if (env->ExceptionCheck()) + goto leave; + + mResIds[attrNameID] = htodl(attrResID); + } + + // find original resource IDs for reference values (app packages only) + if (attr->typedValue.dataType != android::Res_value::TYPE_REFERENCE) + continue; + + jint oldValue = dtohl(attr->typedValue.data); + if (oldValue < 0x7f000000) + continue; + + jint newValue = env->CallStaticIntMethod(classXResources, + methodXResourcesTranslateResId, + oldValue, origRes, repRes); + if (env->ExceptionCheck()) + goto leave; + + if (newValue != oldValue) + attr->typedValue.data = htodl(newValue); + } + continue; + case android::ResXMLParser::END_DOCUMENT: + case android::ResXMLParser::BAD_DOCUMENT: + goto leave; + default: + continue; + } + } while (true); + + leave: + ResXMLParser_restart(parser); + } + +} \ No newline at end of file diff --git a/edxp-core/src/main/cpp/main/src/resource_hook.h b/edxp-core/src/main/cpp/main/src/resource_hook.h new file mode 100644 index 00000000..d2b061e2 --- /dev/null +++ b/edxp-core/src/main/cpp/main/src/resource_hook.h @@ -0,0 +1,12 @@ + +#pragma once + +#include + +namespace edxp { + + jboolean XposedBridge_initXResourcesNative(JNIEnv *env, jclass); + + void XResources_rewriteXmlReferencesNative(JNIEnv *env, jclass, + jlong parserPtr, jobject origRes, jobject repRes); +} diff --git a/edxp-core/src/main/cpp/main/src/riru_hook.cpp b/edxp-core/src/main/cpp/main/src/riru_hook.cpp new file mode 100644 index 00000000..dbbae41d --- /dev/null +++ b/edxp-core/src/main/cpp/main/src/riru_hook.cpp @@ -0,0 +1,91 @@ +// +// Created by solo on 2019/3/16. +// + +#include +#include +#include +#include +#include +#include +#include +#include "riru_hook.h" + +namespace edxp { + + static int api_level = 0; + + NEW_FUNC_DEF(int, __system_property_get, const char *key, char *value) { + int res = old___system_property_get(key, value); + if (key) { + if (strcmp(kPropKeyCompilerFilter, key) == 0) { + strcpy(value, kPropValueCompilerFilter); + LOGI("system_property_get: %s -> %s", key, value); + } else if (strcmp(kPropKeyCompilerFlags, key) == 0) { + strcpy(value, kPropValueCompilerFlags); + LOGI("system_property_get: %s -> %s", key, value); + } + if (api_level == ANDROID_O_MR1) { + // https://android.googlesource.com/platform/art/+/f5516d38736fb97bfd0435ad03bbab17ddabbe4e + // Android 8.1 add a fatal check for debugging (removed in Android 9.0), + // which will be triggered by EdXposed in cases where target method is hooked + // (native flag set) after it has been called several times(getCounter() return positive number) + if (strcmp(kPropKeyUseJitProfiles, key) == 0) { + strcpy(value, "false"); + } else if (strcmp(kPropKeyPmBgDexopt, key) == 0) { + // use speed as bg-dexopt filter since that speed-profile won't work after + // jit profiles is disabled + strcpy(value, kPropValuePmBgDexopt); + } + LOGD("system_property_get: %s -> %s", key, value); + } + } + return res; + } + + NEW_FUNC_DEF(std::string, + _ZN7android4base11GetPropertyERKNSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEES9_, + const std::string &key, const std::string &default_value) { + std::string res = old__ZN7android4base11GetPropertyERKNSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEES9_( + key, default_value); + if (strcmp(kPropKeyCompilerFilter, key.c_str()) == 0) { + res = kPropValueCompilerFilter; + LOGI("android::base::GetProperty: %s -> %s", key.c_str(), res.c_str()); + } else if (strcmp(kPropKeyCompilerFlags, key.c_str()) == 0) { + res = kPropValueCompilerFlags; + LOGI("android::base::GetProperty: %s -> %s", key.c_str(), res.c_str()); + } + if (api_level == ANDROID_O_MR1) { + // see __system_property_get hook above for explanations + if (strcmp(kPropKeyUseJitProfiles, key.c_str()) == 0) { + res = "false"; + } else if (strcmp(kPropKeyPmBgDexopt, key.c_str()) == 0) { + res = kPropValuePmBgDexopt; + } + LOGD("android::base::GetProperty: %s -> %s", key.c_str(), res.c_str()); + } + return res; + } + + void InstallRiruHooks() { + + LOGI("Start to install Riru hook"); + + api_level = GetAndroidApiLevel(); + + XHOOK_REGISTER(__system_property_get); + + if (GetAndroidApiLevel() >= ANDROID_P) { + XHOOK_REGISTER( + _ZN7android4base11GetPropertyERKNSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEES9_); + } + + if (xhook_refresh(0) == 0) { + xhook_clear(); + LOGI("Riru hooks installed"); + } else { + LOGE("Failed to install riru hooks"); + } + } + +} \ No newline at end of file diff --git a/edxp-core/src/main/cpp/main/src/riru_hook.h b/edxp-core/src/main/cpp/main/src/riru_hook.h new file mode 100644 index 00000000..892afa90 --- /dev/null +++ b/edxp-core/src/main/cpp/main/src/riru_hook.h @@ -0,0 +1,35 @@ + +#pragma once + +#define XHOOK_REGISTER(NAME) \ + if (xhook_register(".*", #NAME, (void*) new_##NAME, (void **) &old_##NAME) == 0) { \ + if (riru_get_version() >= 8) { \ + void *f = riru_get_func(#NAME); \ + if (f != nullptr) { \ + memcpy(&old_##NAME, &f, sizeof(void *)); \ + } \ + riru_set_func(#NAME, (void *) new_##NAME); \ + } \ + } else { \ + LOGE("failed to register riru hook " #NAME "."); \ + } + +#define NEW_FUNC_DEF(ret, func, ...) \ + static ret (*old_##func)(__VA_ARGS__); \ + static ret new_##func(__VA_ARGS__) + +namespace edxp { + + static constexpr const char *kPropKeyCompilerFilter = "dalvik.vm.dex2oat-filter"; + static constexpr const char *kPropKeyCompilerFlags = "dalvik.vm.dex2oat-flags"; + static constexpr const char *kPropKeyUseJitProfiles = "dalvik.vm.usejitprofiles"; + static constexpr const char *kPropKeyPmBgDexopt = "pm.dexopt.bg-dexopt"; + + static constexpr const char *kPropValueCompilerFilter = "quicken"; + static constexpr const char *kPropValuePmBgDexopt = "speed"; + static constexpr const char *kPropValueCompilerFlags = "--inline-max-code-units=0"; + + + void InstallRiruHooks(); + +} diff --git a/edxp-core/template_override/common/util_functions.sh b/edxp-core/template_override/common/util_functions.sh index 79710b6e..d4b00e65 100644 --- a/edxp-core/template_override/common/util_functions.sh +++ b/edxp-core/template_override/common/util_functions.sh @@ -1,6 +1,6 @@ #!/system/bin/sh -EDXP_VERSION="0.4.2.3_alpha (4230)" +EDXP_VERSION="0.4.3.0_alpha (4300)" ANDROID_SDK=`getprop ro.build.version.sdk` BUILD_DESC=`getprop ro.build.description` PRODUCT=`getprop ro.build.product` @@ -79,7 +79,7 @@ start_log_cather () { } start_verbose_log_catcher () { - start_log_cather all.log "EdXposed-Fwk:V EdXposed-dexmaker:V XSharedPreferences:V EdXposed-Bridge:V EdXposed-YAHFA:V EdXposed-Core-Native:V EdXposed-Manager:V XposedInstaller:V" true ${LOG_VERBOSE} + start_log_cather all.log "EdXposed:V EdXposed-Fwk:V EdXposed-dexmaker:V XSharedPreferences:V EdXposed-Bridge:V EdXposed-YAHFA:V EdXposed-Manager:V XposedInstaller:V" true ${LOG_VERBOSE} } start_bridge_log_catcher () { diff --git a/edxp-sandhook/build.gradle b/edxp-sandhook/build.gradle index 415ecdfc..a26f6132 100644 --- a/edxp-sandhook/build.gradle +++ b/edxp-sandhook/build.gradle @@ -1,5 +1,8 @@ apply plugin: 'com.android.application' +sourceCompatibility = "7" +targetCompatibility = "7" + android { compileSdkVersion 28 diff --git a/edxp-sandhook/proguard-rules.pro b/edxp-sandhook/proguard-rules.pro index 48bb9c90..520e46fd 100644 --- a/edxp-sandhook/proguard-rules.pro +++ b/edxp-sandhook/proguard-rules.pro @@ -30,4 +30,8 @@ -keep class * implements com.elderdrivers.riru.common.KeepAll { *; } -keepclassmembers class * implements com.elderdrivers.riru.common.KeepMembers { *; } --keep class com.swift.sandhook.** {*;} \ No newline at end of file +-keep class com.swift.sandhook.** {*;} + +-keepclasseswithmember class * { + native ; +} \ No newline at end of file diff --git a/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/Main.java b/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/Main.java index 37cd5493..6979ece7 100644 --- a/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/Main.java +++ b/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/Main.java @@ -5,7 +5,9 @@ import android.os.Build; import android.os.Process; import com.elderdrivers.riru.common.KeepAll; +import com.elderdrivers.riru.edxp.config.ConfigManager; import com.elderdrivers.riru.edxp.config.InstallerChooser; +import com.elderdrivers.riru.edxp.core.Yahfa; import com.elderdrivers.riru.edxp.sandhook.BuildConfig; import com.elderdrivers.riru.edxp.sandhook.core.HookMethodResolver; import com.elderdrivers.riru.edxp.sandhook.entry.Router; @@ -15,7 +17,6 @@ import com.elderdrivers.riru.edxp.util.Utils; import com.swift.sandhook.xposedcompat.XposedCompat; import com.swift.sandhook.xposedcompat.methodgen.SandHookXposedBridge; -import java.lang.reflect.Method; import java.util.Arrays; @SuppressLint("DefaultLocale") @@ -28,10 +29,10 @@ public class Main implements KeepAll { private static String forkSystemServerPramsStr = ""; static { - init(Build.VERSION.SDK_INT); + Yahfa.init(Build.VERSION.SDK_INT); HookMethodResolver.init(); Router.injectConfig(); - InstallerChooser.setInstallerPackageName(getInstallerPkgName()); + InstallerChooser.setInstallerPackageName(ConfigManager.getInstallerPackageName()); SandHookXposedBridge.init(); } @@ -56,7 +57,7 @@ public class Main implements KeepAll { mountExternal, seInfo, niceName, Arrays.toString(fdsToClose), Arrays.toString(fdsToIgnore), startChildZygote, instructionSet, appDataDir); } - if (isBlackWhiteListEnabled()) { + if (ConfigManager.isBlackWhiteListEnabled()) { BlackWhiteListProxy.forkAndSpecializePre(uid, gid, gids, debugFlags, rlimits, mountExternal, seInfo, niceName, fdsToClose, fdsToIgnore, startChildZygote, instructionSet, appDataDir); @@ -70,7 +71,7 @@ public class Main implements KeepAll { public static void forkAndSpecializePost(int pid, String appDataDir, String niceName) { if (pid == 0) { Utils.logD(forkAndSpecializePramsStr + " = " + Process.myPid()); - if (isBlackWhiteListEnabled()) { + if (ConfigManager.isBlackWhiteListEnabled()) { BlackWhiteListProxy.forkAndSpecializePost(pid, appDataDir, niceName); } else { NormalProxy.forkAndSpecializePost(pid, appDataDir, niceName); @@ -88,7 +89,7 @@ public class Main implements KeepAll { uid, gid, Arrays.toString(gids), debugFlags, Arrays.toString(rlimits), permittedCapabilities, effectiveCapabilities); } - if (isBlackWhiteListEnabled()) { + if (ConfigManager.isBlackWhiteListEnabled()) { BlackWhiteListProxy.forkSystemServerPre(uid, gid, gids, debugFlags, rlimits, permittedCapabilities, effectiveCapabilities); } else { @@ -100,7 +101,7 @@ public class Main implements KeepAll { public static void forkSystemServerPost(int pid) { if (pid == 0) { Utils.logD(forkSystemServerPramsStr + " = " + Process.myPid()); - if (isBlackWhiteListEnabled()) { + if (ConfigManager.isBlackWhiteListEnabled()) { BlackWhiteListProxy.forkSystemServerPost(pid); } else { NormalProxy.forkSystemServerPost(pid); @@ -111,46 +112,4 @@ public class Main implements KeepAll { } } - /////////////////////////////////////////////////////////////////////////////////////////////// - // native methods - /////////////////////////////////////////////////////////////////////////////////////////////// - - public static native boolean backupAndHookNative(Object target, Method hook, Method backup); - - public static native void setMethodNonCompilable(Object member); - - public static native void ensureMethodCached(Method hook, Method backup); - - // JNI.ToReflectedMethod() could return either Method or Constructor - public static native Object findMethodNative(Class targetClass, String methodName, String methodSig); - - private static native void init(int SDK_version); - - public static native String getInstallerPkgName(); - - public static native boolean isBlackWhiteListEnabled(); - - public static native boolean isDynamicModulesEnabled(); - - public static native boolean isAppNeedHook(String appDataDir); - - // prevent from fatal error caused by holding not whitelisted file descriptors when forking zygote - // https://github.com/rovo89/Xposed/commit/b3ba245ad04cd485699fb1d2ebde7117e58214ff - public static native void closeFilesBeforeForkNative(); - - public static native void reopenFilesAfterForkNative(); - - public static native void deoptMethodNative(Object object); - - public static native long suspendAllThreads(); - - public static native void resumeAllThreads(long obj); - - public static native int waitForGcToComplete(long thread); - - public static native boolean initXResourcesNative(); - - public static native boolean removeFinalFlagNative(Class clazz); - - public static native boolean isResourcesHookEnabled(); } diff --git a/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/config/SandHookEdxpConfig.java b/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/config/SandHookEdxpConfig.java index 35c08ebd..894e4432 100644 --- a/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/config/SandHookEdxpConfig.java +++ b/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/config/SandHookEdxpConfig.java @@ -1,6 +1,7 @@ package com.elderdrivers.riru.edxp.sandhook.config; import com.elderdrivers.riru.edxp.Main; +import com.elderdrivers.riru.edxp.config.ConfigManager; import com.elderdrivers.riru.edxp.config.EdXpConfig; import com.elderdrivers.riru.edxp.config.InstallerChooser; import com.elderdrivers.riru.edxp.hooker.XposedBlackListHooker; @@ -18,11 +19,11 @@ public class SandHookEdxpConfig implements EdXpConfig { @Override public boolean isDynamicModulesMode() { - return Main.isDynamicModulesEnabled(); + return ConfigManager.isDynamicModulesEnabled(); } @Override public boolean isResourcesHookEnabled() { - return Main.isResourcesHookEnabled(); + return ConfigManager.isResourcesHookEnabled(); } } diff --git a/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/config/SandHookProvider.java b/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/config/SandHookProvider.java index d00024eb..1d5e0e4e 100644 --- a/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/config/SandHookProvider.java +++ b/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/config/SandHookProvider.java @@ -3,7 +3,10 @@ package com.elderdrivers.riru.edxp.sandhook.config; import android.util.Log; import com.elderdrivers.riru.edxp.Main; +import com.elderdrivers.riru.edxp.art.ClassLinker; import com.elderdrivers.riru.edxp.config.BaseHookProvider; +import com.elderdrivers.riru.edxp.core.ResourcesHook; +import com.elderdrivers.riru.edxp.core.Yahfa; import com.elderdrivers.riru.edxp.sandhook.dexmaker.DynamicBridge; import com.swift.sandhook.xposedcompat.XposedCompat; import com.swift.sandhook.xposedcompat.methodgen.SandHookXposedBridge; @@ -46,12 +49,12 @@ public class SandHookProvider extends BaseHookProvider { @Override public Object findMethodNative(Class clazz, String methodName, String methodSig) { - return Main.findMethodNative(clazz, methodName, methodSig); + return Yahfa.findMethodNative(clazz, methodName, methodSig); } @Override public void deoptMethodNative(Object method) { - Main.deoptMethodNative(method); + ClassLinker.setEntryPointsToInterpreter((Member) method); } @Override @@ -61,11 +64,11 @@ public class SandHookProvider extends BaseHookProvider { @Override public boolean initXResourcesNative() { - return Main.initXResourcesNative(); + return ResourcesHook.initXResourcesNative(); } @Override public boolean removeFinalFlagNative(Class clazz) { - return Main.removeFinalFlagNative(clazz); + return ResourcesHook.removeFinalFlagNative(clazz); } } diff --git a/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/core/HookMain.java b/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/core/HookMain.java index 88dcda91..f86b64d5 100644 --- a/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/core/HookMain.java +++ b/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/core/HookMain.java @@ -1,5 +1,7 @@ package com.elderdrivers.riru.edxp.sandhook.core; +import com.elderdrivers.riru.edxp.art.Heap; +import com.elderdrivers.riru.edxp.core.Yahfa; import com.elderdrivers.riru.edxp.util.Utils; import com.elderdrivers.riru.edxp.Main; import com.elderdrivers.riru.edxp.sandhook._hooker.OnePlusWorkAroundHooker; @@ -14,9 +16,6 @@ import java.util.Set; import de.robv.android.xposed.XposedHelpers; -import static com.elderdrivers.riru.edxp.Main.backupAndHookNative; -import static com.elderdrivers.riru.edxp.Main.findMethodNative; - public class HookMain { private static Set hookItemWhiteList = Collections.singleton(OnePlusWorkAroundHooker.class.getName()); @@ -116,13 +115,13 @@ public class HookMain { } // make sure GC completed before hook Thread currentThread = Thread.currentThread(); - int lastGcType = Main.waitForGcToComplete( + int lastGcType = Heap.waitForGcToComplete( XposedHelpers.getLongField(currentThread, "nativePeer")); if (lastGcType < 0) { Utils.logW("waitForGcToComplete failed, using fallback"); Runtime.getRuntime().gc(); } - if (!backupAndHookNative(target, hook, backup)) { + if (!Yahfa.backupAndHookNative(target, hook, backup)) { throw new RuntimeException("Failed to hook " + target + " with " + hook); } } @@ -137,7 +136,7 @@ public class HookMain { if (methodSig == null) { throw new IllegalArgumentException("null method signature"); } - return findMethodNative(cls, methodName, methodSig); + return Yahfa.findMethodNative(cls, methodName, methodSig); } private static void checkCompatibleMethods(Object original, Method replacement, String originalName, String replacementName) { diff --git a/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/core/HookMethodResolver.java b/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/core/HookMethodResolver.java index c5290914..7e02fc1c 100644 --- a/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/core/HookMethodResolver.java +++ b/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/core/HookMethodResolver.java @@ -2,6 +2,7 @@ package com.elderdrivers.riru.edxp.sandhook.core; import android.os.Build; +import com.elderdrivers.riru.edxp.core.Yahfa; import com.elderdrivers.riru.edxp.util.Utils; import com.elderdrivers.riru.edxp.Main; @@ -122,7 +123,7 @@ public class HookMethodResolver { } private static void resolveInNative(Method hook, Method backup) { - Main.ensureMethodCached(hook, backup); + Yahfa.ensureMethodCached(hook, backup); } public static Field getField(Class topClass, String fieldName) throws NoSuchFieldException { diff --git a/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/dexmaker/HookerDexMaker.java b/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/dexmaker/HookerDexMaker.java index 537a217a..dbff519a 100644 --- a/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/dexmaker/HookerDexMaker.java +++ b/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/dexmaker/HookerDexMaker.java @@ -5,6 +5,7 @@ import android.os.Build; import android.text.TextUtils; import com.elderdrivers.riru.edxp.Main; +import com.elderdrivers.riru.edxp.core.Yahfa; import com.elderdrivers.riru.edxp.sandhook.core.HookMain; import java.io.File; @@ -219,7 +220,7 @@ public class HookerDexMaker { mHookMethod = mHookClass.getMethod(METHOD_NAME_HOOK, mActualParameterTypes); mBackupMethod = mHookClass.getMethod(METHOD_NAME_BACKUP, mActualParameterTypes); mCallBackupMethod = mHookClass.getMethod(METHOD_NAME_CALL_BACKUP, mActualParameterTypes); - Main.setMethodNonCompilable(mCallBackupMethod); + Yahfa.setMethodNonCompilable(mCallBackupMethod); HookMain.backupAndHook(mMember, mHookMethod, mBackupMethod); } diff --git a/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/proxy/BlackWhiteListProxy.java b/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/proxy/BlackWhiteListProxy.java index 02b1feda..c251a21e 100644 --- a/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/proxy/BlackWhiteListProxy.java +++ b/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/proxy/BlackWhiteListProxy.java @@ -5,13 +5,13 @@ import android.text.TextUtils; import com.elderdrivers.riru.edxp.Main; import com.elderdrivers.riru.edxp.config.ConfigManager; import com.elderdrivers.riru.edxp.deopt.PrebuiltMethodsDeopter; +import com.elderdrivers.riru.edxp.framework.Zygote; import com.elderdrivers.riru.edxp.sandhook.entry.Router; import com.elderdrivers.riru.edxp.util.ProcessUtils; import com.elderdrivers.riru.edxp.util.Utils; import de.robv.android.xposed.XposedBridge; -import static com.elderdrivers.riru.edxp.Main.isAppNeedHook; import static com.elderdrivers.riru.edxp.util.FileUtils.getDataPathPrefix; /** @@ -40,7 +40,7 @@ public class BlackWhiteListProxy { String niceName, int[] fdsToClose, int[] fdsToIgnore, boolean startChildZygote, String instructionSet, String appDataDir) { - final boolean isDynamicModulesMode = Main.isDynamicModulesEnabled(); + final boolean isDynamicModulesMode = ConfigManager.isDynamicModulesEnabled(); if (isDynamicModulesMode) { // should never happen return; @@ -56,7 +56,7 @@ public class BlackWhiteListProxy { public static void forkSystemServerPre(int uid, int gid, int[] gids, int debugFlags, int[][] rlimits, long permittedCapabilities, long effectiveCapabilities) { - final boolean isDynamicModulesMode = Main.isDynamicModulesEnabled(); + final boolean isDynamicModulesMode = ConfigManager.isDynamicModulesEnabled(); if (isDynamicModulesMode) { // should never happen return; @@ -75,7 +75,6 @@ public class BlackWhiteListProxy { private static void onForkPreForNonDynamicMode(boolean isSystemServer) { Router.onForkStart(); Router.initResourcesHook(); - ConfigManager.setDynamicModulesMode(false); // set startsSystemServer flag used when loadModules Router.prepare(isSystemServer); // deoptBootMethods once for all child processes of zygote @@ -86,16 +85,15 @@ public class BlackWhiteListProxy { // loadModules once for all child processes of zygote // TODO maybe just save initZygote callbacks and call them when whitelisted process forked? Router.loadModulesSafely(true); - Main.closeFilesBeforeForkNative(); + Zygote.closeFilesBeforeFork(); } private static void onForkPostCommon(boolean isSystemServer, String appDataDir, String niceName) { Main.setAppDataDir(appDataDir); Main.niceName = niceName; - final boolean isDynamicModulesMode = Main.isDynamicModulesEnabled(); - ConfigManager.setDynamicModulesMode(isDynamicModulesMode); + final boolean isDynamicModulesMode = ConfigManager.isDynamicModulesEnabled(); if (!isDynamicModulesMode) { - Main.reopenFilesAfterForkNative(); + Zygote.reopenFilesAfterFork(); } Router.onEnterChildProcess(); if (!checkNeedHook(appDataDir, niceName)) { @@ -123,7 +121,7 @@ public class BlackWhiteListProxy { needHook = false; } else { // FIXME some process cannot read app_data_file because of MLS, e.g. bluetooth - needHook = isAppNeedHook(appDataDir); + needHook = ConfigManager.isAppNeedHook(appDataDir); } if (!needHook) { // clean up the scene diff --git a/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/proxy/NormalProxy.java b/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/proxy/NormalProxy.java index 476977ae..403e06ea 100644 --- a/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/proxy/NormalProxy.java +++ b/edxp-sandhook/src/main/java/com/elderdrivers/riru/edxp/sandhook/proxy/NormalProxy.java @@ -3,6 +3,7 @@ package com.elderdrivers.riru.edxp.sandhook.proxy; import com.elderdrivers.riru.edxp.Main; import com.elderdrivers.riru.edxp.config.ConfigManager; import com.elderdrivers.riru.edxp.deopt.PrebuiltMethodsDeopter; +import com.elderdrivers.riru.edxp.framework.Zygote; import com.elderdrivers.riru.edxp.sandhook.entry.Router; import static com.elderdrivers.riru.edxp.util.FileUtils.getDataPathPrefix; @@ -17,8 +18,6 @@ public class NormalProxy { // mainly for secondary zygote Router.onForkStart(); Router.initResourcesHook(); - final boolean isDynamicModulesMode = Main.isDynamicModulesEnabled(); - ConfigManager.setDynamicModulesMode(isDynamicModulesMode); // call this to ensure the flag is set to false ASAP Router.prepare(false); PrebuiltMethodsDeopter.deoptBootMethods(); // do it once for secondary zygote @@ -26,7 +25,7 @@ public class NormalProxy { Router.installBootstrapHooks(false); // only load modules for secondary zygote Router.loadModulesSafely(true); - Main.closeFilesBeforeForkNative(); + Zygote.closeFilesBeforeFork(); } public static void forkAndSpecializePost(int pid, String appDataDir, String niceName) { @@ -34,7 +33,7 @@ public class NormalProxy { Main.setAppDataDir(appDataDir); Main.niceName = niceName; Router.prepare(false); - Main.reopenFilesAfterForkNative(); + Zygote.reopenFilesAfterFork(); Router.onEnterChildProcess(); // load modules for each app process on its forked if dynamic modules mode is on Router.loadModulesSafely(false); @@ -45,8 +44,6 @@ public class NormalProxy { long permittedCapabilities, long effectiveCapabilities) { Router.onForkStart(); Router.initResourcesHook(); - final boolean isDynamicModulesMode = Main.isDynamicModulesEnabled(); - ConfigManager.setDynamicModulesMode(isDynamicModulesMode); // set startsSystemServer flag used when loadModules Router.prepare(true); PrebuiltMethodsDeopter.deoptBootMethods(); // do it once for main zygote @@ -58,7 +55,7 @@ public class NormalProxy { // because if not global hooks installed in initZygote might not be // propagated to processes not forked via forkAndSpecialize Router.loadModulesSafely(true); - Main.closeFilesBeforeForkNative(); + Zygote.closeFilesBeforeFork(); } public static void forkSystemServerPost(int pid) { @@ -66,7 +63,7 @@ public class NormalProxy { Main.setAppDataDir(getDataPathPrefix() + "android"); Main.niceName = "system_server"; Router.prepare(true); - Main.reopenFilesAfterForkNative(); + Zygote.reopenFilesAfterFork(); Router.onEnterChildProcess(); // reload module list if dynamic mode is on Router.loadModulesSafely(false); diff --git a/edxp-whale/build.gradle b/edxp-whale/build.gradle index ae5eaea8..bb0fa267 100644 --- a/edxp-whale/build.gradle +++ b/edxp-whale/build.gradle @@ -1,5 +1,8 @@ apply plugin: 'com.android.application' +sourceCompatibility = "7" +targetCompatibility = "7" + android { compileSdkVersion 28 diff --git a/edxp-whale/proguard-rules.pro b/edxp-whale/proguard-rules.pro index 92942582..503061cb 100644 --- a/edxp-whale/proguard-rules.pro +++ b/edxp-whale/proguard-rules.pro @@ -30,4 +30,8 @@ -keep class * implements com.elderdrivers.riru.common.KeepAll { *; } -keepclassmembers class * implements com.elderdrivers.riru.common.KeepMembers { *; } --keep class com.lody.** {*;} \ No newline at end of file +-keep class com.lody.** {*;} + +-keepclasseswithmember class * { + native ; +} \ No newline at end of file diff --git a/edxp-whale/src/main/java/com/elderdrivers/riru/edxp/Main.java b/edxp-whale/src/main/java/com/elderdrivers/riru/edxp/Main.java index 6fe810b9..5899d658 100644 --- a/edxp-whale/src/main/java/com/elderdrivers/riru/edxp/Main.java +++ b/edxp-whale/src/main/java/com/elderdrivers/riru/edxp/Main.java @@ -5,14 +5,15 @@ import android.os.Build; import android.os.Process; import com.elderdrivers.riru.common.KeepAll; +import com.elderdrivers.riru.edxp.config.ConfigManager; import com.elderdrivers.riru.edxp.config.InstallerChooser; +import com.elderdrivers.riru.edxp.core.Yahfa; import com.elderdrivers.riru.edxp.util.Utils; import com.elderdrivers.riru.edxp.whale.core.HookMethodResolver; import com.elderdrivers.riru.edxp.whale.entry.Router; import com.elderdrivers.riru.edxp.whale.proxy.BlackWhiteListProxy; import com.elderdrivers.riru.edxp.whale.proxy.NormalProxy; -import java.lang.reflect.Method; import java.util.Arrays; @SuppressLint("DefaultLocale") @@ -25,10 +26,10 @@ public class Main implements KeepAll { private static String forkSystemServerPramsStr = ""; static { - init(Build.VERSION.SDK_INT); + Yahfa.init(Build.VERSION.SDK_INT); HookMethodResolver.init(); Router.injectConfig(); - InstallerChooser.setInstallerPackageName(getInstallerPkgName()); + InstallerChooser.setInstallerPackageName(ConfigManager.getInstallerPackageName()); } /////////////////////////////////////////////////////////////////////////////////////////////// @@ -47,7 +48,7 @@ public class Main implements KeepAll { mountExternal, seInfo, niceName, Arrays.toString(fdsToClose), Arrays.toString(fdsToIgnore), startChildZygote, instructionSet, appDataDir); } - if (isBlackWhiteListEnabled()) { + if (ConfigManager.isBlackWhiteListEnabled()) { BlackWhiteListProxy.forkAndSpecializePre(uid, gid, gids, debugFlags, rlimits, mountExternal, seInfo, niceName, fdsToClose, fdsToIgnore, startChildZygote, instructionSet, appDataDir); @@ -61,7 +62,7 @@ public class Main implements KeepAll { public static void forkAndSpecializePost(int pid, String appDataDir, String niceName) { if (pid == 0) { Utils.logD(forkAndSpecializePramsStr + " = " + Process.myPid()); - if (isBlackWhiteListEnabled()) { + if (ConfigManager.isBlackWhiteListEnabled()) { BlackWhiteListProxy.forkAndSpecializePost(pid, appDataDir, niceName); } else { NormalProxy.forkAndSpecializePost(pid, appDataDir, niceName); @@ -79,7 +80,7 @@ public class Main implements KeepAll { uid, gid, Arrays.toString(gids), debugFlags, Arrays.toString(rlimits), permittedCapabilities, effectiveCapabilities); } - if (isBlackWhiteListEnabled()) { + if (ConfigManager.isBlackWhiteListEnabled()) { BlackWhiteListProxy.forkSystemServerPre(uid, gid, gids, debugFlags, rlimits, permittedCapabilities, effectiveCapabilities); } else { @@ -91,7 +92,7 @@ public class Main implements KeepAll { public static void forkSystemServerPost(int pid) { if (pid == 0) { Utils.logD(forkSystemServerPramsStr + " = " + Process.myPid()); - if (isBlackWhiteListEnabled()) { + if (ConfigManager.isBlackWhiteListEnabled()) { BlackWhiteListProxy.forkSystemServerPost(pid); } else { NormalProxy.forkSystemServerPost(pid); @@ -102,46 +103,4 @@ public class Main implements KeepAll { } } - /////////////////////////////////////////////////////////////////////////////////////////////// - // native methods - /////////////////////////////////////////////////////////////////////////////////////////////// - - public static native boolean backupAndHookNative(Object target, Method hook, Method backup); - - public static native void setMethodNonCompilable(Object member); - - public static native void ensureMethodCached(Method hook, Method backup); - - // JNI.ToReflectedMethod() could return either Method or Constructor - public static native Object findMethodNative(Class targetClass, String methodName, String methodSig); - - private static native void init(int SDK_version); - - public static native String getInstallerPkgName(); - - public static native boolean isBlackWhiteListEnabled(); - - public static native boolean isDynamicModulesEnabled(); - - public static native boolean isAppNeedHook(String appDataDir); - - // prevent from fatal error caused by holding not whitelisted file descriptors when forking zygote - // https://github.com/rovo89/Xposed/commit/b3ba245ad04cd485699fb1d2ebde7117e58214ff - public static native void closeFilesBeforeForkNative(); - - public static native void reopenFilesAfterForkNative(); - - public static native void deoptMethodNative(Object object); - - public static native long suspendAllThreads(); - - public static native void resumeAllThreads(long obj); - - public static native int waitForGcToComplete(long thread); - - public static native boolean initXResourcesNative(); - - public static native boolean removeFinalFlagNative(Class clazz); - - public static native boolean isResourcesHookEnabled(); } diff --git a/edxp-whale/src/main/java/com/elderdrivers/riru/edxp/whale/config/WhaleEdxpConfig.java b/edxp-whale/src/main/java/com/elderdrivers/riru/edxp/whale/config/WhaleEdxpConfig.java index f68976e6..6246b282 100644 --- a/edxp-whale/src/main/java/com/elderdrivers/riru/edxp/whale/config/WhaleEdxpConfig.java +++ b/edxp-whale/src/main/java/com/elderdrivers/riru/edxp/whale/config/WhaleEdxpConfig.java @@ -1,6 +1,7 @@ package com.elderdrivers.riru.edxp.whale.config; import com.elderdrivers.riru.edxp.Main; +import com.elderdrivers.riru.edxp.config.ConfigManager; import com.elderdrivers.riru.edxp.config.EdXpConfig; import com.elderdrivers.riru.edxp.config.InstallerChooser; import com.elderdrivers.riru.edxp.hooker.XposedBlackListHooker; @@ -18,11 +19,11 @@ public class WhaleEdxpConfig implements EdXpConfig { @Override public boolean isDynamicModulesMode() { - return Main.isDynamicModulesEnabled(); + return ConfigManager.isDynamicModulesEnabled(); } @Override public boolean isResourcesHookEnabled() { - return Main.isResourcesHookEnabled(); + return ConfigManager.isResourcesHookEnabled(); } } diff --git a/edxp-whale/src/main/java/com/elderdrivers/riru/edxp/whale/config/WhaleHookProvider.java b/edxp-whale/src/main/java/com/elderdrivers/riru/edxp/whale/config/WhaleHookProvider.java index 5c1904a1..f4a7997e 100644 --- a/edxp-whale/src/main/java/com/elderdrivers/riru/edxp/whale/config/WhaleHookProvider.java +++ b/edxp-whale/src/main/java/com/elderdrivers/riru/edxp/whale/config/WhaleHookProvider.java @@ -1,7 +1,10 @@ package com.elderdrivers.riru.edxp.whale.config; import com.elderdrivers.riru.edxp.Main; +import com.elderdrivers.riru.edxp.art.ClassLinker; import com.elderdrivers.riru.edxp.config.BaseHookProvider; +import com.elderdrivers.riru.edxp.core.ResourcesHook; +import com.elderdrivers.riru.edxp.core.Yahfa; import com.lody.whale.WhaleRuntime; import java.lang.reflect.Member; @@ -45,12 +48,12 @@ public class WhaleHookProvider extends BaseHookProvider { @Override public Object findMethodNative(Class clazz, String methodName, String methodSig) { - return Main.findMethodNative(clazz, methodName, methodSig); + return Yahfa.findMethodNative(clazz, methodName, methodSig); } @Override public void deoptMethodNative(Object method) { - Main.deoptMethodNative(method); + ClassLinker.setEntryPointsToInterpreter((Member) method); } @Override @@ -60,12 +63,12 @@ public class WhaleHookProvider extends BaseHookProvider { @Override public boolean initXResourcesNative() { - return Main.initXResourcesNative(); + return ResourcesHook.initXResourcesNative(); } @Override public boolean removeFinalFlagNative(Class clazz) { - return Main.removeFinalFlagNative(clazz); + return ResourcesHook.removeFinalFlagNative(clazz); } diff --git a/edxp-whale/src/main/java/com/elderdrivers/riru/edxp/whale/core/HookMain.java b/edxp-whale/src/main/java/com/elderdrivers/riru/edxp/whale/core/HookMain.java index d8337af0..5abe1d3e 100644 --- a/edxp-whale/src/main/java/com/elderdrivers/riru/edxp/whale/core/HookMain.java +++ b/edxp-whale/src/main/java/com/elderdrivers/riru/edxp/whale/core/HookMain.java @@ -1,6 +1,8 @@ package com.elderdrivers.riru.edxp.whale.core; import com.elderdrivers.riru.edxp.Main; +import com.elderdrivers.riru.edxp.art.Heap; +import com.elderdrivers.riru.edxp.core.Yahfa; import com.elderdrivers.riru.edxp.whale._hooker.OnePlusWorkAroundHooker; import com.elderdrivers.riru.edxp.util.Utils; @@ -14,9 +16,6 @@ import java.util.Set; import de.robv.android.xposed.XposedHelpers; -import static com.elderdrivers.riru.edxp.Main.backupAndHookNative; -import static com.elderdrivers.riru.edxp.Main.findMethodNative; - public class HookMain { private static Set hookItemWhiteList = Collections.singleton(OnePlusWorkAroundHooker.class.getName()); @@ -116,13 +115,13 @@ public class HookMain { } // make sure GC completed before hook Thread currentThread = Thread.currentThread(); - int lastGcType = Main.waitForGcToComplete( + int lastGcType = Heap.waitForGcToComplete( XposedHelpers.getLongField(currentThread, "nativePeer")); if (lastGcType < 0) { Utils.logW("waitForGcToComplete failed, using fallback"); Runtime.getRuntime().gc(); } - if (!backupAndHookNative(target, hook, backup)) { + if (!Yahfa.backupAndHookNative(target, hook, backup)) { throw new RuntimeException("Failed to hook " + target + " with " + hook); } } @@ -137,7 +136,7 @@ public class HookMain { if (methodSig == null) { throw new IllegalArgumentException("null method signature"); } - return findMethodNative(cls, methodName, methodSig); + return Yahfa.findMethodNative(cls, methodName, methodSig); } private static void checkCompatibleMethods(Object original, Method replacement, String originalName, String replacementName) { diff --git a/edxp-whale/src/main/java/com/elderdrivers/riru/edxp/whale/core/HookMethodResolver.java b/edxp-whale/src/main/java/com/elderdrivers/riru/edxp/whale/core/HookMethodResolver.java index 5cdb9f02..b7f5a17c 100644 --- a/edxp-whale/src/main/java/com/elderdrivers/riru/edxp/whale/core/HookMethodResolver.java +++ b/edxp-whale/src/main/java/com/elderdrivers/riru/edxp/whale/core/HookMethodResolver.java @@ -3,6 +3,7 @@ package com.elderdrivers.riru.edxp.whale.core; import android.os.Build; import com.elderdrivers.riru.edxp.Main; +import com.elderdrivers.riru.edxp.core.Yahfa; import com.elderdrivers.riru.edxp.util.Utils; import java.lang.reflect.Field; @@ -122,7 +123,7 @@ public class HookMethodResolver { } private static void resolveInNative(Method hook, Method backup) { - Main.ensureMethodCached(hook, backup); + Yahfa.ensureMethodCached(hook, backup); } public static Field getField(Class topClass, String fieldName) throws NoSuchFieldException { diff --git a/edxp-whale/src/main/java/com/elderdrivers/riru/edxp/whale/proxy/BlackWhiteListProxy.java b/edxp-whale/src/main/java/com/elderdrivers/riru/edxp/whale/proxy/BlackWhiteListProxy.java index ea9fa7b5..d8e97686 100644 --- a/edxp-whale/src/main/java/com/elderdrivers/riru/edxp/whale/proxy/BlackWhiteListProxy.java +++ b/edxp-whale/src/main/java/com/elderdrivers/riru/edxp/whale/proxy/BlackWhiteListProxy.java @@ -5,13 +5,13 @@ import android.text.TextUtils; import com.elderdrivers.riru.edxp.Main; import com.elderdrivers.riru.edxp.config.ConfigManager; import com.elderdrivers.riru.edxp.deopt.PrebuiltMethodsDeopter; +import com.elderdrivers.riru.edxp.framework.Zygote; import com.elderdrivers.riru.edxp.util.ProcessUtils; import com.elderdrivers.riru.edxp.util.Utils; import com.elderdrivers.riru.edxp.whale.entry.Router; import de.robv.android.xposed.XposedBridge; -import static com.elderdrivers.riru.edxp.Main.isAppNeedHook; import static com.elderdrivers.riru.edxp.util.FileUtils.getDataPathPrefix; /** @@ -40,7 +40,7 @@ public class BlackWhiteListProxy { String niceName, int[] fdsToClose, int[] fdsToIgnore, boolean startChildZygote, String instructionSet, String appDataDir) { - final boolean isDynamicModulesMode = Main.isDynamicModulesEnabled(); + final boolean isDynamicModulesMode = ConfigManager.isDynamicModulesEnabled(); if (isDynamicModulesMode) { // should never happen return; @@ -56,7 +56,7 @@ public class BlackWhiteListProxy { public static void forkSystemServerPre(int uid, int gid, int[] gids, int debugFlags, int[][] rlimits, long permittedCapabilities, long effectiveCapabilities) { - final boolean isDynamicModulesMode = Main.isDynamicModulesEnabled(); + final boolean isDynamicModulesMode = ConfigManager.isDynamicModulesEnabled(); if (isDynamicModulesMode) { // should never happen return; @@ -75,7 +75,6 @@ public class BlackWhiteListProxy { private static void onForkPreForNonDynamicMode(boolean isSystemServer) { Router.onForkStart(); Router.initResourcesHook(); - ConfigManager.setDynamicModulesMode(false); // set startsSystemServer flag used when loadModules Router.prepare(isSystemServer); // deoptBootMethods once for all child processes of zygote @@ -86,16 +85,15 @@ public class BlackWhiteListProxy { // loadModules once for all child processes of zygote // TODO maybe just save initZygote callbacks and call them when whitelisted process forked? Router.loadModulesSafely(true); - Main.closeFilesBeforeForkNative(); + Zygote.closeFilesBeforeFork(); } private static void onForkPostCommon(boolean isSystemServer, String appDataDir, String niceName) { Main.appDataDir = appDataDir; Main.niceName = niceName; - final boolean isDynamicModulesMode = Main.isDynamicModulesEnabled(); - ConfigManager.setDynamicModulesMode(isDynamicModulesMode); + final boolean isDynamicModulesMode = ConfigManager.isDynamicModulesEnabled(); if (!isDynamicModulesMode) { - Main.reopenFilesAfterForkNative(); + Zygote.reopenFilesAfterFork(); } Router.onEnterChildProcess(); if (!checkNeedHook(appDataDir, niceName)) { @@ -123,7 +121,7 @@ public class BlackWhiteListProxy { needHook = false; } else { // FIXME some process cannot read app_data_file because of MLS, e.g. bluetooth - needHook = isAppNeedHook(appDataDir); + needHook = ConfigManager.isAppNeedHook(appDataDir); } if (!needHook) { // clean up the scene diff --git a/edxp-whale/src/main/java/com/elderdrivers/riru/edxp/whale/proxy/NormalProxy.java b/edxp-whale/src/main/java/com/elderdrivers/riru/edxp/whale/proxy/NormalProxy.java index 2327c6be..9f39c0dd 100644 --- a/edxp-whale/src/main/java/com/elderdrivers/riru/edxp/whale/proxy/NormalProxy.java +++ b/edxp-whale/src/main/java/com/elderdrivers/riru/edxp/whale/proxy/NormalProxy.java @@ -3,6 +3,7 @@ package com.elderdrivers.riru.edxp.whale.proxy; import com.elderdrivers.riru.edxp.Main; import com.elderdrivers.riru.edxp.config.ConfigManager; import com.elderdrivers.riru.edxp.deopt.PrebuiltMethodsDeopter; +import com.elderdrivers.riru.edxp.framework.Zygote; import com.elderdrivers.riru.edxp.whale.entry.Router; import static com.elderdrivers.riru.edxp.util.FileUtils.getDataPathPrefix; @@ -17,8 +18,6 @@ public class NormalProxy { // mainly for secondary zygote Router.onForkStart(); Router.initResourcesHook(); - final boolean isDynamicModulesMode = Main.isDynamicModulesEnabled(); - ConfigManager.setDynamicModulesMode(isDynamicModulesMode); // call this to ensure the flag is set to false ASAP Router.prepare(false); PrebuiltMethodsDeopter.deoptBootMethods(); // do it once for secondary zygote @@ -26,7 +25,7 @@ public class NormalProxy { Router.installBootstrapHooks(false); // only load modules for secondary zygote Router.loadModulesSafely(true); - Main.closeFilesBeforeForkNative(); + Zygote.closeFilesBeforeFork(); } public static void forkAndSpecializePost(int pid, String appDataDir, String niceName) { @@ -34,7 +33,7 @@ public class NormalProxy { Main.appDataDir = appDataDir; Main.niceName = niceName; Router.prepare(false); - Main.reopenFilesAfterForkNative(); + Zygote.reopenFilesAfterFork(); Router.onEnterChildProcess(); // load modules for each app process on its forked if dynamic modules mode is on Router.loadModulesSafely(false); @@ -45,8 +44,6 @@ public class NormalProxy { long permittedCapabilities, long effectiveCapabilities) { Router.onForkStart(); Router.initResourcesHook(); - final boolean isDynamicModulesMode = Main.isDynamicModulesEnabled(); - ConfigManager.setDynamicModulesMode(isDynamicModulesMode); // set startsSystemServer flag used when loadModules Router.prepare(true); PrebuiltMethodsDeopter.deoptBootMethods(); // do it once for main zygote @@ -58,7 +55,7 @@ public class NormalProxy { // because if not global hooks installed in initZygote might not be // propagated to processes not forked via forkAndSpecialize Router.loadModulesSafely(true); - Main.closeFilesBeforeForkNative(); + Zygote.closeFilesBeforeFork(); } public static void forkSystemServerPost(int pid) { @@ -66,7 +63,7 @@ public class NormalProxy { Main.appDataDir = getDataPathPrefix() + "android"; Main.niceName = "system_server"; Router.prepare(true); - Main.reopenFilesAfterForkNative(); + Zygote.reopenFilesAfterFork(); Router.onEnterChildProcess(); // reload module list if dynamic mode is on Router.loadModulesSafely(false); diff --git a/edxp-yahfa/build.gradle b/edxp-yahfa/build.gradle index 4af8c8a7..7250442b 100644 --- a/edxp-yahfa/build.gradle +++ b/edxp-yahfa/build.gradle @@ -1,5 +1,8 @@ apply plugin: 'com.android.application' +sourceCompatibility = "7" +targetCompatibility = "7" + android { compileSdkVersion 28 @@ -45,7 +48,7 @@ preBuild.doLast { afterEvaluate { tasks.withType(JavaCompile) { - options.compilerArgs.add("-Xbootclasspath/p:${hiddenApiStubJarFilePath}") + options.compilerArgs << "-Xbootclasspath/p:${hiddenApiStubJarFilePath}" } android.applicationVariants.all { variant -> diff --git a/edxp-yahfa/proguard-rules.pro b/edxp-yahfa/proguard-rules.pro index 721fdf5c..c600fee6 100644 --- a/edxp-yahfa/proguard-rules.pro +++ b/edxp-yahfa/proguard-rules.pro @@ -28,4 +28,8 @@ -keep interface com.elderdrivers.riru.common.KeepMembers -keep class * implements com.elderdrivers.riru.common.KeepAll { *; } --keepclassmembers class * implements com.elderdrivers.riru.common.KeepMembers { *; } \ No newline at end of file +-keepclassmembers class * implements com.elderdrivers.riru.common.KeepMembers { *; } + +-keepclasseswithmember class * { + native ; +} \ No newline at end of file diff --git a/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/Main.java b/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/Main.java index d94b3092..7eed9a4a 100644 --- a/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/Main.java +++ b/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/Main.java @@ -1,20 +1,19 @@ package com.elderdrivers.riru.edxp; import android.annotation.SuppressLint; -import android.content.res.Resources; -import android.content.res.XResources; import android.os.Build; import android.os.Process; import com.elderdrivers.riru.common.KeepAll; +import com.elderdrivers.riru.edxp.config.ConfigManager; import com.elderdrivers.riru.edxp.config.InstallerChooser; +import com.elderdrivers.riru.edxp.core.Yahfa; import com.elderdrivers.riru.edxp.util.Utils; import com.elderdrivers.riru.edxp.yahfa.core.HookMethodResolver; import com.elderdrivers.riru.edxp.yahfa.entry.Router; import com.elderdrivers.riru.edxp.yahfa.proxy.BlackWhiteListProxy; import com.elderdrivers.riru.edxp.yahfa.proxy.NormalProxy; -import java.lang.reflect.Method; import java.util.Arrays; @SuppressLint("DefaultLocale") @@ -27,10 +26,10 @@ public class Main implements KeepAll { private static String forkSystemServerPramsStr = ""; static { - init(Build.VERSION.SDK_INT); + Yahfa.init(Build.VERSION.SDK_INT); HookMethodResolver.init(); Router.injectConfig(); - InstallerChooser.setInstallerPackageName(getInstallerPkgName()); + InstallerChooser.setInstallerPackageName(ConfigManager.getInstallerPackageName()); } /////////////////////////////////////////////////////////////////////////////////////////////// @@ -49,7 +48,7 @@ public class Main implements KeepAll { mountExternal, seInfo, niceName, Arrays.toString(fdsToClose), Arrays.toString(fdsToIgnore), startChildZygote, instructionSet, appDataDir); } - if (isBlackWhiteListEnabled()) { + if (ConfigManager.isBlackWhiteListEnabled()) { BlackWhiteListProxy.forkAndSpecializePre(uid, gid, gids, debugFlags, rlimits, mountExternal, seInfo, niceName, fdsToClose, fdsToIgnore, startChildZygote, instructionSet, appDataDir); @@ -63,7 +62,7 @@ public class Main implements KeepAll { public static void forkAndSpecializePost(int pid, String appDataDir, String niceName) { if (pid == 0) { Utils.logD(forkAndSpecializePramsStr + " = " + Process.myPid()); - if (isBlackWhiteListEnabled()) { + if (ConfigManager.isBlackWhiteListEnabled()) { BlackWhiteListProxy.forkAndSpecializePost(pid, appDataDir, niceName); } else { NormalProxy.forkAndSpecializePost(pid, appDataDir, niceName); @@ -81,7 +80,7 @@ public class Main implements KeepAll { uid, gid, Arrays.toString(gids), debugFlags, Arrays.toString(rlimits), permittedCapabilities, effectiveCapabilities); } - if (isBlackWhiteListEnabled()) { + if (ConfigManager.isBlackWhiteListEnabled()) { BlackWhiteListProxy.forkSystemServerPre(uid, gid, gids, debugFlags, rlimits, permittedCapabilities, effectiveCapabilities); } else { @@ -93,7 +92,7 @@ public class Main implements KeepAll { public static void forkSystemServerPost(int pid) { if (pid == 0) { Utils.logD(forkSystemServerPramsStr + " = " + Process.myPid()); - if (isBlackWhiteListEnabled()) { + if (ConfigManager.isBlackWhiteListEnabled()) { BlackWhiteListProxy.forkSystemServerPost(pid); } else { NormalProxy.forkSystemServerPost(pid); @@ -104,46 +103,4 @@ public class Main implements KeepAll { } } - /////////////////////////////////////////////////////////////////////////////////////////////// - // native methods - /////////////////////////////////////////////////////////////////////////////////////////////// - - public static native boolean backupAndHookNative(Object target, Method hook, Method backup); - - public static native void setMethodNonCompilable(Object member); - - public static native void ensureMethodCached(Method hook, Method backup); - - // JNI.ToReflectedMethod() could return either Method or Constructor - public static native Object findMethodNative(Class targetClass, String methodName, String methodSig); - - private static native void init(int SDK_version); - - public static native String getInstallerPkgName(); - - public static native boolean isBlackWhiteListEnabled(); - - public static native boolean isDynamicModulesEnabled(); - - public static native boolean isAppNeedHook(String appDataDir); - - // prevent from fatal error caused by holding not whitelisted file descriptors when forking zygote - // https://github.com/rovo89/Xposed/commit/b3ba245ad04cd485699fb1d2ebde7117e58214ff - public static native void closeFilesBeforeForkNative(); - - public static native void reopenFilesAfterForkNative(); - - public static native void deoptMethodNative(Object object); - - public static native long suspendAllThreads(); - - public static native void resumeAllThreads(long obj); - - public static native int waitForGcToComplete(long thread); - - public static native boolean initXResourcesNative(); - - public static native boolean removeFinalFlagNative(Class clazz); - - public static native boolean isResourcesHookEnabled(); } diff --git a/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/config/YahfaEdxpConfig.java b/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/config/YahfaEdxpConfig.java index 4dc330cc..308fccc4 100644 --- a/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/config/YahfaEdxpConfig.java +++ b/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/config/YahfaEdxpConfig.java @@ -1,6 +1,7 @@ package com.elderdrivers.riru.edxp.yahfa.config; import com.elderdrivers.riru.edxp.Main; +import com.elderdrivers.riru.edxp.config.ConfigManager; import com.elderdrivers.riru.edxp.config.EdXpConfig; import com.elderdrivers.riru.edxp.config.InstallerChooser; import com.elderdrivers.riru.edxp.hooker.XposedBlackListHooker; @@ -18,11 +19,11 @@ public class YahfaEdxpConfig implements EdXpConfig { @Override public boolean isDynamicModulesMode() { - return Main.isDynamicModulesEnabled(); + return ConfigManager.isDynamicModulesEnabled(); } @Override public boolean isResourcesHookEnabled() { - return Main.isResourcesHookEnabled(); + return ConfigManager.isResourcesHookEnabled(); } } diff --git a/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/config/YahfaHookProvider.java b/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/config/YahfaHookProvider.java index 0f810562..8eabd2e4 100644 --- a/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/config/YahfaHookProvider.java +++ b/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/config/YahfaHookProvider.java @@ -1,7 +1,9 @@ package com.elderdrivers.riru.edxp.yahfa.config; -import com.elderdrivers.riru.edxp.Main; +import com.elderdrivers.riru.edxp.art.ClassLinker; import com.elderdrivers.riru.edxp.config.BaseHookProvider; +import com.elderdrivers.riru.edxp.core.ResourcesHook; +import com.elderdrivers.riru.edxp.core.Yahfa; import com.elderdrivers.riru.edxp.yahfa.dexmaker.DexMakerUtils; import com.elderdrivers.riru.edxp.yahfa.dexmaker.DynamicBridge; @@ -28,21 +30,21 @@ public class YahfaHookProvider extends BaseHookProvider { @Override public Object findMethodNative(Class clazz, String methodName, String methodSig) { - return Main.findMethodNative(clazz, methodName, methodSig); + return Yahfa.findMethodNative(clazz, methodName, methodSig); } @Override public void deoptMethodNative(Object method) { - Main.deoptMethodNative(method); + ClassLinker.setEntryPointsToInterpreter((Member) method); } @Override public boolean initXResourcesNative() { - return Main.initXResourcesNative(); + return ResourcesHook.initXResourcesNative(); } @Override public boolean removeFinalFlagNative(Class clazz) { - return Main.removeFinalFlagNative(clazz); + return ResourcesHook.removeFinalFlagNative(clazz); } } diff --git a/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/core/HookMain.java b/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/core/HookMain.java index 82b98da5..59a4a7ba 100644 --- a/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/core/HookMain.java +++ b/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/core/HookMain.java @@ -1,8 +1,9 @@ package com.elderdrivers.riru.edxp.yahfa.core; -import com.elderdrivers.riru.edxp.Main; -import com.elderdrivers.riru.edxp.yahfa._hooker.OnePlusWorkAroundHooker; +import com.elderdrivers.riru.edxp.art.Heap; +import com.elderdrivers.riru.edxp.core.Yahfa; import com.elderdrivers.riru.edxp.util.Utils; +import com.elderdrivers.riru.edxp.yahfa._hooker.OnePlusWorkAroundHooker; import java.lang.reflect.Constructor; import java.lang.reflect.Method; @@ -14,9 +15,6 @@ import java.util.Set; import de.robv.android.xposed.XposedHelpers; -import static com.elderdrivers.riru.edxp.Main.backupAndHookNative; -import static com.elderdrivers.riru.edxp.Main.findMethodNative; - public class HookMain { private static Set hookItemWhiteList = Collections.singleton(OnePlusWorkAroundHooker.class.getName()); @@ -116,13 +114,13 @@ public class HookMain { } // make sure GC completed before hook Thread currentThread = Thread.currentThread(); - int lastGcType = Main.waitForGcToComplete( + int lastGcType = Heap.waitForGcToComplete( XposedHelpers.getLongField(currentThread, "nativePeer")); if (lastGcType < 0) { Utils.logW("waitForGcToComplete failed, using fallback"); Runtime.getRuntime().gc(); } - if (!backupAndHookNative(target, hook, backup)) { + if (!Yahfa.backupAndHookNative(target, hook, backup)) { throw new RuntimeException("Failed to hook " + target + " with " + hook); } } @@ -137,7 +135,7 @@ public class HookMain { if (methodSig == null) { throw new IllegalArgumentException("null method signature"); } - return findMethodNative(cls, methodName, methodSig); + return Yahfa.findMethodNative(cls, methodName, methodSig); } private static void checkCompatibleMethods(Object original, Method replacement, String originalName, String replacementName) { diff --git a/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/core/HookMethodResolver.java b/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/core/HookMethodResolver.java index d8f616b5..49b9edb3 100644 --- a/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/core/HookMethodResolver.java +++ b/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/core/HookMethodResolver.java @@ -3,6 +3,7 @@ package com.elderdrivers.riru.edxp.yahfa.core; import android.os.Build; import com.elderdrivers.riru.edxp.Main; +import com.elderdrivers.riru.edxp.core.Yahfa; import com.elderdrivers.riru.edxp.util.Utils; import java.lang.reflect.Field; @@ -122,7 +123,7 @@ public class HookMethodResolver { } private static void resolveInNative(Method hook, Method backup) { - Main.ensureMethodCached(hook, backup); + Yahfa.ensureMethodCached(hook, backup); } public static Field getField(Class topClass, String fieldName) throws NoSuchFieldException { diff --git a/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/dexmaker/HookerDexMaker.java b/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/dexmaker/HookerDexMaker.java index 56695e55..ee731009 100644 --- a/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/dexmaker/HookerDexMaker.java +++ b/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/dexmaker/HookerDexMaker.java @@ -5,6 +5,7 @@ import android.os.Build; import android.text.TextUtils; import com.elderdrivers.riru.edxp.Main; +import com.elderdrivers.riru.edxp.core.Yahfa; import com.elderdrivers.riru.edxp.yahfa.core.HookMain; import java.io.File; @@ -219,7 +220,7 @@ public class HookerDexMaker { mHookMethod = mHookClass.getMethod(METHOD_NAME_HOOK, mActualParameterTypes); mBackupMethod = mHookClass.getMethod(METHOD_NAME_BACKUP, mActualParameterTypes); mCallBackupMethod = mHookClass.getMethod(METHOD_NAME_CALL_BACKUP, mActualParameterTypes); - Main.setMethodNonCompilable(mCallBackupMethod); + Yahfa.setMethodNonCompilable(mCallBackupMethod); HookMain.backupAndHook(mMember, mHookMethod, mBackupMethod); } diff --git a/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/proxy/BlackWhiteListProxy.java b/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/proxy/BlackWhiteListProxy.java index 5f2578c7..680fed3f 100644 --- a/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/proxy/BlackWhiteListProxy.java +++ b/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/proxy/BlackWhiteListProxy.java @@ -5,13 +5,13 @@ import android.text.TextUtils; import com.elderdrivers.riru.edxp.Main; import com.elderdrivers.riru.edxp.config.ConfigManager; import com.elderdrivers.riru.edxp.deopt.PrebuiltMethodsDeopter; +import com.elderdrivers.riru.edxp.framework.Zygote; import com.elderdrivers.riru.edxp.util.ProcessUtils; import com.elderdrivers.riru.edxp.util.Utils; import com.elderdrivers.riru.edxp.yahfa.entry.Router; import de.robv.android.xposed.XposedBridge; -import static com.elderdrivers.riru.edxp.Main.isAppNeedHook; import static com.elderdrivers.riru.edxp.util.FileUtils.getDataPathPrefix; /** @@ -40,7 +40,7 @@ public class BlackWhiteListProxy { String niceName, int[] fdsToClose, int[] fdsToIgnore, boolean startChildZygote, String instructionSet, String appDataDir) { - final boolean isDynamicModulesMode = Main.isDynamicModulesEnabled(); + final boolean isDynamicModulesMode = ConfigManager.isDynamicModulesEnabled(); if (isDynamicModulesMode) { // should never happen return; @@ -56,7 +56,7 @@ public class BlackWhiteListProxy { public static void forkSystemServerPre(int uid, int gid, int[] gids, int debugFlags, int[][] rlimits, long permittedCapabilities, long effectiveCapabilities) { - final boolean isDynamicModulesMode = Main.isDynamicModulesEnabled(); + final boolean isDynamicModulesMode = ConfigManager.isDynamicModulesEnabled(); if (isDynamicModulesMode) { // should never happen return; @@ -75,7 +75,6 @@ public class BlackWhiteListProxy { private static void onForkPreForNonDynamicMode(boolean isSystemServer) { Router.onForkStart(); Router.initResourcesHook(); - ConfigManager.setDynamicModulesMode(false); // set startsSystemServer flag used when loadModules Router.prepare(isSystemServer); // deoptBootMethods once for all child processes of zygote @@ -86,16 +85,15 @@ public class BlackWhiteListProxy { // loadModules once for all child processes of zygote // TODO maybe just save initZygote callbacks and call them when whitelisted process forked? Router.loadModulesSafely(true); - Main.closeFilesBeforeForkNative(); + Zygote.closeFilesBeforeFork(); } private static void onForkPostCommon(boolean isSystemServer, String appDataDir, String niceName) { Main.appDataDir = appDataDir; Main.niceName = niceName; - final boolean isDynamicModulesMode = Main.isDynamicModulesEnabled(); - ConfigManager.setDynamicModulesMode(isDynamicModulesMode); + final boolean isDynamicModulesMode = ConfigManager.isDynamicModulesEnabled(); if (!isDynamicModulesMode) { - Main.reopenFilesAfterForkNative(); + Zygote.reopenFilesAfterFork(); } Router.onEnterChildProcess(); if (!checkNeedHook(appDataDir, niceName)) { @@ -123,7 +121,7 @@ public class BlackWhiteListProxy { needHook = false; } else { // FIXME some process cannot read app_data_file because of MLS, e.g. bluetooth - needHook = isAppNeedHook(appDataDir); + needHook = ConfigManager.isAppNeedHook(appDataDir); } if (!needHook) { // clean up the scene diff --git a/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/proxy/NormalProxy.java b/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/proxy/NormalProxy.java index c0bfcbf7..9b108e42 100644 --- a/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/proxy/NormalProxy.java +++ b/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/proxy/NormalProxy.java @@ -1,8 +1,8 @@ package com.elderdrivers.riru.edxp.yahfa.proxy; import com.elderdrivers.riru.edxp.Main; -import com.elderdrivers.riru.edxp.config.ConfigManager; import com.elderdrivers.riru.edxp.deopt.PrebuiltMethodsDeopter; +import com.elderdrivers.riru.edxp.framework.Zygote; import com.elderdrivers.riru.edxp.yahfa.entry.Router; import static com.elderdrivers.riru.edxp.util.FileUtils.getDataPathPrefix; @@ -17,8 +17,6 @@ public class NormalProxy { // mainly for secondary zygote Router.onForkStart(); Router.initResourcesHook(); - final boolean isDynamicModulesMode = Main.isDynamicModulesEnabled(); - ConfigManager.setDynamicModulesMode(isDynamicModulesMode); // call this to ensure the flag is set to false ASAP Router.prepare(false); PrebuiltMethodsDeopter.deoptBootMethods(); // do it once for secondary zygote @@ -26,7 +24,7 @@ public class NormalProxy { Router.installBootstrapHooks(false); // only load modules for secondary zygote Router.loadModulesSafely(true); - Main.closeFilesBeforeForkNative(); + Zygote.closeFilesBeforeFork(); } public static void forkAndSpecializePost(int pid, String appDataDir, String niceName) { @@ -34,7 +32,7 @@ public class NormalProxy { Main.appDataDir = appDataDir; Main.niceName = niceName; Router.prepare(false); - Main.reopenFilesAfterForkNative(); + Zygote.reopenFilesAfterFork(); Router.onEnterChildProcess(); // load modules for each app process on its forked if dynamic modules mode is on Router.loadModulesSafely(false); @@ -45,8 +43,6 @@ public class NormalProxy { long permittedCapabilities, long effectiveCapabilities) { Router.onForkStart(); Router.initResourcesHook(); - final boolean isDynamicModulesMode = Main.isDynamicModulesEnabled(); - ConfigManager.setDynamicModulesMode(isDynamicModulesMode); // set startsSystemServer flag used when loadModules Router.prepare(true); PrebuiltMethodsDeopter.deoptBootMethods(); // do it once for main zygote @@ -58,7 +54,7 @@ public class NormalProxy { // because if not global hooks installed in initZygote might not be // propagated to processes not forked via forkAndSpecialize Router.loadModulesSafely(true); - Main.closeFilesBeforeForkNative(); + Zygote.closeFilesBeforeFork(); } public static void forkSystemServerPost(int pid) { @@ -66,7 +62,7 @@ public class NormalProxy { Main.appDataDir = getDataPathPrefix() + "android"; Main.niceName = "system_server"; Router.prepare(true); - Main.reopenFilesAfterForkNative(); + Zygote.reopenFilesAfterFork(); Router.onEnterChildProcess(); // reload module list if dynamic mode is on Router.loadModulesSafely(false);