Merge remote-tracking branch 'origin/main'
# Conflicts: # app/src/main/java/me/singleneuron/hook/AppCenterHook.kt # gradle/wrapper/gradle-wrapper.properties
This commit is contained in:
2
.github/workflows/pr_ci.yml
vendored
2
.github/workflows/pr_ci.yml
vendored
@@ -60,7 +60,7 @@ jobs:
|
||||
echo "sdk.dir=${ANDROID_HOME}" > local.properties
|
||||
|
||||
- name: Setup Gradle
|
||||
uses: gradle/gradle-build-action@v3.4.1
|
||||
uses: gradle/gradle-build-action@v3.5.0
|
||||
|
||||
- name: Build with Gradle
|
||||
run: |
|
||||
|
||||
2
.github/workflows/push_ci.yml
vendored
2
.github/workflows/push_ci.yml
vendored
@@ -60,7 +60,7 @@ jobs:
|
||||
restore-keys: native-cache-
|
||||
|
||||
- name: Setup Gradle
|
||||
uses: gradle/gradle-build-action@v3.4.1
|
||||
uses: gradle/gradle-build-action@v3.5.0
|
||||
|
||||
- name: Build with Gradle
|
||||
run: |
|
||||
|
||||
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -37,3 +37,6 @@
|
||||
path = libs/LSPlant
|
||||
url = https://github.com/LSPosed/LSPlant
|
||||
shallow = true
|
||||
[submodule "libs/libxposed/api"]
|
||||
path = libs/libxposed/api
|
||||
url = https://github.com/libxposed/api
|
||||
|
||||
@@ -43,10 +43,17 @@ plugins {
|
||||
id("build-logic.android.application")
|
||||
alias(libs.plugins.changelog)
|
||||
alias(libs.plugins.ksp)
|
||||
alias(libs.plugins.protobuf)
|
||||
alias(libs.plugins.serialization)
|
||||
alias(libs.plugins.aboutlibraries)
|
||||
}
|
||||
|
||||
// ------ buildscript config ------
|
||||
|
||||
val buildAllAbiForDebug = false
|
||||
val isNewXposedApiEnabled = true
|
||||
val fullNativeDebugMode = false
|
||||
|
||||
val currentBuildUuid = UUID.randomUUID().toString()
|
||||
println("Current build ID is $currentBuildUuid")
|
||||
|
||||
@@ -58,8 +65,6 @@ if (ccacheExecutablePath != null) {
|
||||
println("No ccache found.")
|
||||
}
|
||||
|
||||
val fullNativeDebugMode = false
|
||||
|
||||
fun getSignatureKeyDigest(signConfig: SigningConfig?): String? {
|
||||
var key1: String? = if (signConfig != null && signConfig.storeFile != null) {
|
||||
// extract certificate digest
|
||||
@@ -173,10 +178,12 @@ android {
|
||||
if (fullNativeDebugMode) {
|
||||
isJniDebuggable = true
|
||||
} else {
|
||||
if (!buildAllAbiForDebug) {
|
||||
@Suppress("ChromeOsAbiSupport")
|
||||
abiFilters += arrayOf("arm64-v8a", "armeabi-v7a")
|
||||
}
|
||||
}
|
||||
}
|
||||
isCrunchPngs = false
|
||||
proguardFiles("proguard-rules.pro")
|
||||
var debugFlags = arrayOf<String>(
|
||||
@@ -205,12 +212,17 @@ android {
|
||||
)
|
||||
}
|
||||
packaging {
|
||||
resources.excludes.addAll(arrayOf(
|
||||
"META-INF/**",
|
||||
// libxposed API uses META-INF/xposed
|
||||
resources.excludes.addAll(
|
||||
arrayOf(
|
||||
"kotlin/**",
|
||||
"**.bin",
|
||||
"kotlin-tooling-metadata.json"
|
||||
))
|
||||
)
|
||||
)
|
||||
if (!isNewXposedApiEnabled) {
|
||||
resources.excludes.add("META-INF/xposed/**")
|
||||
}
|
||||
}
|
||||
|
||||
buildFeatures {
|
||||
@@ -247,15 +259,24 @@ kotlin {
|
||||
sourceSets.configureEach {
|
||||
kotlin.srcDir("$buildDir/generated/ksp/$name/kotlin/")
|
||||
}
|
||||
sourceSets.main {
|
||||
kotlin.srcDir(File(rootDir, "libs/ezxhelper/src/main/java"))
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// loader
|
||||
compileOnly(projects.loader.hookapi)
|
||||
runtimeOnly(projects.loader.sbl)
|
||||
implementation(projects.loader.startup)
|
||||
// ksp
|
||||
ksp(projects.libs.ksp)
|
||||
// host stub
|
||||
compileOnly(projects.libs.stub)
|
||||
// libraries
|
||||
implementation(projects.libs.mmkv)
|
||||
implementation(projects.libs.dexkit)
|
||||
implementation(projects.libs.xView)
|
||||
ksp(projects.libs.ksp)
|
||||
compileOnly(libs.xposed)
|
||||
implementation(libs.androidx.core.ktx)
|
||||
implementation(libs.androidx.constraintlayout)
|
||||
implementation(libs.androidx.browser)
|
||||
@@ -269,7 +290,6 @@ dependencies {
|
||||
implementation(libs.colorpicker)
|
||||
implementation(libs.material.dialogs.core)
|
||||
implementation(libs.material.dialogs.input)
|
||||
implementation(libs.ezXHelper)
|
||||
// festival title
|
||||
implementation(libs.confetti)
|
||||
implementation(libs.weatherView)
|
||||
@@ -281,7 +301,7 @@ dependencies {
|
||||
implementation(libs.byte.buddy)
|
||||
implementation(libs.dalvik.dx)
|
||||
ksp(libs.sealedEnum.ksp)
|
||||
implementation(libs.google.protobuf)
|
||||
implementation(libs.google.protobuf.java)
|
||||
}
|
||||
|
||||
val adb: String = androidComponents.sdkComponents.adb.get().asFile.absolutePath
|
||||
@@ -474,3 +494,21 @@ val generateEulaAndPrivacy by tasks.registering {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// see https://github.com/google/protobuf-gradle-plugin/issues/518
|
||||
protobuf {
|
||||
protoc {
|
||||
artifact = libs.google.protobuf.protoc.get().toString()
|
||||
}
|
||||
plugins {
|
||||
generateProtoTasks {
|
||||
all().forEach {
|
||||
it.builtins {
|
||||
create("java") {
|
||||
option("lite")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
5
app/proguard-rules.pro
vendored
5
app/proguard-rules.pro
vendored
@@ -75,6 +75,11 @@
|
||||
-dontwarn com.sun.jna.**
|
||||
-dontwarn edu.umd.cs.findbugs.annotations.**
|
||||
-dontwarn java.lang.instrument.**
|
||||
|
||||
# Xposed API
|
||||
-dontwarn de.robv.android.xposed.**
|
||||
-dontwarn io.github.libxposed.api.**
|
||||
|
||||
-keep class com.android.dx.** {
|
||||
*;
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
package hook
|
||||
|
||||
import android.content.Intent
|
||||
import de.robv.android.xposed.XC_MethodHook
|
||||
import io.github.qauxv.util.xpcompat.XC_MethodHook
|
||||
import io.github.qauxv.base.annotation.FunctionHookEntry
|
||||
import io.github.qauxv.base.annotation.UiItemAgentEntry
|
||||
import io.github.qauxv.config.ConfigManager
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
-->
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<permission
|
||||
android:name="${applicationId}.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION"
|
||||
android:protectionLevel="signature"
|
||||
@@ -36,6 +37,7 @@
|
||||
android:multiArch="true"
|
||||
android:name=".util.hookstatus.ModuleAppImpl"
|
||||
android:resizeableActivity="true"
|
||||
android:description="@string/xposeddescription"
|
||||
android:theme="@style/AppTheme.Def"
|
||||
tools:ignore="AllowBackup,GoogleAppIndexingWarning,UnusedAttribute">
|
||||
<activity
|
||||
|
||||
@@ -1 +1 @@
|
||||
io.github.qauxv.startup.HookEntry
|
||||
io.github.qauxv.loader.sbl.xp51.Xp51HookEntry
|
||||
|
||||
@@ -63,6 +63,7 @@ add_library(qauxv SHARED
|
||||
qauxv_core/SilkCodec.cc
|
||||
qauxv_core/HostInfo.cc
|
||||
qauxv_core/NativeCoreBridge.cc
|
||||
qauxv_core/linker_utils.cc
|
||||
|
||||
utils/shared_memory.cpp
|
||||
utils/auto_close_fd.cc
|
||||
|
||||
@@ -75,7 +75,7 @@ namespace teble::v2sign {
|
||||
const uint32_t v2Id = 0x7109871a;
|
||||
|
||||
std::string getModulePath(JNIEnv *env) {
|
||||
jclass cMainHook = env->FindClass("io/github/qauxv/startup/HookEntry");
|
||||
jclass cMainHook = env->FindClass("io/github/qauxv/core/MainHook");
|
||||
jclass cClass = env->FindClass("java/lang/Class");
|
||||
jmethodID mGetClassLoader = env->GetMethodID(cClass, "getClassLoader",
|
||||
"()Ljava/lang/ClassLoader;");
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include <optional>
|
||||
#include <sys/mman.h>
|
||||
#include <ucontext.h>
|
||||
#include <dlfcn.h>
|
||||
#include <type_traits>
|
||||
//#include <chrono>
|
||||
#include <unordered_map>
|
||||
@@ -32,7 +33,9 @@
|
||||
#include "utils/AobScanUtils.h"
|
||||
#include "utils/MemoryUtils.h"
|
||||
#include "utils/arch_utils.h"
|
||||
#include "utils/endian.h"
|
||||
#include "qauxv_core/natives_utils.h"
|
||||
#include "qauxv_core/linker_utils.h"
|
||||
|
||||
#ifndef STACK_GUARD
|
||||
// for debug purpose only
|
||||
@@ -52,251 +55,10 @@ jclass klassRevokeMsgHook = nullptr;
|
||||
jobject gInstanceRevokeMsgHook = nullptr;
|
||||
jmethodID handleRecallSysMsgFromNtKernel = nullptr;
|
||||
|
||||
uintptr_t gOffsetGetDecoderSp = 0;
|
||||
|
||||
uintptr_t gOffsetForTmpRev5048 = 0;
|
||||
|
||||
NOINLINE
|
||||
uint64_t ThunkGetInt64Property(const void* thiz, int property) {
|
||||
// vtable
|
||||
// 4160. [[this+8]+0x58]
|
||||
void* thisp8 = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(thiz) + 8);
|
||||
uintptr_t vtable = *reinterpret_cast<uintptr_t*>(thisp8);
|
||||
void* func = *reinterpret_cast<void**>(vtable + 0x58);
|
||||
return reinterpret_cast<decltype(ThunkGetInt64Property)*>(func)(thisp8, property);
|
||||
}
|
||||
|
||||
NOINLINE
|
||||
uint32_t ThunkGetInt32Property(const void* thiz, int property) {
|
||||
// vtable
|
||||
// 4160. [[this+8]+0x38]
|
||||
void* thisp8 = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(thiz) + 8);
|
||||
uintptr_t vtable = *reinterpret_cast<uintptr_t*>(thisp8);
|
||||
void* func = *reinterpret_cast<void**>(vtable + 0x38);
|
||||
return reinterpret_cast<decltype(ThunkGetInt32Property)*>(func)(thisp8, property);
|
||||
}
|
||||
|
||||
NOINLINE
|
||||
std::string ThunkGetStringProperty(void* thiz, int property) {
|
||||
// vtable
|
||||
// 4160. [[this+8]+0x70]
|
||||
void* thisp8 = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(thiz) + 8);
|
||||
uintptr_t vtable = *reinterpret_cast<uintptr_t*>(thisp8);
|
||||
void* func = *reinterpret_cast<void**>(vtable + 0x70);
|
||||
return reinterpret_cast<decltype(ThunkGetStringProperty)*>(func)(thisp8, property);
|
||||
}
|
||||
|
||||
template<typename ReturnType, uintptr_t vtableOffset, uintptr_t thizOffset, typename... ArgTypes>
|
||||
requires((std::is_same_v<ReturnType, void> || std::is_integral_v<ReturnType> || std::is_pointer_v<ReturnType>)
|
||||
&& ((std::is_integral_v<ArgTypes> || std::is_pointer_v<ArgTypes>) && ...))
|
||||
NOINLINE
|
||||
ReturnType vcall(void* thiz, ArgTypes... args) {
|
||||
// vtable
|
||||
// [[this+thizOff]+offsetVT]
|
||||
void* thisp8 = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(thiz) + thizOffset);
|
||||
uintptr_t vtable = *reinterpret_cast<uintptr_t*>(thisp8);
|
||||
void* func = *reinterpret_cast<void**>(vtable + vtableOffset);
|
||||
if constexpr (std::is_same_v<ReturnType, void>) {
|
||||
reinterpret_cast<ReturnType(*)(void*, ArgTypes...)>(func)(thisp8, args...);
|
||||
return;
|
||||
} else {
|
||||
return reinterpret_cast<ReturnType(*)(void*, ArgTypes...)>(func)(thisp8, args...);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename... ArgTypes>
|
||||
requires(((std::is_integral_v<ArgTypes> || std::is_pointer_v<ArgTypes>) && ...))
|
||||
NOINLINE
|
||||
void vcall_x8_v2(void* thiz, uintptr_t vtableOffset, uintptr_t thizOffset, void* x8, ArgTypes... args) {
|
||||
// vtable
|
||||
// [[this+thizOff]+offsetVT]
|
||||
void* thisp8 = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(thiz) + thizOffset);
|
||||
uintptr_t vtable = *reinterpret_cast<uintptr_t*>(thisp8);
|
||||
void* func = *reinterpret_cast<void**>(vtable + vtableOffset);
|
||||
static_assert(sizeof...(args) <= 3);
|
||||
std::array<void*, 3> argArray = {reinterpret_cast<void*>(args)...};
|
||||
call_func_with_x8(func, x8, thisp8, argArray[0], argArray[1], argArray[2]);
|
||||
}
|
||||
|
||||
// helper for uintptr_t as this
|
||||
template<typename ReturnType, uintptr_t vtableOffset, uintptr_t thizOffset, typename... ArgTypes>
|
||||
requires((std::is_same_v<ReturnType, void> || std::is_integral_v<ReturnType> || std::is_pointer_v<ReturnType>)
|
||||
&& ((std::is_integral_v<ArgTypes> || std::is_pointer_v<ArgTypes>) && ...))
|
||||
static inline ReturnType vcall(uintptr_t thiz, ArgTypes... args) {
|
||||
if constexpr (std::is_same_v<ReturnType, void>) {
|
||||
vcall<vtableOffset, thizOffset, ArgTypes...>(reinterpret_cast<void*>(thiz), args...);
|
||||
return;
|
||||
} else {
|
||||
return vcall<vtableOffset, thizOffset, ArgTypes...>(reinterpret_cast<void*>(thiz), args...);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename... ArgTypes> requires((std::is_integral_v<ArgTypes> || std::is_pointer_v<ArgTypes>) && ...)
|
||||
static inline void vcall_x8_v2(uintptr_t thiz, uintptr_t vtableOffset, uintptr_t thizOffset, void* x8, ArgTypes... args) {
|
||||
vcall_x8_v2<ArgTypes...>(reinterpret_cast<void*>(thiz), vtableOffset, thizOffset, x8, args...);
|
||||
}
|
||||
|
||||
//void ThunkCallAPI(void* x0, uintptr_t api_caller_id, int x2, int x3, int& x4, std::string& x5) {
|
||||
// // 4160. 0x00cc0750
|
||||
// // "CallAPI"
|
||||
// // "!!! RegisterAPIHandler Error crash: api_caller_id is empty can not use You can use GlobalAPI or set other value to api_caller_id !!!"
|
||||
// auto func = reinterpret_cast<decltype(ThunkCallAPI)*>((uintptr_t) gLibkernelBaseAddress + 0x00cc0750);
|
||||
// func(x0, api_caller_id, x2, x3, x4, x5);
|
||||
//}
|
||||
|
||||
class RevokeMsgInfoAccess {
|
||||
public:
|
||||
|
||||
struct UnknownObjectStub16 {
|
||||
void* _unk0_8;
|
||||
void* _unk8_8;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
|
||||
void NotifyRecallSysMsgEvent(int chatType, const std::string& peerUid, const std::string& recallOpUid, const std::string& msgAuthorUid,
|
||||
const std::string& toUid, uint64_t random64, uint64_t timeSeconds, uint64_t msgUid, uint64_t msgSeq, uint32_t msgClientSeq) {
|
||||
JavaVM* vm = HostInfo::GetJavaVM();
|
||||
if (vm == nullptr) {
|
||||
LOGE("NotifyRecallSysMsgEvent fatal vm == null");
|
||||
return;
|
||||
}
|
||||
if (klassRevokeMsgHook == nullptr) {
|
||||
LOGE("NotifyRecallSysMsgEvent fatal klassRevokeMsgHook == null");
|
||||
return;
|
||||
}
|
||||
// check if current thread is attached to jvm
|
||||
JNIEnv* env = nullptr;
|
||||
bool isAttachedManually = false;
|
||||
jint err = vm->GetEnv((void**) &env, JNI_VERSION_1_6);
|
||||
if (err == JNI_EDETACHED) {
|
||||
if (vm->AttachCurrentThread(&env, nullptr) != JNI_OK) {
|
||||
LOGE("NotifyRecallSysMsgEvent fatal AttachCurrentThread failed");
|
||||
return;
|
||||
}
|
||||
isAttachedManually = true;
|
||||
} else if (env == nullptr) {
|
||||
LOGE("NotifyRecallSysMsgEvent fatal GetEnv failed, err = {}", err);
|
||||
return;
|
||||
}
|
||||
// call java method
|
||||
env->CallStaticVoidMethod(klassRevokeMsgHook, handleRecallSysMsgFromNtKernel,
|
||||
jint(chatType), env->NewStringUTF(peerUid.c_str()), env->NewStringUTF(recallOpUid.c_str()),
|
||||
env->NewStringUTF(msgAuthorUid.c_str()), env->NewStringUTF(toUid.c_str()),
|
||||
jlong(random64), jlong(timeSeconds), jlong(msgUid), jlong(msgSeq), jint(msgClientSeq));
|
||||
// check if exception occurred
|
||||
if (env->ExceptionCheck()) {
|
||||
env->ExceptionDescribe();
|
||||
env->ExceptionClear();
|
||||
}
|
||||
// detach thread if attached manually
|
||||
if (isAttachedManually) {
|
||||
vm->DetachCurrentThread();
|
||||
}
|
||||
}
|
||||
|
||||
void NotifyRecallMsgEventForC2c(const std::string& fromUid, const std::string& toUid,
|
||||
uint64_t random64, uint64_t timeSeconds,
|
||||
uint64_t msgUid, uint64_t msgSeq, uint32_t msgClientSeq) {
|
||||
NotifyRecallSysMsgEvent(1, fromUid, fromUid, fromUid, toUid, random64, timeSeconds, msgUid, msgSeq, msgClientSeq);
|
||||
}
|
||||
|
||||
void NotifyRecallMsgEventForGroup(const std::string& peerUid, const std::string& recallOpUid, const std::string& msgAuthorUid,
|
||||
uint64_t random64, uint64_t timeSeconds, uint64_t msgSeq) {
|
||||
NotifyRecallSysMsgEvent(2, peerUid, recallOpUid, msgAuthorUid, peerUid, random64, timeSeconds, 0, msgSeq, 0);
|
||||
}
|
||||
|
||||
|
||||
void (* sOriginHandleGroupRecallSysMsgCallback)(void*, void*, void*) = nullptr;
|
||||
|
||||
void HandleGroupRecallSysMsgCallback([[maybe_unused]] void* x0, void* x1, [[maybe_unused]] void* x2, [[maybe_unused]] int x3) {
|
||||
// LOGD("HandleGroupRecallSysMsgCallback start p1={:p}, p2={:p}, p3={:p}", x0, x1, x2);
|
||||
// we can still do it... hitherto p3 == null... we need to decode the message manually...
|
||||
uintptr_t base = (uintptr_t) gLibkernelBaseAddress;
|
||||
void* pVar1 = *(void**) x1;
|
||||
if ((vcall<int, 0x118, 8, int>(pVar1, 3) & 1) == 0) {
|
||||
LOGE("msg_recall: HandleRecallSysMsg: on recall group sys msg! hasn't msg_common::Msg::kBody");
|
||||
return;
|
||||
}
|
||||
void* pVar2 = *(void**) x1;
|
||||
STACK_GUARD;
|
||||
std::array<uint8_t, 0x100> objVar1 = {}; // actual size unknown, maybe 0x90
|
||||
STACK_GUARD;
|
||||
vcall_x8_v2<int>(pVar2, gOffsetForTmpRev5048, 0, &objVar1, 3);
|
||||
auto pVar3 = *(void**) (objVar1.data());
|
||||
if (pVar3 == nullptr) {
|
||||
LOGE("msg_recall: HandleRecallSysMsg: on recall group sys msg! msg_common::Msg::kBody = null");
|
||||
return;
|
||||
}
|
||||
if ((vcall<int, 0x118, 8, int>(pVar3, 2) & 1) == 0) {
|
||||
LOGE("msg_recall: HandleRecallSysMsg: on recall group sys msg! hasn't im_msg_body::MsgBody::kBytesMsgContent");
|
||||
return;
|
||||
}
|
||||
STACK_GUARD;
|
||||
std::vector<uint8_t> msgContentBytes;
|
||||
STACK_GUARD;
|
||||
vcall_x8_v2<int>(pVar3, 0x78, 8, &msgContentBytes, 2);
|
||||
if (msgContentBytes.size() < 8) {
|
||||
LOGE("msg_recall: HandleRecallSysMsg: on recall group sys msg! im_msg_body::MsgBody::kBytesMsgContent is error");
|
||||
return;
|
||||
}
|
||||
std::vector<uint8_t> content = {msgContentBytes.begin() + 7, msgContentBytes.end()};
|
||||
STACK_GUARD;
|
||||
std::array<void*, 2> objVar3 = {}; // actual size 0x10, maybe shared_ptr, but we don't have dtor
|
||||
STACK_GUARD;
|
||||
call_func_with_x8((void*) (base + gOffsetGetDecoderSp), &objVar3, 0, 0, 0, 0);
|
||||
void* notifyMsgBody = objVar3[0];
|
||||
if ((vcall<int, 0x100, 8, std::vector<uint8_t>*>(notifyMsgBody, &content) & 1) == 0) {
|
||||
LOGE("on recall group sys msg! decode kBytesMsgContent fail");
|
||||
return;
|
||||
}
|
||||
uint64_t groupCode = vcall<uint64_t, 0x58, 8, int>(notifyMsgBody, 4);
|
||||
if (groupCode == 0) {
|
||||
LOGE("on recall group sys msg! group code is 0");
|
||||
return;
|
||||
}
|
||||
uint64_t opType = vcall<uint64_t, 0x58, 8, int>(notifyMsgBody, 1);
|
||||
if (opType != 7) {
|
||||
LOGW("HandleGroupRecallSysMsgCallback: on recall group sys msg! no Prompt_MsgRecallReminder op_type:{}", opType);
|
||||
return;
|
||||
}
|
||||
if ((vcall<uint64_t, 0x118, 8, int>(notifyMsgBody, 0xb) & 1) == 0) {
|
||||
LOGE("HandleGroupRecallSysMsgCallback: on recall group sys msg! no NotifyMsgBody::opt_msg_recall");
|
||||
return;
|
||||
}
|
||||
STACK_GUARD;
|
||||
std::array<void*, 2> optMsgRecall = {}; // actual size 0x10, maybe shared_ptr, but we don't have dtor
|
||||
STACK_GUARD;
|
||||
vcall_x8_v2<int>(notifyMsgBody, gOffsetForTmpRev5048, 0, &optMsgRecall, 0xb);
|
||||
if (optMsgRecall[0] == nullptr) {
|
||||
LOGE("HandleGroupRecallSysMsgCallback: on recall group sys msg! NotifyMsgBody::opt_msg_recall == null");
|
||||
return;
|
||||
}
|
||||
if ((vcall<int, 0x118, 8, int>(optMsgRecall[0], 3) & 1) == 0) {
|
||||
LOGE("HandleGroupRecallSysMsgCallback: on recall group sys msg! on recall group sys msg! no msg_infos");
|
||||
return;
|
||||
}
|
||||
std::array<void*, 3> vectorResultStub = {nullptr, nullptr, nullptr};
|
||||
vcall_x8_v2<int>(optMsgRecall[0], 0xf0, 8, &vectorResultStub, 3);
|
||||
std::string recallOpUid = ThunkGetStringProperty(optMsgRecall[0], 1);
|
||||
const auto& msgInfoList = *reinterpret_cast<const std::vector<RevokeMsgInfoAccess::UnknownObjectStub16>*>(&vectorResultStub);
|
||||
if (msgInfoList.empty()) {
|
||||
LOGE("HandleGroupRecallSysMsgCallback: on recall group sys msg! no any msg info");
|
||||
return;
|
||||
}
|
||||
std::string peerUid = fmt::format("{}", groupCode);
|
||||
for (const auto& msgInfo: msgInfoList) {
|
||||
uint32_t msgSeq = ThunkGetInt32Property(msgInfo._unk0_8, 1);
|
||||
uint32_t random = ThunkGetInt32Property(msgInfo._unk0_8, 3);
|
||||
uint64_t time = ThunkGetInt64Property(msgInfo._unk0_8, 2);
|
||||
std::string msgAuthorUid = ThunkGetStringProperty(msgInfo._unk0_8, 6);
|
||||
|
||||
// Unfortunately, I didn't find a way to find the origMsgSenderUid.
|
||||
// The only thing we can do is to get message by msgSeq, and get senderUid from it, iff we have the message.
|
||||
|
||||
NotifyRecallMsgEventForGroup(peerUid, recallOpUid, msgAuthorUid, random, time, msgSeq);
|
||||
}
|
||||
}
|
||||
|
||||
void (* sOriginHandleC2cRecallSysMsgCallback)(void*, void*, void*) = nullptr;
|
||||
@@ -306,134 +68,43 @@ void HandleC2cRecallSysMsgCallback([[maybe_unused]] void* p1, [[maybe_unused]] v
|
||||
LOGE("HandleC2cGroupSysMsgCallback BUG !!! *p3 = null, this should not happen!!!");
|
||||
return;
|
||||
}
|
||||
|
||||
std::array<void*, 3> vectorResultStub = {nullptr, nullptr, nullptr};
|
||||
|
||||
void* v1 = *(void**) p3;
|
||||
void** v2 = (void**) ((uintptr_t) v1 + 8);
|
||||
|
||||
// 4160. 0xf0
|
||||
auto func = reinterpret_cast<std::array<void*, 3>(*)(void*, int)>(*(void**) ((uintptr_t) *v2 + 0xf0));
|
||||
// XXX: memory leak, no dtor available
|
||||
vectorResultStub = func(v2, 1);
|
||||
|
||||
static_assert(sizeof(std::vector<int>) == sizeof(std::array<void*, 3>), "libcxx vector size not match");
|
||||
const auto& objects = *reinterpret_cast<const std::vector<RevokeMsgInfoAccess::UnknownObjectStub16>*>(&vectorResultStub);
|
||||
|
||||
if (!objects.empty()) {
|
||||
|
||||
// void* x0 = nullptr;
|
||||
// uintptr_t x1 = 0;
|
||||
// {
|
||||
// uint8_t* param_1 = static_cast<uint8_t*>(p1) + 8;
|
||||
// uint8_t* pbVar1;
|
||||
// uint64_t uVar2;
|
||||
// uVar2 = *(uint64_t*) (param_1 + 8);
|
||||
// pbVar1 = *(uint8_t**) (param_1 + 0x10);
|
||||
// if ((*param_1 & 1) == 0) {
|
||||
// pbVar1 = param_1 + 1;
|
||||
// uVar2 = (uint64_t) (*param_1 >> 1);
|
||||
// }
|
||||
// x0 = pbVar1;
|
||||
// x1 = uVar2;
|
||||
// }
|
||||
// int tmpInt = 0x138b;
|
||||
// std::string tmpString;
|
||||
// ThunkCallAPI(x0, x1, 0x10, 1, tmpInt, tmpString);
|
||||
|
||||
for (const auto& obj: objects) {
|
||||
auto fromUid = ThunkGetStringProperty(obj._unk0_8, 1);
|
||||
auto toUid = ThunkGetStringProperty(obj._unk0_8, 2);
|
||||
auto randomId = ThunkGetInt64Property(obj._unk0_8, 6);
|
||||
auto timeSeconds = ThunkGetInt64Property(obj._unk0_8, 5);
|
||||
auto msgUid = ThunkGetInt64Property(obj._unk0_8, 4);
|
||||
auto msgSeq = ThunkGetInt64Property(obj._unk0_8, 0x14);
|
||||
auto msgClientSeq = ThunkGetInt32Property(obj._unk0_8, 3);
|
||||
NotifyRecallMsgEventForC2c(fromUid, toUid, randomId, timeSeconds, msgUid, msgSeq, msgClientSeq);
|
||||
}
|
||||
}
|
||||
// LOGD("HandleC2cRecallSysMsgCallback start p1={:p}, p2={:p}, p3={:p}", p1, p2, p3);
|
||||
}
|
||||
|
||||
// Nobody uses PaiYiPai, right?
|
||||
|
||||
bool InitInitNtKernelRecallMsgHook() {
|
||||
using namespace utils;
|
||||
if (sIsHooked) {
|
||||
LOGW("InitInitNtKernelRecallMsgHook failed, already hooked");
|
||||
return false;
|
||||
}
|
||||
auto fnHookProc = [](uint64_t baseAddress) {
|
||||
bool PerformNtRecallMsgHook(uint64_t baseAddress) {
|
||||
if (sIsHooked) {
|
||||
return false;
|
||||
}
|
||||
sIsHooked = true;
|
||||
gLibkernelBaseAddress = reinterpret_cast<void*>(baseAddress);
|
||||
// RecallC2cSysMsg 09 8d 40 f8 f5 03 00 aa 21 00 80 52 f3 03 02 aa 29 8d 40 f9
|
||||
|
||||
//@formatter:off
|
||||
// RecallC2cSysMsg 09 8d 40 f8 f5 03 00 aa 21 00 80 52 f3 03 02 aa 29 ?? 40 f9
|
||||
auto targetRecallC2cSysMsg = AobScanTarget()
|
||||
.WithName("RecallC2cSysMsg")
|
||||
.WithSequence({0x09, 0x8d, 0x40, 0xf8, 0xf5, 0x03, 0x00, 0xaa, 0x21, 0x00, 0x80, 0x52, 0xf3, 0x03, 0x02, 0xaa, 0x29, 0x8d, 0x40, 0xf9})
|
||||
.WithSequence({0x09, 0x8d, 0x40, 0xf8, 0xf5, 0x03, 0x00, 0xaa, 0x21, 0x00, 0x80, 0x52, 0xf3, 0x03, 0x02, 0xaa, 0x29, 0x00, 0x40, 0xf9})
|
||||
.WithMask( {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff})
|
||||
.WithStep(4)
|
||||
.WithExecMemOnly(true)
|
||||
.WithOffsetsForResult({-0x20, -0x24, -0x28})
|
||||
.WithResultValidator(CommonAobScanValidator::kArm64StpX29X30SpImm);
|
||||
|
||||
// RecallGroupSysMsg 28 00 40 f9 61 00 80 52 09 8d 40 f8 29 8d 40 f9
|
||||
// RecallGroupSysMsg 28 00 40 f9 61 00 80 52 09 8d 40 f8 29 !! 40 f9
|
||||
auto targetRecallGroupSysMsg = AobScanTarget()
|
||||
.WithName("RecallGroupSysMsg")
|
||||
.WithSequence({0x28, 0x00, 0x40, 0xf9, 0x61, 0x00, 0x80, 0x52, 0x09, 0x8d, 0x40, 0xf8, 0x29, 0x8d, 0x40, 0xf9})
|
||||
.WithSequence({0x28, 0x00, 0x40, 0xf9, 0x61, 0x00, 0x80, 0x52, 0x09, 0x8d, 0x40, 0xf8, 0x29, 0x00, 0x40, 0xf9})
|
||||
.WithMask( {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff})
|
||||
.WithStep(4)
|
||||
.WithExecMemOnly(true)
|
||||
.WithOffsetsForResult({-0x18, -0x24, -0x28})
|
||||
.WithResultValidator(CommonAobScanValidator::kArm64StpX29X30SpImm);
|
||||
|
||||
// GetDecoder 3f 8d 01 f8 f4 03 00 aa 1f 10 00 f9
|
||||
auto targetGetDecoder = AobScanTarget()
|
||||
.WithName("GetDecoder")
|
||||
.WithSequence({0x3f, 0x8d, 0x01, 0xf8, 0xf4, 0x03, 0x00, 0xaa, 0x1f, 0x10, 0x00, 0xf9})
|
||||
.WithStep(4)
|
||||
.WithExecMemOnly(true)
|
||||
.WithOffsetsForResult({-0x78})
|
||||
.WithResultValidator(CommonAobScanValidator::kArm64StpX29X30SpImm);
|
||||
|
||||
//@formatter:off
|
||||
//OffsetForTmpRev5048
|
||||
//61 01 80 52 mov w1,#0xb
|
||||
//e0 03 ?? aa mov x0,x?
|
||||
//?? 10 00 94 bl FUN_?
|
||||
//?? ?? 00 36 tbz w0,#0x0,LAB_?
|
||||
//?? 02 40 f9 ldr x8,[x??]
|
||||
//61 01 80 52 mov w1,#0xb
|
||||
//e0 03 ?? aa mov x0,x??
|
||||
//09 !! 40 f9 ldr x9,[x8, #0x!!] <-- we need to find this
|
||||
//e8 ?? ?? 91 add x8,sp,#0x??
|
||||
//20 01 3f d6 blr x9
|
||||
auto targetInstructionOffsetForTmpRev5048 = AobScanTarget()
|
||||
.WithName("InstructionOffsetForTmpRev5048")
|
||||
// 0x61 0x01 0x80 0x52 0xe0 0x03 0x?? 0xaa
|
||||
// 0x?? 0x10 0x00 0x94 0x?? 0x?? 0x00 0x36
|
||||
// 0x?? 0x02 0x40 0xf9 0x61 0x01 0x80 0x52
|
||||
// 0xe0 0x03 0x?? 0xaa 0x09 0x?? 0x40 0xf9
|
||||
// 0xe8 0x?? 0x?? 0x91 0x20 0x01 0x3f 0xd6
|
||||
.WithSequence({0x61, 0x01, 0x80, 0x52, 0xe0, 0x03, 0x00, 0xaa,
|
||||
0x00, 0x10, 0x00, 0x94, 0x00, 0x00, 0x00, 0x36,
|
||||
0x00, 0x02, 0x40, 0xf9, 0x61, 0x01, 0x80, 0x52,
|
||||
0xe0, 0x03, 0x00, 0xaa, 0x09, 0x00, 0x40, 0xf9,
|
||||
0xe8, 0x00, 0x00, 0x91, 0x20, 0x01, 0x3f, 0xd6})
|
||||
.WithMask({ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff,
|
||||
0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff,
|
||||
0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff,
|
||||
0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff})
|
||||
.WithStep(4)
|
||||
.WithExecMemOnly(true)
|
||||
.WithOffsetsForResult({7 * 4});
|
||||
//@formatter:on
|
||||
|
||||
std::vector<std::string> errorMsgList;
|
||||
// auto start = std::chrono::steady_clock::now();
|
||||
if (!SearchForAllAobScanTargets({&targetRecallC2cSysMsg, &targetRecallGroupSysMsg, &targetGetDecoder,
|
||||
&targetInstructionOffsetForTmpRev5048},
|
||||
gLibkernelBaseAddress, true, errorMsgList)) {
|
||||
if (!SearchForAllAobScanTargets({&targetRecallC2cSysMsg, &targetRecallGroupSysMsg}, gLibkernelBaseAddress, true, errorMsgList)) {
|
||||
LOGE("InitInitNtKernelRecallMsgHook SearchForAllAobScanTargets failed");
|
||||
// sth went wrong
|
||||
for (const auto& msg: errorMsgList) {
|
||||
@@ -442,33 +113,15 @@ bool InitInitNtKernelRecallMsgHook() {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// auto end = std::chrono::steady_clock::now();
|
||||
// LOGD("InitInitNtKernelRecallMsgHook AobScan elapsed: {}us", std::chrono::duration_cast<std::chrono::microseconds>(end - start).count());
|
||||
|
||||
if (auto pkg = HostInfo::GetPackageName(); pkg != "com.tencent.mobileqq") {
|
||||
TraceErrorF(nullptr, gInstanceRevokeMsgHook, "InitInitNtKernelRecallMsgHook failed, unexpected package name: {}", pkg);
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t offsetC2c = targetRecallC2cSysMsg.GetResultOffset();
|
||||
uint64_t offsetGroup = targetRecallGroupSysMsg.GetResultOffset();
|
||||
uint64_t offsetGetDecoder = targetGetDecoder.GetResultOffset();
|
||||
uint64_t offsetInstForTmpRev5048 = targetInstructionOffsetForTmpRev5048.GetResultOffset();
|
||||
|
||||
uint32_t instructionForTmpRev5048 = *reinterpret_cast<uint32_t*>(
|
||||
reinterpret_cast<uintptr_t>(gLibkernelBaseAddress) + offsetInstForTmpRev5048);
|
||||
{
|
||||
// LOGD("instructionForTmpRev5048={:08x}", instructionForTmpRev5048);
|
||||
// 09 !! 40 f9 ldr x9,[x8, #0x!!]
|
||||
uint32_t imm12 = ((instructionForTmpRev5048 >> 10u) & 0xfffu) << 3u;
|
||||
// LOGD("imm12={:x}", imm12);
|
||||
gOffsetForTmpRev5048 = imm12;
|
||||
}
|
||||
LOGD("offsetC2c={:x}, offsetGroup={:x}", offsetC2c, offsetGroup);
|
||||
|
||||
LOGD("offsetC2c={:x}, offsetGroup={:x}, offsetGetDecoder={:x}, offsetInstForTmpRev5048={:x}, gOffsetForTmpRev5048={:x}",
|
||||
offsetC2c, offsetGroup, offsetGetDecoder, offsetInstForTmpRev5048, gOffsetForTmpRev5048);
|
||||
|
||||
gOffsetGetDecoderSp = offsetGetDecoder;
|
||||
if (offsetC2c != 0) {
|
||||
void* c2c = (void*) (baseAddress + offsetC2c);
|
||||
if (CreateInlineHook(c2c, (void*) &HandleC2cRecallSysMsgCallback, (void**) &sOriginHandleC2cRecallSysMsgCallback) != 0) {
|
||||
@@ -492,7 +145,16 @@ bool InitInitNtKernelRecallMsgHook() {
|
||||
TraceErrorF(nullptr, gInstanceRevokeMsgHook, "InitInitNtKernelRecallMsgHook failed, offsetGroup == 0");
|
||||
}
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
bool InitInitNtKernelRecallMsgHook() {
|
||||
using namespace utils;
|
||||
if (sIsHooked) {
|
||||
LOGW("InitInitNtKernelRecallMsgHook failed, already hooked");
|
||||
return false;
|
||||
}
|
||||
auto fnHookProc = &PerformNtRecallMsgHook;
|
||||
ProcessView self;
|
||||
if (int err;(err = self.readProcess(getpid())) != 0) {
|
||||
TraceErrorF(nullptr, gInstanceRevokeMsgHook, "InitInitNtKernelRecallMsgHook failed, readProcess failed: {}", err);
|
||||
@@ -561,7 +223,7 @@ bool InitInitNtKernelRecallMsgHook() {
|
||||
} // ntqq::hook
|
||||
|
||||
extern "C" JNIEXPORT jboolean JNICALL
|
||||
Java_cc_ioctl_hook_msg_RevokeMsgHook_nativeInitNtKernelRecallMsgHook(JNIEnv* env, jobject thiz) {
|
||||
Java_cc_ioctl_hook_msg_RevokeMsgHook_nativeInitNtKernelRecallMsgHookV1p2(JNIEnv* env, jobject thiz) {
|
||||
using ntqq::hook::klassRevokeMsgHook;
|
||||
using ntqq::hook::gInstanceRevokeMsgHook;
|
||||
using ntqq::hook::handleRecallSysMsgFromNtKernel;
|
||||
|
||||
119
app/src/main/cpp/qauxv_core/linker_utils.cc
Normal file
119
app/src/main/cpp/qauxv_core/linker_utils.cc
Normal file
@@ -0,0 +1,119 @@
|
||||
//
|
||||
// Created by sulfate on 2024-07-11.
|
||||
//
|
||||
|
||||
#include "linker_utils.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <atomic>
|
||||
#include <optional>
|
||||
#include <mutex>
|
||||
|
||||
#include <dlfcn.h>
|
||||
#include <android/dlext.h>
|
||||
|
||||
#include "utils/Log.h"
|
||||
#include "qauxv_core/HostInfo.h"
|
||||
#include "utils/ProcessView.h"
|
||||
#include "utils/ElfScan.h"
|
||||
#include "utils/ElfView.h"
|
||||
#include "utils/FileMemMap.h"
|
||||
#include "utils/ThreadUtils.h"
|
||||
|
||||
namespace qauxv {
|
||||
|
||||
void* loader_android_dlopen_ext(const char* filename,
|
||||
int flag,
|
||||
const android_dlextinfo* extinfo,
|
||||
const void* caller_addr) {
|
||||
int sdk = qauxv::HostInfo::GetSdkInt();
|
||||
// there is no linker namespace pre-N
|
||||
if (sdk < 24) {
|
||||
return android_dlopen_ext(filename, flag, extinfo);
|
||||
}
|
||||
static std::mutex sMutex;
|
||||
std::scoped_lock lock_(sMutex);
|
||||
// 1. get ld-android.so
|
||||
static void* handleLoader = nullptr;
|
||||
if (handleLoader == nullptr) {
|
||||
handleLoader = dlopen("libdl.so", RTLD_NOW | RTLD_NOLOAD);
|
||||
}
|
||||
if (handleLoader == nullptr) {
|
||||
// this should not happen
|
||||
LOGE("loader_android_dlopen_ext: failed to find libdl.so");
|
||||
return nullptr;
|
||||
}
|
||||
// for 8.0/SDK26+, we have the famous __loader_android_dlopen_ext
|
||||
static std::optional<void*> p_loader_android_dlopen_ext = std::nullopt;
|
||||
if (!p_loader_android_dlopen_ext.has_value()) {
|
||||
p_loader_android_dlopen_ext = dlsym(handleLoader, "__loader_android_dlopen_ext");
|
||||
}
|
||||
// for 7.0/7.1, use static symbol __dl__ZL10dlopen_extPKciPK17android_dlextinfoPv
|
||||
if (p_loader_android_dlopen_ext.value() == nullptr) {
|
||||
using utils::ElfView;
|
||||
const char* soname;
|
||||
// it's actually ld-android.so, not linker(64)
|
||||
if constexpr (sizeof(void*) == 8) {
|
||||
soname = "linker64";
|
||||
} else {
|
||||
soname = "linker";
|
||||
}
|
||||
utils::ProcessView processView;
|
||||
int rc;
|
||||
if ((rc = processView.readProcess(getpid())) != 0) {
|
||||
LOGE("HookLoadLibrary: failed to read process, rc = {}", rc);
|
||||
return nullptr;
|
||||
}
|
||||
const void* linkerBaseAddress = nullptr;
|
||||
std::string linkerPath;
|
||||
for (const auto& m: processView.getModules()) {
|
||||
if (m.name == soname || m.name == "ld.so" || m.name == "ld-android.so") {
|
||||
linkerBaseAddress = reinterpret_cast<void*>(m.baseAddress);
|
||||
linkerPath = m.path;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (linkerBaseAddress == nullptr || linkerPath.empty()) {
|
||||
LOGE("HookLoadLibrary: failed to find linker module");
|
||||
return nullptr;
|
||||
}
|
||||
FileMemMap linkerFileMap;
|
||||
if ((rc = linkerFileMap.mapFilePath(linkerPath.c_str())) != 0) {
|
||||
LOGE("HookLoadLibrary: failed to map linker file, rc = {}", rc);
|
||||
return nullptr;
|
||||
}
|
||||
ElfView linkerElfView;
|
||||
linkerElfView.AttachFileMemMapping(linkerFileMap.getAddress(), linkerFileMap.getLength());
|
||||
if (!linkerElfView.IsValid()) {
|
||||
LOGE("HookLoadLibrary: failed to attach linker file");
|
||||
return nullptr;
|
||||
}
|
||||
auto linkerSymbolResolver = [&](const char* symbol) -> void* {
|
||||
auto offset = linkerElfView.GetSymbolOffset(symbol);
|
||||
if (offset == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
return reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(linkerBaseAddress) + static_cast<uintptr_t>(offset));
|
||||
};
|
||||
const char* sym_dlopen_ext = "__dl__ZL10dlopen_extPKciPK17android_dlextinfoPv";
|
||||
p_loader_android_dlopen_ext = linkerSymbolResolver(sym_dlopen_ext);
|
||||
if (p_loader_android_dlopen_ext.value() == nullptr) {
|
||||
LOGE("loader_android_dlopen_ext: failed to find symbol {}", sym_dlopen_ext);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
using loader_android_dlopen_ext_t = void* (*)(const char*, int, const android_dlextinfo*, const void*);
|
||||
auto fn_loader_android_dlopen_ext = reinterpret_cast<loader_android_dlopen_ext_t>(p_loader_android_dlopen_ext.value());
|
||||
if (fn_loader_android_dlopen_ext == nullptr) {
|
||||
LOGE("loader_android_dlopen_ext: failed to find symbol");
|
||||
return nullptr;
|
||||
}
|
||||
// invoke the function
|
||||
return fn_loader_android_dlopen_ext(filename, flag, extinfo, caller_addr);
|
||||
}
|
||||
|
||||
void* loader_dlopen(const char* filename, int flag, const void* caller_addr) {
|
||||
return loader_android_dlopen_ext(filename, flag, nullptr, caller_addr);
|
||||
}
|
||||
|
||||
}
|
||||
21
app/src/main/cpp/qauxv_core/linker_utils.h
Normal file
21
app/src/main/cpp/qauxv_core/linker_utils.h
Normal file
@@ -0,0 +1,21 @@
|
||||
//
|
||||
// Created by sulfate on 2024-07-11.
|
||||
//
|
||||
|
||||
#ifndef QAUXV_LINKER_UTILS_H
|
||||
#define QAUXV_LINKER_UTILS_H
|
||||
|
||||
#include <android/dlext.h>
|
||||
|
||||
namespace qauxv {
|
||||
|
||||
void* loader_android_dlopen_ext(const char* filename,
|
||||
int flag,
|
||||
const android_dlextinfo* extinfo,
|
||||
const void* caller_addr);
|
||||
|
||||
void* loader_dlopen(const char* filename, int flag, const void* caller_addr);
|
||||
|
||||
}
|
||||
|
||||
#endif //QAUXV_LINKER_UTILS_H
|
||||
113
app/src/main/cpp/utils/endian.h
Normal file
113
app/src/main/cpp/utils/endian.h
Normal file
@@ -0,0 +1,113 @@
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <type_traits>
|
||||
|
||||
namespace platform::arch::endian {
|
||||
|
||||
#if __BYTE_ORDER == __LITTLE_ENDIAN
|
||||
static constexpr bool kIsLittleEndian = true;
|
||||
#elif __BYTE_ORDER == __BIG_ENDIAN
|
||||
static constexpr bool kIsLittleEndian = false;
|
||||
#else
|
||||
#error "Unknown endianness"
|
||||
#endif
|
||||
|
||||
static constexpr bool kIsBigEndian = !kIsLittleEndian;
|
||||
|
||||
template<typename T>
|
||||
requires(std::is_integral_v<T> && std::is_unsigned_v<T> && sizeof(T) == 2)
|
||||
static constexpr T SwapEndian16(T value) {
|
||||
return static_cast<T>((value >> 8) | (value << 8));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
requires(std::is_integral_v<T> && std::is_unsigned_v<T> && sizeof(T) == 4)
|
||||
static constexpr T SwapEndian32(T value) {
|
||||
return static_cast<T>((value >> 24) | ((value & 0x00ff0000u) >> 8) |
|
||||
((value & 0x0000ff00u) << 8) | (value << 24));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
requires(std::is_integral_v<T> && std::is_unsigned_v<T> && sizeof(T) == 8)
|
||||
static constexpr T SwapEndian64(T value) {
|
||||
return static_cast<T>((value >> 56) | ((value & 0x00ff000000000000ULL) >> 40) |
|
||||
((value & 0x0000ff0000000000ULL) >> 24) |
|
||||
((value & 0x000000ff00000000ULL) >> 8) |
|
||||
((value & 0x00000000ff000000ULL) << 8) |
|
||||
((value & 0x0000000000ff0000ULL) << 24) |
|
||||
((value & 0x000000000000ff00ULL) << 40) | (value << 56));
|
||||
}
|
||||
|
||||
static constexpr uint16_t ltoh16(uint16_t value) {
|
||||
return kIsLittleEndian ? value : SwapEndian16(value);
|
||||
}
|
||||
|
||||
static constexpr uint32_t ltoh32(uint32_t value) {
|
||||
return kIsLittleEndian ? value : SwapEndian32(value);
|
||||
}
|
||||
|
||||
static constexpr uint64_t ltoh64(uint64_t value) {
|
||||
return kIsLittleEndian ? value : SwapEndian64(value);
|
||||
}
|
||||
|
||||
static constexpr uint16_t htol16(uint16_t value) {
|
||||
return kIsLittleEndian ? value : SwapEndian16(value);
|
||||
}
|
||||
|
||||
static constexpr uint32_t htol32(uint32_t value) {
|
||||
return kIsLittleEndian ? value : SwapEndian32(value);
|
||||
}
|
||||
|
||||
static constexpr uint64_t htol64(uint64_t value) {
|
||||
return kIsLittleEndian ? value : SwapEndian64(value);
|
||||
}
|
||||
|
||||
static constexpr uint16_t btoh16(uint16_t value) {
|
||||
return kIsBigEndian ? value : SwapEndian16(value);
|
||||
}
|
||||
|
||||
static constexpr uint32_t btoh32(uint32_t value) {
|
||||
return kIsBigEndian ? value : SwapEndian32(value);
|
||||
}
|
||||
|
||||
static constexpr uint64_t btoh64(uint64_t value) {
|
||||
return kIsBigEndian ? value : SwapEndian64(value);
|
||||
}
|
||||
|
||||
static constexpr uint16_t htob16(uint16_t value) {
|
||||
return kIsBigEndian ? value : SwapEndian16(value);
|
||||
}
|
||||
|
||||
static constexpr uint32_t htob32(uint32_t value) {
|
||||
return kIsBigEndian ? value : SwapEndian32(value);
|
||||
}
|
||||
|
||||
static constexpr uint64_t htob64(uint64_t value) {
|
||||
return kIsBigEndian ? value : SwapEndian64(value);
|
||||
}
|
||||
|
||||
static constexpr uint16_t ntoh16(uint16_t value) {
|
||||
return btoh16(value);
|
||||
}
|
||||
|
||||
static constexpr uint32_t ntoh32(uint32_t value) {
|
||||
return btoh32(value);
|
||||
}
|
||||
|
||||
static constexpr uint64_t ntoh64(uint64_t value) {
|
||||
return btoh64(value);
|
||||
}
|
||||
|
||||
static constexpr uint16_t hton16(uint16_t value) {
|
||||
return htob16(value);
|
||||
}
|
||||
|
||||
static constexpr uint32_t hton32(uint32_t value) {
|
||||
return htob32(value);
|
||||
}
|
||||
|
||||
static constexpr uint64_t hton64(uint64_t value) {
|
||||
return htob64(value);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -9,7 +9,7 @@ import android.util.Log;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
import cc.hicore.Env;
|
||||
import de.robv.android.xposed.XposedBridge;
|
||||
import io.github.qauxv.util.xpcompat.XposedBridge;
|
||||
import io.github.qauxv.ui.CommonContextWrapper;
|
||||
import io.github.qauxv.util.Toasts;
|
||||
import java.io.File;
|
||||
|
||||
@@ -39,7 +39,7 @@ import cc.hicore.ReflectUtil.XField;
|
||||
import cc.hicore.ReflectUtil.XMethod;
|
||||
import cc.hicore.dialog.RepeaterPlusIconSettingDialog;
|
||||
import cc.ioctl.util.LayoutHelper;
|
||||
import de.robv.android.xposed.XC_MethodHook;
|
||||
import io.github.qauxv.util.xpcompat.XC_MethodHook;
|
||||
import io.github.qauxv.R;
|
||||
import io.github.qauxv.util.CustomMenu;
|
||||
import io.github.qauxv.util.LicenseStatus;
|
||||
|
||||
@@ -51,9 +51,9 @@ import com.tencent.qqnt.kernel.nativeinterface.MsgAttributeInfo;
|
||||
import com.tencent.qqnt.kernel.nativeinterface.MsgRecord;
|
||||
import com.xiaoniu.dispatcher.OnMenuBuilder;
|
||||
import com.xiaoniu.util.ContextUtils;
|
||||
import de.robv.android.xposed.XC_MethodHook;
|
||||
import de.robv.android.xposed.XposedBridge;
|
||||
import de.robv.android.xposed.XposedHelpers;
|
||||
import io.github.qauxv.util.xpcompat.XC_MethodHook;
|
||||
import io.github.qauxv.util.xpcompat.XposedBridge;
|
||||
import io.github.qauxv.util.xpcompat.XposedHelpers;
|
||||
import io.github.qauxv.R;
|
||||
import io.github.qauxv.base.ISwitchCellAgent;
|
||||
import io.github.qauxv.base.IUiItemAgent;
|
||||
|
||||
@@ -33,7 +33,7 @@ import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import cc.ioctl.util.HookUtils;
|
||||
import cc.ioctl.util.Reflex;
|
||||
import de.robv.android.xposed.XC_MethodHook;
|
||||
import io.github.qauxv.util.xpcompat.XC_MethodHook;
|
||||
import io.github.qauxv.base.annotation.FunctionHookEntry;
|
||||
import io.github.qauxv.base.annotation.UiItemAgentEntry;
|
||||
import io.github.qauxv.bridge.AppRuntimeHelper;
|
||||
|
||||
@@ -25,8 +25,8 @@ import androidx.annotation.NonNull;
|
||||
import cc.hicore.ReflectUtil.XMethod;
|
||||
import cc.ioctl.util.HookUtils;
|
||||
import cc.ioctl.util.HostInfo;
|
||||
import de.robv.android.xposed.XC_MethodHook;
|
||||
import de.robv.android.xposed.XposedHelpers;
|
||||
import io.github.qauxv.util.xpcompat.XC_MethodHook;
|
||||
import io.github.qauxv.util.xpcompat.XposedHelpers;
|
||||
import io.github.qauxv.base.annotation.FunctionHookEntry;
|
||||
import io.github.qauxv.base.annotation.UiItemAgentEntry;
|
||||
import io.github.qauxv.dsl.FunctionEntryRouter;
|
||||
|
||||
@@ -26,8 +26,8 @@ import androidx.annotation.NonNull;
|
||||
import cc.ioctl.util.HookUtils;
|
||||
import cc.ioctl.util.HostInfo;
|
||||
import cc.ioctl.util.Reflex;
|
||||
import de.robv.android.xposed.XC_MethodHook;
|
||||
import de.robv.android.xposed.XposedHelpers;
|
||||
import io.github.qauxv.util.xpcompat.XC_MethodHook;
|
||||
import io.github.qauxv.util.xpcompat.XposedHelpers;
|
||||
import io.github.qauxv.base.annotation.FunctionHookEntry;
|
||||
import io.github.qauxv.base.annotation.UiItemAgentEntry;
|
||||
import io.github.qauxv.dsl.FunctionEntryRouter;
|
||||
|
||||
@@ -48,7 +48,7 @@ import com.tencent.qqnt.kernel.nativeinterface.MsgRecord;
|
||||
import com.tencent.qqnt.kernel.nativeinterface.PicElement;
|
||||
import com.xiaoniu.dispatcher.OnMenuBuilder;
|
||||
import com.xiaoniu.util.ContextUtils;
|
||||
import de.robv.android.xposed.XC_MethodHook;
|
||||
import io.github.qauxv.util.xpcompat.XC_MethodHook;
|
||||
import io.github.qauxv.R;
|
||||
import io.github.qauxv.base.annotation.FunctionHookEntry;
|
||||
import io.github.qauxv.base.annotation.UiItemAgentEntry;
|
||||
|
||||
@@ -26,8 +26,8 @@ import cc.hicore.QApp.QAppUtils;
|
||||
import cc.hicore.ReflectUtil.XField;
|
||||
import cc.hicore.hook.RepeaterPlus;
|
||||
import cc.hicore.hook.stickerPanel.Hooker.StickerPanelEntryHooker;
|
||||
import de.robv.android.xposed.XC_MethodHook;
|
||||
import de.robv.android.xposed.XposedBridge;
|
||||
import io.github.qauxv.util.xpcompat.XC_MethodHook;
|
||||
import io.github.qauxv.util.xpcompat.XposedBridge;
|
||||
import io.github.qauxv.base.IDynamicHook;
|
||||
import io.github.qauxv.base.annotation.FunctionHookEntry;
|
||||
import io.github.qauxv.hook.BaseHookDispatcher;
|
||||
|
||||
@@ -25,7 +25,7 @@ package cc.ioctl.hook;
|
||||
import static android.widget.LinearLayout.LayoutParams.MATCH_PARENT;
|
||||
import static android.widget.LinearLayout.LayoutParams.WRAP_CONTENT;
|
||||
import static cc.ioctl.util.LayoutHelper.dip2px;
|
||||
import static de.robv.android.xposed.XposedHelpers.findAndHookMethod;
|
||||
import static io.github.qauxv.util.xpcompat.XposedHelpers.findAndHookMethod;
|
||||
import static io.github.qauxv.util.Initiator.load;
|
||||
|
||||
import android.app.Activity;
|
||||
@@ -42,8 +42,8 @@ import cc.ioctl.hook.friend.ShowDeletedFriendListEntry;
|
||||
import cc.ioctl.util.ExfriendManager;
|
||||
import cc.ioctl.util.LayoutHelper;
|
||||
import cc.ioctl.util.Reflex;
|
||||
import de.robv.android.xposed.XC_MethodHook;
|
||||
import de.robv.android.xposed.XposedHelpers;
|
||||
import io.github.qauxv.util.xpcompat.XC_MethodHook;
|
||||
import io.github.qauxv.util.xpcompat.XposedHelpers;
|
||||
import io.github.qauxv.activity.SettingsUiFragmentHostActivity;
|
||||
import io.github.qauxv.base.annotation.FunctionHookEntry;
|
||||
import io.github.qauxv.bridge.AppRuntimeHelper;
|
||||
|
||||
@@ -40,9 +40,9 @@ import cc.ioctl.util.HookUtils;
|
||||
import cc.ioctl.util.HostInfo;
|
||||
import cc.ioctl.util.LayoutHelper;
|
||||
import cc.ioctl.util.Reflex;
|
||||
import de.robv.android.xposed.XC_MethodHook;
|
||||
import de.robv.android.xposed.XposedBridge;
|
||||
import de.robv.android.xposed.XposedHelpers;
|
||||
import io.github.qauxv.util.xpcompat.XC_MethodHook;
|
||||
import io.github.qauxv.util.xpcompat.XposedBridge;
|
||||
import io.github.qauxv.util.xpcompat.XposedHelpers;
|
||||
import io.github.qauxv.BuildConfig;
|
||||
import io.github.qauxv.R;
|
||||
import io.github.qauxv.activity.SettingsUiFragmentHostActivity;
|
||||
|
||||
@@ -28,8 +28,8 @@ import static io.github.qauxv.util.Initiator.load;
|
||||
import cc.ioctl.util.ExfriendManager;
|
||||
import cc.ioctl.util.HookUtils;
|
||||
import cc.ioctl.util.Reflex;
|
||||
import de.robv.android.xposed.XC_MethodHook;
|
||||
import de.robv.android.xposed.XposedHelpers;
|
||||
import io.github.qauxv.util.xpcompat.XC_MethodHook;
|
||||
import io.github.qauxv.util.xpcompat.XposedHelpers;
|
||||
import io.github.qauxv.util.SyncUtils;
|
||||
import io.github.qauxv.config.ConfigItems;
|
||||
import io.github.qauxv.hook.BasePersistBackgroundHook;
|
||||
|
||||
@@ -30,8 +30,8 @@ import cc.ioctl.util.HookUtils;
|
||||
import cc.ioctl.util.HostInfo;
|
||||
import cc.ioctl.util.Reflex;
|
||||
import com.tencent.qqnt.kernel.nativeinterface.VASMsgBubble;
|
||||
import de.robv.android.xposed.XC_MethodHook;
|
||||
import de.robv.android.xposed.XposedBridge;
|
||||
import io.github.qauxv.util.xpcompat.XC_MethodHook;
|
||||
import io.github.qauxv.util.xpcompat.XposedBridge;
|
||||
import io.github.qauxv.base.annotation.FunctionHookEntry;
|
||||
import io.github.qauxv.base.annotation.UiItemAgentEntry;
|
||||
import io.github.qauxv.dsl.FunctionEntryRouter.Locations.Simplify;
|
||||
|
||||
@@ -29,8 +29,8 @@ import androidx.annotation.Nullable;
|
||||
import cc.hicore.QApp.QAppUtils;
|
||||
import cc.ioctl.util.HookUtils;
|
||||
import com.tencent.qqnt.kernel.nativeinterface.VASMsgFont;
|
||||
import de.robv.android.xposed.XC_MethodHook;
|
||||
import de.robv.android.xposed.XposedBridge;
|
||||
import io.github.qauxv.util.xpcompat.XC_MethodHook;
|
||||
import io.github.qauxv.util.xpcompat.XposedBridge;
|
||||
import io.github.qauxv.base.annotation.FunctionHookEntry;
|
||||
import io.github.qauxv.base.annotation.UiItemAgentEntry;
|
||||
import io.github.qauxv.dsl.FunctionEntryRouter.Locations.Simplify;
|
||||
|
||||
@@ -29,8 +29,8 @@ import androidx.annotation.NonNull;
|
||||
import cc.hicore.QApp.QAppUtils;
|
||||
import cc.hicore.ReflectUtil.XMethod;
|
||||
import cc.ioctl.util.HookUtils;
|
||||
import de.robv.android.xposed.XC_MethodHook;
|
||||
import de.robv.android.xposed.XposedHelpers;
|
||||
import io.github.qauxv.util.xpcompat.XC_MethodHook;
|
||||
import io.github.qauxv.util.xpcompat.XposedHelpers;
|
||||
import io.github.qauxv.base.annotation.FunctionHookEntry;
|
||||
import io.github.qauxv.base.annotation.UiItemAgentEntry;
|
||||
import io.github.qauxv.bridge.AIOUtilsImpl;
|
||||
|
||||
@@ -24,8 +24,8 @@ package cc.ioctl.hook.chat;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import cc.ioctl.util.HookUtils;
|
||||
import de.robv.android.xposed.XC_MethodHook;
|
||||
import de.robv.android.xposed.XposedBridge;
|
||||
import io.github.qauxv.util.xpcompat.XC_MethodHook;
|
||||
import io.github.qauxv.util.xpcompat.XposedBridge;
|
||||
import io.github.qauxv.base.annotation.FunctionHookEntry;
|
||||
import io.github.qauxv.base.annotation.UiItemAgentEntry;
|
||||
import io.github.qauxv.dsl.FunctionEntryRouter.Locations.Auxiliary;
|
||||
|
||||
@@ -29,8 +29,8 @@ import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import cc.ioctl.util.HostInfo;
|
||||
import cc.ioctl.util.Reflex;
|
||||
import de.robv.android.xposed.XC_MethodHook;
|
||||
import de.robv.android.xposed.XposedHelpers;
|
||||
import io.github.qauxv.util.xpcompat.XC_MethodHook;
|
||||
import io.github.qauxv.util.xpcompat.XposedHelpers;
|
||||
import io.github.qauxv.base.annotation.FunctionHookEntry;
|
||||
import io.github.qauxv.base.annotation.UiItemAgentEntry;
|
||||
import io.github.qauxv.dsl.FunctionEntryRouter.Locations.Auxiliary;
|
||||
|
||||
@@ -131,7 +131,8 @@ object GagInfoDisclosure : CommonSwitchFunctionHook(
|
||||
if (vMsg[4].toInt() == 12) {
|
||||
val selfUin = AppRuntimeHelper.getAccount()
|
||||
val troopUin = getLongData(vMsg, 0).toString()
|
||||
val opUin = getLongData(vMsg, 6).toString()
|
||||
val opUinTmp = getLongData(vMsg, 6)
|
||||
val opUin = (opUinTmp.takeIf { it > 0 } ?: (opUinTmp and 0xFFFFFFFFL)).toString()
|
||||
val victimUinTmp = getLongData(vMsg, 16)
|
||||
val victimUin = (victimUinTmp.takeIf { it > 0 } ?: (victimUinTmp and 0xFFFFFFFFL)).toString()
|
||||
val victimTime = getLongData(vMsg, 20)
|
||||
|
||||
@@ -26,8 +26,8 @@ import static io.github.qauxv.util.QQVersion.QQ_8_8_11;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import cc.ioctl.util.HostInfo;
|
||||
import de.robv.android.xposed.XC_MethodHook;
|
||||
import de.robv.android.xposed.XposedBridge;
|
||||
import io.github.qauxv.util.xpcompat.XC_MethodHook;
|
||||
import io.github.qauxv.util.xpcompat.XposedBridge;
|
||||
import io.github.qauxv.base.annotation.FunctionHookEntry;
|
||||
import io.github.qauxv.base.annotation.UiItemAgentEntry;
|
||||
import io.github.qauxv.dsl.FunctionEntryRouter.Locations.Auxiliary;
|
||||
|
||||
@@ -36,9 +36,9 @@ import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import cc.ioctl.fragment.FakeBatteryConfigFragment;
|
||||
import cc.ioctl.util.HostInfo;
|
||||
import de.robv.android.xposed.XC_MethodHook;
|
||||
import de.robv.android.xposed.XposedBridge;
|
||||
import de.robv.android.xposed.XposedHelpers;
|
||||
import io.github.qauxv.util.xpcompat.XC_MethodHook;
|
||||
import io.github.qauxv.util.xpcompat.XposedBridge;
|
||||
import io.github.qauxv.util.xpcompat.XposedHelpers;
|
||||
import io.github.qauxv.util.SyncUtils;
|
||||
import io.github.qauxv.activity.SettingsUiFragmentHostActivity;
|
||||
import io.github.qauxv.base.ISwitchCellAgent;
|
||||
|
||||
@@ -38,7 +38,7 @@ import cc.ioctl.util.HostInfo
|
||||
import cc.ioctl.util.hookAfterIfEnabled
|
||||
import com.github.kyuubiran.ezxhelper.utils.Log
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import de.robv.android.xposed.XposedHelpers
|
||||
import io.github.qauxv.util.xpcompat.XposedHelpers
|
||||
import io.github.duzhaokun123.util.FilePicker
|
||||
import io.github.qauxv.base.IUiItemAgent
|
||||
import io.github.qauxv.base.annotation.FunctionHookEntry
|
||||
|
||||
@@ -28,8 +28,8 @@ import android.view.View
|
||||
import cc.hicore.QApp.QAppUtils
|
||||
import cc.ioctl.util.hookBeforeIfEnabled
|
||||
import com.github.kyuubiran.ezxhelper.utils.isPrivate
|
||||
import de.robv.android.xposed.XC_MethodHook
|
||||
import de.robv.android.xposed.XposedBridge
|
||||
import io.github.qauxv.util.xpcompat.XC_MethodHook
|
||||
import io.github.qauxv.util.xpcompat.XposedBridge
|
||||
import io.github.qauxv.base.annotation.FunctionHookEntry
|
||||
import io.github.qauxv.base.annotation.UiItemAgentEntry
|
||||
import io.github.qauxv.dsl.FunctionEntryRouter
|
||||
|
||||
@@ -35,8 +35,8 @@ import androidx.annotation.Nullable;
|
||||
import cc.ioctl.dialog.RikkaBaseApkFormatDialog;
|
||||
import cc.ioctl.util.HookUtils;
|
||||
import cc.ioctl.util.HostInfo;
|
||||
import de.robv.android.xposed.XC_MethodHook;
|
||||
import de.robv.android.xposed.XposedHelpers;
|
||||
import io.github.qauxv.util.xpcompat.XC_MethodHook;
|
||||
import io.github.qauxv.util.xpcompat.XposedHelpers;
|
||||
import io.github.qauxv.base.IUiItemAgent;
|
||||
import io.github.qauxv.base.annotation.FunctionHookEntry;
|
||||
import io.github.qauxv.base.annotation.UiItemAgentEntry;
|
||||
|
||||
@@ -29,9 +29,9 @@ import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import cc.ioctl.dialog.RikkaCustomDeviceModelDialog;
|
||||
import cc.ioctl.util.HostInfo;
|
||||
import de.robv.android.xposed.XC_MethodReplacement;
|
||||
import de.robv.android.xposed.XposedBridge;
|
||||
import de.robv.android.xposed.XposedHelpers;
|
||||
import io.github.qauxv.util.xpcompat.XC_MethodReplacement;
|
||||
import io.github.qauxv.util.xpcompat.XposedBridge;
|
||||
import io.github.qauxv.util.xpcompat.XposedHelpers;
|
||||
import io.github.qauxv.base.IUiItemAgent;
|
||||
import io.github.qauxv.base.annotation.FunctionHookEntry;
|
||||
import io.github.qauxv.base.annotation.UiItemAgentEntry;
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
package cc.ioctl.hook.misc
|
||||
|
||||
import android.content.Intent
|
||||
import de.robv.android.xposed.XC_MethodHook
|
||||
import io.github.qauxv.util.xpcompat.XC_MethodHook
|
||||
import io.github.qauxv.base.IDynamicHook
|
||||
import io.github.qauxv.base.annotation.FunctionHookEntry
|
||||
import io.github.qauxv.base.annotation.UiItemAgentEntry
|
||||
@@ -60,7 +60,7 @@ object NoAskContinueRecvShortVideoOrNot : BaseSwitchFunctionDecorator(), IStartA
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
return false
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ import android.view.View
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import cc.ioctl.util.HookUtils
|
||||
import cc.ioctl.util.Reflex
|
||||
import de.robv.android.xposed.XposedBridge
|
||||
import io.github.qauxv.util.xpcompat.XposedBridge
|
||||
import io.github.qauxv.R
|
||||
import io.github.qauxv.base.annotation.ComponentHookEntry
|
||||
import io.github.qauxv.hook.BaseComponentHook
|
||||
|
||||
@@ -47,8 +47,8 @@ import androidx.core.view.inputmethod.InputConnectionCompat;
|
||||
import cc.hicore.message.bridge.Chat_facade_bridge;
|
||||
import cc.ioctl.util.SendCacheUtils;
|
||||
import cc.ioctl.util.ui.FaultyDialog;
|
||||
import de.robv.android.xposed.XC_MethodHook;
|
||||
import de.robv.android.xposed.XposedBridge;
|
||||
import io.github.qauxv.util.xpcompat.XC_MethodHook;
|
||||
import io.github.qauxv.util.xpcompat.XposedBridge;
|
||||
import io.github.qauxv.R;
|
||||
import io.github.qauxv.base.annotation.FunctionHookEntry;
|
||||
import io.github.qauxv.base.annotation.UiItemAgentEntry;
|
||||
|
||||
@@ -31,9 +31,9 @@ import cc.ioctl.util.beforeHookIfEnabled
|
||||
import com.tencent.qqnt.kernel.nativeinterface.MsgElement
|
||||
import com.xiaoniu.dispatcher.OnMenuBuilder
|
||||
import com.xiaoniu.util.ContextUtils
|
||||
import de.robv.android.xposed.XC_MethodHook.MethodHookParam
|
||||
import de.robv.android.xposed.XposedBridge
|
||||
import de.robv.android.xposed.XposedHelpers
|
||||
import io.github.qauxv.util.xpcompat.XC_MethodHook.MethodHookParam
|
||||
import io.github.qauxv.util.xpcompat.XposedBridge
|
||||
import io.github.qauxv.util.xpcompat.XposedHelpers
|
||||
import io.github.qauxv.R
|
||||
import io.github.qauxv.base.annotation.FunctionHookEntry
|
||||
import io.github.qauxv.base.annotation.UiItemAgentEntry
|
||||
|
||||
@@ -37,8 +37,6 @@ import cc.ioctl.util.HookUtils;
|
||||
import cc.ioctl.util.HostInfo;
|
||||
import cc.ioctl.util.Reflex;
|
||||
import cc.ioctl.util.ui.FaultyDialog;
|
||||
import de.robv.android.xposed.XC_MethodHook;
|
||||
import de.robv.android.xposed.XposedBridge;
|
||||
import io.github.qauxv.R;
|
||||
import io.github.qauxv.activity.ShadowShareFileAgentActivity;
|
||||
import io.github.qauxv.base.annotation.FunctionHookEntry;
|
||||
@@ -56,6 +54,8 @@ import io.github.qauxv.util.dexkit.DefaultFileModel;
|
||||
import io.github.qauxv.util.dexkit.DexKit;
|
||||
import io.github.qauxv.util.dexkit.DexKitTarget;
|
||||
import io.github.qauxv.util.dexkit.FileBrowserActivity_InnerClass_onItemClick;
|
||||
import io.github.qauxv.util.xpcompat.XC_MethodHook;
|
||||
import io.github.qauxv.util.xpcompat.XposedBridge;
|
||||
import java.io.File;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
@@ -112,7 +112,9 @@ public class FileShareExtHook extends CommonSwitchFunctionHook {
|
||||
Class<?> kFileBrowserModelBase = Initiator.loadClass("com.tencent.mobileqq.filemanager.fileviewer.model.FileBrowserModelBase");
|
||||
Class<?> kDefaultFileModel = DexKit.requireClassFromCache(DefaultFileModel.INSTANCE);
|
||||
String fileViewerAdapterClassName;
|
||||
if (requireMinQQVersion(QQVersion.QQ_9_0_15)) {
|
||||
if (requireMinQQVersion(QQVersion.QQ_9_0_80)) {
|
||||
fileViewerAdapterClassName = "com.tencent.mobileqq.filemanager.fileviewer.h";
|
||||
} else if (requireMinQQVersion(QQVersion.QQ_9_0_15)) {
|
||||
fileViewerAdapterClassName = "com.tencent.mobileqq.filemanager.fileviewer.g";
|
||||
} else if (requireMinQQVersion(QQVersion.QQ_8_9_0)) {
|
||||
fileViewerAdapterClassName = "com.tencent.mobileqq.filemanager.fileviewer.h";
|
||||
|
||||
@@ -34,7 +34,7 @@ import cc.hicore.QApp.QAppUtils;
|
||||
import cc.ioctl.util.HookUtils;
|
||||
import cc.ioctl.util.Reflex;
|
||||
import com.tencent.qqnt.kernel.nativeinterface.MsgRecord;
|
||||
import de.robv.android.xposed.XposedHelpers;
|
||||
import io.github.qauxv.util.xpcompat.XposedHelpers;
|
||||
import io.github.qauxv.base.annotation.FunctionHookEntry;
|
||||
import io.github.qauxv.base.annotation.UiItemAgentEntry;
|
||||
import io.github.qauxv.dsl.FunctionEntryRouter.Locations.Auxiliary;
|
||||
|
||||
@@ -44,7 +44,7 @@ import cc.ioctl.util.hookBeforeIfEnabled
|
||||
import cc.ioctl.util.ui.FaultyDialog
|
||||
import com.github.kyuubiran.ezxhelper.utils.hookBefore
|
||||
import com.tencent.qqnt.kernel.nativeinterface.MsgRecord
|
||||
import de.robv.android.xposed.XC_MethodHook.MethodHookParam
|
||||
import io.github.qauxv.util.xpcompat.XC_MethodHook.MethodHookParam
|
||||
import io.github.qauxv.R
|
||||
import io.github.qauxv.base.annotation.DexDeobfs
|
||||
import io.github.qauxv.base.annotation.FunctionHookEntry
|
||||
|
||||
@@ -39,9 +39,9 @@ import com.tencent.qphone.base.remote.FromServiceMsg;
|
||||
import com.tencent.qqnt.kernel.nativeinterface.PicElement;
|
||||
import com.xiaoniu.dispatcher.OnMenuBuilder;
|
||||
import com.xiaoniu.util.ContextUtils;
|
||||
import de.robv.android.xposed.XC_MethodHook;
|
||||
import de.robv.android.xposed.XposedBridge;
|
||||
import de.robv.android.xposed.XposedHelpers;
|
||||
import io.github.qauxv.util.xpcompat.XC_MethodHook;
|
||||
import io.github.qauxv.util.xpcompat.XposedBridge;
|
||||
import io.github.qauxv.util.xpcompat.XposedHelpers;
|
||||
import io.github.qauxv.R;
|
||||
import io.github.qauxv.base.annotation.FunctionHookEntry;
|
||||
import io.github.qauxv.base.annotation.UiItemAgentEntry;
|
||||
|
||||
@@ -28,7 +28,7 @@ import static cc.ioctl.util.LayoutHelper.dip2sp;
|
||||
import static cc.ioctl.util.LayoutHelper.newLinearLayoutParams;
|
||||
import static cc.ioctl.util.Reflex.findField;
|
||||
import static cc.ioctl.util.Reflex.getFirstByType;
|
||||
import static de.robv.android.xposed.XposedHelpers.findAndHookMethod;
|
||||
import static io.github.qauxv.util.xpcompat.XposedHelpers.findAndHookMethod;
|
||||
import static io.github.qauxv.bridge.AppRuntimeHelper.getQQAppInterface;
|
||||
import static io.github.qauxv.util.Initiator._PttItemBuilder;
|
||||
import static io.github.qauxv.util.Initiator.load;
|
||||
@@ -60,8 +60,8 @@ import cc.ioctl.util.Reflex;
|
||||
import com.tencent.qqnt.kernel.nativeinterface.PttElement;
|
||||
import com.xiaoniu.dispatcher.OnMenuBuilder;
|
||||
import com.xiaoniu.util.ContextUtils;
|
||||
import de.robv.android.xposed.XC_MethodHook;
|
||||
import de.robv.android.xposed.XposedBridge;
|
||||
import io.github.qauxv.util.xpcompat.XC_MethodHook;
|
||||
import io.github.qauxv.util.xpcompat.XposedBridge;
|
||||
import io.github.qauxv.R;
|
||||
import io.github.qauxv.base.annotation.FunctionHookEntry;
|
||||
import io.github.qauxv.base.annotation.UiItemAgentEntry;
|
||||
|
||||
@@ -21,8 +21,8 @@
|
||||
*/
|
||||
package cc.ioctl.hook.msg;
|
||||
|
||||
import static de.robv.android.xposed.XposedHelpers.callMethod;
|
||||
import static de.robv.android.xposed.XposedHelpers.setObjectField;
|
||||
import static io.github.qauxv.util.xpcompat.XposedHelpers.callMethod;
|
||||
import static io.github.qauxv.util.xpcompat.XposedHelpers.setObjectField;
|
||||
import static io.github.qauxv.util.Initiator._C2CMessageProcessor;
|
||||
import static io.github.qauxv.util.Initiator._QQMessageFacade;
|
||||
|
||||
@@ -33,12 +33,16 @@ import android.text.TextUtils;
|
||||
import android.view.View;
|
||||
import androidx.annotation.Keep;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import cc.hicore.QApp.QAppUtils;
|
||||
import cc.ioctl.fragment.RevokeMsgConfigFragment;
|
||||
import cc.ioctl.util.HookUtils;
|
||||
import cc.ioctl.util.HostInfo;
|
||||
import cc.ioctl.util.Reflex;
|
||||
import com.google.protobuf.ByteString;
|
||||
import com.google.protobuf.InvalidProtocolBufferException;
|
||||
import com.tencent.qqnt.kernel.nativeinterface.MsgRecord;
|
||||
import io.github.qauxv.util.xpcompat.XC_MethodHook;
|
||||
import io.github.qauxv.activity.SettingsUiFragmentHostActivity;
|
||||
import io.github.qauxv.base.IUiItemAgent;
|
||||
import io.github.qauxv.base.annotation.FunctionHookEntry;
|
||||
@@ -56,6 +60,14 @@ import io.github.qauxv.bridge.ntapi.RelationNTUinAndUidApi;
|
||||
import io.github.qauxv.config.ConfigManager;
|
||||
import io.github.qauxv.dsl.FunctionEntryRouter.Locations.Auxiliary;
|
||||
import io.github.qauxv.hook.CommonConfigFunctionHook;
|
||||
import io.github.qauxv.proto.trpc.msg.C2CMsgRecallOuterClass;
|
||||
import io.github.qauxv.proto.trpc.msg.ContentHeadOuterClass;
|
||||
import io.github.qauxv.proto.trpc.msg.GroupMsgRecallOuterClass;
|
||||
import io.github.qauxv.proto.trpc.msg.InfoSyncPushOuterClass;
|
||||
import io.github.qauxv.proto.trpc.msg.MessageBodyOuterClass;
|
||||
import io.github.qauxv.proto.trpc.msg.MessageOuterClass;
|
||||
import io.github.qauxv.proto.trpc.msg.MsgPushOuterClass;
|
||||
import io.github.qauxv.util.Initiator;
|
||||
import io.github.qauxv.util.Log;
|
||||
import io.github.qauxv.util.QQVersion;
|
||||
import io.github.qauxv.util.SyncUtils;
|
||||
@@ -64,13 +76,19 @@ import io.github.qauxv.util.dexkit.DexKit;
|
||||
import io.github.qauxv.util.dexkit.DexKitTarget;
|
||||
import io.github.qauxv.util.dexkit.NContactUtils_getBuddyName;
|
||||
import io.github.qauxv.util.dexkit.NContactUtils_getDiscussionMemberShowName;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
import kotlin.Unit;
|
||||
import kotlin.collections.ArraysKt;
|
||||
import kotlin.jvm.functions.Function3;
|
||||
import kotlin.jvm.internal.Intrinsics;
|
||||
import kotlinx.coroutines.flow.MutableStateFlow;
|
||||
@@ -89,6 +107,8 @@ import mqq.app.AppRuntime;
|
||||
* 2023-06-16 Fri.12:40 Initial support for NT kernel.
|
||||
* <p>
|
||||
* 2023-07-12 Mon.23:12 Basic support for NT kernel.
|
||||
* <p>
|
||||
* 2024-07-17 Wed.22:03 Use ProtoBuf to get recall info.
|
||||
*/
|
||||
@FunctionHookEntry
|
||||
@UiItemAgentEntry
|
||||
@@ -101,6 +121,8 @@ public class RevokeMsgHook extends CommonConfigFunctionHook {
|
||||
private static final String KEY_KEEP_SELF_REVOKE_MSG = "RevokeMsgHook.KEY_KEEP_SELF_REVOKE_MSG";
|
||||
private static final String KEY_SHOW_SHMSGSEQ = "RevokeMsgHook.KEY_SHOW_SHMSGSEQ";
|
||||
|
||||
private static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
|
||||
|
||||
private RevokeMsgHook() {
|
||||
//FIXME: is MSF really necessary?
|
||||
super(SyncUtils.PROC_MAIN | SyncUtils.PROC_MSF, new DexKitTarget[]{
|
||||
@@ -182,7 +204,9 @@ public class RevokeMsgHook extends CommonConfigFunctionHook {
|
||||
public boolean initOnce() throws Exception {
|
||||
boolean isSuccess = true;
|
||||
if (QAppUtils.isQQnt()) {
|
||||
isSuccess = nativeInitNtKernelRecallMsgHook();
|
||||
boolean p1 = nativeInitNtKernelRecallMsgHookV1p2();
|
||||
boolean p2 = initNtRecallMsgHookV2();
|
||||
isSuccess = p1 && p2;
|
||||
}
|
||||
// The method is still there, even on NT.
|
||||
// I decided to hook them as long as they are there.
|
||||
@@ -216,7 +240,225 @@ public class RevokeMsgHook extends CommonConfigFunctionHook {
|
||||
return isSuccess;
|
||||
}
|
||||
|
||||
private native boolean nativeInitNtKernelRecallMsgHook();
|
||||
private boolean initNtRecallMsgHookV2() throws ReflectiveOperationException {
|
||||
Class<?> kIQQNTWrapperSessionProxy = Initiator.loadClass("com.tencent.qqnt.kernel.nativeinterface.IQQNTWrapperSession$CppProxy");
|
||||
Method onMsfPushMethod = ArraysKt.single(kIQQNTWrapperSessionProxy.getDeclaredMethods(), method -> {
|
||||
return "onMsfPush".equals(method.getName()) && (method.getParameterCount() == 3 || method.getParameterCount() == 2);
|
||||
});
|
||||
Class<?> kPushExtraInfo = null;
|
||||
final Field transInfoMapField;
|
||||
if (onMsfPushMethod.getParameterCount() == 3) {
|
||||
kPushExtraInfo = onMsfPushMethod.getParameterTypes()[2];
|
||||
transInfoMapField = kPushExtraInfo.getDeclaredField("transInfoMap");
|
||||
transInfoMapField.setAccessible(true);
|
||||
} else {
|
||||
transInfoMapField = null;
|
||||
}
|
||||
HookUtils.hookBeforeIfEnabled(this, onMsfPushMethod, -51, param -> {
|
||||
String cmd = (String) param.args[0];
|
||||
if (cmd == null) {
|
||||
return;
|
||||
}
|
||||
byte[] protoBuf = (byte[]) param.args[1];
|
||||
if (protoBuf == null) {
|
||||
protoBuf = EMPTY_BYTE_ARRAY;
|
||||
}
|
||||
HashMap<String, byte[]> transInfoMap = null;
|
||||
if (transInfoMapField != null) {
|
||||
Object pushExtraInfo = param.args[2];
|
||||
if (pushExtraInfo != null) {
|
||||
transInfoMap = (HashMap<String, byte[]>) transInfoMapField.get(pushExtraInfo);
|
||||
}
|
||||
}
|
||||
onMsfPushNtMethodCallback(param, cmd, protoBuf, transInfoMap);
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
private void onMsfPushNtMethodCallback(@NonNull XC_MethodHook.MethodHookParam param, @NonNull String cmd,
|
||||
@NonNull byte[] protoBuf, @Nullable HashMap<String, byte[]> transInfoMap) {
|
||||
if ("trpc.msg.register_proxy.RegisterProxy.InfoSyncPush".equals(cmd)) {
|
||||
handleRegisterProxyInfoSyncPush(param, protoBuf, transInfoMap);
|
||||
} else if ("trpc.msg.olpush.OlPushService.MsgPush".equals(cmd)) {
|
||||
handleOlPushServiceMsgPush(param, protoBuf, transInfoMap);
|
||||
}
|
||||
}
|
||||
|
||||
// trpc.msg.register_proxy.RegisterProxy.InfoSyncPush
|
||||
private void handleRegisterProxyInfoSyncPush(
|
||||
@NonNull XC_MethodHook.MethodHookParam param,
|
||||
@NonNull byte[] protoBuf,
|
||||
@Nullable HashMap<String, byte[]> transInfoMap
|
||||
) {
|
||||
try {
|
||||
InfoSyncPushOuterClass.InfoSyncPush infoSyncPush = InfoSyncPushOuterClass.InfoSyncPush.parseFrom(protoBuf);
|
||||
if (!infoSyncPush.hasSyncMsgRecall()) {
|
||||
return;
|
||||
}
|
||||
InfoSyncPushOuterClass.InfoSyncPush.SyncMsgRecall syncMsgRecall = infoSyncPush.getSyncMsgRecall();
|
||||
List<InfoSyncPushOuterClass.InfoSyncPush.SyncMsgRecall.SyncInfoBody> msgRecalls = syncMsgRecall.getBodyList();
|
||||
for (InfoSyncPushOuterClass.InfoSyncPush.SyncMsgRecall.SyncInfoBody msgRecall : msgRecalls) {
|
||||
String peerUid = msgRecall.getPeerUid();
|
||||
long groupCode;
|
||||
try {
|
||||
groupCode = Long.parseLong(peerUid);
|
||||
} catch (NumberFormatException e) {
|
||||
// not a group
|
||||
continue;
|
||||
}
|
||||
if (groupCode == 0) {
|
||||
continue;
|
||||
}
|
||||
List<MessageOuterClass.Message> msgs = msgRecall.getMsgList();
|
||||
for (MessageOuterClass.Message msg : msgs) {
|
||||
handleCommonGroupMessageRecall(msg);
|
||||
}
|
||||
}
|
||||
} catch (InvalidProtocolBufferException | ReflectiveOperationException e) {
|
||||
traceError(e);
|
||||
}
|
||||
}
|
||||
|
||||
// trpc.msg.olpush.OlPushService.MsgPush
|
||||
private void handleOlPushServiceMsgPush(@NonNull XC_MethodHook.MethodHookParam param,
|
||||
@NonNull byte[] protoBuf, @Nullable HashMap<String, byte[]> transInfoMap) {
|
||||
try {
|
||||
MsgPushOuterClass.MsgPush msgPush = MsgPushOuterClass.MsgPush.parseFrom(protoBuf);
|
||||
if (!msgPush.hasMessage()) {
|
||||
return;
|
||||
}
|
||||
MessageOuterClass.Message message = msgPush.getMessage();
|
||||
if (!message.hasBody() || !message.hasContentHead()) {
|
||||
return;
|
||||
}
|
||||
ContentHeadOuterClass.ContentHead contentHead = message.getContentHead();
|
||||
int type = contentHead.getType();
|
||||
int subType = contentHead.getSubType();
|
||||
// c2c recall: 528, 138
|
||||
// group recall: 732, 17
|
||||
if (type == 528 && subType == 138) {
|
||||
handleMsgPushForC2cRecall(param, msgPush, message);
|
||||
} else if (type == 732 && subType == 17) {
|
||||
handleMsgPushForGroupRecall(param, msgPush, message);
|
||||
}
|
||||
} catch (InvalidProtocolBufferException | ReflectiveOperationException e) {
|
||||
traceError(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleMsgPushForC2cRecall(
|
||||
@NonNull XC_MethodHook.MethodHookParam param,
|
||||
@NonNull MsgPushOuterClass.MsgPush msgPush,
|
||||
@NonNull MessageOuterClass.Message message
|
||||
) throws InvalidProtocolBufferException, ReflectiveOperationException {
|
||||
MessageBodyOuterClass.MessageBody body = message.getBody();
|
||||
if (!body.hasMsgContent()) {
|
||||
return;
|
||||
}
|
||||
ByteString msgContent = body.getMsgContent();
|
||||
C2CMsgRecallOuterClass.C2CMsgRecall msgRecall = C2CMsgRecallOuterClass.C2CMsgRecall.parseFrom(msgContent);
|
||||
List<C2CMsgRecallOuterClass.C2CMsgRecall.MsgInfo> msgInfoList = msgRecall.getMsgInfosList();
|
||||
String selfUid = RelationNTUinAndUidApi.getUidFromUin(AppRuntimeHelper.getAccount());
|
||||
if (TextUtils.isEmpty(selfUid)) {
|
||||
Log.e("handleMsgPushForC2cRecall fatal: selfUid is empty");
|
||||
return;
|
||||
}
|
||||
for (C2CMsgRecallOuterClass.C2CMsgRecall.MsgInfo msgInfo : msgInfoList) {
|
||||
long msgUid = msgInfo.getMsgUid();
|
||||
long msgSeq = msgInfo.getMsgSeq();
|
||||
long msgClientSeq = msgInfo.getMsgClientSeq();
|
||||
long timeSeconds = msgInfo.getTimestamp();
|
||||
String fromUid = msgInfo.getFromUid();
|
||||
String toUid = msgInfo.getToUid();
|
||||
long random64 = msgInfo.getRandomId();
|
||||
// from uid is always operator
|
||||
String peerUid;
|
||||
if (selfUid.equals(fromUid)) {
|
||||
// msg is revoked by myself
|
||||
peerUid = toUid;
|
||||
} else {
|
||||
peerUid = fromUid;
|
||||
}
|
||||
// invoke the handler
|
||||
onRecallSysMsgForNT(ChatTypeConstants.C2C, peerUid, fromUid, fromUid, toUid, random64, timeSeconds, msgUid, msgSeq, (int) msgClientSeq);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleMsgPushForGroupRecall(
|
||||
@NonNull XC_MethodHook.MethodHookParam param,
|
||||
@NonNull MsgPushOuterClass.MsgPush msgPush,
|
||||
@NonNull MessageOuterClass.Message message
|
||||
) throws InvalidProtocolBufferException, ReflectiveOperationException {
|
||||
handleCommonGroupMessageRecall(message);
|
||||
}
|
||||
|
||||
private void handleCommonGroupMessageRecall(
|
||||
@NonNull MessageOuterClass.Message message
|
||||
) throws InvalidProtocolBufferException, ReflectiveOperationException {
|
||||
String selfUid = RelationNTUinAndUidApi.getUidFromUin(AppRuntimeHelper.getAccount());
|
||||
if (TextUtils.isEmpty(selfUid)) {
|
||||
Log.e("handleMsgPushForC2cRecall fatal: selfUid is empty");
|
||||
return;
|
||||
}
|
||||
// verify the message type
|
||||
ContentHeadOuterClass.ContentHead contentHead = message.getContentHead();
|
||||
int type = contentHead.getType();
|
||||
int subType = contentHead.getSubType();
|
||||
// group recall: 732, 17
|
||||
if (!(type == 732 && subType == 17)) {
|
||||
// not our business
|
||||
return;
|
||||
}
|
||||
MessageBodyOuterClass.MessageBody body = message.getBody();
|
||||
if (!body.hasMsgContent()) {
|
||||
return;
|
||||
}
|
||||
ByteString msgContent = body.getMsgContent();
|
||||
if (msgContent.size() < 8) {
|
||||
Log.e("handleMsgPushForGroupRecall: msgContent size is less than 8");
|
||||
return;
|
||||
}
|
||||
// skip first 7 bytes
|
||||
byte[] msgContentBytes = msgContent.toByteArray();
|
||||
byte[] msgContentBytesTrimmed = new byte[msgContentBytes.length - 7];
|
||||
System.arraycopy(msgContentBytes, 7, msgContentBytesTrimmed, 0, msgContentBytesTrimmed.length);
|
||||
GroupMsgRecallOuterClass.GroupMsgRecall groupMsgRecall = GroupMsgRecallOuterClass.GroupMsgRecall.parseFrom(msgContentBytesTrimmed);
|
||||
if (groupMsgRecall.getOpType() != 7) {
|
||||
Log.w("handleMsgPushForGroupRecall: on recall group sys msg! no Prompt_MsgRecallReminder op_type: " + groupMsgRecall.getOpType());
|
||||
return;
|
||||
}
|
||||
if (groupMsgRecall.getGroupCode() == 0) {
|
||||
Log.e("handleMsgPushForGroupRecall: groupMsgRecall has no groupCode");
|
||||
return;
|
||||
}
|
||||
String groupCodeText = String.valueOf(groupMsgRecall.getGroupCode());
|
||||
if (!groupMsgRecall.hasMsgRecall()) {
|
||||
Log.e("handleMsgPushForGroupRecall: groupMsgRecall has no MsgRecall");
|
||||
return;
|
||||
}
|
||||
GroupMsgRecallOuterClass.GroupMsgRecall.MsgRecallInfo groupMsgRecallInfo = groupMsgRecall.getMsgRecall();
|
||||
String operatorUid = groupMsgRecallInfo.getOperatorUid();
|
||||
List<GroupMsgRecallOuterClass.GroupMsgRecall.MsgRecallInfo.MsgInfo> msgs = groupMsgRecallInfo.getMsgInfosList();
|
||||
for (GroupMsgRecallOuterClass.GroupMsgRecall.MsgRecallInfo.MsgInfo msgInfo : msgs) {
|
||||
long msgSeq = msgInfo.getMsgSeq();
|
||||
long timeSeconds = msgInfo.getTimestamp();
|
||||
String authorUid = msgInfo.getMsgAuthorUid();
|
||||
long random64 = msgInfo.getRandomId();
|
||||
|
||||
// Log.d("handleMsgPushForGroupRecall: groupCode=" + groupCodeText + ", operatorUid=" + operatorUid
|
||||
// + ", authorUid=" + authorUid + ", random64=" + random64 + ", timeSeconds=" + timeSeconds
|
||||
// + ", msgSeq=" + msgSeq);
|
||||
|
||||
// invoke the handler
|
||||
onRecallSysMsgForNT(ChatTypeConstants.GROUP, groupCodeText, operatorUid, authorUid, groupCodeText, random64, timeSeconds, 0, msgSeq, 0);
|
||||
}
|
||||
}
|
||||
|
||||
private static String b64encode(byte[] data) {
|
||||
return android.util.Base64.encodeToString(data, android.util.Base64.NO_WRAP);
|
||||
}
|
||||
|
||||
private native boolean nativeInitNtKernelRecallMsgHookV1p2();
|
||||
|
||||
private void onRevokeMsgLegacy(Object revokeMsgInfo) throws Exception {
|
||||
RevokeMsgInfoImpl info = new RevokeMsgInfoImpl((Parcelable) revokeMsgInfo);
|
||||
|
||||
@@ -37,8 +37,8 @@ import cc.ioctl.util.HookUtils;
|
||||
import cc.ioctl.util.HostInfo;
|
||||
import cc.ioctl.util.Reflex;
|
||||
import cc.ioctl.util.ui.FaultyDialog;
|
||||
import de.robv.android.xposed.XC_MethodHook;
|
||||
import de.robv.android.xposed.XposedBridge;
|
||||
import io.github.qauxv.util.xpcompat.XC_MethodHook;
|
||||
import io.github.qauxv.util.xpcompat.XposedBridge;
|
||||
import io.github.qauxv.R;
|
||||
import io.github.qauxv.activity.ShadowShareFileAgentActivity;
|
||||
import io.github.qauxv.base.annotation.FunctionHookEntry;
|
||||
|
||||
@@ -28,17 +28,20 @@ import cc.ioctl.util.HookUtils.BeforeAndAfterHookedMethod
|
||||
import cc.ioctl.util.HookUtils.hookBeforeAndAfterIfEnabled
|
||||
import cc.ioctl.util.LayoutHelper
|
||||
import cc.ioctl.util.hookBeforeIfEnabled
|
||||
import de.robv.android.xposed.XC_MethodHook.MethodHookParam
|
||||
import com.github.kyuubiran.ezxhelper.utils.hookAfter
|
||||
import io.github.qauxv.base.annotation.FunctionHookEntry
|
||||
import io.github.qauxv.base.annotation.UiItemAgentEntry
|
||||
import io.github.qauxv.dsl.FunctionEntryRouter
|
||||
import io.github.qauxv.hook.CommonSwitchFunctionHook
|
||||
import io.github.qauxv.util.Initiator
|
||||
import io.github.qauxv.util.QQVersion
|
||||
import io.github.qauxv.util.dexkit.AIOTitleVB_updateLeftTopBack_NT
|
||||
import io.github.qauxv.util.dexkit.CCustomWidgetUtil_updateCustomNoteTxt_NT
|
||||
import io.github.qauxv.util.dexkit.DexKit
|
||||
import io.github.qauxv.util.dexkit.NCustomWidgetUtil_updateCustomNoteTxt
|
||||
import io.github.qauxv.util.requireMinQQVersion
|
||||
import io.github.qauxv.util.xpcompat.XC_MethodHook.MethodHookParam
|
||||
import xyz.nextalone.util.get
|
||||
import xyz.nextalone.util.throwOrTrue
|
||||
|
||||
/**
|
||||
@@ -50,8 +53,9 @@ import xyz.nextalone.util.throwOrTrue
|
||||
@UiItemAgentEntry
|
||||
object ShowMsgCount : CommonSwitchFunctionHook(
|
||||
targets = arrayOf(
|
||||
NCustomWidgetUtil_updateCustomNoteTxt,
|
||||
CCustomWidgetUtil_updateCustomNoteTxt_NT,
|
||||
AIOTitleVB_updateLeftTopBack_NT,
|
||||
NCustomWidgetUtil_updateCustomNoteTxt,
|
||||
)
|
||||
) {
|
||||
|
||||
@@ -60,8 +64,8 @@ object ShowMsgCount : CommonSwitchFunctionHook(
|
||||
|
||||
override fun initOnce() = throwOrTrue {
|
||||
|
||||
// 群消息数量
|
||||
if (requireMinQQVersion(QQVersion.QQ_9_0_8)) {
|
||||
// 群消息数量 + 群聊左上角返回消息数量
|
||||
val clz = Initiator.loadClass("com.tencent.mobileqq.quibadge.QUIBadge")
|
||||
val (updateNumName, mNumName, mTextName) = if (requireMinQQVersion(QQVersion.QQ_9_0_15)) {
|
||||
Triple("updateNum", "mNum", "mText")
|
||||
@@ -78,6 +82,8 @@ object ShowMsgCount : CommonSwitchFunctionHook(
|
||||
param.result = null
|
||||
}
|
||||
} else {
|
||||
if (requireMinQQVersion(QQVersion.QQ_8_9_63)) {
|
||||
// 群消息数量
|
||||
val clz = DexKit.requireClassFromCache(CCustomWidgetUtil_updateCustomNoteTxt_NT)
|
||||
val updateNum = clz.declaredMethods.single { method ->
|
||||
val params = method.parameterTypes
|
||||
@@ -100,10 +106,29 @@ object ShowMsgCount : CommonSwitchFunctionHook(
|
||||
tv.layoutParams = lp
|
||||
}
|
||||
})
|
||||
// 群聊左上角返回消息数量
|
||||
DexKit.requireMethodFromCache(AIOTitleVB_updateLeftTopBack_NT).hookAfter {
|
||||
if (it.args[0] is Int) {
|
||||
val count = it.args[0] as Int
|
||||
if (count > 0) {
|
||||
val (mTitleBinding, unreadTv) = when {
|
||||
requireMinQQVersion(QQVersion.QQ_9_0_0) -> Pair("e", "v")
|
||||
requireMinQQVersion(QQVersion.QQ_8_9_80) -> Pair("e", "s")
|
||||
requireMinQQVersion(QQVersion.QQ_8_9_70) -> Pair("e", "t")
|
||||
requireMinQQVersion(QQVersion.QQ_8_9_63) -> Pair("e", "s")
|
||||
else -> Pair("", "")
|
||||
}
|
||||
if (mTitleBinding.isNotEmpty() && unreadTv.isNotEmpty()) {
|
||||
(it.thisObject.get(mTitleBinding).get(unreadTv) as TextView).text = count.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 总消息数量
|
||||
if (requireMinQQVersion(QQVersion.QQ_9_0_8)) {
|
||||
// 总消息数量
|
||||
val clz = DexKit.requireClassFromCache(NCustomWidgetUtil_updateCustomNoteTxt)
|
||||
val method = clz.declaredMethods.single { method ->
|
||||
val params = method.parameterTypes
|
||||
@@ -115,6 +140,7 @@ object ShowMsgCount : CommonSwitchFunctionHook(
|
||||
param.args[3] = Int.MAX_VALUE
|
||||
}
|
||||
} else {
|
||||
// 总消息数量(QQ[9.0.8]之前) + 群消息数量(QQNT[8.9.63]之前)
|
||||
val clz = DexKit.requireClassFromCache(NCustomWidgetUtil_updateCustomNoteTxt)
|
||||
val method = clz.declaredMethods.single { method ->
|
||||
val params = method.parameterTypes
|
||||
@@ -136,11 +162,11 @@ object ShowMsgCount : CommonSwitchFunctionHook(
|
||||
}
|
||||
|
||||
override fun afterHookedMethod(param: MethodHookParam) {
|
||||
val tv = param.args[0] as TextView
|
||||
tv.maxWidth = Int.MAX_VALUE
|
||||
val lp = tv.layoutParams
|
||||
lp.width = ViewGroup.LayoutParams.WRAP_CONTENT
|
||||
tv.layoutParams = lp
|
||||
(param.args[0] as TextView).apply {
|
||||
maxWidth = Int.MAX_VALUE
|
||||
layoutParams.width = ViewGroup.LayoutParams.WRAP_CONTENT
|
||||
setPadding(0, 0, 0, 0)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ package cc.ioctl.hook.notification
|
||||
|
||||
import cc.ioctl.util.Reflex
|
||||
import cc.ioctl.util.msg.MessageManager
|
||||
import de.robv.android.xposed.XC_MethodHook
|
||||
import io.github.qauxv.util.xpcompat.XC_MethodHook
|
||||
import io.github.qauxv.base.annotation.FunctionHookEntry
|
||||
import io.github.qauxv.hook.BasePersistBackgroundHook
|
||||
import io.github.qauxv.util.Initiator
|
||||
|
||||
@@ -29,7 +29,7 @@ import androidx.annotation.NonNull;
|
||||
import cc.ioctl.util.HookUtils;
|
||||
import cc.ioctl.util.HostInfo;
|
||||
import cc.ioctl.util.Reflex;
|
||||
import de.robv.android.xposed.XposedBridge;
|
||||
import io.github.qauxv.util.xpcompat.XposedBridge;
|
||||
import io.github.qauxv.base.annotation.FunctionHookEntry;
|
||||
import io.github.qauxv.base.annotation.UiItemAgentEntry;
|
||||
import io.github.qauxv.dsl.FunctionEntryRouter.Locations.Auxiliary;
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* QAuxiliary - An Xposed module for QQ/TIM
|
||||
* Copyright (C) 2019-2024 QAuxiliary developers
|
||||
* https://github.com/cinit/QAuxiliary
|
||||
*
|
||||
* This software is an opensource software: you can redistribute it
|
||||
* and/or modify it under the terms of the General Public License
|
||||
* as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version as published
|
||||
* by QAuxiliary contributors.
|
||||
*
|
||||
* This software 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the General Public License
|
||||
* along with this software.
|
||||
* If not, see
|
||||
* <https://github.com/cinit/QAuxiliary/blob/master/LICENSE.md>.
|
||||
*/
|
||||
|
||||
package cc.ioctl.hook.profile
|
||||
|
||||
import android.app.Activity
|
||||
import cc.hicore.QApp.QAppUtils
|
||||
import com.github.kyuubiran.ezxhelper.utils.ArgTypes
|
||||
import com.github.kyuubiran.ezxhelper.utils.Args
|
||||
import com.github.kyuubiran.ezxhelper.utils.hookBefore
|
||||
import com.github.kyuubiran.ezxhelper.utils.newInstance
|
||||
import com.xiaoniu.util.ContextUtils
|
||||
import io.github.qauxv.R
|
||||
import io.github.qauxv.base.annotation.FunctionHookEntry
|
||||
import io.github.qauxv.base.annotation.UiItemAgentEntry
|
||||
import io.github.qauxv.dsl.FunctionEntryRouter
|
||||
import io.github.qauxv.hook.CommonSwitchFunctionHook
|
||||
import io.github.qauxv.ui.CommonContextWrapper
|
||||
import io.github.qauxv.util.Initiator
|
||||
import io.github.qauxv.util.dexkit.DexKit
|
||||
import io.github.qauxv.util.dexkit.RecentPopup_onClickAction
|
||||
import xyz.nextalone.util.get
|
||||
import xyz.nextalone.util.throwOrTrue
|
||||
|
||||
@FunctionHookEntry
|
||||
@UiItemAgentEntry
|
||||
object OpenProfileCardMenu : CommonSwitchFunctionHook(
|
||||
targets = arrayOf(RecentPopup_onClickAction)
|
||||
) {
|
||||
override val name = "打开资料卡"
|
||||
override val description = "在首页加号菜单中添加打开资料卡功能"
|
||||
override val uiItemLocation = FunctionEntryRouter.Locations.Auxiliary.PROFILE_CATEGORY
|
||||
override val isAvailable = QAppUtils.isQQnt()
|
||||
|
||||
private fun context() = CommonContextWrapper.createMaterialDesignContext(ContextUtils.getCurrentActivity())
|
||||
|
||||
override fun initOnce() = throwOrTrue {
|
||||
val menuItemId = 414414
|
||||
val entryMenuItem = Initiator.loadClass("com.tencent.widget.PopupMenuDialog\$MenuItem").newInstance(
|
||||
Args(arrayOf(menuItemId, "打开资料卡", "打开资料卡", R.drawable.ic_item_tool_72dp)),
|
||||
ArgTypes(arrayOf(Int::class.java, String::class.java, String::class.java, Int::class.java))
|
||||
)!!
|
||||
Initiator.loadClass("com.tencent.widget.PopupMenuDialog").getDeclaredMethod(
|
||||
"conversationPlusBuild",
|
||||
Activity::class.java,
|
||||
List::class.java,
|
||||
Initiator.loadClass("com.tencent.widget.PopupMenuDialog\$OnClickActionListener"),
|
||||
Initiator.loadClass("com.tencent.widget.PopupMenuDialog\$OnDismissListener")
|
||||
).hookBefore {
|
||||
it.args[1] = it.args[1] as List<*> + listOf(entryMenuItem)
|
||||
}
|
||||
DexKit.requireMethodFromCache(RecentPopup_onClickAction).hookBefore {
|
||||
if (it.args[0].get("id") == menuItemId) {
|
||||
OpenProfileCard.onClick(context())
|
||||
it.result = null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -30,7 +30,7 @@ import cc.ioctl.util.Reflex
|
||||
import cc.ioctl.util.hookAfterIfEnabled
|
||||
import cc.ioctl.util.hookBeforeIfEnabled
|
||||
import com.github.kyuubiran.ezxhelper.utils.hookBefore
|
||||
import de.robv.android.xposed.XC_MethodHook.MethodHookParam
|
||||
import io.github.qauxv.util.xpcompat.XC_MethodHook.MethodHookParam
|
||||
import io.github.qauxv.base.annotation.FunctionHookEntry
|
||||
import io.github.qauxv.base.annotation.UiItemAgentEntry
|
||||
import io.github.qauxv.dsl.FunctionEntryRouter
|
||||
|
||||
@@ -26,6 +26,7 @@ import android.view.ViewGroup
|
||||
import android.view.ViewTreeObserver
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.TextView
|
||||
import androidx.core.view.children
|
||||
import androidx.core.view.forEach
|
||||
import androidx.core.view.forEachIndexed
|
||||
import androidx.core.view.get
|
||||
@@ -38,8 +39,6 @@ import com.github.kyuubiran.ezxhelper.utils.hookAfter
|
||||
import com.github.kyuubiran.ezxhelper.utils.hookReturnConstant
|
||||
import com.github.kyuubiran.ezxhelper.utils.paramCount
|
||||
import com.github.kyuubiran.ezxhelper.utils.setViewZeroSize
|
||||
import de.robv.android.xposed.XC_MethodReplacement
|
||||
import de.robv.android.xposed.XposedBridge
|
||||
import io.github.qauxv.base.annotation.FunctionHookEntry
|
||||
import io.github.qauxv.base.annotation.UiItemAgentEntry
|
||||
import io.github.qauxv.dsl.FunctionEntryRouter
|
||||
@@ -59,6 +58,8 @@ import io.github.qauxv.util.dexkit.QQSettingMeABTestHelper_isZplanExpGroup_Old
|
||||
import io.github.qauxv.util.dexkit.QQ_SETTING_ME_CONFIG_CLASS
|
||||
import io.github.qauxv.util.hostInfo
|
||||
import io.github.qauxv.util.requireMinQQVersion
|
||||
import io.github.qauxv.util.xpcompat.XC_MethodReplacement
|
||||
import io.github.qauxv.util.xpcompat.XposedBridge
|
||||
import xyz.nextalone.base.MultiItemDelayableHook
|
||||
import xyz.nextalone.util.*
|
||||
import java.lang.reflect.Array
|
||||
@@ -284,6 +285,19 @@ object SimplifyQQSettingMe :
|
||||
}
|
||||
}
|
||||
|
||||
// QQ 9.0.80 在xml布局中固定了14个item,这里将上面步骤完成后还未初始化的item隐藏
|
||||
if (requireMinQQVersion(QQVersion.QQ_9_0_80)) {
|
||||
"com.tencent.mobileqq.bizParts.QQSettingMeMenuPanelPart".clazz!!.method("onInitView")!!.hookAfter { param ->
|
||||
// val parent=param.thisObject.javaClass.declaredFields.first {
|
||||
// it.javaClass==ViewGroup::class.java && (it.get(param.thisObject) as ViewGroup).childCount>=14
|
||||
// }.get(param.thisObject) as ViewGroup
|
||||
val parent = param.thisObject.get("h") as ViewGroup
|
||||
parent.children.forEach {
|
||||
if (!it.isClickable) it.setViewZeroSize()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (requireMinQQVersion(QQVersion.QQ_9_0_0)) {
|
||||
// View层隐藏超级QQ秀 (到这里应该只需要处理 超级QQ秀 了
|
||||
// 9.0.20起此段代码无效果,且该版本起超级QQ秀可以在上面移除,所以不再执行
|
||||
|
||||
@@ -24,7 +24,7 @@ package cc.ioctl.hook.troop
|
||||
|
||||
import android.view.View
|
||||
import cc.ioctl.util.Reflex
|
||||
import de.robv.android.xposed.XC_MethodHook
|
||||
import io.github.qauxv.util.xpcompat.XC_MethodHook
|
||||
import io.github.qauxv.base.annotation.FunctionHookEntry
|
||||
import io.github.qauxv.base.annotation.UiItemAgentEntry
|
||||
import io.github.qauxv.dsl.FunctionEntryRouter
|
||||
|
||||
@@ -24,8 +24,8 @@ package cc.ioctl.hook.ui.chat;
|
||||
import static io.github.qauxv.util.Initiator.load;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import de.robv.android.xposed.XC_MethodHook;
|
||||
import de.robv.android.xposed.XposedHelpers;
|
||||
import io.github.qauxv.util.xpcompat.XC_MethodHook;
|
||||
import io.github.qauxv.util.xpcompat.XposedHelpers;
|
||||
import io.github.qauxv.base.annotation.FunctionHookEntry;
|
||||
import io.github.qauxv.base.annotation.UiItemAgentEntry;
|
||||
import io.github.qauxv.dsl.FunctionEntryRouter.Locations.Simplify;
|
||||
|
||||
@@ -52,7 +52,7 @@ import cc.ioctl.util.HookUtils.BeforeAndAfterHookedMethod;
|
||||
import cc.ioctl.util.HostInfo;
|
||||
import cc.ioctl.util.LayoutHelper;
|
||||
import cc.ioctl.util.Reflex;
|
||||
import de.robv.android.xposed.XC_MethodHook;
|
||||
import io.github.qauxv.util.xpcompat.XC_MethodHook;
|
||||
import io.github.qauxv.base.ISwitchCellAgent;
|
||||
import io.github.qauxv.base.IUiItemAgent;
|
||||
import io.github.qauxv.base.annotation.FunctionHookEntry;
|
||||
|
||||
@@ -25,8 +25,8 @@ import com.github.kyuubiran.ezxhelper.utils.findMethod
|
||||
import com.github.kyuubiran.ezxhelper.utils.hookAfter
|
||||
import com.github.kyuubiran.ezxhelper.utils.hookAllConstructorAfter
|
||||
import com.github.kyuubiran.ezxhelper.utils.paramCount
|
||||
import de.robv.android.xposed.XC_MethodReplacement
|
||||
import de.robv.android.xposed.XposedHelpers
|
||||
import io.github.qauxv.util.xpcompat.XC_MethodReplacement
|
||||
import io.github.qauxv.util.xpcompat.XposedHelpers
|
||||
import io.github.qauxv.base.annotation.FunctionHookEntry
|
||||
import io.github.qauxv.base.annotation.UiItemAgentEntry
|
||||
import io.github.qauxv.config.ConfigItems
|
||||
|
||||
@@ -29,7 +29,7 @@ import android.view.View
|
||||
import androidx.appcompat.widget.AppCompatCheckBox
|
||||
import androidx.appcompat.widget.AppCompatTextView
|
||||
import cc.ioctl.util.HookUtils
|
||||
import de.robv.android.xposed.XposedBridge
|
||||
import io.github.qauxv.util.xpcompat.XposedBridge
|
||||
import io.github.qauxv.base.IUiItemAgent
|
||||
import io.github.qauxv.base.annotation.FunctionHookEntry
|
||||
import io.github.qauxv.base.annotation.UiItemAgentEntry
|
||||
|
||||
@@ -36,8 +36,8 @@ import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import cc.ioctl.util.HostInfo;
|
||||
import cc.ioctl.util.LayoutHelper;
|
||||
import de.robv.android.xposed.XC_MethodHook;
|
||||
import de.robv.android.xposed.XposedBridge;
|
||||
import io.github.qauxv.util.xpcompat.XC_MethodHook;
|
||||
import io.github.qauxv.util.xpcompat.XposedBridge;
|
||||
import io.github.qauxv.base.IUiItemAgent;
|
||||
import io.github.qauxv.base.annotation.FunctionHookEntry;
|
||||
import io.github.qauxv.base.annotation.UiItemAgentEntry;
|
||||
|
||||
@@ -28,8 +28,8 @@ import androidx.annotation.NonNull;
|
||||
import cc.hicore.QApp.QAppUtils;
|
||||
import cc.ioctl.util.HookUtils;
|
||||
import com.tencent.qqnt.kernel.nativeinterface.VASMsgAvatarPendant;
|
||||
import de.robv.android.xposed.XC_MethodHook;
|
||||
import de.robv.android.xposed.XposedBridge;
|
||||
import io.github.qauxv.util.xpcompat.XC_MethodHook;
|
||||
import io.github.qauxv.util.xpcompat.XposedBridge;
|
||||
import io.github.qauxv.base.annotation.FunctionHookEntry;
|
||||
import io.github.qauxv.base.annotation.UiItemAgentEntry;
|
||||
import io.github.qauxv.dsl.FunctionEntryRouter.Locations.Simplify;
|
||||
|
||||
@@ -29,7 +29,7 @@ import com.github.kyuubiran.ezxhelper.utils.findMethod
|
||||
import com.github.kyuubiran.ezxhelper.utils.getObjectAs
|
||||
import com.github.kyuubiran.ezxhelper.utils.hookAfter
|
||||
import com.github.kyuubiran.ezxhelper.utils.setViewZeroSize
|
||||
import de.robv.android.xposed.XposedBridge
|
||||
import io.github.qauxv.util.xpcompat.XposedBridge
|
||||
import io.github.qauxv.base.annotation.FunctionHookEntry
|
||||
import io.github.qauxv.base.annotation.UiItemAgentEntry
|
||||
import io.github.qauxv.dsl.FunctionEntryRouter
|
||||
|
||||
@@ -24,8 +24,8 @@ package cc.ioctl.hook.ui.title;
|
||||
import android.view.View;
|
||||
import androidx.annotation.NonNull;
|
||||
import cc.ioctl.util.HookUtils;
|
||||
import de.robv.android.xposed.XC_MethodReplacement;
|
||||
import de.robv.android.xposed.XposedBridge;
|
||||
import io.github.qauxv.util.xpcompat.XC_MethodReplacement;
|
||||
import io.github.qauxv.util.xpcompat.XposedBridge;
|
||||
import io.github.qauxv.base.annotation.FunctionHookEntry;
|
||||
import io.github.qauxv.base.annotation.UiItemAgentEntry;
|
||||
import io.github.qauxv.dsl.FunctionEntryRouter.Locations.Simplify;
|
||||
|
||||
@@ -23,8 +23,8 @@ package cc.ioctl.util;
|
||||
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import de.robv.android.xposed.XC_MethodHook;
|
||||
import de.robv.android.xposed.XC_MethodHook.MethodHookParam;
|
||||
import io.github.qauxv.util.xpcompat.XC_MethodHook;
|
||||
import io.github.qauxv.util.xpcompat.XC_MethodHook.MethodHookParam;
|
||||
import io.github.qauxv.util.Log;
|
||||
import io.github.qauxv.util.dexkit.DexMethodDescriptor;
|
||||
import java.io.File;
|
||||
|
||||
@@ -93,13 +93,46 @@ public class ExfriendManager {
|
||||
private boolean dirtySerializedFlag = true;
|
||||
|
||||
private static final long[] ROBOT_ENTERPRISE_UIN_ARRAY = new long[]{
|
||||
66600000L, // BabyQ
|
||||
2854196306L, // 小冰
|
||||
// 查询ROBOT信息 https://qun.qq.com/qunpro/robot/qunshare?robot_uin=66600000
|
||||
66600000L, // babyQ
|
||||
2854196925L, // QQ小店助手
|
||||
2854202683L, // 游戏助手
|
||||
2854204259L, // 赞噢机器人
|
||||
2854196925L, // 小店助手
|
||||
2854211892L,
|
||||
2854202683L,
|
||||
2854209338L, // 频道管理助手
|
||||
2854211892L, // 饭团团
|
||||
// 热门
|
||||
3889031420L, // 庆余年 | 庆帝
|
||||
3889019833L, // 鹅探长
|
||||
2854208500L, // 修仙之路
|
||||
2854202509L, // 饲养小猫
|
||||
2854197266L, // AL_1S
|
||||
2854214035L, // 心情复杂
|
||||
2854196310L, // Q群管家
|
||||
2854203763L, // 小YOYO
|
||||
// 游戏
|
||||
3889011373L, // 武林侠影
|
||||
3889017942L, // 快说喜欢我
|
||||
2854211478L, // 小念同学
|
||||
2854198976L, // 小德娱乐菌
|
||||
3889009909L, // 钓鱼达人
|
||||
2854196306L, // 小冰
|
||||
3889001741L, // 小小
|
||||
// 娱乐
|
||||
2854197548L, // 开心农场
|
||||
3889000472L, // 麦麦子MaiBot
|
||||
3889001044L, // 益智扫雷
|
||||
2854207033L, // 房东人生
|
||||
2854202692L, // 元梦甜橙喵
|
||||
// 工具
|
||||
2854203783L, // 阿罗娜小助手
|
||||
2854213832L, // 小虾米
|
||||
3889001607L, // 海兰德小助手
|
||||
3889000871L, // 战地1小电视
|
||||
2854196311L, // 王者荣耀小狐狸
|
||||
2854207085L, // DNF手游-赛丽亚
|
||||
2854196316L, // 和平精英-小几
|
||||
2854212997L, // 机器人66
|
||||
2854203945L, // 暗区突围老皮
|
||||
};
|
||||
|
||||
private ExfriendManager(long uin) {
|
||||
|
||||
@@ -23,8 +23,8 @@
|
||||
package cc.ioctl.util;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import de.robv.android.xposed.XC_MethodHook;
|
||||
import de.robv.android.xposed.XposedBridge;
|
||||
import io.github.qauxv.util.xpcompat.XC_MethodHook;
|
||||
import io.github.qauxv.util.xpcompat.XposedBridge;
|
||||
import io.github.qauxv.base.ITraceableDynamicHook;
|
||||
import io.github.qauxv.hook.BaseFunctionHook;
|
||||
import io.github.qauxv.hook.BaseHookDispatcher;
|
||||
|
||||
@@ -23,8 +23,8 @@
|
||||
package cc.ioctl.util
|
||||
|
||||
import dalvik.system.BaseDexClassLoader
|
||||
import de.robv.android.xposed.XC_MethodHook
|
||||
import de.robv.android.xposed.XposedBridge
|
||||
import io.github.qauxv.util.xpcompat.XC_MethodHook
|
||||
import io.github.qauxv.util.xpcompat.XposedBridge
|
||||
import io.github.qauxv.hook.BaseFunctionHook
|
||||
import io.github.qauxv.util.LicenseStatus
|
||||
import java.lang.reflect.Constructor
|
||||
|
||||
@@ -24,7 +24,7 @@ package cc.ioctl.util;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import de.robv.android.xposed.XposedBridge;
|
||||
import io.github.qauxv.util.xpcompat.XposedBridge;
|
||||
import io.github.qauxv.util.Natives;
|
||||
import io.github.qauxv.util.dexkit.DexMethodDescriptor;
|
||||
import java.lang.reflect.Constructor;
|
||||
|
||||
@@ -110,7 +110,7 @@ object ImageCustomSummary : CommonConfigFunctionHook("ImageCustomSummary", array
|
||||
@SuppressLint("SetTextI18n")
|
||||
private fun showConfigDialog(ctx: Context) {
|
||||
val switchEnable = SwitchCompat(ctx).apply {
|
||||
isChecked = isEnabled
|
||||
isChecked = this@ImageCustomSummary.isEnabled
|
||||
textSize = 16f
|
||||
text = "功能总开关"
|
||||
}
|
||||
@@ -179,6 +179,9 @@ object ImageCustomSummary : CommonConfigFunctionHook("ImageCustomSummary", array
|
||||
setView(rootLayout)
|
||||
setPositiveButton("确定") { _, _ ->
|
||||
isEnabled = switchEnable.isChecked
|
||||
typePic0 = checkBoxTypePic0.isChecked
|
||||
typePic1247 = checkBoxTypePic1247.isChecked
|
||||
typeMarketFace = checkBoxTypeMarketFace.isChecked
|
||||
summaryText = summaryTextEdit.text.toString()
|
||||
valueState.update { if (isEnabled) "已开启" else "禁用" }
|
||||
}
|
||||
@@ -191,7 +194,6 @@ object ImageCustomSummary : CommonConfigFunctionHook("ImageCustomSummary", array
|
||||
val sendMsgMethod = Initiator.loadClass("com.tencent.qqnt.kernel.nativeinterface.IKernelMsgService\$CppProxy").method("sendMsg")!!
|
||||
hookBeforeIfEnabled(sendMsgMethod) { param ->
|
||||
val contact = ContactCompat.fromKernelObject(param.args[1] as Serializable)
|
||||
val chatType = contact.chatType
|
||||
val elements = param.args[2] as ArrayList<*>
|
||||
for (element in elements) {
|
||||
val msgElement = (element as MsgElement)
|
||||
@@ -202,9 +204,7 @@ object ImageCustomSummary : CommonConfigFunctionHook("ImageCustomSummary", array
|
||||
}
|
||||
if (typePic1247 && picSubType != 0) {
|
||||
picElement.summary = summaryText
|
||||
if (chatType != 4) { // 不是频道才修改类型, 不然会发送不出去
|
||||
picElement.picSubType = 7
|
||||
}
|
||||
if (contact.chatType != 4) picElement.picSubType = 7
|
||||
}
|
||||
}
|
||||
msgElement.marketFaceElement?.let { marketFaceElement ->
|
||||
|
||||
@@ -23,7 +23,7 @@ package cn.lliiooll.hook
|
||||
|
||||
import cc.ioctl.hook.notification.MessageInterception
|
||||
import cc.ioctl.util.msg.MessageReceiver
|
||||
import de.robv.android.xposed.XposedHelpers
|
||||
import io.github.qauxv.util.xpcompat.XposedHelpers
|
||||
import io.github.qauxv.base.annotation.FunctionHookEntry
|
||||
import io.github.qauxv.base.annotation.UiItemAgentEntry
|
||||
import io.github.qauxv.bridge.AppRuntimeHelper
|
||||
|
||||
@@ -5,8 +5,8 @@ import android.view.View;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import cc.ioctl.util.HookUtils;
|
||||
import de.robv.android.xposed.XC_MethodHook;
|
||||
import de.robv.android.xposed.XposedBridge;
|
||||
import io.github.qauxv.util.xpcompat.XC_MethodHook;
|
||||
import io.github.qauxv.util.xpcompat.XposedBridge;
|
||||
import io.github.qauxv.base.annotation.FunctionHookEntry;
|
||||
import io.github.qauxv.base.annotation.UiItemAgentEntry;
|
||||
import io.github.qauxv.dsl.FunctionEntryRouter;
|
||||
|
||||
@@ -4,8 +4,8 @@ import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import cc.ioctl.util.HookUtils;
|
||||
import cc.ioctl.util.Reflex;
|
||||
import de.robv.android.xposed.XC_MethodHook;
|
||||
import de.robv.android.xposed.XposedBridge;
|
||||
import io.github.qauxv.util.xpcompat.XC_MethodHook;
|
||||
import io.github.qauxv.util.xpcompat.XposedBridge;
|
||||
import io.github.qauxv.base.annotation.FunctionHookEntry;
|
||||
import io.github.qauxv.base.annotation.UiItemAgentEntry;
|
||||
import io.github.qauxv.dsl.FunctionEntryRouter;
|
||||
|
||||
@@ -30,14 +30,18 @@ import cc.ioctl.hook.msg.PicMd5Hook
|
||||
import cc.ioctl.hook.msg.PttForwardHook
|
||||
import cc.ioctl.util.HookUtils
|
||||
import com.github.kyuubiran.ezxhelper.utils.isAbstract
|
||||
import de.robv.android.xposed.XC_MethodHook
|
||||
import io.github.qauxv.util.xpcompat.XC_MethodHook
|
||||
import io.github.duzhaokun123.hook.MessageCopyHook
|
||||
import io.github.duzhaokun123.hook.MessageTTSHook
|
||||
import io.github.qauxv.base.annotation.FunctionHookEntry
|
||||
import io.github.qauxv.hook.BasePersistBackgroundHook
|
||||
import io.github.qauxv.util.Initiator
|
||||
import me.hd.hook.CopyMarkdown
|
||||
import me.hd.hook.menu.CopyMarkdown
|
||||
import me.hd.hook.menu.EditTextContent
|
||||
import me.hd.hook.menu.RecallMsgRecord
|
||||
import me.hd.hook.menu.RepeatToImg
|
||||
import me.ketal.hook.PicCopyToClipboard
|
||||
import me.qcuncle.hook.TranslateTextMsg
|
||||
import top.xunflash.hook.MiniAppDirectJump
|
||||
import java.lang.reflect.Method
|
||||
|
||||
@@ -55,6 +59,10 @@ object MenuBuilderHook : BasePersistBackgroundHook() {
|
||||
MiniAppDirectJump,
|
||||
CopyMarkdown,
|
||||
MessageTTSHook,
|
||||
EditTextContent,
|
||||
TranslateTextMsg,
|
||||
RecallMsgRecord,
|
||||
RepeatToImg,
|
||||
)
|
||||
|
||||
override fun initOnce(): Boolean {
|
||||
|
||||
@@ -32,23 +32,21 @@ import io.github.qauxv.hook.CommonSwitchFunctionHook
|
||||
import io.github.qauxv.util.QQVersion
|
||||
import io.github.qauxv.util.dexkit.DexKit
|
||||
import io.github.qauxv.util.dexkit.TroopInfoCardPageABConfig
|
||||
import io.github.qauxv.util.requireMinQQVersion
|
||||
import io.github.qauxv.util.hostInfo
|
||||
import xyz.nextalone.util.throwOrTrue
|
||||
|
||||
@FunctionHookEntry
|
||||
@UiItemAgentEntry
|
||||
object DisableNewTroopInfoPage : CommonSwitchFunctionHook(arrayOf(TroopInfoCardPageABConfig)) {
|
||||
|
||||
override val name = "禁用新版群资料页"
|
||||
|
||||
override val description = "新版群资料页功能缺失,中看不中用,遂禁用之"
|
||||
|
||||
override val isAvailable = requireMinQQVersion(QQVersion.QQ_8_9_78)
|
||||
override val uiItemLocation = FunctionEntryRouter.Locations.Auxiliary.GROUP_CATEGORY
|
||||
override val isAvailable = hostInfo.versionCode in QQVersion.QQ_8_9_78..QQVersion.QQ_9_0_71
|
||||
|
||||
override fun initOnce() = throwOrTrue {
|
||||
DexKit.requireClassFromCache(TroopInfoCardPageABConfig).findMethod {
|
||||
returnType == Boolean::class.java && paramCount == 0
|
||||
}.hookReturnConstant(false)
|
||||
}
|
||||
|
||||
override val uiItemLocation = FunctionEntryRouter.Locations.Auxiliary.GROUP_CATEGORY
|
||||
}
|
||||
@@ -24,7 +24,7 @@ package com.xiaoniu.hook
|
||||
import android.content.Context
|
||||
import cc.ioctl.util.HostInfo.isQQ
|
||||
import cc.ioctl.util.afterHookIfEnabled
|
||||
import de.robv.android.xposed.XposedHelpers
|
||||
import io.github.qauxv.util.xpcompat.XposedHelpers
|
||||
import io.github.qauxv.base.annotation.FunctionHookEntry
|
||||
import io.github.qauxv.base.annotation.UiItemAgentEntry
|
||||
import io.github.qauxv.dsl.FunctionEntryRouter.Locations.Auxiliary
|
||||
|
||||
@@ -339,7 +339,7 @@ object TroopGroupHook : CommonSwitchFunctionHook(arrayOf(RecentPopup_onClickActi
|
||||
override fun initOnce() = throwOrTrue {
|
||||
val itemClazz = "Lcom/tencent/widget/PopupMenuDialog\$MenuItem;".clazz!!
|
||||
val entryItem = itemClazz.newInstance(
|
||||
Args(arrayOf(415411, "群聊分组", "群聊分组", R.drawable.ic_troop_group)),
|
||||
Args(arrayOf(415411, "群聊分组", "群聊分组", R.drawable.ic_item_troop_group_72dp)),
|
||||
ArgTypes(arrayOf(Int::class.java, String::class.java, String::class.java, Int::class.java))
|
||||
)!!
|
||||
"Lcom/tencent/widget/PopupMenuDialog;->conversationPlusBuild(Landroid/app/Activity;Ljava/util/List;Lcom/tencent/widget/PopupMenuDialog\$OnClickActionListener;Lcom/tencent/widget/PopupMenuDialog\$OnDismissListener;)Lcom/tencent/widget/PopupMenuDialog;".method.hookBefore {
|
||||
|
||||
@@ -34,9 +34,9 @@ import cc.ioctl.util.Reflex
|
||||
import cc.ioctl.util.afterHookIfEnabled
|
||||
import com.xiaoniu.dispatcher.OnMenuBuilder
|
||||
import com.xiaoniu.util.ContextUtils
|
||||
import de.robv.android.xposed.XC_MethodHook
|
||||
import de.robv.android.xposed.XposedBridge
|
||||
import de.robv.android.xposed.XposedHelpers
|
||||
import io.github.qauxv.util.xpcompat.XC_MethodHook
|
||||
import io.github.qauxv.util.xpcompat.XposedBridge
|
||||
import io.github.qauxv.util.xpcompat.XposedHelpers
|
||||
import io.github.qauxv.R
|
||||
import io.github.qauxv.base.annotation.FunctionHookEntry
|
||||
import io.github.qauxv.base.annotation.UiItemAgentEntry
|
||||
|
||||
@@ -32,9 +32,9 @@ import cc.ioctl.util.Reflex
|
||||
import cc.ioctl.util.afterHookIfEnabled
|
||||
import com.xiaoniu.dispatcher.OnMenuBuilder
|
||||
import com.xiaoniu.util.ContextUtils
|
||||
import de.robv.android.xposed.XC_MethodHook
|
||||
import de.robv.android.xposed.XposedBridge
|
||||
import de.robv.android.xposed.XposedHelpers
|
||||
import io.github.qauxv.util.xpcompat.XC_MethodHook
|
||||
import io.github.qauxv.util.xpcompat.XposedBridge
|
||||
import io.github.qauxv.util.xpcompat.XposedHelpers
|
||||
import io.github.duzhaokun123.hook.MessageCopyHook.TAG
|
||||
import io.github.duzhaokun123.util.TTS
|
||||
import io.github.qauxv.R
|
||||
@@ -132,7 +132,7 @@ object MessageTTSHook : CommonSwitchFunctionHook(), OnMenuBuilder, DexKitFinder
|
||||
|
||||
override fun onGetMenuNt(msg: Any, componentType: String, param: XC_MethodHook.MethodHookParam) {
|
||||
if (!isEnabled) return
|
||||
val item = CustomMenu.createItemIconNt(msg, "TTS", 0, R.id.item_tts) {
|
||||
val item = CustomMenu.createItemIconNt(msg, "TTS", R.drawable.ic_item_tts_72dp, R.id.item_tts) {
|
||||
val ctx = ContextUtils.getCurrentActivity()
|
||||
val wc = CommonContextWrapper.createAppCompatContext(ctx)
|
||||
val text = try {
|
||||
@@ -144,7 +144,7 @@ object MessageTTSHook : CommonSwitchFunctionHook(), OnMenuBuilder, DexKitFinder
|
||||
}
|
||||
TTS.speak(wc, text.toString())
|
||||
}
|
||||
val item2 = CustomMenu.createItemIconNt(msg, "TTS+", 0, R.id.item_tts2) {
|
||||
val item2 = CustomMenu.createItemIconNt(msg, "TTS+", R.drawable.ic_item_tts_plus_72dp, R.id.item_tts2) {
|
||||
val ctx = ContextUtils.getCurrentActivity()
|
||||
val wc = CommonContextWrapper.createAppCompatContext(ctx)
|
||||
val text = try {
|
||||
|
||||
@@ -26,7 +26,7 @@ import android.view.View
|
||||
import cc.ioctl.util.HookUtils.hookBeforeIfEnabled
|
||||
import cc.ioctl.util.hookBeforeIfEnabled
|
||||
import com.alphi.qhmk.module.HiddenVipIconForSe
|
||||
import de.robv.android.xposed.XC_MethodHook.MethodHookParam
|
||||
import io.github.qauxv.util.xpcompat.XC_MethodHook.MethodHookParam
|
||||
import io.github.qauxv.base.annotation.FunctionHookEntry
|
||||
import io.github.qauxv.base.annotation.UiItemAgentEntry
|
||||
import io.github.qauxv.dsl.FunctionEntryRouter
|
||||
|
||||
@@ -25,7 +25,7 @@ package io.github.fusumayuki.hook
|
||||
import android.view.View
|
||||
import android.view.ViewStub
|
||||
import cc.ioctl.util.HookUtils.hookBeforeIfEnabled
|
||||
import de.robv.android.xposed.XC_MethodHook.MethodHookParam
|
||||
import io.github.qauxv.util.xpcompat.XC_MethodHook.MethodHookParam
|
||||
import io.github.qauxv.base.annotation.FunctionHookEntry
|
||||
import io.github.qauxv.base.annotation.UiItemAgentEntry
|
||||
import io.github.qauxv.dsl.FunctionEntryRouter
|
||||
|
||||
56
app/src/main/java/io/github/memory2314/hook/GuildOldStyle.kt
Normal file
56
app/src/main/java/io/github/memory2314/hook/GuildOldStyle.kt
Normal file
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* QAuxiliary - An Xposed module for QQ/TIM
|
||||
* Copyright (C) 2019-2024 QAuxiliary developers
|
||||
* https://github.com/cinit/QAuxiliary
|
||||
*
|
||||
* This software is an opensource software: you can redistribute it
|
||||
* and/or modify it under the terms of the General Public License
|
||||
* as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version as published
|
||||
* by QAuxiliary contributors.
|
||||
*
|
||||
* This software 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the General Public License
|
||||
* along with this software.
|
||||
* If not, see
|
||||
* <https://github.com/cinit/QAuxiliary/blob/master/LICENSE.md>.
|
||||
*/
|
||||
|
||||
package io.github.memory2314.hook
|
||||
|
||||
import cc.ioctl.util.hookBeforeIfEnabled
|
||||
import io.github.qauxv.base.annotation.FunctionHookEntry
|
||||
import io.github.qauxv.base.annotation.UiItemAgentEntry
|
||||
import io.github.qauxv.dsl.FunctionEntryRouter
|
||||
import io.github.qauxv.hook.CommonSwitchFunctionHook
|
||||
import io.github.qauxv.util.QQVersion
|
||||
import io.github.qauxv.util.hostInfo
|
||||
import io.github.qauxv.util.requireMinQQVersion
|
||||
import xyz.nextalone.util.invoke
|
||||
import xyz.nextalone.util.method
|
||||
import xyz.nextalone.util.throwOrTrue
|
||||
|
||||
@FunctionHookEntry
|
||||
@UiItemAgentEntry
|
||||
object GuildOldStyle : CommonSwitchFunctionHook() {
|
||||
override val name = "频道旧版样式"
|
||||
override val description = "仅支持QQ9.0.15~9.0.73版本"
|
||||
override val uiItemLocation = FunctionEntryRouter.Locations.Auxiliary.GUILD_CATEGORY
|
||||
override val isAvailable = hostInfo.versionCode in QQVersion.QQ_9_0_15..QQVersion.QQ_9_0_73
|
||||
|
||||
override fun initOnce() = throwOrTrue {
|
||||
if (requireMinQQVersion(QQVersion.QQ_9_0_30)) { // 9.0.30~9.0.73(之后的版本获取布局方法不存在)
|
||||
hookBeforeIfEnabled("Lcom/tencent/mobileqq/guild/mainframe/GuildFragmentDelegateFrame;->getCurrentMainFragment()Lcom/tencent/mobileqq/guild/mainframe/AbsGuildMainFragment;".method) {
|
||||
it.result = it.thisObject.invoke("getMainFrameFragment")
|
||||
}
|
||||
} else { // (第一个新版频道)9.0.15~9.0.25
|
||||
hookBeforeIfEnabled("Lcom/tencent/mobileqq/guild/mainframe/GuildFragmentDelegateFrame;->s()Lcom/tencent/mobileqq/guild/mainframe/AbsGuildMainFragment;".method) {
|
||||
it.result = it.thisObject.invoke("u")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* QAuxiliary - An Xposed module for QQ/TIM
|
||||
* Copyright (C) 2019-2024 QAuxiliary developers
|
||||
* https://github.com/cinit/QAuxiliary
|
||||
*
|
||||
* This software is an opensource software: you can redistribute it
|
||||
* and/or modify it under the terms of the General Public License
|
||||
* as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version as published
|
||||
* by QAuxiliary contributors.
|
||||
*
|
||||
* This software 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the General Public License
|
||||
* along with this software.
|
||||
* If not, see
|
||||
* <https://github.com/cinit/QAuxiliary/blob/master/LICENSE.md>.
|
||||
*/
|
||||
|
||||
package io.github.memory2314.hook
|
||||
|
||||
import io.github.qauxv.base.annotation.FunctionHookEntry
|
||||
import io.github.qauxv.base.annotation.UiItemAgentEntry
|
||||
import io.github.qauxv.dsl.FunctionEntryRouter
|
||||
import io.github.qauxv.hook.CommonSwitchFunctionHook
|
||||
import xyz.nextalone.util.method
|
||||
import xyz.nextalone.util.replace
|
||||
import xyz.nextalone.util.throwOrTrue
|
||||
|
||||
@FunctionHookEntry
|
||||
@UiItemAgentEntry
|
||||
object RemoveShareLimit : CommonSwitchFunctionHook() {
|
||||
override val name = "去除转发9名联系人限制"
|
||||
override val uiItemLocation = FunctionEntryRouter.Locations.Auxiliary.CHAT_CATEGORY
|
||||
|
||||
override fun initOnce() = throwOrTrue {
|
||||
"Lcom/tencent/mobileqq/activity/ForwardRecentActivity;->add2ForwardTargetList(Lcom/tencent/mobileqq/selectmember/ResultRecord;)Z".method.replace(this, true)
|
||||
}
|
||||
}
|
||||
@@ -56,7 +56,7 @@ import io.github.qauxv.fragment.AboutFragment;
|
||||
import io.github.qauxv.fragment.CheckAbiVariantFragment;
|
||||
import io.github.qauxv.fragment.CheckAbiVariantModel;
|
||||
import io.github.qauxv.lifecycle.JumpActivityEntryHook;
|
||||
import io.github.qauxv.startup.HookEntry;
|
||||
import io.github.qauxv.util.PackageConstants;
|
||||
import io.github.qauxv.util.SyncUtils;
|
||||
import io.github.qauxv.util.Toasts;
|
||||
import io.github.qauxv.util.UiThread;
|
||||
@@ -165,11 +165,11 @@ public class ConfigV2Activity extends AppCompatTransferActivity {
|
||||
String pkg = null;
|
||||
var id = view.getId();
|
||||
if (id == R.id.mainRelativeLayoutButtonOpenQQ) {
|
||||
pkg = HookEntry.PACKAGE_NAME_QQ;
|
||||
pkg = PackageConstants.PACKAGE_NAME_QQ;
|
||||
} else if (id == R.id.mainRelativeLayoutButtonOpenTIM) {
|
||||
pkg = HookEntry.PACKAGE_NAME_TIM;
|
||||
pkg = PackageConstants.PACKAGE_NAME_TIM;
|
||||
} else if (id == R.id.mainRelativeLayoutButtonOpenQQLite) {
|
||||
pkg = HookEntry.PACKAGE_NAME_QQ_LITE;
|
||||
pkg = PackageConstants.PACKAGE_NAME_QQ_LITE;
|
||||
}
|
||||
if (pkg != null) {
|
||||
Intent intent = new Intent();
|
||||
@@ -205,19 +205,19 @@ public class ConfigV2Activity extends AppCompatTransferActivity {
|
||||
String pkg = null;
|
||||
switch (which) {
|
||||
case 0: {
|
||||
pkg = HookEntry.PACKAGE_NAME_QQ;
|
||||
pkg = PackageConstants.PACKAGE_NAME_QQ;
|
||||
break;
|
||||
}
|
||||
case 1: {
|
||||
pkg = HookEntry.PACKAGE_NAME_TIM;
|
||||
pkg = PackageConstants.PACKAGE_NAME_TIM;
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
pkg = HookEntry.PACKAGE_NAME_QQ_LITE;
|
||||
pkg = PackageConstants.PACKAGE_NAME_QQ_LITE;
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
pkg = HookEntry.PACKAGE_NAME_QQ_HD;
|
||||
pkg = PackageConstants.PACKAGE_NAME_QQ_HD;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
|
||||
@@ -26,7 +26,7 @@ import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import cc.ioctl.util.Reflex;
|
||||
import com.tencent.common.app.AppInterface;
|
||||
import de.robv.android.xposed.XposedHelpers;
|
||||
import io.github.qauxv.util.xpcompat.XposedHelpers;
|
||||
import io.github.qauxv.base.annotation.DexDeobfs;
|
||||
import io.github.qauxv.bridge.ntapi.RelationNTUinAndUidApi;
|
||||
import io.github.qauxv.util.HostInfo;
|
||||
|
||||
@@ -21,8 +21,8 @@
|
||||
*/
|
||||
package io.github.qauxv.bridge;
|
||||
|
||||
import static de.robv.android.xposed.XposedHelpers.callMethod;
|
||||
import static de.robv.android.xposed.XposedHelpers.setObjectField;
|
||||
import static io.github.qauxv.util.xpcompat.XposedHelpers.callMethod;
|
||||
import static io.github.qauxv.util.xpcompat.XposedHelpers.setObjectField;
|
||||
|
||||
import android.os.Bundle;
|
||||
import cc.ioctl.util.Reflex;
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
package io.github.qauxv.config;
|
||||
|
||||
import android.os.Environment;
|
||||
import io.github.qauxv.startup.HookEntry;
|
||||
import io.github.qauxv.util.PackageConstants;
|
||||
import io.github.qauxv.util.HostInfo;
|
||||
import io.github.qauxv.util.Log;
|
||||
import java.io.File;
|
||||
@@ -44,7 +44,7 @@ public class SafeModeManager {
|
||||
}
|
||||
INSTANCE.mSafeModeEnableFile = new File(
|
||||
Environment.getExternalStorageDirectory().getAbsolutePath() + "/Android/data/" +
|
||||
HookEntry.sCurrentPackageName + "/" + SAFE_MODE_FILE_NAME
|
||||
HostInfo.getHostInfo().getPackageName() + "/" + SAFE_MODE_FILE_NAME
|
||||
);
|
||||
return INSTANCE;
|
||||
}
|
||||
@@ -73,10 +73,6 @@ public class SafeModeManager {
|
||||
if (!isAvailable()) {
|
||||
return false;
|
||||
}
|
||||
if (HookEntry.sCurrentPackageName == null || HookEntry.sCurrentPackageName.isBlank()) {
|
||||
Log.e("Failed to enable or disable safe mode, sCurrentPackageName is null or blank");
|
||||
return false;
|
||||
}
|
||||
if (isEnable) {
|
||||
try {
|
||||
boolean isCreated = mSafeModeEnableFile.createNewFile();
|
||||
|
||||
@@ -27,7 +27,7 @@ import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import cc.ioctl.hook.SettingEntryHook;
|
||||
import cc.ioctl.util.HostInfo;
|
||||
import de.robv.android.xposed.XposedBridge;
|
||||
import io.github.qauxv.util.xpcompat.XposedBridge;
|
||||
import io.github.qauxv.BuildConfig;
|
||||
import io.github.qauxv.util.SyncUtils;
|
||||
import io.github.qauxv.base.IDynamicHook;
|
||||
|
||||
@@ -123,6 +123,9 @@ public class InjectDelayableHooks {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
final ArrayList<Step> steps = new ArrayList<>(todos);
|
||||
// collect all dex-deobfs steps if backend supports
|
||||
HashSet<String> deobfIndexList = new HashSet<>(16);
|
||||
@@ -208,6 +211,8 @@ public class InjectDelayableHooks {
|
||||
}
|
||||
} finally {
|
||||
DexDeobfsProvider.INSTANCE.exitDeobfsSection();
|
||||
long end = System.currentTimeMillis();
|
||||
Log.i("DexDeobfsProvider: cost " + (end - start) + "ms");
|
||||
}
|
||||
}
|
||||
if (LicenseStatus.hasUserAcceptEula()) {
|
||||
|
||||
@@ -44,8 +44,8 @@ import cc.ioctl.hook.ui.misc.OptXListViewScrollBar;
|
||||
import cc.ioctl.hook.ui.title.RemoveCameraButton;
|
||||
import cc.ioctl.util.HostInfo;
|
||||
import cc.ioctl.util.Reflex;
|
||||
import de.robv.android.xposed.XC_MethodHook;
|
||||
import de.robv.android.xposed.XposedBridge;
|
||||
import io.github.qauxv.util.xpcompat.XC_MethodHook;
|
||||
import io.github.qauxv.util.xpcompat.XposedBridge;
|
||||
import io.github.qauxv.config.ConfigItems;
|
||||
import io.github.qauxv.config.SafeModeManager;
|
||||
import io.github.qauxv.lifecycle.ActProxyMgr;
|
||||
|
||||
@@ -46,8 +46,17 @@ class HotUpdateConfigFragment : BaseRootLayoutFragment(), View.OnClickListener {
|
||||
HotUpdateManager.CHANNEL_CANARY to R.id.hotUpdateConfig_channel_canary,
|
||||
)
|
||||
|
||||
private val actionIdToViewId = mapOf(
|
||||
HotUpdateManager.ACTION_DISABLE to R.id.hotUpdateConfig_action_disabled,
|
||||
HotUpdateManager.ACTION_QUERY to R.id.hotUpdateConfig_action_query_before_update,
|
||||
HotUpdateManager.ACTION_AUTO_UPDATE_WITH_NOTIFICATION to R.id.hotUpdateConfig_notice_after_update,
|
||||
HotUpdateManager.ACTION_AUTO_UPDATE_WITHOUT_NOTIFICATION to R.id.hotUpdateConfig_auto_update_without_notice,
|
||||
)
|
||||
|
||||
private fun viewIdToChannelId(viewId: Int) = channelIdToViewId.filterValues { it == viewId }.keys.first()
|
||||
|
||||
private fun viewIdToActionId(viewId: Int) = actionIdToViewId.filterValues { it == viewId }.keys.first()
|
||||
|
||||
override fun doOnCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||
title = "热更新配置"
|
||||
binding = FragmentHotUpdateConfigBinding.inflate(inflater, container, false).apply {
|
||||
@@ -55,6 +64,12 @@ class HotUpdateConfigFragment : BaseRootLayoutFragment(), View.OnClickListener {
|
||||
hotUpdateConfigChannelStable.setOnClickListener(this@HotUpdateConfigFragment)
|
||||
hotUpdateConfigChannelBeta.setOnClickListener(this@HotUpdateConfigFragment)
|
||||
hotUpdateConfigChannelCanary.setOnClickListener(this@HotUpdateConfigFragment)
|
||||
|
||||
hotUpdateConfigActionDisabled.setOnClickListener(this@HotUpdateConfigFragment)
|
||||
hotUpdateConfigActionQueryBeforeUpdate.setOnClickListener(this@HotUpdateConfigFragment)
|
||||
hotUpdateConfigNoticeAfterUpdate.setOnClickListener(this@HotUpdateConfigFragment)
|
||||
hotUpdateConfigAutoUpdateWithoutNotice.setOnClickListener(this@HotUpdateConfigFragment)
|
||||
|
||||
updateViewStatus(this)
|
||||
|
||||
fun adjustTitleTextSize(v: AppCompatTextView) {
|
||||
@@ -79,6 +94,11 @@ class HotUpdateConfigFragment : BaseRootLayoutFragment(), View.OnClickListener {
|
||||
adjustTitleTextSize(hotUpdateConfigChannelStable)
|
||||
adjustTitleTextSize(hotUpdateConfigChannelBeta)
|
||||
adjustTitleTextSize(hotUpdateConfigChannelCanary)
|
||||
|
||||
adjustTitleTextSize(hotUpdateConfigActionDisabled)
|
||||
adjustTitleTextSize(hotUpdateConfigActionQueryBeforeUpdate)
|
||||
adjustTitleTextSize(hotUpdateConfigNoticeAfterUpdate)
|
||||
adjustTitleTextSize(hotUpdateConfigAutoUpdateWithoutNotice)
|
||||
}
|
||||
rootLayoutView = binding!!.root
|
||||
return binding!!.root
|
||||
@@ -92,6 +112,7 @@ class HotUpdateConfigFragment : BaseRootLayoutFragment(), View.OnClickListener {
|
||||
private fun updateViewStatus(binding: FragmentHotUpdateConfigBinding) {
|
||||
val ctx = requireContext()
|
||||
val currentChannel = HotUpdateManager.currentChannel
|
||||
val currentAction = HotUpdateManager.currentAction
|
||||
|
||||
binding.hotUpdateConfigCurrentInfo.text = "别看了,这个功能还没做好,选什么都没用"
|
||||
|
||||
@@ -101,6 +122,12 @@ class HotUpdateConfigFragment : BaseRootLayoutFragment(), View.OnClickListener {
|
||||
binding.hotUpdateConfigChannelBeta,
|
||||
binding.hotUpdateConfigChannelCanary,
|
||||
)
|
||||
val actionButtons = arrayOf(
|
||||
binding.hotUpdateConfigActionDisabled,
|
||||
binding.hotUpdateConfigActionQueryBeforeUpdate,
|
||||
binding.hotUpdateConfigNoticeAfterUpdate,
|
||||
binding.hotUpdateConfigAutoUpdateWithoutNotice,
|
||||
)
|
||||
val accentColor = ThemeAttrUtils.resolveColorOrDefaultColorRes(
|
||||
ctx,
|
||||
androidx.appcompat.R.attr.colorAccent,
|
||||
@@ -123,6 +150,22 @@ class HotUpdateConfigFragment : BaseRootLayoutFragment(), View.OnClickListener {
|
||||
it.setTextColor(secondTextColor)
|
||||
}
|
||||
}
|
||||
actionButtons.forEach {
|
||||
val actionId = viewIdToActionId(it.id)
|
||||
if (currentAction == actionId) {
|
||||
it.setTextColor(accentColor)
|
||||
if (it.compoundDrawables[2] == null) {
|
||||
it.setCompoundDrawablesWithIntrinsicBounds(
|
||||
null, null, ResourcesCompat.getDrawable(
|
||||
ctx.resources, R.drawable.ic_check_24, ctx.theme
|
||||
), null
|
||||
)
|
||||
}
|
||||
} else {
|
||||
it.setCompoundDrawables(null, null, null, null)
|
||||
it.setTextColor(secondTextColor)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun onChannelClick(v: View, channelId: Int) {
|
||||
@@ -159,12 +202,25 @@ class HotUpdateConfigFragment : BaseRootLayoutFragment(), View.OnClickListener {
|
||||
}
|
||||
}
|
||||
|
||||
private fun onActionClick(v: View, actionId: Int) {
|
||||
if (HotUpdateManager.currentAction == actionId) {
|
||||
return
|
||||
}
|
||||
HotUpdateManager.currentAction = actionId
|
||||
updateViewStatus(binding!!)
|
||||
}
|
||||
|
||||
override fun onClick(v: View) {
|
||||
when (v.id) {
|
||||
R.id.hotUpdateConfig_channel_disabled -> onChannelClick(v, HotUpdateManager.CHANNEL_DISABLED)
|
||||
R.id.hotUpdateConfig_channel_stable -> onChannelClick(v, HotUpdateManager.CHANNEL_STABLE)
|
||||
R.id.hotUpdateConfig_channel_beta -> onChannelClick(v, HotUpdateManager.CHANNEL_BETA)
|
||||
R.id.hotUpdateConfig_channel_canary -> onChannelClick(v, HotUpdateManager.CHANNEL_CANARY)
|
||||
|
||||
R.id.hotUpdateConfig_action_disabled -> onActionClick(v, HotUpdateManager.ACTION_DISABLE)
|
||||
R.id.hotUpdateConfig_action_query_before_update -> onActionClick(v, HotUpdateManager.ACTION_QUERY)
|
||||
R.id.hotUpdateConfig_notice_after_update -> onActionClick(v, HotUpdateManager.ACTION_AUTO_UPDATE_WITH_NOTIFICATION)
|
||||
R.id.hotUpdateConfig_auto_update_without_notice -> onActionClick(v, HotUpdateManager.ACTION_AUTO_UPDATE_WITHOUT_NOTIFICATION)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -39,9 +39,14 @@ import android.util.TypedValue
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.CheckBox
|
||||
import android.widget.EditText
|
||||
import android.widget.FrameLayout
|
||||
import android.widget.LinearLayout
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.appcompat.widget.AppCompatCheckBox
|
||||
import androidx.appcompat.widget.AppCompatEditText
|
||||
import androidx.appcompat.widget.AppCompatTextView
|
||||
import androidx.core.content.pm.ShortcutManagerCompat
|
||||
import androidx.core.content.res.ResourcesCompat
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
@@ -49,12 +54,13 @@ import cc.ioctl.fragment.ExfriendListFragment
|
||||
import cc.ioctl.util.ExfriendManager
|
||||
import cc.ioctl.util.HostInfo
|
||||
import cc.ioctl.util.LayoutHelper
|
||||
import cc.ioctl.util.LayoutHelper.dip2px
|
||||
import cc.ioctl.util.Reflex
|
||||
import cc.ioctl.util.data.EventRecord
|
||||
import cc.ioctl.util.data.FriendRecord
|
||||
import cc.ioctl.util.ui.ThemeAttrUtils
|
||||
import cc.ioctl.util.ui.dsl.RecyclerListViewController
|
||||
import de.robv.android.xposed.XposedBridge
|
||||
import io.github.qauxv.util.xpcompat.XposedBridge
|
||||
import io.github.qauxv.R
|
||||
import io.github.qauxv.activity.SettingsUiFragmentHostActivity
|
||||
import io.github.qauxv.activity.SettingsUiFragmentHostActivity.Companion.createStartActivityForFragmentIntent
|
||||
@@ -66,11 +72,12 @@ import io.github.qauxv.dsl.item.CategoryItem
|
||||
import io.github.qauxv.dsl.item.DslTMsgListItemInflatable
|
||||
import io.github.qauxv.dsl.item.TextSwitchItem
|
||||
import io.github.qauxv.lifecycle.ActProxyMgr
|
||||
import io.github.qauxv.startup.HookEntry
|
||||
import io.github.qauxv.poststartup.StartupInfo
|
||||
import io.github.qauxv.startup.HybridClassLoader
|
||||
import io.github.qauxv.tlb.ConfigTable.cacheMap
|
||||
import io.github.qauxv.ui.CustomDialog
|
||||
import io.github.qauxv.util.Initiator
|
||||
import io.github.qauxv.util.LoaderExtensionHelper
|
||||
import io.github.qauxv.util.Natives
|
||||
import io.github.qauxv.util.Toasts
|
||||
import io.github.qauxv.util.dexkit.DexKit
|
||||
@@ -166,14 +173,7 @@ class TroubleshootFragment : BaseRootLayoutFragment() {
|
||||
})
|
||||
},
|
||||
CategoryItem("调试信息") {
|
||||
val statusInfo = "PID: " + android.os.Process.myPid() +
|
||||
", UID: " + android.os.Process.myUid() +
|
||||
", " + (if (android.os.Process.is64Bit()) "64 bit" else "32 bit") + "\n" +
|
||||
"Xposed API version: " + XposedBridge.getXposedVersion() + "\n" +
|
||||
HybridClassLoader.getXposedBridgeClassName() + "\n" +
|
||||
"module: " + HookEntry.getModulePath() + "\n" +
|
||||
"ctx.dataDir: " + hostInfo.application.dataDir
|
||||
description(statusInfo, isTextSelectable = true)
|
||||
description(generateStatusText(), isTextSelectable = true)
|
||||
description(generateDebugInfo(), isTextSelectable = true)
|
||||
}
|
||||
)
|
||||
@@ -222,8 +222,9 @@ class TroubleshootFragment : BaseRootLayoutFragment() {
|
||||
exitProcess(0)
|
||||
}
|
||||
|
||||
private val clickToResetDefaultConfig = confirmBeforeAction(
|
||||
"此操作将删除该模块的所有配置信息,包括屏蔽通知的群列表,但不包括历史好友列表.点击确认后请等待3秒后手动重启" + hostInfo.hostName + ".\n此操作不可恢复"
|
||||
private val clickToResetDefaultConfig = confirmTwiceBeforeAction(
|
||||
"此操作将删除该模块的所有配置信息,包括屏蔽通知的群列表,但不包括历史好友列表.点击确认后请等待3秒后手动重启" + hostInfo.hostName + ".\n此操作不可恢复",
|
||||
"删除所有配置信息"
|
||||
) {
|
||||
ConfigManager.getCache().apply {
|
||||
clear()
|
||||
@@ -237,12 +238,13 @@ class TroubleshootFragment : BaseRootLayoutFragment() {
|
||||
exitProcess(0)
|
||||
}
|
||||
|
||||
private val clickToClearRecoveredFriends = confirmBeforeAction(
|
||||
private val clickToClearRecoveredFriends = confirmTwiceBeforeAction(
|
||||
"""
|
||||
此操作将删除当前账号(${getLongAccountUin()})下的 已恢复 的历史好友记录(记录可单独删除).
|
||||
如果因 BUG 大量好友被标记为已删除, 请先刷新好友列表, 然后再点击此按钮.
|
||||
此操作不可恢复
|
||||
""".trimIndent()
|
||||
""".trimIndent(),
|
||||
"删除已恢复的好友记录"
|
||||
) {
|
||||
val exm = ExfriendManager.getCurrent()
|
||||
val it: MutableIterator<*> = exm.events.entries.iterator()
|
||||
@@ -258,11 +260,12 @@ class TroubleshootFragment : BaseRootLayoutFragment() {
|
||||
Toasts.success(requireContext(), "操作成功")
|
||||
}
|
||||
|
||||
private val clickToClearAllFriends = confirmBeforeAction(
|
||||
private val clickToClearAllFriends = confirmTwiceBeforeAction(
|
||||
"此操作将删除当前账号(" + getLongAccountUin()
|
||||
+ ")下的 全部 的历史好友记录, 通常您不需要进行此操作. \n" +
|
||||
"如果您的历史好友列表中因bug出现大量好友,请在联系人列表下拉刷新后点击 删除标记为已恢复的好友. \n" +
|
||||
"此操作不可恢复"
|
||||
"此操作不可恢复",
|
||||
"删除所有好友记录"
|
||||
) {
|
||||
val uin = getLongAccountUin()
|
||||
if (uin < 10000) {
|
||||
@@ -303,6 +306,57 @@ class TroubleshootFragment : BaseRootLayoutFragment() {
|
||||
dialog.show()
|
||||
}
|
||||
|
||||
private fun confirmTwiceBeforeAction(
|
||||
confirmMessage: String,
|
||||
secondConfirmCheckBoxText: String,
|
||||
action: () -> Unit
|
||||
) = View.OnClickListener {
|
||||
val ctx = requireContext()
|
||||
val builder = AlertDialog.Builder(ctx)
|
||||
builder.setPositiveButton(android.R.string.ok) { _, _ ->
|
||||
try {
|
||||
action()
|
||||
} catch (e: Exception) {
|
||||
CustomDialog.createFailsafe(ctx)
|
||||
.setTitle(Reflex.getShortClassName(e))
|
||||
.setCancelable(true)
|
||||
.setMessage(e.toString())
|
||||
.ok().show()
|
||||
}
|
||||
}
|
||||
builder.setNegativeButton(android.R.string.cancel, null)
|
||||
builder.setCancelable(true)
|
||||
builder.setTitle("确认操作")
|
||||
// create a linear layout to hold the message and checkbox
|
||||
val layout = LinearLayout(ctx).apply {
|
||||
orientation = LinearLayout.VERTICAL
|
||||
val padding = ctx.resources.getDimension(androidx.appcompat.R.dimen.abc_dialog_padding_material).toInt()
|
||||
setPadding(padding, padding / 3, padding, 0)
|
||||
}
|
||||
val message = AppCompatTextView(ctx).apply {
|
||||
text = confirmMessage
|
||||
setTextSize(TypedValue.COMPLEX_UNIT_SP, 16f)
|
||||
setTextColor(ResourcesCompat.getColor(resources, R.color.firstTextColor, ctx.theme))
|
||||
}
|
||||
val checkBox = AppCompatCheckBox(ctx).apply {
|
||||
text = secondConfirmCheckBoxText
|
||||
setTextSize(TypedValue.COMPLEX_UNIT_SP, 16f)
|
||||
setTextColor(ResourcesCompat.getColor(resources, R.color.firstTextColor, ctx.theme))
|
||||
isClickable = true
|
||||
isChecked = false
|
||||
}
|
||||
layout.addView(message)
|
||||
layout.addView(checkBox)
|
||||
builder.setView(layout)
|
||||
val dialog = builder.show()
|
||||
// get positive button and set listener
|
||||
val positiveButton = dialog.getButton(AlertDialog.BUTTON_POSITIVE)
|
||||
checkBox.setOnCheckedChangeListener { _, isChecked ->
|
||||
positiveButton.isEnabled = isChecked
|
||||
}
|
||||
positiveButton.isEnabled = false
|
||||
}
|
||||
|
||||
private fun actionOrShowError(action: () -> Unit) = View.OnClickListener {
|
||||
runOrShowError(action)
|
||||
}
|
||||
@@ -423,6 +477,26 @@ class TroubleshootFragment : BaseRootLayoutFragment() {
|
||||
startActivity(intent)
|
||||
}
|
||||
|
||||
private fun generateStatusText(): String {
|
||||
val loader = StartupInfo.getLoaderInfo()
|
||||
val hook = StartupInfo.requireHookBridge()
|
||||
var statusInfo = "PID: " + android.os.Process.myPid() +
|
||||
", UID: " + android.os.Process.myUid() +
|
||||
", " + (if (android.os.Process.is64Bit()) "64 bit" else "32 bit") + "\n" +
|
||||
"Xposed API version: " + hook.apiLevel + "\n" +
|
||||
"module: " + StartupInfo.getModulePath() + "\n" +
|
||||
"ctx.dataDir: " + hostInfo.application.dataDir + "\n"
|
||||
statusInfo += "entry: " + loader.entryPointName + "\n"
|
||||
var xp = loader.queryExtension("GetXposedBridgeClass") as Class<*>?
|
||||
if (xp == null) {
|
||||
xp = loader.queryExtension("GetXposedInterfaceClass") as Class<*>?
|
||||
}
|
||||
statusInfo += "XposedBridge: " + (if (xp != null) xp.name else "null") + "\n"
|
||||
statusInfo += hook.frameworkName + " " + hook.frameworkVersion + " (" + hook.frameworkVersionCode + ")\n"
|
||||
statusInfo += "Hook counter: " + LoaderExtensionHelper.getHookCounter();
|
||||
return statusInfo
|
||||
}
|
||||
|
||||
private fun generateDebugInfo(): CharSequence {
|
||||
val ctx = requireContext()
|
||||
val colorError: Int = ThemeAttrUtils.resolveColorOrDefaultColorInt(ctx, R.attr.unusableColor, Color.RED)
|
||||
|
||||
@@ -28,8 +28,8 @@ import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import de.robv.android.xposed.XC_MethodHook;
|
||||
import de.robv.android.xposed.XposedBridge;
|
||||
import io.github.qauxv.util.xpcompat.XC_MethodHook;
|
||||
import io.github.qauxv.util.xpcompat.XposedBridge;
|
||||
import io.github.qauxv.activity.SettingsUiFragmentHostActivity;
|
||||
import io.github.qauxv.fragment.EulaFragment;
|
||||
import io.github.qauxv.fragment.TroubleshootFragment;
|
||||
|
||||
@@ -53,7 +53,8 @@ import androidx.annotation.RequiresApi;
|
||||
import cc.ioctl.util.HostInfo;
|
||||
import io.github.qauxv.R;
|
||||
import io.github.qauxv.core.MainHook;
|
||||
import io.github.qauxv.startup.HookEntry;
|
||||
import io.github.qauxv.poststartup.StartupInfo;
|
||||
import io.github.qauxv.util.PackageConstants;
|
||||
import io.github.qauxv.ui.WindowIsTranslucent;
|
||||
import io.github.qauxv.util.Initiator;
|
||||
import io.github.qauxv.util.Log;
|
||||
@@ -113,7 +114,7 @@ public class Parasitics {
|
||||
return;
|
||||
} catch (Resources.NotFoundException ignored) {
|
||||
}
|
||||
String sModulePath = HookEntry.getModulePath();
|
||||
String sModulePath = StartupInfo.getModulePath();
|
||||
if (sModulePath == null) {
|
||||
throw new RuntimeException("get module path failed, loader=" + MainHook.class.getClassLoader());
|
||||
}
|
||||
|
||||
@@ -32,8 +32,8 @@ import android.provider.OpenableColumns;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import cc.ioctl.util.HostInfo;
|
||||
import de.robv.android.xposed.XC_MethodHook;
|
||||
import de.robv.android.xposed.XposedBridge;
|
||||
import io.github.qauxv.util.xpcompat.XC_MethodHook;
|
||||
import io.github.qauxv.util.xpcompat.XposedBridge;
|
||||
import io.github.qauxv.util.Initiator;
|
||||
import io.github.qauxv.util.Log;
|
||||
import io.github.qauxv.util.SyncUtils;
|
||||
|
||||
@@ -25,8 +25,8 @@ package io.github.qauxv.omnifix.hw;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import androidx.annotation.NonNull;
|
||||
import de.robv.android.xposed.XC_MethodHook;
|
||||
import de.robv.android.xposed.XposedBridge;
|
||||
import io.github.qauxv.util.xpcompat.XC_MethodHook;
|
||||
import io.github.qauxv.util.xpcompat.XposedBridge;
|
||||
import io.github.qauxv.BuildConfig;
|
||||
import io.github.qauxv.util.Log;
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
138
app/src/main/java/io/github/qauxv/poststartup/StartupAgent.java
Normal file
138
app/src/main/java/io/github/qauxv/poststartup/StartupAgent.java
Normal file
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
* QAuxiliary - An Xposed module for QQ/TIM
|
||||
* Copyright (C) 2019-2024 QAuxiliary developers
|
||||
* https://github.com/cinit/QAuxiliary
|
||||
*
|
||||
* This software is an opensource software: you can redistribute it
|
||||
* and/or modify it under the terms of the General Public License
|
||||
* as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version as published
|
||||
* by QAuxiliary contributors.
|
||||
*
|
||||
* This software 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the General Public License
|
||||
* along with this software.
|
||||
* If not, see
|
||||
* <https://github.com/cinit/QAuxiliary/blob/master/LICENSE.md>.
|
||||
*/
|
||||
|
||||
package io.github.qauxv.poststartup;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Application;
|
||||
import android.content.Context;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.os.Build;
|
||||
import androidx.annotation.Keep;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import io.github.qauxv.loader.hookapi.IHookBridge;
|
||||
import io.github.qauxv.loader.hookapi.ILoaderInfo;
|
||||
import io.github.qauxv.util.IoUtils;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import org.lsposed.hiddenapibypass.HiddenApiBypass;
|
||||
|
||||
@Keep
|
||||
public class StartupAgent {
|
||||
|
||||
private static boolean sInitialized = false;
|
||||
|
||||
private StartupAgent() {
|
||||
throw new AssertionError("No instance for you!");
|
||||
}
|
||||
|
||||
@Keep
|
||||
public static void startup(
|
||||
@NonNull String modulePath,
|
||||
@NonNull ApplicationInfo appInfo,
|
||||
@NonNull ILoaderInfo loaderInfo,
|
||||
@NonNull ClassLoader hostClassLoader,
|
||||
@Nullable IHookBridge hookBridge
|
||||
) {
|
||||
if (sInitialized) {
|
||||
return;
|
||||
}
|
||||
sInitialized = true;
|
||||
if (io.github.qauxv.R.string.res_inject_success >>> 24 == 0x7f) {
|
||||
throw new AssertionError("package id must NOT be 0x7f, reject loading...");
|
||||
}
|
||||
if ("true".equals(System.getProperty(StartupAgent.class.getName()))) {
|
||||
android.util.Log.e("QAuxv", "Error: QAuxiliary reloaded??");
|
||||
// I don't know... What happened?
|
||||
return;
|
||||
}
|
||||
System.setProperty(StartupAgent.class.getName(), "true");
|
||||
StartupInfo.setModulePath(modulePath);
|
||||
StartupInfo.setLoaderInfo(loaderInfo);
|
||||
StartupInfo.setHookBridge(hookBridge);
|
||||
StartupInfo.setInHostProcess(true);
|
||||
// bypass hidden api
|
||||
ensureHiddenApiAccess();
|
||||
// we want context
|
||||
Context ctx = getBaseApplicationImpl(hostClassLoader);
|
||||
if (ctx == null) {
|
||||
if (hookBridge == null) {
|
||||
throw new UnsupportedOperationException("neither base application nor hook bridge found");
|
||||
}
|
||||
StartupHook.getInstance().initializeBeforeAppCreate(hostClassLoader);
|
||||
} else {
|
||||
StartupHook.getInstance().initializeAfterAppCreate(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
public static Context getBaseApplicationImpl(@NonNull ClassLoader classLoader) {
|
||||
Context app;
|
||||
try {
|
||||
Class<?> clz = classLoader.loadClass("com.tencent.common.app.BaseApplicationImpl");
|
||||
Field fsApp = null;
|
||||
for (Field f : clz.getDeclaredFields()) {
|
||||
if (f.getType() == clz) {
|
||||
fsApp = f;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (fsApp == null) {
|
||||
throw new UnsupportedOperationException("field BaseApplicationImpl.sApplication not found");
|
||||
}
|
||||
app = (Context) fsApp.get(null);
|
||||
return app;
|
||||
} catch (ReflectiveOperationException e) {
|
||||
android.util.Log.e("QAuxv", "getBaseApplicationImpl: failed", e);
|
||||
throw IoUtils.unsafeThrow(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static void ensureHiddenApiAccess() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && !isHiddenApiAccessible()) {
|
||||
android.util.Log.w("QAuxv", "Hidden API access not accessible, SDK_INT is " + Build.VERSION.SDK_INT);
|
||||
HiddenApiBypass.setHiddenApiExemptions("L");
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint({"BlockedPrivateApi", "PrivateApi"})
|
||||
public static boolean isHiddenApiAccessible() {
|
||||
Class<?> kContextImpl;
|
||||
try {
|
||||
kContextImpl = Class.forName("android.app.ContextImpl");
|
||||
} catch (ClassNotFoundException e) {
|
||||
return false;
|
||||
}
|
||||
Field mActivityToken = null;
|
||||
Field mToken = null;
|
||||
try {
|
||||
mActivityToken = kContextImpl.getDeclaredField("mActivityToken");
|
||||
} catch (NoSuchFieldException ignored) {
|
||||
}
|
||||
try {
|
||||
mToken = kContextImpl.getDeclaredField("mToken");
|
||||
} catch (NoSuchFieldException ignored) {
|
||||
}
|
||||
return mActivityToken != null || mToken != null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,34 +1,36 @@
|
||||
/*
|
||||
* QAuxiliary - An Xposed module for QQ/TIM
|
||||
* Copyright (C) 2019-2022 qwq233@qwq2333.top
|
||||
* Copyright (C) 2019-2024 QAuxiliary developers
|
||||
* https://github.com/cinit/QAuxiliary
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
* and/or modify it under the terms of the GNU Affero General Public License
|
||||
* This software is an opensource software: you can redistribute it
|
||||
* and/or modify it under the terms of the General Public License
|
||||
* as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version and our eula as published
|
||||
* version 3 of the License, or any later version as published
|
||||
* by QAuxiliary contributors.
|
||||
*
|
||||
* This software 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
|
||||
* Affero General Public License for more details.
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
* You should have received a copy of the General Public License
|
||||
* along with this software.
|
||||
* If not, see
|
||||
* <https://github.com/cinit/QAuxiliary/blob/master/LICENSE.md>.
|
||||
*/
|
||||
package io.github.qauxv.startup;
|
||||
package io.github.qauxv.poststartup;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.os.Environment;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.Log;
|
||||
import de.robv.android.xposed.XC_MethodHook;
|
||||
import de.robv.android.xposed.XposedBridge;
|
||||
import de.robv.android.xposed.XposedHelpers;
|
||||
import androidx.annotation.NonNull;
|
||||
import io.github.qauxv.startup.HybridClassLoader;
|
||||
import io.github.qauxv.util.IoUtils;
|
||||
import io.github.qauxv.util.xpcompat.XC_MethodHook;
|
||||
import io.github.qauxv.util.xpcompat.XposedBridge;
|
||||
import io.github.qauxv.util.xpcompat.XposedHelpers;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
@@ -38,9 +40,11 @@ import java.lang.reflect.Modifier;
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* Startup hook for QQ/TIM They should act differently according to the process they belong to. I don't want to cope
|
||||
* with them any more, enjoy it as long as possible. DO NOT INVOKE ANY METHOD THAT MAY GET IN TOUCH WITH KOTLIN HERE. DO
|
||||
* NOT MODIFY ANY CODE HERE UNLESS NECESSARY.
|
||||
* Startup hook for QQ/TIM They should act differently according to the process they belong to.
|
||||
* <p>
|
||||
* I don't want to cope with them anymore, enjoy it as long as possible.
|
||||
* <p>
|
||||
* DO NOT MODIFY ANY CODE HERE UNLESS NECESSARY.
|
||||
*
|
||||
* @author cinit
|
||||
*/
|
||||
@@ -48,7 +52,6 @@ public class StartupHook {
|
||||
|
||||
private static StartupHook sInstance;
|
||||
private static boolean sSecondStageInit = false;
|
||||
private boolean mFirstStageInit = false;
|
||||
|
||||
private StartupHook() {
|
||||
}
|
||||
@@ -65,44 +68,12 @@ public class StartupHook {
|
||||
if (sSecondStageInit) {
|
||||
return;
|
||||
}
|
||||
ClassLoader classLoader = ctx.getClassLoader();
|
||||
if (classLoader == null) {
|
||||
throw new AssertionError("ERROR: classLoader == null");
|
||||
}
|
||||
if ("true".equals(System.getProperty(StartupHook.class.getName()))) {
|
||||
XposedBridge.log("Err:QAuxiliary reloaded??");
|
||||
//I don't know... What happened?
|
||||
return;
|
||||
}
|
||||
System.setProperty(StartupHook.class.getName(), "true");
|
||||
injectClassLoader(classLoader);
|
||||
HybridClassLoader.setHostClassLoader(ctx.getClassLoader());
|
||||
StartupRoutine.execPostStartupInit(ctx, step, lpwReserved, bReserved);
|
||||
sSecondStageInit = true;
|
||||
deleteDirIfNecessaryNoThrow(ctx);
|
||||
}
|
||||
|
||||
@SuppressWarnings("JavaReflectionMemberAccess")
|
||||
@SuppressLint("DiscouragedPrivateApi")
|
||||
private static void injectClassLoader(ClassLoader classLoader) {
|
||||
if (classLoader == null) {
|
||||
throw new NullPointerException("classLoader == null");
|
||||
}
|
||||
try {
|
||||
Field fParent = ClassLoader.class.getDeclaredField("parent");
|
||||
fParent.setAccessible(true);
|
||||
ClassLoader mine = StartupHook.class.getClassLoader();
|
||||
ClassLoader curr = (ClassLoader) fParent.get(mine);
|
||||
if (curr == null) {
|
||||
curr = XposedBridge.class.getClassLoader();
|
||||
}
|
||||
if (!curr.getClass().getName().equals(HybridClassLoader.class.getName())) {
|
||||
fParent.set(mine, new HybridClassLoader(curr, classLoader));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log_e(e);
|
||||
}
|
||||
}
|
||||
|
||||
static void deleteDirIfNecessaryNoThrow(Context ctx) {
|
||||
try {
|
||||
deleteFile(new File(ctx.getDataDir(), "app_qqprotect"));
|
||||
@@ -147,58 +118,27 @@ public class StartupHook {
|
||||
String msg = Log.getStackTraceString(th);
|
||||
Log.e("QAuxv", msg);
|
||||
try {
|
||||
XposedBridge.log(th);
|
||||
} catch (NoClassDefFoundError e) {
|
||||
StartupInfo.getLoaderInfo().log(th);
|
||||
} catch (NoClassDefFoundError | NullPointerException e) {
|
||||
Log.e("Xposed", msg);
|
||||
Log.e("EdXposed-Bridge", msg);
|
||||
}
|
||||
}
|
||||
|
||||
private static void checkClassLoaderIsolation() {
|
||||
Class<?> stub;
|
||||
try {
|
||||
stub = Class.forName("com.tencent.common.app.BaseApplicationImpl");
|
||||
} catch (ClassNotFoundException e) {
|
||||
Log.d("QAuxv", "checkClassLoaderIsolation success");
|
||||
return;
|
||||
}
|
||||
Log.e("QAuxv", "checkClassLoaderIsolation failure!");
|
||||
Log.e("QAuxv", "HostApp: " + stub.getClassLoader());
|
||||
Log.e("QAuxv", "Module: " + StartupHook.class.getClassLoader());
|
||||
Log.e("QAuxv", "Module.parent: " + StartupHook.class.getClassLoader().getParent());
|
||||
Log.e("QAuxv", "XposedBridge: " + XposedBridge.class.getClassLoader());
|
||||
Log.e("QAuxv", "SystemClassLoader: " + ClassLoader.getSystemClassLoader());
|
||||
Log.e("QAuxv", "Context.class: " + Context.class.getClassLoader());
|
||||
public void initializeAfterAppCreate(@NonNull Context ctx) {
|
||||
execStartupInit(ctx, null, null, false);
|
||||
applyTargetDpiIfNecessary(ctx);
|
||||
deleteDirIfNecessaryNoThrow(ctx);
|
||||
}
|
||||
|
||||
public void initialize(ClassLoader rtLoader) throws Throwable {
|
||||
if (mFirstStageInit) {
|
||||
return;
|
||||
}
|
||||
public void initializeBeforeAppCreate(@NonNull ClassLoader rtLoader) {
|
||||
try {
|
||||
XC_MethodHook startup = new XC_MethodHook(51) {
|
||||
@Override
|
||||
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
|
||||
try {
|
||||
Context app;
|
||||
Class<?> clz = param.thisObject.getClass().getClassLoader()
|
||||
.loadClass("com.tencent.common.app.BaseApplicationImpl");
|
||||
Field fsApp = null;
|
||||
for (Field f : clz.getDeclaredFields()) {
|
||||
if (f.getType() == clz) {
|
||||
fsApp = f;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (fsApp == null) {
|
||||
throw new NoSuchFieldException("field BaseApplicationImpl.sApplication not found");
|
||||
}
|
||||
app = (Context) fsApp.get(null);
|
||||
ClassLoader cl = param.thisObject.getClass().getClassLoader();
|
||||
Context app = StartupAgent.getBaseApplicationImpl(cl);
|
||||
execStartupInit(app, param.thisObject, null, false);
|
||||
} catch (Throwable e) {
|
||||
log_e(e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
};
|
||||
Class<?> loadDex = findLoadDexTaskClass(rtLoader);
|
||||
@@ -218,7 +158,6 @@ public class StartupHook {
|
||||
}
|
||||
}
|
||||
XposedBridge.hookMethod(m, startup);
|
||||
mFirstStageInit = true;
|
||||
} catch (Throwable e) {
|
||||
if ((e + "").contains("com.bug.zqq")) {
|
||||
return;
|
||||
@@ -227,7 +166,7 @@ public class StartupHook {
|
||||
return;
|
||||
}
|
||||
log_e(e);
|
||||
throw e;
|
||||
throw IoUtils.unsafeThrow(e);
|
||||
}
|
||||
try {
|
||||
XposedHelpers.findAndHookMethod(rtLoader.loadClass("com.tencent.mobileqq.qfix.QFixApplication"),
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* QAuxiliary - An Xposed module for QQ/TIM
|
||||
* Copyright (C) 2019-2024 QAuxiliary developers
|
||||
* https://github.com/cinit/QAuxiliary
|
||||
*
|
||||
* This software is an opensource software: you can redistribute it
|
||||
* and/or modify it under the terms of the General Public License
|
||||
* as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version as published
|
||||
* by QAuxiliary contributors.
|
||||
*
|
||||
* This software 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the General Public License
|
||||
* along with this software.
|
||||
* If not, see
|
||||
* <https://github.com/cinit/QAuxiliary/blob/master/LICENSE.md>.
|
||||
*/
|
||||
|
||||
package io.github.qauxv.poststartup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import io.github.qauxv.loader.hookapi.IHookBridge;
|
||||
import io.github.qauxv.loader.hookapi.ILoaderInfo;
|
||||
import java.util.Objects;
|
||||
|
||||
public class StartupInfo {
|
||||
|
||||
private StartupInfo() {
|
||||
throw new AssertionError("No instance for you!");
|
||||
}
|
||||
|
||||
private static String modulePath;
|
||||
|
||||
private static ILoaderInfo loaderInfo;
|
||||
|
||||
private static IHookBridge hookBridge;
|
||||
|
||||
private static Boolean inHostProcess = null;
|
||||
|
||||
@NonNull
|
||||
public static String getModulePath() {
|
||||
return modulePath;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static ILoaderInfo getLoaderInfo() {
|
||||
return loaderInfo;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static IHookBridge getHookBridge() {
|
||||
return hookBridge;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static IHookBridge requireHookBridge() {
|
||||
if (hookBridge == null) {
|
||||
throw new IllegalStateException("HookBridge is not initialized");
|
||||
}
|
||||
return hookBridge;
|
||||
}
|
||||
|
||||
public static void setHookBridge(@Nullable IHookBridge hookBridge) {
|
||||
StartupInfo.hookBridge = hookBridge;
|
||||
}
|
||||
|
||||
public static void setLoaderInfo(@NonNull ILoaderInfo loaderInfo) {
|
||||
Objects.requireNonNull(loaderInfo);
|
||||
StartupInfo.loaderInfo = loaderInfo;
|
||||
}
|
||||
|
||||
public static void setModulePath(@NonNull String modulePath) {
|
||||
Objects.requireNonNull(modulePath);
|
||||
StartupInfo.modulePath = modulePath;
|
||||
}
|
||||
|
||||
public static boolean isInHostProcess() {
|
||||
if (inHostProcess == null) {
|
||||
throw new IllegalStateException("Host process status is not initialized");
|
||||
}
|
||||
return inHostProcess;
|
||||
}
|
||||
|
||||
public static void setInHostProcess(boolean inHostProcess) {
|
||||
if (StartupInfo.inHostProcess != null) {
|
||||
throw new IllegalStateException("Host process status is already initialized");
|
||||
}
|
||||
StartupInfo.inHostProcess = inHostProcess;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,38 +1,37 @@
|
||||
/*
|
||||
* QAuxiliary - An Xposed module for QQ/TIM
|
||||
* Copyright (C) 2019-2022 qwq233@qwq2333.top
|
||||
* Copyright (C) 2019-2024 QAuxiliary developers
|
||||
* https://github.com/cinit/QAuxiliary
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
* and/or modify it under the terms of the GNU Affero General Public License
|
||||
* This software is an opensource software: you can redistribute it
|
||||
* and/or modify it under the terms of the General Public License
|
||||
* as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version and our eula as published
|
||||
* version 3 of the License, or any later version as published
|
||||
* by QAuxiliary contributors.
|
||||
*
|
||||
* This software 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
|
||||
* Affero General Public License for more details.
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
* You should have received a copy of the General Public License
|
||||
* along with this software.
|
||||
* If not, see
|
||||
* <https://github.com/cinit/QAuxiliary/blob/master/LICENSE.md>.
|
||||
*/
|
||||
package io.github.qauxv.startup;
|
||||
package io.github.qauxv.poststartup;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Application;
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import com.github.kyuubiran.ezxhelper.init.EzXHelperInit;
|
||||
import com.github.kyuubiran.ezxhelper.init.InitFields;
|
||||
import com.github.kyuubiran.ezxhelper.utils.Log;
|
||||
import io.github.qauxv.core.MainHook;
|
||||
import io.github.qauxv.core.NativeCoreBridge;
|
||||
import io.github.qauxv.util.HostInfo;
|
||||
import io.github.qauxv.util.Initiator;
|
||||
import io.github.qauxv.util.Natives;
|
||||
import java.lang.reflect.Field;
|
||||
import org.lsposed.hiddenapibypass.HiddenApiBypass;
|
||||
|
||||
public class StartupRoutine {
|
||||
|
||||
@@ -51,16 +50,13 @@ public class StartupRoutine {
|
||||
* @param bReserved false, not used
|
||||
*/
|
||||
public static void execPostStartupInit(Context ctx, Object step, String lpwReserved, boolean bReserved) {
|
||||
ensureHiddenApiAccess();
|
||||
// init all kotlin utils here
|
||||
EzXHelperInit.INSTANCE.initZygote(HookEntry.getInitZygoteStartupParam());
|
||||
EzXHelperInit.INSTANCE.initHandleLoadPackage(HookEntry.getLoadPackageParam());
|
||||
// resource injection is done somewhere else, do not init it here
|
||||
EzXHelperInit.INSTANCE.initAppContext(ctx, false, false);
|
||||
EzXHelperInit.INSTANCE.setLogTag("QAuxv");
|
||||
HostInfo.init((Application) ctx);
|
||||
Initiator.init(ctx.getClassLoader());
|
||||
Natives.load(ctx);
|
||||
InitFields.ezXClassLoader = ctx.getClassLoader();
|
||||
// resource injection is done somewhere else, do not init it here
|
||||
Log.INSTANCE.getCurrentLogger().setLogTag("QAuxv");
|
||||
Natives.initialize(ctx);
|
||||
overrideLSPatchModifiedVersionCodeIfNecessary(ctx);
|
||||
NativeCoreBridge.initNativeCore(ctx.getPackageName(), Build.VERSION.SDK_INT,
|
||||
HostInfo.getHostInfo().getVersionName(), HostInfo.getHostInfo().getVersionCode());
|
||||
@@ -92,31 +88,4 @@ public class StartupRoutine {
|
||||
}
|
||||
}
|
||||
|
||||
private static void ensureHiddenApiAccess() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && !isHiddenApiAccessible()) {
|
||||
android.util.Log.w("QAuxv", "Hidden API access not accessible, SDK_INT is " + Build.VERSION.SDK_INT);
|
||||
HiddenApiBypass.setHiddenApiExemptions("L");
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint({"BlockedPrivateApi", "PrivateApi"})
|
||||
public static boolean isHiddenApiAccessible() {
|
||||
Class<?> kContextImpl;
|
||||
try {
|
||||
kContextImpl = Class.forName("android.app.ContextImpl");
|
||||
} catch (ClassNotFoundException e) {
|
||||
return false;
|
||||
}
|
||||
Field mActivityToken = null;
|
||||
Field mToken = null;
|
||||
try {
|
||||
mActivityToken = kContextImpl.getDeclaredField("mActivityToken");
|
||||
} catch (NoSuchFieldException ignored) {
|
||||
}
|
||||
try {
|
||||
mToken = kContextImpl.getDeclaredField("mToken");
|
||||
} catch (NoSuchFieldException ignored) {
|
||||
}
|
||||
return mActivityToken != null || mToken != null;
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,7 @@
|
||||
*/
|
||||
package io.github.qauxv.router.decorator
|
||||
|
||||
import de.robv.android.xposed.XC_MethodHook
|
||||
import io.github.qauxv.util.xpcompat.XC_MethodHook
|
||||
import io.github.qauxv.base.IDynamicHook
|
||||
import io.github.qauxv.base.RuntimeErrorTracer
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user