[core] Completely remove SandHook (#293)

This commit is contained in:
LoveSy 2021-03-08 22:50:43 +08:00 committed by GitHub
parent 370a400abb
commit 7d52c215ad
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
590 changed files with 207 additions and 107500 deletions

View File

@ -163,37 +163,6 @@ public class ConfigManager {
} }
} }
public static int getVariant() {
try {
return LSPosedManagerServiceClient.getVariant();
} catch (RemoteException | NullPointerException e) {
Log.e(App.TAG, Log.getStackTraceString(e));
return 1;
}
}
public static String getVariantString() {
int variant = getVariant();
switch (variant) {
case 1:
return "YAHFA";
case 2:
return "SandHook";
default:
return "Unknown";
}
}
public static boolean setVariant(int variant) {
try {
LSPosedManagerServiceClient.setVariant(variant);
return true;
} catch (RemoteException | NullPointerException e) {
Log.e(App.TAG, Log.getStackTraceString(e));
return false;
}
}
public static boolean isPermissive() { public static boolean isPermissive() {
try { try {
return LSPosedManagerServiceClient.isPermissive(); return LSPosedManagerServiceClient.isPermissive();

View File

@ -115,16 +115,6 @@ public class LSPosedManagerServiceClient {
service.setVerboseLog(enabled); service.setVerboseLog(enabled);
} }
public static int getVariant() throws RemoteException, NullPointerException {
ensureService();
return service.getVariant();
}
public static void setVariant(int variant) throws RemoteException, NullPointerException {
ensureService();
service.setVariant(variant);
}
public static boolean isPermissive() throws RemoteException, NullPointerException { public static boolean isPermissive() throws RemoteException, NullPointerException {
ensureService(); ensureService();
return service.isPermissive(); return service.isPermissive();

View File

@ -92,7 +92,7 @@ public class MainActivity extends BaseActivity {
String installXposedVersion = ConfigManager.getXposedVersionName(); String installXposedVersion = ConfigManager.getXposedVersionName();
int cardBackgroundColor; int cardBackgroundColor;
if (installXposedVersion != null) { if (installXposedVersion != null) {
binding.statusTitle.setText(getString(R.string.Activated, ConfigManager.getVariantString())); binding.statusTitle.setText(getString(R.string.Activated, "YAHFA"));
if (!ConfigManager.isPermissive()) { if (!ConfigManager.isPermissive()) {
if (Helpers.currentHoliday == Helpers.Holidays.LUNARNEWYEAR) { if (Helpers.currentHoliday == Helpers.Holidays.LUNARNEWYEAR) {
cardBackgroundColor = 0xfff05654; cardBackgroundColor = 0xfff05654;

View File

@ -41,7 +41,7 @@ val commitCount = Git(repo).log().add(refId).call().count()
val defaultManagerPackageName by extra("io.github.lsposed.manager") val defaultManagerPackageName by extra("io.github.lsposed.manager")
val verCode by extra(commitCount + 4200) val verCode by extra(commitCount + 4200)
val verName by extra("v1.2.2") val verName by extra("v1.3.0")
val androidTargetSdkVersion by extra(30) val androidTargetSdkVersion by extra(30)
val androidMinSdkVersion by extra(27) val androidMinSdkVersion by extra(27)
val androidBuildToolsVersion by extra("30.0.3") val androidBuildToolsVersion by extra("30.0.3")

View File

@ -66,7 +66,6 @@ val verName: String by rootProject.extra
dependencies { dependencies {
implementation("dev.rikka.ndk:riru:10") implementation("dev.rikka.ndk:riru:10")
implementation("com.android.tools.build:apksig:4.1.2") implementation("com.android.tools.build:apksig:4.1.2")
implementation(project(":sandhook-hooklib"))
compileOnly(project(":hiddenapi-stubs")) compileOnly(project(":hiddenapi-stubs"))
compileOnly("androidx.annotation:annotation:1.1.0") compileOnly("androidx.annotation:annotation:1.1.0")
implementation(project(":interface")) implementation(project(":interface"))

View File

@ -5,8 +5,6 @@ interface ILSPApplicationService {
IBinder requestManagerBinder() = 3; IBinder requestManagerBinder() = 3;
int getVariant() = 4;
boolean isResourcesHookEnabled() = 5; boolean isResourcesHookEnabled() = 5;
String[] getModulesList(String processName) = 6; String[] getModulesList(String processName) = 6;

View File

@ -15,8 +15,5 @@ add_subdirectory(Dobby)
target_include_directories(dobby PUBLIC Dobby/include) target_include_directories(dobby PUBLIC Dobby/include)
target_include_directories(dobby PUBLIC Dobby/builtin-plugin/BionicLinkerRestriction) target_include_directories(dobby PUBLIC Dobby/builtin-plugin/BionicLinkerRestriction)
add_subdirectory(SandHook)
target_include_directories(sandhook.lspd PUBLIC SandHook)
add_subdirectory(DexBuilder) add_subdirectory(DexBuilder)
target_include_directories(dex_builder PUBLIC DexBuilder) target_include_directories(dex_builder PUBLIC DexBuilder)

View File

@ -1,36 +0,0 @@
cmake_minimum_required(VERSION 3.4.1)
project(sandhook.lspd)
ENABLE_LANGUAGE(ASM)
add_definitions(-std=c++11)
if (${CMAKE_ANDROID_ARCH_ABI} STREQUAL armeabi-v7a OR ${CMAKE_ANDROID_ARCH_ABI} STREQUAL arm64-v8a)
set(${PROJECT_NAME}_SOURCES
sandhook.cpp
trampoline/trampoline.cpp
trampoline/trampoline_manager.cpp
utils/dlfcn_nougat.cpp
utils/hide_api.cpp
utils/utils.cpp
utils/offset.cpp
utils/elf_util.cpp
casts/cast_art_method.cpp
casts/cast_compiler_options.cpp
art/art_method.cpp
art/art_compiler_options.cpp
art/art_classlinker.cpp
trampoline/arch/arm32.S
trampoline/arch/arm64.S
inst/insts_arm32.cpp
inst/insts_arm64.cpp
nativehook/native_hook.cpp
)
else()
set(${PROJECT_NAME}_SOURCES dummy.cpp)
endif()
add_library(${PROJECT_NAME}
STATIC
${${PROJECT_NAME}_SOURCES})
target_link_libraries(${PROJECT_NAME} log)

View File

@ -1,9 +0,0 @@
//
// Created by 双草酸酯 on 11/27/20.
//
#include "../includes/art_classlinker.h"
using namespace art;
void ClassLinker::MakeInitializedClassesVisiblyInitialized(void* self, bool wait) {
}

View File

@ -1,25 +0,0 @@
//
// Created by 甘尧 on 2019/2/24.
//
#include "../includes/art_compiler_options.h"
#include "../includes/cast_compiler_options.h"
#include "../includes/hide_api.h"
using namespace SandHook;
using namespace art;
extern int SDK_INT;
size_t CompilerOptions::getInlineMaxCodeUnits() {
if (SDK_INT < ANDROID_N)
return 0;
return CastCompilerOptions::inlineMaxCodeUnits->get(this);
}
bool CompilerOptions::setInlineMaxCodeUnits(size_t units) {
if (SDK_INT < ANDROID_N)
return false;
CastCompilerOptions::inlineMaxCodeUnits->set(this, units);
return true;
}

View File

@ -1,197 +0,0 @@
//
// Created by swift on 2019/2/3.
//
#include <cstdint>
#include "../includes/art_method.h"
#include "../includes/cast_art_method.h"
#include "../includes/hide_api.h"
#include "../includes/utils.h"
extern int SDK_INT;
extern bool DEBUG;
using namespace art::mirror;
using namespace SandHook;
// Non-intrinsics: Caches whether we can use fast-path in the interpreter invokes.
// Intrinsics: These bits are part of the intrinsic ordinal.
static constexpr uint32_t kAccFastInterpreterToInterpreterInvoke = 0x40000000; // method.
void ArtMethod::tryDisableInline() {
if (SDK_INT < ANDROID_O)
return;
uint32_t accessFlag = getAccessFlags();
accessFlag &= ~ 0x08000000;
setAccessFlags(accessFlag);
}
void ArtMethod::disableInterpreterForO() {
/*if (SDK_INT >= ANDROID_O && SDK_INT < ANDROID_R && DEBUG) {
setNative();
}*/
}
void ArtMethod::disableFastInterpreterForQ() {
if (SDK_INT < ANDROID_Q)
return;
uint32_t accessFlag = getAccessFlags();
accessFlag &= ~kAccFastInterpreterToInterpreterInvoke;
setAccessFlags(accessFlag);
}
void ArtMethod::disableCompilable() {
if (SDK_INT < ANDROID_N)
return;
uint32_t accessFlag = getAccessFlags();
if (SDK_INT >= ANDROID_O2) {
accessFlag |= 0x02000000;
accessFlag |= 0x00800000;
} else {
accessFlag |= 0x01000000;
}
setAccessFlags(accessFlag);
}
bool ArtMethod::isAbstract() {
uint32_t accessFlags = getAccessFlags();
return ((accessFlags & 0x0400) != 0);
}
bool ArtMethod::isNative() {
uint32_t accessFlags = getAccessFlags();
return ((accessFlags & 0x0100) != 0);
}
bool ArtMethod::isStatic() {
uint32_t accessFlags = getAccessFlags();
return ((accessFlags & 0x0008) != 0);
}
bool ArtMethod::isCompiled() {
return getQuickCodeEntry() != CastArtMethod::quickToInterpreterBridge &&
getQuickCodeEntry() != CastArtMethod::genericJniStub;
}
bool ArtMethod::isThumbCode() {
#if defined(__arm__)
return (reinterpret_cast<Size>(getQuickCodeEntry()) & 0x1) == 0x1;
#else
return false;
#endif
}
void ArtMethod::setAccessFlags(uint32_t flags) {
CastArtMethod::accessFlag->set(this, flags);
}
void ArtMethod::setPrivate() {
uint32_t accessFlag = getAccessFlags();
accessFlag &= ~ 0x0001;
accessFlag &= ~ 0x0004;
accessFlag |= 0x0002;
setAccessFlags(accessFlag);
}
void ArtMethod::setStatic() {
uint32_t accessFlag = getAccessFlags();
accessFlag |= 0x0008;
setAccessFlags(accessFlag);
};
void ArtMethod::setNative() {
uint32_t accessFlag = getAccessFlags();
accessFlag |= 0x0100;
setAccessFlags(accessFlag);
}
uint32_t ArtMethod::getAccessFlags() {
return CastArtMethod::accessFlag->get(this);
}
uint32_t ArtMethod::getDexMethodIndex() {
return CastArtMethod::dexMethodIndex->get(this);
}
void* ArtMethod::getQuickCodeEntry() {
return CastArtMethod::entryPointQuickCompiled->get(this);
}
void* ArtMethod::getInterpreterCodeEntry() {
return CastArtMethod::entryPointFromInterpreter->get(this);
}
GCRoot ArtMethod::getDeclaringClass() {
return CastArtMethod::declaringClass->get(this);
}
uint16_t ArtMethod::getHotnessCount() {
return CastArtMethod::hotnessCount->get(this);
}
void ArtMethod::setQuickCodeEntry(void *entry) {
CastArtMethod::entryPointQuickCompiled->set(this, entry);
}
void ArtMethod::setJniCodeEntry(void *entry) {
CastArtMethod::entryPointFromJNI->set(this, entry);
}
void ArtMethod::setInterpreterCodeEntry(void *entry) {
CastArtMethod::entryPointFromInterpreter->set(this, entry);
}
void ArtMethod::setDexCacheResolveList(void *list) {
CastArtMethod::dexCacheResolvedMethods->set(this, list);
}
void ArtMethod::setDexCacheResolveItem(uint32_t index, void* item) {
CastArtMethod::dexCacheResolvedMethods->setElement(this, index, item);
}
void ArtMethod::setDeclaringClass(GCRoot classPtr) {
CastArtMethod::declaringClass->set(this, classPtr);
}
void ArtMethod::setHotnessCount(uint16_t count) {
CastArtMethod::hotnessCount->set(this, count);
}
bool ArtMethod::compile(JNIEnv* env) {
if (isCompiled())
return true;
//some unknown error when trigger jit for jni method manually
if (isNative())
return false;
Size threadId = getAddressFromJavaByCallMethod(env, "com/swift/sandhook/SandHook", "getThreadId");
if (threadId == 0)
return false;
return compileMethod(this, reinterpret_cast<void *>(threadId)) && isCompiled();
}
bool ArtMethod::deCompile() {
if (!isCompiled())
return true;
if ((isNative() && CastArtMethod::canGetJniBridge) || (!isNative() && CastArtMethod::canGetInterpreterBridge)) {
setQuickCodeEntry(isNative() ? CastArtMethod::genericJniStub : CastArtMethod::quickToInterpreterBridge);
if (SDK_INT < ANDROID_N) {
//TODO SetEntryPointFromInterpreterCode
}
flushCache();
return true;
} else {
return false;
}
}
void ArtMethod::flushCache() {
// flushCacheExt(reinterpret_cast<Size>(this), size());
}
void ArtMethod::backup(ArtMethod *backup) {
memcpy(backup, this, size());
}
Size ArtMethod::size() {
return CastArtMethod::size;
}

View File

@ -1,290 +0,0 @@
//
// Created by swift on 2019/2/3.
//
#include "../includes/cast_art_method.h"
#include "../includes/utils.h"
#include "../includes/never_call.h"
#include "../includes/log.h"
extern int SDK_INT;
namespace SandHook {
class CastDexCacheResolvedMethods : public ArrayMember<art::mirror::ArtMethod, void *> {
protected:
Size calOffset(JNIEnv *jniEnv, art::mirror::ArtMethod *p) override {
if (SDK_INT >= ANDROID_P)
return getParentSize() + 1;
int offset = 0;
Size addr = getAddressFromJava(jniEnv, "com/swift/sandhook/SandHookMethodResolver",
"resolvedMethodsAddress");
if (addr != 0) {
offset = findOffset(p, getParentSize(), 2, addr);
if (offset >= 0) {
return static_cast<Size>(offset);
}
}
if (SDK_INT == ANDROID_M) {
return 4;
} else if (SDK_INT >= ANDROID_L && SDK_INT <= ANDROID_L2) {
return 4 * 3;
}
return getParentSize() + 1;
}
public:
Size arrayStart(mirror::ArtMethod *parent) override {
void *p = IMember<mirror::ArtMethod, void *>::get(parent);
if (SDK_INT <= ANDROID_M) {
return reinterpret_cast<Size>(p) + 4 * 3;
} else {
return reinterpret_cast<Size>(p);
}
}
};
class CastEntryPointFormInterpreter : public IMember<art::mirror::ArtMethod, void *> {
protected:
Size calOffset(JNIEnv *jniEnv, art::mirror::ArtMethod *p) override {
if (SDK_INT == ANDROID_L2) {
return RoundUpToPtrSize(4 * 7 + 4 * 2);
} else if (SDK_INT == ANDROID_M) {
return getParentSize() - 3 * BYTE_POINT;
} else if (SDK_INT <= ANDROID_L) {
Size addr = getAddressFromJava(jniEnv, "com/swift/sandhook/SandHookMethodResolver",
"entryPointFromInterpreter");
int offset = 0;
if (addr != 0) {
offset = findOffset(p, getParentSize(), 2, addr);
if (offset >= 0) {
return static_cast<Size>(offset);
}
}
return getParentSize() - 4 * 8 - 4 * 4;
}
else
return getParentSize() + 1;
}
};
class CastEntryPointQuickCompiled : public IMember<art::mirror::ArtMethod, void *> {
protected:
Size calOffset(JNIEnv *jniEnv, art::mirror::ArtMethod *p) override {
if (SDK_INT >= ANDROID_M) {
return getParentSize() - BYTE_POINT;
} else if (SDK_INT <= ANDROID_L) {
Size addr = getAddressFromJava(jniEnv, "com/swift/sandhook/SandHookMethodResolver",
"entryPointFromCompiledCode");
int offset = 0;
if (addr != 0) {
offset = findOffset(p, getParentSize(), 2, addr);
if (offset >= 0) {
return static_cast<Size>(offset);
}
}
return getParentSize() - 4 - 2 * BYTE_POINT;
} else {
return CastArtMethod::entryPointFromInterpreter->getOffset() + 2 * BYTE_POINT;
}
}
};
class CastEntryPointFromJni : public IMember<art::mirror::ArtMethod, void *> {
protected:
Size calOffset(JNIEnv *jniEnv, art::mirror::ArtMethod *p) override {
Size jniAddr = reinterpret_cast<Size>(Java_com_swift_sandhook_ClassNeverCall_neverCallNative);
int offset = findOffset(p, getParentSize(), 2, jniAddr);
if (offset >= 0) {
return static_cast<Size>(offset);
}
if (SDK_INT >= ANDROID_L2 && SDK_INT <= ANDROID_N) {
return getParentSize() - 2 * BYTE_POINT;
} else {
return getParentSize() - 8 * 2 - 4 * 4;
}
}
};
class CastAccessFlag : public IMember<art::mirror::ArtMethod, uint32_t> {
protected:
Size calOffset(JNIEnv *jniEnv, art::mirror::ArtMethod *p) override {
uint32_t accessFlag = getIntFromJava(jniEnv, "com/swift/sandhook/SandHook",
"testAccessFlag");
if (accessFlag == 0) {
accessFlag = 524313;
//kAccPublicApi
if (SDK_INT >= ANDROID_Q) {
accessFlag |= 0x10000000;
}
}
int offset = findOffset(p, getParentSize(), 2, accessFlag);
if (offset < 0) {
if (SDK_INT >= ANDROID_N) {
return 4;
} else if (SDK_INT == ANDROID_L2) {
return 20;
} else if (SDK_INT == ANDROID_L) {
return 56;
} else {
return getParentSize() + 1;
}
} else {
return static_cast<size_t>(offset);
}
}
};
class CastShadowClass : public IMember<art::mirror::ArtMethod, GCRoot> {
protected:
Size calOffset(JNIEnv *jniEnv, mirror::ArtMethod *p) override {
if (SDK_INT < ANDROID_N)
return getParentSize() + 1;
return 0;
}
};
class CastDexMethodIndex : public IMember<art::mirror::ArtMethod, uint32_t> {
protected:
Size calOffset(JNIEnv *jniEnv, art::mirror::ArtMethod *p) override {
if (SDK_INT >= ANDROID_P) {
return CastArtMethod::accessFlag->getOffset()
+ CastArtMethod::accessFlag->size()
+ sizeof(uint32_t);
}
int offset = 0;
jint index = getIntFromJava(jniEnv, "com/swift/sandhook/SandHookMethodResolver",
"dexMethodIndex");
if (index != 0) {
offset = findOffset(p, getParentSize(), 2, static_cast<uint32_t>(index));
if (offset >= 0) {
return static_cast<Size>(offset);
}
}
return getParentSize() + 1;
}
};
class CastHotnessCount : public IMember<art::mirror::ArtMethod, uint16_t> {
protected:
Size calOffset(JNIEnv *jniEnv, mirror::ArtMethod *p) override {
if (SDK_INT <= ANDROID_N)
return getParentSize() + 1;
return CastArtMethod::dexMethodIndex->getOffset()
+ CastArtMethod::dexMethodIndex->size()
+ sizeof(uint16_t);
}
};
void CastArtMethod::init(JNIEnv *env) {
//init ArtMethodSize
jclass sizeTestClass = env->FindClass("com/swift/sandhook/ArtMethodSizeTest");
jobject artMethod1 = getMethodObject(env, "com.swift.sandhook.ArtMethodSizeTest", "method1");
jobject artMethod2 = getMethodObject(env, "com.swift.sandhook.ArtMethodSizeTest", "method2");
env->CallStaticVoidMethod(sizeTestClass, env->FromReflectedMethod(artMethod1));
std::atomic_thread_fence(std::memory_order_acquire);
art::mirror::ArtMethod *m1 = getArtMethod(env, artMethod1);
art::mirror::ArtMethod *m2 = getArtMethod(env, artMethod2);
size = m2 - m1;
//init Members
accessFlag = new CastAccessFlag();
accessFlag->init(env, m1, size);
entryPointFromInterpreter = new CastEntryPointFormInterpreter();
entryPointFromInterpreter->init(env, m1, size);
entryPointQuickCompiled = new CastEntryPointQuickCompiled();
entryPointQuickCompiled->init(env, m1, size);
dexMethodIndex = new CastDexMethodIndex();
dexMethodIndex->init(env, m1, size);
dexCacheResolvedMethods = new CastDexCacheResolvedMethods();
dexCacheResolvedMethods->init(env, m1, size);
declaringClass = new CastShadowClass();
declaringClass->init(env, m1, size);
hotnessCount = new CastHotnessCount();
hotnessCount->init(env, m1, size);
auto neverCallTestClass = "com.swift.sandhook.ClassNeverCall";
art::mirror::ArtMethod *neverCall = getArtMethod(env, getMethodObject(env,
neverCallTestClass,
"neverCall"));
art::mirror::ArtMethod *neverCall2 = getArtMethod(env, getMethodObject(env,
neverCallTestClass,
"neverCall2"));
bool beAot = entryPointQuickCompiled->get(neverCall) != entryPointQuickCompiled->get(neverCall2);
if (beAot) {
quickToInterpreterBridge = getInterpreterBridge(false);
if (quickToInterpreterBridge == nullptr) {
quickToInterpreterBridge = entryPointQuickCompiled->get(neverCall);
canGetInterpreterBridge = false;
}
} else {
quickToInterpreterBridge = entryPointQuickCompiled->get(neverCall);
}
art::mirror::ArtMethod *neverCallNative = getArtMethod(env, getMethodObject(env,
neverCallTestClass,
"neverCallNative"));
art::mirror::ArtMethod *neverCallNative2 = getArtMethod(env, getMethodObject(env,
neverCallTestClass,
"neverCallNative2"));
beAot = entryPointQuickCompiled->get(neverCallNative) != entryPointQuickCompiled->get(neverCallNative2);
if (beAot) {
genericJniStub = getInterpreterBridge(true);
if (genericJniStub == nullptr) {
genericJniStub = entryPointQuickCompiled->get(neverCallNative);
canGetJniBridge = false;
}
} else {
genericJniStub = entryPointQuickCompiled->get(neverCallNative);
}
entryPointFromJNI = new CastEntryPointFromJni();
entryPointFromJNI->init(env, neverCallNative, size);
art::mirror::ArtMethod *neverCallStatic = getArtMethod(env, getMethodObject(env,
neverCallTestClass,
"neverCallStatic"));
staticResolveStub = entryPointQuickCompiled->get(neverCallStatic);
}
void CastArtMethod::copy(art::mirror::ArtMethod *from, art::mirror::ArtMethod *to) {
memcpy(to, from, size);
}
Size CastArtMethod::size = 0;
IMember<art::mirror::ArtMethod, void *> *CastArtMethod::entryPointQuickCompiled = nullptr;
IMember<art::mirror::ArtMethod, void *> *CastArtMethod::entryPointFromInterpreter = nullptr;
IMember<art::mirror::ArtMethod, void *> *CastArtMethod::entryPointFromJNI = nullptr;
ArrayMember<art::mirror::ArtMethod, void *> *CastArtMethod::dexCacheResolvedMethods = nullptr;
IMember<art::mirror::ArtMethod, uint32_t> *CastArtMethod::dexMethodIndex = nullptr;
IMember<art::mirror::ArtMethod, uint32_t> *CastArtMethod::accessFlag = nullptr;
IMember<art::mirror::ArtMethod, GCRoot> *CastArtMethod::declaringClass = nullptr;
IMember<art::mirror::ArtMethod, uint16_t> *CastArtMethod::hotnessCount = nullptr;
void *CastArtMethod::quickToInterpreterBridge = nullptr;
void *CastArtMethod::genericJniStub = nullptr;
void *CastArtMethod::staticResolveStub = nullptr;
bool CastArtMethod::canGetInterpreterBridge = true;
bool CastArtMethod::canGetJniBridge = true;
}

View File

@ -1,35 +0,0 @@
//
// Created by 甘尧 on 2019/2/24.
//
#include "../includes/cast_compiler_options.h"
#include "../includes/hide_api.h"
extern int SDK_INT;
namespace SandHook {
class CastInlineMaxCodeUnits : public IMember<art::CompilerOptions, size_t> {
protected:
Size calOffset(JNIEnv *jniEnv, art::CompilerOptions *p) override {
if (SDK_INT < ANDROID_N)
return getParentSize() + 1;
if (SDK_INT >= ANDROID_Q) {
return BYTE_POINT + 3 * sizeof(size_t);
}
if (SDK_INT >= ANDROID_O) {
return BYTE_POINT + 5 * sizeof(size_t);
} else {
return BYTE_POINT + 6 * sizeof(size_t);
}
}
};
void CastCompilerOptions::init(JNIEnv *jniEnv) {
inlineMaxCodeUnits->init(jniEnv, nullptr, sizeof(art::CompilerOptions));
}
IMember<art::CompilerOptions, size_t>* CastCompilerOptions::inlineMaxCodeUnits = new CastInlineMaxCodeUnits();
}

View File

@ -1,10 +0,0 @@
//
// Created by Kotori0 on 2021/1/30.
//
#include "sandhook.h"
#include "includes/log.h"
bool JNI_Load_Ex(JNIEnv* env, jclass classSandHook, jclass classNeverCall) {
LOGE("Sandhook: Unsupported platform.");
return false;
}

View File

@ -1,49 +0,0 @@
//
// Created by 甘尧 on 2019/1/12.
//
#ifndef SANDHOOK_ARCH_H
#define SANDHOOK_ARCH_H
#define BYTE_POINT sizeof(void*)
typedef size_t Size;
//32bit
#if defined(__i386__) || defined(__arm__)
//64bit
#elif defined(__aarch64__) || defined(__x86_64__)
#else
#endif
#if defined(__arm__)
static void clearCacheArm32(char* begin, char *end)
{
const int syscall = 0xf0002;
__asm __volatile (
"mov r0, %0\n"
"mov r1, %1\n"
"mov r3, %2\n"
"mov r2, #0x0\n"
"svc 0x00000000\n"
:
: "r" (begin), "r" (end), "r" (syscall)
: "r0", "r1", "r3"
);
}
#endif
#define ANDROID_K 19
#define ANDROID_L 21
#define ANDROID_L2 22
#define ANDROID_M 23
#define ANDROID_N 24
#define ANDROID_N2 25
#define ANDROID_O 26
#define ANDROID_O2 27
#define ANDROID_P 28
#define ANDROID_Q 29
#define ANDROID_R 30
#define ANDROID_S 31
#endif //SANDHOOK_ARCH_H

View File

@ -1,34 +0,0 @@
//
// Created by SwiftGan on 2019/1/17.
//
#ifndef SANDHOOK_BASE_H
#define SANDHOOK_BASE_H
#define FUNCTION_START(x) \
.text; \
.align 4; \
.global x; \
x: \
#define FUNCTION_START_T(x) \
.syntax unified; \
.text; \
.align 4; \
.thumb; \
.thumb_func; \
.global x; \
x: \
#define FUNCTION_END(x) .size x, .-x
#define REPLACEMENT_HOOK_TRAMPOLINE replacement_hook_trampoline
#define INLINE_HOOK_TRAMPOLINE inline_hook_trampoline
#define DIRECT_JUMP_TRAMPOLINE direct_jump_trampoline
#define CALL_ORIGIN_TRAMPOLINE call_origin_trampoline
#define INLINE_HOOK_TRAMPOLINE_T inline_hook_trampoline_t
#define DIRECT_JUMP_TRAMPOLINE_T direct_jump_trampoline_t
#define CALL_ORIGIN_TRAMPOLINE_T call_origin_trampoline_t
#endif //SANDHOOK_BASE_H

View File

@ -1,14 +0,0 @@
//
// Created by 双草酸酯 on 11/27/20.
//
#ifndef SANDHOOK_ART_CLASSLINKER_H
#define SANDHOOK_ART_CLASSLINKER_H
#endif //SANDHOOK_ART_CLASSLINKER_H
namespace art {
class ClassLinker {
public:
void MakeInitializedClassesVisiblyInitialized(void* self, bool wait);
};
}

View File

@ -1,66 +0,0 @@
/*
* Copyright (C) 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef ART_RUNTIME_GC_COLLECTOR_TYPE_H_
#define ART_RUNTIME_GC_COLLECTOR_TYPE_H_
#include <iosfwd>
namespace art {
namespace gc {
// Which types of collections are able to be performed.
enum CollectorType {
// No collector selected.
kCollectorTypeNone,
// Non concurrent mark-sweep.
kCollectorTypeMS,
// Concurrent mark-sweep.
kCollectorTypeCMS,
// Semi-space / mark-sweep hybrid, enables compaction.
kCollectorTypeSS,
// Heap trimming collector, doesn't do any actual collecting.
kCollectorTypeHeapTrim,
// A (mostly) concurrent copying collector.
kCollectorTypeCC,
// The background compaction of the concurrent copying collector.
kCollectorTypeCCBackground,
// Instrumentation critical section fake collector.
kCollectorTypeInstrumentation,
// Fake collector for adding or removing application image spaces.
kCollectorTypeAddRemoveAppImageSpace,
// Fake collector used to implement exclusion between GC and debugger.
kCollectorTypeDebugger,
// A homogeneous space compaction collector used in background transition
// when both foreground and background collector are CMS.
kCollectorTypeHomogeneousSpaceCompact,
// Class linker fake collector.
kCollectorTypeClassLinker,
// JIT Code cache fake collector.
kCollectorTypeJitCodeCache,
// Hprof fake collector.
kCollectorTypeHprof,
// Fake collector for installing/removing a system-weak holder.
kCollectorTypeAddRemoveSystemWeakHolder,
// Fake collector type for GetObjectsAllocated
kCollectorTypeGetObjectsAllocated,
// Fake collector type for ScopedGCCriticalSection
kCollectorTypeCriticalSection,
};
} // namespace gc
} // namespace art
#endif // ART_RUNTIME_GC_COLLECTOR_TYPE_H_

View File

@ -1,28 +0,0 @@
//
// Created by 甘尧 on 2019/2/23.
//
#ifndef ART_COMPILER_OPTIONS_H
#define ART_COMPILER_OPTIONS_H
#include <stddef.h>
namespace art {
class CompilerOptions {
public:
void* compiler_filter_;
size_t huge_method_threshold_;
size_t large_method_threshold_;
size_t small_method_threshold_;
size_t tiny_method_threshold_;
size_t num_dex_methods_threshold_;
size_t inline_depth_limit_;
size_t inline_max_code_units_;
size_t getInlineMaxCodeUnits();
bool setInlineMaxCodeUnits(size_t units);
};
}
#endif //ART_COMPILER_OPTIONS_H

View File

@ -1,71 +0,0 @@
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef ART_RUNTIME_GC_GC_CAUSE_H_
#define ART_RUNTIME_GC_GC_CAUSE_H_
#include <iosfwd>
namespace art {
namespace gc {
// What caused the GC?
enum GcCause {
// Invalid GC cause used as a placeholder.
kGcCauseNone,
// GC triggered by a failed allocation. Thread doing allocation is blocked waiting for GC before
// retrying allocation.
kGcCauseForAlloc,
// A background GC trying to ensure there is free memory ahead of allocations.
kGcCauseBackground,
// An explicit System.gc() call.
kGcCauseExplicit,
// GC triggered for a native allocation when NativeAllocationGcWatermark is exceeded.
// (This may be a blocking GC depending on whether we run a non-concurrent collector).
kGcCauseForNativeAlloc,
// GC triggered for a collector transition.
kGcCauseCollectorTransition,
// Not a real GC cause, used when we disable moving GC (currently for GetPrimitiveArrayCritical).
kGcCauseDisableMovingGc,
// Not a real GC cause, used when we trim the heap.
kGcCauseTrim,
// Not a real GC cause, used to implement exclusion between GC and instrumentation.
kGcCauseInstrumentation,
// Not a real GC cause, used to add or remove app image spaces.
kGcCauseAddRemoveAppImageSpace,
// Not a real GC cause, used to implement exclusion between GC and debugger.
kGcCauseDebugger,
// GC triggered for background transition when both foreground and background collector are CMS.
kGcCauseHomogeneousSpaceCompact,
// Class linker cause, used to guard filling art methods with special values.
kGcCauseClassLinker,
// Not a real GC cause, used to implement exclusion between code cache metadata and GC.
kGcCauseJitCodeCache,
// Not a real GC cause, used to add or remove system-weak holders.
kGcCauseAddRemoveSystemWeakHolder,
// Not a real GC cause, used to prevent hprof running in the middle of GC.
kGcCauseHprof,
// Not a real GC cause, used to prevent GetObjectsAllocated running in the middle of GC.
kGcCauseGetObjectsAllocated,
// GC cause for the profile saver.
kGcCauseProfileSaver,
// GC cause for running an empty checkpoint.
kGcCauseRunEmptyCheckpoint,
};
} // namespace gc
} // namespace art
#endif // ART_RUNTIME_GC_GC_CAUSE_H_

View File

@ -1,28 +0,0 @@
//
// Created by 甘尧 on 2019/2/23.
//
#ifndef SANDHOOK_ART_JIT_H
#define SANDHOOK_ART_JIT_H
namespace art {
namespace jit {
//7.0 - 9.0
class JitCompiler {
public:
virtual ~JitCompiler();
std::unique_ptr<art::CompilerOptions> compilerOptions;
};
class Jit {
public:
//void* getCompilerOptions();
};
};
}
#endif //SANDHOOK_ART_JIT_H

View File

@ -1,87 +0,0 @@
/*
*
* Copyright (c) 2011 The Android Open Source Project
* Copyright (c) 2015, alipay.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef ART_H
#define ART_H
#include <jni.h>
#include "arch.h"
//7.0 - 10.0
#define GCRoot uint32_t
namespace art {
namespace mirror {
class Object {
public:
};
class Class: public Object {
public:
};
class ArtField {
public:
};
class ArtMethod {
public:
bool isAbstract();
bool isNative();
bool isStatic();
bool isCompiled();
bool isThumbCode();
void setAccessFlags(uint32_t flags);
void disableCompilable();
void tryDisableInline();
void disableInterpreterForO();
void disableFastInterpreterForQ();
void setPrivate();
void setStatic();
void setNative();
void setQuickCodeEntry(void* entry);
void setJniCodeEntry(void* entry);
void setInterpreterCodeEntry(void* entry);
void setDexCacheResolveList(void* list);
void setDexCacheResolveItem(uint32_t index, void* item);
void setDeclaringClass(GCRoot classPtr);
void setHotnessCount(uint16_t count);
void* getQuickCodeEntry();
void* getInterpreterCodeEntry();
uint32_t getAccessFlags();
uint32_t getDexMethodIndex();
GCRoot getDeclaringClass();
uint16_t getHotnessCount();
bool compile(JNIEnv* env);
bool deCompile();
void flushCache();
void backup(ArtMethod* backup);
static Size size();
};
}
}
#endif //ART_H

View File

@ -1,18 +0,0 @@
//
// Created by 甘尧 on 2019/2/23.
//
#ifndef SANDHOOK_ART_RUNTIME_H
#define SANDHOOK_ART_RUNTIME_H
#include "art_jit.h"
namespace art {
class Runtime {
public:
jit::Jit* getJit();
};
}
#endif //SANDHOOK_ART_RUNTIME_H

View File

@ -1,123 +0,0 @@
//
// Created by 甘尧 on 2019/1/12.
//
#ifndef SANDHOOK_ICAST_H
#define SANDHOOK_ICAST_H
#include <stdint.h>
#include <string.h>
#include <jni.h>
#include "arch.h"
#include "utils.h"
namespace SandHook {
template <typename T>
class cast {
public:
cast(T t) {
this->origin = t;
};
virtual Size getSize() { return sizeof(T); };
private:
T origin;
};
template <typename PType, typename MType>
class IMember {
public:
virtual void init(JNIEnv *jniEnv, PType* p, Size size) {
this->parentSize = size;
offset = calOffset(jniEnv, p);
}
Size size() {
return sizeof(MType);
}
virtual Size getOffset() {
return offset;
}
virtual Size getParentSize() {
return parentSize;
}
virtual MType get(PType* p) {
if (offset > parentSize)
return 0;
return *reinterpret_cast<MType*>((Size)p + getOffset());
};
virtual void set(PType* p, MType t) {
if (offset > parentSize)
return;
memcpy(reinterpret_cast<void *>((Size)p + getOffset()), &t, size());
};
template<typename T>
int findOffset(void *start, size_t len, size_t step, T value) {
if (nullptr == start) {
return -1;
}
for (int i = 0; i <= len; i += step) {
T current_value = *reinterpret_cast<T *>((size_t) start + i);
if (value == current_value) {
return i;
}
}
return -1;
}
private:
Size offset = 0;
protected:
Size parentSize = 0;
virtual Size calOffset(JNIEnv *jniEnv, PType* p) = 0;
};
template<typename PType, typename ElementType>
class ArrayMember : public IMember<PType, void*> {
public:
virtual void init(JNIEnv *jniEnv, PType* p, Size parentSize) override {
IMember<PType,void*>::init(jniEnv, p, parentSize);
elementSize = calElementSize(jniEnv, p);
}
virtual Size getElementSize() {
return elementSize;
}
virtual Size arrayStart(PType* parent) {
void* p = IMember<PType,void*>::get(parent);
return reinterpret_cast<Size>(p);
}
using IMember<PType,void*>::getParentSize;
virtual void setElement(PType* parent, int position, ElementType elementPoint) {
Size array = arrayStart(parent);
memcpy(reinterpret_cast<void*>(array + position * getElementSize()), &elementPoint, getElementSize());
}
private:
Size elementSize = 0;
protected:
virtual Size calElementSize(JNIEnv *jniEnv, PType* p) {
return sizeof(ElementType);
};
};
}
#endif //SANDHOOK_ICAST_H

View File

@ -1,39 +0,0 @@
//
// Created by 甘尧 on 2019/1/12.
//
#ifndef SANDHOOK_CAST_ART_METHOD_H
#define SANDHOOK_CAST_ART_METHOD_H
#include "cast.h"
#include "trampoline_manager.h"
namespace SandHook {
class CastArtMethod {
public:
static Size size;
static IMember<art::mirror::ArtMethod, void*>* entryPointQuickCompiled;
static IMember<art::mirror::ArtMethod, void*>* entryPointFromInterpreter;
static IMember<art::mirror::ArtMethod, void*>* entryPointFromJNI;
static ArrayMember<art::mirror::ArtMethod,void*>* dexCacheResolvedMethods;
static IMember<art::mirror::ArtMethod, uint32_t>* dexMethodIndex;
static IMember<art::mirror::ArtMethod, uint32_t>* accessFlag;
static IMember<art::mirror::ArtMethod, GCRoot>* declaringClass;
static IMember<art::mirror::ArtMethod, uint16_t>* hotnessCount;
static void* quickToInterpreterBridge;
static void* genericJniStub;
static void* staticResolveStub;
static bool canGetJniBridge;
static bool canGetInterpreterBridge;
static void init(JNIEnv *env);
static void copy(art::mirror::ArtMethod* from, art::mirror::ArtMethod* to);
};
}
#endif //SANDHOOK_CAST_ART_METHOD_H

View File

@ -1,24 +0,0 @@
//
// Created by 甘尧 on 2019/1/12.
//
#ifndef SANDHOOK_CAST_COMPILER_OPTIONS_H
#define SANDHOOK_CAST_COMPILER_OPTIONS_H
#include "cast.h"
#include "art_compiler_options.h"
namespace SandHook {
class CastCompilerOptions {
public:
static void init(JNIEnv *jniEnv);
static IMember<art::CompilerOptions, size_t>* inlineMaxCodeUnits;
};
}
#endif //SANDHOOK_CAST_COMPILER_OPTIONS_H

View File

@ -1,17 +0,0 @@
#ifndef DLFCN_NOUGAT_H
#define DLFCN_NOUGAT_H
//see implementation in https://tech.meituan.com/2017/07/20/android-remote-debug.html
extern "C" {
int fake_dlclose(void *handle);
void *fake_dlopen(const char *filename, int flags);
void *fake_dlsym(void *handle, const char *name);
const char *fake_dlerror();
void *getSymCompat(const char *filename, const char *name);
}
#endif //DLFCN_NOUGAT_H

View File

@ -1,72 +0,0 @@
//
// Created by Swift Gan on 2019/3/14.
//
#ifndef SANDHOOK_ELF_UTIL_H
#define SANDHOOK_ELF_UTIL_H
#include <linux/elf.h>
#if defined(__LP64__)
typedef Elf64_Ehdr Elf_Ehdr;
typedef Elf64_Shdr Elf_Shdr;
typedef Elf64_Addr Elf_Addr;
typedef Elf64_Dyn Elf_Dyn;
typedef Elf64_Rela Elf_Rela;
typedef Elf64_Sym Elf_Sym;
typedef Elf64_Off Elf_Off;
#define ELF_R_SYM(i) ELF64_R_SYM(i)
#else
typedef Elf32_Ehdr Elf_Ehdr;
typedef Elf32_Shdr Elf_Shdr;
typedef Elf32_Addr Elf_Addr;
typedef Elf32_Dyn Elf_Dyn;
typedef Elf32_Rel Elf_Rela;
typedef Elf32_Sym Elf_Sym;
typedef Elf32_Off Elf_Off;
#define ELF_R_SYM(i) ELF32_R_SYM(i)
#endif
namespace SandHook {
class ElfImg {
public:
ElfImg(const char* elf);
Elf_Addr getSymbOffset(const char* name);
void* getModuleBase(const char* name);
Elf_Addr getSymbAddress(const char* name);
~ElfImg();
private:
const char* elf = nullptr;
void* base = nullptr;
char* buffer = nullptr;
off_t size = 0;
off_t bias = -4396;
Elf_Ehdr* header = nullptr;
Elf_Shdr* section_header = nullptr;
Elf_Shdr* symtab = nullptr;
Elf_Shdr* strtab = nullptr;
Elf_Shdr* dynsym = nullptr;
Elf_Off dynsym_count = 0;
Elf_Sym* symtab_start = nullptr;
Elf_Sym* dynsym_start = nullptr;
Elf_Sym* strtab_start = nullptr;
Elf_Off symtab_count = 0;
Elf_Off symstr_offset = 0;
Elf_Off symstr_offset_for_symtab = 0;
Elf_Off symtab_offset = 0;
Elf_Off dynsym_offset = 0;
Elf_Off symtab_size = 0;
Elf_Off dynsym_size = 0;
};
}
#endif //SANDHOOK_ELF_UTIL_H

View File

@ -1,59 +0,0 @@
//
// Created by swift on 2019/1/21.
//
#ifndef SANDHOOK_HIDE_API_H
#define SANDHOOK_HIDE_API_H
#include <jni.h>
#include "dlfcn_nougat.h"
#include "dlfcn.h"
#include <memory>
#include "../includes/art_compiler_options.h"
#include "../includes/art_jit.h"
#include "../includes/art_method.h"
#if defined(__aarch64__)
# define __get_tls() ({ void** __val; __asm__("mrs %0, tpidr_el0" : "=r"(__val)); __val; })
#elif defined(__arm__)
# define __get_tls() ({ void** __val; __asm__("mrc p15, 0, %0, c13, c0, 3" : "=r"(__val)); __val; })
#endif
#define TLS_SLOT_ART_THREAD 7
using namespace art::mirror;
extern "C" {
void initHideApi(JNIEnv *env);
bool compileMethod(void *artMethod, void *thread);
void suspendVM(void *);
void resumeVM(void *);
bool canGetObject();
jobject getJavaObject(JNIEnv* env, void* thread, void* address);
void *getCurrentThread();
art::jit::JitCompiler* getGlobalJitCompiler();
art::CompilerOptions* getCompilerOptions(art::jit::JitCompiler* compiler);
art::CompilerOptions* getGlobalCompilerOptions();
bool disableJitInline(art::CompilerOptions* compilerOptions);
void* getInterpreterBridge(bool isNative);
bool replaceUpdateCompilerOptionsQ();
bool forceProcessProfiles();
bool hookClassInit(void(*callback)(void*));
JNIEnv *attachAndGetEvn();
ArtMethod* getArtMethod(JNIEnv *env, jobject method);
}
#endif //SANDHOOK_HIDE_API_H

View File

@ -1,122 +0,0 @@
//
// Created by swift on 2019/2/3.
//
#ifndef SANDHOOK_INST_VISTOR_H
#define SANDHOOK_INST_VISTOR_H
#include <cstdint>
#include "arch.h"
#define CASE(inst,mask,match,type) \
if ((inst & mask) == match) { return type; } \
namespace SandHook {
union Arm32Code {
uint32_t code;
struct {
uint32_t cond:4;
uint32_t empty:2;
uint32_t opcode:4;
uint32_t s:1;
uint32_t rn:4;
uint32_t rd:4;
uint32_t operand2:12;
} units;
};
union Arm16Code {
uint16_t code;
struct {
uint32_t cond:16;
} units;
};
enum InstArch {
ARM32 = 0,
Thumb16,
Thumb32,
Arm64,
X86,
X64
};
enum class InstType_Thumb32 {
// BLX <label>
BLX_THUMB32 = 0,
// BL <label>
BL_THUMB32,
// B.W <label>
B1_THUMB32,
// B.W <label>
B2_THUMB32,
// ADR.W Rd, <label>
ADR1_THUMB32,
// ADR.W Rd, <label>
ADR2_THUMB32,
// LDR.W Rt, <label>
LDR_THUMB32,
// TBB [PC, Rm]
TBB_THUMB32,
// TBH [PC, Rm, LSL #1]
TBH_THUMB32,
PC_NO_RELATED
};
enum class InstType_Thumb16 {
// B <label>
B1_THUMB16 = 0,
// B <label>
B2_THUMB16,
// BX PC
BX_THUMB16,
// ADD <Rdn>, PC (Rd != PC, Rn != PC) 在对ADD进行修正时
//采用了替换PC为Rr的方法当Rd也为PC时由于之前更改了Rr的值
//可能会影响跳转后的正常功能。
ADD_THUMB16,
// MOV Rd, PC
MOV_THUMB16,
// ADR Rd, <label>
ADR_THUMB16,
// LDR Rt, <label>
LDR_THUMB16,
PC_NO_RELATED
};
enum class InstType_Arm64 {
CBZ_CBNZ = 0,
B_COND,
TBZ_TBNZ,
B_BL,
LDR_LIT,
ADR_ADRP,
PC_NO_RELATED
};
class Inst {
public:
virtual int instLen() const = 0;
virtual InstArch instArch() const = 0;
virtual bool pcRelated() = 0;
virtual Size bin() = 0;
virtual ~Inst() = default;
};
class InstVisitor {
public:
virtual bool visit(Inst* inst, Size offset, Size length) = 0;
};
class InstDecode {
public:
static void decode(void* codeStart, Size codeLen, InstVisitor* visitor);
};
}
#endif //SANDHOOK_INST_VISTOR_H

View File

@ -1,16 +0,0 @@
//
// Created by SwiftGan on 2019/2/15.
//
#ifndef SANDHOOK_LOG_H
#define SANDHOOK_LOG_H
#include "android/log.h"
#define TAG "SandHook-Native"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__)
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__)
#endif //SANDHOOK_LOG_H

View File

@ -1,19 +0,0 @@
//
// Created by SwiftGan on 2019/4/12.
//
#ifndef SANDHOOK_NATIVE_HOOK_H
#define SANDHOOK_NATIVE_HOOK_H
#include "sandhook.h"
namespace SandHook {
class NativeHook {
public:
static bool hookDex2oat(bool disableDex2oat);
};
}
#endif //SANDHOOK_NATIVE_HOOK_H

View File

@ -1,14 +0,0 @@
//
// Created by swift on 2019/6/3.
//
#ifndef SANDHOOK_NEVER_CALL_H
#define SANDHOOK_NEVER_CALL_H
#include <jni.h>
extern "C"
JNIEXPORT void JNICALL
Java_com_swift_sandhook_ClassNeverCall_neverCallNative(JNIEnv *env, jobject instance);
#endif //SANDHOOK_NEVER_CALL_H

View File

@ -1,28 +0,0 @@
//
// Created by swift on 2019/2/3.
//
#ifndef SANDHOOK_OFFSET_H
#define SANDHOOK_OFFSET_H
#include <unistd.h>
namespace SandHook {
class Offset {
public:
template<typename T>
static int findOffset(void *start, size_t len, size_t step, T value);
template<typename T>
static int findOffsetWithCB1(void *start, size_t len, size_t step, bool func(int, T));
template<typename T>
static int findOffsetWithCB2(void *start1, void *start2, size_t len, size_t step, bool func(T, T));
};
}
#endif //SANDHOOK_OFFSET_H

View File

@ -1,16 +0,0 @@
//
// Created by SwiftGan on 2019/4/12.
//
#ifndef SANDHOOK_SANDHOOK_H
#define SANDHOOK_SANDHOOK_H
#include <jni.h>
extern "C"
JNIEXPORT bool nativeHookNoBackup(void* origin, void* hook);
extern "C"
JNIEXPORT void* findSym(const char *elf, const char *sym_name);
#endif //SANDHOOK_SANDHOOK_H

View File

@ -1,231 +0,0 @@
//
// Created by SwiftGan on 2019/1/17.
//
#ifndef SANDHOOK_TRAMPOLINE_H
#define SANDHOOK_TRAMPOLINE_H
#include <cstdint>
#include <string.h>
#include "arch.h"
#include "arch_base.h"
#include "stdlib.h"
#include <sys/mman.h>
#define Code unsigned char *
#if defined(__i386__)
#define SIZE_REPLACEMENT_HOOK_TRAMPOLINE 4 * 9
#define OFFSET_REPLACEMENT_ADDR_ART_METHOD 4 * 5
#define OFFSET_REPLACEMENT_OFFSET_ENTRY_CODE 4 * 7
#define SIZE_DIRECT_JUMP_TRAMPOLINE 4 * 4
#define OFFSET_JUMP_ADDR_TARGET 4 * 2
#define SIZE_INLINE_HOOK_TRAMPOLINE 4 * 24
#define OFFSET_INLINE_ADDR_ORIGIN_METHOD 4 * 10
#define OFFSET_INLINE_ORIGIN_CODE 4 * 12
#define OFFSET_INLINE_OFFSET_ENTRY_CODE 4 * 20
#define OFFSET_INLINE_ADDR_HOOK_METHOD 4 * 22
#elif defined(__x86_64__)
#define SIZE_REPLACEMENT_HOOK_TRAMPOLINE 4 * 9
#define OFFSET_REPLACEMENT_ADDR_ART_METHOD 4 * 5
#define OFFSET_REPLACEMENT_OFFSET_ENTRY_CODE 4 * 7
#define SIZE_DIRECT_JUMP_TRAMPOLINE 4 * 4
#define OFFSET_JUMP_ADDR_TARGET 4 * 2
#define SIZE_INLINE_HOOK_TRAMPOLINE 4 * 24
#define OFFSET_INLINE_ADDR_ORIGIN_METHOD 4 * 10
#define OFFSET_INLINE_ORIGIN_CODE 4 * 12
#define OFFSET_INLINE_OFFSET_ENTRY_CODE 4 * 20
#define OFFSET_INLINE_ADDR_HOOK_METHOD 4 * 22
#elif defined(__arm__)
#define SIZE_REPLACEMENT_HOOK_TRAMPOLINE 4 * 5
#define OFFSET_REPLACEMENT_ART_METHOD 4 * 3
#define OFFSET_REPLACEMENT_OFFSET_CODE_ENTRY 4 * 4
#define SIZE_DIRECT_JUMP_TRAMPOLINE 4 * 2
#define OFFSET_JUMP_ADDR_TARGET 4 * 1
#define SIZE_INLINE_HOOK_TRAMPOLINE 4 * 17
#define OFFSET_INLINE_ORIGIN_CODE 4 * 6
#define OFFSET_INLINE_ORIGIN_ART_METHOD 4 * 13
#define OFFSET_INLINE_ADDR_ORIGIN_CODE_ENTRY 4 * 14
#define OFFSET_INLINE_HOOK_ART_METHOD 4 * 15
#define OFFSET_INLINE_ADDR_HOOK_CODE_ENTRY 4 * 16
#define OFFSET_INLINE_OP_ORIGIN_OFFSET_CODE 4 * 11
#define SIZE_CALL_ORIGIN_TRAMPOLINE 4 * 4
#define OFFSET_CALL_ORIGIN_ART_METHOD 4 * 2
#define OFFSET_CALL_ORIGIN_JUMP_ADDR 4 * 3
#define SIZE_ORIGIN_PLACE_HOLDER 4 * 3
#elif defined(__aarch64__)
#define SIZE_REPLACEMENT_HOOK_TRAMPOLINE 4 * 8
#define OFFSET_REPLACEMENT_ART_METHOD 4 * 4
#define OFFSET_REPLACEMENT_OFFSET_CODE_ENTRY 4 * 6
#define SIZE_DIRECT_JUMP_TRAMPOLINE 4 * 4
#define OFFSET_JUMP_ADDR_TARGET 4 * 2
#define SIZE_INLINE_HOOK_TRAMPOLINE 4 * 23
#define OFFSET_INLINE_ORIGIN_CODE 4 * 7
#define OFFSET_INLINE_ORIGIN_ART_METHOD 4 * 15
#define OFFSET_INLINE_ADDR_ORIGIN_CODE_ENTRY 4 * 17
#define OFFSET_INLINE_HOOK_ART_METHOD 4 * 19
#define OFFSET_INLINE_ADDR_HOOK_CODE_ENTRY 4 * 21
#define SIZE_CALL_ORIGIN_TRAMPOLINE 4 * 7
#define OFFSET_CALL_ORIGIN_ART_METHOD 4 * 3
#define OFFSET_CALL_ORIGIN_JUMP_ADDR 4 * 5
#define SIZE_ORIGIN_PLACE_HOLDER 4 * 4
#else
#endif
extern "C" void DIRECT_JUMP_TRAMPOLINE();
extern "C" void INLINE_HOOK_TRAMPOLINE();
extern "C" void REPLACEMENT_HOOK_TRAMPOLINE();
extern "C" void CALL_ORIGIN_TRAMPOLINE();
#if defined(__arm__)
#include <unistd.h>
extern "C" void DIRECT_JUMP_TRAMPOLINE_T();
extern "C" void INLINE_HOOK_TRAMPOLINE_T();
extern "C" void CALL_ORIGIN_TRAMPOLINE_T();
#endif
namespace SandHook {
//deal with little or big edn
union Code32Bit {
uint32_t code;
struct {
uint32_t op1:8;
uint32_t op2:8;
uint32_t op3:8;
uint32_t op4:8;
} op;
};
class Trampoline {
public:
Code code;
Trampoline() = default;
virtual void init() {
codeLen = codeLength();
tempCode = templateCode();
}
void setThumb(bool thumb) {
isThumb = thumb;
}
bool isThumbCode() {
return isThumb;
}
void setExecuteSpace(Code start) {
code = start;
memcpy(code, tempCode, codeLen);
flushCache(reinterpret_cast<Size>(code), codeLen);
}
void setEntryCodeOffset(Size offSet) {
this->codeEntryOffSet = offSet;
}
void codeCopy(Code src, Size targetOffset, Size len) {
memcpy(reinterpret_cast<void*>((Size)code + targetOffset), src, len);
flushCache((Size)code + targetOffset, len);
}
static bool flushCache(Size addr, Size len) {
#if defined(__arm__)
//clearCacheArm32(reinterpret_cast<char*>(addr), reinterpret_cast<char*>(addr + len));
int i = cacheflush(addr, addr + len, 0);
if (i == -1) {
return false;
}
return true;
#elif defined(__aarch64__)
char *begin = reinterpret_cast<char *>(addr);
__builtin___clear_cache(begin, begin + len);
#endif
return true;
}
void clone(Code dest) {
memcpy(dest, code, codeLen);
}
Code getCode() {
if (isThumbCode()) {
return getThumbCodePcAddress(code);
} else {
return code;
}
}
Size getCodeLen() {
return codeLen;
}
bool isBigEnd(void) {
int i = 1;
unsigned char *pointer;
pointer = (unsigned char *) &i;
return *pointer == 0;
}
//tweak imm of a 32bit asm code
void tweakOpImm(Size codeOffset, unsigned char imm) {
Code32Bit code32Bit;
code32Bit.code = *reinterpret_cast<uint32_t*>(((Size)code + codeOffset));
if (isBigEnd()) {
code32Bit.op.op2 = imm;
} else {
code32Bit.op.op3 = imm;
}
codeCopy(reinterpret_cast<Code>(&code32Bit.code), codeOffset, 4);
flushCache((Size)code + codeOffset, 4);
}
//work for thumb
static Code getThumbCodeAddress(Code code) {
Size addr = reinterpret_cast<Size>(code) & (~0x00000001);
return reinterpret_cast<Code>(addr);
}
static Code getThumbCodePcAddress(Code code) {
Size addr = reinterpret_cast<Size>(code) & (~0x00000001);
return reinterpret_cast<Code>(addr + 1);
}
void* getEntryCodeAddr(void* method) {
return reinterpret_cast<void*>((Size)method + codeEntryOffSet);
}
Code getEntryCode(void* method) {
Code entryCode = *reinterpret_cast<Code*>((Size)method + codeEntryOffSet);
return entryCode;
}
protected:
virtual Size codeLength() = 0;
virtual Code templateCode() = 0;
private:
Code tempCode;
Size codeLen;
Size codeEntryOffSet;
bool isThumb = false;
};
}
#endif //SANDHOOK_TRAMPOLINE_H

View File

@ -1,115 +0,0 @@
//
// Created by swift on 2019/1/20.
//
#ifndef SANDHOOK_TRAMPOLINE_MANAGER_H
#define SANDHOOK_TRAMPOLINE_MANAGER_H
#include "map"
#include "list"
#include "../trampoline/trampoline.cpp"
#include "../utils/lock.h"
#include <sys/mman.h>
#include "art_method.h"
#include "log.h"
#include <unistd.h>
namespace SandHook {
#define MMAP_PAGE_SIZE sysconf(_SC_PAGESIZE)
#define EXE_BLOCK_SIZE MMAP_PAGE_SIZE
using namespace art;
class HookTrampoline {
public:
HookTrampoline() = default;
Trampoline* replacement = nullptr;
Trampoline* inlineJump = nullptr;
Trampoline* inlineSecondory = nullptr;
Trampoline* callOrigin = nullptr;
Trampoline* hookNative = nullptr;
Code originCode = nullptr;
};
class TrampolineManager {
public:
TrampolineManager() = default;
static TrampolineManager &get();
void init(Size quickCompileOffset) {
this->quickCompileOffset = quickCompileOffset;
}
Code allocExecuteSpace(Size size);
//java hook
HookTrampoline* installReplacementTrampoline(mirror::ArtMethod* originMethod, mirror::ArtMethod* hookMethod, mirror::ArtMethod* backupMethod);
HookTrampoline* installInlineTrampoline(mirror::ArtMethod* originMethod, mirror::ArtMethod* hookMethod, mirror::ArtMethod* backupMethod);
//native hook
HookTrampoline* installNativeHookTrampolineNoBackup(void* origin, void* hook);
bool canSafeInline(mirror::ArtMethod* method);
uint32_t sizeOfEntryCode(mirror::ArtMethod* method);
HookTrampoline* getHookTrampoline(mirror::ArtMethod* method) {
return trampolines[method];
}
bool methodHooked(ArtMethod *method) {
return trampolines.find(method) != trampolines.end();
}
bool memUnprotect(Size addr, Size len) {
long pagesize = sysconf(_SC_PAGESIZE);
unsigned alignment = (unsigned)((unsigned long long)addr % pagesize);
int i = mprotect((void *) (addr - alignment), (size_t) (alignment + len),
PROT_READ | PROT_WRITE | PROT_EXEC);
if (i == -1) {
return false;
}
return true;
}
Code getEntryCode(void* method) {
Code entryCode = *reinterpret_cast<Code*>((Size)method + quickCompileOffset);
return entryCode;
}
static bool isThumbCode(Size codeAddr) {
return (codeAddr & 0x1) == 0x1;
}
static void checkThumbCode(Trampoline* trampoline, Code code) {
#if defined(__arm__)
trampoline->setThumb(isThumbCode(reinterpret_cast<Size>(code)));
#endif
}
static Code getThumbCodeAddress(Code code) {
Size addr = reinterpret_cast<Size>(code) & (~0x00000001);
return reinterpret_cast<Code>(addr);
}
bool inlineSecurityCheck = true;
bool skipAllCheck = false;
private:
Size quickCompileOffset;
std::map<mirror::ArtMethod*,HookTrampoline*> trampolines;
std::list<Code> executeSpaceList = std::list<Code>();
std::mutex allocSpaceLock;
std::mutex installLock;
Size executePageOffset = 0;
};
}
#endif //SANDHOOK_TRAMPOLINE_MANAGER_H

View File

@ -1,41 +0,0 @@
//
// Created by 甘尧 on 2019/1/13.
//
#ifndef SANDHOOK_UTILS_H
#define SANDHOOK_UTILS_H
#include <stdlib.h>
#include <sys/mman.h>
#include "jni.h"
#include "../includes/arch.h"
#include <unistd.h>
#include <sys/mman.h>
#include <type_traits>
#define RoundUpToPtrSize(x) (x + BYTE_POINT - 1 - ((x + BYTE_POINT - 1) & (BYTE_POINT - 1)))
extern "C" {
Size getAddressFromJava(JNIEnv *env, const char *className, const char *fieldName);
Size callStaticMethodAddr(JNIEnv *env, const char *className, const char *method, const char *sig, ...);
jobject callStaticMethodObject(JNIEnv *env, const char *className, const char *method, const char *sig, ...);
jobject getMethodObject(JNIEnv *env, const char *clazz, const char *method);
Size getAddressFromJavaByCallMethod(JNIEnv *env, const char *className, const char *methodName);
jint getIntFromJava(JNIEnv *env, const char *className, const char *fieldName);
bool getBooleanFromJava(JNIEnv *env, const char *className, const char *fieldName);
bool munprotect(size_t addr, size_t len);
bool flushCacheExt(Size addr, Size len);
}
#endif //SANDHOOK_UTILS_H

View File

@ -1,134 +0,0 @@
//
// Created by SwiftGan on 2019/2/11.
//
#if defined(__arm__)
#include "../includes/inst.h"
#include "../includes/trampoline.h"
namespace SandHook {
class InstThumb32 : public Inst {
public:
Arm32Code code;
InstType_Thumb32 instType = InstType_Thumb32::PC_NO_RELATED;
InstThumb32(uint32_t code) {
this->code.code = code;
instType = initType();
}
private:
int instLen() const override {
return 4;
}
InstArch instArch() const override {
return Thumb32;
}
bool pcRelated() override {
return instType < InstType_Thumb32::PC_NO_RELATED;
}
Size bin() override {
return code.code;
}
InstType_Thumb32 initType() {
CASE(code.code, 0xF800D000, 0xF000C000, InstType_Thumb32::BLX_THUMB32)
CASE(code.code, 0xF800D000, 0xF000D000, InstType_Thumb32::BL_THUMB32)
CASE(code.code, 0xF800D000, 0xF0008000, InstType_Thumb32::B1_THUMB32)
CASE(code.code, 0xF800D000, 0xF0009000, InstType_Thumb32::B2_THUMB32)
CASE(code.code, 0xFBFF8000, 0xF2AF0000, InstType_Thumb32::ADR1_THUMB32)
CASE(code.code, 0xFBFF8000, 0xF20F0000, InstType_Thumb32::ADR2_THUMB32)
CASE(code.code, 0xFF7F0000, 0xF85F0000, InstType_Thumb32::LDR_THUMB32)
CASE(code.code, 0xFFFF00F0, 0xE8DF0000, InstType_Thumb32::TBB_THUMB32)
CASE(code.code, 0xFFFF00F0, 0xE8DF0010, InstType_Thumb32::TBH_THUMB32)
return InstType_Thumb32::PC_NO_RELATED;
}
};
class InstThumb16 : public Inst {
public:
Arm16Code code;
InstType_Thumb16 instType = InstType_Thumb16::PC_NO_RELATED;
InstThumb16(uint16_t code) {
this->code.code = code;
instType = initType();
}
private:
int instLen() const override {
return 2;
}
InstArch instArch() const override {
return Thumb16;
}
bool pcRelated() override {
return instType < InstType_Thumb16 ::PC_NO_RELATED;
}
Size bin() override {
return code.code;
}
InstType_Thumb16 initType() {
CASE(code.code, 0xF000, 0xD000, InstType_Thumb16::B1_THUMB16)
CASE(code.code, 0xF800, 0xE000, InstType_Thumb16::B2_THUMB16)
CASE(code.code, 0xFFF8, 0x4778, InstType_Thumb16::BX_THUMB16)
CASE(code.code, 0xFF78, 0x4478, InstType_Thumb16::ADD_THUMB16)
CASE(code.code, 0xFF78, 0x4678, InstType_Thumb16::MOV_THUMB16)
CASE(code.code, 0xF800, 0xA000, InstType_Thumb16::ADR_THUMB16)
CASE(code.code, 0xF800, 0x4800, InstType_Thumb16::LDR_THUMB16)
return InstType_Thumb16::PC_NO_RELATED;
}
};
bool isThumbCode(Size codeAddr) {
return (codeAddr & 0x1) == 0x1;
}
bool isThumb32(uint16_t code) {
return ((code & 0xF000) == 0xF000) || ((code & 0xF800) == 0xE800);
}
void InstDecode::decode(void *codeStart, Size codeLen, InstVisitor *visitor) {
Size offset = 0;
Inst* inst = nullptr;
if (isThumbCode(reinterpret_cast<Size>(codeStart))) {
codeStart = Trampoline::getThumbCodeAddress(static_cast<Code>(codeStart));
Size codeAddr = reinterpret_cast<Size>(codeStart);
while (offset < codeLen) {
uint16_t ram16 = *reinterpret_cast<uint16_t*>(codeAddr + offset);
uint32_t ram32 = *reinterpret_cast<uint32_t*>(codeAddr + offset);
if (isThumb32(ram16)) {
//thumb32
inst = new InstThumb32(ram32);
} else {
//thumb16
inst = new InstThumb16(ram16);
}
if (!visitor->visit(inst, offset, codeLen)) {
delete inst;
break;
}
offset += inst->instLen();
delete inst;
}
}
}
}
#endif

View File

@ -1,74 +0,0 @@
//
// Created by SwiftGan on 2019/2/11.
//
#if defined(__aarch64__)
#include "../includes/inst.h"
#include "../includes/trampoline.h"
namespace SandHook {
class InstArm64 : public Inst {
public:
Arm32Code code;
InstType_Arm64 instType;
InstArm64(uint32_t code) {
this->code.code = code;
instType = initType();
}
private:
int instLen() const override {
return 4;
}
InstArch instArch() const override {
return Arm64;
}
bool pcRelated() override {
return instType < InstType_Arm64::PC_NO_RELATED;
}
Size bin() override {
return code.code;
}
private:
InstType_Arm64 initType() {
CASE(code.code, 0x7e000000, 0x34000000, InstType_Arm64::CBZ_CBNZ);
CASE(code.code, 0xff000010, 0x54000000, InstType_Arm64::B_COND);
CASE(code.code, 0x7e000000, 0x36000000, InstType_Arm64::TBZ_TBNZ);
CASE(code.code, 0x7c000000, 0x14000000, InstType_Arm64::B_BL);
CASE(code.code, 0x3b000000, 0x18000000, InstType_Arm64::LDR_LIT);
CASE(code.code, 0x1f000000, 0x10000000, InstType_Arm64::ADR_ADRP);
return InstType_Arm64::PC_NO_RELATED;
}
};
void InstDecode::decode(void *codeStart, Size codeLen, InstVisitor *visitor) {
Size offset = 0;
Inst *inst = nullptr;
codeStart = Trampoline::getThumbCodeAddress(static_cast<Code>(codeStart));
Size codeAddr = reinterpret_cast<Size>(codeStart);
while (offset < codeLen) {
uint32_t ram32 = *reinterpret_cast<uint32_t *>(codeAddr + offset);
inst = new InstArm64(ram32);
if (!visitor->visit(inst, offset, codeLen)) {
delete inst;
break;
}
offset += inst->instLen();
delete inst;
}
}
}
#endif

View File

@ -1,96 +0,0 @@
//
// Created by SwiftGan on 2019/4/12.
//
#include <syscall.h>
#include <cstdlib>
#include <cstring>
#include <unistd.h>
#include "../includes/native_hook.h"
#include "../includes/arch.h"
#include "../includes/log.h"
extern int SDK_INT;
int inline getArrayItemCount(char *const array[]) {
int i;
for (i = 0; array[i]; ++i);
return i;
}
bool isSandHooker(char *const args[]) {
int orig_arg_count = getArrayItemCount(args);
for (int i = 0; i < orig_arg_count; i++) {
if (strstr(args[i], "SandHooker")) {
LOGE("skip dex2oat hooker!");
return true;
}
}
return false;
}
char **build_new_argv(char *const argv[]) {
int orig_argv_count = getArrayItemCount(argv);
int new_argv_count = orig_argv_count + 2;
char **new_argv = (char **) malloc(new_argv_count * sizeof(char *));
int cur = 0;
for (int i = 0; i < orig_argv_count; ++i) {
new_argv[cur++] = argv[i];
}
if (SDK_INT >= ANDROID_L2 && SDK_INT < ANDROID_Q) {
new_argv[cur++] = (char *) "--compile-pic";
}
if (SDK_INT >= ANDROID_M) {
new_argv[cur++] = (char *) (SDK_INT > ANDROID_N2 ? "--inline-max-code-units=0" : "--inline-depth-limit=0");
}
new_argv[cur] = NULL;
return new_argv;
}
int fake_execve_disable_inline(const char *pathname, char *argv[], char *const envp[]) {
if (strstr(pathname, "dex2oat")) {
if (SDK_INT >= ANDROID_N && isSandHooker(argv)) {
LOGE("skip dex2oat!");
return -1;
}
char **new_args = build_new_argv(argv);
LOGE("dex2oat by disable inline!");
int ret = static_cast<int>(syscall(__NR_execve, pathname, new_args, envp));
free(new_args);
return ret;
}
int ret = static_cast<int>(syscall(__NR_execve, pathname, argv, envp));
return ret;
}
int fake_execve_disable_oat(const char *pathname, char *argv[], char *const envp[]) {
if (strstr(pathname, "dex2oat")) {
LOGE("skip dex2oat!");
return -1;
}
return static_cast<int>(syscall(__NR_execve, pathname, argv, envp));
}
namespace SandHook {
volatile bool hasHookedDex2oat = false;
bool NativeHook::hookDex2oat(bool disableDex2oat) {
if (hasHookedDex2oat)
return false;
hasHookedDex2oat = true;
return nativeHookNoBackup(reinterpret_cast<void *>(execve),
reinterpret_cast<void *>(disableDex2oat ? fake_execve_disable_oat : fake_execve_disable_inline));
}
}

View File

@ -1,566 +0,0 @@
#include "includes/sandhook.h"
#include "includes/cast_art_method.h"
#include "includes/trampoline_manager.h"
#include "includes/hide_api.h"
#include "includes/cast_compiler_options.h"
#include "includes/log.h"
#include "includes/native_hook.h"
#include "includes/elf_util.h"
#include "includes/never_call.h"
#include <jni.h>
SandHook::TrampolineManager &trampolineManager = SandHook::TrampolineManager::get();
extern "C" int SDK_INT = 0;
extern "C" bool DEBUG = false;
enum HookMode {
AUTO = 0,
INLINE = 1,
REPLACE = 2
};
HookMode gHookMode = AUTO;
void ensureMethodCached(art::mirror::ArtMethod *hookMethod, art::mirror::ArtMethod *backupMethod) {
if (SDK_INT >= ANDROID_P)
return;
SandHook::StopTheWorld stopTheWorld;
uint32_t index = backupMethod->getDexMethodIndex();
if (SDK_INT < ANDROID_O2) {
hookMethod->setDexCacheResolveItem(index, backupMethod);
} else {
int cacheSize = 1024;
Size slotIndex = index % cacheSize;
Size newCachedMethodsArray = reinterpret_cast<Size>(calloc(cacheSize, BYTE_POINT * 2));
unsigned int one = 1;
memcpy(reinterpret_cast<void *>(newCachedMethodsArray + BYTE_POINT), &one, 4);
memcpy(reinterpret_cast<void *>(newCachedMethodsArray + BYTE_POINT * 2 * slotIndex),
(&backupMethod),
BYTE_POINT
);
memcpy(reinterpret_cast<void *>(newCachedMethodsArray + BYTE_POINT * 2 * slotIndex + BYTE_POINT),
&index,
4
);
hookMethod->setDexCacheResolveList(&newCachedMethodsArray);
}
}
void ensureDeclareClass(JNIEnv *env, jclass type, jobject originMethod,
jobject backupMethod) {
if (originMethod == NULL || backupMethod == NULL)
return;
art::mirror::ArtMethod* origin = getArtMethod(env, originMethod);
art::mirror::ArtMethod* backup = getArtMethod(env, backupMethod);
if (origin->getDeclaringClass() != backup->getDeclaringClass()) {
LOGW("declaring class has been moved!");
backup->setDeclaringClass(origin->getDeclaringClass());
}
}
bool doHookWithReplacement(JNIEnv* env,
art::mirror::ArtMethod *originMethod,
art::mirror::ArtMethod *hookMethod,
art::mirror::ArtMethod *backupMethod) {
if (!hookMethod->compile(env)) {
hookMethod->disableCompilable();
}
if (SDK_INT > ANDROID_N && SDK_INT < ANDROID_Q) {
forceProcessProfiles();
}
if ((SDK_INT >= ANDROID_N && SDK_INT <= ANDROID_P)
|| (SDK_INT >= ANDROID_Q && !originMethod->isAbstract())) {
originMethod->setHotnessCount(0);
}
if (backupMethod != nullptr) {
originMethod->backup(backupMethod);
backupMethod->disableCompilable();
if (!backupMethod->isStatic()) {
backupMethod->setPrivate();
}
backupMethod->flushCache();
}
originMethod->disableCompilable();
hookMethod->disableCompilable();
hookMethod->flushCache();
originMethod->disableInterpreterForO();
originMethod->disableFastInterpreterForQ();
SandHook::HookTrampoline* hookTrampoline = trampolineManager.installReplacementTrampoline(originMethod, hookMethod, backupMethod);
if (hookTrampoline != nullptr) {
originMethod->setQuickCodeEntry(hookTrampoline->replacement->getCode());
void* entryPointFormInterpreter = hookMethod->getInterpreterCodeEntry();
if (entryPointFormInterpreter != NULL) {
originMethod->setInterpreterCodeEntry(entryPointFormInterpreter);
}
if (hookTrampoline->callOrigin != nullptr) {
backupMethod->setQuickCodeEntry(hookTrampoline->callOrigin->getCode());
backupMethod->flushCache();
}
originMethod->flushCache();
return true;
} else {
return false;
}
}
bool doHookWithInline(JNIEnv* env,
art::mirror::ArtMethod *originMethod,
art::mirror::ArtMethod *hookMethod,
art::mirror::ArtMethod *backupMethod) {
//fix >= 8.1
if (!hookMethod->compile(env)) {
hookMethod->disableCompilable();
}
originMethod->disableCompilable();
if (SDK_INT > ANDROID_N && SDK_INT < ANDROID_Q) {
forceProcessProfiles();
}
if ((SDK_INT >= ANDROID_N && SDK_INT <= ANDROID_P)
|| (SDK_INT >= ANDROID_Q && !originMethod->isAbstract())) {
originMethod->setHotnessCount(0);
}
originMethod->flushCache();
SandHook::HookTrampoline* hookTrampoline = trampolineManager.installInlineTrampoline(originMethod, hookMethod, backupMethod);
if (hookTrampoline == nullptr)
return false;
hookMethod->flushCache();
if (hookTrampoline->callOrigin != nullptr) {
//backup
originMethod->backup(backupMethod);
backupMethod->setQuickCodeEntry(hookTrampoline->callOrigin->getCode());
backupMethod->disableCompilable();
if (!backupMethod->isStatic()) {
backupMethod->setPrivate();
}
backupMethod->flushCache();
}
return true;
}
extern "C"
JNIEXPORT jboolean JNICALL
Java_com_swift_sandhook_SandHook_initNative(JNIEnv *env, jclass type, jint sdk, jboolean debug) {
SDK_INT = sdk;
DEBUG = debug;
SandHook::CastCompilerOptions::init(env);
initHideApi(env);
SandHook::CastArtMethod::init(env);
trampolineManager.init(SandHook::CastArtMethod::entryPointQuickCompiled->getOffset());
return JNI_TRUE;
}
extern "C"
JNIEXPORT jint JNICALL
Java_com_swift_sandhook_SandHook_hookMethod(JNIEnv *env, jclass type, jobject originMethod,
jobject hookMethod, jobject backupMethod, jint hookMode) {
art::mirror::ArtMethod* origin = getArtMethod(env, originMethod);
art::mirror::ArtMethod* hook = getArtMethod(env, hookMethod);
art::mirror::ArtMethod* backup = backupMethod == NULL ? nullptr : getArtMethod(env,
backupMethod);
bool isInlineHook = false;
int mode = reinterpret_cast<int>(hookMode);
if (mode == INLINE) {
if (!origin->isCompiled()) {
if (SDK_INT >= ANDROID_N) {
isInlineHook = origin->compile(env);
}
} else {
isInlineHook = true;
}
goto label_hook;
} else if (mode == REPLACE) {
isInlineHook = false;
goto label_hook;
}
if (origin->isAbstract()) {
isInlineHook = false;
} else if (gHookMode != AUTO) {
if (gHookMode == INLINE) {
isInlineHook = origin->compile(env);
} else {
isInlineHook = false;
}
} else if (SDK_INT >= ANDROID_O) {
isInlineHook = false;
} else if (!origin->isCompiled()) {
if (SDK_INT >= ANDROID_N) {
isInlineHook = origin->compile(env);
} else {
isInlineHook = false;
}
} else {
isInlineHook = true;
}
label_hook:
//suspend other threads
SandHook::StopTheWorld stopTheWorld;
if (isInlineHook && trampolineManager.canSafeInline(origin)) {
return doHookWithInline(env, origin, hook, backup) ? INLINE : -1;
} else {
return doHookWithReplacement(env, origin, hook, backup) ? REPLACE : -1;
}
}
extern "C"
JNIEXPORT void JNICALL
Java_com_swift_sandhook_SandHook_ensureMethodCached(JNIEnv *env, jclass type, jobject hook,
jobject backup) {
art::mirror::ArtMethod* hookeMethod = getArtMethod(env, hook);
art::mirror::ArtMethod* backupMethod = backup == NULL ? nullptr : getArtMethod(env, backup);
ensureMethodCached(hookeMethod, backupMethod);
}
extern "C"
JNIEXPORT jboolean JNICALL
Java_com_swift_sandhook_SandHook_compileMethod(JNIEnv *env, jclass type, jobject member) {
if (member == NULL)
return JNI_FALSE;
art::mirror::ArtMethod* method = getArtMethod(env, member);
if (method == nullptr)
return JNI_FALSE;
if (!method->isCompiled()) {
SandHook::StopTheWorld stopTheWorld;
if (!method->compile(env)) {
if (SDK_INT >= ANDROID_N) {
method->disableCompilable();
method->flushCache();
}
return JNI_FALSE;
} else {
return JNI_TRUE;
}
} else {
return JNI_TRUE;
}
}
extern "C"
JNIEXPORT jboolean JNICALL
Java_com_swift_sandhook_SandHook_deCompileMethod(JNIEnv *env, jclass type, jobject member, jboolean disableJit) {
if (member == NULL)
return JNI_FALSE;
art::mirror::ArtMethod* method = getArtMethod(env, member);
if (method == nullptr)
return JNI_FALSE;
if (disableJit) {
method->disableCompilable();
}
if (method->isCompiled()) {
SandHook::StopTheWorld stopTheWorld;
if (SDK_INT >= ANDROID_N) {
method->disableCompilable();
}
return static_cast<jboolean>(method->deCompile());
} else {
return JNI_TRUE;
}
}
extern "C"
JNIEXPORT jobject JNICALL
Java_com_swift_sandhook_SandHook_getObjectNative(JNIEnv *env, jclass type, jlong thread,
jlong address) {
return getJavaObject(env, thread ? reinterpret_cast<void *>(thread) : getCurrentThread(), reinterpret_cast<void *>(address));
}
extern "C"
JNIEXPORT jboolean JNICALL
Java_com_swift_sandhook_SandHook_canGetObject(JNIEnv *env, jclass type) {
return static_cast<jboolean>(canGetObject());
}
extern "C"
JNIEXPORT void JNICALL
Java_com_swift_sandhook_SandHook_setHookMode(JNIEnv *env, jclass type, jint mode) {
gHookMode = static_cast<HookMode>(mode);
}
extern "C"
JNIEXPORT void JNICALL
Java_com_swift_sandhook_SandHook_setInlineSafeCheck(JNIEnv *env, jclass type, jboolean check) {
trampolineManager.inlineSecurityCheck = check;
}
extern "C"
JNIEXPORT void JNICALL
Java_com_swift_sandhook_SandHook_skipAllSafeCheck(JNIEnv *env, jclass type, jboolean skip) {
trampolineManager.skipAllCheck = skip;
}
extern "C"
JNIEXPORT jboolean JNICALL
Java_com_swift_sandhook_SandHook_is64Bit(JNIEnv *env, jclass type) {
return static_cast<jboolean>(BYTE_POINT == 8);
}
extern "C"
JNIEXPORT jboolean JNICALL
Java_com_swift_sandhook_SandHook_disableVMInline(JNIEnv *env, jclass type) {
if (SDK_INT < ANDROID_N)
return JNI_FALSE;
replaceUpdateCompilerOptionsQ();
art::CompilerOptions* compilerOptions = getGlobalCompilerOptions();
if (compilerOptions == nullptr)
return JNI_FALSE;
return static_cast<jboolean>(disableJitInline(compilerOptions));
}
extern "C"
JNIEXPORT jboolean JNICALL
Java_com_swift_sandhook_SandHook_disableDex2oatInline(JNIEnv *env, jclass type, jboolean disableDex2oat) {
return static_cast<jboolean>(SandHook::NativeHook::hookDex2oat(disableDex2oat));
}
extern "C"
JNIEXPORT jboolean JNICALL
Java_com_swift_sandhook_SandHook_setNativeEntry(JNIEnv *env, jclass type, jobject origin, jobject hook, jlong jniTrampoline) {
if (origin == nullptr || hook == NULL)
return JNI_FALSE;
art::mirror::ArtMethod* hookMethod = getArtMethod(env, hook);
art::mirror::ArtMethod* originMethod = getArtMethod(env, origin);
originMethod->backup(hookMethod);
hookMethod->setNative();
hookMethod->setQuickCodeEntry(SandHook::CastArtMethod::genericJniStub);
hookMethod->setJniCodeEntry(reinterpret_cast<void *>(jniTrampoline));
hookMethod->disableCompilable();
hookMethod->flushCache();
return JNI_TRUE;
}
static jclass class_pending_hook = nullptr;
static jmethodID method_class_init = nullptr;
extern "C"
JNIEXPORT jboolean JNICALL
Java_com_swift_sandhook_SandHook_initForPendingHook(JNIEnv *env, jclass type) {
class_pending_hook = static_cast<jclass>(env->NewGlobalRef(
env->FindClass("com/swift/sandhook/PendingHookHandler")));
method_class_init = env->GetStaticMethodID(class_pending_hook, "onClassInit", "(J)V");
auto class_init_handler = [](void *clazz_ptr) {
attachAndGetEvn()->CallStaticVoidMethod(class_pending_hook, method_class_init, (jlong) clazz_ptr);
attachAndGetEvn()->ExceptionClear();
};
return static_cast<jboolean>(hookClassInit(class_init_handler));
}
extern "C"
JNIEXPORT void JNICALL
Java_com_swift_sandhook_ClassNeverCall_neverCallNative(JNIEnv *env, jobject instance) {
int a = 1 + 1;
int b = a + 1;
}
extern "C"
JNIEXPORT void JNICALL
Java_com_swift_sandhook_ClassNeverCall_neverCallNative2(JNIEnv *env, jobject instance) {
int a = 4 + 3;
int b = 9 + 6;
}
extern "C"
JNIEXPORT void JNICALL
Java_com_swift_sandhook_test_TestClass_jni_1test(JNIEnv *env, jobject instance) {
int a = 1 + 1;
int b = a + 1;
}
//native hook
extern "C"
JNIEXPORT bool nativeHookNoBackup(void* origin, void* hook) {
if (origin == nullptr || hook == nullptr)
return false;
SandHook::StopTheWorld stopTheWorld;
return trampolineManager.installNativeHookTrampolineNoBackup(origin, hook) != nullptr;
}
extern "C"
JNIEXPORT void* findSym(const char *elf, const char *sym_name) {
SandHook::ElfImg elfImg(elf);
return reinterpret_cast<void *>(elfImg.getSymbAddress(sym_name));
}
static JNINativeMethod jniSandHook[] = {
{
"initNative",
"(IZ)Z",
(void *) Java_com_swift_sandhook_SandHook_initNative
},
{
"hookMethod",
"(Ljava/lang/reflect/Member;Ljava/lang/reflect/Method;Ljava/lang/reflect/Method;I)I",
(void *) Java_com_swift_sandhook_SandHook_hookMethod
},
{
"ensureMethodCached",
"(Ljava/lang/reflect/Method;Ljava/lang/reflect/Method;)V",
(void *) Java_com_swift_sandhook_SandHook_ensureMethodCached
},
{
"ensureDeclareClass",
"(Ljava/lang/reflect/Member;Ljava/lang/reflect/Method;)V",
(void *) ensureDeclareClass
},
{
"compileMethod",
"(Ljava/lang/reflect/Member;)Z",
(void *) Java_com_swift_sandhook_SandHook_compileMethod
},
{
"deCompileMethod",
"(Ljava/lang/reflect/Member;Z)Z",
(void *) Java_com_swift_sandhook_SandHook_deCompileMethod
},
{
"getObjectNative",
"(JJ)Ljava/lang/Object;",
(void *) Java_com_swift_sandhook_SandHook_getObjectNative
},
{
"canGetObject",
"()Z",
(void *) Java_com_swift_sandhook_SandHook_canGetObject
},
{
"setHookMode",
"(I)V",
(void *) Java_com_swift_sandhook_SandHook_setHookMode
},
{
"setInlineSafeCheck",
"(Z)V",
(void *) Java_com_swift_sandhook_SandHook_setInlineSafeCheck
},
{
"skipAllSafeCheck",
"(Z)V",
(void *) Java_com_swift_sandhook_SandHook_skipAllSafeCheck
},
{
"is64Bit",
"()Z",
(void *) Java_com_swift_sandhook_SandHook_is64Bit
},
{
"disableVMInline",
"()Z",
(void *) Java_com_swift_sandhook_SandHook_disableVMInline
},
{
"disableDex2oatInline",
"(Z)Z",
(void *) Java_com_swift_sandhook_SandHook_disableDex2oatInline
},
{
"setNativeEntry",
"(Ljava/lang/reflect/Member;Ljava/lang/reflect/Member;J)Z",
(void *) Java_com_swift_sandhook_SandHook_setNativeEntry
},
{
"initForPendingHook",
"()Z",
(void *) Java_com_swift_sandhook_SandHook_initForPendingHook
}
};
static JNINativeMethod jniNeverCall[] = {
{
"neverCallNative",
"()V",
(void *) Java_com_swift_sandhook_ClassNeverCall_neverCallNative
},
{
"neverCallNative2",
"()V",
(void *) Java_com_swift_sandhook_ClassNeverCall_neverCallNative2
}
};
static bool registerNativeMethods(JNIEnv *env, const char *className, JNINativeMethod *jniMethods, int methods) {
jclass clazz = env->FindClass(className);
if (clazz == NULL) {
return false;
}
return env->RegisterNatives(clazz, jniMethods, methods) >= 0;
}
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
const char* CLASS_SAND_HOOK = "com/swift/sandhook/SandHook";
const char* CLASS_NEVER_CALL = "com/swift/sandhook/ClassNeverCall";
int jniMethodSize = sizeof(JNINativeMethod);
JNIEnv *env = NULL;
if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) {
return -1;
}
if (!registerNativeMethods(env, CLASS_SAND_HOOK, jniSandHook, sizeof(jniSandHook) / jniMethodSize)) {
return -1;
}
if (!registerNativeMethods(env, CLASS_NEVER_CALL, jniNeverCall, sizeof(jniNeverCall) / jniMethodSize)) {
return -1;
}
LOGW("JNI Loaded");
return JNI_VERSION_1_6;
}
bool JNI_Load_Ex(JNIEnv* env, jclass classSandHook, jclass classNeverCall) {
int jniMethodSize = sizeof(JNINativeMethod);
if (env == nullptr || classSandHook == nullptr || classNeverCall == nullptr)
return false;
if (env->RegisterNatives(classSandHook, jniSandHook, sizeof(jniSandHook) / jniMethodSize) < 0) {
return false;
}
if (env->RegisterNatives(classNeverCall, jniNeverCall, sizeof(jniNeverCall) / jniMethodSize) < 0) {
return false;
}
LOGW("JNI Loaded");
return true;
}

View File

@ -1,9 +0,0 @@
//
// Created by Kotori0 on 2021/1/29.
//
#ifndef EDXPOSED_SANDHOOK_H
#define EDXPOSED_SANDHOOK_H
#include <jni.h>
bool JNI_Load_Ex(JNIEnv* env, jclass classSandHook, jclass classNeverCall);
#endif //EDXPOSED_SANDHOOK_H

View File

@ -1,124 +0,0 @@
#include "../../includes/arch_base.h"
#if defined(__arm__)
#define Reg0 ip
//need restore
#define RegT ip
#define RegMethod r0
FUNCTION_START(REPLACEMENT_HOOK_TRAMPOLINE)
ldr RegMethod, addr_art_method
ldr Reg0, addr_code_entry
ldr pc, [Reg0]
addr_art_method:
.long 0
addr_code_entry:
.long 0
FUNCTION_END(REPLACEMENT_HOOK_TRAMPOLINE)
#define SIZE_JUMP #0x8
FUNCTION_START(DIRECT_JUMP_TRAMPOLINE)
ldr pc, addr_target
addr_target:
.long 0
FUNCTION_END(DIRECT_JUMP_TRAMPOLINE)
FUNCTION_START(INLINE_HOOK_TRAMPOLINE)
ldr Reg0, origin_art_method
cmp RegMethod, Reg0
bne origin_code
ldr RegMethod, hook_art_method
ldr Reg0, addr_hook_code_entry
ldr pc, [Reg0]
origin_code:
.long 0
.long 0
nop
ldr Reg0, addr_origin_code_entry
ldr Reg0, [Reg0]
add Reg0, Reg0, SIZE_JUMP
mov pc, Reg0
origin_art_method:
.long 0
addr_origin_code_entry:
.long 0
hook_art_method:
.long 0
addr_hook_code_entry:
.long 0
FUNCTION_END(INLINE_HOOK_TRAMPOLINE)
FUNCTION_START(CALL_ORIGIN_TRAMPOLINE)
ldr RegMethod, origin_method
ldr pc, addr_origin
origin_method:
.long 0
addr_origin:
.long 0
FUNCTION_END(CALL_ORIGIN_TRAMPOLINE)
//thumb-2
FUNCTION_START_T(DIRECT_JUMP_TRAMPOLINE_T)
ldr pc, addr_target_t
addr_target_t:
.long 0
FUNCTION_END(DIRECT_JUMP_TRAMPOLINE_T)
FUNCTION_START_T(INLINE_HOOK_TRAMPOLINE_T)
//4 byte
ldr RegT, origin_art_method_t
//2 byte
cmp RegMethod, RegT
nop
//2 byte
bne origin_code_t
nop
//4 byte
ldr RegMethod, hook_art_method_t
//4 byte
ldr RegT, addr_hook_code_entry_t
//4 byte
ldr pc, [RegT]
origin_code_t:
//4 byte
.long 0
//4 byte
.long 0
//4byte
nop
nop
//4 byte
ldr RegT, addr_origin_code_entry_t
//4 byte
ldr RegT, [RegT]
//4 byte
add RegT, RegT, SIZE_JUMP
//2 byte
mov pc, RegT
nop
origin_art_method_t:
.long 0
addr_origin_code_entry_t:
.long 0
hook_art_method_t:
.long 0
addr_hook_code_entry_t:
.long 0
FUNCTION_END(INLINE_HOOK_TRAMPOLINE_T)
FUNCTION_START_T(CALL_ORIGIN_TRAMPOLINE_T)
ldr RegMethod, origin_method_t
ldr pc, addr_origin_t
origin_method_t:
.long 0
addr_origin_t:
.long 0
FUNCTION_END(CALL_ORIGIN_TRAMPOLINE_T)
#endif

View File

@ -1,139 +0,0 @@
#include "../../includes/arch_base.h"
/**
*
//aarch64 ART 使
// Method register on invoke.
//
static const vixl::aarch64::Register kArtMethodRegister = vixl::aarch64::x0;
//
static const vixl::aarch64::Register kParameterCoreRegisters[] = {
vixl::aarch64::x1,
vixl::aarch64::x2,
vixl::aarch64::x3,
vixl::aarch64::x4,
vixl::aarch64::x5,
vixl::aarch64::x6,
vixl::aarch64::x7
};
//
const vixl::aarch64::CPURegList vixl_reserved_core_registers(vixl::aarch64::ip0,
vixl::aarch64::ip1);
//
static const vixl::aarch64::FPRegister kParameterFPRegisters[] = {
vixl::aarch64::d0,
vixl::aarch64::d1,
vixl::aarch64::d2,
vixl::aarch64::d3,
vixl::aarch64::d4,
vixl::aarch64::d5,
vixl::aarch64::d6,
vixl::aarch64::d7
};
// Thread Register.
// 线
const vixl::aarch64::Register tr = vixl::aarch64::x19;
// Marking Register.
// GC
const vixl::aarch64::Register mr = vixl::aarch64::x20;
// Callee-save registers AAPCS64, without x19 (Thread Register) (nor
// x20 (Marking Register) when emitting Baker read barriers).
const vixl::aarch64::CPURegList callee_saved_core_registers(
vixl::aarch64::CPURegister::kRegister,
vixl::aarch64::kXRegSize,
((kEmitCompilerReadBarrier && kUseBakerReadBarrier)
? vixl::aarch64::x21.GetCode()
: vixl::aarch64::x20.GetCode()),
vixl::aarch64::x30.GetCode());
x16/x17
X16 = IP0
Stub 使
使 X17
*
*/
#if defined(__aarch64__)
#define Reg0 x17
#define Reg1 x16
#define RegMethod x0
FUNCTION_START(REPLACEMENT_HOOK_TRAMPOLINE)
ldr RegMethod, addr_art_method
ldr Reg0, addr_code_entry
ldr Reg0, [Reg0]
br Reg0
addr_art_method:
.long 0
.long 0
addr_code_entry:
.long 0
.long 0
FUNCTION_END(REPLACEMENT_HOOK_TRAMPOLINE)
#define SIZE_JUMP #0x10
FUNCTION_START(DIRECT_JUMP_TRAMPOLINE)
ldr Reg0, addr_target
br Reg0
addr_target:
.long 0
.long 0
FUNCTION_END(DIRECT_JUMP_TRAMPOLINE)
FUNCTION_START(INLINE_HOOK_TRAMPOLINE)
ldr Reg0, origin_art_method
cmp RegMethod, Reg0
bne origin_code
ldr RegMethod, hook_art_method
ldr Reg0, addr_hook_code_entry
ldr Reg0, [Reg0]
br Reg0
origin_code:
.long 0
.long 0
.long 0
.long 0
ldr Reg0, addr_origin_code_entry
ldr Reg0, [Reg0]
add Reg0, Reg0, SIZE_JUMP
br Reg0
origin_art_method:
.long 0
.long 0
addr_origin_code_entry:
.long 0
.long 0
hook_art_method:
.long 0
.long 0
addr_hook_code_entry:
.long 0
.long 0
FUNCTION_END(INLINE_HOOK_TRAMPOLINE)
FUNCTION_START(CALL_ORIGIN_TRAMPOLINE)
ldr RegMethod, call_origin_art_method
ldr Reg0, addr_call_origin_code
br Reg0
call_origin_art_method:
.long 0
.long 0
addr_call_origin_code:
.long 0
.long 0
FUNCTION_END(CALL_ORIGIN_TRAMPOLINE)
#endif

View File

@ -1,144 +0,0 @@
//
// Created by SwiftGan on 2019/1/17.
//
#ifndef SANDHOOK_TRAMPOLINE_CPP
#define SANDHOOK_TRAMPOLINE_CPP
#include "../includes/trampoline.h"
namespace SandHook {
class DirectJumpTrampoline : public Trampoline {
public:
DirectJumpTrampoline() : Trampoline::Trampoline() {}
void setJumpTarget(Code target) {
codeCopy(reinterpret_cast<Code>(&target), OFFSET_JUMP_ADDR_TARGET, BYTE_POINT);
}
protected:
Size codeLength() override {
return SIZE_DIRECT_JUMP_TRAMPOLINE;
}
Code templateCode() override {
#if defined(__arm__)
if (isThumbCode()) {
return getThumbCodeAddress(reinterpret_cast<Code>(DIRECT_JUMP_TRAMPOLINE_T));
} else {
return reinterpret_cast<Code>(DIRECT_JUMP_TRAMPOLINE);
}
#else
return reinterpret_cast<Code>(DIRECT_JUMP_TRAMPOLINE);
#endif
}
};
class ReplacementHookTrampoline : public Trampoline {
public:
void setHookMethod(Code hookMethod) {
codeCopy(reinterpret_cast<Code>(&hookMethod), OFFSET_REPLACEMENT_ART_METHOD, BYTE_POINT);
void* codeEntry = getEntryCodeAddr(hookMethod);
codeCopy(reinterpret_cast<Code>(&codeEntry), OFFSET_REPLACEMENT_OFFSET_CODE_ENTRY, BYTE_POINT);
}
protected:
Size codeLength() override {
return SIZE_REPLACEMENT_HOOK_TRAMPOLINE;
}
Code templateCode() override {
return reinterpret_cast<Code>(REPLACEMENT_HOOK_TRAMPOLINE);
}
};
class InlineHookTrampoline : public Trampoline {
public:
void setOriginMethod(Code originMethod) {
codeCopy(reinterpret_cast<Code>(&originMethod), OFFSET_INLINE_ORIGIN_ART_METHOD, BYTE_POINT);
void* codeEntry = getEntryCodeAddr(originMethod);
codeCopy(reinterpret_cast<Code>(&codeEntry), OFFSET_INLINE_ADDR_ORIGIN_CODE_ENTRY, BYTE_POINT);
}
void setHookMethod(Code hookMethod) {
codeCopy(reinterpret_cast<Code>(&hookMethod), OFFSET_INLINE_HOOK_ART_METHOD, BYTE_POINT);
void* codeEntry = getEntryCodeAddr(hookMethod);
codeCopy(reinterpret_cast<Code>(&codeEntry), OFFSET_INLINE_ADDR_HOOK_CODE_ENTRY, BYTE_POINT);
}
// void setEntryCodeOffset(Size offSet) {
// codeCopy(reinterpret_cast<Code>(&offSet), OFFSET_INLINE_OFFSET_ENTRY_CODE, BYTE_POINT);
// #if defined(__arm__)
// Code32Bit offset32;
// offset32.code = offSet;
// unsigned char offsetOP = isBigEnd() ? offset32.op.op2 : offset32.op.op1;
// tweakOpImm(OFFSET_INLINE_OP_OFFSET_CODE, offsetOP);
// #endif
// }
void setOriginCode(Code originCode) {
codeCopy(originCode, OFFSET_INLINE_ORIGIN_CODE, SIZE_DIRECT_JUMP_TRAMPOLINE);
}
void setOriginCode(Code originCode, Size codeLen) {
codeCopy(originCode, OFFSET_INLINE_ORIGIN_CODE, codeLen);
}
Code getCallOriginCode() {
return reinterpret_cast<Code>((Size)getCode() + OFFSET_INLINE_ORIGIN_CODE);
}
protected:
Size codeLength() override {
return SIZE_INLINE_HOOK_TRAMPOLINE;
}
Code templateCode() override {
#if defined(__arm__)
if (isThumbCode()) {
return getThumbCodeAddress(reinterpret_cast<Code>(INLINE_HOOK_TRAMPOLINE_T));
} else {
return reinterpret_cast<Code>(INLINE_HOOK_TRAMPOLINE);
}
#else
return reinterpret_cast<Code>(INLINE_HOOK_TRAMPOLINE);
#endif
}
};
class CallOriginTrampoline : public Trampoline {
public:
void setOriginMethod(Code originMethod) {
codeCopy(reinterpret_cast<Code>(&originMethod), OFFSET_CALL_ORIGIN_ART_METHOD, BYTE_POINT);
}
void setOriginCode(Code originCode) {
codeCopy(reinterpret_cast<Code>(&originCode), OFFSET_CALL_ORIGIN_JUMP_ADDR, BYTE_POINT);
}
protected:
Size codeLength() override {
return SIZE_CALL_ORIGIN_TRAMPOLINE;
}
Code templateCode() override {
#if defined(__arm__)
if (isThumbCode()) {
return getThumbCodeAddress(reinterpret_cast<Code>(CALL_ORIGIN_TRAMPOLINE_T));
} else {
return reinterpret_cast<Code>(CALL_ORIGIN_TRAMPOLINE);
}
#else
return reinterpret_cast<Code>(CALL_ORIGIN_TRAMPOLINE);
#endif
}
};
}
#endif //SANDHOOK_TRAMPOLINE_CPP

View File

@ -1,324 +0,0 @@
//
// Created by swift on 2019/1/20.
//
#include "../includes/trampoline_manager.h"
#include "../includes/trampoline.h"
#include "../includes/inst.h"
#include "../includes/log.h"
extern int SDK_INT;
#define SWITCH_SETX0 false
namespace SandHook {
uint32_t TrampolineManager::sizeOfEntryCode(mirror::ArtMethod *method) {
Code codeEntry = getEntryCode(method);
if (codeEntry == nullptr)
return 0;
#if defined(__arm__)
if (isThumbCode(reinterpret_cast<Size>(codeEntry))) {
codeEntry = getThumbCodeAddress(codeEntry);
}
#endif
uint32_t size = *reinterpret_cast<uint32_t *>((Size)codeEntry - 4);
return size;
}
class PCRelatedCheckVisitor : public InstVisitor {
public:
bool pcRelated = false;
bool canSafeBackup = true;
int instSize = 0;
TrampolineManager* trampolineManager;
PCRelatedCheckVisitor(TrampolineManager* t) {
trampolineManager = t;
}
bool visit(Inst *inst, Size offset, Size length) override {
instSize += inst->instLen();
if (inst->pcRelated()) {
LOGW("found pc related inst: %zx !", inst->bin());
if (trampolineManager->inlineSecurityCheck) {
pcRelated = true;
return false;
}
}
if (instSize > SIZE_ORIGIN_PLACE_HOLDER) {
canSafeBackup = false;
}
return true;
}
};
class InstSizeNeedBackupVisitor : public InstVisitor {
public:
Size instSize = 0;
bool visit(Inst *inst, Size offset, Size length) override {
instSize += inst->instLen();
return true;
}
};
bool TrampolineManager::canSafeInline(mirror::ArtMethod *method) {
if (skipAllCheck)
return true;
//check size
if (method->isCompiled()) {
uint32_t originCodeSize = sizeOfEntryCode(method);
if (originCodeSize < SIZE_DIRECT_JUMP_TRAMPOLINE) {
LOGW("can not inline due to origin code is too small(size is %d)", originCodeSize);
return false;
}
}
//check pc relate inst & backup inst len
PCRelatedCheckVisitor visitor(this);
InstDecode::decode(method->getQuickCodeEntry(), SIZE_DIRECT_JUMP_TRAMPOLINE, &visitor);
return (!visitor.pcRelated) && visitor.canSafeBackup;
}
Code TrampolineManager::allocExecuteSpace(Size size) {
if (size > EXE_BLOCK_SIZE)
return 0;
AutoLock autoLock(allocSpaceLock);
void* mmapRes;
Code exeSpace = 0;
if (executeSpaceList.size() == 0) {
goto label_alloc_new_space;
} else if (executePageOffset + size > EXE_BLOCK_SIZE) {
goto label_alloc_new_space;
} else {
exeSpace = executeSpaceList.back();
Code retSpace = exeSpace + executePageOffset;
executePageOffset += size;
return retSpace;
}
label_alloc_new_space:
mmapRes = mmap(NULL, EXE_BLOCK_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_ANON | MAP_PRIVATE, -1, 0);
if (mmapRes == MAP_FAILED) {
return 0;
}
memset(mmapRes, 0, EXE_BLOCK_SIZE);
exeSpace = static_cast<Code>(mmapRes);
executeSpaceList.push_back(exeSpace);
executePageOffset = size;
return exeSpace;
}
HookTrampoline* TrampolineManager::installReplacementTrampoline(mirror::ArtMethod *originMethod,
mirror::ArtMethod *hookMethod,
mirror::ArtMethod *backupMethod) {
AutoLock autoLock(installLock);
if (trampolines.count(originMethod) != 0)
return getHookTrampoline(originMethod);
HookTrampoline* hookTrampoline = new HookTrampoline();
ReplacementHookTrampoline* replacementHookTrampoline = nullptr;
CallOriginTrampoline* callOriginTrampoline = nullptr;
Code replacementHookTrampolineSpace;
Code callOriginTrampolineSpace;
replacementHookTrampoline = new ReplacementHookTrampoline();
replacementHookTrampoline->init();
replacementHookTrampolineSpace = allocExecuteSpace(replacementHookTrampoline->getCodeLen());
if (replacementHookTrampolineSpace == 0) {
LOGE("hook error due to can not alloc execute space!");
goto label_error;
}
replacementHookTrampoline->setExecuteSpace(replacementHookTrampolineSpace);
replacementHookTrampoline->setEntryCodeOffset(quickCompileOffset);
replacementHookTrampoline->setHookMethod(reinterpret_cast<Code>(hookMethod));
hookTrampoline->replacement = replacementHookTrampoline;
hookTrampoline->originCode = static_cast<Code>(originMethod->getQuickCodeEntry());
if (SWITCH_SETX0 && SDK_INT >= ANDROID_N && backupMethod != nullptr) {
callOriginTrampoline = new CallOriginTrampoline();
checkThumbCode(callOriginTrampoline, getEntryCode(originMethod));
callOriginTrampoline->init();
callOriginTrampolineSpace = allocExecuteSpace(callOriginTrampoline->getCodeLen());
if (callOriginTrampolineSpace == 0)
goto label_error;
callOriginTrampoline->setExecuteSpace(callOriginTrampolineSpace);
callOriginTrampoline->setOriginMethod(reinterpret_cast<Code>(originMethod));
Code originCode = getEntryCode(originMethod);
if (callOriginTrampoline->isThumbCode()) {
originCode = callOriginTrampoline->getThumbCodePcAddress(originCode);
}
callOriginTrampoline->setOriginCode(originCode);
hookTrampoline->callOrigin = callOriginTrampoline;
}
trampolines[originMethod] = hookTrampoline;
return hookTrampoline;
label_error:
delete hookTrampoline;
delete replacementHookTrampoline;
if (callOriginTrampoline != nullptr)
delete callOriginTrampoline;
return nullptr;
}
HookTrampoline* TrampolineManager::installInlineTrampoline(mirror::ArtMethod *originMethod,
mirror::ArtMethod *hookMethod,
mirror::ArtMethod *backupMethod) {
AutoLock autoLock(installLock);
if (trampolines.count(originMethod) != 0)
return getHookTrampoline(originMethod);
HookTrampoline* hookTrampoline = new HookTrampoline();
InlineHookTrampoline* inlineHookTrampoline = nullptr;
DirectJumpTrampoline* directJumpTrampoline = nullptr;
CallOriginTrampoline* callOriginTrampoline = nullptr;
Code inlineHookTrampolineSpace;
Code callOriginTrampolineSpace;
Code originEntry;
Size sizeNeedBackup = SIZE_DIRECT_JUMP_TRAMPOLINE;
InstSizeNeedBackupVisitor instVisitor;
InstDecode::decode(originMethod->getQuickCodeEntry(), SIZE_DIRECT_JUMP_TRAMPOLINE, &instVisitor);
sizeNeedBackup = instVisitor.instSize;
//生成二段跳板
inlineHookTrampoline = new InlineHookTrampoline();
checkThumbCode(inlineHookTrampoline, getEntryCode(originMethod));
inlineHookTrampoline->init();
inlineHookTrampolineSpace = allocExecuteSpace(inlineHookTrampoline->getCodeLen());
if (inlineHookTrampolineSpace == 0) {
LOGE("hook error due to can not alloc execute space!");
goto label_error;
}
inlineHookTrampoline->setExecuteSpace(inlineHookTrampolineSpace);
inlineHookTrampoline->setEntryCodeOffset(quickCompileOffset);
inlineHookTrampoline->setOriginMethod(reinterpret_cast<Code>(originMethod));
inlineHookTrampoline->setHookMethod(reinterpret_cast<Code>(hookMethod));
if (inlineHookTrampoline->isThumbCode()) {
inlineHookTrampoline->setOriginCode(inlineHookTrampoline->getThumbCodeAddress(getEntryCode(originMethod)), sizeNeedBackup);
} else {
inlineHookTrampoline->setOriginCode(getEntryCode(originMethod), sizeNeedBackup);
}
hookTrampoline->inlineSecondory = inlineHookTrampoline;
//注入 EntryCode
directJumpTrampoline = new DirectJumpTrampoline();
checkThumbCode(directJumpTrampoline, getEntryCode(originMethod));
directJumpTrampoline->init();
originEntry = getEntryCode(originMethod);
if (!memUnprotect(reinterpret_cast<Size>(originEntry), directJumpTrampoline->getCodeLen())) {
LOGE("hook error due to can not write origin code!");
goto label_error;
}
if (directJumpTrampoline->isThumbCode()) {
originEntry = directJumpTrampoline->getThumbCodeAddress(originEntry);
}
directJumpTrampoline->setExecuteSpace(originEntry);
directJumpTrampoline->setJumpTarget(inlineHookTrampoline->getCode());
hookTrampoline->inlineJump = directJumpTrampoline;
//备份原始方法
if (backupMethod != nullptr) {
callOriginTrampoline = new CallOriginTrampoline();
checkThumbCode(callOriginTrampoline, getEntryCode(originMethod));
callOriginTrampoline->init();
callOriginTrampolineSpace = allocExecuteSpace(callOriginTrampoline->getCodeLen());
if (callOriginTrampolineSpace == 0) {
goto label_error;
}
callOriginTrampoline->setExecuteSpace(callOriginTrampolineSpace);
callOriginTrampoline->setOriginMethod(reinterpret_cast<Code>(originMethod));
Code originCode = nullptr;
if (callOriginTrampoline->isThumbCode()) {
originCode = callOriginTrampoline->getThumbCodePcAddress(inlineHookTrampoline->getCallOriginCode());
#if defined(__arm__)
Code originRemCode = callOriginTrampoline->getThumbCodePcAddress(originEntry + sizeNeedBackup);
Size offset = originRemCode - getEntryCode(originMethod);
if (offset != directJumpTrampoline->getCodeLen()) {
Code32Bit offset32;
offset32.code = offset;
uint8_t offsetOP = callOriginTrampoline->isBigEnd() ? offset32.op.op4 : offset32.op.op1;
inlineHookTrampoline->tweakOpImm(OFFSET_INLINE_OP_ORIGIN_OFFSET_CODE, offsetOP);
}
#endif
} else {
originCode = inlineHookTrampoline->getCallOriginCode();
}
callOriginTrampoline->setOriginCode(originCode);
hookTrampoline->callOrigin = callOriginTrampoline;
}
trampolines[originMethod] = hookTrampoline;
return hookTrampoline;
label_error:
delete hookTrampoline;
if (inlineHookTrampoline != nullptr) {
delete inlineHookTrampoline;
}
if (directJumpTrampoline != nullptr) {
delete directJumpTrampoline;
}
if (callOriginTrampoline != nullptr) {
delete callOriginTrampoline;
}
return nullptr;
}
HookTrampoline* TrampolineManager::installNativeHookTrampolineNoBackup(void *origin,
void *hook) { HookTrampoline* hookTrampoline = new HookTrampoline();
DirectJumpTrampoline* directJumpTrampoline = new DirectJumpTrampoline();
if (!memUnprotect(reinterpret_cast<Size>(origin), directJumpTrampoline->getCodeLen())) {
LOGE("hook error due to can not write origin code!");
goto label_error;
}
directJumpTrampoline->init();
#if defined(__arm__)
checkThumbCode(directJumpTrampoline, reinterpret_cast<Code>(origin));
if (directJumpTrampoline->isThumbCode()) {
origin = directJumpTrampoline->getThumbCodeAddress(reinterpret_cast<Code>(origin));
}
if (isThumbCode(reinterpret_cast<Size>(hook))) {
hook = directJumpTrampoline->getThumbCodePcAddress(reinterpret_cast<Code>(hook));
}
#endif
directJumpTrampoline->setExecuteSpace(reinterpret_cast<Code>(origin));
directJumpTrampoline->setJumpTarget(reinterpret_cast<Code>(hook));
hookTrampoline->inlineJump = directJumpTrampoline;
directJumpTrampoline->flushCache(reinterpret_cast<Size>(origin), directJumpTrampoline->getCodeLen());
hookTrampoline->hookNative = directJumpTrampoline;
return hookTrampoline;
label_error:
delete hookTrampoline;
delete directJumpTrampoline;
return nullptr;
}
TrampolineManager &TrampolineManager::get() {
static TrampolineManager trampolineManager;
return trampolineManager;
}
}

View File

@ -1,296 +0,0 @@
// Copyright (c) 2016 avs333
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//fork from https://github.com/avs333/Nougat_dlfunctions
//do some modify
//support all cpu abi such as x86, x86_64
//support filename search if filename is not start with '/'
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <elf.h>
#include <android/log.h>
#include <dlfcn.h>
#include <string>
#include "../includes/arch.h"
#include "../includes/log.h"
#define TAG_NAME "nougat_dlfcn"
#define log_info(fmt, args...) __android_log_print(ANDROID_LOG_INFO, TAG_NAME, (const char *) fmt, ##args)
#define log_err(fmt, args...) __android_log_print(ANDROID_LOG_ERROR, TAG_NAME, (const char *) fmt, ##args)
#ifdef LOG_DBG
#define log_dbg log_info
#else
#define log_dbg(...)
#endif
#ifdef __LP64__
#define Elf_Ehdr Elf64_Ehdr
#define Elf_Shdr Elf64_Shdr
#define Elf_Sym Elf64_Sym
#else
#define Elf_Ehdr Elf32_Ehdr
#define Elf_Shdr Elf32_Shdr
#define Elf_Sym Elf32_Sym
#endif
extern int SDK_INT;
struct ctx {
void *load_addr;
void *dynstr;
void *dynsym;
int nsyms;
off_t bias;
};
extern "C" {
int fake_dlclose(void *handle) {
if (handle) {
struct ctx *ctx = (struct ctx *) handle;
if (ctx->dynsym) free(ctx->dynsym); /* we're saving dynsym and dynstr */
if (ctx->dynstr) free(ctx->dynstr); /* from library file just in case */
free(ctx);
}
return 0;
}
char *rtrim(char *str)
{
if (str == NULL || *str == '\0')
{
return str;
}
int len = static_cast<int>(strlen(str));
char *p = str + len - 1;
while (p >= str && isspace(*p))
{
*p = '\0'; --p;
}
return str;
}
/* flags are ignored */
void *fake_dlopen_with_path(char *libpath, int flags) {
FILE *maps;
char buff[256];
struct ctx *ctx = 0;
off_t load_addr, size;
int k, fd = -1, found = 0;
char *shoff;
char* p;
Elf_Ehdr *elf = (Elf_Ehdr *) MAP_FAILED;
#define fatal(fmt, args...) do { log_err(fmt,##args); goto err_exit; } while(0)
maps = fopen("/proc/self/maps", "r");
if (!maps) fatal("failed to open maps");
while (fgets(buff, sizeof(buff), maps)) {
if ((strstr(buff, "r-xp") || strstr(buff, "r--p")) && strstr(buff, libpath)) {
found = 1;
__android_log_print(ANDROID_LOG_DEBUG, "dlopen", "%s\n", buff);
break;
}
}
fclose(maps);
if (!found) fatal("%s not found in my userspace", libpath);
if (sscanf(buff, "%lx", &load_addr) != 1)
fatal("failed to read load address for %s", libpath);
log_info("%s loaded in Android at 0x%08lx", libpath, load_addr);
/* Now, mmap the same library once again */
if (SDK_INT >= ANDROID_Q) {
p = std::strtok(buff, " ");
while (p) {
p = std::strtok(NULL, " ");
if (p) {
libpath = rtrim(p);
}
}
}
fd = open(libpath, O_RDONLY);
if (fd < 0) fatal("failed to open %s", libpath);
size = lseek(fd, 0, SEEK_END);
if (size <= 0) fatal("lseek() failed for %s", libpath);
elf = (Elf_Ehdr *) mmap(0, size, PROT_READ, MAP_SHARED, fd, 0);
close(fd);
fd = -1;
if (elf == MAP_FAILED) fatal("mmap() failed for %s", libpath);
ctx = (struct ctx *) calloc(1, sizeof(struct ctx));
if (!ctx) fatal("no memory for %s", libpath);
ctx->load_addr = (void *) load_addr;
shoff = ((char *) elf) + elf->e_shoff;
for (k = 0; k < elf->e_shnum; k++, shoff += elf->e_shentsize) {
Elf_Shdr *sh = (Elf_Shdr *) shoff;
log_dbg("%s: k=%d shdr=%p type=%x", __func__, k, sh, sh->sh_type);
switch (sh->sh_type) {
case SHT_DYNSYM:
if (ctx->dynsym) fatal("%s: duplicate DYNSYM sections", libpath); /* .dynsym */
ctx->dynsym = malloc(sh->sh_size);
if (!ctx->dynsym) fatal("%s: no memory for .dynsym", libpath);
memcpy(ctx->dynsym, ((char *) elf) + sh->sh_offset, sh->sh_size);
ctx->nsyms = (sh->sh_size / sizeof(Elf_Sym));
break;
case SHT_STRTAB:
if (ctx->dynstr) break; /* .dynstr is guaranteed to be the first STRTAB */
ctx->dynstr = malloc(sh->sh_size);
if (!ctx->dynstr) fatal("%s: no memory for .dynstr", libpath);
memcpy(ctx->dynstr, ((char *) elf) + sh->sh_offset, sh->sh_size);
break;
case SHT_PROGBITS:
if (!ctx->dynstr || !ctx->dynsym) break;
/* won't even bother checking against the section name */
ctx->bias = (off_t) sh->sh_addr - (off_t) sh->sh_offset;
k = elf->e_shnum; /* exit for */
break;
}
}
munmap(elf, size);
elf = 0;
if (!ctx->dynstr || !ctx->dynsym) fatal("dynamic sections not found in %s", libpath);
#undef fatal
log_dbg("%s: ok, dynsym = %p, dynstr = %p", libpath, ctx->dynsym, ctx->dynstr);
return ctx;
err_exit:
if (fd >= 0) close(fd);
if (elf != MAP_FAILED) munmap(elf, size);
fake_dlclose(ctx);
return 0;
}
#if defined(__LP64__)
static const char *const kSystemLibDir = "/system/lib64/";
static const char *const kOdmLibDir = "/odm/lib64/";
static const char *const kVendorLibDir = "/vendor/lib64/";
#else
static const char* const kSystemLibDir = "/system/lib/";
static const char* const kOdmLibDir = "/odm/lib/";
static const char* const kVendorLibDir = "/vendor/lib/";
#endif
void *fake_dlopen(char *filename, int flags) {
if (strlen(filename) > 0 && filename[0] == '/') {
return fake_dlopen_with_path(filename, flags);
} else {
char buf[512] = {0};
void *handle = NULL;
//sysmtem
strcpy(buf, kSystemLibDir);
strcat(buf, filename);
handle = fake_dlopen_with_path(buf, flags);
if (handle) {
return handle;
}
//odm
memset(buf, 0, sizeof(buf));
strcpy(buf, kOdmLibDir);
strcat(buf, filename);
handle = fake_dlopen_with_path(buf, flags);
if (handle) {
return handle;
}
//vendor
memset(buf, 0, sizeof(buf));
strcpy(buf, kVendorLibDir);
strcat(buf, filename);
handle = fake_dlopen_with_path(buf, flags);
if (handle) {
return handle;
}
return fake_dlopen_with_path(filename, flags);
}
}
void *fake_dlsym(void *handle, const char *name) {
int k;
struct ctx *ctx = (struct ctx *) handle;
Elf_Sym *sym = (Elf_Sym *) ctx->dynsym;
char *strings = (char *) ctx->dynstr;
for (k = 0; k < ctx->nsyms; k++, sym++)
if (strcmp(strings + sym->st_name, name) == 0) {
/* NB: sym->st_value is an offset into the section for relocatables,
but a VMA for shared libs or exe files, so we have to subtract the bias */
void *ret = (char *) ctx->load_addr + sym->st_value - ctx->bias;
log_info("%s found at %p", name, ret);
return ret;
}
return 0;
}
const char *fake_dlerror() {
return NULL;
}
void *getSymCompat(const char *filename, const char *name) {
if (SDK_INT >= ANDROID_N) {
void* handle = fake_dlopen(const_cast<char *>(filename), RTLD_NOW);
if (handle) {
void* ret = fake_dlsym(handle, name);
fake_dlclose(handle);
return ret;
}
} else {
void* handle = dlopen(filename, RTLD_LAZY | RTLD_GLOBAL);
if (handle) {
return dlsym(handle, name);
}
}
return nullptr;
}
}

View File

@ -1,171 +0,0 @@
//
// Created by Swift Gan on 2019/3/14.
//
#include <malloc.h>
#include <string.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <assert.h>
#include "../includes/elf_util.h"
#include "../includes/log.h"
using namespace SandHook;
ElfImg::ElfImg(const char *elf) {
this->elf = elf;
//load elf
int fd = open(elf, O_RDONLY);
if (fd < 0) {
LOGE("failed to open %s", elf);
return;
}
size = lseek(fd, 0, SEEK_END);
if (size <= 0) {
LOGE("lseek() failed for %s", elf);
}
header = reinterpret_cast<Elf_Ehdr *>(mmap(0, size, PROT_READ, MAP_SHARED, fd, 0));
close(fd);
section_header = reinterpret_cast<Elf_Shdr *>(((size_t) header) + header->e_shoff);
size_t shoff = reinterpret_cast<size_t>(section_header);
char *section_str = reinterpret_cast<char *>(section_header[header->e_shstrndx].sh_offset +
((size_t) header));
for (int i = 0; i < header->e_shnum; i++, shoff += header->e_shentsize) {
Elf_Shdr *section_h = (Elf_Shdr *) shoff;
char *sname = section_h->sh_name + section_str;
Elf_Off entsize = section_h->sh_entsize;
switch (section_h->sh_type) {
case SHT_DYNSYM:
if (bias == -4396) {
dynsym = section_h;
dynsym_offset = section_h->sh_offset;
dynsym_size = section_h->sh_size;
dynsym_count = dynsym_size / entsize;
dynsym_start = reinterpret_cast<Elf_Sym *>(((size_t) header) + dynsym_offset);
}
break;
case SHT_SYMTAB:
if (strcmp(sname, ".symtab") == 0) {
symtab = section_h;
symtab_offset = section_h->sh_offset;
symtab_size = section_h->sh_size;
symtab_count = symtab_size / entsize;
symtab_start = reinterpret_cast<Elf_Sym *>(((size_t) header) + symtab_offset);
}
break;
case SHT_STRTAB:
if (bias == -4396) {
strtab = section_h;
symstr_offset = section_h->sh_offset;
strtab_start = reinterpret_cast<Elf_Sym *>(((size_t) header) + symstr_offset);
}
if (strcmp(sname, ".strtab") == 0) {
symstr_offset_for_symtab = section_h->sh_offset;
}
break;
case SHT_PROGBITS:
if (strtab == nullptr || dynsym == nullptr) break;
if (bias == -4396) {
bias = (off_t) section_h->sh_addr - (off_t) section_h->sh_offset;
}
break;
}
}
if (!symtab_offset) {
LOGW("can't find symtab from sections\n");
}
//load module base
base = getModuleBase(elf);
}
ElfImg::~ElfImg() {
//open elf file local
if (buffer) {
free(buffer);
buffer = nullptr;
}
//use mmap
if (header) {
munmap(header, size);
}
}
Elf_Addr ElfImg::getSymbOffset(const char *name) {
Elf_Addr _offset = 0;
//search dynmtab
if (dynsym_start != nullptr && strtab_start != nullptr) {
Elf_Sym *sym = dynsym_start;
char *strings = (char *) strtab_start;
int k;
for (k = 0; k < dynsym_count; k++, sym++)
if (strcmp(strings + sym->st_name, name) == 0) {
_offset = sym->st_value;
LOGD("find %s: %x\n", elf, _offset);
return _offset;
}
}
//search symtab
if (symtab_start != nullptr && symstr_offset_for_symtab != 0) {
for (int i = 0; i < symtab_count; i++) {
unsigned int st_type = ELF_ST_TYPE(symtab_start[i].st_info);
char *st_name = reinterpret_cast<char *>(((size_t) header) + symstr_offset_for_symtab +
symtab_start[i].st_name);
if (st_type == STT_FUNC && symtab_start[i].st_size) {
if (strcmp(st_name, name) == 0) {
_offset = symtab_start[i].st_value;
LOGD("find %s: %x\n", elf, _offset);
return _offset;
}
}
}
}
return 0;
}
Elf_Addr ElfImg::getSymbAddress(const char *name) {
Elf_Addr offset = getSymbOffset(name);
if (offset > 0 && base != nullptr) {
return static_cast<Elf_Addr>((size_t) base + offset - bias);
} else {
return 0;
}
}
void *ElfImg::getModuleBase(const char *name) {
FILE *maps;
char buff[256];
off_t load_addr;
int found = 0;
maps = fopen("/proc/self/maps", "r");
while (fgets(buff, sizeof(buff), maps)) {
if ((strstr(buff, "r-xp") || strstr(buff, "r--p")) && strstr(buff, name)) {
found = 1;
__android_log_print(ANDROID_LOG_DEBUG, "dlopen", "%s\n", buff);
break;
}
}
if (!found) {
LOGE("failed to read load address for %s", name);
return nullptr;
}
if (sscanf(buff, "%lx", &load_addr) != 1)
LOGE("failed to read load address for %s", name);
fclose(maps);
LOGD("get module base %s: %lu", name, load_addr);
return reinterpret_cast<void *>(load_addr);
}

View File

@ -1,337 +0,0 @@
//
// Created by swift on 2019/1/21.
//
#include "../includes/hide_api.h"
#include "../includes/arch.h"
#include "../includes/elf_util.h"
#include "../includes/log.h"
#include "../includes/utils.h"
#include "../includes/trampoline_manager.h"
#include "../includes/art_collector_type.h"
#include "../includes/art_gc_cause.h"
extern int SDK_INT;
extern "C" {
void* jitCompilerHandle = nullptr;
bool (*jitCompileMethod)(void*, void*, void*, bool) = nullptr;
bool (*jitCompileMethodQ)(void*, void*, void*, bool, bool) = nullptr;
void (*scoped_suspend_all_ctor)(void *, const char *, bool) = nullptr;
void (*scoped_suspend_all_dtor)(void *) = nullptr;
void (*scoped_gc_critical_section_ctor)(void *, void *, art::gc::GcCause, art::gc::CollectorType) = nullptr;
void (*scoped_gc_critical_section_dtor)(void *) = nullptr;
jobject (*addWeakGlobalRef)(JavaVM *, void *, void *) = nullptr;
art::jit::JitCompiler** globalJitCompileHandlerAddr = nullptr;
//for Android Q
void (**origin_jit_update_options)(void *) = nullptr;
void (*profileSaver_ForceProcessProfiles)() = nullptr;
jfieldID fieldArtMethod = nullptr;
// paths
const char* art_lib_path;
const char* jit_lib_path;
JavaVM* jvm;
void *(*hook_native)(void* origin, void *replace) = nullptr;
void (*class_init_callback)(void*) = nullptr;
void (*backup_fixup_static_trampolines)(void *, void *) = nullptr;
void initHideApi(JNIEnv* env) {
env->GetJavaVM(&jvm);
if (BYTE_POINT == 8) {
if (SDK_INT >= ANDROID_Q) {
art_lib_path = "/lib64/libart.so";
jit_lib_path = "/lib64/libart-compiler.so";
} else {
art_lib_path = "/system/lib64/libart.so";
jit_lib_path = "/system/lib64/libart-compiler.so";
}
} else {
if (SDK_INT >= ANDROID_Q) {
art_lib_path = "/lib/libart.so";
jit_lib_path = "/lib/libart-compiler.so";
} else {
art_lib_path = "/system/lib/libart.so";
jit_lib_path = "/system/lib/libart-compiler.so";
}
}
//init compile
if (SDK_INT >= ANDROID_N) {
globalJitCompileHandlerAddr = reinterpret_cast<art::jit::JitCompiler **>(getSymCompat(art_lib_path, "_ZN3art3jit3Jit20jit_compiler_handle_E"));
if (SDK_INT >= ANDROID_Q) {
jitCompileMethodQ = reinterpret_cast<bool (*)(void *, void *, void *, bool,
bool)>(getSymCompat(jit_lib_path, "jit_compile_method"));
} else {
jitCompileMethod = reinterpret_cast<bool (*)(void *, void *, void *,
bool)>(getSymCompat(jit_lib_path,
"jit_compile_method"));
}
auto jit_load = getSymCompat(jit_lib_path, "jit_load");
if (jit_load) {
if (SDK_INT >= ANDROID_Q) {
// Android 10void* jit_load()
// Android 11: JitCompilerInterface* jit_load()
jitCompilerHandle = reinterpret_cast<void*(*)()>(jit_load)();
} else {
// void* jit_load(bool* generate_debug_info)
bool generate_debug_info = false;
jitCompilerHandle = reinterpret_cast<void*(*)(void*)>(jit_load)(&generate_debug_info);
}
} else {
jitCompilerHandle = getGlobalJitCompiler();
}
if (jitCompilerHandle != nullptr) {
art::CompilerOptions* compilerOptions = getCompilerOptions(
reinterpret_cast<art::jit::JitCompiler *>(jitCompilerHandle));
disableJitInline(compilerOptions);
}
}
//init suspend
scoped_suspend_all_ctor = reinterpret_cast<decltype(scoped_suspend_all_ctor)>(getSymCompat(art_lib_path,
"_ZN3art16ScopedSuspendAllC2EPKcb"));
scoped_suspend_all_dtor = reinterpret_cast<decltype(scoped_suspend_all_dtor)>(getSymCompat(art_lib_path,
"_ZN3art16ScopedSuspendAllD2Ev"));
scoped_gc_critical_section_ctor = reinterpret_cast<decltype(scoped_gc_critical_section_ctor)>(getSymCompat(art_lib_path,
"_ZN3art2gc23ScopedGCCriticalSectionC2EPNS_6ThreadENS0_7GcCauseENS0_13CollectorTypeE"));
scoped_gc_critical_section_dtor = reinterpret_cast<decltype(scoped_gc_critical_section_dtor)>(getSymCompat(art_lib_path,
"_ZN3art2gc23ScopedGCCriticalSectionD2Ev"));
//init for getObject & JitCompiler
const char* add_weak_ref_sym;
if (SDK_INT < ANDROID_M) {
add_weak_ref_sym = "_ZN3art9JavaVMExt22AddWeakGlobalReferenceEPNS_6ThreadEPNS_6mirror6ObjectE";
} else if (SDK_INT < ANDROID_N) {
add_weak_ref_sym = "_ZN3art9JavaVMExt16AddWeakGlobalRefEPNS_6ThreadEPNS_6mirror6ObjectE";
} else {
add_weak_ref_sym = SDK_INT <= ANDROID_N2
? "_ZN3art9JavaVMExt16AddWeakGlobalRefEPNS_6ThreadEPNS_6mirror6ObjectE"
: "_ZN3art9JavaVMExt16AddWeakGlobalRefEPNS_6ThreadENS_6ObjPtrINS_6mirror6ObjectEEE";
}
addWeakGlobalRef = reinterpret_cast<jobject (*)(JavaVM *, void *,
void *)>(getSymCompat(art_lib_path, add_weak_ref_sym));
if (SDK_INT >= ANDROID_Q) {
origin_jit_update_options = reinterpret_cast<void (**)(void *)>(getSymCompat(art_lib_path, "_ZN3art3jit3Jit20jit_update_options_E"));
}
if (SDK_INT > ANDROID_N) {
profileSaver_ForceProcessProfiles = reinterpret_cast<void (*)()>(getSymCompat(art_lib_path, "_ZN3art12ProfileSaver20ForceProcessProfilesEv"));
}
if (SDK_INT >=ANDROID_R) {
auto classExecutable = env->FindClass("java/lang/reflect/Executable");
fieldArtMethod = env->GetFieldID(classExecutable, "artMethod", "J");
}
}
bool canCompile() {
if (SDK_INT >= ANDROID_R)
return false;
if (getGlobalJitCompiler() == nullptr) {
LOGE("JIT not init!");
return false;
}
JNIEnv *env;
jvm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_6);
return getBooleanFromJava(env, "com/swift/sandhook/SandHookConfig",
"compiler");
}
bool compileMethod(void* artMethod, void* thread) {
if (jitCompilerHandle == nullptr)
return false;
if (!canCompile()) return false;
//backup thread flag and state because of jit compile function will modify thread state
uint32_t old_flag_and_state = *((uint32_t *) thread);
bool ret;
if (SDK_INT >= ANDROID_Q) {
if (jitCompileMethodQ == nullptr) {
return false;
}
ret = jitCompileMethodQ(jitCompilerHandle, artMethod, thread, false, false);
} else {
if (jitCompileMethod == nullptr) {
return false;
}
ret= jitCompileMethod(jitCompilerHandle, artMethod, thread, false);
}
memcpy(thread, &old_flag_and_state, 4);
return ret;
}
void suspendVM(void * thiz) {
if (scoped_suspend_all_ctor == nullptr || scoped_suspend_all_dtor == nullptr || scoped_gc_critical_section_ctor == nullptr || scoped_gc_critical_section_dtor == nullptr)
return;
scoped_gc_critical_section_ctor(thiz, getCurrentThread(), art::gc::kGcCauseDebugger, art::gc::kCollectorTypeDebugger);
scoped_suspend_all_ctor(thiz, "Sandhook", false);
}
void resumeVM(void * thiz) {
if (scoped_suspend_all_ctor == nullptr || scoped_suspend_all_dtor == nullptr || scoped_gc_critical_section_ctor == nullptr || scoped_gc_critical_section_dtor == nullptr)
return;
scoped_gc_critical_section_dtor(thiz);
scoped_suspend_all_dtor(thiz);
}
bool canGetObject() {
return addWeakGlobalRef != nullptr;
}
void *getCurrentThread() {
return __get_tls()[TLS_SLOT_ART_THREAD];
}
jobject getJavaObject(JNIEnv* env, void* thread, void* address) {
if (addWeakGlobalRef == nullptr)
return nullptr;
jobject object = addWeakGlobalRef(jvm, thread, address);
if (object == nullptr)
return nullptr;
jobject result = env->NewLocalRef(object);
env->DeleteWeakGlobalRef(object);
return result;
}
art::jit::JitCompiler* getGlobalJitCompiler() {
if (SDK_INT < ANDROID_N)
return nullptr;
if (globalJitCompileHandlerAddr == nullptr)
return nullptr;
return *globalJitCompileHandlerAddr;
}
art::CompilerOptions* getCompilerOptions(art::jit::JitCompiler* compiler) {
if (compiler == nullptr)
return nullptr;
return compiler->compilerOptions.get();
}
art::CompilerOptions* getGlobalCompilerOptions() {
return getCompilerOptions(getGlobalJitCompiler());
}
bool disableJitInline(art::CompilerOptions* compilerOptions) {
if (compilerOptions == nullptr)
return false;
size_t originOptions = compilerOptions->getInlineMaxCodeUnits();
//maybe a real inlineMaxCodeUnits
if (originOptions > 0 && originOptions <= 1024) {
compilerOptions->setInlineMaxCodeUnits(0);
return true;
} else {
return false;
}
}
void* getInterpreterBridge(bool isNative) {
SandHook::ElfImg libart(art_lib_path);
if (isNative) {
return reinterpret_cast<void *>(libart.getSymbAddress("art_quick_generic_jni_trampoline"));
} else {
return reinterpret_cast<void *>(libart.getSymbAddress("art_quick_to_interpreter_bridge"));
}
}
//to replace jit_update_option
void fake_jit_update_options(void* handle) {
//do nothing
LOGW("android q: art request update compiler options");
}
bool replaceUpdateCompilerOptionsQ() {
if (SDK_INT < ANDROID_Q)
return false;
if (origin_jit_update_options == nullptr
|| *origin_jit_update_options == nullptr)
return false;
*origin_jit_update_options = fake_jit_update_options;
return true;
}
bool forceProcessProfiles() {
if (profileSaver_ForceProcessProfiles == nullptr)
return false;
profileSaver_ForceProcessProfiles();
return true;
}
void replaceFixupStaticTrampolines(void *thiz, void *clazz_ptr) {
backup_fixup_static_trampolines(thiz, clazz_ptr);
if (class_init_callback) {
class_init_callback(clazz_ptr);
}
}
bool hookClassInit(void(*callback)(void*)) {
void* symFixupStaticTrampolines = getSymCompat(art_lib_path, "_ZN3art11ClassLinker22FixupStaticTrampolinesENS_6ObjPtrINS_6mirror5ClassEEE");
if (symFixupStaticTrampolines == nullptr) {
//huawei lon-al00 android 7.0 api level 24
symFixupStaticTrampolines = getSymCompat(art_lib_path,
"_ZN3art11ClassLinker22FixupStaticTrampolinesEPNS_6mirror5ClassE");
}
if (symFixupStaticTrampolines == nullptr || hook_native == nullptr)
return false;
backup_fixup_static_trampolines = reinterpret_cast<void (*)(void *, void *)>(hook_native(
symFixupStaticTrampolines, (void *) replaceFixupStaticTrampolines));
if (backup_fixup_static_trampolines) {
class_init_callback = callback;
return true;
} else {
return false;
}
}
JNIEnv *getEnv() {
JNIEnv *env;
jvm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_6);
return env;
}
JNIEnv *attachAndGetEvn() {
JNIEnv *env = getEnv();
if (env == nullptr) {
jvm->AttachCurrentThread(&env, nullptr);
}
return env;
}
static bool isIndexId(jmethodID mid) {
return (reinterpret_cast<uintptr_t>(mid) % 2) != 0;
}
ArtMethod* getArtMethod(JNIEnv *env, jobject method) {
if (SDK_INT >= ANDROID_R) {
return reinterpret_cast<ArtMethod *>(env->GetLongField(method, fieldArtMethod));
} else {
jmethodID methodId = env->FromReflectedMethod(method);
return reinterpret_cast<ArtMethod *>(methodId);
}
}
}

View File

@ -1,34 +0,0 @@
//
// Created by SwiftGan on 2019/1/21.
//
#ifndef SANDHOOK_LOCK_H
#define SANDHOOK_LOCK_H
#include "mutex"
#include "../includes/hide_api.h"
namespace SandHook {
class AutoLock {
public:
inline AutoLock(std::mutex& mutex) : mLock(mutex) { mLock.lock(); }
inline AutoLock(std::mutex* mutex) : mLock(*mutex) { mLock.lock(); }
inline ~AutoLock() { mLock.unlock(); }
private:
std::mutex& mLock;
};
class StopTheWorld {
public:
inline StopTheWorld() { suspendVM(this); }
inline ~StopTheWorld() { resumeVM(this); }
private:
void* self_;
const char* section_name_;
const char* old_no_suspend_reason_;
};
}
#endif //SANDHOOK_LOCK_H

View File

@ -1,59 +0,0 @@
//
// Created by swift on 2019/2/3.
//
#include "../includes/offset.h"
namespace SandHook {
template<typename T>
int Offset::findOffset(void *start, size_t len, size_t step, T value) {
if (nullptr == start) {
return -1;
}
for (int i = 0; i <= len; i += step) {
T current_value = *reinterpret_cast<T *>((size_t) start + i);
if (value == current_value) {
return i;
}
}
return -1;
}
template<typename T>
int Offset::findOffsetWithCB1(void *start, size_t len, size_t step, bool func(int, T)) {
if (nullptr == start) {
return -1;
}
for (int i = 0; i <= len; i += step) {
T current_value = *reinterpret_cast<T *>((size_t) start + i);
if (func(i, current_value)) {
return i;
}
}
return -1;
}
template<typename T>
int Offset::findOffsetWithCB2(void *start1, void *start2, size_t len, size_t step, bool func(T, T)) {
if (nullptr == start1 || nullptr == start2) {
return -1;
}
for (int i = 0; i <= len; i += step) {
T v1 = *reinterpret_cast<T *>((size_t) start1 + i);
T v2 = *reinterpret_cast<T *>((size_t) start2 + i);
if (func(v1, v2)) {
return i;
}
}
return -1;
}
}

View File

@ -1,143 +0,0 @@
//
// Created by swift on 2019/2/3.
//
#include <cstring>
#include "../includes/utils.h"
extern "C" {
Size getAddressFromJava(JNIEnv *env, const char *className, const char *fieldName) {
jclass clazz = env->FindClass(className);
if (clazz == NULL) {
printf("find class error !");
return 0;
}
jfieldID id = env->GetStaticFieldID(clazz, fieldName, "J");
if (id == NULL) {
printf("find field error !");
return 0;
}
return static_cast<Size>(env->GetStaticLongField(clazz, id));
}
Size callStaticMethodAddr(JNIEnv *env, const char *className, const char *method, const char *sig, ...) {
jclass clazz = env->FindClass(className);
if (clazz == NULL) {
printf("find class error !");
return 0;
}
jmethodID id = env->GetStaticMethodID(clazz, method, sig);
if (id == NULL) {
printf("find field error !");
return 0;
}
va_list vas;
va_start(vas, sig);
auto res = static_cast<Size>(env->CallStaticLongMethodV(clazz, id, vas));
env->ExceptionClear();
va_end(vas);
return res;
}
jobject callStaticMethodObject(JNIEnv *env, const char *className, const char *method, const char *sig, ...) {
jclass clazz = env->FindClass(className);
if (clazz == NULL) {
printf("find class error !");
return 0;
}
jmethodID id = env->GetStaticMethodID(clazz, method, sig);
if (id == NULL) {
printf("find field error !");
return 0;
}
va_list vas;
va_start(vas, sig);
auto res = env->CallStaticObjectMethodV(clazz, id, vas);
env->ExceptionClear();
va_end(vas);
return res;
}
jobject getMethodObject(JNIEnv *env, const char *clazz, const char *method) {
auto methodStr = env->NewStringUTF(method);
auto clazzStr = env->NewStringUTF(clazz);
auto res = callStaticMethodObject(env, "com/swift/sandhook/SandHook", "getJavaMethod",
"(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object;", clazzStr, methodStr);
env->ExceptionClear();
env->DeleteLocalRef(methodStr);
env->DeleteLocalRef(clazzStr);
return res;
}
Size getAddressFromJavaByCallMethod(JNIEnv *env, const char *className, const char *methodName) {
jclass clazz = env->FindClass(className);
if (clazz == NULL) {
printf("find class error !");
return 0;
}
jmethodID id = env->GetStaticMethodID(clazz, methodName, "()J");
if (id == NULL) {
printf("find field error !");
return 0;
}
auto res = env->CallStaticLongMethodA(clazz, id, nullptr);
env->ExceptionClear();
return static_cast<Size>(res);
}
jint getIntFromJava(JNIEnv *env, const char *className, const char *fieldName) {
jclass clazz = env->FindClass(className);
if (clazz == NULL) {
printf("find class error !");
return 0;
}
jfieldID id = env->GetStaticFieldID(clazz, fieldName, "I");
if (id == NULL) {
printf("find field error !");
return 0;
}
return env->GetStaticIntField(clazz, id);
}
bool getBooleanFromJava(JNIEnv *env, const char *className, const char *fieldName) {
jclass clazz = env->FindClass(className);
if (clazz == NULL) {
printf("find class error !");
return false;
}
jfieldID id = env->GetStaticFieldID(clazz, fieldName, "Z");
if (id == NULL) {
printf("find field error !");
return false;
}
return env->GetStaticBooleanField(clazz, id);
}
bool munprotect(size_t addr, size_t len) {
long pagesize = sysconf(_SC_PAGESIZE);
unsigned alignment = (unsigned) ((unsigned long long) addr % pagesize);
int i = mprotect((void *) (addr - alignment), (size_t) (alignment + len),
PROT_READ | PROT_WRITE | PROT_EXEC);
if (i == -1) {
return false;
}
return true;
}
bool flushCacheExt(Size addr, Size len) {
#if defined(__arm__)
int i = cacheflush(addr, addr + len, 0);
if (i == -1) {
return false;
}
return true;
#elif defined(__aarch64__)
char *begin = reinterpret_cast<char *>(addr);
__builtin___clear_cache(begin, begin + len);
#endif
return true;
}
}

View File

@ -29,4 +29,4 @@ SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--dynamic-list=${CMAK
find_package(riru REQUIRED CONFIG) find_package(riru REQUIRED CONFIG)
find_library(log-lib log) find_library(log-lib log)
target_link_libraries(riru_lspd yahfa riru::riru android dobby sandhook.lspd dex_builder ${log-lib}) target_link_libraries(riru_lspd yahfa riru::riru android dobby dex_builder ${log-lib})

View File

@ -78,7 +78,7 @@ ALWAYS_INLINE static int ClearException(JNIEnv *env) {
#define JNI_GetMethodID(env, class, name, sig) \ #define JNI_GetMethodID(env, class, name, sig) \
env->GetMethodID(class, name, sig); \ env->GetMethodID(class, name, sig); \
if (ClearException(env)) LOGE("GetMethodID " #name) //if (ClearException(env)) LOGE("GetMethodID " #name)
#define JNI_CallObjectMethod(env, obj, ...) \ #define JNI_CallObjectMethod(env, obj, ...) \
env->CallObjectMethod(obj, __VA_ARGS__); \ env->CallObjectMethod(obj, __VA_ARGS__); \

View File

@ -22,7 +22,6 @@
#include "jni_helper.h" #include "jni_helper.h"
#include "jni/art_class_linker.h" #include "jni/art_class_linker.h"
#include "jni/yahfa.h" #include "jni/yahfa.h"
#include "jni/sandhook.h"
#include "jni/resources_hook.h" #include "jni/resources_hook.h"
#include <dl_util.h> #include <dl_util.h>
#include <art/runtime/jni_env_ext.h> #include <art/runtime/jni_env_ext.h>
@ -122,7 +121,6 @@ namespace lspd {
RegisterYahfa(env); RegisterYahfa(env);
RegisterPendingHooks(env); RegisterPendingHooks(env);
RegisterNativeAPI(env); RegisterNativeAPI(env);
RegisterSandHook(env);
} }
jclass jclass

View File

@ -44,7 +44,7 @@ namespace lspd {
static JNINativeMethod gMethods[] = { static JNINativeMethod gMethods[] = {
LSP_NATIVE_METHOD(ClassLinker, setEntryPointsToInterpreter, LSP_NATIVE_METHOD(ClassLinker, setEntryPointsToInterpreter,
"(Ljava/lang/reflect/Member;)V") "(Ljava/lang/reflect/Executable;)V")
}; };
void RegisterArtClassLinker(JNIEnv *env) { void RegisterArtClassLinker(JNIEnv *env) {

View File

@ -1,37 +0,0 @@
/*
* This file is part of LSPosed.
*
* LSPosed is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* LSPosed is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with LSPosed. If not, see <https://www.gnu.org/licenses/>.
*
* Copyright (C) 2021 LSPosed Contributors
*/
#include <sandhook.h>
#include "sandhook.h"
#include "native_util.h"
namespace lspd {
LSP_DEF_NATIVE_METHOD(bool, SandHook, init, jclass classSandHook, jclass classNeverCall) {
return JNI_Load_Ex(env, classSandHook, classNeverCall);
}
static JNINativeMethod gMethods[] = {
LSP_NATIVE_METHOD(SandHook, init,
"(Ljava/lang/Class;Ljava/lang/Class;)Z"),
};
void RegisterSandHook(JNIEnv *env) {
REGISTER_LSP_NATIVE_METHODS(SandHook);
}
}

View File

@ -1,32 +0,0 @@
/*
* This file is part of LSPosed.
*
* LSPosed is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* LSPosed is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with LSPosed. If not, see <https://www.gnu.org/licenses/>.
*
* Copyright (C) 2021 LSPosed Contributors
*/
//
// Created by loves on 2/18/2021.
//
#pragma once
#include "jni.h"
namespace lspd {
void RegisterSandHook(JNIEnv *env);
};

View File

@ -173,11 +173,11 @@ namespace lspd {
static JNINativeMethod gMethods[] = { static JNINativeMethod gMethods[] = {
LSP_NATIVE_METHOD(Yahfa, init, "(I)V"), LSP_NATIVE_METHOD(Yahfa, init, "(I)V"),
LSP_NATIVE_METHOD(Yahfa, findMethodNative, LSP_NATIVE_METHOD(Yahfa, findMethodNative,
"(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/reflect/Member;"), "(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/reflect/Executable;"),
LSP_NATIVE_METHOD(Yahfa, backupAndHookNative, LSP_NATIVE_METHOD(Yahfa, backupAndHookNative,
"(Ljava/lang/Object;Ljava/lang/reflect/Method;Ljava/lang/reflect/Method;)Z"), "(Ljava/lang/reflect/Executable;Ljava/lang/reflect/Method;Ljava/lang/reflect/Method;)Z"),
LSP_NATIVE_METHOD(Yahfa, recordHooked, "(Ljava/lang/reflect/Member;)V"), LSP_NATIVE_METHOD(Yahfa, recordHooked, "(Ljava/lang/reflect/Executable;)V"),
LSP_NATIVE_METHOD(Yahfa, isHooked, "(Ljava/lang/reflect/Member;)Z"), LSP_NATIVE_METHOD(Yahfa, isHooked, "(Ljava/lang/reflect/Executable;)Z"),
LSP_NATIVE_METHOD(Yahfa, buildHooker, "(Ljava/lang/ClassLoader;Ljava/lang/Class;[Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Class;"), LSP_NATIVE_METHOD(Yahfa, buildHooker, "(Ljava/lang/ClassLoader;Ljava/lang/Class;[Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Class;"),
}; };

View File

@ -1,57 +0,0 @@
package com.swift.sandhook.xposedcompat;
import io.github.lsposed.lspd.util.ProxyClassLoader;
import com.swift.sandhook.wrapper.HookWrapper;
import com.swift.sandhook.xposedcompat.methodgen.SandHookXposedBridge;
import com.swift.sandhook.xposedcompat.utils.ApplicationUtils;
import java.lang.reflect.Member;
import de.robv.android.xposed.XposedBridge;
public class XposedCompat {
public static volatile ClassLoader classLoader;
//try to use internal stub hooker & backup method to speed up hook
public static volatile boolean useInternalStub = true;
public static volatile boolean useNewCallBackup = true;
public static volatile boolean retryWhenCallOriginError = false;
private static ClassLoader sandHookXposedClassLoader;
public static void addHookers(ClassLoader classLoader, Class[] hookers) {
if (hookers == null)
return;
for (Class hooker : hookers) {
try {
HookWrapper.addHookClass(classLoader, hooker);
} catch (Throwable throwable) {
}
}
}
public static void onForkProcess() {
classLoader = null;
sandHookXposedClassLoader = null;
}
public static ClassLoader getClassLoader() {
if (classLoader == null) {
classLoader = getSandHookXposedClassLoader(ApplicationUtils.currentApplication().getClassLoader(), XposedCompat.class.getClassLoader());
}
return classLoader;
}
public static synchronized void hookMethod(Member hookMethod, XposedBridge.AdditionalHookInfo additionalHookInfo) {
SandHookXposedBridge.hookMethod(hookMethod, additionalHookInfo);
}
public static ClassLoader getSandHookXposedClassLoader(ClassLoader appOriginClassLoader, ClassLoader sandBoxHostClassLoader) {
if (sandHookXposedClassLoader != null) {
return sandHookXposedClassLoader;
} else {
sandHookXposedClassLoader = new ProxyClassLoader(sandBoxHostClassLoader, appOriginClassLoader);
return sandHookXposedClassLoader;
}
}
}

View File

@ -1,5 +0,0 @@
package com.swift.sandhook.xposedcompat.hookstub;
public interface CallOriginCallBack {
long call(long... args) throws Throwable;
}

View File

@ -1,93 +0,0 @@
package com.swift.sandhook.xposedcompat.hookstub;
import com.swift.sandhook.SandHook;
import com.swift.sandhook.utils.ParamWrapper;
import java.lang.reflect.Constructor;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
public class HookMethodEntity {
public Member origin;
public Method hook;
public Method backup;
public Class[] parType;
public Class retType;
public HookMethodEntity(Member origin, Method hook, Method backup) {
this.origin = origin;
this.hook = hook;
this.backup = backup;
}
public Object[] getArgs(long... addresses) {
if (addresses == null || addresses.length == 0)
return new Object[0];
if (parType == null || parType.length == 0)
return new Object[0];
int argStart = 0;
if (!isStatic()) {
argStart = 1;
}
Object[] args = new Object[parType.length];
for (int i = argStart;i < parType.length + argStart;i++) {
args[i - argStart] = getArg(i - argStart, addresses[i]);
}
return args;
}
public long[] getArgsAddress(long[] oldAddress, Object... args) {
if (oldAddress == null || oldAddress.length == 0)
return new long[0];
long[] addresses;
int argStart = 0;
if (!isStatic()) {
argStart = 1;
addresses = new long[oldAddress.length + 1];
addresses[0] = oldAddress[0];
} else {
addresses = new long[oldAddress.length];
}
for (int i = 0;i < parType.length;i++) {
addresses[i + argStart] = ParamWrapper.objectToAddress(parType[i], args[i]);
}
return addresses;
}
public Object getThis(long address) {
if (isStatic())
return null;
return SandHook.getObject(address);
}
public Object getArg(int index, long address) {
return ParamWrapper.addressToObject(parType[index], address);
}
public Object getResult(long address) {
if (isVoid())
return null;
return ParamWrapper.addressToObject(retType, address);
}
public long getResultAddress(Object result) {
if (isVoid())
return 0;
return ParamWrapper.objectToAddress(retType, result);
}
public boolean isVoid() {
return retType == null || Void.TYPE.equals(retType);
}
public boolean isConstructor() {
return origin instanceof Constructor;
}
public boolean isStatic() {
return Modifier.isStatic(origin.getModifiers());
}
}

View File

@ -1,411 +0,0 @@
package com.swift.sandhook.xposedcompat.hookstub;
import android.util.Log;
import io.github.lsposed.lspd.BuildConfig;
import com.swift.sandhook.SandHook;
import com.swift.sandhook.SandHookMethodResolver;
import com.swift.sandhook.utils.ParamWrapper;
import com.swift.sandhook.wrapper.StubMethodsFactory;
import com.swift.sandhook.xposedcompat.XposedCompat;
import com.swift.sandhook.xposedcompat.utils.DexLog;
import java.lang.reflect.Constructor;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.concurrent.atomic.AtomicInteger;
import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.XposedBridge;
import de.robv.android.xposed.XposedHelpers;
public class HookStubManager {
public static volatile boolean is64Bit;
//64bits arg0 - arg7 is in reg x1 - x7 and > 7 is in stack, but can not match
public final static int MAX_64_ARGS = 7;
public static int MAX_STUB_ARGS = 0;
public static int[] stubSizes;
public static boolean hasStubBackup;
public static AtomicInteger[] curUseStubIndexes;
public static int ALL_STUB = 0;
public static Member[] originMethods;
public static HookMethodEntity[] hookMethodEntities;
public static XposedBridge.AdditionalHookInfo[] additionalHookInfos;
static {
is64Bit = SandHook.is64Bit();
Class stubClass = is64Bit ? MethodHookerStubs64.class : MethodHookerStubs32.class;
stubSizes = (int[]) XposedHelpers.getStaticObjectField(stubClass, "stubSizes");
Boolean hasBackup = (Boolean) XposedHelpers.getStaticObjectField(stubClass, "hasStubBackup");
hasStubBackup = hasBackup != null && (hasBackup && !XposedCompat.useNewCallBackup);
if (stubSizes != null && stubSizes.length > 0) {
MAX_STUB_ARGS = stubSizes.length - 1;
curUseStubIndexes = new AtomicInteger[MAX_STUB_ARGS + 1];
for (int i = 0; i < MAX_STUB_ARGS + 1; i++) {
curUseStubIndexes[i] = new AtomicInteger(0);
ALL_STUB += stubSizes[i];
}
originMethods = new Member[ALL_STUB];
hookMethodEntities = new HookMethodEntity[ALL_STUB];
additionalHookInfos = new XposedBridge.AdditionalHookInfo[ALL_STUB];
}
}
public static HookMethodEntity getHookMethodEntity(Member origin, XposedBridge.AdditionalHookInfo additionalHookInfo) {
if (!support()) {
return null;
}
Class[] parType;
Class retType;
boolean isStatic = Modifier.isStatic(origin.getModifiers());
if (origin instanceof Method) {
Method method = (Method) origin;
retType = method.getReturnType();
parType = method.getParameterTypes();
} else if (origin instanceof Constructor) {
Constructor constructor = (Constructor) origin;
retType = Void.TYPE;
parType = constructor.getParameterTypes();
} else {
return null;
}
if (!ParamWrapper.support(retType))
return null;
int needStubArgCount = isStatic ? 0 : 1;
if (parType != null) {
needStubArgCount += parType.length;
if (needStubArgCount > MAX_STUB_ARGS)
return null;
if (is64Bit && needStubArgCount > MAX_64_ARGS)
return null;
for (Class par:parType) {
if (!ParamWrapper.support(par))
return null;
}
} else {
parType = new Class[0];
}
synchronized (HookStubManager.class) {
StubMethodsInfo stubMethodInfo = getStubMethodPair(is64Bit, needStubArgCount);
if (stubMethodInfo == null)
return null;
HookMethodEntity entity = new HookMethodEntity(origin, stubMethodInfo.hook, stubMethodInfo.backup);
entity.retType = retType;
entity.parType = parType;
if (hasStubBackup && !tryCompileAndResolveCallOriginMethod(entity.backup, stubMethodInfo.args, stubMethodInfo.index)) {
DexLog.w("internal stub <" + entity.hook.getName() + "> call origin compile failure, skip use internal stub");
return null;
} else {
int id = getMethodId(stubMethodInfo.args, stubMethodInfo.index);
originMethods[id] = origin;
hookMethodEntities[id] = entity;
additionalHookInfos[id] = additionalHookInfo;
return entity;
}
}
}
public static int getMethodId(int args, int index) {
int id = index;
for (int i = 0;i < args;i++) {
id += stubSizes[i];
}
return id;
}
public static String getHookMethodName(int index) {
return "stub_hook_" + index;
}
public static String getBackupMethodName(int index) {
return "stub_backup_" + index;
}
public static String getCallOriginClassName(int args, int index) {
return "call_origin_" + args + "_" + index;
}
static class StubMethodsInfo {
int args = 0;
int index = 0;
Method hook;
Method backup;
public StubMethodsInfo(int args, int index, Method hook, Method backup) {
this.args = args;
this.index = index;
this.hook = hook;
this.backup = backup;
}
}
private static synchronized StubMethodsInfo getStubMethodPair(boolean is64Bit, int stubArgs) {
stubArgs = getMatchStubArgsCount(stubArgs);
if (stubArgs < 0)
return null;
int curUseStubIndex = curUseStubIndexes[stubArgs].getAndIncrement();
Class[] pars = getFindMethodParTypes(is64Bit, stubArgs);
try {
if (is64Bit) {
Method hook = MethodHookerStubs64.class.getDeclaredMethod(getHookMethodName(curUseStubIndex), pars);
Method backup = hasStubBackup ? MethodHookerStubs64.class.getDeclaredMethod(getBackupMethodName(curUseStubIndex), pars) : StubMethodsFactory.getStubMethod();
if (hook == null || backup == null)
return null;
return new StubMethodsInfo(stubArgs, curUseStubIndex, hook, backup);
} else {
Method hook = MethodHookerStubs32.class.getDeclaredMethod(getHookMethodName(curUseStubIndex), pars);
Method backup = hasStubBackup ? MethodHookerStubs32.class.getDeclaredMethod(getBackupMethodName(curUseStubIndex), pars) : StubMethodsFactory.getStubMethod();
if (hook == null || backup == null)
return null;
return new StubMethodsInfo(stubArgs, curUseStubIndex, hook, backup);
}
} catch (Throwable throwable) {
return null;
}
}
public static Method getCallOriginMethod(int args, int index) {
Class stubClass = is64Bit ? MethodHookerStubs64.class : MethodHookerStubs32.class;
String className = stubClass.getName();
className += "$";
className += getCallOriginClassName(args, index);
try {
Class callOriginClass = Class.forName(className, true, stubClass.getClassLoader());
return callOriginClass.getDeclaredMethod("call", long[].class);
} catch (Throwable e) {
Log.e("HookStubManager", "load call origin class error!", e);
return null;
}
}
public static boolean tryCompileAndResolveCallOriginMethod(Method backupMethod, int args, int index) {
Method method = getCallOriginMethod(args, index);
if (method != null) {
SandHookMethodResolver.resolveMethod(method, backupMethod);
return SandHook.compileMethod(method);
} else {
return false;
}
}
public static int getMatchStubArgsCount(int stubArgs) {
for (int i = stubArgs;i <= MAX_STUB_ARGS;i++) {
if (curUseStubIndexes[i].get() < stubSizes[i])
return i;
}
return -1;
}
public static Class[] getFindMethodParTypes(boolean is64Bit, int stubArgs) {
if (stubArgs == 0)
return null;
Class[] args = new Class[stubArgs];
if (is64Bit) {
for (int i = 0;i < stubArgs;i++) {
args[i] = long.class;
}
} else {
for (int i = 0;i < stubArgs;i++) {
args[i] = int.class;
}
}
return args;
}
public static long hookBridge(int id, CallOriginCallBack callOrigin, long... stubArgs) throws Throwable {
Member originMethod = originMethods[id];
HookMethodEntity entity = hookMethodEntities[id];
Object thiz = null;
Object[] args = null;
if (hasArgs(stubArgs)) {
thiz = entity.getThis(stubArgs[0]);
args = entity.getArgs(stubArgs);
}
if (thiz == null)
{
thiz = originMethod.getDeclaringClass();
}
DexLog.printMethodHookIn(originMethod);
Object[] snapshot = additionalHookInfos[id].callbacks.getSnapshot();
if (snapshot == null || snapshot.length == 0) {
if (hasStubBackup) {
return callOrigin.call(stubArgs);
} else {
return callOrigin(entity, originMethod, thiz, args);
}
}
XC_MethodHook.MethodHookParam param = new XC_MethodHook.MethodHookParam();
param.method = originMethod;
param.thisObject = thiz;
param.args = args;
int beforeIdx = 0;
do {
try {
((XC_MethodHook) snapshot[beforeIdx]).callBeforeHookedMethod(param);
} catch (Throwable t) {
// reset result (ignoring what the unexpectedly exiting callback did)
if( BuildConfig.DEBUG ) XposedBridge.log(t);
param.setResult(null);
param.returnEarly = false;
continue;
}
if (param.returnEarly) {
// skip remaining "before" callbacks and corresponding "after" callbacks
beforeIdx++;
break;
}
} while (++beforeIdx < snapshot.length);
// call original method if not requested otherwise
if (!param.returnEarly) {
try {
if (hasStubBackup) {
//prepare new args
long[] newArgs = entity.getArgsAddress(stubArgs, param.args);
param.setResult(entity.getResult(callOrigin.call(newArgs)));
} else {
param.setResult(SandHook.callOriginMethod(originMethod, entity.backup, thiz, param.args));
}
} catch (Throwable e) {
if( BuildConfig.DEBUG ) XposedBridge.log(e);
param.setThrowable(e);
}
}
// call "after method" callbacks
int afterIdx = beforeIdx - 1;
do {
Object lastResult = param.getResult();
Throwable lastThrowable = param.getThrowable();
try {
((XC_MethodHook) snapshot[afterIdx]).callAfterHookedMethod(param);
} catch (Throwable t) {
if( BuildConfig.DEBUG ) XposedBridge.log(t);
if (lastThrowable == null)
param.setResult(lastResult);
else
param.setThrowable(lastThrowable);
}
} while (--afterIdx >= 0);
if (!param.hasThrowable()) {
return entity.getResultAddress(param.getResult());
} else {
throw param.getThrowable();
}
}
public static Object hookBridge(Member origin, Method backup, XposedBridge.AdditionalHookInfo additionalHookInfo, Object thiz, Object... args) throws Throwable {
DexLog.printMethodHookIn(origin);
Object[] snapshot = additionalHookInfo.callbacks.getSnapshot();
if (snapshot == null || snapshot.length == 0) {
return SandHook.callOriginMethod(origin, backup, thiz, args);
}
XC_MethodHook.MethodHookParam param = new XC_MethodHook.MethodHookParam();
param.method = origin;
param.thisObject = thiz;
param.args = args;
int beforeIdx = 0;
do {
try {
((XC_MethodHook) snapshot[beforeIdx]).callBeforeHookedMethod(param);
} catch (Throwable t) {
if( BuildConfig.DEBUG ) XposedBridge.log(t);
// reset result (ignoring what the unexpectedly exiting callback did)
param.setResult(null);
param.returnEarly = false;
continue;
}
if (param.returnEarly) {
// skip remaining "before" callbacks and corresponding "after" callbacks
beforeIdx++;
break;
}
} while (++beforeIdx < snapshot.length);
// call original method if not requested otherwise
if (!param.returnEarly) {
try {
param.setResult(SandHook.callOriginMethod(true, origin, backup, thiz, param.args));
} catch (Throwable e) {
if( BuildConfig.DEBUG ) XposedBridge.log(e);
param.setThrowable(e);
}
}
// call "after method" callbacks
int afterIdx = beforeIdx - 1;
do {
Object lastResult = param.getResult();
Throwable lastThrowable = param.getThrowable();
try {
((XC_MethodHook) snapshot[afterIdx]).callAfterHookedMethod(param);
} catch (Throwable t) {
if( BuildConfig.DEBUG ) XposedBridge.log(t);
if (lastThrowable == null)
param.setResult(lastResult);
else
param.setThrowable(lastThrowable);
}
} while (--afterIdx >= 0);
if (!param.hasThrowable()) {
return param.getResult();
} else {
throw param.getThrowable();
}
}
public final static long callOrigin(HookMethodEntity entity, Member origin, Object thiz, Object[] args) throws Throwable {
Object res = SandHook.callOriginMethod(true, origin, entity.backup, thiz, args);
return entity.getResultAddress(res);
}
private static boolean hasArgs(long... args) {
return args != null && args.length > 0;
}
public static boolean support() {
return MAX_STUB_ARGS > 0 && SandHook.canGetObject() && SandHook.canGetObjectAddress();
}
}

View File

@ -1,22 +0,0 @@
package com.swift.sandhook.xposedcompat.methodgen;
import android.util.Log;
import com.swift.sandhook.SandHook;
import com.swift.sandhook.xposedcompat.XposedCompat;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
public class ErrorCatch {
public static Object callOriginError(Member originMethod, Method backupMethod, Object thiz, Object[] args) throws Throwable {
if (XposedCompat.retryWhenCallOriginError) {
Log.w("SandHook", "method <" + originMethod.toString() + "> use invoke to call origin!");
return SandHook.callOriginMethod(originMethod, backupMethod, thiz, args);
} else {
return null;
}
}
}

View File

@ -1,14 +0,0 @@
package com.swift.sandhook.xposedcompat.methodgen;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import de.robv.android.xposed.XposedBridge;
public interface HookMaker {
void start(Member member, XposedBridge.AdditionalHookInfo hookInfo,
ClassLoader appClassLoader) throws Exception;
Method getHookMethod();
Method getBackupMethod();
Method getCallBackupMethod();
}

View File

@ -1,703 +0,0 @@
package com.swift.sandhook.xposedcompat.methodgen;
import com.swift.sandhook.SandHook;
import com.swift.sandhook.SandHookMethodResolver;
import com.swift.sandhook.wrapper.HookWrapper;
import com.swift.sandhook.xposedcompat.XposedCompat;
import com.swift.sandhook.xposedcompat.utils.DexLog;
import java.io.File;
import java.lang.reflect.Constructor;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.nio.ByteBuffer;
import java.util.Map;
import dalvik.system.InMemoryDexClassLoader;
import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.XposedBridge;
import external.com.android.dx.BinaryOp;
import external.com.android.dx.Code;
import external.com.android.dx.Comparison;
import external.com.android.dx.DexMaker;
import external.com.android.dx.FieldId;
import external.com.android.dx.Label;
import external.com.android.dx.Local;
import external.com.android.dx.MethodId;
import external.com.android.dx.TypeId;
import static com.swift.sandhook.xposedcompat.utils.DexMakerUtils.autoBoxIfNecessary;
import static com.swift.sandhook.xposedcompat.utils.DexMakerUtils.autoUnboxIfNecessary;
import static com.swift.sandhook.xposedcompat.utils.DexMakerUtils.canCache;
import static com.swift.sandhook.xposedcompat.utils.DexMakerUtils.createResultLocals;
import static com.swift.sandhook.xposedcompat.utils.DexMakerUtils.getObjTypeIdIfPrimitive;
import static com.swift.sandhook.xposedcompat.utils.DexMakerUtils.getSha1Hex;
import static io.github.lsposed.lspd.config.LSPApplicationServiceClient.serviceClient;
public class HookerDexMaker implements HookMaker {
public static final String METHOD_NAME_BACKUP = "backup";
public static final String METHOD_NAME_HOOK = "hook";
public static final String METHOD_NAME_CALL_BACKUP = "callBackup";
public static final String METHOD_NAME_SETUP = "setup";
public static final String METHOD_NAME_LOG = "printMethodHookIn";
public static final TypeId<Object[]> objArrayTypeId = TypeId.get(Object[].class);
private static final String CLASS_DESC_PREFIX = "L";
private static final String CLASS_NAME_PREFIX = "SandHooker";
private static final String FIELD_NAME_HOOK_INFO = "additionalHookInfo";
private static final String FIELD_NAME_METHOD = "method";
private static final String FIELD_NAME_BACKUP_METHOD = "backupMethod";
private static final String PARAMS_FIELD_NAME_METHOD = "method";
private static final String PARAMS_FIELD_NAME_THIS_OBJECT = "thisObject";
private static final String PARAMS_FIELD_NAME_ARGS = "args";
private static final String CALLBACK_METHOD_NAME_BEFORE = "callBeforeHookedMethod";
private static final String CALLBACK_METHOD_NAME_AFTER = "callAfterHookedMethod";
private static final TypeId<Throwable> throwableTypeId = TypeId.get(Throwable.class);
private static final TypeId<Member> memberTypeId = TypeId.get(Member.class);
private static final TypeId<Method> methodTypeId = TypeId.get(Method.class);
private static final TypeId<XC_MethodHook> callbackTypeId = TypeId.get(XC_MethodHook.class);
private static final TypeId<XposedBridge.AdditionalHookInfo> hookInfoTypeId
= TypeId.get(XposedBridge.AdditionalHookInfo.class);
private static final TypeId<XposedBridge.CopyOnWriteSortedSet> callbacksTypeId
= TypeId.get(XposedBridge.CopyOnWriteSortedSet.class);
private static final TypeId<XC_MethodHook.MethodHookParam> paramTypeId
= TypeId.get(XC_MethodHook.MethodHookParam.class);
private static final MethodId<XC_MethodHook.MethodHookParam, Void> setResultMethodId =
paramTypeId.getMethod(TypeId.VOID, "setResult", TypeId.OBJECT);
private static final MethodId<XC_MethodHook.MethodHookParam, Void> setThrowableMethodId =
paramTypeId.getMethod(TypeId.VOID, "setThrowable", throwableTypeId);
private static final MethodId<XC_MethodHook.MethodHookParam, Object> getResultMethodId =
paramTypeId.getMethod(TypeId.OBJECT, "getResult");
private static final MethodId<XC_MethodHook.MethodHookParam, Throwable> getThrowableMethodId =
paramTypeId.getMethod(throwableTypeId, "getThrowable");
private static final MethodId<XC_MethodHook.MethodHookParam, Boolean> hasThrowableMethodId =
paramTypeId.getMethod(TypeId.BOOLEAN, "hasThrowable");
private static final MethodId<XC_MethodHook, Void> callAfterCallbackMethodId =
callbackTypeId.getMethod(TypeId.VOID, CALLBACK_METHOD_NAME_AFTER, paramTypeId);
private static final MethodId<XC_MethodHook, Void> callBeforeCallbackMethodId =
callbackTypeId.getMethod(TypeId.VOID, CALLBACK_METHOD_NAME_BEFORE, paramTypeId);
private static final FieldId<XC_MethodHook.MethodHookParam, Boolean> returnEarlyFieldId =
paramTypeId.getField(TypeId.BOOLEAN, "returnEarly");
private static final TypeId<XposedBridge> xposedBridgeTypeId = TypeId.get(XposedBridge.class);
private static final MethodId<XposedBridge, Void> logThrowableMethodId =
xposedBridgeTypeId.getMethod(TypeId.VOID, "log", throwableTypeId);
private FieldId<?, XposedBridge.AdditionalHookInfo> mHookInfoFieldId;
private FieldId<?, Member> mMethodFieldId;
private FieldId<?, Method> mBackupMethodFieldId;
private MethodId<?, ?> mBackupMethodId;
private MethodId<?, ?> mCallBackupMethodId;
private MethodId<?, ?> mHookMethodId;
private MethodId<?, ?> mPrintLogMethodId;
private MethodId<?, ?> mSandHookCallOriginMethodId;
private TypeId<?> mHookerTypeId;
private TypeId<?>[] mParameterTypeIds;
private Class<?>[] mActualParameterTypes;
private Class<?> mReturnType;
private TypeId<?> mReturnTypeId;
private boolean mIsStatic;
// TODO use this to generate methods
private boolean mHasThrowable;
private DexMaker mDexMaker;
private Member mMember;
private XposedBridge.AdditionalHookInfo mHookInfo;
private ClassLoader mAppClassLoader;
private Class<?> mHookClass;
private Method mHookMethod;
private Method mBackupMethod;
private Method mCallBackupMethod;
private static TypeId<?>[] getParameterTypeIds(Class<?>[] parameterTypes, boolean isStatic) {
int parameterSize = parameterTypes.length;
int targetParameterSize = isStatic ? parameterSize : parameterSize + 1;
TypeId<?>[] parameterTypeIds = new TypeId<?>[targetParameterSize];
int offset = 0;
if (!isStatic) {
parameterTypeIds[0] = TypeId.OBJECT;
offset = 1;
}
for (int i = 0; i < parameterTypes.length; i++) {
parameterTypeIds[i + offset] = TypeId.get(parameterTypes[i]);
}
return parameterTypeIds;
}
private static Class<?>[] getParameterTypes(Class<?>[] parameterTypes, boolean isStatic) {
if (isStatic) {
return parameterTypes;
}
int parameterSize = parameterTypes.length;
int targetParameterSize = parameterSize + 1;
Class<?>[] newParameterTypes = new Class<?>[targetParameterSize];
int offset = 1;
newParameterTypes[0] = Object.class;
System.arraycopy(parameterTypes, 0, newParameterTypes, offset, parameterTypes.length);
return newParameterTypes;
}
public void start(Member member, XposedBridge.AdditionalHookInfo hookInfo,
ClassLoader appClassLoader) throws Exception {
if (member instanceof Method) {
Method method = (Method) member;
mIsStatic = Modifier.isStatic(method.getModifiers());
mReturnType = method.getReturnType();
if (mReturnType.equals(Void.class) || mReturnType.equals(void.class)
|| mReturnType.isPrimitive()) {
mReturnTypeId = TypeId.get(mReturnType);
} else {
// all others fallback to plain Object for convenience
mReturnType = Object.class;
mReturnTypeId = TypeId.OBJECT;
}
mParameterTypeIds = getParameterTypeIds(method.getParameterTypes(), mIsStatic);
mActualParameterTypes = getParameterTypes(method.getParameterTypes(), mIsStatic);
mHasThrowable = method.getExceptionTypes().length > 0;
} else if (member instanceof Constructor) {
Constructor constructor = (Constructor) member;
mIsStatic = false;
mReturnType = void.class;
mReturnTypeId = TypeId.VOID;
mParameterTypeIds = getParameterTypeIds(constructor.getParameterTypes(), mIsStatic);
mActualParameterTypes = getParameterTypes(constructor.getParameterTypes(), mIsStatic);
mHasThrowable = constructor.getExceptionTypes().length > 0;
} else if (member.getDeclaringClass().isInterface()) {
throw new IllegalArgumentException("Cannot hook interfaces: " + member.toString());
} else if (Modifier.isAbstract(member.getModifiers())) {
throw new IllegalArgumentException("Cannot hook abstract methods: " + member.toString());
} else {
throw new IllegalArgumentException("Only methods and constructors can be hooked: " + member.toString());
}
mMember = member;
mHookInfo = hookInfo;
if (appClassLoader == null
|| appClassLoader.getClass().getName().equals("java.lang.BootClassLoader")) {
mAppClassLoader = this.getClass().getClassLoader();
} else {
mAppClassLoader = appClassLoader;
}
mDexMaker = new DexMaker();
// Generate a Hooker class.
String className = getClassName(mMember);
String dexName = className + ".jar";
HookWrapper.HookEntity hookEntity = null;
//try load cache first
try {
ClassLoader loader = mDexMaker.loadClassDirect(mAppClassLoader, new File(serviceClient.getCachePath("")), dexName);
if (loader != null) {
hookEntity = loadHookerClass(loader, className);
}
} catch (Throwable throwable) {}
//do generate
if (hookEntity == null) {
hookEntity = doMake(className, dexName);
}
SandHook.hook(hookEntity);
}
private HookWrapper.HookEntity doMake(String className, String dexName) throws Exception {
mHookerTypeId = TypeId.get(CLASS_DESC_PREFIX + className + ";");
mDexMaker.declare(mHookerTypeId, className + ".generated", Modifier.PUBLIC, TypeId.OBJECT);
generateFields();
generateSetupMethod();
if (XposedCompat.retryWhenCallOriginError) {
generateBackupAndCallOriginCheckMethod();
} else {
generateBackupMethod();
}
generateCallBackupMethod();
generateHookMethod();
ClassLoader loader;
if (canCache) {
loader = mDexMaker.generateAndLoad(mAppClassLoader, new File(serviceClient.getCachePath("")), dexName, true);
File dexFile = new File(serviceClient.getCachePath(dexName));
dexFile.setWritable(true, false);
dexFile.setReadable(true, false);
} else {
//can not write file
byte[] dexBytes = mDexMaker.generate();
loader = new InMemoryDexClassLoader(ByteBuffer.wrap(dexBytes), mAppClassLoader);
}
return loadHookerClass(loader, className);
}
private HookWrapper.HookEntity loadHookerClass(ClassLoader loader, String className) throws Exception {
mHookClass = loader.loadClass(className);
// Execute our newly-generated code in-process.
mHookMethod = mHookClass.getMethod(METHOD_NAME_HOOK, mActualParameterTypes);
mBackupMethod = mHookClass.getMethod(METHOD_NAME_BACKUP, mActualParameterTypes);
mCallBackupMethod = mHookClass.getMethod(METHOD_NAME_CALL_BACKUP, mActualParameterTypes);
SandHook.resolveStaticMethod(mCallBackupMethod);
SandHookMethodResolver.resolveMethod(mCallBackupMethod, mBackupMethod);
SandHook.compileMethod(mCallBackupMethod);
mHookClass.getMethod(METHOD_NAME_SETUP, Member.class, Method.class, XposedBridge.AdditionalHookInfo.class).invoke(null, mMember, mBackupMethod, mHookInfo);
return new HookWrapper.HookEntity(mMember, mHookMethod, mBackupMethod);
}
private String getClassName(Member originMethod) {
return CLASS_NAME_PREFIX + "_" + getSha1Hex(originMethod.toString());
}
public Method getHookMethod() {
return mHookMethod;
}
public Method getBackupMethod() {
return mBackupMethod;
}
public Method getCallBackupMethod() {
return mCallBackupMethod;
}
public Class getHookClass() {
return mHookClass;
}
private void generateFields() {
mHookInfoFieldId = mHookerTypeId.getField(hookInfoTypeId, FIELD_NAME_HOOK_INFO);
mMethodFieldId = mHookerTypeId.getField(memberTypeId, FIELD_NAME_METHOD);
mBackupMethodFieldId = mHookerTypeId.getField(methodTypeId, FIELD_NAME_BACKUP_METHOD);
mDexMaker.declare(mHookInfoFieldId, Modifier.STATIC, null);
mDexMaker.declare(mMethodFieldId, Modifier.STATIC, null);
mDexMaker.declare(mBackupMethodFieldId, Modifier.STATIC, null);
}
private void generateSetupMethod() {
MethodId<?, Void> setupMethodId = mHookerTypeId.getMethod(
TypeId.VOID, METHOD_NAME_SETUP, memberTypeId, methodTypeId, hookInfoTypeId);
Code code = mDexMaker.declare(setupMethodId, Modifier.PUBLIC | Modifier.STATIC);
// init logic
// get parameters
Local<Member> method = code.getParameter(0, memberTypeId);
Local<Method> backupMethod = code.getParameter(1, methodTypeId);
Local<XposedBridge.AdditionalHookInfo> hookInfo = code.getParameter(2, hookInfoTypeId);
// save params to static
code.sput(mMethodFieldId, method);
code.sput(mBackupMethodFieldId, backupMethod);
code.sput(mHookInfoFieldId, hookInfo);
code.returnVoid();
}
private void generateBackupMethod() {
mBackupMethodId = mHookerTypeId.getMethod(mReturnTypeId, METHOD_NAME_BACKUP, mParameterTypeIds);
Code code = mDexMaker.declare(mBackupMethodId, Modifier.PUBLIC | Modifier.STATIC);
Local<Member> method = code.newLocal(memberTypeId);
Map<TypeId, Local> resultLocals = createResultLocals(code);
MethodId<?, ?> errLogMethod = TypeId.get(DexLog.class).getMethod(TypeId.get(Void.TYPE), "printCallOriginError", memberTypeId);
//very very important!!!!!!!!!!!
//add a try cache block avoid inline
Label tryCatchBlock = new Label();
code.addCatchClause(throwableTypeId, tryCatchBlock);
code.sget(mMethodFieldId, method);
code.invokeStatic(errLogMethod, null, method);
// start of try
code.mark(tryCatchBlock);
// do nothing
if (mReturnTypeId.equals(TypeId.VOID)) {
code.returnVoid();
} else {
// we have limited the returnType to primitives or Object, so this should be safe
code.returnValue(resultLocals.get(mReturnTypeId));
}
}
private void generateBackupAndCallOriginCheckMethod() {
mBackupMethodId = mHookerTypeId.getMethod(mReturnTypeId, METHOD_NAME_BACKUP, mParameterTypeIds);
mSandHookCallOriginMethodId = TypeId.get(ErrorCatch.class).getMethod(TypeId.get(Object.class), "callOriginError", memberTypeId, methodTypeId, TypeId.get(Object.class), TypeId.get(Object[].class));
MethodId<?, ?> errLogMethod = TypeId.get(DexLog.class).getMethod(TypeId.get(Void.TYPE), "printCallOriginError", methodTypeId);
Code code = mDexMaker.declare(mBackupMethodId, Modifier.PUBLIC | Modifier.STATIC);
Local<Member> method = code.newLocal(memberTypeId);
Local<Method> backupMethod = code.newLocal(methodTypeId);
Local<Object> thisObject = code.newLocal(TypeId.OBJECT);
Local<Object[]> args = code.newLocal(objArrayTypeId);
Local<Integer> actualParamSize = code.newLocal(TypeId.INT);
Local<Integer> argIndex = code.newLocal(TypeId.INT);
Local<Object> resultObj = code.newLocal(TypeId.OBJECT);
Label tryCatchBlock = new Label();
Local[] allArgsLocals = createParameterLocals(code);
Map<TypeId, Local> resultLocals = createResultLocals(code);
//very very important!!!!!!!!!!!
//add a try cache block avoid inline
// start of try
code.addCatchClause(throwableTypeId, tryCatchBlock);
code.sget(mMethodFieldId, method);
code.invokeStatic(errLogMethod, null, method);
//call origin by invoke
code.loadConstant(args, null);
code.loadConstant(argIndex, 0);
code.sget(mBackupMethodFieldId, backupMethod);
int paramsSize = mParameterTypeIds.length;
int offset = 0;
// thisObject
if (mIsStatic) {
// thisObject = null
code.loadConstant(thisObject, null);
} else {
// thisObject = args[0]
offset = 1;
code.move(thisObject, allArgsLocals[0]);
}
// offset = mIsStatic ? 0 : 1;
// for (int i = offset; i < allArgsLocals.length; i++) {
// code.loadConstant(argIndex, i - offset);
// code.aget(resultObj, args, argIndex);
// autoUnboxIfNecessary(code, allArgsLocals[i], resultObj, resultLocals, true);
// }
// actual args (exclude thisObject if this is not a static method)
code.loadConstant(actualParamSize, paramsSize - offset);
code.newArray(args, actualParamSize);
for (int i = offset; i < paramsSize; i++) {
Local parameter = allArgsLocals[i];
// save parameter to resultObj as Object
autoBoxIfNecessary(code, resultObj, parameter);
code.loadConstant(argIndex, i - offset);
// save Object to args
code.aput(args, argIndex, resultObj);
}
if (mReturnTypeId.equals(TypeId.VOID)) {
code.invokeStatic(mSandHookCallOriginMethodId, null, method, backupMethod, thisObject, args);
code.returnVoid();
} else {
code.invokeStatic(mSandHookCallOriginMethodId, resultObj, method, backupMethod, thisObject, args);
TypeId objTypeId = getObjTypeIdIfPrimitive(mReturnTypeId);
Local matchObjLocal = resultLocals.get(objTypeId);
code.cast(matchObjLocal, resultObj);
// have to use matching typed Object(Integer, Double ...) to do unboxing
Local toReturn = resultLocals.get(mReturnTypeId);
autoUnboxIfNecessary(code, toReturn, matchObjLocal, resultLocals, true);
code.returnValue(toReturn);
}
code.mark(tryCatchBlock);
// do nothing
if (mReturnTypeId.equals(TypeId.VOID)) {
code.returnVoid();
} else {
// we have limited the returnType to primitives or Object, so this should be safe
code.returnValue(resultLocals.get(mReturnTypeId));
}
}
private void generateCallBackupMethod() {
mCallBackupMethodId = mHookerTypeId.getMethod(mReturnTypeId, METHOD_NAME_CALL_BACKUP, mParameterTypeIds);
Code code = mDexMaker.declare(mCallBackupMethodId, Modifier.PUBLIC | Modifier.STATIC);
// just call backup and return its result
Local localOrigin = code.newLocal(memberTypeId);
Local localBackup = code.newLocal(methodTypeId);
Local[] allArgsLocals = createParameterLocals(code);
Map<TypeId, Local> resultLocals = createResultLocals(code);
code.sget(mMethodFieldId, localOrigin);
code.sget(mBackupMethodFieldId, localBackup);
MethodId methodId = TypeId.get(SandHook.class).getMethod(TypeId.get(Void.TYPE), "ensureBackupMethod", memberTypeId, methodTypeId);
code.invokeStatic(methodId, null, localOrigin, localBackup);
if (mReturnTypeId.equals(TypeId.VOID)) {
code.invokeStatic(mBackupMethodId, null, allArgsLocals);
code.returnVoid();
} else {
Local result = resultLocals.get(mReturnTypeId);
code.invokeStatic(mBackupMethodId, result, allArgsLocals);
code.returnValue(result);
}
}
private void generateHookMethod() {
mHookMethodId = mHookerTypeId.getMethod(mReturnTypeId, METHOD_NAME_HOOK, mParameterTypeIds);
mPrintLogMethodId = TypeId.get(DexLog.class).getMethod(TypeId.get(Void.TYPE), METHOD_NAME_LOG, TypeId.get(Member.class));
Code code = mDexMaker.declare(mHookMethodId, Modifier.PUBLIC | Modifier.STATIC);
// code starts
// prepare common labels
Label noHookReturn = new Label();
Label incrementAndCheckBefore = new Label();
Label tryBeforeCatch = new Label();
Label noExceptionBefore = new Label();
Label checkAndCallBackup = new Label();
Label beginCallBefore = new Label();
Label beginCallAfter = new Label();
Label tryOrigCatch = new Label();
Label noExceptionOrig = new Label();
Label tryAfterCatch = new Label();
Label decrementAndCheckAfter = new Label();
Label noBackupThrowable = new Label();
Label throwThrowable = new Label();
// prepare locals
Local<Boolean> disableHooks = code.newLocal(TypeId.BOOLEAN);
Local<XposedBridge.AdditionalHookInfo> hookInfo = code.newLocal(hookInfoTypeId);
Local<XposedBridge.CopyOnWriteSortedSet> callbacks = code.newLocal(callbacksTypeId);
Local<Object[]> snapshot = code.newLocal(objArrayTypeId);
Local<Integer> snapshotLen = code.newLocal(TypeId.INT);
Local<Object> callbackObj = code.newLocal(TypeId.OBJECT);
Local<XC_MethodHook> callback = code.newLocal(callbackTypeId);
Local<Object> resultObj = code.newLocal(TypeId.OBJECT); // as a temp Local
Local<Integer> one = code.newLocal(TypeId.INT);
Local<Object> nullObj = code.newLocal(TypeId.OBJECT);
Local<Throwable> throwable = code.newLocal(throwableTypeId);
Local<XC_MethodHook.MethodHookParam> param = code.newLocal(paramTypeId);
Local<Member> method = code.newLocal(memberTypeId);
Local<Object> thisObject = code.newLocal(TypeId.OBJECT);
Local<Object[]> args = code.newLocal(objArrayTypeId);
Local<Boolean> returnEarly = code.newLocal(TypeId.BOOLEAN);
Local<Integer> actualParamSize = code.newLocal(TypeId.INT);
Local<Integer> argIndex = code.newLocal(TypeId.INT);
Local<Integer> beforeIdx = code.newLocal(TypeId.INT);
Local<Object> lastResult = code.newLocal(TypeId.OBJECT);
Local<Throwable> lastThrowable = code.newLocal(throwableTypeId);
Local<Boolean> hasThrowable = code.newLocal(TypeId.BOOLEAN);
Local[] allArgsLocals = createParameterLocals(code);
Map<TypeId, Local> resultLocals = createResultLocals(code);
code.loadConstant(args, null);
code.loadConstant(argIndex, 0);
code.loadConstant(one, 1);
code.loadConstant(snapshotLen, 0);
code.loadConstant(nullObj, null);
code.sget(mMethodFieldId, method);
//print log
code.invokeStatic(mPrintLogMethodId, null, method);
// check XposedBridge.disableHooks flag
FieldId<XposedBridge, Boolean> disableHooksField =
xposedBridgeTypeId.getField(TypeId.BOOLEAN, "disableHooks");
code.sget(disableHooksField, disableHooks);
// disableHooks == true => no hooking
code.compareZ(Comparison.NE, noHookReturn, disableHooks);
// check callbacks length
code.sget(mHookInfoFieldId, hookInfo);
code.iget(hookInfoTypeId.getField(callbacksTypeId, "callbacks"), callbacks, hookInfo);
code.invokeVirtual(callbacksTypeId.getMethod(objArrayTypeId, "getSnapshot"), snapshot, callbacks);
code.arrayLength(snapshotLen, snapshot);
// snapshotLen == 0 => no hooking
code.compareZ(Comparison.EQ, noHookReturn, snapshotLen);
// start hooking
// prepare hooking locals
int paramsSize = mParameterTypeIds.length;
int offset = 0;
// thisObject
if (mIsStatic) {
// thisObject = null
code.loadConstant(thisObject, null);
} else {
// thisObject = args[0]
offset = 1;
code.move(thisObject, allArgsLocals[0]);
}
// actual args (exclude thisObject if this is not a static method)
code.loadConstant(actualParamSize, paramsSize - offset);
code.newArray(args, actualParamSize);
for (int i = offset; i < paramsSize; i++) {
Local parameter = allArgsLocals[i];
// save parameter to resultObj as Object
autoBoxIfNecessary(code, resultObj, parameter);
code.loadConstant(argIndex, i - offset);
// save Object to args
code.aput(args, argIndex, resultObj);
}
// create param
code.newInstance(param, paramTypeId.getConstructor());
// set method, thisObject, args
code.iput(paramTypeId.getField(memberTypeId, PARAMS_FIELD_NAME_METHOD), param, method);
code.iput(paramTypeId.getField(TypeId.OBJECT, PARAMS_FIELD_NAME_THIS_OBJECT), param, thisObject);
code.iput(paramTypeId.getField(objArrayTypeId, PARAMS_FIELD_NAME_ARGS), param, args);
// call beforeCallbacks
code.loadConstant(beforeIdx, 0);
code.mark(beginCallBefore);
// start of try
code.addCatchClause(throwableTypeId, tryBeforeCatch);
code.aget(callbackObj, snapshot, beforeIdx);
code.cast(callback, callbackObj);
code.invokeVirtual(callBeforeCallbackMethodId, null, callback, param);
code.jump(noExceptionBefore);
// end of try
code.removeCatchClause(throwableTypeId);
// start of catch
code.mark(tryBeforeCatch);
code.moveException(throwable);
code.invokeStatic(logThrowableMethodId, null, throwable);
code.invokeVirtual(setResultMethodId, null, param, nullObj);
code.loadConstant(returnEarly, false);
code.iput(returnEarlyFieldId, param, returnEarly);
code.jump(incrementAndCheckBefore);
// no exception when calling beforeCallbacks
code.mark(noExceptionBefore);
code.iget(returnEarlyFieldId, returnEarly, param);
// if returnEarly == false, continue
code.compareZ(Comparison.EQ, incrementAndCheckBefore, returnEarly);
// returnEarly == true, break
code.op(BinaryOp.ADD, beforeIdx, beforeIdx, one);
code.jump(checkAndCallBackup);
// increment and check to continue
code.mark(incrementAndCheckBefore);
code.op(BinaryOp.ADD, beforeIdx, beforeIdx, one);
code.compare(Comparison.LT, beginCallBefore, beforeIdx, snapshotLen);
// check and call backup
code.mark(checkAndCallBackup);
code.iget(returnEarlyFieldId, returnEarly, param);
// if returnEarly == true, go to call afterCallbacks directly
code.compareZ(Comparison.NE, noExceptionOrig, returnEarly);
// try to call backup
// try start
code.addCatchClause(throwableTypeId, tryOrigCatch);
// we have to load args[] to paramLocals
// because args[] may be changed in beforeHookedMethod
// should consider first param is thisObj if hooked method is not static
offset = mIsStatic ? 0 : 1;
for (int i = offset; i < allArgsLocals.length; i++) {
code.loadConstant(argIndex, i - offset);
code.aget(resultObj, args, argIndex);
autoUnboxIfNecessary(code, allArgsLocals[i], resultObj, resultLocals, true);
}
// get pre-created Local with a matching typeId
if (mReturnTypeId.equals(TypeId.VOID)) {
code.invokeStatic(mBackupMethodId, null, allArgsLocals);
// TODO maybe keep preset result to do some magic?
code.invokeVirtual(setResultMethodId, null, param, nullObj);
} else {
Local returnedResult = resultLocals.get(mReturnTypeId);
code.invokeStatic(mBackupMethodId, returnedResult, allArgsLocals);
// save returnedResult to resultObj as a Object
autoBoxIfNecessary(code, resultObj, returnedResult);
// save resultObj to param
code.invokeVirtual(setResultMethodId, null, param, resultObj);
}
// go to call afterCallbacks
code.jump(noExceptionOrig);
// try end
code.removeCatchClause(throwableTypeId);
// catch
code.mark(tryOrigCatch);
code.moveException(throwable);
// exception occurred when calling backup, save throwable to param
code.invokeVirtual(setThrowableMethodId, null, param, throwable);
code.mark(noExceptionOrig);
code.op(BinaryOp.SUBTRACT, beforeIdx, beforeIdx, one);
// call afterCallbacks
code.mark(beginCallAfter);
// save results of backup calling
code.invokeVirtual(getResultMethodId, lastResult, param);
code.invokeVirtual(getThrowableMethodId, lastThrowable, param);
// try start
code.addCatchClause(throwableTypeId, tryAfterCatch);
code.aget(callbackObj, snapshot, beforeIdx);
code.cast(callback, callbackObj);
code.invokeVirtual(callAfterCallbackMethodId, null, callback, param);
// all good, just continue
code.jump(decrementAndCheckAfter);
// try end
code.removeCatchClause(throwableTypeId);
// catch
code.mark(tryAfterCatch);
code.moveException(throwable);
code.invokeStatic(logThrowableMethodId, null, throwable);
// if lastThrowable == null, go to recover lastResult
code.compareZ(Comparison.EQ, noBackupThrowable, lastThrowable);
// lastThrowable != null, recover lastThrowable
code.invokeVirtual(setThrowableMethodId, null, param, lastThrowable);
// continue
code.jump(decrementAndCheckAfter);
code.mark(noBackupThrowable);
// recover lastResult and continue
code.invokeVirtual(setResultMethodId, null, param, lastResult);
// decrement and check continue
code.mark(decrementAndCheckAfter);
code.op(BinaryOp.SUBTRACT, beforeIdx, beforeIdx, one);
code.compareZ(Comparison.GE, beginCallAfter, beforeIdx);
// callbacks end
// return
code.invokeVirtual(hasThrowableMethodId, hasThrowable, param);
// if hasThrowable, throw the throwable and return
code.compareZ(Comparison.NE, throwThrowable, hasThrowable);
// return getResult
if (mReturnTypeId.equals(TypeId.VOID)) {
code.returnVoid();
} else {
// getResult always return an Object, so save to resultObj
code.invokeVirtual(getResultMethodId, resultObj, param);
// have to unbox it if returnType is primitive
// casting Object
TypeId objTypeId = getObjTypeIdIfPrimitive(mReturnTypeId);
Local matchObjLocal = resultLocals.get(objTypeId);
code.cast(matchObjLocal, resultObj);
// have to use matching typed Object(Integer, Double ...) to do unboxing
Local toReturn = resultLocals.get(mReturnTypeId);
autoUnboxIfNecessary(code, toReturn, matchObjLocal, resultLocals, true);
// return
code.returnValue(toReturn);
}
// throw throwable
code.mark(throwThrowable);
code.invokeVirtual(getThrowableMethodId, throwable, param);
code.throwValue(throwable);
// call backup and return
code.mark(noHookReturn);
if (mReturnTypeId.equals(TypeId.VOID)) {
code.invokeStatic(mBackupMethodId, null, allArgsLocals);
code.returnVoid();
} else {
Local result = resultLocals.get(mReturnTypeId);
code.invokeStatic(mBackupMethodId, result, allArgsLocals);
code.returnValue(result);
}
}
private Local[] createParameterLocals(Code code) {
Local[] paramLocals = new Local[mParameterTypeIds.length];
for (int i = 0; i < mParameterTypeIds.length; i++) {
paramLocals[i] = code.getParameter(i, mParameterTypeIds[i]);
}
return paramLocals;
}
}

View File

@ -1,313 +0,0 @@
package com.swift.sandhook.xposedcompat.methodgen;
import com.swift.sandhook.SandHook;
import com.swift.sandhook.wrapper.HookWrapper;
import com.swift.sandhook.xposedcompat.hookstub.HookStubManager;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.nio.ByteBuffer;
import java.util.Map;
import dalvik.system.InMemoryDexClassLoader;
import de.robv.android.xposed.XposedBridge;
import de.robv.android.xposed.XposedHelpers;
import external.com.android.dx.Code;
import external.com.android.dx.DexMaker;
import external.com.android.dx.FieldId;
import external.com.android.dx.Local;
import external.com.android.dx.MethodId;
import external.com.android.dx.TypeId;
import static com.swift.sandhook.xposedcompat.utils.DexMakerUtils.autoBoxIfNecessary;
import static com.swift.sandhook.xposedcompat.utils.DexMakerUtils.autoUnboxIfNecessary;
import static com.swift.sandhook.xposedcompat.utils.DexMakerUtils.canCache;
import static com.swift.sandhook.xposedcompat.utils.DexMakerUtils.createResultLocals;
import static com.swift.sandhook.xposedcompat.utils.DexMakerUtils.getObjTypeIdIfPrimitive;
import static com.swift.sandhook.xposedcompat.utils.DexMakerUtils.getSha1Hex;
import static io.github.lsposed.lspd.config.LSPApplicationServiceClient.serviceClient;
public class HookerDexMakerNew implements HookMaker {
public static final String METHOD_NAME_BACKUP = "backup";
public static final String METHOD_NAME_HOOK = "hook";
public static final TypeId<Object[]> objArrayTypeId = TypeId.get(Object[].class);
private static final String CLASS_DESC_PREFIX = "L";
private static final String CLASS_NAME_PREFIX = "SandHookerNew";
private static final String FIELD_NAME_HOOK_INFO = "additionalHookInfo";
private static final String FIELD_NAME_METHOD = "method";
private static final String FIELD_NAME_BACKUP_METHOD = "backupMethod";
private static final TypeId<Member> memberTypeId = TypeId.get(Member.class);
private static final TypeId<Method> methodTypeId = TypeId.get(Method.class);
private static final TypeId<XposedBridge.AdditionalHookInfo> hookInfoTypeId
= TypeId.get(XposedBridge.AdditionalHookInfo.class);
private FieldId<?, XposedBridge.AdditionalHookInfo> mHookInfoFieldId;
private FieldId<?, Member> mMethodFieldId;
private FieldId<?, Method> mBackupMethodFieldId;
private MethodId<?, ?> mHookMethodId;
private MethodId<?, ?> mBackupMethodId;
private MethodId<?, ?> mSandHookBridgeMethodId;
private TypeId<?> mHookerTypeId;
private TypeId<?>[] mParameterTypeIds;
private Class<?>[] mActualParameterTypes;
private Class<?> mReturnType;
private TypeId<?> mReturnTypeId;
private boolean mIsStatic;
// TODO use this to generate methods
private boolean mHasThrowable;
private DexMaker mDexMaker;
private Member mMember;
private XposedBridge.AdditionalHookInfo mHookInfo;
private ClassLoader mAppClassLoader;
private Class<?> mHookClass;
private Method mHookMethod;
private Method mBackupMethod;
private static TypeId<?>[] getParameterTypeIds(Class<?>[] parameterTypes, boolean isStatic) {
int parameterSize = parameterTypes.length;
int targetParameterSize = isStatic ? parameterSize : parameterSize + 1;
TypeId<?>[] parameterTypeIds = new TypeId<?>[targetParameterSize];
int offset = 0;
if (!isStatic) {
parameterTypeIds[0] = TypeId.OBJECT;
offset = 1;
}
for (int i = 0; i < parameterTypes.length; i++) {
parameterTypeIds[i + offset] = TypeId.get(parameterTypes[i]);
}
return parameterTypeIds;
}
private static Class<?>[] getParameterTypes(Class<?>[] parameterTypes, boolean isStatic) {
if (isStatic) {
return parameterTypes;
}
int parameterSize = parameterTypes.length;
int targetParameterSize = parameterSize + 1;
Class<?>[] newParameterTypes = new Class<?>[targetParameterSize];
int offset = 1;
newParameterTypes[0] = Object.class;
System.arraycopy(parameterTypes, 0, newParameterTypes, offset, parameterTypes.length);
return newParameterTypes;
}
public void start(Member member, XposedBridge.AdditionalHookInfo hookInfo,
ClassLoader appClassLoader) throws Exception {
if (member instanceof Method) {
Method method = (Method) member;
mIsStatic = Modifier.isStatic(method.getModifiers());
mReturnType = method.getReturnType();
if (mReturnType.equals(Void.class) || mReturnType.equals(void.class)
|| mReturnType.isPrimitive()) {
mReturnTypeId = TypeId.get(mReturnType);
} else {
// all others fallback to plain Object for convenience
mReturnType = Object.class;
mReturnTypeId = TypeId.OBJECT;
}
mParameterTypeIds = getParameterTypeIds(method.getParameterTypes(), mIsStatic);
mActualParameterTypes = getParameterTypes(method.getParameterTypes(), mIsStatic);
mHasThrowable = method.getExceptionTypes().length > 0;
} else if (member instanceof Constructor) {
Constructor constructor = (Constructor) member;
mIsStatic = false;
mReturnType = void.class;
mReturnTypeId = TypeId.VOID;
mParameterTypeIds = getParameterTypeIds(constructor.getParameterTypes(), mIsStatic);
mActualParameterTypes = getParameterTypes(constructor.getParameterTypes(), mIsStatic);
mHasThrowable = constructor.getExceptionTypes().length > 0;
} else if (member.getDeclaringClass().isInterface()) {
throw new IllegalArgumentException("Cannot hook interfaces: " + member.toString());
} else if (Modifier.isAbstract(member.getModifiers())) {
throw new IllegalArgumentException("Cannot hook abstract methods: " + member.toString());
} else {
throw new IllegalArgumentException("Only methods and constructors can be hooked: " + member.toString());
}
mMember = member;
mHookInfo = hookInfo;
if (appClassLoader == null
|| appClassLoader.getClass().getName().equals("java.lang.BootClassLoader")) {
mAppClassLoader = this.getClass().getClassLoader();
} else {
mAppClassLoader = appClassLoader;
}
mDexMaker = new DexMaker();
// Generate a Hooker class.
String className = getClassName(mMember);
String dexName = className + ".jar";
HookWrapper.HookEntity hookEntity = null;
//try load cache first
try {
ClassLoader loader = mDexMaker.loadClassDirect(mAppClassLoader, new File(serviceClient.getCachePath("")), dexName);
if (loader != null) {
hookEntity = loadHookerClass(loader, className);
}
} catch (Throwable throwable) {}
//do generate
if (hookEntity == null) {
hookEntity = doMake(className, dexName);
}
SandHook.hook(hookEntity);
}
private HookWrapper.HookEntity doMake(String className, String dexName) throws Exception {
mHookerTypeId = TypeId.get(CLASS_DESC_PREFIX + className + ";");
mDexMaker.declare(mHookerTypeId, className + ".generated", Modifier.PUBLIC, TypeId.OBJECT);
generateFields();
generateHookMethod();
generateBackupMethod();
ClassLoader loader;
if (!canCache) {
byte[] dexBytes = mDexMaker.generate();
loader = new InMemoryDexClassLoader(ByteBuffer.wrap(dexBytes), mAppClassLoader);
return loadHookerClass(loader, className);
}
// Create the dex file and load it.
try {
loader = mDexMaker.generateAndLoad(mAppClassLoader, new File(serviceClient.getCachePath("")), dexName, true);
File dexFile = new File(serviceClient.getCachePath(dexName));
dexFile.setWritable(true, false);
dexFile.setReadable(true, false);
} catch (IOException e) {
//can not write file
byte[] dexBytes = mDexMaker.generate();
loader = new InMemoryDexClassLoader(ByteBuffer.wrap(dexBytes), mAppClassLoader);
}
return loadHookerClass(loader, className);
}
private HookWrapper.HookEntity loadHookerClass(ClassLoader loader, String className) throws Exception {
mHookClass = loader.loadClass(className);
// Execute our newly-generated code in-process.
mHookMethod = mHookClass.getMethod(METHOD_NAME_HOOK, mActualParameterTypes);
mBackupMethod = mHookClass.getMethod(METHOD_NAME_BACKUP);
setup(mHookClass);
return new HookWrapper.HookEntity(mMember, mHookMethod, mBackupMethod, false);
}
private void setup(Class mHookClass) {
XposedHelpers.setStaticObjectField(mHookClass, FIELD_NAME_METHOD, mMember);
XposedHelpers.setStaticObjectField(mHookClass, FIELD_NAME_BACKUP_METHOD, mBackupMethod);
XposedHelpers.setStaticObjectField(mHookClass, FIELD_NAME_HOOK_INFO, mHookInfo);
}
private String getClassName(Member originMethod) {
return CLASS_NAME_PREFIX + "_" + getSha1Hex(originMethod.toString());
}
public Method getHookMethod() {
return mHookMethod;
}
public Method getBackupMethod() {
return mBackupMethod;
}
public Method getCallBackupMethod() {
return mBackupMethod;
}
public Class getHookClass() {
return mHookClass;
}
private void generateFields() {
mHookInfoFieldId = mHookerTypeId.getField(hookInfoTypeId, FIELD_NAME_HOOK_INFO);
mMethodFieldId = mHookerTypeId.getField(memberTypeId, FIELD_NAME_METHOD);
mBackupMethodFieldId = mHookerTypeId.getField(methodTypeId, FIELD_NAME_BACKUP_METHOD);
mDexMaker.declare(mHookInfoFieldId, Modifier.STATIC, null);
mDexMaker.declare(mMethodFieldId, Modifier.STATIC, null);
mDexMaker.declare(mBackupMethodFieldId, Modifier.STATIC, null);
}
private void generateBackupMethod() {
mBackupMethodId = mHookerTypeId.getMethod(TypeId.VOID, METHOD_NAME_BACKUP);
Code code = mDexMaker.declare(mBackupMethodId, Modifier.PUBLIC | Modifier.STATIC);
code.returnVoid();
}
private void generateHookMethod() {
mHookMethodId = mHookerTypeId.getMethod(mReturnTypeId, METHOD_NAME_HOOK, mParameterTypeIds);
mSandHookBridgeMethodId = TypeId.get(HookStubManager.class).getMethod(TypeId.get(Object.class), "hookBridge", memberTypeId, methodTypeId, hookInfoTypeId, TypeId.get(Object.class), TypeId.get(Object[].class));
Code code = mDexMaker.declare(mHookMethodId, Modifier.PUBLIC | Modifier.STATIC);
Local<Member> originMethod = code.newLocal(memberTypeId);
Local<Method> backupMethod = code.newLocal(methodTypeId);
Local<XposedBridge.AdditionalHookInfo> hookInfo = code.newLocal(hookInfoTypeId);
Local<Object> thisObject = code.newLocal(TypeId.OBJECT);
Local<Object[]> args = code.newLocal(objArrayTypeId);
Local<Integer> actualParamSize = code.newLocal(TypeId.INT);
Local<Integer> argIndex = code.newLocal(TypeId.INT);
Local<Object> resultObj = code.newLocal(TypeId.OBJECT);
Local[] allArgsLocals = createParameterLocals(code);
Map<TypeId, Local> resultLocals = createResultLocals(code);
code.loadConstant(args, null);
code.loadConstant(argIndex, 0);
code.sget(mMethodFieldId, originMethod);
code.sget(mBackupMethodFieldId, backupMethod);
code.sget(mHookInfoFieldId, hookInfo);
int paramsSize = mParameterTypeIds.length;
int offset = 0;
// thisObject
if (mIsStatic) {
// thisObject = null
code.loadConstant(thisObject, null);
} else {
// thisObject = args[0]
offset = 1;
code.move(thisObject, allArgsLocals[0]);
}
// actual args (exclude thisObject if this is not a static method)
code.loadConstant(actualParamSize, paramsSize - offset);
code.newArray(args, actualParamSize);
for (int i = offset; i < paramsSize; i++) {
Local parameter = allArgsLocals[i];
// save parameter to resultObj as Object
autoBoxIfNecessary(code, resultObj, parameter);
code.loadConstant(argIndex, i - offset);
// save Object to args
code.aput(args, argIndex, resultObj);
}
if (mReturnTypeId.equals(TypeId.VOID)) {
code.invokeStatic(mSandHookBridgeMethodId, null, originMethod, backupMethod, hookInfo, thisObject, args);
code.returnVoid();
} else {
code.invokeStatic(mSandHookBridgeMethodId, resultObj, originMethod, backupMethod, hookInfo, thisObject, args);
TypeId objTypeId = getObjTypeIdIfPrimitive(mReturnTypeId);
Local matchObjLocal = resultLocals.get(objTypeId);
code.cast(matchObjLocal, resultObj);
// have to use matching typed Object(Integer, Double ...) to do unboxing
Local toReturn = resultLocals.get(mReturnTypeId);
autoUnboxIfNecessary(code, toReturn, matchObjLocal, resultLocals, true);
code.returnValue(toReturn);
}
}
private Local[] createParameterLocals(Code code) {
Local[] paramLocals = new Local[mParameterTypeIds.length];
for (int i = 0; i < mParameterTypeIds.length; i++) {
paramLocals[i] = code.getParameter(i, mParameterTypeIds[i]);
}
return paramLocals;
}
}

View File

@ -1,133 +0,0 @@
package com.swift.sandhook.xposedcompat.methodgen;
import android.os.Build;
import android.os.Process;
import android.os.Trace;
import io.github.lsposed.lspd.nativebridge.Yahfa;
import io.github.lsposed.lspd.util.ClassLoaderUtils;
import io.github.lsposed.lspd.util.FileUtils;
import com.swift.sandhook.SandHook;
import com.swift.sandhook.SandHookConfig;
import com.swift.sandhook.blacklist.HookBlackList;
import com.swift.sandhook.wrapper.HookWrapper;
import com.swift.sandhook.xposedcompat.XposedCompat;
import com.swift.sandhook.xposedcompat.hookstub.HookMethodEntity;
import com.swift.sandhook.xposedcompat.hookstub.HookStubManager;
import com.swift.sandhook.xposedcompat.utils.DexLog;
import java.io.File;
import java.lang.reflect.Constructor;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import de.robv.android.xposed.XposedBridge;
public final class SandHookXposedBridge {
private static final Map<Member, Method> hookedInfo = new ConcurrentHashMap<>();
private static HookMaker defaultHookMaker = XposedCompat.useNewCallBackup ? new HookerDexMakerNew() : new HookerDexMaker();
public static Map<Member, HookMethodEntity> entityMap = new ConcurrentHashMap<>();
public static void onForkPost() {
XposedCompat.onForkProcess();
}
public static boolean hooked(Member member) {
return hookedInfo.containsKey(member) || entityMap.containsKey(member);
}
public static synchronized void hookMethod(Member hookMethod, XposedBridge.AdditionalHookInfo additionalHookInfo) {
if (!checkMember(hookMethod)) {
return;
}
if (hookedInfo.containsKey(hookMethod) || entityMap.containsKey(hookMethod)) {
DexLog.w("already hook method:" + hookMethod.toString());
return;
}
Yahfa.recordHooked(hookMethod); // in case static method got reset.
try {
Trace.beginSection("SandXposed");
long timeStart = System.currentTimeMillis();
HookMethodEntity stub = null;
if (XposedCompat.useInternalStub && !HookBlackList.canNotHookByStub(hookMethod) && !HookBlackList.canNotHookByBridge(hookMethod)) {
stub = HookStubManager.getHookMethodEntity(hookMethod, additionalHookInfo);
}
if (stub != null) {
SandHook.hook(new HookWrapper.HookEntity(hookMethod, stub.hook, stub.backup, false));
entityMap.put(hookMethod, stub);
} else {
HookMaker hookMaker;
if (HookBlackList.canNotHookByBridge(hookMethod)) {
hookMaker = new HookerDexMaker();
} else {
hookMaker = defaultHookMaker;
}
hookMaker.start(hookMethod, additionalHookInfo,
ClassLoaderUtils.createProxyClassLoader(
hookMethod.getDeclaringClass().getClassLoader()));
hookedInfo.put(hookMethod, hookMaker.getCallBackupMethod());
}
DexLog.d("hook method <" + hookMethod.toString() + "> cost " + (System.currentTimeMillis() - timeStart) + " ms, by " + (stub != null ? "internal stub" : "dex maker"));
Trace.endSection();
} catch (Exception e) {
DexLog.e("error occur when hook method <" + hookMethod.toString() + ">", e);
}
}
private static boolean checkMember(Member member) {
if (member instanceof Method) {
return true;
} else if (member instanceof Constructor<?>) {
return true;
} else if (member.getDeclaringClass().isInterface()) {
DexLog.e("Cannot hook interfaces: " + member.toString());
return false;
} else if (Modifier.isAbstract(member.getModifiers())) {
DexLog.e("Cannot hook abstract methods: " + member.toString());
return false;
} else {
DexLog.e("Only methods and constructors can be hooked: " + member.toString());
return false;
}
}
public static Object invokeOriginalMethod(Member method, Object thisObject, Object[] args)
throws Throwable {
return SandHook.callOriginMethod(method, thisObject, args);
}
public static void init() {
SandHookConfig.libSandHookPath = "";
SandHookConfig.libLoader = new SandHookConfig.LibLoader() {
@Override
public void loadLib() {
//do it in loadDexAndInit
}
};
SandHookConfig.DEBUG = true;
SandHookConfig.compiler = false;
//already impl in lspd
SandHookConfig.delayHook = false;
//use when call origin
HookBlackList.methodBlackList.add("java.lang.reflect.isStatic");
HookBlackList.methodBlackList.add("java.lang.reflect.Method.getModifiers");
if (Build.VERSION.SDK_INT >= 29) {
//unknown bug, disable tmp
//TODO Fix
XposedCompat.useInternalStub = false;
}
//in zygote disable compile
}
}

View File

@ -1,34 +0,0 @@
package com.swift.sandhook.xposedcompat.utils;
import android.app.Application;
import java.lang.reflect.Method;
public class ApplicationUtils {
private static Class classActivityThread;
private static Method currentApplicationMethod;
static Application application;
public static Application currentApplication() {
if (application != null)
return application;
if (currentApplicationMethod == null) {
try {
classActivityThread = Class.forName("android.app.ActivityThread");
currentApplicationMethod = classActivityThread.getDeclaredMethod("currentApplication");
} catch (Exception e) {
e.printStackTrace();
}
}
if (currentApplicationMethod == null)
return null;
try {
application = (Application) currentApplicationMethod.invoke(null);
} catch (Exception e) {
}
return application;
}
}

View File

@ -1,55 +0,0 @@
package com.swift.sandhook.xposedcompat.utils;
import android.util.Log;
import io.github.lsposed.lspd.BuildConfig;
import java.lang.reflect.Member;
public class DexLog {
public static final String TAG = "SandXposed";
public static volatile boolean DEBUG = BuildConfig.DEBUG;
public static int v(String s) {
return Log.v(TAG, s);
}
public static int i(String s) {
return Log.i(TAG, s);
}
public static int d(String s) {
if (DEBUG) {
return Log.d(TAG, s);
} else {
return 0;
}
}
public static void printMethodHookIn(Member member) {
if (DEBUG && member != null) {
Log.d("SandHook", "method <" + member.toString() + "> hook in");
}
}
public static void printCallOriginError(Member member) {
Log.e("SandHook", "method <" + member.toString() + "> call origin error!");
}
public static int w(String s) {
return Log.w(TAG, s);
}
public static int e(String s) {
return Log.e(TAG, s);
}
public static int e(String s, Throwable t) {
return Log.e(TAG, s, t);
}
}

View File

@ -1,255 +0,0 @@
package com.swift.sandhook.xposedcompat.utils;
import io.github.lsposed.lspd.util.Utils;
import java.io.File;
import java.lang.reflect.Method;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.Map;
import external.com.android.dx.Code;
import external.com.android.dx.Local;
import external.com.android.dx.TypeId;
import static io.github.lsposed.lspd.config.LSPApplicationServiceClient.serviceClient;
public class DexMakerUtils {
public static boolean canCache = true;
static {
File cacheDir = new File(serviceClient.getCachePath(""));
if(!cacheDir.canRead() || !cacheDir.canWrite()) {
Utils.logW("Cache disabled");
canCache = false;
}
}
private static volatile Method addInstMethod, specMethod;
public static void autoBoxIfNecessary(Code code, Local<Object> target, Local source) {
String boxMethod = "valueOf";
TypeId<?> boxTypeId;
TypeId typeId = source.getType();
if (typeId.equals(TypeId.BOOLEAN)) {
boxTypeId = TypeId.get(Boolean.class);
code.invokeStatic(boxTypeId.getMethod(boxTypeId, boxMethod, TypeId.BOOLEAN), target, source);
} else if (typeId.equals(TypeId.BYTE)) {
boxTypeId = TypeId.get(Byte.class);
code.invokeStatic(boxTypeId.getMethod(boxTypeId, boxMethod, TypeId.BYTE), target, source);
} else if (typeId.equals(TypeId.CHAR)) {
boxTypeId = TypeId.get(Character.class);
code.invokeStatic(boxTypeId.getMethod(boxTypeId, boxMethod, TypeId.CHAR), target, source);
} else if (typeId.equals(TypeId.DOUBLE)) {
boxTypeId = TypeId.get(Double.class);
code.invokeStatic(boxTypeId.getMethod(boxTypeId, boxMethod, TypeId.DOUBLE), target, source);
} else if (typeId.equals(TypeId.FLOAT)) {
boxTypeId = TypeId.get(Float.class);
code.invokeStatic(boxTypeId.getMethod(boxTypeId, boxMethod, TypeId.FLOAT), target, source);
} else if (typeId.equals(TypeId.INT)) {
boxTypeId = TypeId.get(Integer.class);
code.invokeStatic(boxTypeId.getMethod(boxTypeId, boxMethod, TypeId.INT), target, source);
} else if (typeId.equals(TypeId.LONG)) {
boxTypeId = TypeId.get(Long.class);
code.invokeStatic(boxTypeId.getMethod(boxTypeId, boxMethod, TypeId.LONG), target, source);
} else if (typeId.equals(TypeId.SHORT)) {
boxTypeId = TypeId.get(Short.class);
code.invokeStatic(boxTypeId.getMethod(boxTypeId, boxMethod, TypeId.SHORT), target, source);
} else if (typeId.equals(TypeId.VOID)) {
code.loadConstant(target, null);
} else {
code.move(target, source);
}
}
public static void autoUnboxIfNecessary(Code code, Local target, Local source,
Map<TypeId, Local> tmpLocals, boolean castObj) {
String unboxMethod;
TypeId typeId = target.getType();
TypeId<?> boxTypeId;
if (typeId.equals(TypeId.BOOLEAN)) {
unboxMethod = "booleanValue";
boxTypeId = TypeId.get("Ljava/lang/Boolean;");
Local boxTypedLocal = tmpLocals.get(boxTypeId);
code.cast(boxTypedLocal, source);
code.invokeVirtual(boxTypeId.getMethod(TypeId.BOOLEAN, unboxMethod), target, boxTypedLocal);
} else if (typeId.equals(TypeId.BYTE)) {
unboxMethod = "byteValue";
boxTypeId = TypeId.get("Ljava/lang/Byte;");
Local boxTypedLocal = tmpLocals.get(boxTypeId);
code.cast(boxTypedLocal, source);
code.invokeVirtual(boxTypeId.getMethod(TypeId.BYTE, unboxMethod), target, boxTypedLocal);
} else if (typeId.equals(TypeId.CHAR)) {
unboxMethod = "charValue";
boxTypeId = TypeId.get("Ljava/lang/Character;");
Local boxTypedLocal = tmpLocals.get(boxTypeId);
code.cast(boxTypedLocal, source);
code.invokeVirtual(boxTypeId.getMethod(TypeId.CHAR, unboxMethod), target, boxTypedLocal);
} else if (typeId.equals(TypeId.DOUBLE)) {
unboxMethod = "doubleValue";
boxTypeId = TypeId.get("Ljava/lang/Double;");
Local boxTypedLocal = tmpLocals.get(boxTypeId);
code.cast(boxTypedLocal, source);
code.invokeVirtual(boxTypeId.getMethod(TypeId.DOUBLE, unboxMethod), target, boxTypedLocal);
} else if (typeId.equals(TypeId.FLOAT)) {
unboxMethod = "floatValue";
boxTypeId = TypeId.get("Ljava/lang/Float;");
Local boxTypedLocal = tmpLocals.get(boxTypeId);
code.cast(boxTypedLocal, source);
code.invokeVirtual(boxTypeId.getMethod(TypeId.FLOAT, unboxMethod), target, boxTypedLocal);
} else if (typeId.equals(TypeId.INT)) {
unboxMethod = "intValue";
boxTypeId = TypeId.get("Ljava/lang/Integer;");
Local boxTypedLocal = tmpLocals.get(boxTypeId);
code.cast(boxTypedLocal, source);
code.invokeVirtual(boxTypeId.getMethod(TypeId.INT, unboxMethod), target, boxTypedLocal);
} else if (typeId.equals(TypeId.LONG)) {
unboxMethod = "longValue";
boxTypeId = TypeId.get("Ljava/lang/Long;");
Local boxTypedLocal = tmpLocals.get(boxTypeId);
code.cast(boxTypedLocal, source);
code.invokeVirtual(boxTypeId.getMethod(TypeId.LONG, unboxMethod), target, boxTypedLocal);
} else if (typeId.equals(TypeId.SHORT)) {
unboxMethod = "shortValue";
boxTypeId = TypeId.get("Ljava/lang/Short;");
Local boxTypedLocal = tmpLocals.get(boxTypeId);
code.cast(boxTypedLocal, source);
code.invokeVirtual(boxTypeId.getMethod(TypeId.SHORT, unboxMethod), target, boxTypedLocal);
} else if (typeId.equals(TypeId.VOID)) {
code.loadConstant(target, null);
} else if (castObj) {
code.cast(target, source);
} else {
code.move(target, source);
}
}
public static Map<TypeId, Local> createResultLocals(Code code) {
HashMap<TypeId, Local> resultMap = new HashMap<>();
Local<Boolean> booleanLocal = code.newLocal(TypeId.BOOLEAN);
Local<Byte> byteLocal = code.newLocal(TypeId.BYTE);
Local<Character> charLocal = code.newLocal(TypeId.CHAR);
Local<Double> doubleLocal = code.newLocal(TypeId.DOUBLE);
Local<Float> floatLocal = code.newLocal(TypeId.FLOAT);
Local<Integer> intLocal = code.newLocal(TypeId.INT);
Local<Long> longLocal = code.newLocal(TypeId.LONG);
Local<Short> shortLocal = code.newLocal(TypeId.SHORT);
Local<Void> voidLocal = code.newLocal(TypeId.VOID);
Local<Object> objectLocal = code.newLocal(TypeId.OBJECT);
Local<Object> booleanObjLocal = code.newLocal(TypeId.get("Ljava/lang/Boolean;"));
Local<Object> byteObjLocal = code.newLocal(TypeId.get("Ljava/lang/Byte;"));
Local<Object> charObjLocal = code.newLocal(TypeId.get("Ljava/lang/Character;"));
Local<Object> doubleObjLocal = code.newLocal(TypeId.get("Ljava/lang/Double;"));
Local<Object> floatObjLocal = code.newLocal(TypeId.get("Ljava/lang/Float;"));
Local<Object> intObjLocal = code.newLocal(TypeId.get("Ljava/lang/Integer;"));
Local<Object> longObjLocal = code.newLocal(TypeId.get("Ljava/lang/Long;"));
Local<Object> shortObjLocal = code.newLocal(TypeId.get("Ljava/lang/Short;"));
Local<Object> voidObjLocal = code.newLocal(TypeId.get("Ljava/lang/Void;"));
// backup need initialized locals
code.loadConstant(booleanLocal, false);
code.loadConstant(byteLocal, (byte) 0);
code.loadConstant(charLocal, '\0');
code.loadConstant(doubleLocal,0.0);
code.loadConstant(floatLocal,0.0f);
code.loadConstant(intLocal, 0);
code.loadConstant(longLocal, 0L);
code.loadConstant(shortLocal, (short) 0);
code.loadConstant(voidLocal, null);
code.loadConstant(objectLocal, null);
// all to null
code.loadConstant(booleanObjLocal, null);
code.loadConstant(byteObjLocal, null);
code.loadConstant(charObjLocal, null);
code.loadConstant(doubleObjLocal, null);
code.loadConstant(floatObjLocal, null);
code.loadConstant(intObjLocal, null);
code.loadConstant(longObjLocal, null);
code.loadConstant(shortObjLocal, null);
code.loadConstant(voidObjLocal, null);
// package all
resultMap.put(TypeId.BOOLEAN, booleanLocal);
resultMap.put(TypeId.BYTE, byteLocal);
resultMap.put(TypeId.CHAR, charLocal);
resultMap.put(TypeId.DOUBLE, doubleLocal);
resultMap.put(TypeId.FLOAT, floatLocal);
resultMap.put(TypeId.INT, intLocal);
resultMap.put(TypeId.LONG, longLocal);
resultMap.put(TypeId.SHORT, shortLocal);
resultMap.put(TypeId.VOID, voidLocal);
resultMap.put(TypeId.OBJECT, objectLocal);
resultMap.put(TypeId.get("Ljava/lang/Boolean;"), booleanObjLocal);
resultMap.put(TypeId.get("Ljava/lang/Byte;"), byteObjLocal);
resultMap.put(TypeId.get("Ljava/lang/Character;"), charObjLocal);
resultMap.put(TypeId.get("Ljava/lang/Double;"), doubleObjLocal);
resultMap.put(TypeId.get("Ljava/lang/Float;"), floatObjLocal);
resultMap.put(TypeId.get("Ljava/lang/Integer;"), intObjLocal);
resultMap.put(TypeId.get("Ljava/lang/Long;"), longObjLocal);
resultMap.put(TypeId.get("Ljava/lang/Short;"), shortObjLocal);
resultMap.put(TypeId.get("Ljava/lang/Void;"), voidObjLocal);
return resultMap;
}
public static TypeId getObjTypeIdIfPrimitive(TypeId typeId) {
if (typeId.equals(TypeId.BOOLEAN)) {
return TypeId.get("Ljava/lang/Boolean;");
} else if (typeId.equals(TypeId.BYTE)) {
return TypeId.get("Ljava/lang/Byte;");
} else if (typeId.equals(TypeId.CHAR)) {
return TypeId.get("Ljava/lang/Character;");
} else if (typeId.equals(TypeId.DOUBLE)) {
return TypeId.get("Ljava/lang/Double;");
} else if (typeId.equals(TypeId.FLOAT)) {
return TypeId.get("Ljava/lang/Float;");
} else if (typeId.equals(TypeId.INT)) {
return TypeId.get("Ljava/lang/Integer;");
} else if (typeId.equals(TypeId.LONG)) {
return TypeId.get("Ljava/lang/Long;");
} else if (typeId.equals(TypeId.SHORT)) {
return TypeId.get("Ljava/lang/Short;");
} else if (typeId.equals(TypeId.VOID)) {
return TypeId.get("Ljava/lang/Void;");
} else {
return typeId;
}
}
public static void returnRightValue(Code code, Class<?> returnType, Map<Class, Local> resultLocals) {
String unboxMethod;
TypeId<?> boxTypeId;
code.returnValue(resultLocals.get(returnType));
}
public static String MD5(String source) {
try {
MessageDigest messageDigest = MessageDigest.getInstance("MD5");
messageDigest.update(source.getBytes());
return new BigInteger(1, messageDigest.digest()).toString(32);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return source;
}
public static String getSha1Hex(String text) {
final MessageDigest digest;
try {
digest = MessageDigest.getInstance("SHA-1");
byte[] result = digest.digest(text.getBytes(StandardCharsets.UTF_8));
StringBuilder sb = new StringBuilder();
for (byte b : result) {
sb.append(String.format("%02x", b));
}
return sb.toString();
} catch (Exception e) {
DexLog.e("error hashing target method: " + text, e);
}
return "";
}
}

View File

@ -20,25 +20,26 @@
package de.robv.android.xposed; package de.robv.android.xposed;
import java.lang.reflect.Executable;
import java.lang.reflect.Member; import java.lang.reflect.Member;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import io.github.lsposed.lspd.config.LSPdConfigGlobal; import io.github.lsposed.lspd.yahfa.hooker.YahfaHooker;
import static io.github.lsposed.lspd.nativebridge.PendingHooks.recordPendingMethodNative; import static io.github.lsposed.lspd.nativebridge.PendingHooks.recordPendingMethodNative;
public final class PendingHooks { public final class PendingHooks {
// GuardedBy("PendingHooks.class") // GuardedBy("PendingHooks.class")
private static final ConcurrentHashMap<Class<?>, ConcurrentHashMap<Member, XposedBridge.AdditionalHookInfo>> private static final ConcurrentHashMap<Class<?>, ConcurrentHashMap<Executable, XposedBridge.AdditionalHookInfo>>
sPendingHooks = new ConcurrentHashMap<>(); sPendingHooks = new ConcurrentHashMap<>();
public synchronized static void hookPendingMethod(Class<?> clazz) { public synchronized static void hookPendingMethod(Class<?> clazz) {
if (sPendingHooks.containsKey(clazz)) { if (sPendingHooks.containsKey(clazz)) {
for (Map.Entry<Member, XposedBridge.AdditionalHookInfo> hook : sPendingHooks.get(clazz).entrySet()) { for (Map.Entry<Executable, XposedBridge.AdditionalHookInfo> hook : sPendingHooks.get(clazz).entrySet()) {
LSPdConfigGlobal.getHookProvider().hookMethod(hook.getKey(), hook.getValue()); YahfaHooker.hookMethod(hook.getKey(), hook.getValue());
} }
sPendingHooks.remove(clazz); sPendingHooks.remove(clazz);
} }
@ -46,7 +47,7 @@ public final class PendingHooks {
public synchronized static void recordPendingMethod(Method hookMethod, public synchronized static void recordPendingMethod(Method hookMethod,
XposedBridge.AdditionalHookInfo additionalInfo) { XposedBridge.AdditionalHookInfo additionalInfo) {
ConcurrentHashMap<Member, XposedBridge.AdditionalHookInfo> pending = ConcurrentHashMap<Executable, XposedBridge.AdditionalHookInfo> pending =
sPendingHooks.computeIfAbsent(hookMethod.getDeclaringClass(), aClass -> new ConcurrentHashMap<>()); sPendingHooks.computeIfAbsent(hookMethod.getDeclaringClass(), aClass -> new ConcurrentHashMap<>());
pending.put(hookMethod, additionalInfo); pending.put(hookMethod, additionalInfo);

View File

@ -25,31 +25,26 @@ import android.content.res.TypedArray;
import android.util.Log; import android.util.Log;
import io.github.lsposed.lspd.BuildConfig; import io.github.lsposed.lspd.BuildConfig;
import io.github.lsposed.lspd.config.LSPdConfigGlobal;
import java.lang.reflect.AccessibleObject; import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Constructor; import java.lang.reflect.Executable;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Member; import java.lang.reflect.Member;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.nio.ByteBuffer;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import dalvik.system.InMemoryDexClassLoader;
import io.github.lsposed.lspd.annotation.ApiSensitive;
import io.github.lsposed.lspd.annotation.Level;
import de.robv.android.xposed.callbacks.XC_InitPackageResources; import de.robv.android.xposed.callbacks.XC_InitPackageResources;
import de.robv.android.xposed.callbacks.XC_InitZygote; import de.robv.android.xposed.callbacks.XC_InitZygote;
import de.robv.android.xposed.callbacks.XC_LoadPackage; import de.robv.android.xposed.callbacks.XC_LoadPackage;
import external.com.android.dx.DexMaker;
import external.com.android.dx.TypeId;
import io.github.lsposed.lspd.nativebridge.ModuleLogger; import io.github.lsposed.lspd.nativebridge.ModuleLogger;
import io.github.lsposed.lspd.nativebridge.ResourcesHook; import io.github.lsposed.lspd.nativebridge.ResourcesHook;
import io.github.lsposed.lspd.yahfa.dexmaker.DynamicBridge;
import io.github.lsposed.lspd.yahfa.hooker.YahfaHooker;
import static de.robv.android.xposed.XposedHelpers.setObjectField; import static de.robv.android.xposed.XposedHelpers.setObjectField;
@ -91,7 +86,6 @@ public final class XposedBridge {
public static volatile ClassLoader dummyClassLoader = null; public static volatile ClassLoader dummyClassLoader = null;
@ApiSensitive(Level.MIDDLE)
public static void initXResources() { public static void initXResources() {
if (dummyClassLoader != null) { if (dummyClassLoader != null) {
return; return;
@ -108,22 +102,10 @@ public final class XposedBridge {
} catch (Resources.NotFoundException nfe) { } catch (Resources.NotFoundException nfe) {
XposedBridge.log(nfe); XposedBridge.log(nfe);
} }
XposedBridge.removeFinalFlagNative(resClass); ResourcesHook.removeFinalFlagNative(resClass);
XposedBridge.removeFinalFlagNative(taClass); ResourcesHook.removeFinalFlagNative(taClass);
ClassLoader myCL = XposedBridge.class.getClassLoader(); ClassLoader myCL = XposedBridge.class.getClassLoader();
dummyClassLoader = ResourcesHook.buildDummyClassLoader(myCL.getParent(), resClass, taClass); dummyClassLoader = ResourcesHook.buildDummyClassLoader(myCL.getParent(), resClass, taClass);
if (dummyClassLoader == null) {
XposedBridge.log("Dexbuilder fails, fallback to dexmaker");
DexMaker dexMaker = new DexMaker();
dexMaker.declare(TypeId.get("Lxposed/dummy/XResourcesSuperClass;"),
"XResourcesSuperClass.java",
Modifier.PUBLIC, TypeId.get(resClass));
dexMaker.declare(TypeId.get("Lxposed/dummy/XTypedArraySuperClass;"),
"XTypedArraySuperClass.java",
Modifier.PUBLIC, TypeId.get(taClass));
dummyClassLoader = new InMemoryDexClassLoader(
ByteBuffer.wrap(dexMaker.generate()), myCL.getParent());
}
dummyClassLoader.loadClass("xposed.dummy.XResourcesSuperClass"); dummyClassLoader.loadClass("xposed.dummy.XResourcesSuperClass");
dummyClassLoader.loadClass("xposed.dummy.XTypedArraySuperClass"); dummyClassLoader.loadClass("xposed.dummy.XTypedArraySuperClass");
setObjectField(myCL, "parent", dummyClassLoader); setObjectField(myCL, "parent", dummyClassLoader);
@ -183,7 +165,7 @@ public final class XposedBridge {
* @see #hookAllConstructors * @see #hookAllConstructors
*/ */
public static XC_MethodHook.Unhook hookMethod(Member hookMethod, XC_MethodHook callback) { public static XC_MethodHook.Unhook hookMethod(Member hookMethod, XC_MethodHook callback) {
if (!(hookMethod instanceof Method) && !(hookMethod instanceof Constructor<?>)) { if (!(hookMethod instanceof Executable)) {
throw new IllegalArgumentException("Only methods and constructors can be hooked: " + hookMethod.toString()); throw new IllegalArgumentException("Only methods and constructors can be hooked: " + hookMethod.toString());
} else if (hookMethod.getDeclaringClass().isInterface()) { } else if (hookMethod.getDeclaringClass().isInterface()) {
throw new IllegalArgumentException("Cannot hook interfaces: " + hookMethod.toString()); throw new IllegalArgumentException("Cannot hook interfaces: " + hookMethod.toString());
@ -191,6 +173,8 @@ public final class XposedBridge {
throw new IllegalArgumentException("Cannot hook abstract methods: " + hookMethod.toString()); throw new IllegalArgumentException("Cannot hook abstract methods: " + hookMethod.toString());
} }
Executable targetMethod = (Executable) hookMethod;
if (callback == null) { if (callback == null) {
throw new IllegalArgumentException("callback should not be null!"); throw new IllegalArgumentException("callback should not be null!");
} }
@ -198,10 +182,10 @@ public final class XposedBridge {
boolean newMethod = false; boolean newMethod = false;
CopyOnWriteSortedSet<XC_MethodHook> callbacks; CopyOnWriteSortedSet<XC_MethodHook> callbacks;
synchronized (sHookedMethodCallbacks) { synchronized (sHookedMethodCallbacks) {
callbacks = sHookedMethodCallbacks.get(hookMethod); callbacks = sHookedMethodCallbacks.get(targetMethod);
if (callbacks == null) { if (callbacks == null) {
callbacks = new CopyOnWriteSortedSet<>(); callbacks = new CopyOnWriteSortedSet<>();
sHookedMethodCallbacks.put(hookMethod, callbacks); sHookedMethodCallbacks.put(targetMethod, callbacks);
newMethod = true; newMethod = true;
} }
} }
@ -209,9 +193,8 @@ public final class XposedBridge {
if (newMethod) { if (newMethod) {
AdditionalHookInfo additionalInfo = new AdditionalHookInfo(callbacks); AdditionalHookInfo additionalInfo = new AdditionalHookInfo(callbacks);
Member reflectMethod = LSPdConfigGlobal.getHookProvider().findMethodNative(hookMethod); if (!YahfaHooker.shouldDelayHook(targetMethod)) {
if (reflectMethod != null) { DynamicBridge.hookMethod(targetMethod, (AdditionalHookInfo) additionalInfo);
LSPdConfigGlobal.getHookProvider().hookMethod(reflectMethod, (AdditionalHookInfo) additionalInfo);
} else { } else {
PendingHooks.recordPendingMethod((Method)hookMethod, additionalInfo); PendingHooks.recordPendingMethod((Method)hookMethod, additionalInfo);
} }
@ -231,7 +214,6 @@ public final class XposedBridge {
*/ */
@Deprecated @Deprecated
public static void unhookMethod(Member hookMethod, XC_MethodHook callback) { public static void unhookMethod(Member hookMethod, XC_MethodHook callback) {
LSPdConfigGlobal.getHookProvider().unhookMethod(hookMethod);
CopyOnWriteSortedSet<XC_MethodHook> callbacks; CopyOnWriteSortedSet<XC_MethodHook> callbacks;
synchronized (sHookedMethodCallbacks) { synchronized (sHookedMethodCallbacks) {
callbacks = sHookedMethodCallbacks.get(hookMethod); callbacks = sHookedMethodCallbacks.get(hookMethod);
@ -348,16 +330,11 @@ public final class XposedBridge {
args = EMPTY_ARRAY; args = EMPTY_ARRAY;
} }
if (!(method instanceof Method) && !(method instanceof Constructor)) { if (!(method instanceof Executable)) {
throw new IllegalArgumentException("method must be of type Method or Constructor"); throw new IllegalArgumentException("method must be of type Method or Constructor");
} }
long methodId = LSPdConfigGlobal.getHookProvider().getMethodId(method); return YahfaHooker.invokeOriginalMethod((Executable) method, thisObject, args);
return LSPdConfigGlobal.getHookProvider().invokeOriginalMethod(method, methodId, thisObject, args);
}
private static void removeFinalFlagNative(Class clazz) {
LSPdConfigGlobal.getHookProvider().removeFinalFlagNative(clazz);
} }
/** @hide */ /** @hide */

View File

@ -23,12 +23,9 @@ package de.robv.android.xposed;
import android.content.res.AssetManager; import android.content.res.AssetManager;
import android.content.res.Resources; import android.content.res.Resources;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.Closeable; import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.FileReader;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
@ -41,15 +38,11 @@ import java.math.BigInteger;
import java.security.MessageDigest; import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Set;
import java.util.WeakHashMap; import java.util.WeakHashMap;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.zip.ZipFile;
import dalvik.system.DexFile;
import external.org.apache.commons.lang3.ClassUtils; import external.org.apache.commons.lang3.ClassUtils;
import external.org.apache.commons.lang3.reflect.MemberUtils; import external.org.apache.commons.lang3.reflect.MemberUtils;

View File

@ -54,10 +54,8 @@ import de.robv.android.xposed.callbacks.XC_InitZygote;
import de.robv.android.xposed.callbacks.XC_LoadPackage; import de.robv.android.xposed.callbacks.XC_LoadPackage;
import de.robv.android.xposed.callbacks.XCallback; import de.robv.android.xposed.callbacks.XCallback;
import hidden.HiddenApiBridge; import hidden.HiddenApiBridge;
import io.github.lsposed.lspd.annotation.ApiSensitive;
import io.github.lsposed.lspd.annotation.Level;
import io.github.lsposed.lspd.config.LSPdConfigGlobal;
import io.github.lsposed.lspd.nativebridge.NativeAPI; import io.github.lsposed.lspd.nativebridge.NativeAPI;
import io.github.lsposed.lspd.nativebridge.ResourcesHook;
import static de.robv.android.xposed.XposedBridge.hookAllMethods; import static de.robv.android.xposed.XposedBridge.hookAllMethods;
import static de.robv.android.xposed.XposedBridge.sInitPackageResourcesCallbacks; import static de.robv.android.xposed.XposedBridge.sInitPackageResourcesCallbacks;
@ -106,13 +104,12 @@ public final class XposedInit {
hookResources(); hookResources();
} }
@ApiSensitive(Level.MIDDLE)
private static void hookResources() throws Throwable { private static void hookResources() throws Throwable {
if (!serviceClient.isResourcesHookEnabled() || disableResources) { if (!serviceClient.isResourcesHookEnabled() || disableResources) {
return; return;
} }
if (!LSPdConfigGlobal.getHookProvider().initXResourcesNative()) { if (!ResourcesHook.initXResourcesNative()) {
Log.e(TAG, "Cannot hook resources"); Log.e(TAG, "Cannot hook resources");
disableResources = true; disableResources = true;
return; return;
@ -209,7 +206,6 @@ public final class XposedInit {
XResources.init(latestResKey); XResources.init(latestResKey);
} }
@ApiSensitive(Level.MIDDLE)
private static XResources cloneToXResources(XC_MethodHook.MethodHookParam param, String resDir) { private static XResources cloneToXResources(XC_MethodHook.MethodHookParam param, String resDir) {
Object result = param.getResult(); Object result = param.getResult();
if (result == null || result instanceof XResources || if (result == null || result instanceof XResources ||

View File

@ -22,13 +22,12 @@ package de.robv.android.xposed.callbacks;
import android.os.Bundle; import android.os.Bundle;
import io.github.lsposed.lspd.config.LSPdConfigGlobal;
import java.io.Serializable; import java.io.Serializable;
import de.robv.android.xposed.IModuleContext; import de.robv.android.xposed.IModuleContext;
import de.robv.android.xposed.XposedBridge; import de.robv.android.xposed.XposedBridge;
import de.robv.android.xposed.XposedBridge.CopyOnWriteSortedSet; import de.robv.android.xposed.XposedBridge.CopyOnWriteSortedSet;
import io.github.lsposed.lspd.deopt.PrebuiltMethodsDeopter;
/** /**
* Base class for Xposed callbacks. * Base class for Xposed callbacks.
@ -126,7 +125,7 @@ public abstract class XCallback implements Comparable<XCallback>, IModuleContext
// deopt methods in system apps or priv-apps, this would be not necessary // deopt methods in system apps or priv-apps, this would be not necessary
// only if we found out how to recompile their apks // only if we found out how to recompile their apks
XC_LoadPackage.LoadPackageParam lpp = (XC_LoadPackage.LoadPackageParam) param; XC_LoadPackage.LoadPackageParam lpp = (XC_LoadPackage.LoadPackageParam) param;
LSPdConfigGlobal.getHookProvider().deoptMethods(lpp.packageName, lpp.classLoader); PrebuiltMethodsDeopter.deoptMethods(lpp.packageName, lpp.classLoader);
} }
if (param.callbacks == null) if (param.callbacks == null)

View File

@ -1,65 +0,0 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package external.com.android.dex;
import static external.com.android.dex.EncodedValueReader.ENCODED_ANNOTATION;
/**
* An annotation.
*/
public final class Annotation implements Comparable<Annotation> {
private final Dex dex;
private final byte visibility;
private final EncodedValue encodedAnnotation;
public Annotation(Dex dex, byte visibility, EncodedValue encodedAnnotation) {
this.dex = dex;
this.visibility = visibility;
this.encodedAnnotation = encodedAnnotation;
}
public byte getVisibility() {
return visibility;
}
public EncodedValueReader getReader() {
return new EncodedValueReader(encodedAnnotation, ENCODED_ANNOTATION);
}
public int getTypeIndex() {
EncodedValueReader reader = getReader();
reader.readAnnotation();
return reader.getAnnotationType();
}
public void writeTo(Dex.Section out) {
out.writeByte(visibility);
encodedAnnotation.writeTo(out);
}
@Override
public int compareTo(Annotation other) {
return encodedAnnotation.compareTo(other.encodedAnnotation);
}
@Override
public String toString() {
return dex == null
? visibility + " " + getTypeIndex()
: visibility + " " + dex.typeNames().get(getTypeIndex());
}
}

View File

@ -1,55 +0,0 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package external.com.android.dex;
import external.com.android.dex.Dex.Section;
import external.com.android.dex.util.Unsigned;
/**
* A call_site_id_item: https://source.android.com/devices/tech/dalvik/dex-format#call-site-id-item
*/
public class CallSiteId implements Comparable<CallSiteId> {
private final Dex dex;
private final int offset;
public CallSiteId(Dex dex, int offset) {
this.dex = dex;
this.offset = offset;
}
@Override
public int compareTo(CallSiteId o) {
return Unsigned.compare(offset, o.offset);
}
public int getCallSiteOffset() {
return offset;
}
public void writeTo(Section out) {
out.writeInt(offset);
}
@Override
public String toString() {
if (dex == null) {
return String.valueOf(offset);
}
return dex.protoIds().get(offset).toString();
}
}

View File

@ -1,104 +0,0 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package external.com.android.dex;
public final class ClassData {
private final Field[] staticFields;
private final Field[] instanceFields;
private final Method[] directMethods;
private final Method[] virtualMethods;
public ClassData(Field[] staticFields, Field[] instanceFields,
Method[] directMethods, Method[] virtualMethods) {
this.staticFields = staticFields;
this.instanceFields = instanceFields;
this.directMethods = directMethods;
this.virtualMethods = virtualMethods;
}
public Field[] getStaticFields() {
return staticFields;
}
public Field[] getInstanceFields() {
return instanceFields;
}
public Method[] getDirectMethods() {
return directMethods;
}
public Method[] getVirtualMethods() {
return virtualMethods;
}
public Field[] allFields() {
Field[] result = new Field[staticFields.length + instanceFields.length];
System.arraycopy(staticFields, 0, result, 0, staticFields.length);
System.arraycopy(instanceFields, 0, result, staticFields.length, instanceFields.length);
return result;
}
public Method[] allMethods() {
Method[] result = new Method[directMethods.length + virtualMethods.length];
System.arraycopy(directMethods, 0, result, 0, directMethods.length);
System.arraycopy(virtualMethods, 0, result, directMethods.length, virtualMethods.length);
return result;
}
public static class Field {
private final int fieldIndex;
private final int accessFlags;
public Field(int fieldIndex, int accessFlags) {
this.fieldIndex = fieldIndex;
this.accessFlags = accessFlags;
}
public int getFieldIndex() {
return fieldIndex;
}
public int getAccessFlags() {
return accessFlags;
}
}
public static class Method {
private final int methodIndex;
private final int accessFlags;
private final int codeOffset;
public Method(int methodIndex, int accessFlags, int codeOffset) {
this.methodIndex = methodIndex;
this.accessFlags = accessFlags;
this.codeOffset = codeOffset;
}
public int getMethodIndex() {
return methodIndex;
}
public int getAccessFlags() {
return accessFlags;
}
public int getCodeOffset() {
return codeOffset;
}
}
}

View File

@ -1,103 +0,0 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package external.com.android.dex;
/**
* A type definition.
*/
public final class ClassDef {
public static final int NO_INDEX = -1;
private final Dex buffer;
private final int offset;
private final int typeIndex;
private final int accessFlags;
private final int supertypeIndex;
private final int interfacesOffset;
private final int sourceFileIndex;
private final int annotationsOffset;
private final int classDataOffset;
private final int staticValuesOffset;
public ClassDef(Dex buffer, int offset, int typeIndex, int accessFlags,
int supertypeIndex, int interfacesOffset, int sourceFileIndex,
int annotationsOffset, int classDataOffset, int staticValuesOffset) {
this.buffer = buffer;
this.offset = offset;
this.typeIndex = typeIndex;
this.accessFlags = accessFlags;
this.supertypeIndex = supertypeIndex;
this.interfacesOffset = interfacesOffset;
this.sourceFileIndex = sourceFileIndex;
this.annotationsOffset = annotationsOffset;
this.classDataOffset = classDataOffset;
this.staticValuesOffset = staticValuesOffset;
}
public int getOffset() {
return offset;
}
public int getTypeIndex() {
return typeIndex;
}
public int getSupertypeIndex() {
return supertypeIndex;
}
public int getInterfacesOffset() {
return interfacesOffset;
}
public short[] getInterfaces() {
return buffer.readTypeList(interfacesOffset).getTypes();
}
public int getAccessFlags() {
return accessFlags;
}
public int getSourceFileIndex() {
return sourceFileIndex;
}
public int getAnnotationsOffset() {
return annotationsOffset;
}
public int getClassDataOffset() {
return classDataOffset;
}
public int getStaticValuesOffset() {
return staticValuesOffset;
}
@Override
public String toString() {
if (buffer == null) {
return typeIndex + " " + supertypeIndex;
}
StringBuilder result = new StringBuilder();
result.append(buffer.typeNames().get(typeIndex));
if (supertypeIndex != NO_INDEX) {
result.append(" extends ").append(buffer.typeNames().get(supertypeIndex));
}
return result.toString();
}
}

View File

@ -1,124 +0,0 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package external.com.android.dex;
public final class Code {
private final int registersSize;
private final int insSize;
private final int outsSize;
private final int debugInfoOffset;
private final short[] instructions;
private final Try[] tries;
private final CatchHandler[] catchHandlers;
public Code(int registersSize, int insSize, int outsSize, int debugInfoOffset,
short[] instructions, Try[] tries, CatchHandler[] catchHandlers) {
this.registersSize = registersSize;
this.insSize = insSize;
this.outsSize = outsSize;
this.debugInfoOffset = debugInfoOffset;
this.instructions = instructions;
this.tries = tries;
this.catchHandlers = catchHandlers;
}
public int getRegistersSize() {
return registersSize;
}
public int getInsSize() {
return insSize;
}
public int getOutsSize() {
return outsSize;
}
public int getDebugInfoOffset() {
return debugInfoOffset;
}
public short[] getInstructions() {
return instructions;
}
public Try[] getTries() {
return tries;
}
public CatchHandler[] getCatchHandlers() {
return catchHandlers;
}
public static class Try {
final int startAddress;
final int instructionCount;
final int catchHandlerIndex;
Try(int startAddress, int instructionCount, int catchHandlerIndex) {
this.startAddress = startAddress;
this.instructionCount = instructionCount;
this.catchHandlerIndex = catchHandlerIndex;
}
public int getStartAddress() {
return startAddress;
}
public int getInstructionCount() {
return instructionCount;
}
/**
* Returns this try's catch handler <strong>index</strong>. Note that
* this is distinct from the its catch handler <strong>offset</strong>.
*/
public int getCatchHandlerIndex() {
return catchHandlerIndex;
}
}
public static class CatchHandler {
final int[] typeIndexes;
final int[] addresses;
final int catchAllAddress;
final int offset;
public CatchHandler(int[] typeIndexes, int[] addresses, int catchAllAddress, int offset) {
this.typeIndexes = typeIndexes;
this.addresses = addresses;
this.catchAllAddress = catchAllAddress;
this.offset = offset;
}
public int[] getTypeIndexes() {
return typeIndexes;
}
public int[] getAddresses() {
return addresses;
}
public int getCatchAllAddress() {
return catchAllAddress;
}
public int getOffset() {
return offset;
}
}
}

View File

@ -1,819 +0,0 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package external.com.android.dex;
import external.com.android.dex.Code.CatchHandler;
import external.com.android.dex.Code.Try;
import external.com.android.dex.MethodHandle.MethodHandleType;
import external.com.android.dex.util.ByteInput;
import external.com.android.dex.util.ByteOutput;
import external.com.android.dex.util.FileUtils;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UTFDataFormatException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.AbstractList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.RandomAccess;
import java.util.zip.Adler32;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
/**
* The bytes of a dex file in memory for reading and writing. All int offsets
* are unsigned.
*/
public final class Dex {
private static final int CHECKSUM_OFFSET = 8;
private static final int CHECKSUM_SIZE = 4;
private static final int SIGNATURE_OFFSET = CHECKSUM_OFFSET + CHECKSUM_SIZE;
private static final int SIGNATURE_SIZE = 20;
// Provided as a convenience to avoid a memory allocation to benefit Dalvik.
// Note: libcore.util.EmptyArray cannot be accessed when this code isn't run on Dalvik.
static final short[] EMPTY_SHORT_ARRAY = new short[0];
private ByteBuffer data;
private final TableOfContents tableOfContents = new TableOfContents();
private int nextSectionStart = 0;
private final StringTable strings = new StringTable();
private final TypeIndexToDescriptorIndexTable typeIds = new TypeIndexToDescriptorIndexTable();
private final TypeIndexToDescriptorTable typeNames = new TypeIndexToDescriptorTable();
private final ProtoIdTable protoIds = new ProtoIdTable();
private final FieldIdTable fieldIds = new FieldIdTable();
private final MethodIdTable methodIds = new MethodIdTable();
/**
* Creates a new dex that reads from {@code data}. It is an error to modify
* {@code data} after using it to create a dex buffer.
*/
public Dex(byte[] data) throws IOException {
this(ByteBuffer.wrap(data));
}
private Dex(ByteBuffer data) throws IOException {
this.data = data;
this.data.order(ByteOrder.LITTLE_ENDIAN);
this.tableOfContents.readFrom(this);
}
/**
* Creates a new empty dex of the specified size.
*/
public Dex(int byteCount) throws IOException {
this.data = ByteBuffer.wrap(new byte[byteCount]);
this.data.order(ByteOrder.LITTLE_ENDIAN);
}
/**
* Creates a new dex buffer of the dex in {@code in}, and closes {@code in}.
*/
public Dex(InputStream in) throws IOException {
try {
loadFrom(in);
} finally {
in.close();
}
}
/**
* Creates a new dex buffer from the dex file {@code file}.
*/
public Dex(File file) throws IOException {
if (FileUtils.hasArchiveSuffix(file.getName())) {
ZipFile zipFile = new ZipFile(file);
ZipEntry entry = zipFile.getEntry(DexFormat.DEX_IN_JAR_NAME);
if (entry != null) {
try (InputStream inputStream = zipFile.getInputStream(entry)) {
loadFrom(inputStream);
}
zipFile.close();
} else {
throw new DexException("Expected " + DexFormat.DEX_IN_JAR_NAME + " in " + file);
}
} else if (file.getName().endsWith(".dex")) {
try (InputStream inputStream = new FileInputStream(file)) {
loadFrom(inputStream);
}
} else {
throw new DexException("unknown output extension: " + file);
}
}
/**
* It is the caller's responsibility to close {@code in}.
*/
private void loadFrom(InputStream in) throws IOException {
ByteArrayOutputStream bytesOut = new ByteArrayOutputStream();
byte[] buffer = new byte[8192];
int count;
while ((count = in.read(buffer)) != -1) {
bytesOut.write(buffer, 0, count);
}
this.data = ByteBuffer.wrap(bytesOut.toByteArray());
this.data.order(ByteOrder.LITTLE_ENDIAN);
this.tableOfContents.readFrom(this);
}
private static void checkBounds(int index, int length) {
if (index < 0 || index >= length) {
throw new IndexOutOfBoundsException("index:" + index + ", length=" + length);
}
}
public void writeTo(OutputStream out) throws IOException {
byte[] buffer = new byte[8192];
ByteBuffer data = this.data.duplicate(); // positioned ByteBuffers aren't thread safe
data.clear();
while (data.hasRemaining()) {
int count = Math.min(buffer.length, data.remaining());
data.get(buffer, 0, count);
out.write(buffer, 0, count);
}
}
public void writeTo(File dexOut) throws IOException {
try (OutputStream out = new FileOutputStream(dexOut)) {
writeTo(out);
}
}
public TableOfContents getTableOfContents() {
return tableOfContents;
}
public Section open(int position) {
if (position < 0 || position >= data.capacity()) {
throw new IllegalArgumentException("position=" + position
+ " length=" + data.capacity());
}
ByteBuffer sectionData = data.duplicate();
sectionData.order(ByteOrder.LITTLE_ENDIAN); // necessary?
sectionData.position(position);
sectionData.limit(data.capacity());
return new Section("section", sectionData);
}
public Section appendSection(int maxByteCount, String name) {
if ((maxByteCount & 3) != 0) {
throw new IllegalStateException("Not four byte aligned!");
}
int limit = nextSectionStart + maxByteCount;
ByteBuffer sectionData = data.duplicate();
sectionData.order(ByteOrder.LITTLE_ENDIAN); // necessary?
sectionData.position(nextSectionStart);
sectionData.limit(limit);
Section result = new Section(name, sectionData);
nextSectionStart = limit;
return result;
}
public int getLength() {
return data.capacity();
}
public int getNextSectionStart() {
return nextSectionStart;
}
/**
* Returns a copy of the the bytes of this dex.
*/
public byte[] getBytes() {
ByteBuffer data = this.data.duplicate(); // positioned ByteBuffers aren't thread safe
byte[] result = new byte[data.capacity()];
data.position(0);
data.get(result);
return result;
}
public List<String> strings() {
return strings;
}
public List<Integer> typeIds() {
return typeIds;
}
public List<String> typeNames() {
return typeNames;
}
public List<ProtoId> protoIds() {
return protoIds;
}
public List<FieldId> fieldIds() {
return fieldIds;
}
public List<MethodId> methodIds() {
return methodIds;
}
public Iterable<ClassDef> classDefs() {
return new ClassDefIterable();
}
public TypeList readTypeList(int offset) {
if (offset == 0) {
return TypeList.EMPTY;
}
return open(offset).readTypeList();
}
public ClassData readClassData(ClassDef classDef) {
int offset = classDef.getClassDataOffset();
if (offset == 0) {
throw new IllegalArgumentException("offset == 0");
}
return open(offset).readClassData();
}
public Code readCode(ClassData.Method method) {
int offset = method.getCodeOffset();
if (offset == 0) {
throw new IllegalArgumentException("offset == 0");
}
return open(offset).readCode();
}
/**
* Returns the signature of all but the first 32 bytes of this dex. The
* first 32 bytes of dex files are not specified to be included in the
* signature.
*/
public byte[] computeSignature() throws IOException {
MessageDigest digest;
try {
digest = MessageDigest.getInstance("SHA-1");
} catch (NoSuchAlgorithmException e) {
throw new AssertionError();
}
byte[] buffer = new byte[8192];
ByteBuffer data = this.data.duplicate(); // positioned ByteBuffers aren't thread safe
data.limit(data.capacity());
data.position(SIGNATURE_OFFSET + SIGNATURE_SIZE);
while (data.hasRemaining()) {
int count = Math.min(buffer.length, data.remaining());
data.get(buffer, 0, count);
digest.update(buffer, 0, count);
}
return digest.digest();
}
/**
* Returns the checksum of all but the first 12 bytes of {@code dex}.
*/
public int computeChecksum() throws IOException {
Adler32 adler32 = new Adler32();
byte[] buffer = new byte[8192];
ByteBuffer data = this.data.duplicate(); // positioned ByteBuffers aren't thread safe
data.limit(data.capacity());
data.position(CHECKSUM_OFFSET + CHECKSUM_SIZE);
while (data.hasRemaining()) {
int count = Math.min(buffer.length, data.remaining());
data.get(buffer, 0, count);
adler32.update(buffer, 0, count);
}
return (int) adler32.getValue();
}
/**
* Generates the signature and checksum of the dex file {@code out} and
* writes them to the file.
*/
public void writeHashes() throws IOException {
open(SIGNATURE_OFFSET).write(computeSignature());
open(CHECKSUM_OFFSET).writeInt(computeChecksum());
}
/**
* Look up a descriptor index from a type index. Cheaper than:
* {@code open(tableOfContents.typeIds.off + (index * SizeOf.TYPE_ID_ITEM)).readInt();}
*/
public int descriptorIndexFromTypeIndex(int typeIndex) {
checkBounds(typeIndex, tableOfContents.typeIds.size);
int position = tableOfContents.typeIds.off + (SizeOf.TYPE_ID_ITEM * typeIndex);
return data.getInt(position);
}
public final class Section implements ByteInput, ByteOutput {
private final String name;
private final ByteBuffer data;
private final int initialPosition;
private Section(String name, ByteBuffer data) {
this.name = name;
this.data = data;
this.initialPosition = data.position();
}
public int getPosition() {
return data.position();
}
public int readInt() {
return data.getInt();
}
public short readShort() {
return data.getShort();
}
public int readUnsignedShort() {
return readShort() & 0xffff;
}
@Override
public byte readByte() {
return data.get();
}
public byte[] readByteArray(int length) {
byte[] result = new byte[length];
data.get(result);
return result;
}
public short[] readShortArray(int length) {
if (length == 0) {
return EMPTY_SHORT_ARRAY;
}
short[] result = new short[length];
for (int i = 0; i < length; i++) {
result[i] = readShort();
}
return result;
}
public int readUleb128() {
return Leb128.readUnsignedLeb128(this);
}
public int readUleb128p1() {
return Leb128.readUnsignedLeb128(this) - 1;
}
public int readSleb128() {
return Leb128.readSignedLeb128(this);
}
public void writeUleb128p1(int i) {
writeUleb128(i + 1);
}
public TypeList readTypeList() {
int size = readInt();
short[] types = readShortArray(size);
alignToFourBytes();
return new TypeList(Dex.this, types);
}
public String readString() {
int offset = readInt();
int savedPosition = data.position();
int savedLimit = data.limit();
data.position(offset);
data.limit(data.capacity());
try {
int expectedLength = readUleb128();
String result = Mutf8.decode(this, new char[expectedLength]);
if (result.length() != expectedLength) {
throw new DexException("Declared length " + expectedLength
+ " doesn't match decoded length of " + result.length());
}
return result;
} catch (UTFDataFormatException e) {
throw new DexException(e);
} finally {
data.position(savedPosition);
data.limit(savedLimit);
}
}
public FieldId readFieldId() {
int declaringClassIndex = readUnsignedShort();
int typeIndex = readUnsignedShort();
int nameIndex = readInt();
return new FieldId(Dex.this, declaringClassIndex, typeIndex, nameIndex);
}
public MethodId readMethodId() {
int declaringClassIndex = readUnsignedShort();
int protoIndex = readUnsignedShort();
int nameIndex = readInt();
return new MethodId(Dex.this, declaringClassIndex, protoIndex, nameIndex);
}
public ProtoId readProtoId() {
int shortyIndex = readInt();
int returnTypeIndex = readInt();
int parametersOffset = readInt();
return new ProtoId(Dex.this, shortyIndex, returnTypeIndex, parametersOffset);
}
public CallSiteId readCallSiteId() {
int offset = readInt();
return new CallSiteId(Dex.this, offset);
}
public MethodHandle readMethodHandle() {
MethodHandleType methodHandleType = MethodHandleType.fromValue(readUnsignedShort());
int unused1 = readUnsignedShort();
int fieldOrMethodId = readUnsignedShort();
int unused2 = readUnsignedShort();
return new MethodHandle(Dex.this, methodHandleType, unused1, fieldOrMethodId, unused2);
}
public ClassDef readClassDef() {
int offset = getPosition();
int type = readInt();
int accessFlags = readInt();
int supertype = readInt();
int interfacesOffset = readInt();
int sourceFileIndex = readInt();
int annotationsOffset = readInt();
int classDataOffset = readInt();
int staticValuesOffset = readInt();
return new ClassDef(Dex.this, offset, type, accessFlags, supertype,
interfacesOffset, sourceFileIndex, annotationsOffset, classDataOffset,
staticValuesOffset);
}
private Code readCode() {
int registersSize = readUnsignedShort();
int insSize = readUnsignedShort();
int outsSize = readUnsignedShort();
int triesSize = readUnsignedShort();
int debugInfoOffset = readInt();
int instructionsSize = readInt();
short[] instructions = readShortArray(instructionsSize);
Try[] tries;
CatchHandler[] catchHandlers;
if (triesSize > 0) {
if (instructions.length % 2 == 1) {
readShort(); // padding
}
/*
* We can't read the tries until we've read the catch handlers.
* Unfortunately they're in the opposite order in the dex file
* so we need to read them out-of-order.
*/
Section triesSection = open(data.position());
skip(triesSize * SizeOf.TRY_ITEM);
catchHandlers = readCatchHandlers();
tries = triesSection.readTries(triesSize, catchHandlers);
} else {
tries = new Try[0];
catchHandlers = new CatchHandler[0];
}
return new Code(registersSize, insSize, outsSize, debugInfoOffset, instructions,
tries, catchHandlers);
}
private CatchHandler[] readCatchHandlers() {
int baseOffset = data.position();
int catchHandlersSize = readUleb128();
CatchHandler[] result = new CatchHandler[catchHandlersSize];
for (int i = 0; i < catchHandlersSize; i++) {
int offset = data.position() - baseOffset;
result[i] = readCatchHandler(offset);
}
return result;
}
private Try[] readTries(int triesSize, CatchHandler[] catchHandlers) {
Try[] result = new Try[triesSize];
for (int i = 0; i < triesSize; i++) {
int startAddress = readInt();
int instructionCount = readUnsignedShort();
int handlerOffset = readUnsignedShort();
int catchHandlerIndex = findCatchHandlerIndex(catchHandlers, handlerOffset);
result[i] = new Try(startAddress, instructionCount, catchHandlerIndex);
}
return result;
}
private int findCatchHandlerIndex(CatchHandler[] catchHandlers, int offset) {
for (int i = 0; i < catchHandlers.length; i++) {
CatchHandler catchHandler = catchHandlers[i];
if (catchHandler.getOffset() == offset) {
return i;
}
}
throw new IllegalArgumentException();
}
private CatchHandler readCatchHandler(int offset) {
int size = readSleb128();
int handlersCount = Math.abs(size);
int[] typeIndexes = new int[handlersCount];
int[] addresses = new int[handlersCount];
for (int i = 0; i < handlersCount; i++) {
typeIndexes[i] = readUleb128();
addresses[i] = readUleb128();
}
int catchAllAddress = size <= 0 ? readUleb128() : -1;
return new CatchHandler(typeIndexes, addresses, catchAllAddress, offset);
}
private ClassData readClassData() {
int staticFieldsSize = readUleb128();
int instanceFieldsSize = readUleb128();
int directMethodsSize = readUleb128();
int virtualMethodsSize = readUleb128();
ClassData.Field[] staticFields = readFields(staticFieldsSize);
ClassData.Field[] instanceFields = readFields(instanceFieldsSize);
ClassData.Method[] directMethods = readMethods(directMethodsSize);
ClassData.Method[] virtualMethods = readMethods(virtualMethodsSize);
return new ClassData(staticFields, instanceFields, directMethods, virtualMethods);
}
private ClassData.Field[] readFields(int count) {
ClassData.Field[] result = new ClassData.Field[count];
int fieldIndex = 0;
for (int i = 0; i < count; i++) {
fieldIndex += readUleb128(); // field index diff
int accessFlags = readUleb128();
result[i] = new ClassData.Field(fieldIndex, accessFlags);
}
return result;
}
private ClassData.Method[] readMethods(int count) {
ClassData.Method[] result = new ClassData.Method[count];
int methodIndex = 0;
for (int i = 0; i < count; i++) {
methodIndex += readUleb128(); // method index diff
int accessFlags = readUleb128();
int codeOff = readUleb128();
result[i] = new ClassData.Method(methodIndex, accessFlags, codeOff);
}
return result;
}
/**
* Returns a byte array containing the bytes from {@code start} to this
* section's current position.
*/
private byte[] getBytesFrom(int start) {
int end = data.position();
byte[] result = new byte[end - start];
data.position(start);
data.get(result);
return result;
}
public Annotation readAnnotation() {
byte visibility = readByte();
int start = data.position();
new EncodedValueReader(this, EncodedValueReader.ENCODED_ANNOTATION).skipValue();
return new Annotation(Dex.this, visibility, new EncodedValue(getBytesFrom(start)));
}
public EncodedValue readEncodedArray() {
int start = data.position();
new EncodedValueReader(this, EncodedValueReader.ENCODED_ARRAY).skipValue();
return new EncodedValue(getBytesFrom(start));
}
public void skip(int count) {
if (count < 0) {
throw new IllegalArgumentException();
}
data.position(data.position() + count);
}
/**
* Skips bytes until the position is aligned to a multiple of 4.
*/
public void alignToFourBytes() {
data.position((data.position() + 3) & ~3);
}
/**
* Writes 0x00 until the position is aligned to a multiple of 4.
*/
public void alignToFourBytesWithZeroFill() {
while ((data.position() & 3) != 0) {
data.put((byte) 0);
}
}
public void assertFourByteAligned() {
if ((data.position() & 3) != 0) {
throw new IllegalStateException("Not four byte aligned!");
}
}
public void write(byte[] bytes) {
this.data.put(bytes);
}
@Override
public void writeByte(int b) {
data.put((byte) b);
}
public void writeShort(short i) {
data.putShort(i);
}
public void writeUnsignedShort(int i) {
short s = (short) i;
if (i != (s & 0xffff)) {
throw new IllegalArgumentException("Expected an unsigned short: " + i);
}
writeShort(s);
}
public void write(short[] shorts) {
for (short s : shorts) {
writeShort(s);
}
}
public void writeInt(int i) {
data.putInt(i);
}
public void writeUleb128(int i) {
try {
Leb128.writeUnsignedLeb128(this, i);
} catch (ArrayIndexOutOfBoundsException e) {
throw new DexException("Section limit " + data.limit() + " exceeded by " + name);
}
}
public void writeSleb128(int i) {
try {
Leb128.writeSignedLeb128(this, i);
} catch (ArrayIndexOutOfBoundsException e) {
throw new DexException("Section limit " + data.limit() + " exceeded by " + name);
}
}
public void writeStringData(String value) {
try {
int length = value.length();
writeUleb128(length);
write(Mutf8.encode(value));
writeByte(0);
} catch (UTFDataFormatException e) {
throw new AssertionError();
}
}
public void writeTypeList(TypeList typeList) {
short[] types = typeList.getTypes();
writeInt(types.length);
for (short type : types) {
writeShort(type);
}
alignToFourBytesWithZeroFill();
}
/**
* Returns the number of bytes used by this section.
*/
public int used() {
return data.position() - initialPosition;
}
}
private final class StringTable extends AbstractList<String> implements RandomAccess {
@Override
public String get(int index) {
checkBounds(index, tableOfContents.stringIds.size);
return open(tableOfContents.stringIds.off + (index * SizeOf.STRING_ID_ITEM))
.readString();
}
@Override
public int size() {
return tableOfContents.stringIds.size;
}
}
private final class TypeIndexToDescriptorIndexTable extends AbstractList<Integer>
implements RandomAccess {
@Override
public Integer get(int index) {
return descriptorIndexFromTypeIndex(index);
}
@Override
public int size() {
return tableOfContents.typeIds.size;
}
}
private final class TypeIndexToDescriptorTable extends AbstractList<String>
implements RandomAccess {
@Override
public String get(int index) {
return strings.get(descriptorIndexFromTypeIndex(index));
}
@Override
public int size() {
return tableOfContents.typeIds.size;
}
}
private final class ProtoIdTable extends AbstractList<ProtoId> implements RandomAccess {
@Override
public ProtoId get(int index) {
checkBounds(index, tableOfContents.protoIds.size);
return open(tableOfContents.protoIds.off + (SizeOf.PROTO_ID_ITEM * index))
.readProtoId();
}
@Override
public int size() {
return tableOfContents.protoIds.size;
}
}
private final class FieldIdTable extends AbstractList<FieldId> implements RandomAccess {
@Override
public FieldId get(int index) {
checkBounds(index, tableOfContents.fieldIds.size);
return open(tableOfContents.fieldIds.off + (SizeOf.MEMBER_ID_ITEM * index))
.readFieldId();
}
@Override
public int size() {
return tableOfContents.fieldIds.size;
}
}
private final class MethodIdTable extends AbstractList<MethodId> implements RandomAccess {
@Override
public MethodId get(int index) {
checkBounds(index, tableOfContents.methodIds.size);
return open(tableOfContents.methodIds.off + (SizeOf.MEMBER_ID_ITEM * index))
.readMethodId();
}
@Override
public int size() {
return tableOfContents.methodIds.size;
}
}
private final class ClassDefIterator implements Iterator<ClassDef> {
private final Dex.Section in = open(tableOfContents.classDefs.off);
private int count = 0;
@Override
public boolean hasNext() {
return count < tableOfContents.classDefs.size;
}
@Override
public ClassDef next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
count++;
return in.readClassDef();
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
}
private final class ClassDefIterable implements Iterable<ClassDef> {
@Override
public Iterator<ClassDef> iterator() {
return !tableOfContents.classDefs.exists()
? Collections.<ClassDef>emptySet().iterator()
: new ClassDefIterator();
}
}
}

View File

@ -1,33 +0,0 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package external.com.android.dex;
import external.com.android.dex.util.ExceptionWithContext;
/**
* Thrown when there's a format problem reading, writing, or generally
* processing a dex file.
*/
public class DexException extends ExceptionWithContext {
public DexException(String message) {
super(message);
}
public DexException(Throwable cause) {
super(cause);
}
}

View File

@ -1,170 +0,0 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package external.com.android.dex;
/**
* Constants that show up in and are otherwise related to {@code .dex}
* files, and helper methods for same.
*/
public final class DexFormat {
private DexFormat() {}
/** API level to target in order to generate const-method-handle and const-method-type */
public static final int API_CONST_METHOD_HANDLE = 28;
/** API level to target in order to generate invoke-polymorphic and invoke-custom */
public static final int API_METHOD_HANDLES = 26;
/** API level to target in order to define default and static interface methods */
public static final int API_DEFINE_INTERFACE_METHODS = 24;
/** API level to target in order to invoke default and static interface methods */
public static final int API_INVOKE_INTERFACE_METHODS = 24;
/** API level at which the invocation of static interface methods is permitted by dx.
* This value has been determined experimentally by testing on different VM versions. */
public static final int API_INVOKE_STATIC_INTERFACE_METHODS = 21;
/** API level to target in order to suppress extended opcode usage */
public static final int API_NO_EXTENDED_OPCODES = 13;
/**
* API level to target in order to produce the most modern file
* format
*/
public static final int API_CURRENT = API_CONST_METHOD_HANDLE;
/** dex file version number for API level 28 and earlier */
public static final String VERSION_FOR_API_28 = "039";
/** dex file version number for API level 26 and earlier */
public static final String VERSION_FOR_API_26 = "038";
/** dex file version number for API level 24 and earlier */
public static final String VERSION_FOR_API_24 = "037";
/** dex file version number for API level 13 and earlier */
public static final String VERSION_FOR_API_13 = "035";
/**
* Dex file version number for dalvik.
* <p>
* Note: Dex version 36 was loadable in some versions of Dalvik but was never fully supported or
* completed and is not considered a valid dex file format.
* </p>
*/
public static final String VERSION_CURRENT = VERSION_FOR_API_28;
/**
* file name of the primary {@code .dex} file inside an
* application or library {@code .jar} file
*/
public static final String DEX_IN_JAR_NAME = "classes.dex";
/** common prefix for all dex file "magic numbers" */
public static final String MAGIC_PREFIX = "dex\n";
/** common suffix for all dex file "magic numbers" */
public static final String MAGIC_SUFFIX = "\0";
/**
* value used to indicate endianness of file contents
*/
public static final int ENDIAN_TAG = 0x12345678;
/**
* Maximum addressable field or method index.
* The largest addressable member is 0xffff, in the "instruction formats" spec as field@CCCC or
* meth@CCCC.
*/
public static final int MAX_MEMBER_IDX = 0xFFFF;
/**
* Maximum addressable type index.
* The largest addressable type is 0xffff, in the "instruction formats" spec as type@CCCC.
*/
public static final int MAX_TYPE_IDX = 0xFFFF;
/**
* Returns the API level corresponding to the given magic number,
* or {@code -1} if the given array is not a well-formed dex file
* magic number.
*
* @param magic array of bytes containing DEX file magic string
* @return API level corresponding to magic string if valid, -1 otherwise.
*/
public static int magicToApi(byte[] magic) {
if (magic.length != 8) {
return -1;
}
if ((magic[0] != 'd') || (magic[1] != 'e') || (magic[2] != 'x') || (magic[3] != '\n') ||
(magic[7] != '\0')) {
return -1;
}
String version = "" + ((char) magic[4]) + ((char) magic[5]) +((char) magic[6]);
if (version.equals(VERSION_FOR_API_13)) {
return API_NO_EXTENDED_OPCODES;
} else if (version.equals(VERSION_FOR_API_24)) {
return API_DEFINE_INTERFACE_METHODS;
} else if (version.equals(VERSION_FOR_API_26)) {
return API_METHOD_HANDLES;
} else if (version.equals(VERSION_FOR_API_28)) {
return API_CONST_METHOD_HANDLE;
} else if (version.equals(VERSION_CURRENT)) {
return API_CURRENT;
}
return -1;
}
/**
* Returns the magic number corresponding to the given target API level.
*
* @param targetApiLevel level of API (minimum supported value 13).
* @return Magic string corresponding to API level supplied.
*/
public static String apiToMagic(int targetApiLevel) {
String version;
if (targetApiLevel >= API_CURRENT) {
version = VERSION_CURRENT;
} else if (targetApiLevel >= API_CONST_METHOD_HANDLE) {
version = VERSION_FOR_API_28;
} else if (targetApiLevel >= API_METHOD_HANDLES) {
version = VERSION_FOR_API_26;
} else if (targetApiLevel >= API_DEFINE_INTERFACE_METHODS) {
version = VERSION_FOR_API_24;
} else {
version = VERSION_FOR_API_13;
}
return MAGIC_PREFIX + version + MAGIC_SUFFIX;
}
/**
* Checks whether a DEX file magic string is supported.
* @param magic string from DEX file
* @return
*/
public static boolean isSupportedDexMagic(byte[] magic) {
int api = magicToApi(magic);
return api > 0;
}
}

View File

@ -1,30 +0,0 @@
/*
* Copyright (C) 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package external.com.android.dex;
/**
* Thrown when there's an index overflow writing a dex file.
*/
public final class DexIndexOverflowException extends DexException {
public DexIndexOverflowException(String message) {
super(message);
}
public DexIndexOverflowException(Throwable cause) {
super(cause);
}
}

View File

@ -1,59 +0,0 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package external.com.android.dex;
import external.com.android.dex.util.ByteArrayByteInput;
import external.com.android.dex.util.ByteInput;
/**
* An encoded value or array.
*/
public final class EncodedValue implements Comparable<EncodedValue> {
private final byte[] data;
public EncodedValue(byte[] data) {
this.data = data;
}
public ByteInput asByteInput() {
return new ByteArrayByteInput(data);
}
public byte[] getBytes() {
return data;
}
public void writeTo(Dex.Section out) {
out.write(data);
}
@Override
public int compareTo(EncodedValue other) {
int size = Math.min(data.length, other.data.length);
for (int i = 0; i < size; i++) {
if (data[i] != other.data[i]) {
return (data[i] & 0xff) - (other.data[i] & 0xff);
}
}
return data.length - other.data.length;
}
@Override
public String toString() {
return Integer.toHexString(data[0] & 0xff) + "...(" + data.length + ")";
}
}

View File

@ -1,187 +0,0 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package external.com.android.dex;
import external.com.android.dex.util.ByteInput;
import external.com.android.dex.util.ByteOutput;
/**
* Read and write {@code encoded_value} primitives.
*/
public final class EncodedValueCodec {
private EncodedValueCodec() {
}
/**
* Writes a signed integral to {@code out}.
*/
public static void writeSignedIntegralValue(ByteOutput out, int type, long value) {
/*
* Figure out how many bits are needed to represent the value,
* including a sign bit: The bit count is subtracted from 65
* and not 64 to account for the sign bit. The xor operation
* has the effect of leaving non-negative values alone and
* unary complementing negative values (so that a leading zero
* count always returns a useful number for our present
* purpose).
*/
int requiredBits = 65 - Long.numberOfLeadingZeros(value ^ (value >> 63));
// Round up the requiredBits to a number of bytes.
int requiredBytes = (requiredBits + 0x07) >> 3;
/*
* Write the header byte, which includes the type and
* requiredBytes - 1.
*/
out.writeByte(type | ((requiredBytes - 1) << 5));
// Write the value, per se.
while (requiredBytes > 0) {
out.writeByte((byte) value);
value >>= 8;
requiredBytes--;
}
}
/**
* Writes an unsigned integral to {@code out}.
*/
public static void writeUnsignedIntegralValue(ByteOutput out, int type, long value) {
// Figure out how many bits are needed to represent the value.
int requiredBits = 64 - Long.numberOfLeadingZeros(value);
if (requiredBits == 0) {
requiredBits = 1;
}
// Round up the requiredBits to a number of bytes.
int requiredBytes = (requiredBits + 0x07) >> 3;
/*
* Write the header byte, which includes the type and
* requiredBytes - 1.
*/
out.writeByte(type | ((requiredBytes - 1) << 5));
// Write the value, per se.
while (requiredBytes > 0) {
out.writeByte((byte) value);
value >>= 8;
requiredBytes--;
}
}
/**
* Writes a right-zero-extended value to {@code out}.
*/
public static void writeRightZeroExtendedValue(ByteOutput out, int type, long value) {
// Figure out how many bits are needed to represent the value.
int requiredBits = 64 - Long.numberOfTrailingZeros(value);
if (requiredBits == 0) {
requiredBits = 1;
}
// Round up the requiredBits to a number of bytes.
int requiredBytes = (requiredBits + 0x07) >> 3;
// Scootch the first bits to be written down to the low-order bits.
value >>= 64 - (requiredBytes * 8);
/*
* Write the header byte, which includes the type and
* requiredBytes - 1.
*/
out.writeByte(type | ((requiredBytes - 1) << 5));
// Write the value, per se.
while (requiredBytes > 0) {
out.writeByte((byte) value);
value >>= 8;
requiredBytes--;
}
}
/**
* Read a signed integer.
*
* @param zwidth byte count minus one
*/
public static int readSignedInt(ByteInput in, int zwidth) {
int result = 0;
for (int i = zwidth; i >= 0; i--) {
result = (result >>> 8) | ((in.readByte() & 0xff) << 24);
}
result >>= (3 - zwidth) * 8;
return result;
}
/**
* Read an unsigned integer.
*
* @param zwidth byte count minus one
* @param fillOnRight true to zero fill on the right; false on the left
*/
public static int readUnsignedInt(ByteInput in, int zwidth, boolean fillOnRight) {
int result = 0;
if (!fillOnRight) {
for (int i = zwidth; i >= 0; i--) {
result = (result >>> 8) | ((in.readByte() & 0xff) << 24);
}
result >>>= (3 - zwidth) * 8;
} else {
for (int i = zwidth; i >= 0; i--) {
result = (result >>> 8) | ((in.readByte() & 0xff) << 24);
}
}
return result;
}
/**
* Read a signed long.
*
* @param zwidth byte count minus one
*/
public static long readSignedLong(ByteInput in, int zwidth) {
long result = 0;
for (int i = zwidth; i >= 0; i--) {
result = (result >>> 8) | ((in.readByte() & 0xffL) << 56);
}
result >>= (7 - zwidth) * 8;
return result;
}
/**
* Read an unsigned long.
*
* @param zwidth byte count minus one
* @param fillOnRight true to zero fill on the right; false on the left
*/
public static long readUnsignedLong(ByteInput in, int zwidth, boolean fillOnRight) {
long result = 0;
if (!fillOnRight) {
for (int i = zwidth; i >= 0; i--) {
result = (result >>> 8) | ((in.readByte() & 0xffL) << 56);
}
result >>>= (7 - zwidth) * 8;
} else {
for (int i = zwidth; i >= 0; i--) {
result = (result >>> 8) | ((in.readByte() & 0xffL) << 56);
}
}
return result;
}
}

View File

@ -1,307 +0,0 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package external.com.android.dex;
import external.com.android.dex.util.ByteInput;
/**
* Pull parser for encoded values.
*/
public final class EncodedValueReader {
public static final int ENCODED_BYTE = 0x00;
public static final int ENCODED_SHORT = 0x02;
public static final int ENCODED_CHAR = 0x03;
public static final int ENCODED_INT = 0x04;
public static final int ENCODED_LONG = 0x06;
public static final int ENCODED_FLOAT = 0x10;
public static final int ENCODED_DOUBLE = 0x11;
public static final int ENCODED_METHOD_TYPE = 0x15;
public static final int ENCODED_METHOD_HANDLE = 0x16;
public static final int ENCODED_STRING = 0x17;
public static final int ENCODED_TYPE = 0x18;
public static final int ENCODED_FIELD = 0x19;
public static final int ENCODED_ENUM = 0x1b;
public static final int ENCODED_METHOD = 0x1a;
public static final int ENCODED_ARRAY = 0x1c;
public static final int ENCODED_ANNOTATION = 0x1d;
public static final int ENCODED_NULL = 0x1e;
public static final int ENCODED_BOOLEAN = 0x1f;
/** placeholder type if the type is not yet known */
private static final int MUST_READ = -1;
protected final ByteInput in;
private int type = MUST_READ;
private int annotationType;
private int arg;
public EncodedValueReader(ByteInput in) {
this.in = in;
}
public EncodedValueReader(EncodedValue in) {
this(in.asByteInput());
}
/**
* Creates a new encoded value reader whose only value is the specified
* known type. This is useful for encoded values without a type prefix,
* such as class_def_item's encoded_array or annotation_item's
* encoded_annotation.
*/
public EncodedValueReader(ByteInput in, int knownType) {
this.in = in;
this.type = knownType;
}
public EncodedValueReader(EncodedValue in, int knownType) {
this(in.asByteInput(), knownType);
}
/**
* Returns the type of the next value to read.
*/
public int peek() {
if (type == MUST_READ) {
int argAndType = in.readByte() & 0xff;
type = argAndType & 0x1f;
arg = (argAndType & 0xe0) >> 5;
}
return type;
}
/**
* Begins reading the elements of an array, returning the array's size. The
* caller must follow up by calling a read method for each element in the
* array. For example, this reads a byte array: <pre> {@code
* int arraySize = readArray();
* for (int i = 0, i < arraySize; i++) {
* readByte();
* }
* }</pre>
*/
public int readArray() {
checkType(ENCODED_ARRAY);
type = MUST_READ;
return Leb128.readUnsignedLeb128(in);
}
/**
* Begins reading the fields of an annotation, returning the number of
* fields. The caller must follow up by making alternating calls to {@link
* #readAnnotationName()} and another read method. For example, this reads
* an annotation whose fields are all bytes: <pre> {@code
* int fieldCount = readAnnotation();
* int annotationType = getAnnotationType();
* for (int i = 0; i < fieldCount; i++) {
* readAnnotationName();
* readByte();
* }
* }</pre>
*/
public int readAnnotation() {
checkType(ENCODED_ANNOTATION);
type = MUST_READ;
annotationType = Leb128.readUnsignedLeb128(in);
return Leb128.readUnsignedLeb128(in);
}
/**
* Returns the type of the annotation just returned by {@link
* #readAnnotation()}. This method's value is undefined unless the most
* recent call was to {@link #readAnnotation()}.
*/
public int getAnnotationType() {
return annotationType;
}
public int readAnnotationName() {
return Leb128.readUnsignedLeb128(in);
}
public byte readByte() {
checkType(ENCODED_BYTE);
type = MUST_READ;
return (byte) EncodedValueCodec.readSignedInt(in, arg);
}
public short readShort() {
checkType(ENCODED_SHORT);
type = MUST_READ;
return (short) EncodedValueCodec.readSignedInt(in, arg);
}
public char readChar() {
checkType(ENCODED_CHAR);
type = MUST_READ;
return (char) EncodedValueCodec.readUnsignedInt(in, arg, false);
}
public int readInt() {
checkType(ENCODED_INT);
type = MUST_READ;
return EncodedValueCodec.readSignedInt(in, arg);
}
public long readLong() {
checkType(ENCODED_LONG);
type = MUST_READ;
return EncodedValueCodec.readSignedLong(in, arg);
}
public float readFloat() {
checkType(ENCODED_FLOAT);
type = MUST_READ;
return Float.intBitsToFloat(EncodedValueCodec.readUnsignedInt(in, arg, true));
}
public double readDouble() {
checkType(ENCODED_DOUBLE);
type = MUST_READ;
return Double.longBitsToDouble(EncodedValueCodec.readUnsignedLong(in, arg, true));
}
public int readMethodType() {
checkType(ENCODED_METHOD_TYPE);
type = MUST_READ;
return EncodedValueCodec.readUnsignedInt(in, arg, false);
}
public int readMethodHandle() {
checkType(ENCODED_METHOD_HANDLE);
type = MUST_READ;
return EncodedValueCodec.readUnsignedInt(in, arg, false);
}
public int readString() {
checkType(ENCODED_STRING);
type = MUST_READ;
return EncodedValueCodec.readUnsignedInt(in, arg, false);
}
public int readType() {
checkType(ENCODED_TYPE);
type = MUST_READ;
return EncodedValueCodec.readUnsignedInt(in, arg, false);
}
public int readField() {
checkType(ENCODED_FIELD);
type = MUST_READ;
return EncodedValueCodec.readUnsignedInt(in, arg, false);
}
public int readEnum() {
checkType(ENCODED_ENUM);
type = MUST_READ;
return EncodedValueCodec.readUnsignedInt(in, arg, false);
}
public int readMethod() {
checkType(ENCODED_METHOD);
type = MUST_READ;
return EncodedValueCodec.readUnsignedInt(in, arg, false);
}
public void readNull() {
checkType(ENCODED_NULL);
type = MUST_READ;
}
public boolean readBoolean() {
checkType(ENCODED_BOOLEAN);
type = MUST_READ;
return arg != 0;
}
/**
* Skips a single value, including its nested values if it is an array or
* annotation.
*/
public void skipValue() {
switch (peek()) {
case ENCODED_BYTE:
readByte();
break;
case ENCODED_SHORT:
readShort();
break;
case ENCODED_CHAR:
readChar();
break;
case ENCODED_INT:
readInt();
break;
case ENCODED_LONG:
readLong();
break;
case ENCODED_FLOAT:
readFloat();
break;
case ENCODED_DOUBLE:
readDouble();
break;
case ENCODED_METHOD_TYPE:
readMethodType();
break;
case ENCODED_METHOD_HANDLE:
readMethodHandle();
break;
case ENCODED_STRING:
readString();
break;
case ENCODED_TYPE:
readType();
break;
case ENCODED_FIELD:
readField();
break;
case ENCODED_ENUM:
readEnum();
break;
case ENCODED_METHOD:
readMethod();
break;
case ENCODED_ARRAY:
for (int i = 0, size = readArray(); i < size; i++) {
skipValue();
}
break;
case ENCODED_ANNOTATION:
for (int i = 0, size = readAnnotation(); i < size; i++) {
readAnnotationName();
skipValue();
}
break;
case ENCODED_NULL:
readNull();
break;
case ENCODED_BOOLEAN:
readBoolean();
break;
default:
throw new DexException("Unexpected type: " + Integer.toHexString(type));
}
}
private void checkType(int expected) {
if (peek() != expected) {
throw new IllegalStateException(
String.format("Expected %x but was %x", expected, peek()));
}
}
}

View File

@ -1,70 +0,0 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package external.com.android.dex;
import external.com.android.dex.util.Unsigned;
public final class FieldId implements Comparable<FieldId> {
private final Dex dex;
private final int declaringClassIndex;
private final int typeIndex;
private final int nameIndex;
public FieldId(Dex dex, int declaringClassIndex, int typeIndex, int nameIndex) {
this.dex = dex;
this.declaringClassIndex = declaringClassIndex;
this.typeIndex = typeIndex;
this.nameIndex = nameIndex;
}
public int getDeclaringClassIndex() {
return declaringClassIndex;
}
public int getTypeIndex() {
return typeIndex;
}
public int getNameIndex() {
return nameIndex;
}
@Override
public int compareTo(FieldId other) {
if (declaringClassIndex != other.declaringClassIndex) {
return Unsigned.compare(declaringClassIndex, other.declaringClassIndex);
}
if (nameIndex != other.nameIndex) {
return Unsigned.compare(nameIndex, other.nameIndex);
}
return Unsigned.compare(typeIndex, other.typeIndex); // should always be 0
}
public void writeTo(Dex.Section out) {
out.writeUnsignedShort(declaringClassIndex);
out.writeUnsignedShort(typeIndex);
out.writeInt(nameIndex);
}
@Override
public String toString() {
if (dex == null) {
return declaringClassIndex + " " + typeIndex + " " + nameIndex;
}
return dex.typeNames().get(typeIndex) + "." + dex.strings().get(nameIndex);
}
}

View File

@ -1,134 +0,0 @@
/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package external.com.android.dex;
import external.com.android.dex.util.ByteInput;
import external.com.android.dex.util.ByteOutput;
/**
* Reads and writes DWARFv3 LEB 128 signed and unsigned integers. See DWARF v3
* section 7.6.
*/
public final class Leb128 {
private Leb128() {
}
/**
* Gets the number of bytes in the unsigned LEB128 encoding of the
* given value.
*
* @param value the value in question
* @return its write size, in bytes
*/
public static int unsignedLeb128Size(int value) {
// TODO: This could be much cleverer.
int remaining = value >> 7;
int count = 0;
while (remaining != 0) {
remaining >>= 7;
count++;
}
return count + 1;
}
/**
* Reads an signed integer from {@code in}.
*/
public static int readSignedLeb128(ByteInput in) {
int result = 0;
int cur;
int count = 0;
int signBits = -1;
do {
cur = in.readByte() & 0xff;
result |= (cur & 0x7f) << (count * 7);
signBits <<= 7;
count++;
} while (((cur & 0x80) == 0x80) && count < 5);
if ((cur & 0x80) == 0x80) {
throw new DexException("invalid LEB128 sequence");
}
// Sign extend if appropriate
if (((signBits >> 1) & result) != 0 ) {
result |= signBits;
}
return result;
}
/**
* Reads an unsigned integer from {@code in}.
*/
public static int readUnsignedLeb128(ByteInput in) {
int result = 0;
int cur;
int count = 0;
do {
cur = in.readByte() & 0xff;
result |= (cur & 0x7f) << (count * 7);
count++;
} while (((cur & 0x80) == 0x80) && count < 5);
if ((cur & 0x80) == 0x80) {
throw new DexException("invalid LEB128 sequence");
}
return result;
}
/**
* Writes {@code value} as an unsigned integer to {@code out}, starting at
* {@code offset}. Returns the number of bytes written.
*/
public static void writeUnsignedLeb128(ByteOutput out, int value) {
int remaining = value >>> 7;
while (remaining != 0) {
out.writeByte((byte) ((value & 0x7f) | 0x80));
value = remaining;
remaining >>>= 7;
}
out.writeByte((byte) (value & 0x7f));
}
/**
* Writes {@code value} as a signed integer to {@code out}, starting at
* {@code offset}. Returns the number of bytes written.
*/
public static void writeSignedLeb128(ByteOutput out, int value) {
int remaining = value >> 7;
boolean hasMore = true;
int end = ((value & Integer.MIN_VALUE) == 0) ? 0 : -1;
while (hasMore) {
hasMore = (remaining != end)
|| ((remaining & 1) != ((value >> 6) & 1));
out.writeByte((byte) ((value & 0x7f) | (hasMore ? 0x80 : 0)));
value = remaining;
remaining >>= 7;
}
}
}

View File

@ -1,132 +0,0 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package external.com.android.dex;
import external.com.android.dex.Dex.Section;
import external.com.android.dex.util.Unsigned;
/**
* A method_handle_item:
* https://source.android.com/devices/tech/dalvik/dex-format#method-handle-item
*/
public class MethodHandle implements Comparable<MethodHandle> {
/**
* A method handle type code:
* https://source.android.com/devices/tech/dalvik/dex-format#method-handle-type-codes
*/
public enum MethodHandleType {
METHOD_HANDLE_TYPE_STATIC_PUT(0x00),
METHOD_HANDLE_TYPE_STATIC_GET(0x01),
METHOD_HANDLE_TYPE_INSTANCE_PUT(0x02),
METHOD_HANDLE_TYPE_INSTANCE_GET(0x03),
METHOD_HANDLE_TYPE_INVOKE_STATIC(0x04),
METHOD_HANDLE_TYPE_INVOKE_INSTANCE(0x05),
METHOD_HANDLE_TYPE_INVOKE_DIRECT(0x06),
METHOD_HANDLE_TYPE_INVOKE_CONSTRUCTOR(0x07),
METHOD_HANDLE_TYPE_INVOKE_INTERFACE(0x08);
private final int value;
MethodHandleType(int value) {
this.value = value;
}
static MethodHandleType fromValue(int value) {
for (MethodHandleType methodHandleType : values()) {
if (methodHandleType.value == value) {
return methodHandleType;
}
}
throw new IllegalArgumentException(String.valueOf(value));
}
public boolean isField() {
switch (this) {
case METHOD_HANDLE_TYPE_STATIC_PUT:
case METHOD_HANDLE_TYPE_STATIC_GET:
case METHOD_HANDLE_TYPE_INSTANCE_PUT:
case METHOD_HANDLE_TYPE_INSTANCE_GET:
return true;
default:
return false;
}
}
}
private final Dex dex;
private final MethodHandleType methodHandleType;
private final int unused1;
private final int fieldOrMethodId;
private final int unused2;
public MethodHandle(
Dex dex,
MethodHandleType methodHandleType,
int unused1,
int fieldOrMethodId,
int unused2) {
this.dex = dex;
this.methodHandleType = methodHandleType;
this.unused1 = unused1;
this.fieldOrMethodId = fieldOrMethodId;
this.unused2 = unused2;
}
@Override
public int compareTo(MethodHandle o) {
if (methodHandleType != o.methodHandleType) {
return methodHandleType.compareTo(o.methodHandleType);
}
return Unsigned.compare(fieldOrMethodId, o.fieldOrMethodId);
}
public MethodHandleType getMethodHandleType() {
return methodHandleType;
}
public int getUnused1() {
return unused1;
}
public int getFieldOrMethodId() {
return fieldOrMethodId;
}
public int getUnused2() {
return unused2;
}
public void writeTo(Section out) {
out.writeUnsignedShort(methodHandleType.value);
out.writeUnsignedShort(unused1);
out.writeUnsignedShort(fieldOrMethodId);
out.writeUnsignedShort(unused2);
}
@Override
public String toString() {
if (dex == null) {
return methodHandleType + " " + fieldOrMethodId;
}
return methodHandleType
+ " "
+ (methodHandleType.isField()
? dex.fieldIds().get(fieldOrMethodId)
: dex.methodIds().get(fieldOrMethodId));
}
}

View File

@ -1,72 +0,0 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package external.com.android.dex;
import external.com.android.dex.util.Unsigned;
public final class MethodId implements Comparable<MethodId> {
private final Dex dex;
private final int declaringClassIndex;
private final int protoIndex;
private final int nameIndex;
public MethodId(Dex dex, int declaringClassIndex, int protoIndex, int nameIndex) {
this.dex = dex;
this.declaringClassIndex = declaringClassIndex;
this.protoIndex = protoIndex;
this.nameIndex = nameIndex;
}
public int getDeclaringClassIndex() {
return declaringClassIndex;
}
public int getProtoIndex() {
return protoIndex;
}
public int getNameIndex() {
return nameIndex;
}
@Override
public int compareTo(MethodId other) {
if (declaringClassIndex != other.declaringClassIndex) {
return Unsigned.compare(declaringClassIndex, other.declaringClassIndex);
}
if (nameIndex != other.nameIndex) {
return Unsigned.compare(nameIndex, other.nameIndex);
}
return Unsigned.compare(protoIndex, other.protoIndex);
}
public void writeTo(Dex.Section out) {
out.writeUnsignedShort(declaringClassIndex);
out.writeUnsignedShort(protoIndex);
out.writeInt(nameIndex);
}
@Override
public String toString() {
if (dex == null) {
return declaringClassIndex + " " + protoIndex + " " + nameIndex;
}
return dex.typeNames().get(declaringClassIndex)
+ "." + dex.strings().get(nameIndex)
+ dex.readTypeList(dex.protoIds().get(protoIndex).getParametersOffset());
}
}

View File

@ -1,115 +0,0 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package external.com.android.dex;
import external.com.android.dex.util.ByteInput;
import java.io.UTFDataFormatException;
/**
* Modified UTF-8 as described in the dex file format spec.
*
* <p>Derived from libcore's MUTF-8 encoder at java.nio.charset.ModifiedUtf8.
*/
public final class Mutf8 {
private Mutf8() {}
/**
* Decodes bytes from {@code in} into {@code out} until a delimiter 0x00 is
* encountered. Returns a new string containing the decoded characters.
*/
public static String decode(ByteInput in, char[] out) throws UTFDataFormatException {
int s = 0;
while (true) {
char a = (char) (in.readByte() & 0xff);
if (a == 0) {
return new String(out, 0, s);
}
out[s] = a;
if (a < '\u0080') {
s++;
} else if ((a & 0xe0) == 0xc0) {
int b = in.readByte() & 0xff;
if ((b & 0xC0) != 0x80) {
throw new UTFDataFormatException("bad second byte");
}
out[s++] = (char) (((a & 0x1F) << 6) | (b & 0x3F));
} else if ((a & 0xf0) == 0xe0) {
int b = in.readByte() & 0xff;
int c = in.readByte() & 0xff;
if (((b & 0xC0) != 0x80) || ((c & 0xC0) != 0x80)) {
throw new UTFDataFormatException("bad second or third byte");
}
out[s++] = (char) (((a & 0x0F) << 12) | ((b & 0x3F) << 6) | (c & 0x3F));
} else {
throw new UTFDataFormatException("bad byte");
}
}
}
/**
* Returns the number of bytes the modified UTF8 representation of 's' would take.
*/
private static long countBytes(String s, boolean shortLength) throws UTFDataFormatException {
long result = 0;
final int length = s.length();
for (int i = 0; i < length; ++i) {
char ch = s.charAt(i);
if (ch != 0 && ch <= 127) { // U+0000 uses two bytes.
++result;
} else if (ch <= 2047) {
result += 2;
} else {
result += 3;
}
if (shortLength && result > 65535) {
throw new UTFDataFormatException("String more than 65535 UTF bytes long");
}
}
return result;
}
/**
* Encodes the modified UTF-8 bytes corresponding to {@code s} into {@code
* dst}, starting at {@code offset}.
*/
public static void encode(byte[] dst, int offset, String s) {
final int length = s.length();
for (int i = 0; i < length; i++) {
char ch = s.charAt(i);
if (ch != 0 && ch <= 127) { // U+0000 uses two bytes.
dst[offset++] = (byte) ch;
} else if (ch <= 2047) {
dst[offset++] = (byte) (0xc0 | (0x1f & (ch >> 6)));
dst[offset++] = (byte) (0x80 | (0x3f & ch));
} else {
dst[offset++] = (byte) (0xe0 | (0x0f & (ch >> 12)));
dst[offset++] = (byte) (0x80 | (0x3f & (ch >> 6)));
dst[offset++] = (byte) (0x80 | (0x3f & ch));
}
}
}
/**
* Returns an array containing the <i>modified UTF-8</i> form of {@code s}.
*/
public static byte[] encode(String s) throws UTFDataFormatException {
int utfCount = (int) countBytes(s, true);
byte[] result = new byte[utfCount];
encode(result, 0, s);
return result;
}
}

View File

@ -1,70 +0,0 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package external.com.android.dex;
import external.com.android.dex.util.Unsigned;
public final class ProtoId implements Comparable<ProtoId> {
private final Dex dex;
private final int shortyIndex;
private final int returnTypeIndex;
private final int parametersOffset;
public ProtoId(Dex dex, int shortyIndex, int returnTypeIndex, int parametersOffset) {
this.dex = dex;
this.shortyIndex = shortyIndex;
this.returnTypeIndex = returnTypeIndex;
this.parametersOffset = parametersOffset;
}
@Override
public int compareTo(ProtoId other) {
if (returnTypeIndex != other.returnTypeIndex) {
return Unsigned.compare(returnTypeIndex, other.returnTypeIndex);
}
return Unsigned.compare(parametersOffset, other.parametersOffset);
}
public int getShortyIndex() {
return shortyIndex;
}
public int getReturnTypeIndex() {
return returnTypeIndex;
}
public int getParametersOffset() {
return parametersOffset;
}
public void writeTo(Dex.Section out) {
out.writeInt(shortyIndex);
out.writeInt(returnTypeIndex);
out.writeInt(parametersOffset);
}
@Override
public String toString() {
if (dex == null) {
return shortyIndex + " " + returnTypeIndex + " " + parametersOffset;
}
return dex.strings().get(shortyIndex)
+ ": " + dex.typeNames().get(returnTypeIndex)
+ " " + dex.readTypeList(parametersOffset);
}
}

View File

@ -1,123 +0,0 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package external.com.android.dex;
public final class SizeOf {
private SizeOf() {}
public static final int UBYTE = 1;
public static final int USHORT = 2;
public static final int UINT = 4;
public static final int SIGNATURE = UBYTE * 20;
/**
* magic ubyte[8]
* checksum uint
* signature ubyte[20]
* file_size uint
* header_size uint
* endian_tag uint
* link_size uint
* link_off uint
* map_off uint
* string_ids_size uint
* string_ids_off uint
* type_ids_size uint
* type_ids_off uint
* proto_ids_size uint
* proto_ids_off uint
* field_ids_size uint
* field_ids_off uint
* method_ids_size uint
* method_ids_off uint
* class_defs_size uint
* class_defs_off uint
* data_size uint
* data_off uint
*/
public static final int HEADER_ITEM = (8 * UBYTE) + UINT + SIGNATURE + (20 * UINT); // 0x70
/**
* string_data_off uint
*/
public static final int STRING_ID_ITEM = UINT;
/**
* descriptor_idx uint
*/
public static final int TYPE_ID_ITEM = UINT;
/**
* type_idx ushort
*/
public static final int TYPE_ITEM = USHORT;
/**
* shorty_idx uint
* return_type_idx uint
* return_type_idx uint
*/
public static final int PROTO_ID_ITEM = UINT + UINT + UINT;
/**
* class_idx ushort
* type_idx/proto_idx ushort
* name_idx uint
*/
public static final int MEMBER_ID_ITEM = USHORT + USHORT + UINT;
/**
* class_idx uint
* access_flags uint
* superclass_idx uint
* interfaces_off uint
* source_file_idx uint
* annotations_off uint
* class_data_off uint
* static_values_off uint
*/
public static final int CLASS_DEF_ITEM = 8 * UINT;
/**
* type ushort
* unused ushort
* size uint
* offset uint
*/
public static final int MAP_ITEM = USHORT + USHORT + UINT + UINT;
/**
* start_addr uint
* insn_count ushort
* handler_off ushort
*/
public static final int TRY_ITEM = UINT + USHORT + USHORT;
/**
* call_site_off uint
*/
public static final int CALL_SITE_ID_ITEM = UINT;
/**
* method_handle_type ushort
* unused ushort
* field_or_method_id ushort
* unused ushort
*/
public static final int METHOD_HANDLE_ITEM = USHORT + USHORT + USHORT + USHORT;
}

View File

@ -1,246 +0,0 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package external.com.android.dex;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
/**
* The file header and map.
*/
public final class TableOfContents {
/*
* TODO: factor out ID constants.
*/
public final Section header = new Section(0x0000);
public final Section stringIds = new Section(0x0001);
public final Section typeIds = new Section(0x0002);
public final Section protoIds = new Section(0x0003);
public final Section fieldIds = new Section(0x0004);
public final Section methodIds = new Section(0x0005);
public final Section classDefs = new Section(0x0006);
public final Section callSiteIds = new Section(0x0007);
public final Section methodHandles = new Section(0x0008);
public final Section mapList = new Section(0x1000);
public final Section typeLists = new Section(0x1001);
public final Section annotationSetRefLists = new Section(0x1002);
public final Section annotationSets = new Section(0x1003);
public final Section classDatas = new Section(0x2000);
public final Section codes = new Section(0x2001);
public final Section stringDatas = new Section(0x2002);
public final Section debugInfos = new Section(0x2003);
public final Section annotations = new Section(0x2004);
public final Section encodedArrays = new Section(0x2005);
public final Section annotationsDirectories = new Section(0x2006);
public final Section[] sections = {
header, stringIds, typeIds, protoIds, fieldIds, methodIds, classDefs, mapList, callSiteIds,
methodHandles, typeLists, annotationSetRefLists, annotationSets, classDatas, codes,
stringDatas, debugInfos, annotations, encodedArrays, annotationsDirectories
};
public int apiLevel;
public int checksum;
public byte[] signature;
public int fileSize;
public int linkSize;
public int linkOff;
public int dataSize;
public int dataOff;
public TableOfContents() {
signature = new byte[20];
}
public void readFrom(Dex dex) throws IOException {
readHeader(dex.open(0));
readMap(dex.open(mapList.off));
computeSizesFromOffsets();
}
private void readHeader(Dex.Section headerIn) throws UnsupportedEncodingException {
byte[] magic = headerIn.readByteArray(8);
if (!DexFormat.isSupportedDexMagic(magic)) {
String msg =
String.format("Unexpected magic: [0x%02x, 0x%02x, 0x%02x, 0x%02x, "
+ "0x%02x, 0x%02x, 0x%02x, 0x%02x]",
magic[0], magic[1], magic[2], magic[3],
magic[4], magic[5], magic[6], magic[7]);
throw new DexException(msg);
}
apiLevel = DexFormat.magicToApi(magic);
checksum = headerIn.readInt();
signature = headerIn.readByteArray(20);
fileSize = headerIn.readInt();
int headerSize = headerIn.readInt();
if (headerSize != SizeOf.HEADER_ITEM) {
throw new DexException("Unexpected header: 0x" + Integer.toHexString(headerSize));
}
int endianTag = headerIn.readInt();
if (endianTag != DexFormat.ENDIAN_TAG) {
throw new DexException("Unexpected endian tag: 0x" + Integer.toHexString(endianTag));
}
linkSize = headerIn.readInt();
linkOff = headerIn.readInt();
mapList.off = headerIn.readInt();
if (mapList.off == 0) {
throw new DexException("Cannot merge dex files that do not contain a map");
}
stringIds.size = headerIn.readInt();
stringIds.off = headerIn.readInt();
typeIds.size = headerIn.readInt();
typeIds.off = headerIn.readInt();
protoIds.size = headerIn.readInt();
protoIds.off = headerIn.readInt();
fieldIds.size = headerIn.readInt();
fieldIds.off = headerIn.readInt();
methodIds.size = headerIn.readInt();
methodIds.off = headerIn.readInt();
classDefs.size = headerIn.readInt();
classDefs.off = headerIn.readInt();
dataSize = headerIn.readInt();
dataOff = headerIn.readInt();
}
private void readMap(Dex.Section in) throws IOException {
int mapSize = in.readInt();
Section previous = null;
for (int i = 0; i < mapSize; i++) {
short type = in.readShort();
in.readShort(); // unused
Section section = getSection(type);
int size = in.readInt();
int offset = in.readInt();
if ((section.size != 0 && section.size != size)
|| (section.off != -1 && section.off != offset)) {
throw new DexException("Unexpected map value for 0x" + Integer.toHexString(type));
}
section.size = size;
section.off = offset;
if (previous != null && previous.off > section.off) {
throw new DexException("Map is unsorted at " + previous + ", " + section);
}
previous = section;
}
Arrays.sort(sections);
}
public void computeSizesFromOffsets() {
int end = dataOff + dataSize;
for (int i = sections.length - 1; i >= 0; i--) {
Section section = sections[i];
if (section.off == -1) {
continue;
}
if (section.off > end) {
throw new DexException("Map is unsorted at " + section);
}
section.byteCount = end - section.off;
end = section.off;
}
}
private Section getSection(short type) {
for (Section section : sections) {
if (section.type == type) {
return section;
}
}
throw new IllegalArgumentException("No such map item: " + type);
}
public void writeHeader(Dex.Section out, int api) throws IOException {
out.write(DexFormat.apiToMagic(api).getBytes("UTF-8"));
out.writeInt(checksum);
out.write(signature);
out.writeInt(fileSize);
out.writeInt(SizeOf.HEADER_ITEM);
out.writeInt(DexFormat.ENDIAN_TAG);
out.writeInt(linkSize);
out.writeInt(linkOff);
out.writeInt(mapList.off);
out.writeInt(stringIds.size);
out.writeInt(stringIds.off);
out.writeInt(typeIds.size);
out.writeInt(typeIds.off);
out.writeInt(protoIds.size);
out.writeInt(protoIds.off);
out.writeInt(fieldIds.size);
out.writeInt(fieldIds.off);
out.writeInt(methodIds.size);
out.writeInt(methodIds.off);
out.writeInt(classDefs.size);
out.writeInt(classDefs.off);
out.writeInt(dataSize);
out.writeInt(dataOff);
}
public void writeMap(Dex.Section out) throws IOException {
int count = 0;
for (Section section : sections) {
if (section.exists()) {
count++;
}
}
out.writeInt(count);
for (Section section : sections) {
if (section.exists()) {
out.writeShort(section.type);
out.writeShort((short) 0);
out.writeInt(section.size);
out.writeInt(section.off);
}
}
}
public static class Section implements Comparable<Section> {
public final short type;
public int size = 0;
public int off = -1;
public int byteCount = 0;
public Section(int type) {
this.type = (short) type;
}
public boolean exists() {
return size > 0;
}
@Override
public int compareTo(Section section) {
if (off != section.off) {
return off < section.off ? -1 : 1;
}
return 0;
}
@Override
public String toString() {
return String.format("Section[type=%#x,off=%#x,size=%#x]", type, off, size);
}
}
}

View File

@ -1,57 +0,0 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package external.com.android.dex;
import external.com.android.dex.util.Unsigned;
public final class TypeList implements Comparable<TypeList> {
public static final TypeList EMPTY = new TypeList(null, Dex.EMPTY_SHORT_ARRAY);
private final Dex dex;
private final short[] types;
public TypeList(Dex dex, short[] types) {
this.dex = dex;
this.types = types;
}
public short[] getTypes() {
return types;
}
@Override
public int compareTo(TypeList other) {
for (int i = 0; i < types.length && i < other.types.length; i++) {
if (types[i] != other.types[i]) {
return Unsigned.compare(types[i], other.types[i]);
}
}
return Unsigned.compare(types.length, other.types.length);
}
@Override
public String toString() {
StringBuilder result = new StringBuilder();
result.append("(");
for (int i = 0, typesLength = types.length; i < typesLength; i++) {
result.append(dex != null ? dex.typeNames().get(types[i]) : types[i]);
}
result.append(")");
return result.toString();
}
}

Some files were not shown because too many files have changed in this diff Show More