diff --git a/app/build.gradle.kts b/app/build.gradle.kts index d721e59..2096e9f 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -20,6 +20,7 @@ android { testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" ndk { + abiFilters.clear() abiFilters.addAll(listOf("arm64-v8a")) } } @@ -54,7 +55,7 @@ android { ndk { println("Full architecture and full compilation.") abiFilters.add("arm64-v8a") - //abiFilters.add("x86_64") // todo: may crash? + abiFilters.add("x86_64") } } create("arm64") { @@ -88,6 +89,11 @@ android { useLegacyPackaging = true excludes += "lib/armeabi/**" excludes += "lib/x86/**" + excludes += "lib/x86_64/libBaiduMapSDK**" + excludes += "lib/x86_64/libc++_shared.so" + excludes += "lib/x86_64/libc++_shared.so" + excludes += "lib/x86_64/liblocSDK8b.so" + excludes += "lib/x86_64/libtiny_magic.so" } resources { excludes += "/META-INF/{AL2.0,LGPL2.1}" diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 231efea..8ecd137 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -44,6 +44,7 @@ android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme" + android:extractNativeLibs="true" tools:targetApi="31"> + var nativeDir = context.applicationInfo.nativeLibraryDir + val soFile = File(nativeDir, "libportal.so") + if (soFile.exists()) { + ShellUtils.executeCommand("cp ${soFile.absolutePath} ${file.absolutePath}") + } else { + Log.e("MockServiceHelper", "Failed to copy portal library: ${soFile.absolutePath}") + return@runCatching + } + } + if (soDir.exists()) { + val originalHash = ShellUtils.executeCommand("head -c 32 ${soFile.absolutePath}") + val newHash = ShellUtils.executeCommand("head -c 32 ${tmpSoFile.absolutePath}") + if (originalHash != newHash) { + ShellUtils.executeCommand("rm ${soFile.absolutePath}") + ShellUtils.executeCommand("mv ${tmpSoFile.absolutePath} ${soFile.absolutePath}") + } + } else if (tmpSoFile.exists()) { + ShellUtils.executeCommand("mv ${tmpSoFile.absolutePath} ${soFile.absolutePath}") + } + }.onFailure { + Log.w("MockServiceHelper", "Failed to copy portal library", it) + } + + ShellUtils.executeCommand("chmod 777 ${soFile.absolutePath}") + + val result = loadLibrary(context.getSystemService(Context.LOCATION_SERVICE) as LocationManager, soFile.absolutePath) + + Log.d("MockServiceHelper", "load portal library result: $result") + } } \ No newline at end of file diff --git a/app/src/main/java/moe/fuqiuluo/portal/ui/mock/MockFragment.kt b/app/src/main/java/moe/fuqiuluo/portal/ui/mock/MockFragment.kt index d0334e8..199cc18 100644 --- a/app/src/main/java/moe/fuqiuluo/portal/ui/mock/MockFragment.kt +++ b/app/src/main/java/moe/fuqiuluo/portal/ui/mock/MockFragment.kt @@ -21,6 +21,7 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import moe.fuqiuluo.portal.R +import moe.fuqiuluo.portal.android.root.ShellUtils import moe.fuqiuluo.portal.android.widget.RockerView import moe.fuqiuluo.portal.android.window.OverlayUtils import moe.fuqiuluo.portal.databinding.FragmentMockBinding @@ -212,6 +213,11 @@ class MockFragment : Fragment() { return } + if (ShellUtils.hasRoot()) { + ShellUtils.setEnforceMode(false) // 关闭SELinux + MockServiceHelper.copyPortalLibrary(requireContext()) + } + lifecycleScope.launch { button.isClickable = false try { diff --git a/xposed/build.gradle.kts b/xposed/build.gradle.kts index c3f3e4b..18968f6 100644 --- a/xposed/build.gradle.kts +++ b/xposed/build.gradle.kts @@ -6,12 +6,21 @@ plugins { android { namespace = "moe.fuqiuluo.xposed" compileSdk = 34 + ndkVersion = "26.1.10909125" defaultConfig { minSdk = 24 testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" consumerProguardFiles("consumer-rules.pro") + externalNativeBuild { + cmake { + cppFlags += "" + } + } + ndk { + abiFilters.addAll(arrayOf("armeabi-v7a", "arm64-v8a", "x86", "x86_64")) + } } buildTypes { @@ -23,6 +32,9 @@ android { ) } } + buildFeatures { + prefab = true + } compileOptions { sourceCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17 @@ -30,14 +42,25 @@ android { kotlinOptions { jvmTarget = "17" } + externalNativeBuild { + cmake { + path = file("src/main/cpp/CMakeLists.txt") + version = "3.22.1" + } + } } dependencies { compileOnly(libs.xposed.api) compileOnly(project(":system-api")) - implementation(project(":nmea")) + // Just Test? + //noinspection GradleDynamicVersion + //implementation("org.lsposed.lsplant:lsplant-standalone:+") + //noinspection UseTomlInstead + implementation("io.github.vvb2060.ndk:dobby:1.2") + testImplementation(libs.junit) androidTestImplementation(libs.androidx.junit) androidTestImplementation(libs.androidx.espresso.core) diff --git a/xposed/src/main/cpp/CMakeLists.txt b/xposed/src/main/cpp/CMakeLists.txt new file mode 100644 index 0000000..b441e49 --- /dev/null +++ b/xposed/src/main/cpp/CMakeLists.txt @@ -0,0 +1,19 @@ +cmake_minimum_required(VERSION 3.22.1) + +project("Portal") + +set(CMAKE_CXX_STANDARD 23) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +# MumuEmulator crash? if enable x86_64 + +#find_package(lsplant REQUIRED CONFIG) +find_package(dobby REQUIRED CONFIG) + +add_library(portal SHARED + main.cpp + elf_util.cpp + sensor_hook.cpp) + +target_link_libraries(portal android log) +target_link_libraries(portal dobby::dobby) +#target_link_libraries(${CMAKE_PROJECT_NAME} lsplant::lsplant) diff --git a/xposed/src/main/cpp/dobby_hook.h b/xposed/src/main/cpp/dobby_hook.h new file mode 100644 index 0000000..e7b88fc --- /dev/null +++ b/xposed/src/main/cpp/dobby_hook.h @@ -0,0 +1,7 @@ +#ifndef PORTAL_DOBBY_HOOK_H +#define PORTAL_DOBBY_HOOK_H + +#include +#include + +#endif //PORTAL_DOBBY_HOOK_H diff --git a/xposed/src/main/cpp/elf_util.cpp b/xposed/src/main/cpp/elf_util.cpp new file mode 100644 index 0000000..51a06f4 --- /dev/null +++ b/xposed/src/main/cpp/elf_util.cpp @@ -0,0 +1,236 @@ +/* + * This file is part of LSPosed. + * + * LSPosed is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * LSPosed is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with LSPosed. If not, see . + * + * Copyright (C) 2019 Swift Gan + * Copyright (C) 2021 LSPosed Contributors + */ +#include +#include +#include +#include +#include "elf_util.h" +#include "logging.h" + +using namespace SandHook; + +ElfImg::ElfImg(std::string_view base_name) : elf(base_name) { + initModuleBase(); + if (!isValid()) { + return; + } + + int fd = open(elf.data(), O_RDONLY); + if (fd < 0) { + LOGE("Failed to open %s", elf.data()); + return; + } + + size = lseek(fd, 0, SEEK_END); + if (size <= 0) { + LOGE("lseek() failed for %s", elf.data()); + } + + header = reinterpret_cast(mmap(nullptr, size, PROT_READ, MAP_SHARED, fd, 0)); + close(fd); + + sectionHeader = offsetOf(header, header->e_shoff); + auto sectionOffset = reinterpret_cast(sectionHeader); + + char *section = offsetOf(header, sectionHeader[header->e_shstrndx].sh_offset); + for (int i = 0; i < header->e_shnum; i++, sectionOffset += header->e_shentsize) { + auto *section_h = (ElfW(Shdr) *) sectionOffset; + char *sectionName = section_h->sh_name + section; + + switch (section_h->sh_type) { + case SHT_DYNSYM: { + if (bias == -4396) { + ElfW(Off) dynsymOffset = section_h->sh_offset; + dynsym = section_h; + dynsymStart = offsetOf(header, dynsymOffset); + } + break; + } + + case SHT_SYMTAB: { + if (strcmp(sectionName, ".symtab") == 0) { + ElfW(Off) symtabSize = section_h->sh_size; + ElfW(Off) symtabOffset = section_h->sh_offset; + symtabCount = symtabSize / section_h->sh_entsize; + symtabStart = offsetOf(header, symtabOffset); + } + break; + } + + case SHT_STRTAB: { + if (bias == -4396) { + ElfW(Off) symstrOffset = section_h->sh_offset; + strtab = section_h; + strtabStart = offsetOf(header, symstrOffset); + } + + if (strcmp(sectionName, ".strtab") == 0) { + symstrOffsetForSymtab = section_h->sh_offset; + } + break; + } + + case SHT_PROGBITS: { + if (strtab == nullptr || dynsym == nullptr) { + break; + } + + if (bias == -4396) { + bias = (off_t) section_h->sh_addr - (off_t) section_h->sh_offset; + } + break; + } + + case SHT_HASH: { + auto *d_un = offsetOf(header, section_h->sh_offset); + nbucket_ = d_un[0]; + bucket_ = d_un + 2; + chain_ = bucket_ + nbucket_; + break; + } + + case SHT_GNU_HASH: { + auto *d_buf = reinterpret_cast(((size_t) header) + section_h->sh_offset); + gnu_nbucket_ = d_buf[0]; + gnu_symndx_ = d_buf[1]; + gnu_bloom_size_ = d_buf[2]; + gnu_shift2_ = d_buf[3]; + gnu_bloom_filter_ = reinterpret_cast(d_buf + 4); + gnu_bucket_ = reinterpret_cast(gnu_bloom_filter_ + gnu_bloom_size_); + gnu_chain_ = gnu_bucket_ + gnu_nbucket_ - gnu_symndx_; + break; + } + } + } +} + +ElfImg::~ElfImg() { + if (header) { + munmap(header, size); + } +} + +ElfW(Addr) ElfImg::elfLookup(std::string_view name, uint32_t hash) const { + if (nbucket_ == 0) { + return 0; + } + + char *strings = (char *) strtabStart; + for (auto n = bucket_[hash % nbucket_]; n != 0; n = chain_[n]) { + auto *sym = dynsymStart + n; + if (name == strings + sym->st_name) { + return sym->st_value; + } + } + return 0; +} + +ElfW(Addr) ElfImg::gnuLookup(std::string_view name, uint32_t hash) const { + static constexpr auto bloomMaskBits = sizeof(ElfW(Addr)) * 8; + if (gnu_nbucket_ == 0 || gnu_bloom_size_ == 0) { + return 0; + } + + auto bloomWord = gnu_bloom_filter_[(hash / bloomMaskBits) % gnu_bloom_size_]; + uintptr_t mask = 0 | (uintptr_t) 1 << (hash % bloomMaskBits) | (uintptr_t) 1 << ((hash >> gnu_shift2_) % bloomMaskBits); + if ((mask & bloomWord) == mask) { + auto symIndex = gnu_bucket_[hash % gnu_nbucket_]; + if (symIndex >= gnu_symndx_) { + char *strings = (char *) strtabStart; + do { + auto *sym = dynsymStart + symIndex; + if (((gnu_chain_[symIndex] ^ hash) >> 1) == 0 + && name == strings + sym->st_name) { + return sym->st_value; + } + } while ((gnu_chain_[symIndex++] & 1) == 0); + } + } + return 0; +} + +ElfW(Addr) ElfImg::linearLookup(std::string_view name) const { + initLinearMap(); + if (auto i = symtabs_.find(name); i != symtabs_.end()) { + return i->second->st_value; + } + return 0; +} + +ElfW(Addr) ElfImg::prefixLookup(std::string_view prefix) const { + initLinearMap(); + if (auto i = symtabs_.lower_bound(prefix); i != symtabs_.end() && i->first.starts_with(prefix)) { + LOGD("Found prefix %s of %s at offset %p in %s at symtab section by linear lookup", prefix.data(), i->first.data(), + reinterpret_cast(i->second->st_value), elf.data()); + return i->second->st_value; + } + return 0; +} + +ElfW(Addr) ElfImg::getSymbolOffset(std::string_view name, uint32_t gnuHash, uint32_t elfHash) const { + if (auto offset = gnuLookup(name, gnuHash); offset > 0) { + LOGD("Found JNI method %s at offset %p in %s at dynsym section by gnu hash", name.data(), reinterpret_cast(offset), elf.data()); + return offset; + } else if (offset = elfLookup(name, elfHash); offset > 0) { + LOGD("Found JNI method %s at offset %p in %s at dynsym section by elf hash", name.data(), reinterpret_cast(offset), elf.data()); + return offset; + } else if (offset = linearLookup(name); offset > 0) { + LOGD("Found JNI method %s at offset %p in %s at symtab section by linear lookup", name.data(), reinterpret_cast(offset), elf.data()); + return offset; + } + return 0; +} + +void ElfImg::initLinearMap() const { + if (symtabs_.empty()) { + if (symtabStart != nullptr && symstrOffsetForSymtab != 0) { + for (ElfW(Off) i = 0; i < symtabCount; i++) { + unsigned int st_type = ELF_ST_TYPE(symtabStart[i].st_info); + const char *st_name = offsetOf(header, symstrOffsetForSymtab + symtabStart[i].st_name); + if ((st_type == STT_FUNC || st_type == STT_OBJECT) && symtabStart[i].st_size) { + symtabs_.emplace(st_name, &symtabStart[i]); + } + } + } + } +} + +void ElfImg::initModuleBase() { + std::string mapsPath = std::string("/proc/self/maps"); + std::ifstream mapsStream(mapsPath); + + if (base) { + return; + } + + if (mapsStream.is_open()) { + std::string line; + while (getline(mapsStream, line)){ + if (line.find(elf) != std::string::npos) { + std::string baseAddress = line.substr(0, line.find('-')); + std::string elfPath = line.substr(line.find('/')); + base = reinterpret_cast(std::stol(baseAddress, nullptr, 16)); + elf = elfPath; + break; + } + } + mapsStream.close(); + } +} \ No newline at end of file diff --git a/xposed/src/main/cpp/elf_util.h b/xposed/src/main/cpp/elf_util.h new file mode 100644 index 0000000..06bb5b3 --- /dev/null +++ b/xposed/src/main/cpp/elf_util.h @@ -0,0 +1,136 @@ +/* + * This file is part of LSPosed. + * + * LSPosed is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * LSPosed is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with LSPosed. If not, see . + * + * Copyright (C) 2019 Swift Gan + * Copyright (C) 2021 LSPosed Contributors + */ +#ifndef SANDHOOK_ELF_UTIL_H +#define SANDHOOK_ELF_UTIL_H + +#include +#include +#include +#include + +#define SHT_GNU_HASH 0x6ffffff6 + +namespace SandHook { + class ElfImg { + public: + ElfImg(std::string_view elf); + + template + requires(std::is_pointer_v) + constexpr const T getSymbolAddress(std::string_view name) const { + auto offset = getSymbolOffset(name, gnuHash(name), elfHash(name)); + if (offset > 0 && base != nullptr) { + return reinterpret_cast(static_cast((uintptr_t) base + offset - bias)); + } + return nullptr; + } + + template + requires(std::is_pointer_v) + constexpr const T getSymbolAddressByPrefix(std::string_view prefix) const { + auto offset = prefixLookup(prefix); + if (offset > 0 && base != nullptr) { + return reinterpret_cast(static_cast((uintptr_t) base + offset - bias)); + } + return nullptr; + } + + template + inline constexpr auto offsetOf(ElfW(Ehdr) *head, ElfW(Off) off) const { + return reinterpret_cast, T, T *>>(reinterpret_cast(head) + off); + } + + constexpr uint32_t elfHash(std::string_view name) const { + uint32_t h = 0, g; + for (unsigned char p: name) { + h = (h << 4) + p; + g = h & 0xf0000000; + h ^= g; + h ^= g >> 24; + } + return h; + } + + constexpr uint32_t gnuHash(std::string_view name) const { + uint32_t h = 5381; + for (unsigned char p: name) { + h += (h << 5) + p; + } + return h; + } + + constexpr inline bool contains(std::string_view a, std::string_view b) const { + return a.find(b) != std::string_view::npos; + } + + bool isValid() const { + return base != nullptr; + } + + const std::string name() const { + return elf; + } + + ~ElfImg(); + private: + ElfW(Addr) getSymbolOffset(std::string_view name, uint32_t gnuHash, uint32_t elfHash) const; + ElfW(Addr) elfLookup(std::string_view name, uint32_t hash) const; + ElfW(Addr) gnuLookup(std::string_view name, uint32_t hash) const; + ElfW(Addr) linearLookup(std::string_view name) const; + ElfW(Addr) prefixLookup(std::string_view prefix) const; + + void initLinearMap() const; + void initModuleBase(); + + std::string elf; + void *base = nullptr; + + off_t size = 0; + off_t bias = -4396; + + ElfW(Ehdr) *header = nullptr; + ElfW(Shdr) *sectionHeader = nullptr; + ElfW(Shdr) *strtab = nullptr; + ElfW(Shdr) *dynsym = nullptr; + + ElfW(Sym) *symtabStart = nullptr; + ElfW(Sym) *dynsymStart = nullptr; + ElfW(Sym) *strtabStart = nullptr; + + ElfW(Off) symtabCount = 0; + ElfW(Off) symstrOffsetForSymtab = 0; + + uint32_t nbucket_{}; + uint32_t *bucket_ = nullptr; + uint32_t *chain_ = nullptr; + + uint32_t gnu_nbucket_{}; + uint32_t gnu_symndx_{}; + uint32_t gnu_bloom_size_; + uint32_t gnu_shift2_; + uintptr_t *gnu_bloom_filter_; + uint32_t *gnu_bucket_; + uint32_t *gnu_chain_; + + mutable std::map symtabs_; + }; +} + +#endif //SANDHOOK_ELF_UTIL_H \ No newline at end of file diff --git a/xposed/src/main/cpp/logging.h b/xposed/src/main/cpp/logging.h new file mode 100644 index 0000000..caad2df --- /dev/null +++ b/xposed/src/main/cpp/logging.h @@ -0,0 +1,29 @@ +#ifndef _LOGGING_H +#define _LOGGING_H + +#include + +#ifndef LOG_TAG +#define LOG_TAG "LSPosed-Bridge" +#endif + +#ifdef LOG_DISABLED +#define LOGD(...) +#define LOGV(...) +#define LOGI(...) +#define LOGW(...) +#define LOGE(...) +#else +#ifndef NDEBUG +#define LOGD(fmt, ...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "[Portal][DEBUG] %s:%d#%s" ": " fmt, __FILE_NAME__, __LINE__, __PRETTY_FUNCTION__ __VA_OPT__(,) __VA_ARGS__) +#define LOGV(fmt, ...) __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, "[Portal][VERBOSE] %s:%d#%s" ": " fmt, __FILE_NAME__, __LINE__, __PRETTY_FUNCTION__ __VA_OPT__(,) __VA_ARGS__) +#else +#define LOGD(...) +#define LOGV(...) +#endif +#define LOGW(fmt, ...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "[Portal][ERROR] %s:%d#%s" ": " fmt, __FILE_NAME__, __LINE__, __PRETTY_FUNCTION__ __VA_OPT__(,) __VA_ARGS__) +#define LOGI(fmt, ...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, "[Portal][INFO] %s:%d#%s" ": " fmt, __FILE_NAME__, __LINE__, __PRETTY_FUNCTION__ __VA_OPT__(,) __VA_ARGS__) +#define LOGE(fmt, ...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "[Portal][ERROR] %s:%d#%s" ": " fmt, __FILE_NAME__, __LINE__, __PRETTY_FUNCTION__ __VA_OPT__(,) __VA_ARGS__) +#endif + +#endif // _LOGGING_H \ No newline at end of file diff --git a/xposed/src/main/cpp/main.cpp b/xposed/src/main/cpp/main.cpp new file mode 100644 index 0000000..55c8c37 --- /dev/null +++ b/xposed/src/main/cpp/main.cpp @@ -0,0 +1,18 @@ + +#include +#include +#include +#include +#include "sensor_hook.h" + +JNIEXPORT jint JNICALL +JNI_OnLoad(JavaVM* vm, void* reserved) { + JNIEnv* env; + if (vm->GetEnv((void**) &env, JNI_VERSION_1_6) != JNI_OK) { + return JNI_ERR; + } + + doSensorHook(); + + return JNI_VERSION_1_6; +} diff --git a/xposed/src/main/cpp/sensor_hook.cpp b/xposed/src/main/cpp/sensor_hook.cpp new file mode 100644 index 0000000..766939e --- /dev/null +++ b/xposed/src/main/cpp/sensor_hook.cpp @@ -0,0 +1,42 @@ +// +// Created by fuqiuluo on 2024/10/15. +// +#include +#include +#include "sensor_hook.h" +#include "logging.h" +#include "elf_util.h" +#include "dobby_hook.h" + +#define LIBSF_PATH "/system/lib64/libsensorservice.so" + +// _ZN7android16SensorEventQueue5writeERKNS_2spINS_7BitTubeEEEPK12ASensorEventm +OriginalSensorEventQueueWriteType OriginalSensorEventQueueWrite = nullptr; + +int64_t SensorEventQueueWrite(void *tube, void *events, int64_t numEvents) { + LOGD("SensorEventQueueWrite called"); + return OriginalSensorEventQueueWrite(tube, events, numEvents); +} + +void doSensorHook() { + SandHook::ElfImg sensorService("/system/lib64/libsensorservice.so"); + + if (!sensorService.isValid()) { + LOGE("failed to load libsensorservice"); + return; + } + + auto sensorWrite = sensorService.getSymbolAddress("_ZN7android16SensorEventQueue5writeERKNS_2spINS_7BitTubeEEEPK12ASensorEventm"); + if (sensorWrite == nullptr) { + sensorWrite = sensorService.getSymbolAddress("_ZN7android16SensorEventQueue5writeERKNS_2spINS_7BitTubeEEEPK12ASensorEventj"); + } + + auto convertToSensorEvent = sensorService.getSymbolAddress("_ZN7android8hardware7sensors4V1_014implementation20convertToSensorEventERKNS2_5EventEP15sensors_event_t"); + + LOGD("Dobby SensorEventQueue::write found at %p", sensorWrite); + LOGD("Dobby convertToSensorEvent found at %p", convertToSensorEvent); + + + //OriginalSensorEventQueueWrite = (OriginalSensorEventQueueWriteType)InlineHooker(sensorWrite, (void *)SensorEventQueueWrite); +} + diff --git a/xposed/src/main/cpp/sensor_hook.h b/xposed/src/main/cpp/sensor_hook.h new file mode 100644 index 0000000..2dc823d --- /dev/null +++ b/xposed/src/main/cpp/sensor_hook.h @@ -0,0 +1,19 @@ +// +// Created by fuqiuluo on 2024/10/15. +// + +#ifndef PORTAL_SENSOR_HOOK_H +#define PORTAL_SENSOR_HOOK_H + +#include "android/sensor.h" + +// ssize_t SensorEventQueue::write(const sp& tube, +// ASensorEvent const* events, size_t numEvents) +typedef int64_t (*OriginalSensorEventQueueWriteType)(void*, void*, int64_t); + +// void convertToSensorEvent(const Event &src, sensors_event_t *dst); +typedef void (*ConvertToSensorEventType)(void*, sensor_event_t*); + +void doSensorHook(); + +#endif //PORTAL_SENSOR_HOOK_H diff --git a/xposed/src/main/java/moe/fuqiuluo/dobby/Dobby.kt b/xposed/src/main/java/moe/fuqiuluo/dobby/Dobby.kt new file mode 100644 index 0000000..08fe9a7 --- /dev/null +++ b/xposed/src/main/java/moe/fuqiuluo/dobby/Dobby.kt @@ -0,0 +1,4 @@ +package moe.fuqiuluo.dobby + +object Dobby { +} \ No newline at end of file diff --git a/xposed/src/main/java/moe/fuqiuluo/xposed/FakeLocation.kt b/xposed/src/main/java/moe/fuqiuluo/xposed/FakeLocation.kt index 61aa580..3e0ad8b 100644 --- a/xposed/src/main/java/moe/fuqiuluo/xposed/FakeLocation.kt +++ b/xposed/src/main/java/moe/fuqiuluo/xposed/FakeLocation.kt @@ -1,8 +1,10 @@ @file:Suppress("LocalVariableName", "PrivateApi", "UNCHECKED_CAST") package moe.fuqiuluo.xposed +import android.os.Build import de.robv.android.xposed.IXposedHookLoadPackage import de.robv.android.xposed.IXposedHookZygoteInit +import de.robv.android.xposed.XC_MethodHook import de.robv.android.xposed.XposedHelpers import de.robv.android.xposed.callbacks.XC_LoadPackage import moe.fuqiuluo.xposed.hooks.LocationManagerHook @@ -13,8 +15,15 @@ import moe.fuqiuluo.xposed.hooks.telephony.miui.MiuiTelephonyManagerHook import moe.fuqiuluo.xposed.hooks.sensor.SystemSensorManagerHook import moe.fuqiuluo.xposed.hooks.telephony.TelephonyHook import moe.fuqiuluo.xposed.hooks.wlan.WlanHook +import moe.fuqiuluo.xposed.utils.BinderUtils import moe.fuqiuluo.xposed.utils.FakeLoc import moe.fuqiuluo.xposed.utils.Logger +import moe.fuqiuluo.xposed.utils.beforeHook +import moe.fuqiuluo.xposed.utils.hookAllMethods +import moe.fuqiuluo.xposed.utils.hookMethodAfter +import java.io.File +import java.util.concurrent.atomic.AtomicBoolean +import kotlin.concurrent.thread class FakeLocation: IXposedHookLoadPackage, IXposedHookZygoteInit { private lateinit var cServiceManager: Class<*> // android.os.ServiceManager @@ -97,7 +106,8 @@ class FakeLocation: IXposedHookLoadPackage, IXposedHookZygoteInit { private fun startFakeLocHook(classLoader: ClassLoader) { cServiceManager = XposedHelpers.findClass("android.os.ServiceManager", classLoader) - val cLocationManager = XposedHelpers.findClass("android.location.LocationManager", classLoader) + val cLocationManager = + XposedHelpers.findClass("android.location.LocationManager", classLoader) XposedHelpers.findClassIfExists("com.android.server.TelephonyRegistry", classLoader)?.let { TelephonyHook.hookTelephonyRegistry(it) @@ -105,6 +115,7 @@ class FakeLocation: IXposedHookLoadPackage, IXposedHookZygoteInit { LocationServiceHook(classLoader) LocationManagerHook(cLocationManager) // intrusive hooks + } // kotlin.runCatching { // val hooks = mutableListOf() @@ -174,7 +185,6 @@ class FakeLocation: IXposedHookLoadPackage, IXposedHookZygoteInit { // }.onFailure { // XposedBridge.log("[Portal] Failed to find SystemServer: ${it.stackTraceToString()}") // } - } // private fun systemServerInitOver() { // XposedBridge.log("[Portal] SystemServer init over") diff --git a/xposed/src/main/java/moe/fuqiuluo/xposed/RemoteCommandHandler.kt b/xposed/src/main/java/moe/fuqiuluo/xposed/RemoteCommandHandler.kt index f1caad0..ecdaa77 100644 --- a/xposed/src/main/java/moe/fuqiuluo/xposed/RemoteCommandHandler.kt +++ b/xposed/src/main/java/moe/fuqiuluo/xposed/RemoteCommandHandler.kt @@ -1,19 +1,23 @@ package moe.fuqiuluo.xposed +import android.annotation.SuppressLint +import android.os.Build import android.os.Bundle import android.os.IBinder import android.os.Parcel import moe.fuqiuluo.xposed.utils.FakeLoc import moe.fuqiuluo.xposed.utils.BinderUtils import moe.fuqiuluo.xposed.utils.Logger +import java.io.File import java.util.Collections import kotlin.random.Random object RemoteCommandHandler { private var proxyBinders = Collections.synchronizedList(arrayListOf()) - private val needProxyCmd = arrayOf("start", "stop", "set_speed_amp", "set_altitude", "update_location", "set_proxy") + private val needProxyCmd = arrayOf("start", "stop", "set_speed_amp", "set_altitude", "update_location") internal val randomKey by lazy { "portal_" + Random.nextDouble() } + @SuppressLint("UnsafeDynamicallyLoadedCode") fun handleInstruction(command: String, rely: Bundle, locationListeners: Map>): Boolean { // Exchange key -> returns a random key -> is used to verify that it is the PortalManager if (command == "exchange_key") { @@ -184,10 +188,33 @@ object RemoteCommandHandler { // } return true } + "load_library" -> { + val path = rely.getString("path") ?: return false + + runCatching { + System.load(path) + }.onSuccess { + rely.putString("result", "success") + }.onFailure { + rely.putString("result", it.stackTraceToString()) + } + + return true + } else -> return false } } + private var hasHookSensor = false + + private fun tryHookSensor(classLoader: ClassLoader = FakeLoc::class.java.classLoader!!) { + if (hasHookSensor || proxyBinders.isNullOrEmpty()) return + + + + hasHookSensor = true + } + // private fun generateLocation(): Location { // val (location, realLocation) = if (FakeLocationConfig.lastLocation != null) { // (FakeLocationConfig.lastLocation!! to true) diff --git a/xposed/src/main/java/moe/fuqiuluo/xposed/hooks/sensor/SystemSensorManagerHook.kt b/xposed/src/main/java/moe/fuqiuluo/xposed/hooks/sensor/SystemSensorManagerHook.kt index 1cf912e..01cbee1 100644 --- a/xposed/src/main/java/moe/fuqiuluo/xposed/hooks/sensor/SystemSensorManagerHook.kt +++ b/xposed/src/main/java/moe/fuqiuluo/xposed/hooks/sensor/SystemSensorManagerHook.kt @@ -32,6 +32,7 @@ object SystemSensorManagerHook { val cSystemSensorManagerQueue = XposedHelpers.findClassIfExists("android.hardware.SystemSensorManager\$SensorEventQueue", classLoader) ?: return + } private fun hookSystemSensorManager(classLoader: ClassLoader) { @@ -49,13 +50,11 @@ object SystemSensorManagerHook { Logger.debug("RegisterListenerImpl: $listener, sensor: ${args[1]}") } - val sensor = args[1] as Sensor + val sensor = args[1] as? Sensor ?: return@beforeHook listenerMap[listener] = sensor.type listener.javaClass.onceHookAllMethod("onSensorChanged", beforeHook { - if (FakeLoc.enable) { - result = null - } + }) } cSystemSensorManager.declaredMethods.filter { diff --git a/xposed/src/main/java/moe/fuqiuluo/xposed/hooks/wlan/WlanHook.kt b/xposed/src/main/java/moe/fuqiuluo/xposed/hooks/wlan/WlanHook.kt index c68dec4..14db2f7 100644 --- a/xposed/src/main/java/moe/fuqiuluo/xposed/hooks/wlan/WlanHook.kt +++ b/xposed/src/main/java/moe/fuqiuluo/xposed/hooks/wlan/WlanHook.kt @@ -13,6 +13,7 @@ import moe.fuqiuluo.xposed.utils.FakeLoc import moe.fuqiuluo.xposed.utils.Logger import moe.fuqiuluo.xposed.utils.afterHook import moe.fuqiuluo.xposed.utils.hookAllMethods +import moe.fuqiuluo.xposed.utils.hookMethodAfter import moe.fuqiuluo.xposed.utils.toClass object WlanHook { diff --git a/xposed/src/main/java/moe/fuqiuluo/xposed/utils/FakeLoc.kt b/xposed/src/main/java/moe/fuqiuluo/xposed/utils/FakeLoc.kt index 03ea219..b4e4c93 100644 --- a/xposed/src/main/java/moe/fuqiuluo/xposed/utils/FakeLoc.kt +++ b/xposed/src/main/java/moe/fuqiuluo/xposed/utils/FakeLoc.kt @@ -19,7 +19,7 @@ object FakeLoc { /** * 是否允许打印调试日志 */ - var enableDebugLog = true + var enableDebugLog = false /** * 模拟定位服务开关 diff --git a/xposed/src/main/java/moe/fuqiuluo/xposed/utils/Logger.kt b/xposed/src/main/java/moe/fuqiuluo/xposed/utils/Logger.kt index 288ace3..b958fa3 100644 --- a/xposed/src/main/java/moe/fuqiuluo/xposed/utils/Logger.kt +++ b/xposed/src/main/java/moe/fuqiuluo/xposed/utils/Logger.kt @@ -4,7 +4,7 @@ import de.robv.android.xposed.XposedBridge object Logger { private fun isEnableLog(): Boolean { - return FakeLoc.enable + return FakeLoc.enableLog } fun info(msg: String) {