[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() {
try {
return LSPosedManagerServiceClient.isPermissive();

View File

@ -115,16 +115,6 @@ public class LSPosedManagerServiceClient {
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 {
ensureService();
return service.isPermissive();

View File

@ -92,7 +92,7 @@ public class MainActivity extends BaseActivity {
String installXposedVersion = ConfigManager.getXposedVersionName();
int cardBackgroundColor;
if (installXposedVersion != null) {
binding.statusTitle.setText(getString(R.string.Activated, ConfigManager.getVariantString()));
binding.statusTitle.setText(getString(R.string.Activated, "YAHFA"));
if (!ConfigManager.isPermissive()) {
if (Helpers.currentHoliday == Helpers.Holidays.LUNARNEWYEAR) {
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 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 androidMinSdkVersion by extra(27)
val androidBuildToolsVersion by extra("30.0.3")

View File

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

View File

@ -5,8 +5,6 @@ interface ILSPApplicationService {
IBinder requestManagerBinder() = 3;
int getVariant() = 4;
boolean isResourcesHookEnabled() = 5;
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/builtin-plugin/BionicLinkerRestriction)
add_subdirectory(SandHook)
target_include_directories(sandhook.lspd PUBLIC SandHook)
add_subdirectory(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_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) \
env->GetMethodID(class, name, sig); \
if (ClearException(env)) LOGE("GetMethodID " #name)
//if (ClearException(env)) LOGE("GetMethodID " #name)
#define JNI_CallObjectMethod(env, obj, ...) \
env->CallObjectMethod(obj, __VA_ARGS__); \

View File

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

View File

@ -44,7 +44,7 @@ namespace lspd {
static JNINativeMethod gMethods[] = {
LSP_NATIVE_METHOD(ClassLinker, setEntryPointsToInterpreter,
"(Ljava/lang/reflect/Member;)V")
"(Ljava/lang/reflect/Executable;)V")
};
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[] = {
LSP_NATIVE_METHOD(Yahfa, init, "(I)V"),
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,
"(Ljava/lang/Object;Ljava/lang/reflect/Method;Ljava/lang/reflect/Method;)Z"),
LSP_NATIVE_METHOD(Yahfa, recordHooked, "(Ljava/lang/reflect/Member;)V"),
LSP_NATIVE_METHOD(Yahfa, isHooked, "(Ljava/lang/reflect/Member;)Z"),
"(Ljava/lang/reflect/Executable;Ljava/lang/reflect/Method;Ljava/lang/reflect/Method;)Z"),
LSP_NATIVE_METHOD(Yahfa, recordHooked, "(Ljava/lang/reflect/Executable;)V"),
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;"),
};

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;
import java.lang.reflect.Executable;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.util.Map;
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;
public final class PendingHooks {
// GuardedBy("PendingHooks.class")
private static final ConcurrentHashMap<Class<?>, ConcurrentHashMap<Member, XposedBridge.AdditionalHookInfo>>
private static final ConcurrentHashMap<Class<?>, ConcurrentHashMap<Executable, XposedBridge.AdditionalHookInfo>>
sPendingHooks = new ConcurrentHashMap<>();
public synchronized static void hookPendingMethod(Class<?> clazz) {
if (sPendingHooks.containsKey(clazz)) {
for (Map.Entry<Member, XposedBridge.AdditionalHookInfo> hook : sPendingHooks.get(clazz).entrySet()) {
LSPdConfigGlobal.getHookProvider().hookMethod(hook.getKey(), hook.getValue());
for (Map.Entry<Executable, XposedBridge.AdditionalHookInfo> hook : sPendingHooks.get(clazz).entrySet()) {
YahfaHooker.hookMethod(hook.getKey(), hook.getValue());
}
sPendingHooks.remove(clazz);
}
@ -46,7 +47,7 @@ public final class PendingHooks {
public synchronized static void recordPendingMethod(Method hookMethod,
XposedBridge.AdditionalHookInfo additionalInfo) {
ConcurrentHashMap<Member, XposedBridge.AdditionalHookInfo> pending =
ConcurrentHashMap<Executable, XposedBridge.AdditionalHookInfo> pending =
sPendingHooks.computeIfAbsent(hookMethod.getDeclaringClass(), aClass -> new ConcurrentHashMap<>());
pending.put(hookMethod, additionalInfo);

View File

@ -25,31 +25,26 @@ import android.content.res.TypedArray;
import android.util.Log;
import io.github.lsposed.lspd.BuildConfig;
import io.github.lsposed.lspd.config.LSPdConfigGlobal;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Constructor;
import java.lang.reflect.Executable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
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_InitZygote;
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.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;
@ -91,7 +86,6 @@ public final class XposedBridge {
public static volatile ClassLoader dummyClassLoader = null;
@ApiSensitive(Level.MIDDLE)
public static void initXResources() {
if (dummyClassLoader != null) {
return;
@ -108,22 +102,10 @@ public final class XposedBridge {
} catch (Resources.NotFoundException nfe) {
XposedBridge.log(nfe);
}
XposedBridge.removeFinalFlagNative(resClass);
XposedBridge.removeFinalFlagNative(taClass);
ResourcesHook.removeFinalFlagNative(resClass);
ResourcesHook.removeFinalFlagNative(taClass);
ClassLoader myCL = XposedBridge.class.getClassLoader();
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.XTypedArraySuperClass");
setObjectField(myCL, "parent", dummyClassLoader);
@ -183,7 +165,7 @@ public final class XposedBridge {
* @see #hookAllConstructors
*/
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());
} else if (hookMethod.getDeclaringClass().isInterface()) {
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());
}
Executable targetMethod = (Executable) hookMethod;
if (callback == null) {
throw new IllegalArgumentException("callback should not be null!");
}
@ -198,10 +182,10 @@ public final class XposedBridge {
boolean newMethod = false;
CopyOnWriteSortedSet<XC_MethodHook> callbacks;
synchronized (sHookedMethodCallbacks) {
callbacks = sHookedMethodCallbacks.get(hookMethod);
callbacks = sHookedMethodCallbacks.get(targetMethod);
if (callbacks == null) {
callbacks = new CopyOnWriteSortedSet<>();
sHookedMethodCallbacks.put(hookMethod, callbacks);
sHookedMethodCallbacks.put(targetMethod, callbacks);
newMethod = true;
}
}
@ -209,9 +193,8 @@ public final class XposedBridge {
if (newMethod) {
AdditionalHookInfo additionalInfo = new AdditionalHookInfo(callbacks);
Member reflectMethod = LSPdConfigGlobal.getHookProvider().findMethodNative(hookMethod);
if (reflectMethod != null) {
LSPdConfigGlobal.getHookProvider().hookMethod(reflectMethod, (AdditionalHookInfo) additionalInfo);
if (!YahfaHooker.shouldDelayHook(targetMethod)) {
DynamicBridge.hookMethod(targetMethod, (AdditionalHookInfo) additionalInfo);
} else {
PendingHooks.recordPendingMethod((Method)hookMethod, additionalInfo);
}
@ -231,7 +214,6 @@ public final class XposedBridge {
*/
@Deprecated
public static void unhookMethod(Member hookMethod, XC_MethodHook callback) {
LSPdConfigGlobal.getHookProvider().unhookMethod(hookMethod);
CopyOnWriteSortedSet<XC_MethodHook> callbacks;
synchronized (sHookedMethodCallbacks) {
callbacks = sHookedMethodCallbacks.get(hookMethod);
@ -348,16 +330,11 @@ public final class XposedBridge {
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");
}
long methodId = LSPdConfigGlobal.getHookProvider().getMethodId(method);
return LSPdConfigGlobal.getHookProvider().invokeOriginalMethod(method, methodId, thisObject, args);
}
private static void removeFinalFlagNative(Class clazz) {
LSPdConfigGlobal.getHookProvider().removeFinalFlagNative(clazz);
return YahfaHooker.invokeOriginalMethod((Executable) method, thisObject, args);
}
/** @hide */

View File

@ -23,12 +23,9 @@ package de.robv.android.xposed;
import android.content.res.AssetManager;
import android.content.res.Resources;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Constructor;
@ -41,15 +38,11 @@ import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.WeakHashMap;
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.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.XCallback;
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.ResourcesHook;
import static de.robv.android.xposed.XposedBridge.hookAllMethods;
import static de.robv.android.xposed.XposedBridge.sInitPackageResourcesCallbacks;
@ -106,13 +104,12 @@ public final class XposedInit {
hookResources();
}
@ApiSensitive(Level.MIDDLE)
private static void hookResources() throws Throwable {
if (!serviceClient.isResourcesHookEnabled() || disableResources) {
return;
}
if (!LSPdConfigGlobal.getHookProvider().initXResourcesNative()) {
if (!ResourcesHook.initXResourcesNative()) {
Log.e(TAG, "Cannot hook resources");
disableResources = true;
return;
@ -209,7 +206,6 @@ public final class XposedInit {
XResources.init(latestResKey);
}
@ApiSensitive(Level.MIDDLE)
private static XResources cloneToXResources(XC_MethodHook.MethodHookParam param, String resDir) {
Object result = param.getResult();
if (result == null || result instanceof XResources ||

View File

@ -22,13 +22,12 @@ package de.robv.android.xposed.callbacks;
import android.os.Bundle;
import io.github.lsposed.lspd.config.LSPdConfigGlobal;
import java.io.Serializable;
import de.robv.android.xposed.IModuleContext;
import de.robv.android.xposed.XposedBridge;
import de.robv.android.xposed.XposedBridge.CopyOnWriteSortedSet;
import io.github.lsposed.lspd.deopt.PrebuiltMethodsDeopter;
/**
* 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
// only if we found out how to recompile their apks
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)

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