Merge branch android_r into master
# Conflicts: # edxp-core/template_override/customize.sh
This commit is contained in:
commit
e9945829aa
|
|
@ -0,0 +1,3 @@
|
|||
[submodule "edxp-core/src/main/cpp/external/Dobby"]
|
||||
path = edxp-core/src/main/cpp/external/Dobby
|
||||
url = https://github.com/jmpews/Dobby.git
|
||||
|
|
@ -101,6 +101,5 @@ Notice: These community group don't accept any bug report, please use [Get help]
|
|||
- [Riru](https://github.com/RikkaApps/Riru): provides a way to inject codes into zygote process
|
||||
- [XposedBridge](https://github.com/rovo89/XposedBridge): the OG xposed framework APIs
|
||||
- [dexmaker](https://github.com/linkedin/dexmaker) and [dalvikdx](https://github.com/JakeWharton/dalvik-dx): to dynamiclly generate YAHFA hooker classes
|
||||
- [Whale](https://github.com/asLody/whale): used for inline hooking
|
||||
- [SandHook](https://github.com/ganyao114/SandHook/): ART hooking framework for SandHook variant
|
||||
|
||||
- [Dobby](https://github.com/jmpews/Dobby): used for inline hooking
|
||||
|
|
|
|||
|
|
@ -91,6 +91,6 @@ Edxposed 拥有三个不同的版本
|
|||
- [Riru](https://github.com/RikkaApps/Riru): 提供一种将代码注入 zygote 进程的方法
|
||||
- [XposedBridge](https://github.com/rovo89/XposedBridge): 原版 xposed 框架的 API
|
||||
- [dexmaker](https://github.com/linkedin/dexmaker) 和 [dalvikdx](https://github.com/JakeWharton/dalvik-dx): 动态生成 YAHFA hook 类
|
||||
- [Whale](https://github.com/asLody/whale): 用于 hook 内联方法
|
||||
- [SandHook](https://github.com/ganyao114/SandHook/): SandHook 分支的 ART hooking 框架
|
||||
- [Dobby](https://github.com/jmpews/Dobby): 用于 hook 内联方法
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
version: '0.4.6.4 ({build})'
|
||||
version: '0.5.0.6_{build}-{branch}'
|
||||
|
||||
environment:
|
||||
ANDROID_HOME: C:\android-sdk-windows
|
||||
|
||||
install:
|
||||
- cd %APPVEYOR_BUILD_FOLDER%
|
||||
- git submodule update --init --recursive
|
||||
- appveyor DownloadFile https://dl.google.com/android/repository/sdk-tools-windows-4333796.zip
|
||||
- 7z x sdk-tools-windows-4333796.zip -oC:\android-sdk-windows > nul
|
||||
- yes | C:\android-sdk-windows\tools\bin\sdkmanager.bat --licenses > nul
|
||||
|
|
@ -12,7 +14,7 @@ install:
|
|||
- C:\android-sdk-windows\tools\bin\sdkmanager.bat "build-tools;28.0.3" > nul
|
||||
- C:\android-sdk-windows\tools\bin\sdkmanager.bat "cmake;3.6.4111459" > nul
|
||||
- C:\android-sdk-windows\tools\bin\sdkmanager.bat "patcher;v4" > nul
|
||||
- C:\android-sdk-windows\tools\bin\sdkmanager.bat "ndk-bundle" > nul
|
||||
- C:\android-sdk-windows\tools\bin\sdkmanager.bat "ndk;22.0.6917172" --channel=1 > nul
|
||||
- set PATH=%PATH%;C:\android-sdk-windows\ndk-bundle;C:\android-sdk-windows\build-tools\28.0.3
|
||||
|
||||
build_script:
|
||||
|
|
@ -34,7 +36,6 @@ only_commits:
|
|||
- edxp-common/
|
||||
- edxp-core/
|
||||
- edxp-sandhook/
|
||||
- edxp-whale/
|
||||
- edxp-yahfa/
|
||||
- hiddenapi-stubs/
|
||||
- xposed-bridge/
|
||||
|
|
|
|||
|
|
@ -7,8 +7,7 @@ buildscript {
|
|||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:3.4.0'
|
||||
|
||||
classpath 'com.android.tools.build:gradle:4.1.1'
|
||||
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
// in the individual module build.gradle files
|
||||
|
|
@ -24,7 +23,7 @@ allprojects {
|
|||
templateLibPath = templateRootPath + "/system/lib/"
|
||||
templateLib64Path = templateRootPath + "/system/lib64/"
|
||||
templateEtcPath = templateRootPath + "/system/etc/"
|
||||
hiddenApiStubJarFilePath = project(":hiddenapi-stubs").projectDir.absolutePath + "/libs/framework-stub.jar"
|
||||
hiddenApiStubJarFilePath = project(":hiddenapi-stubs").buildDir.absolutePath + "/libs/framework-stub.jar"
|
||||
}
|
||||
repositories {
|
||||
google()
|
||||
|
|
|
|||
|
|
@ -1 +1,2 @@
|
|||
/build
|
||||
dex
|
||||
|
|
|
|||
|
|
@ -1,47 +1,33 @@
|
|||
import org.gradle.internal.os.OperatingSystem
|
||||
apply plugin: 'java-library'
|
||||
apply plugin: 'com.android.application'
|
||||
|
||||
dependencies {
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
repositories {
|
||||
jcenter()
|
||||
}
|
||||
|
||||
sourceCompatibility = "7"
|
||||
targetCompatibility = "7"
|
||||
android {
|
||||
compileSdkVersion androidCompileSdkVersion.toInteger()
|
||||
ndkVersion androidCompileNdkVersion
|
||||
}
|
||||
|
||||
task findDx {
|
||||
if (OperatingSystem.current().isWindows()){
|
||||
return true
|
||||
}
|
||||
doLast {
|
||||
new ByteArrayOutputStream().withStream { os ->
|
||||
exec {
|
||||
commandLine "which", "dx"
|
||||
standardOutput os
|
||||
afterEvaluate {
|
||||
android.applicationVariants.all { variant ->
|
||||
def variantNameCapped = variant.name.capitalize()
|
||||
def variantNameLowered = variant.name.toLowerCase()
|
||||
|
||||
task("copyDex${variantNameCapped}", type: Copy) {
|
||||
dependsOn "assemble${variantNameCapped}"
|
||||
def dexOutPath = "${buildDir}/intermediates/dex/${variantNameLowered}/mergeDex${variantNameCapped}"
|
||||
from (dexOutPath){
|
||||
rename("classes.dex", "eddalvikdx.dex")
|
||||
}
|
||||
rootProject.ext.dxPath = os.toString()
|
||||
destinationDir file(templateRootPath + "system/framework/")
|
||||
outputs.upToDateWhen { false }
|
||||
}
|
||||
task("makeJar${variantNameCapped}", type: Jar, dependsOn: "assemble${variantNameCapped}") {
|
||||
dependsOn "assemble${variantNameCapped}"
|
||||
from "${buildDir}/intermediates/javac/${variantNameLowered}/classes"
|
||||
baseName "dalvikdx"
|
||||
outputs.file(archivePath)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
task dexInJar(type: Jar) {
|
||||
dependsOn jar
|
||||
dependsOn findDx
|
||||
doFirst {
|
||||
exec {
|
||||
workingDir jar.destinationDir
|
||||
if (OperatingSystem.current().isWindows()){
|
||||
executable "dx.bat"
|
||||
args "--dex", "--output", "classes.dex", "${jar.archiveName}"
|
||||
} else {
|
||||
executable "bash"
|
||||
args rootProject.ext.dxPath.trim(), "--dex", "--output", "classes.dex", "${jar.archiveName}"
|
||||
}
|
||||
}
|
||||
}
|
||||
from "${jar.destinationDir}/classes.dex"
|
||||
destinationDir jar.destinationDir
|
||||
baseName "eddalvikdx"
|
||||
onlyIf {
|
||||
!jar.state.upToDate || !file(archiveName).exists()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
<manifest package="com.elderdrivers.riru.edxp.eddalvikdx" />
|
||||
|
|
@ -1 +1,2 @@
|
|||
/build
|
||||
/build
|
||||
dex
|
||||
|
|
|
|||
|
|
@ -1,37 +1,39 @@
|
|||
import org.gradle.internal.os.OperatingSystem
|
||||
apply plugin: 'java'
|
||||
apply plugin: 'com.android.application'
|
||||
|
||||
description = "A utility for doing compile or runtime code generation targeting Android's Dalvik VM"
|
||||
|
||||
targetCompatibility = '1.7'
|
||||
sourceCompatibility = '1.7'
|
||||
|
||||
repositories {
|
||||
jcenter()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly project(':dalvikdx')
|
||||
android {
|
||||
compileSdkVersion androidCompileSdkVersion.toInteger()
|
||||
ndkVersion androidCompileNdkVersion
|
||||
}
|
||||
|
||||
task dexInJar(type: Jar) {
|
||||
dependsOn jar
|
||||
doFirst {
|
||||
exec {
|
||||
workingDir jar.destinationDir
|
||||
if (OperatingSystem.current().isWindows()){
|
||||
executable "dx.bat"
|
||||
args "--dex", "--output", "classes.dex", "${jar.archiveName}"
|
||||
} else {
|
||||
executable "bash"
|
||||
args rootProject.ext.dxPath.trim(), "--dex", "--output", "classes.dex", "${jar.archiveName}"
|
||||
dependencies {
|
||||
compileOnly files(project(":dalvikdx").tasks.getByName("makeJarRelease").outputs)
|
||||
}
|
||||
|
||||
afterEvaluate {
|
||||
android.applicationVariants.all { variant ->
|
||||
def variantNameCapped = variant.name.capitalize()
|
||||
def variantNameLowered = variant.name.toLowerCase()
|
||||
|
||||
task("copyDex${variantNameCapped}", type: Copy) {
|
||||
dependsOn "assemble${variantNameCapped}"
|
||||
def dexOutPath = "${buildDir}/intermediates/dex/${variantNameLowered}/mergeDex${variantNameCapped}"
|
||||
from (dexOutPath){
|
||||
rename("classes.dex", "eddexmaker.dex")
|
||||
}
|
||||
destinationDir file(templateRootPath + "system/framework/")
|
||||
outputs.upToDateWhen { false }
|
||||
}
|
||||
task("makeJar${variantNameCapped}", type: Jar, dependsOn: "assemble${variantNameCapped}") {
|
||||
dependsOn "assemble${variantNameCapped}"
|
||||
from "${buildDir}/intermediates/javac/${variantNameLowered}/classes"
|
||||
baseName "dexmaker"
|
||||
outputs.file(archivePath)
|
||||
}
|
||||
}
|
||||
from "${jar.destinationDir}/classes.dex"
|
||||
destinationDir jar.destinationDir
|
||||
baseName "eddexmaker"
|
||||
onlyIf {
|
||||
!jar.state.upToDate || !file(archiveName).exists()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
<manifest package="com.elderdrivers.riru.edxp.dexmaker" />
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
apply plugin: 'com.android.library'
|
||||
|
||||
android {
|
||||
compileSdkVersion 28
|
||||
compileSdkVersion androidCompileSdkVersion.toInteger()
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 26
|
||||
|
|
@ -17,13 +17,14 @@ android {
|
|||
}
|
||||
}
|
||||
|
||||
ndkVersion androidCompileNdkVersion
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly project(':hiddenapi-stubs')
|
||||
implementation project(':xposed-bridge')
|
||||
api project(':xposed-bridge')
|
||||
compileOnly project(':dexmaker')
|
||||
api "androidx.annotation:annotation:1.1.0-rc01"
|
||||
compileOnly 'com.android.support:support-annotations:28.0.0'
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@
|
|||
#-renamesourcefileattribute SourceFile
|
||||
|
||||
-dontobfuscate
|
||||
-dontoptimize
|
||||
-keep class de.robv.android.xposed.** {*;}
|
||||
-keep class android.** { *; }
|
||||
|
||||
|
|
|
|||
|
|
@ -1,36 +0,0 @@
|
|||
package com.elderdrivers.riru.edxp._hooker.impl;
|
||||
|
||||
import com.elderdrivers.riru.edxp.core.Main;
|
||||
import com.elderdrivers.riru.edxp.util.Hookers;
|
||||
|
||||
import dalvik.system.BaseDexClassLoader;
|
||||
import de.robv.android.xposed.XC_MethodHook;
|
||||
import de.robv.android.xposed.XposedBridge;
|
||||
|
||||
/**
|
||||
* On OnePlus stock roms (Android Pie), {@link BaseDexClassLoader#findClass(String)}
|
||||
* will open /dev/binder to communicate with PackageManagerService to check whether
|
||||
* current package name inCompatConfigList, which is an OnePlus OEM feature enabled only when
|
||||
* system prop "persist.sys.oem.region" set to "CN".(detail of related source code:
|
||||
* https://gist.github.com/solohsu/ecc07141759958fc096ba0781fac0a5f)
|
||||
* If we invoke intZygoteCallbacks in
|
||||
* {@link Main#forkAndSpecializePre}, where in zygote process,
|
||||
* we would get a chance to invoke findclass, leaving fd of /dev/binder open in zygote process,
|
||||
* which is not allowed because /dev/binder is not in predefined whitelist here:
|
||||
* http://androidxref.com/9.0.0_r3/xref/frameworks/base/core/jni/fd_utils.cpp#35
|
||||
* So we just hook BaseDexClassLoader#inCompatConfigList to return false to prevent
|
||||
* open of /dev/binder and we haven't found side effects yet.
|
||||
* Other roms might share the same problems but not reported too.
|
||||
*/
|
||||
public class OneplusWorkaround extends XC_MethodHook {
|
||||
|
||||
@Override
|
||||
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
|
||||
if (XposedBridge.disableHooks || Main.getEdxpImpl().getRouter().isForkCompleted()) {
|
||||
return;
|
||||
}
|
||||
Hookers.logD("BaseDexClassLoader#inCompatConfigList() starts");
|
||||
param.setResult(false);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -4,7 +4,10 @@ import com.elderdrivers.riru.common.KeepMembers;
|
|||
import com.elderdrivers.riru.edxp._hooker.impl.HandleBindApp;
|
||||
|
||||
import de.robv.android.xposed.XC_MethodHook;
|
||||
import de.robv.android.xposed.annotation.ApiSensitive;
|
||||
import de.robv.android.xposed.annotation.Level;
|
||||
|
||||
@ApiSensitive(Level.LOW)
|
||||
public class HandleBindAppHooker implements KeepMembers {
|
||||
|
||||
public static String className = "android.app.ActivityThread";
|
||||
|
|
|
|||
|
|
@ -8,7 +8,10 @@ import com.elderdrivers.riru.common.KeepMembers;
|
|||
import com.elderdrivers.riru.edxp._hooker.impl.LoadedApkCstr;
|
||||
|
||||
import de.robv.android.xposed.XC_MethodHook;
|
||||
import de.robv.android.xposed.annotation.ApiSensitive;
|
||||
import de.robv.android.xposed.annotation.Level;
|
||||
|
||||
@ApiSensitive(Level.LOW)
|
||||
public class LoadedApkConstructorHooker implements KeepMembers {
|
||||
public static String className = "android.app.LoadedApk";
|
||||
public static String methodName = "<init>";
|
||||
|
|
|
|||
|
|
@ -1,35 +0,0 @@
|
|||
package com.elderdrivers.riru.edxp._hooker.yahfa;
|
||||
|
||||
import com.elderdrivers.riru.common.KeepMembers;
|
||||
import com.elderdrivers.riru.edxp._hooker.impl.OneplusWorkaround;
|
||||
import com.elderdrivers.riru.edxp.core.yahfa.HookMain;
|
||||
|
||||
import de.robv.android.xposed.XC_MethodHook;
|
||||
|
||||
public class OnePlusWorkAroundHooker implements KeepMembers {
|
||||
|
||||
static {
|
||||
HookMain.addHookItemWhiteList(OnePlusWorkAroundHooker.class.getName());
|
||||
}
|
||||
|
||||
public static String className = "dalvik.system.BaseDexClassLoader";
|
||||
public static String methodName = "inCompatConfigList";
|
||||
public static String methodSig = "(ILjava/lang/String;)Z";
|
||||
|
||||
public static boolean hook(int type, String packageName) throws Throwable {
|
||||
final XC_MethodHook methodHook = new OneplusWorkaround();
|
||||
final XC_MethodHook.MethodHookParam param = new XC_MethodHook.MethodHookParam();
|
||||
param.thisObject = null;
|
||||
param.args = new Object[]{type, packageName};
|
||||
methodHook.callBeforeHookedMethod(param);
|
||||
if (!param.returnEarly) {
|
||||
param.setResult(backup(type, packageName));
|
||||
}
|
||||
methodHook.callAfterHookedMethod(param);
|
||||
return (boolean) param.getResult();
|
||||
}
|
||||
|
||||
public static boolean backup(int type, String packageName) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
package com.elderdrivers.riru.edxp._hooker.yahfa;
|
||||
|
||||
import com.elderdrivers.riru.common.KeepMembers;
|
||||
import com.elderdrivers.riru.edxp._hooker.impl.StartBootstrapServices;
|
||||
|
||||
import de.robv.android.xposed.XC_MethodHook;
|
||||
import de.robv.android.xposed.annotation.ApiSensitive;
|
||||
import de.robv.android.xposed.annotation.Level;
|
||||
|
||||
@ApiSensitive(Level.LOW)
|
||||
public class StartBootstrapServicesHooker11 implements KeepMembers {
|
||||
public static String className = "com.android.server.SystemServer";
|
||||
public static String methodName = "startBootstrapServices";
|
||||
public static String methodSig = "(Lcom/android/server/utils/TimingsTraceAndSlog;)V";
|
||||
|
||||
public static void hook(Object systemServer, Object traceAndSlog) throws Throwable {
|
||||
final XC_MethodHook methodHook = new StartBootstrapServices();
|
||||
final XC_MethodHook.MethodHookParam param = new XC_MethodHook.MethodHookParam();
|
||||
param.thisObject = systemServer;
|
||||
param.args = new Object[]{traceAndSlog};
|
||||
methodHook.callBeforeHookedMethod(param);
|
||||
if (!param.returnEarly) {
|
||||
backup(systemServer, traceAndSlog);
|
||||
}
|
||||
methodHook.callAfterHookedMethod(param);
|
||||
}
|
||||
|
||||
public static void backup(Object systemServer, Object traceAndSlog) {
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -6,7 +6,10 @@ import com.elderdrivers.riru.common.KeepMembers;
|
|||
import com.elderdrivers.riru.edxp._hooker.impl.SystemMain;
|
||||
|
||||
import de.robv.android.xposed.XC_MethodHook;
|
||||
import de.robv.android.xposed.annotation.ApiSensitive;
|
||||
import de.robv.android.xposed.annotation.Level;
|
||||
|
||||
@ApiSensitive(Level.LOW)
|
||||
public class SystemMainHooker implements KeepMembers {
|
||||
|
||||
public static String className = "android.app.ActivityThread";
|
||||
|
|
|
|||
|
|
@ -1,10 +1,8 @@
|
|||
package com.elderdrivers.riru.edxp.art;
|
||||
|
||||
import com.elderdrivers.riru.common.KeepAll;
|
||||
import com.elderdrivers.riru.edxp.core.Yahfa;
|
||||
|
||||
import java.lang.reflect.Member;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import de.robv.android.xposed.PendingHooks;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
package com.elderdrivers.riru.edxp.art;
|
||||
|
||||
import java.lang.reflect.Member;
|
||||
|
||||
public class Heap {
|
||||
|
||||
public static native int waitForGcToComplete(long thread);
|
||||
|
|
|
|||
|
|
@ -27,10 +27,6 @@ public class BaseEdxpConfig implements EdxpConfig {
|
|||
public String getLibSandHookName() {
|
||||
return ConfigManager.getLibSandHookName();
|
||||
}
|
||||
@Override
|
||||
public String getLibWhaleName() {
|
||||
return ConfigManager.getLibWhaleName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDynamicModulesMode() {
|
||||
|
|
@ -51,4 +47,7 @@ public class BaseEdxpConfig implements EdxpConfig {
|
|||
public boolean isBlackWhiteListMode() {
|
||||
return ConfigManager.isBlackWhiteListEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getModulesList() { return ConfigManager.getModulesList(); }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,11 +43,9 @@ public class ConfigManager {
|
|||
|
||||
public static native String getLibSandHookName();
|
||||
|
||||
public static native String getLibWhaleName();
|
||||
|
||||
public static native String getInstallerConfigPath(String suffix);
|
||||
|
||||
public static native String getDataPathPrefix();
|
||||
|
||||
public static native boolean isAppNeedHook(String appDataDir);
|
||||
public static native String getModulesList();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,14 +1,12 @@
|
|||
package com.elderdrivers.riru.edxp.core;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import com.elderdrivers.riru.edxp.proxy.BlackWhiteListProxy;
|
||||
import com.elderdrivers.riru.edxp.proxy.NormalProxy;
|
||||
import com.elderdrivers.riru.edxp.proxy.Router;
|
||||
|
||||
public abstract class BaseEdxpImpl implements EdxpImpl {
|
||||
|
||||
protected Proxy mBlackWhiteListProxy;
|
||||
protected Proxy mNormalProxy;
|
||||
protected Router mRouter;
|
||||
|
||||
|
|
@ -23,15 +21,6 @@ public abstract class BaseEdxpImpl implements EdxpImpl {
|
|||
return mInitialized;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Proxy getBlackWhiteListProxy() {
|
||||
if (mBlackWhiteListProxy == null) {
|
||||
mBlackWhiteListProxy = createBlackWhiteListProxy();
|
||||
}
|
||||
return mBlackWhiteListProxy;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Proxy getNormalProxy() {
|
||||
|
|
@ -50,10 +39,6 @@ public abstract class BaseEdxpImpl implements EdxpImpl {
|
|||
return mRouter;
|
||||
}
|
||||
|
||||
protected Proxy createBlackWhiteListProxy() {
|
||||
return new BlackWhiteListProxy(getRouter());
|
||||
}
|
||||
|
||||
protected Proxy createNormalProxy() {
|
||||
return new NormalProxy(getRouter());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
package com.elderdrivers.riru.edxp.core;
|
||||
|
||||
import androidx.annotation.IntDef;
|
||||
import androidx.annotation.NonNull;
|
||||
import android.support.annotation.IntDef;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import com.elderdrivers.riru.common.KeepAll;
|
||||
import com.elderdrivers.riru.edxp.proxy.Router;
|
||||
|
|
@ -15,14 +15,10 @@ public interface EdxpImpl extends KeepAll {
|
|||
int NONE = 0;
|
||||
int YAHFA = 1;
|
||||
int SANDHOOK = 2;
|
||||
int WHALE = 3;
|
||||
|
||||
@NonNull
|
||||
Proxy getNormalProxy();
|
||||
|
||||
@NonNull
|
||||
Proxy getBlackWhiteListProxy();
|
||||
|
||||
@NonNull
|
||||
Router getRouter();
|
||||
|
||||
|
|
@ -34,7 +30,7 @@ public interface EdxpImpl extends KeepAll {
|
|||
boolean isInitialized();
|
||||
|
||||
@Retention(SOURCE)
|
||||
@IntDef({NONE, YAHFA, SANDHOOK, WHALE})
|
||||
@IntDef({NONE, YAHFA, SANDHOOK})
|
||||
@interface Variant {
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,30 +1,20 @@
|
|||
package com.elderdrivers.riru.edxp.core;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.os.Process;
|
||||
|
||||
import com.elderdrivers.riru.common.KeepAll;
|
||||
import com.elderdrivers.riru.edxp.common.BuildConfig;
|
||||
import com.elderdrivers.riru.edxp.config.ConfigManager;
|
||||
import com.elderdrivers.riru.edxp.framework.ProcessHelper;
|
||||
import com.elderdrivers.riru.edxp.util.Utils;
|
||||
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.ServiceLoader;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import static com.elderdrivers.riru.edxp.proxy.BaseProxy.onBlackListed;
|
||||
import static com.elderdrivers.riru.edxp.core.EdxpImpl.NONE;
|
||||
|
||||
@SuppressLint("DefaultLocale")
|
||||
public class Main implements KeepAll {
|
||||
|
||||
private static final boolean logEnabled = BuildConfig.DEBUG;
|
||||
private static String forkAndSpecializePramsStr = "";
|
||||
private static String forkSystemServerPramsStr = "";
|
||||
|
||||
private static final AtomicReference<EdxpImpl> edxpImplRef = new AtomicReference<>(null);
|
||||
|
||||
static {
|
||||
|
|
@ -40,74 +30,23 @@ public class Main implements KeepAll {
|
|||
String niceName, int[] fdsToClose, int[] fdsToIgnore,
|
||||
boolean startChildZygote, String instructionSet,
|
||||
String appDataDir) {
|
||||
if (isBlackListedProcess(uid)) {
|
||||
return;
|
||||
}
|
||||
final EdxpImpl edxp = getEdxpImpl();
|
||||
if (edxp == null || !edxp.isInitialized()) {
|
||||
return;
|
||||
}
|
||||
if (logEnabled) {
|
||||
forkAndSpecializePramsStr = String.format(
|
||||
"Zygote#forkAndSpecialize(%d, %d, %s, %d, %s, %d, %s, %s, %s, %s, %s, %s, %s)",
|
||||
uid, gid, Arrays.toString(gids), debugFlags, Arrays.toString(rlimits),
|
||||
mountExternal, seInfo, niceName, Arrays.toString(fdsToClose),
|
||||
Arrays.toString(fdsToIgnore), startChildZygote, instructionSet, appDataDir);
|
||||
}
|
||||
|
||||
if (ConfigManager.isBlackWhiteListEnabled()) {
|
||||
edxp.getBlackWhiteListProxy().forkAndSpecializePre(uid, gid, gids, debugFlags, rlimits,
|
||||
mountExternal, seInfo, niceName, fdsToClose, fdsToIgnore, startChildZygote,
|
||||
instructionSet, appDataDir);
|
||||
} else {
|
||||
edxp.getNormalProxy().forkAndSpecializePre(uid, gid, gids, debugFlags, rlimits, mountExternal,
|
||||
seInfo, niceName, fdsToClose, fdsToIgnore, startChildZygote, instructionSet,
|
||||
appDataDir);
|
||||
}
|
||||
// won't be loaded
|
||||
}
|
||||
|
||||
public static void forkAndSpecializePost(int pid, String appDataDir, String niceName) {
|
||||
if (isBlackListedProcess(Process.myUid())) {
|
||||
onBlackListed();
|
||||
return;
|
||||
}
|
||||
final EdxpImpl edxp = getEdxpImpl();
|
||||
if (edxp == null || !edxp.isInitialized()) {
|
||||
Utils.logE("Not started up");
|
||||
return;
|
||||
}
|
||||
if (pid == 0) {
|
||||
if (logEnabled) {
|
||||
Utils.logI(forkAndSpecializePramsStr + " = " + Process.myPid());
|
||||
}
|
||||
if (ConfigManager.isBlackWhiteListEnabled()) {
|
||||
edxp.getBlackWhiteListProxy().forkAndSpecializePost(pid, appDataDir, niceName);
|
||||
} else {
|
||||
edxp.getNormalProxy().forkAndSpecializePost(pid, appDataDir, niceName);
|
||||
}
|
||||
} else {
|
||||
// in zygote process, res is child zygote pid
|
||||
// don't print log here, see https://github.com/RikkaApps/Riru/blob/77adfd6a4a6a81bfd20569c910bc4854f2f84f5e/riru-core/jni/main/jni_native_method.cpp#L55-L66
|
||||
edxp.getNormalProxy().forkAndSpecializePost(pid, appDataDir, niceName);
|
||||
}
|
||||
}
|
||||
|
||||
public static void forkSystemServerPre(int uid, int gid, int[] gids, int debugFlags, int[][] rlimits,
|
||||
long permittedCapabilities, long effectiveCapabilities) {
|
||||
final EdxpImpl edxp = getEdxpImpl();
|
||||
if (edxp == null || !edxp.isInitialized()) {
|
||||
return;
|
||||
}
|
||||
if (logEnabled) {
|
||||
forkSystemServerPramsStr = String.format("Zygote#forkSystemServer(%d, %d, %s, %d, %s, %d, %d)",
|
||||
uid, gid, Arrays.toString(gids), debugFlags, Arrays.toString(rlimits),
|
||||
permittedCapabilities, effectiveCapabilities);
|
||||
}
|
||||
if (ConfigManager.isBlackWhiteListEnabled()) {
|
||||
edxp.getBlackWhiteListProxy().forkSystemServerPre(uid, gid, gids, debugFlags, rlimits,
|
||||
permittedCapabilities, effectiveCapabilities);
|
||||
} else {
|
||||
edxp.getNormalProxy().forkSystemServerPre(uid, gid, gids, debugFlags, rlimits,
|
||||
permittedCapabilities, effectiveCapabilities);
|
||||
}
|
||||
// Won't load
|
||||
}
|
||||
|
||||
public static void forkSystemServerPost(int pid) {
|
||||
|
|
@ -116,17 +55,7 @@ public class Main implements KeepAll {
|
|||
return;
|
||||
}
|
||||
if (pid == 0) {
|
||||
if (logEnabled) {
|
||||
Utils.logI(forkSystemServerPramsStr + " = " + Process.myPid());
|
||||
}
|
||||
if (ConfigManager.isBlackWhiteListEnabled()) {
|
||||
edxp.getBlackWhiteListProxy().forkSystemServerPost(pid);
|
||||
} else {
|
||||
edxp.getNormalProxy().forkSystemServerPost(pid);
|
||||
}
|
||||
} else {
|
||||
// in zygote process, res is child zygote pid
|
||||
// don't print log here, see https://github.com/RikkaApps/Riru/blob/77adfd6a4a6a81bfd20569c910bc4854f2f84f5e/riru-core/jni/main/jni_native_method.cpp#L55-L66
|
||||
edxp.getNormalProxy().forkSystemServerPost(pid);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -144,25 +73,16 @@ public class Main implements KeepAll {
|
|||
}
|
||||
|
||||
private static void loadEdxpImpls() {
|
||||
AccessController.doPrivileged(new PrivilegedAction<Void>() {
|
||||
public Void run() {
|
||||
Iterator<EdxpImpl> iterator = ServiceLoader.load(
|
||||
EdxpImpl.class, Main.class.getClassLoader()).iterator();
|
||||
try {
|
||||
while (iterator.hasNext()) {
|
||||
iterator.next();
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
Utils.logE("error when loadEdxpImpls", t);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static boolean isBlackListedProcess(int uid) {
|
||||
return ProcessHelper.isIsolated(uid)
|
||||
|| ProcessHelper.isRELROUpdater(uid)
|
||||
|| ProcessHelper.isWebViewZygote(uid);
|
||||
// We don't have Manifest now, so we have to load manually.
|
||||
try {
|
||||
Class.forName("com.elderdrivers.riru.edxp.sandhook.core.SandHookEdxpImpl");
|
||||
}catch(Throwable ignored) {
|
||||
Utils.logD("not using sandhook");
|
||||
}
|
||||
try {
|
||||
Class.forName("com.elderdrivers.riru.edxp.yahfa.core.YahfaEdxpImpl");
|
||||
}catch(Throwable ignored) {
|
||||
Utils.logD("not using yahfa");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -122,7 +122,9 @@ public class HookMain {
|
|||
checkCompatibleMethods(target, backup, "Original", "Backup");
|
||||
}
|
||||
if (backup != null) {
|
||||
HookMethodResolver.resolveMethod(hook, backup);
|
||||
HookMethodResolver.resolveMethod(hook, backup, target);
|
||||
} else {
|
||||
Utils.logD("wanna resolve backup method, but it's null, target: " + target);
|
||||
}
|
||||
// make sure GC completed before hook
|
||||
Thread currentThread = Thread.currentThread();
|
||||
|
|
|
|||
|
|
@ -91,22 +91,23 @@ public class HookMethodResolver {
|
|||
}
|
||||
}
|
||||
|
||||
public static void resolveMethod(Method hook, Method backup) {
|
||||
public static void resolveMethod(Method hook, Method backup, Object target) {
|
||||
if (canResolvedInJava && artMethodField != null) {
|
||||
// in java
|
||||
try {
|
||||
resolveInJava(hook, backup);
|
||||
resolveInJava(hook, backup, target);
|
||||
} catch (Exception e) {
|
||||
// in native
|
||||
resolveInNative(hook, backup);
|
||||
resolveInNative(hook, backup, target);
|
||||
}
|
||||
} else {
|
||||
// in native
|
||||
resolveInNative(hook, backup);
|
||||
resolveInNative(hook, backup, target);
|
||||
}
|
||||
}
|
||||
|
||||
private static void resolveInJava(Method hook, Method backup) throws Exception {
|
||||
private static void resolveInJava(Method hook, Method backup, Object target) throws Exception {
|
||||
Utils.logD("start to resolve in java. target: " + target);
|
||||
Object dexCache = dexCacheField.get(hook.getDeclaringClass());
|
||||
if (isArtMethod) {
|
||||
Object artMethod = artMethodField.get(backup);
|
||||
|
|
@ -121,7 +122,8 @@ public class HookMethodResolver {
|
|||
}
|
||||
}
|
||||
|
||||
private static void resolveInNative(Method hook, Method backup) {
|
||||
private static void resolveInNative(Method hook, Method backup, Object target) {
|
||||
Utils.logD("start to resolve in native. target: " + target);
|
||||
Yahfa.ensureMethodCached(hook, backup);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,9 @@ package com.elderdrivers.riru.edxp.deopt;
|
|||
|
||||
import java.util.HashMap;
|
||||
|
||||
import de.robv.android.xposed.annotation.ApiSensitive;
|
||||
import de.robv.android.xposed.annotation.Level;
|
||||
|
||||
/**
|
||||
* Providing a whitelist of methods which are the callers of the target methods we want to hook.
|
||||
* Because the target methods are inlined into the callers, we deoptimize the callers to
|
||||
|
|
@ -10,6 +13,7 @@ import java.util.HashMap;
|
|||
* Only for methods which are included in pre-compiled framework codes.
|
||||
* TODO recompile system apps and priv-apps since their original dex files are available
|
||||
*/
|
||||
@ApiSensitive(Level.MIDDLE)
|
||||
public class InlinedMethodCallers {
|
||||
|
||||
public static final String KEY_BOOT_IMAGE = "boot_image";
|
||||
|
|
@ -28,8 +32,10 @@ public class InlinedMethodCallers {
|
|||
private static final String[][] BOOT_IMAGE = {
|
||||
// callers of Application#attach(Context)
|
||||
{"android.app.Instrumentation", "newApplication", "(Ljava/lang/ClassLoader;Ljava/lang/String;Landroid/content/Context;)Landroid/app/Application;"},
|
||||
{"android.app.Instrumentation", "newApplication", "(Ljava/lang/ClassLoader;Landroid/content/Context;)Landroid/app/Application;"},
|
||||
};
|
||||
|
||||
// TODO deprecate this
|
||||
private static final String[][] BOOT_IMAGE_FOR_MIUI_RES = {
|
||||
// for MIUI resources hooking
|
||||
{"android.content.res.MiuiResources", "init", "(Ljava/lang/String;)V"},
|
||||
|
|
|
|||
|
|
@ -1,4 +0,0 @@
|
|||
package com.elderdrivers.riru.edxp.entry;
|
||||
|
||||
public interface Hook {
|
||||
}
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
package com.elderdrivers.riru.edxp.entry;
|
||||
|
||||
public class HookImpl<T> implements Hook {
|
||||
|
||||
}
|
||||
|
|
@ -3,12 +3,10 @@ package com.elderdrivers.riru.edxp.entry.yahfa;
|
|||
import com.elderdrivers.riru.common.KeepMembers;
|
||||
import com.elderdrivers.riru.edxp._hooker.yahfa.HandleBindAppHooker;
|
||||
import com.elderdrivers.riru.edxp._hooker.yahfa.LoadedApkConstructorHooker;
|
||||
import com.elderdrivers.riru.edxp._hooker.yahfa.OnePlusWorkAroundHooker;
|
||||
|
||||
public class AppBootstrapHookInfo implements KeepMembers {
|
||||
public static String[] hookItemNames = {
|
||||
HandleBindAppHooker.class.getName(),
|
||||
LoadedApkConstructorHooker.class.getName(),
|
||||
OnePlusWorkAroundHooker.class.getName()
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ package com.elderdrivers.riru.edxp.entry.yahfa;
|
|||
import com.elderdrivers.riru.common.KeepMembers;
|
||||
import com.elderdrivers.riru.edxp._hooker.yahfa.HandleBindAppHooker;
|
||||
import com.elderdrivers.riru.edxp._hooker.yahfa.LoadedApkConstructorHooker;
|
||||
import com.elderdrivers.riru.edxp._hooker.yahfa.OnePlusWorkAroundHooker;
|
||||
import com.elderdrivers.riru.edxp._hooker.yahfa.SystemMainHooker;
|
||||
|
||||
public class SysBootstrapHookInfo implements KeepMembers {
|
||||
|
|
@ -11,6 +10,5 @@ public class SysBootstrapHookInfo implements KeepMembers {
|
|||
HandleBindAppHooker.class.getName(),
|
||||
SystemMainHooker.class.getName(),
|
||||
LoadedApkConstructorHooker.class.getName(),
|
||||
OnePlusWorkAroundHooker.class.getName()
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,9 +2,18 @@ package com.elderdrivers.riru.edxp.entry.yahfa;
|
|||
|
||||
import com.elderdrivers.riru.common.KeepMembers;
|
||||
import com.elderdrivers.riru.edxp._hooker.yahfa.StartBootstrapServicesHooker;
|
||||
import com.elderdrivers.riru.edxp._hooker.yahfa.StartBootstrapServicesHooker11;
|
||||
import com.elderdrivers.riru.edxp.util.Versions;
|
||||
|
||||
public class SysInnerHookInfo implements KeepMembers {
|
||||
|
||||
public static Class<?> getSysInnerHookerClass() {
|
||||
return Versions.hasR() ?
|
||||
StartBootstrapServicesHooker11.class :
|
||||
StartBootstrapServicesHooker.class;
|
||||
}
|
||||
|
||||
public static String[] hookItemNames = {
|
||||
StartBootstrapServicesHooker.class.getName()
|
||||
getSysInnerHookerClass().getName()
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +0,0 @@
|
|||
package com.elderdrivers.riru.edxp.entry.yahfa;
|
||||
|
||||
import com.elderdrivers.riru.common.KeepMembers;
|
||||
import com.elderdrivers.riru.edxp._hooker.yahfa.OnePlusWorkAroundHooker;
|
||||
|
||||
public class WorkAroundHookInfo implements KeepMembers {
|
||||
public static String[] hookItemNames = {
|
||||
OnePlusWorkAroundHooker.class.getName()
|
||||
};
|
||||
}
|
||||
|
|
@ -1,52 +0,0 @@
|
|||
package com.elderdrivers.riru.edxp.framework;
|
||||
|
||||
import de.robv.android.xposed.XposedHelpers;
|
||||
|
||||
public class ProcessHelper {
|
||||
|
||||
static {
|
||||
// WEBVIEW_ZYGOTE_UID differ among versions
|
||||
WEBVIEW_ZYGOTE_UID = XposedHelpers.getStaticIntField(android.os.Process.class, "WEBVIEW_ZYGOTE_UID");
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines the UID/GID for the shared RELRO file updater process.
|
||||
*/
|
||||
public static final int SHARED_RELRO_UID = 1037;
|
||||
|
||||
/**
|
||||
* Defines the UID/GID for the WebView zygote process.
|
||||
*/
|
||||
public static final int WEBVIEW_ZYGOTE_UID;
|
||||
|
||||
/**
|
||||
* First uid used for fully isolated sandboxed processes (with no permissions of their own)
|
||||
*/
|
||||
public static final int FIRST_ISOLATED_UID = 99000;
|
||||
/**
|
||||
* Last uid used for fully isolated sandboxed processes (with no permissions of their own)
|
||||
*/
|
||||
public static final int LAST_ISOLATED_UID = 99999;
|
||||
|
||||
/**
|
||||
* Range of uids allocated for a user.
|
||||
*/
|
||||
public static final int PER_USER_RANGE = 100000;
|
||||
|
||||
public static int getAppId(int uid) {
|
||||
return uid % PER_USER_RANGE;
|
||||
}
|
||||
|
||||
public static boolean isRELROUpdater(int uid) {
|
||||
return getAppId(uid) == SHARED_RELRO_UID;
|
||||
}
|
||||
|
||||
public static boolean isWebViewZygote(int uid) {
|
||||
return getAppId(uid) == WEBVIEW_ZYGOTE_UID;
|
||||
}
|
||||
|
||||
public static boolean isIsolated(int uid) {
|
||||
uid = getAppId(uid);
|
||||
return uid >= FIRST_ISOLATED_UID && uid <= LAST_ISOLATED_UID;
|
||||
}
|
||||
}
|
||||
|
|
@ -3,15 +3,11 @@ package com.elderdrivers.riru.edxp.framework;
|
|||
import com.elderdrivers.riru.edxp.util.Utils;
|
||||
|
||||
import de.robv.android.xposed.XposedHelpers;
|
||||
import de.robv.android.xposed.annotation.ApiSensitive;
|
||||
import de.robv.android.xposed.annotation.Level;
|
||||
|
||||
@ApiSensitive(Level.LOW)
|
||||
public class Zygote {
|
||||
|
||||
// prevent from fatal error caused by holding not whitelisted file descriptors when forking zygote
|
||||
// https://github.com/rovo89/Xposed/commit/b3ba245ad04cd485699fb1d2ebde7117e58214ff
|
||||
public static native void closeFilesBeforeFork();
|
||||
|
||||
public static native void reopenFilesAfterFork();
|
||||
|
||||
public static void allowFileAcrossFork(String path) {
|
||||
try {
|
||||
Class zygote = XposedHelpers.findClass("com.android.internal.os.Zygote", null);
|
||||
|
|
|
|||
|
|
@ -4,7 +4,10 @@ import android.os.StrictMode;
|
|||
|
||||
import de.robv.android.xposed.XC_MethodHook;
|
||||
import de.robv.android.xposed.XposedHelpers;
|
||||
import de.robv.android.xposed.annotation.ApiSensitive;
|
||||
import de.robv.android.xposed.annotation.Level;
|
||||
|
||||
@ApiSensitive(Level.LOW)
|
||||
public class SliceProviderFix {
|
||||
|
||||
public static final String SYSTEMUI_PACKAGE_NAME = "com.android.systemui";
|
||||
|
|
|
|||
|
|
@ -21,8 +21,13 @@ public class XposedInstallerHooker {
|
|||
final String xposedAppClass = LEGACY_INSTALLER_PACKAGE_NAME + ".XposedApp";
|
||||
final Class InstallZipUtil = XposedHelpers.findClass(LEGACY_INSTALLER_PACKAGE_NAME
|
||||
+ ".util.InstallZipUtil", classLoader);
|
||||
XposedHelpers.findAndHookMethod(xposedAppClass, classLoader, "getActiveXposedVersion",
|
||||
XC_MethodReplacement.returnConstant(XposedBridge.getXposedVersion()));
|
||||
XposedHelpers.findAndHookMethod(xposedAppClass, classLoader, "getActiveXposedVersion", new XC_MethodHook() {
|
||||
@Override
|
||||
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
|
||||
Utils.logD("after getActiveXposedVersion...");
|
||||
param.setResult(XposedBridge.getXposedVersion());
|
||||
}
|
||||
});
|
||||
XposedHelpers.findAndHookMethod(xposedAppClass, classLoader,
|
||||
"reloadXposedProp", new XC_MethodHook() {
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -7,37 +7,34 @@ import android.text.TextUtils;
|
|||
|
||||
import com.elderdrivers.riru.edxp._hooker.impl.HandleBindApp;
|
||||
import com.elderdrivers.riru.edxp._hooker.impl.LoadedApkCstr;
|
||||
import com.elderdrivers.riru.edxp._hooker.impl.OneplusWorkaround;
|
||||
import com.elderdrivers.riru.edxp._hooker.impl.StartBootstrapServices;
|
||||
import com.elderdrivers.riru.edxp._hooker.impl.SystemMain;
|
||||
import com.elderdrivers.riru.edxp._hooker.yahfa.HandleBindAppHooker;
|
||||
import com.elderdrivers.riru.edxp._hooker.yahfa.LoadedApkConstructorHooker;
|
||||
import com.elderdrivers.riru.edxp._hooker.yahfa.OnePlusWorkAroundHooker;
|
||||
import com.elderdrivers.riru.edxp._hooker.yahfa.StartBootstrapServicesHooker;
|
||||
import com.elderdrivers.riru.edxp._hooker.yahfa.SystemMainHooker;
|
||||
import com.elderdrivers.riru.edxp.core.yahfa.HookMain;
|
||||
import com.elderdrivers.riru.edxp.entry.yahfa.AppBootstrapHookInfo;
|
||||
import com.elderdrivers.riru.edxp.entry.yahfa.SysBootstrapHookInfo;
|
||||
import com.elderdrivers.riru.edxp.entry.yahfa.SysInnerHookInfo;
|
||||
import com.elderdrivers.riru.edxp.entry.yahfa.WorkAroundHookInfo;
|
||||
import com.elderdrivers.riru.edxp.util.Utils;
|
||||
import com.elderdrivers.riru.edxp.util.Versions;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import de.robv.android.xposed.XposedBridge;
|
||||
import de.robv.android.xposed.XposedHelpers;
|
||||
import de.robv.android.xposed.XposedInit;
|
||||
import de.robv.android.xposed.annotation.ApiSensitive;
|
||||
import de.robv.android.xposed.annotation.Level;
|
||||
|
||||
public abstract class BaseRouter implements Router {
|
||||
|
||||
protected volatile boolean forkCompleted = false;
|
||||
|
||||
protected volatile AtomicBoolean bootstrapHooked = new AtomicBoolean(false);
|
||||
|
||||
protected static boolean useXposedApi = false;
|
||||
|
||||
public void initResourcesHook() {
|
||||
startWorkAroundHook(); // for OnePlus devices
|
||||
XposedBridge.initXResources();
|
||||
}
|
||||
|
||||
|
|
@ -46,18 +43,6 @@ public abstract class BaseRouter implements Router {
|
|||
XposedInit.startsSystemServer = isSystem;
|
||||
}
|
||||
|
||||
public void onForkStart() {
|
||||
forkCompleted = false;
|
||||
}
|
||||
|
||||
public void onForkFinish() {
|
||||
forkCompleted = true;
|
||||
}
|
||||
|
||||
public boolean isForkCompleted() {
|
||||
return forkCompleted;
|
||||
}
|
||||
|
||||
public void installBootstrapHooks(boolean isSystem) {
|
||||
// Initialize the Xposed framework
|
||||
try {
|
||||
|
|
@ -72,10 +57,9 @@ public abstract class BaseRouter implements Router {
|
|||
}
|
||||
}
|
||||
|
||||
public void loadModulesSafely(boolean isInZygote, boolean callInitZygote) {
|
||||
public void loadModulesSafely(boolean callInitZygote) {
|
||||
try {
|
||||
// FIXME some coredomain app can't reading modules.list
|
||||
XposedInit.loadModules(isInZygote, callInitZygote);
|
||||
XposedInit.loadModules(callInitZygote);
|
||||
} catch (Exception exception) {
|
||||
Utils.logE("error loading module list", exception);
|
||||
}
|
||||
|
|
@ -93,6 +77,7 @@ public abstract class BaseRouter implements Router {
|
|||
}
|
||||
|
||||
|
||||
@ApiSensitive(Level.LOW)
|
||||
public void startBootstrapHook(boolean isSystem) {
|
||||
Utils.logD("startBootstrapHook starts: isSystem = " + isSystem);
|
||||
ClassLoader classLoader = BaseRouter.class.getClassLoader();
|
||||
|
|
@ -127,9 +112,13 @@ public abstract class BaseRouter implements Router {
|
|||
public void startSystemServerHook() {
|
||||
ClassLoader classLoader = BaseRouter.class.getClassLoader();
|
||||
if (useXposedApi) {
|
||||
StartBootstrapServices sbsHooker = new StartBootstrapServices();
|
||||
Object[] paramTypesAndCallback = Versions.hasR() ?
|
||||
new Object[]{"com.android.server.utils.TimingsTraceAndSlog", sbsHooker} :
|
||||
new Object[]{sbsHooker};
|
||||
XposedHelpers.findAndHookMethod(StartBootstrapServicesHooker.className,
|
||||
SystemMain.systemServerCL,
|
||||
StartBootstrapServicesHooker.methodName, new StartBootstrapServices());
|
||||
StartBootstrapServicesHooker.methodName, paramTypesAndCallback);
|
||||
} else {
|
||||
HookMain.doHookDefault(
|
||||
classLoader,
|
||||
|
|
@ -137,21 +126,4 @@ public abstract class BaseRouter implements Router {
|
|||
SysInnerHookInfo.class.getName());
|
||||
}
|
||||
}
|
||||
|
||||
public void startWorkAroundHook() {
|
||||
ClassLoader classLoader = BaseRouter.class.getClassLoader();
|
||||
if (useXposedApi) {
|
||||
try {
|
||||
XposedHelpers.findAndHookMethod(OnePlusWorkAroundHooker.className,
|
||||
classLoader, OnePlusWorkAroundHooker.methodName,
|
||||
int.class, String.class, new OneplusWorkaround());
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
} else {
|
||||
HookMain.doHookDefault(
|
||||
BaseRouter.class.getClassLoader(),
|
||||
classLoader,
|
||||
WorkAroundHookInfo.class.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,139 +0,0 @@
|
|||
package com.elderdrivers.riru.edxp.proxy;
|
||||
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.elderdrivers.riru.edxp.config.ConfigManager;
|
||||
import com.elderdrivers.riru.edxp.deopt.PrebuiltMethodsDeopter;
|
||||
import com.elderdrivers.riru.edxp.util.ProcessUtils;
|
||||
import com.elderdrivers.riru.edxp.util.Utils;
|
||||
|
||||
import de.robv.android.xposed.XposedBridge;
|
||||
|
||||
import static com.elderdrivers.riru.edxp.util.FileUtils.getDataPathPrefix;
|
||||
|
||||
/**
|
||||
* 1. Non dynamic mode
|
||||
* - system_server is whitelisted
|
||||
* * for all child processes of main zygote
|
||||
* What've been done in main zygote pre-forking system_server
|
||||
* 1) non dynamic flag set (no need to reset)
|
||||
* 2) boot image methods deopted (no need to redo)
|
||||
* 3) startSystemServer flag set to true (need to reset)
|
||||
* 4) workaround hooks installed (need to redo)
|
||||
* 5) module list loaded and initZygote called (no need to redo)
|
||||
* 6) close all fds (no need to redo because of 5))
|
||||
* * for all child processes of secondary zygote
|
||||
* 1) do the same things pre-forking first child process
|
||||
* - system_server is blacklisted:
|
||||
* * for all child processes of both main zygote and secondary zygote
|
||||
* 1) do the same things pre-forking first child process
|
||||
* 2. Dynamic mode:
|
||||
* to be continued
|
||||
*/
|
||||
public class BlackWhiteListProxy extends BaseProxy {
|
||||
|
||||
public BlackWhiteListProxy(Router router) {
|
||||
super(router);
|
||||
}
|
||||
|
||||
public void forkAndSpecializePre(int uid, int gid, int[] gids, int debugFlags,
|
||||
int[][] rlimits, int mountExternal, String seInfo,
|
||||
String niceName, int[] fdsToClose, int[] fdsToIgnore,
|
||||
boolean startChildZygote, String instructionSet,
|
||||
String appDataDir) {
|
||||
final boolean isDynamicModulesMode = ConfigManager.isDynamicModulesEnabled();
|
||||
if (isDynamicModulesMode) {
|
||||
onForkPreForDynamicMode(false);
|
||||
} else {
|
||||
onForkPreForNonDynamicMode(false);
|
||||
}
|
||||
}
|
||||
|
||||
public void forkAndSpecializePost(int pid, String appDataDir, String niceName) {
|
||||
onForkPostCommon(false, appDataDir, niceName);
|
||||
}
|
||||
|
||||
public void forkSystemServerPre(int uid, int gid, int[] gids, int debugFlags,
|
||||
int[][] rlimits, long permittedCapabilities,
|
||||
long effectiveCapabilities) {
|
||||
final boolean isDynamicModulesMode = ConfigManager.isDynamicModulesEnabled();
|
||||
if (isDynamicModulesMode) {
|
||||
onForkPreForDynamicMode(true);
|
||||
} else {
|
||||
onForkPreForNonDynamicMode(true);
|
||||
}
|
||||
}
|
||||
|
||||
public void forkSystemServerPost(int pid) {
|
||||
onForkPostCommon(true, getDataPathPrefix() + "android", "system_server");
|
||||
}
|
||||
|
||||
private void onForkPreForDynamicMode(boolean isSystemServer) {
|
||||
mRouter.onForkStart();
|
||||
mRouter.initResourcesHook();
|
||||
mRouter.prepare(isSystemServer);
|
||||
mRouter.loadModulesSafely(true, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Some details are different between main zygote and secondary zygote.
|
||||
*/
|
||||
private void onForkPreForNonDynamicMode(boolean isSystemServer) {
|
||||
mRouter.onForkStart();
|
||||
mRouter.initResourcesHook();
|
||||
// set startsSystemServer flag used when loadModules
|
||||
mRouter.prepare(isSystemServer);
|
||||
// deoptBootMethods once for all child processes of zygote
|
||||
PrebuiltMethodsDeopter.deoptBootMethods();
|
||||
// we never install bootstrap hooks here in black/white list mode except workaround hooks
|
||||
// because installed hooks would be propagated to all child processes of zygote
|
||||
mRouter.startWorkAroundHook();
|
||||
// loadModules once for all child processes of zygote
|
||||
mRouter.loadModulesSafely(true, false);
|
||||
}
|
||||
|
||||
private void onForkPostCommon(boolean isSystemServer, String appDataDir, String niceName) {
|
||||
ConfigManager.appDataDir = appDataDir;
|
||||
ConfigManager.niceName = niceName;
|
||||
final boolean isDynamicModulesMode = ConfigManager.isDynamicModulesEnabled();
|
||||
mRouter.onEnterChildProcess();
|
||||
if (!checkNeedHook(appDataDir, niceName)) {
|
||||
// if is blacklisted, just stop here
|
||||
mRouter.onForkFinish();
|
||||
return;
|
||||
}
|
||||
if (isDynamicModulesMode) {
|
||||
mRouter.initResourcesHook();
|
||||
}
|
||||
mRouter.prepare(isSystemServer);
|
||||
PrebuiltMethodsDeopter.deoptBootMethods();
|
||||
mRouter.installBootstrapHooks(isSystemServer);
|
||||
|
||||
// under dynamic modules mode, don't call initZygote when loadModule
|
||||
// cuz loaded module won't has that chance to do it
|
||||
if (isDynamicModulesMode) {
|
||||
mRouter.loadModulesSafely(false, false);
|
||||
}
|
||||
// call all initZygote callbacks
|
||||
XposedBridge.callInitZygotes();
|
||||
|
||||
mRouter.onForkFinish();
|
||||
}
|
||||
|
||||
private boolean checkNeedHook(String appDataDir, String niceName) {
|
||||
boolean needHook;
|
||||
if (TextUtils.isEmpty(appDataDir)) {
|
||||
Utils.logE("niceName:" + niceName + ", procName:"
|
||||
+ ProcessUtils.getCurrentProcessName(ConfigManager.appProcessName) + ", appDataDir is null, blacklisted!");
|
||||
needHook = false;
|
||||
} else {
|
||||
// FIXME some process cannot read app_data_file because of MLS, e.g. bluetooth
|
||||
needHook = ConfigManager.isAppNeedHook(appDataDir);
|
||||
}
|
||||
if (!needHook) {
|
||||
// clean up the scene
|
||||
onBlackListed();
|
||||
}
|
||||
return needHook;
|
||||
}
|
||||
}
|
||||
|
|
@ -18,8 +18,9 @@ public class NormalProxy extends BaseProxy {
|
|||
String niceName, int[] fdsToClose, int[] fdsToIgnore,
|
||||
boolean startChildZygote, String instructionSet,
|
||||
String appDataDir) {
|
||||
// mainly for secondary zygote
|
||||
mRouter.onForkStart();
|
||||
}
|
||||
|
||||
public void forkAndSpecializePost(int pid, String appDataDir, String niceName) {
|
||||
SELinuxHelper.initOnce();
|
||||
mRouter.initResourcesHook();
|
||||
// call this to ensure the flag is set to false ASAP
|
||||
|
|
@ -27,18 +28,15 @@ public class NormalProxy extends BaseProxy {
|
|||
PrebuiltMethodsDeopter.deoptBootMethods(); // do it once for secondary zygote
|
||||
// install bootstrap hooks for secondary zygote
|
||||
mRouter.installBootstrapHooks(false);
|
||||
// only load modules for secondary zygote
|
||||
mRouter.loadModulesSafely(true, true);
|
||||
}
|
||||
|
||||
public void forkAndSpecializePost(int pid, String appDataDir, String niceName) {
|
||||
// TODO consider processes without forkAndSpecializePost being called
|
||||
forkPostCommon(pid, false, appDataDir, niceName);
|
||||
}
|
||||
|
||||
public void forkSystemServerPre(int uid, int gid, int[] gids, int debugFlags, int[][] rlimits,
|
||||
long permittedCapabilities, long effectiveCapabilities) {
|
||||
mRouter.onForkStart();
|
||||
}
|
||||
|
||||
public void forkSystemServerPost(int pid) {
|
||||
SELinuxHelper.initOnce();
|
||||
mRouter.initResourcesHook();
|
||||
// set startsSystemServer flag used when loadModules
|
||||
|
|
@ -48,13 +46,6 @@ public class NormalProxy extends BaseProxy {
|
|||
// in case we miss some processes not forked via forkAndSpecialize
|
||||
// for instance com.android.phone
|
||||
mRouter.installBootstrapHooks(true);
|
||||
// loadModules have to be executed in zygote even isDynamicModules is false
|
||||
// because if not global hooks installed in initZygote might not be
|
||||
// propagated to processes not forked via forkAndSpecialize
|
||||
mRouter.loadModulesSafely(true, true);
|
||||
}
|
||||
|
||||
public void forkSystemServerPost(int pid) {
|
||||
// in system_server process
|
||||
forkPostCommon(pid, true,
|
||||
getDataPathPrefix() + "android", "system_server");
|
||||
|
|
@ -66,13 +57,7 @@ public class NormalProxy extends BaseProxy {
|
|||
ConfigManager.niceName = niceName;
|
||||
mRouter.prepare(isSystem);
|
||||
mRouter.onEnterChildProcess();
|
||||
// reload module list if dynamic mode is on
|
||||
if (ConfigManager.isDynamicModulesEnabled()) {
|
||||
// FIXME this could be error-prone because hooks installed inside old versions
|
||||
// of initZygote instances of same module are not unhooked
|
||||
mRouter.loadModulesSafely(false, true);
|
||||
}
|
||||
mRouter.onForkFinish();
|
||||
mRouter.loadModulesSafely(true);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,21 +10,13 @@ public interface Router {
|
|||
|
||||
void installBootstrapHooks(boolean isSystem);
|
||||
|
||||
void loadModulesSafely(boolean isInZygote, boolean callInitZygote);
|
||||
void loadModulesSafely(boolean callInitZygote);
|
||||
|
||||
void startBootstrapHook(boolean isSystem);
|
||||
|
||||
void startSystemServerHook();
|
||||
|
||||
void startWorkAroundHook();
|
||||
|
||||
void onForkStart();
|
||||
|
||||
void onForkFinish();
|
||||
|
||||
void onEnterChildProcess();
|
||||
|
||||
void injectConfig();
|
||||
|
||||
boolean isForkCompleted();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,10 +15,13 @@ import dalvik.system.PathClassLoader;
|
|||
import de.robv.android.xposed.XC_MethodHook;
|
||||
import de.robv.android.xposed.XposedBridge;
|
||||
import de.robv.android.xposed.XposedHelpers;
|
||||
import de.robv.android.xposed.annotation.ApiSensitive;
|
||||
import de.robv.android.xposed.annotation.Level;
|
||||
|
||||
@ApiSensitive(Level.LOW)
|
||||
public class ClassLoaderUtils {
|
||||
|
||||
public static final String DEXPATH = "/system/framework/edxp.jar:/system/framework/eddalvikdx.jar:/system/framework/eddexmaker.jar";
|
||||
public static final String DEXPATH = "/system/framework/edxp.dex:/system/framework/eddalvikdx.dex:/system/framework/eddexmaker.dex";
|
||||
|
||||
public static void replaceParentClassLoader(ClassLoader appClassLoader) {
|
||||
if (appClassLoader == null) {
|
||||
|
|
|
|||
|
|
@ -7,9 +7,12 @@ import java.lang.reflect.Member;
|
|||
import java.lang.reflect.Modifier;
|
||||
|
||||
import de.robv.android.xposed.XposedHelpers;
|
||||
import de.robv.android.xposed.annotation.ApiSensitive;
|
||||
import de.robv.android.xposed.annotation.Level;
|
||||
|
||||
public class ClassUtils {
|
||||
|
||||
@ApiSensitive(Level.MIDDLE)
|
||||
public static int getClassStatus(Class clazz, boolean isUnsigned) {
|
||||
if (clazz == null) {
|
||||
return 0;
|
||||
|
|
@ -25,8 +28,9 @@ public class ClassUtils {
|
|||
/**
|
||||
* 5.0-8.0: kInitialized = 10 int
|
||||
* 8.1: kInitialized = 11 int
|
||||
* 9.0: kInitialized = 14 uint8_t
|
||||
* 9.0+: kInitialized = 14 uint8_t
|
||||
*/
|
||||
@ApiSensitive(Level.MIDDLE)
|
||||
public static boolean isInitialized(Class clazz) {
|
||||
if (Build.VERSION.SDK_INT >= 28) {
|
||||
return getClassStatus(clazz, true) == 14;
|
||||
|
|
|
|||
|
|
@ -8,10 +8,13 @@ import java.lang.reflect.Field;
|
|||
|
||||
import dalvik.system.BaseDexClassLoader;
|
||||
import dalvik.system.DexClassLoader;
|
||||
import de.robv.android.xposed.annotation.ApiSensitive;
|
||||
import de.robv.android.xposed.annotation.Level;
|
||||
|
||||
/**
|
||||
* For 6.0 only.
|
||||
*/
|
||||
@ApiSensitive(Level.LOW)
|
||||
@TargetApi(Build.VERSION_CODES.M)
|
||||
public class DexUtils {
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,10 @@ import java.io.File;
|
|||
import java.io.FileInputStream;
|
||||
import java.io.InputStreamReader;
|
||||
|
||||
import de.robv.android.xposed.annotation.ApiSensitive;
|
||||
import de.robv.android.xposed.annotation.Level;
|
||||
|
||||
@ApiSensitive(Level.LOW)
|
||||
public class ProcessUtils {
|
||||
|
||||
// Copied from UserHandle, indicates range of uids allocated for a user.
|
||||
|
|
|
|||
|
|
@ -21,6 +21,10 @@ import android.util.Log;
|
|||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import de.robv.android.xposed.annotation.ApiSensitive;
|
||||
import de.robv.android.xposed.annotation.Level;
|
||||
|
||||
@ApiSensitive(Level.LOW)
|
||||
public final class Unsafe {
|
||||
private static final String TAG = "Unsafe";
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@ import android.util.Log;
|
|||
import com.elderdrivers.riru.edxp.common.BuildConfig;
|
||||
|
||||
import de.robv.android.xposed.XposedHelpers;
|
||||
import de.robv.android.xposed.annotation.ApiSensitive;
|
||||
import de.robv.android.xposed.annotation.Level;
|
||||
|
||||
|
||||
public class Utils {
|
||||
|
|
@ -45,6 +47,7 @@ public class Utils {
|
|||
Log.e(LOG_TAG, msg, throwable);
|
||||
}
|
||||
|
||||
@ApiSensitive(Level.LOW)
|
||||
public static String getSysProp(String key) {
|
||||
try {
|
||||
Class sysProps = XposedHelpers.findClassIfExists("android.os.SystemProperties", null);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,14 @@
|
|||
package com.elderdrivers.riru.edxp.util;
|
||||
|
||||
import android.os.Build;
|
||||
|
||||
public class Versions {
|
||||
|
||||
public static boolean hasR() {
|
||||
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.R;
|
||||
}
|
||||
|
||||
public static boolean hasQ() {
|
||||
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q;
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -4,5 +4,8 @@
|
|||
/obj
|
||||
/release
|
||||
/template_override/module.prop
|
||||
/template_override/system
|
||||
/template_override/system_x86
|
||||
*.iml
|
||||
/template_override/riru/module.prop.new
|
||||
*.iml
|
||||
/.cxx
|
||||
|
|
|
|||
|
|
@ -1,10 +1,20 @@
|
|||
import org.apache.tools.ant.filters.FixCrLfFilter
|
||||
import org.gradle.internal.os.OperatingSystem
|
||||
|
||||
import java.security.MessageDigest
|
||||
|
||||
apply plugin: 'com.android.library'
|
||||
|
||||
static def calcSha256(file) {
|
||||
def md = MessageDigest.getInstance("SHA-256")
|
||||
file.eachByte 4096, { bytes, size ->
|
||||
md.update(bytes, 0, size);
|
||||
}
|
||||
return md.digest().encodeHex()
|
||||
}
|
||||
|
||||
// Values set here will be overriden by AppVeyor, feel free to modify during development.
|
||||
def buildVersionName = 'v0.4.6.4'
|
||||
def buildVersionName = 'v0.5.0.8'
|
||||
def buildVersionCode = 233
|
||||
|
||||
if (System.env.APPVEYOR_BUILD_VERSION != null) {
|
||||
|
|
@ -24,20 +34,22 @@ ext {
|
|||
module_name = "EdXposed"
|
||||
jar_dest_dir = "${projectDir}/template_override/system/framework/"
|
||||
is_windows = OperatingSystem.current().isWindows()
|
||||
backends = ["YAHFA", "SandHook", "Whale"]
|
||||
backends = ["YAHFA", "SandHook"]
|
||||
yahfa_module_id = "riru_edxposed"
|
||||
sandhook_module_id = yahfa_module_id + "_sandhook"
|
||||
whale_module_id = yahfa_module_id + "_whale"
|
||||
yahfa_authors = "solohsu, MlgmXyysd & rk700"
|
||||
sandhook_authors = "solohsu, MlgmXyysd & ganyao114"
|
||||
whale_authors = "solohsu, MlgmXyysd & asLody"
|
||||
|
||||
riruModuleId = "edxp"
|
||||
zipPathMagiskRelease = "$buildDir/tmp/release/magisk"
|
||||
|
||||
moduleMinRiruApiVersion = 9
|
||||
moduleMinRiruVersionName = "v22.0"
|
||||
moduleMaxRiruApiVersion = 9
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdkVersion 28
|
||||
compileSdkVersion androidCompileSdkVersion.toInteger()
|
||||
defaultConfig {
|
||||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
|
|
@ -45,8 +57,11 @@ android {
|
|||
externalNativeBuild {
|
||||
cmake {
|
||||
abiFilters 'arm64-v8a', 'armeabi-v7a', 'x86', 'x86_64'
|
||||
cppFlags "-std=c++17 -ffixed-x18 -Qunused-arguments -frtti"
|
||||
cFlags "-std=gnu99 -ffixed-x18 -Qunused-arguments -frtti"
|
||||
cppFlags "-std=c++17 -ffixed-x18 -Qunused-arguments -frtti -fomit-frame-pointer"
|
||||
cFlags "-std=gnu99 -ffixed-x18 -Qunused-arguments -frtti -fomit-frame-pointer"
|
||||
arguments "-DRIRU_MODULE_API_VERSION=$moduleMaxRiruApiVersion",
|
||||
"-DRIRU_MODULE_VERSION=$buildVersionCode",
|
||||
"-DRIRU_MODULE_VERSION_NAME:STRING=\"$buildVersionName\""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -63,8 +78,8 @@ android {
|
|||
release {
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
cppFlags "-fvisibility=hidden -fvisibility-inlines-hidden -O2 -s -Wno-unused-value"
|
||||
cFlags "-fvisibility=hidden -fvisibility-inlines-hidden -O2 -s -Wno-unused-value"
|
||||
cppFlags "-fvisibility=hidden -fvisibility-inlines-hidden -O2 -s -Wno-unused-value -fomit-frame-pointer"
|
||||
cFlags "-fvisibility=hidden -fvisibility-inlines-hidden -O2 -s -Wno-unused-value -fomit-frame-pointer"
|
||||
}
|
||||
}
|
||||
minifyEnabled true
|
||||
|
|
@ -76,34 +91,7 @@ android {
|
|||
path "src/main/cpp/CMakeLists.txt"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
task copyDalvikdxJar {
|
||||
def jarTask = tasks.getByPath(':dalvikdx:dexInJar')
|
||||
dependsOn jarTask
|
||||
doLast {
|
||||
copy {
|
||||
from jarTask
|
||||
into jar_dest_dir
|
||||
}
|
||||
}
|
||||
onlyIf {
|
||||
!jarTask.state.upToDate || !file(jar_dest_dir + jarTask.archiveName).exists()
|
||||
}
|
||||
}
|
||||
|
||||
task copyDexmakerJar {
|
||||
def jarTask = tasks.getByPath(':dexmaker:dexInJar')
|
||||
dependsOn jarTask
|
||||
doLast {
|
||||
copy {
|
||||
from jarTask
|
||||
into jar_dest_dir
|
||||
}
|
||||
}
|
||||
onlyIf {
|
||||
!jarTask.state.upToDate || !file(jar_dest_dir + jarTask.archiveName).exists()
|
||||
}
|
||||
ndkVersion androidCompileNdkVersion
|
||||
}
|
||||
|
||||
task cleanTemplate(type: Delete) {
|
||||
|
|
@ -125,8 +113,10 @@ afterEvaluate {
|
|||
def magiskModuleId = property("${backendLowered}" + "_module_id")
|
||||
|
||||
def prepareJarsTask = task("prepareJars${backendCapped}${variantCapped}") {
|
||||
dependsOn cleanTemplate, copyDalvikdxJar, copyDexmakerJar
|
||||
dependsOn tasks.getByPath(":edxp-${backendLowered}:makeAndCopy${variantCapped}")
|
||||
dependsOn cleanTemplate
|
||||
dependsOn tasks.getByPath(":dexmaker:copyDex${variantCapped}")
|
||||
dependsOn tasks.getByPath(":dalvikdx:copyDex${variantCapped}")
|
||||
dependsOn tasks.getByPath(":edxp-${backendLowered}:copyDex${variantCapped}")
|
||||
}
|
||||
|
||||
def prepareMagiskFilesTask = task("prepareMagiskFiles${backendCapped}${variantCapped}", type: Delete) {
|
||||
|
|
@ -148,22 +138,40 @@ afterEvaluate {
|
|||
versionCode: "$versionCode", authorList: "$authorList")
|
||||
filter(FixCrLfFilter.class, eol: FixCrLfFilter.CrLf.newInstance("lf"))
|
||||
}
|
||||
copy {
|
||||
from "${projectDir}/tpl/riru_module.prop.tpl"
|
||||
into "$templateRootPath/riru"
|
||||
rename "riru_module.prop.tpl", "module.prop.new"
|
||||
expand(minApi: "$moduleMinRiruApiVersion",
|
||||
versionName: "$version" + " ($backend)",
|
||||
versionCode: "$versionCode", authorList: "$authorList")
|
||||
filter(FixCrLfFilter.class, eol: FixCrLfFilter.CrLf.newInstance("lf"))
|
||||
}
|
||||
}
|
||||
def libPathRelease = "${buildDir}/intermediates/cmake/${variantLowered}/obj"
|
||||
def exclude_list = ["riru.sh"]
|
||||
if(backendLowered == "yahfa") {
|
||||
exclude_list.add("system/lib/libsandhook-native.so")
|
||||
exclude_list.add("system/lib64/libsandhook-native.so")
|
||||
}
|
||||
doLast {
|
||||
copy {
|
||||
from "${projectDir}/template_override"
|
||||
into zipPathMagiskRelease
|
||||
exclude exclude_list
|
||||
}
|
||||
copy {
|
||||
from "${projectDir}/template_override"
|
||||
into zipPathMagiskRelease
|
||||
include 'riru.sh'
|
||||
filter { line ->
|
||||
line.replaceAll('%%%RIRU_MODULE_ID%%%', riruModuleId)
|
||||
.replaceAll('%%%RIRU_MIN_API_VERSION%%%', moduleMinRiruApiVersion.toString())
|
||||
.replaceAll('%%%RIRU_MIN_VERSION_NAME%%%', moduleMinRiruVersionName)
|
||||
}
|
||||
filter(FixCrLfFilter.class,
|
||||
eol: FixCrLfFilter.CrLf.newInstance("lf"))
|
||||
}
|
||||
// copy {
|
||||
// from "${projectDir}/template_override/util_functions.sh"
|
||||
// into "${zipPathMagiskRelease}/"
|
||||
// filter { line -> line
|
||||
// .replaceAll('%VERSION%', "$version")
|
||||
// .replaceAll('%VERSION_CODE%', "$versionCode")
|
||||
// .replaceAll('%BACKEND%', "$backendCapped") }
|
||||
// filter(FixCrLfFilter.class, eol: FixCrLfFilter.CrLf.newInstance("lf"))
|
||||
// }
|
||||
copy {
|
||||
from "$libPathRelease/armeabi-v7a"
|
||||
into "$zipPathMagiskRelease/system/lib"
|
||||
|
|
@ -180,6 +188,13 @@ afterEvaluate {
|
|||
from "$libPathRelease/x86_64"
|
||||
into "$zipPathMagiskRelease/system_x86/lib64"
|
||||
}
|
||||
// generate sha1sum
|
||||
fileTree(zipPathMagiskRelease).matching {
|
||||
exclude "README.md", "META-INF"
|
||||
}.visit { f ->
|
||||
if (f.directory) return
|
||||
file(f.file.path + ".sha256sum").text = calcSha256(f.file)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -193,9 +208,30 @@ afterEvaluate {
|
|||
task("push${backendCapped}${variantCapped}", type: Exec) {
|
||||
dependsOn zipTask
|
||||
workingDir "${projectDir}/release"
|
||||
def commands = ["adb", "push",
|
||||
def commands = [android.adbExecutable, "push",
|
||||
"${module_name}-${backend}-${project.version}-${variantLowered}.zip",
|
||||
"/sdcard/"]
|
||||
"/data/local/tmp/"]
|
||||
if (is_windows) {
|
||||
commandLine 'cmd', '/c', commands.join(" ")
|
||||
} else {
|
||||
commandLine commands
|
||||
}
|
||||
}
|
||||
task("flash${backendCapped}${variantCapped}", type: Exec) {
|
||||
dependsOn tasks.getByPath("push${backendCapped}${variantCapped}")
|
||||
workingDir "${projectDir}/release"
|
||||
def commands = [android.adbExecutable, "shell", "su", "-c",
|
||||
"magisk --install-module /data/local/tmp/${module_name}-${backend}-${project.version}-${variantLowered}.zip"]
|
||||
if (is_windows) {
|
||||
commandLine 'cmd', '/c', commands.join(" ")
|
||||
} else {
|
||||
commandLine commands
|
||||
}
|
||||
}
|
||||
task("flashAndReboot${backendCapped}${variantCapped}", type: Exec) {
|
||||
dependsOn tasks.getByPath("flash${backendCapped}${variantCapped}")
|
||||
workingDir "${projectDir}/release"
|
||||
def commands = [android.adbExecutable, "shell", "reboot"]
|
||||
if (is_windows) {
|
||||
commandLine 'cmd', '/c', commands.join(" ")
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -103,7 +103,6 @@ remove_edxposed() {
|
|||
ui_print "- Deleting EdXposed..."
|
||||
rm -r -f /data/adb/modules/riru_edxposed/
|
||||
rm -r -f /data/adb/modules/riru_edxposed_sandhook/
|
||||
rm -r -f /data/adb/modules/riru_edxposed_whale/
|
||||
rm -r -f /data/misc/riru/modules/edxposed/
|
||||
rm -r -f /data/misc/riru/modules/edxp/
|
||||
ui_print "- Done"
|
||||
|
|
|
|||
|
|
@ -2,5 +2,10 @@ cmake_minimum_required(VERSION 3.4.1)
|
|||
|
||||
link_libraries("-ffixed-x18")
|
||||
|
||||
add_definitions(-DRIRU_MODULE)
|
||||
add_definitions(-DRIRU_MODULE_API_VERSION=${RIRU_MODULE_API_VERSION})
|
||||
add_definitions(-DRIRU_MODULE_VERSION=${RIRU_MODULE_VERSION})
|
||||
add_definitions(-DRIRU_MODULE_VERSION_NAME=${RIRU_MODULE_VERSION_NAME})
|
||||
|
||||
add_subdirectory(main)
|
||||
add_subdirectory(external)
|
||||
|
|
@ -3,5 +3,13 @@ cmake_minimum_required(VERSION 3.4.1)
|
|||
add_subdirectory(xhook)
|
||||
add_subdirectory(riru)
|
||||
add_subdirectory(yahfa)
|
||||
add_subdirectory(substrate)
|
||||
add_subdirectory(android)
|
||||
add_subdirectory(android)
|
||||
|
||||
macro(SET_OPTION option value)
|
||||
set(${option} ${value} CACHE INTERNAL "" FORCE)
|
||||
endmacro()
|
||||
|
||||
SET_OPTION(DOBBY_GENERATE_SHARED OFF)
|
||||
add_subdirectory(Dobby)
|
||||
target_include_directories(dobby PUBLIC Dobby/include)
|
||||
target_include_directories(dobby PUBLIC Dobby/builtin-plugin/AndroidRestriction)
|
||||
|
|
@ -0,0 +1 @@
|
|||
Subproject commit bb63fbeff16ee275834f3207a7a687afeed4a2b0
|
||||
|
|
@ -1,118 +0,0 @@
|
|||
#include <dlfcn.h>
|
||||
#include <memory.h>
|
||||
#include <jni.h>
|
||||
|
||||
#ifdef __LP64__
|
||||
#define LIB "/system/lib64/libmemtrack.so"
|
||||
#else
|
||||
#define LIB "/system/lib/libmemtrack.so"
|
||||
#endif
|
||||
|
||||
static void *riru_handle;
|
||||
static char *riru_module_name;
|
||||
|
||||
static void *get_handle() {
|
||||
if (riru_handle == NULL)
|
||||
riru_handle = dlopen(LIB, RTLD_NOW | RTLD_GLOBAL);
|
||||
|
||||
return riru_handle;
|
||||
}
|
||||
|
||||
const char *riru_get_module_name() {
|
||||
return riru_module_name;
|
||||
}
|
||||
|
||||
void riru_set_module_name(const char *name) {
|
||||
riru_module_name = strdup(name);
|
||||
}
|
||||
|
||||
int riru_get_version() {
|
||||
static void **sym;
|
||||
void *handle;
|
||||
if ((handle = get_handle()) == NULL) return -1;
|
||||
if (sym == NULL) sym = dlsym(handle, "riru_get_version");
|
||||
if (sym) return ((int (*)()) sym)();
|
||||
return -1;
|
||||
}
|
||||
|
||||
void *riru_get_func(const char *name) {
|
||||
static void **sym;
|
||||
void *handle;
|
||||
if ((handle = get_handle()) == NULL) return NULL;
|
||||
if (sym == NULL) sym = dlsym(handle, "riru_get_func");
|
||||
if (sym) return ((void *(*)(const char *, const char *)) sym)(riru_get_module_name(), name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *riru_get_native_method_func(const char *className, const char *name, const char *signature) {
|
||||
static void **sym;
|
||||
void *handle;
|
||||
if ((handle = get_handle()) == NULL) return NULL;
|
||||
if (sym == NULL) sym = dlsym(handle, "riru_get_native_method_func");
|
||||
if (sym)
|
||||
return ((void *(*)(const char *, const char *, const char *, const char *)) sym)(
|
||||
riru_get_module_name(), className, name, signature);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void riru_set_func(const char *name, void *func) {
|
||||
static void **sym;
|
||||
void *handle;
|
||||
if ((handle = get_handle()) == NULL) return;
|
||||
if (sym == NULL) sym = dlsym(handle, "riru_set_func");
|
||||
if (sym)
|
||||
((void *(*)(const char *, const char *, void *)) sym)(riru_get_module_name(), name, func);
|
||||
}
|
||||
|
||||
void riru_set_native_method_func(const char *className, const char *name, const char *signature,
|
||||
void *func) {
|
||||
static void **sym;
|
||||
void *handle;
|
||||
if ((handle = get_handle()) == NULL) return;
|
||||
if (sym == NULL) sym = dlsym(handle, "riru_set_native_method_func");
|
||||
if (sym)
|
||||
((void *(*)(const char *, const char *, const char *, const char *, void *)) sym)(
|
||||
riru_get_module_name(), className, name, signature, func);
|
||||
}
|
||||
|
||||
const JNINativeMethod *riru_get_original_native_methods(const char *className, const char *name,
|
||||
const char *signature) {
|
||||
static void **sym;
|
||||
void *handle;
|
||||
if ((handle = get_handle()) == NULL) return NULL;
|
||||
if (sym == NULL) sym = dlsym(handle, "riru_get_original_native_methods");
|
||||
if (sym)
|
||||
return ((JNINativeMethod *(*)(const char *, const char *, const char *)) sym)
|
||||
(className, name, signature);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int riru_is_zygote_methods_replaced() {
|
||||
static void **sym;
|
||||
void *handle;
|
||||
if ((handle = get_handle()) == NULL) return 0;
|
||||
if (sym == NULL) sym = dlsym(handle, "riru_is_zygote_methods_replaced");
|
||||
if (sym)
|
||||
return ((int (*)()) sym)();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int riru_get_nativeForkAndSpecialize_calls_count() {
|
||||
static void **sym;
|
||||
void *handle;
|
||||
if ((handle = get_handle()) == NULL) return 0;
|
||||
if (sym == NULL) sym = dlsym(handle, "riru_get_nativeForkAndSpecialize_calls_count");
|
||||
if (sym)
|
||||
return ((int (*)()) sym)();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int riru_get_nativeForkSystemServer_calls_count() {
|
||||
static void **sym;
|
||||
void *handle;
|
||||
if ((handle = get_handle()) == NULL) return 0;
|
||||
if (sym == NULL) sym = dlsym(handle, "riru_get_nativeForkSystemServer_calls_count");
|
||||
if (sym)
|
||||
return ((int (*)()) sym)();
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1,93 +1,169 @@
|
|||
#ifndef RIRU_H
|
||||
#define RIRU_H
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
__attribute__((visibility("default"))) void riru_set_module_name(const char *name);
|
||||
|
||||
/**
|
||||
* Get Riru version.
|
||||
*
|
||||
* @return Riru version
|
||||
*/
|
||||
int riru_get_version();
|
||||
#include <stdint.h>
|
||||
#include <jni.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
// ---------------------------------------------------------
|
||||
|
||||
typedef void(onModuleLoaded_v9)();
|
||||
|
||||
typedef int(shouldSkipUid_v9)(int uid);
|
||||
|
||||
typedef void(nativeForkAndSpecializePre_v9)(
|
||||
JNIEnv *env, jclass cls, jint *uid, jint *gid, jintArray *gids, jint *runtimeFlags,
|
||||
jobjectArray *rlimits, jint *mountExternal, jstring *seInfo, jstring *niceName,
|
||||
jintArray *fdsToClose, jintArray *fdsToIgnore, jboolean *is_child_zygote,
|
||||
jstring *instructionSet, jstring *appDataDir, jboolean *isTopApp, jobjectArray *pkgDataInfoList,
|
||||
jobjectArray *whitelistedDataInfoList, jboolean *bindMountAppDataDirs, jboolean *bindMountAppStorageDirs);
|
||||
|
||||
typedef void(nativeForkAndSpecializePost_v9)(JNIEnv *env, jclass cls, jint res);
|
||||
|
||||
typedef void(nativeForkSystemServerPre_v9)(
|
||||
JNIEnv *env, jclass cls, uid_t *uid, gid_t *gid, jintArray *gids, jint *runtimeFlags,
|
||||
jobjectArray *rlimits, jlong *permittedCapabilities, jlong *effectiveCapabilities);
|
||||
|
||||
typedef void(nativeForkSystemServerPost_v9)(JNIEnv *env, jclass cls, jint res);
|
||||
|
||||
typedef void(nativeSpecializeAppProcessPre_v9)(
|
||||
JNIEnv *env, jclass cls, jint *uid, jint *gid, jintArray *gids, jint *runtimeFlags,
|
||||
jobjectArray *rlimits, jint *mountExternal, jstring *seInfo, jstring *niceName,
|
||||
jboolean *startChildZygote, jstring *instructionSet, jstring *appDataDir,
|
||||
jboolean *isTopApp, jobjectArray *pkgDataInfoList, jobjectArray *whitelistedDataInfoList,
|
||||
jboolean *bindMountAppDataDirs, jboolean *bindMountAppStorageDirs);
|
||||
|
||||
typedef void(nativeSpecializeAppProcessPost_v9)(JNIEnv *env, jclass cls);
|
||||
|
||||
typedef struct {
|
||||
int supportHide;
|
||||
int version;
|
||||
const char *versionName;
|
||||
onModuleLoaded_v9 *onModuleLoaded;
|
||||
shouldSkipUid_v9 *shouldSkipUid;
|
||||
nativeForkAndSpecializePre_v9 *forkAndSpecializePre;
|
||||
nativeForkAndSpecializePost_v9 *forkAndSpecializePost;
|
||||
nativeForkSystemServerPre_v9 *forkSystemServerPre;
|
||||
nativeForkSystemServerPost_v9 *forkSystemServerPost;
|
||||
nativeSpecializeAppProcessPre_v9 *specializeAppProcessPre;
|
||||
nativeSpecializeAppProcessPost_v9 *specializeAppProcessPost;
|
||||
} RiruModuleInfoV9;
|
||||
|
||||
// ---------------------------------------------------------
|
||||
|
||||
typedef void *(RiruGetFunc_v9)(uint32_t token, const char *name);
|
||||
|
||||
typedef void (RiruSetFunc_v9)(uint32_t token, const char *name, void *func);
|
||||
|
||||
typedef void *(RiruGetJNINativeMethodFunc_v9)(uint32_t token, const char *className, const char *name, const char *signature);
|
||||
|
||||
typedef void (RiruSetJNINativeMethodFunc_v9)(uint32_t token, const char *className, const char *name, const char *signature, void *func);
|
||||
|
||||
typedef const JNINativeMethod *(RiruGetOriginalJNINativeMethodFunc_v9)(const char *className, const char *name, const char *signature);
|
||||
|
||||
typedef void *(RiruGetGlobalValue_v9)(const char *key);
|
||||
|
||||
typedef void(RiruPutGlobalValue_v9)(const char *key, void *value);
|
||||
|
||||
typedef struct {
|
||||
|
||||
uint32_t token;
|
||||
RiruGetFunc_v9 *getFunc;
|
||||
RiruGetJNINativeMethodFunc_v9 *getJNINativeMethodFunc;
|
||||
RiruSetFunc_v9 *setFunc;
|
||||
RiruSetJNINativeMethodFunc_v9 *setJNINativeMethodFunc;
|
||||
RiruGetOriginalJNINativeMethodFunc_v9 *getOriginalJNINativeMethodFunc;
|
||||
RiruGetGlobalValue_v9 *getGlobalValue;
|
||||
RiruPutGlobalValue_v9 *putGlobalValue;
|
||||
} RiruApiV9;
|
||||
|
||||
typedef void *(RiruInit_t)(void *);
|
||||
|
||||
#ifdef RIRU_MODULE
|
||||
#define RIRU_EXPORT __attribute__((visibility("default"))) __attribute__((used))
|
||||
|
||||
/*
|
||||
* Get new_func address from last module which hook func.
|
||||
* Use this as your old_func if you want to hook func.
|
||||
* Init will be called three times.
|
||||
*
|
||||
* The first time:
|
||||
* Returns the highest version number supported by both Riru and the module.
|
||||
*
|
||||
* arg: (int *) Riru's API version
|
||||
* returns: (int *) the highest possible API version
|
||||
*
|
||||
* The second time:
|
||||
* Returns the RiruModuleX struct created by the module (X is the return of the first call).
|
||||
*
|
||||
* arg: (RiruApiVX *) RiruApi strcut, this pointer can be saved for further use
|
||||
* returns: (RiruModuleX *) RiruModule strcut
|
||||
*
|
||||
* The second time:
|
||||
* Let the module to cleanup (such as RiruModuleX struct created before).
|
||||
*
|
||||
* arg: null
|
||||
* returns: (ignored)
|
||||
*
|
||||
* @param name a unique name
|
||||
* @return new_func from last module or null
|
||||
*/
|
||||
void *riru_get_func(const char *name);
|
||||
void* init(void *arg) RIRU_EXPORT;
|
||||
|
||||
/*
|
||||
* Java native version of riru_get_func.
|
||||
*
|
||||
* @param className class name
|
||||
* @param name method name
|
||||
* @param signature method signature
|
||||
* @return new_func address from last module or original address
|
||||
*/
|
||||
void *riru_get_native_method_func(const char *className, const char *name, const char *signature);
|
||||
extern int riru_api_version;
|
||||
extern RiruApiV9 *riru_api_v9;
|
||||
|
||||
/*
|
||||
* Set new_func address for next module which wants to hook func.
|
||||
*
|
||||
* @param name a unique name
|
||||
* @param func your new_func address
|
||||
*/
|
||||
void riru_set_func(const char *name, void *func);
|
||||
inline void *riru_get_func(const char *name) {
|
||||
if (riru_api_version == 9) {
|
||||
return riru_api_v9->getFunc(riru_api_v9->token, name);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Java native method version of riru_set_func.
|
||||
*
|
||||
* @param className class name
|
||||
* @param name method name
|
||||
* @param signature method signature
|
||||
* @param func your new_func address
|
||||
*/
|
||||
void riru_set_native_method_func(const char *className, const char *name, const char *signature,
|
||||
void *func);
|
||||
inline void *riru_get_native_method_func(const char *className, const char *name, const char *signature) {
|
||||
if (riru_api_version == 9) {
|
||||
return riru_api_v9->getJNINativeMethodFunc(riru_api_v9->token, className, name, signature);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get native methods from Riru's jniRegisterNativeMethods hook.
|
||||
* If both name and signature are null, all the class's methods will be returned.
|
||||
*
|
||||
* @param className className
|
||||
* @param name method name, null for the method with specific signature
|
||||
* @param signature method signature, null for the method with specific name
|
||||
* @return JNINativeMethod*
|
||||
*/
|
||||
const JNINativeMethod *riru_get_original_native_methods(const char *className, const char *name,
|
||||
const char *signature);
|
||||
inline const JNINativeMethod *riru_get_original_native_methods(const char *className, const char *name, const char *signature) {
|
||||
if (riru_api_version == 9) {
|
||||
return riru_api_v9->getOriginalJNINativeMethodFunc(className, name, signature);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return if Zygote class's native method nativeForkAndSpecialize & nativeForkSystemServer is replaced by
|
||||
* Riru.
|
||||
*
|
||||
* @return methods replaced
|
||||
*/
|
||||
int riru_is_zygote_methods_replaced();
|
||||
inline void riru_set_func(const char *name, void *func) {
|
||||
if (riru_api_version == 9) {
|
||||
riru_api_v9->setFunc(riru_api_v9->token, name, func);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return calls count of Zygote class's native method nativeForkAndSpecialize replaced by Riru.
|
||||
*
|
||||
* @return nativeForkAndSpecialize calls count
|
||||
*/
|
||||
int riru_get_nativeForkAndSpecialize_calls_count();
|
||||
inline void riru_set_native_method_func(const char *className, const char *name, const char *signature,
|
||||
void *func) {
|
||||
if (riru_api_version == 9) {
|
||||
riru_api_v9->setJNINativeMethodFunc(riru_api_v9->token, className, name, signature, func);
|
||||
}
|
||||
}
|
||||
|
||||
inline void *riru_get_global_value(const char *key) {
|
||||
if (riru_api_version == 9) {
|
||||
return riru_api_v9->getGlobalValue(key);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
inline void riru_put_global_value(const char *key, void *value) {
|
||||
if (riru_api_version == 9) {
|
||||
riru_api_v9->putGlobalValue(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Return calls count of Zygote class native's method nativeForkSystemServer replaced by Riru.
|
||||
*
|
||||
* @return nativeForkAndSpecialize calls count
|
||||
*/
|
||||
int riru_get_nativeForkSystemServer_calls_count();
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
#endif //RIRU_H
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
cmake_minimum_required(VERSION 3.4.1)
|
||||
|
||||
aux_source_directory(src SRC_LIST)
|
||||
add_library(substrate STATIC ${SRC_LIST})
|
||||
|
||||
target_include_directories(substrate INTERFACE src)
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
/* Cydia Substrate - Powerful Code Insertion Platform
|
||||
* Copyright (C) 2008-2011 Jay Freeman (saurik)
|
||||
*/
|
||||
|
||||
/* GNU Lesser General Public License, Version 3 {{{ */
|
||||
/*
|
||||
* Substrate is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Substrate 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 Lesser General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
**/
|
||||
/* }}} */
|
||||
|
||||
#ifndef SUBSTRATE_BUFFER_HPP
|
||||
#define SUBSTRATE_BUFFER_HPP
|
||||
|
||||
#include <string.h>
|
||||
|
||||
template <typename Type_>
|
||||
_disused static _finline void MSWrite(uint8_t *&buffer, Type_ value) {
|
||||
*reinterpret_cast<Type_ *>(buffer) = value;
|
||||
buffer += sizeof(Type_);
|
||||
}
|
||||
|
||||
_disused static _finline void MSWrite(uint8_t *&buffer, uint8_t *data, size_t size) {
|
||||
memcpy(buffer, data, size);
|
||||
buffer += size;
|
||||
}
|
||||
|
||||
#endif//SUBSTRATE_BUFFER_HPP
|
||||
|
|
@ -1,152 +0,0 @@
|
|||
/* Cydia Substrate - Powerful Code Insertion Platform
|
||||
* Copyright (C) 2008-2011 Jay Freeman (saurik)
|
||||
*/
|
||||
|
||||
/* GNU Lesser General Public License, Version 3 {{{ */
|
||||
/*
|
||||
* Substrate is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Substrate 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 Lesser General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
**/
|
||||
/* }}} */
|
||||
|
||||
#ifndef SUBSTRATE_H_
|
||||
#define SUBSTRATE_H_
|
||||
|
||||
#ifdef __APPLE__
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
#include <mach-o/nlist.h>
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#include <objc/runtime.h>
|
||||
#include <objc/message.h>
|
||||
#endif
|
||||
|
||||
#include <dlfcn.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define _finline \
|
||||
inline __attribute__((__always_inline__))
|
||||
#define _disused \
|
||||
__attribute__((__unused__))
|
||||
|
||||
#define _extern \
|
||||
extern "C" __attribute__((__visibility__("default")))
|
||||
|
||||
#ifdef __cplusplus
|
||||
#define _default(value) = value
|
||||
#else
|
||||
#define _default(value)
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
bool MSHookProcess(pid_t pid, const char *library);
|
||||
|
||||
typedef const void *MSImageRef;
|
||||
|
||||
MSImageRef MSGetImageByName(const char *file);
|
||||
void *MSFindSymbol(MSImageRef image, const char *name);
|
||||
|
||||
void MSHookFunction(void *symbol, void *replace, void **result);
|
||||
|
||||
#ifdef __APPLE__
|
||||
#ifdef __arm__
|
||||
__attribute__((__deprecated__))
|
||||
IMP MSHookMessage(Class _class, SEL sel, IMP imp, const char *prefix _default(NULL));
|
||||
#endif
|
||||
void MSHookMessageEx(Class _class, SEL sel, IMP imp, IMP *result);
|
||||
#endif
|
||||
|
||||
#ifdef SubstrateInternal
|
||||
typedef void *SubstrateAllocatorRef;
|
||||
typedef struct __SubstrateProcess *SubstrateProcessRef;
|
||||
typedef struct __SubstrateMemory *SubstrateMemoryRef;
|
||||
|
||||
SubstrateProcessRef SubstrateProcessCreate(SubstrateAllocatorRef allocator, pid_t pid);
|
||||
void SubstrateProcessRelease(SubstrateProcessRef process);
|
||||
|
||||
SubstrateMemoryRef SubstrateMemoryCreate(SubstrateAllocatorRef allocator, SubstrateProcessRef process, void *data, size_t size);
|
||||
void SubstrateMemoryRelease(SubstrateMemoryRef memory);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
#ifdef SubstrateInternal
|
||||
struct SubstrateHookMemory {
|
||||
SubstrateMemoryRef handle_;
|
||||
|
||||
SubstrateHookMemory(SubstrateProcessRef process, void *data, size_t size) :
|
||||
handle_(SubstrateMemoryCreate(NULL, NULL, data, size))
|
||||
{
|
||||
}
|
||||
|
||||
~SubstrateHookMemory() {
|
||||
if (handle_ != NULL)
|
||||
SubstrateMemoryRelease(handle_);
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
template<typename Type_>
|
||||
static inline void MSHookFunction(Type_ *symbol, Type_ *replace, Type_ **result) {
|
||||
MSHookFunction(
|
||||
reinterpret_cast<void *>(symbol),
|
||||
reinterpret_cast<void *>(replace),
|
||||
reinterpret_cast<void **>(result)
|
||||
);
|
||||
}
|
||||
|
||||
template<typename Type_>
|
||||
static inline void MSHookFunction(Type_ *symbol, Type_ *replace) {
|
||||
return MSHookFunction(symbol, replace, reinterpret_cast<Type_ **>(NULL));
|
||||
}
|
||||
|
||||
template<typename Type_>
|
||||
static inline void MSHookSymbol(Type_ *&value, const char *name, MSImageRef image = NULL) {
|
||||
value = reinterpret_cast<Type_ *>(MSFindSymbol(image, name));
|
||||
}
|
||||
|
||||
template<typename Type_>
|
||||
static inline void MSHookFunction(const char *name, Type_ *replace, Type_ **result = NULL) {
|
||||
Type_ *symbol;
|
||||
MSHookSymbol(symbol, name);
|
||||
return MSHookFunction(symbol, replace, result);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#define MSHook(type, name, args...) \
|
||||
_disused static type (*_ ## name)(args); \
|
||||
static type $ ## name(args)
|
||||
|
||||
#ifdef __cplusplus
|
||||
#define MSHake(name) \
|
||||
&$ ## name, &_ ## name
|
||||
#else
|
||||
#define MSHake(name) \
|
||||
&$ ## name, (void **) &_ ## name
|
||||
#endif
|
||||
|
||||
|
||||
#endif//SUBSTRATE_H_
|
||||
|
|
@ -1,67 +0,0 @@
|
|||
/* Cydia Substrate - Powerful Code Insertion Platform
|
||||
* Copyright (C) 2008-2011 Jay Freeman (saurik)
|
||||
*/
|
||||
|
||||
/* GNU Lesser General Public License, Version 3 {{{ */
|
||||
/*
|
||||
* Substrate is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Substrate 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 Lesser General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
**/
|
||||
/* }}} */
|
||||
|
||||
#ifndef SUBSTRATE_ARM_HPP
|
||||
#define SUBSTRATE_ARM_HPP
|
||||
|
||||
enum A$r {
|
||||
A$r0, A$r1, A$r2, A$r3,
|
||||
A$r4, A$r5, A$r6, A$r7,
|
||||
A$r8, A$r9, A$r10, A$r11,
|
||||
A$r12, A$r13, A$r14, A$r15,
|
||||
A$sp = A$r13,
|
||||
A$lr = A$r14,
|
||||
A$pc = A$r15
|
||||
};
|
||||
|
||||
enum A$c {
|
||||
A$eq, A$ne, A$cs, A$cc,
|
||||
A$mi, A$pl, A$vs, A$vc,
|
||||
A$hi, A$ls, A$ge, A$lt,
|
||||
A$gt, A$le, A$al,
|
||||
A$hs = A$cs,
|
||||
A$lo = A$cc
|
||||
};
|
||||
|
||||
template<class T> static T xabs(T _Val);
|
||||
|
||||
#define A$mrs_rm_cpsr(rd) /* mrs rd, cpsr */ \
|
||||
(0xe10f0000 | ((rd) << 12))
|
||||
#define A$msr_cpsr_f_rm(rm) /* msr cpsr_f, rm */ \
|
||||
(0xe128f000 | (rm))
|
||||
#define A$ldr_rd_$rn_im$(rd, rn, im) /* ldr rd, [rn, #im] */ \
|
||||
(0xe5100000 | ((im) < 0 ? 0 : 1 << 23) | ((rn) << 16) | ((rd) << 12) | xabs(im))
|
||||
#define A$str_rd_$rn_im$(rd, rn, im) /* sr rd, [rn, #im] */ \
|
||||
(0xe5000000 | ((im) < 0 ? 0 : 1 << 23) | ((rn) << 16) | ((rd) << 12) | xabs(im))
|
||||
#define A$sub_rd_rn_$im(rd, rn, im) /* sub, rd, rn, #im */ \
|
||||
(0xe2400000 | ((rn) << 16) | ((rd) << 12) | (im & 0xff))
|
||||
#define A$blx_rm(rm) /* blx rm */ \
|
||||
(0xe12fff30 | (rm))
|
||||
#define A$mov_rd_rm(rd, rm) /* mov rd, rm */ \
|
||||
(0xe1a00000 | ((rd) << 12) | (rm))
|
||||
#define A$ldmia_sp$_$rs$(rs) /* ldmia sp!, {rs} */ \
|
||||
(0xe8b00000 | (A$sp << 16) | (rs))
|
||||
#define A$stmdb_sp$_$rs$(rs) /* stmdb sp!, {rs} */ \
|
||||
(0xe9200000 | (A$sp << 16) | (rs))
|
||||
#define A$stmia_sp$_$r0$ 0xe8ad0001 /* stmia sp!, {r0} */
|
||||
#define A$bx_r0 0xe12fff10 /* bx r0 */
|
||||
|
||||
#endif//SUBSTRATE_ARM_HPP
|
||||
|
|
@ -1,96 +0,0 @@
|
|||
/* Cydia Substrate - Powerful Code Insertion Platform
|
||||
* Copyright (C) 2008-2011 Jay Freeman (saurik)
|
||||
*/
|
||||
|
||||
/* GNU Lesser General Public License, Version 3 {{{ */
|
||||
/*
|
||||
* Substrate is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Substrate 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 Lesser General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
**/
|
||||
/* }}} */
|
||||
|
||||
#include "SubstrateHook.h"
|
||||
#include "SubstrateDebug.hpp"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
_extern bool MSDebug;
|
||||
bool MSDebug = false;
|
||||
|
||||
static char _MSHexChar(uint8_t value) {
|
||||
return value < 0x20 || value >= 0x80 ? '.' : value;
|
||||
}
|
||||
|
||||
#define HexWidth_ 16
|
||||
#define HexDepth_ 4
|
||||
|
||||
void MSLogHexEx(const void *vdata, size_t size, size_t stride, const char *mark) {
|
||||
const uint8_t *data((const uint8_t *) vdata);
|
||||
|
||||
size_t i(0), j;
|
||||
|
||||
char d[256];
|
||||
size_t b(0);
|
||||
d[0] = '\0';
|
||||
|
||||
while (i != size) {
|
||||
if (i % HexWidth_ == 0) {
|
||||
if (mark != NULL)
|
||||
b += sprintf(d + b, "\n[%s] ", mark);
|
||||
b += sprintf(d + b, "0x%.3zx:", i);
|
||||
}
|
||||
|
||||
b += sprintf(d + b, " ");
|
||||
|
||||
for (size_t q(0); q != stride; ++q)
|
||||
b += sprintf(d + b, "%.2x", data[i + stride - q - 1]);
|
||||
|
||||
i += stride;
|
||||
|
||||
for (size_t q(1); q != stride; ++q)
|
||||
b += sprintf(d + b, " ");
|
||||
|
||||
if (i % HexDepth_ == 0)
|
||||
b += sprintf(d + b, " ");
|
||||
|
||||
if (i % HexWidth_ == 0) {
|
||||
b += sprintf(d + b, " ");
|
||||
for (j = i - HexWidth_; j != i; ++j)
|
||||
b += sprintf(d + b, "%c", _MSHexChar(data[j]));
|
||||
|
||||
lprintf("%s", d);
|
||||
b = 0;
|
||||
d[0] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
if (i % HexWidth_ != 0) {
|
||||
for (j = i % HexWidth_; j != HexWidth_; ++j)
|
||||
b += sprintf(d + b, " ");
|
||||
for (j = 0; j != (HexWidth_ - i % HexWidth_ + HexDepth_ - 1) / HexDepth_; ++j)
|
||||
b += sprintf(d + b, " ");
|
||||
b += sprintf(d + b, " ");
|
||||
for (j = i / HexWidth_ * HexWidth_; j != i; ++j)
|
||||
b += sprintf(d + b, "%c", _MSHexChar(data[j]));
|
||||
|
||||
lprintf("%s", d);
|
||||
b = 0;
|
||||
d[0] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
void MSLogHex(const void *vdata, size_t size, const char *mark) {
|
||||
return MSLogHexEx(vdata, size, 1, mark);
|
||||
}
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
/* Cydia Substrate - Powerful Code Insertion Platform
|
||||
* Copyright (C) 2008-2011 Jay Freeman (saurik)
|
||||
*/
|
||||
|
||||
/* GNU Lesser General Public License, Version 3 {{{ */
|
||||
/*
|
||||
* Substrate is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Substrate 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 Lesser General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
**/
|
||||
/* }}} */
|
||||
|
||||
#ifndef SUBSTRATE_DEBUG_HPP
|
||||
#define SUBSTRATE_DEBUG_HPP
|
||||
|
||||
#include "SubstrateLog.hpp"
|
||||
#define lprintf(format, ...) \
|
||||
MSLog(MSLogLevelNotice, format, ## __VA_ARGS__)
|
||||
|
||||
extern "C" bool MSDebug;
|
||||
void MSLogHexEx(const void *vdata, size_t size, size_t stride, const char *mark = 0);
|
||||
void MSLogHex(const void *vdata, size_t size, const char *mark = 0);
|
||||
|
||||
#endif//SUBSTRATE_DEBUG_HPP
|
||||
|
|
@ -1,945 +0,0 @@
|
|||
/* Cydia Substrate - Powerful Code Insertion Platform
|
||||
* Copyright (C) 2008-2011 Jay Freeman (saurik)
|
||||
*/
|
||||
|
||||
/* GNU Lesser General Public License, Version 3 {{{ */
|
||||
/*
|
||||
* Substrate is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Substrate 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 Lesser General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
**/
|
||||
/* }}} */
|
||||
|
||||
#define SubstrateInternal
|
||||
#include "CydiaSubstrate.h"
|
||||
|
||||
#include <sys/mman.h>
|
||||
|
||||
#define _trace() do { \
|
||||
MSLog(MSLogLevelNotice, "_trace(%u)", __LINE__); \
|
||||
} while (false)
|
||||
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
#include "hde64.h"
|
||||
#endif
|
||||
|
||||
#include "SubstrateDebug.hpp"
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <cmath>
|
||||
|
||||
#ifdef __arm__
|
||||
/* WebCore (ARM) PC-Relative:
|
||||
X 1 ldr r*,[pc,r*] !=
|
||||
2 fldd d*,[pc,#*]
|
||||
X 5 str r*,[pc,r*] !=
|
||||
8 flds s*,[pc,#*]
|
||||
400 ldr r*,[pc,r*] ==
|
||||
515 add r*, pc,r* ==
|
||||
X 4790 ldr r*,[pc,#*] */
|
||||
|
||||
// x=0; while IFS= read -r line; do if [[ ${#line} -ne 0 && $line == +([^\;]): ]]; then x=2; elif [[ $line == ' +'* && $x -ne 0 ]]; then ((--x)); echo "$x${line}"; fi; done <WebCore.asm >WebCore.pc
|
||||
// grep pc WebCore.pc | cut -c 40- | sed -Ee 's/^ldr *(ip|r[0-9]*),\[pc,\#0x[0-9a-f]*\].*/ ldr r*,[pc,#*]/;s/^add *r[0-9]*,pc,r[0-9]*.*/ add r*, pc,r*/;s/^(st|ld)r *r([0-9]*),\[pc,r([0-9]*)\].*/ \1r r\2,[pc,r\3]/;s/^fld(s|d) *(s|d)[0-9]*,\[pc,#0x[0-9a-f]*].*/fld\1 \2*,[pc,#*]/' | sort | uniq -c | sort -n
|
||||
|
||||
#include "SubstrateARM.hpp"
|
||||
|
||||
#define T$Label(l, r) \
|
||||
(((r) - (l)) * 2 - 4 + ((l) % 2 == 0 ? 0 : 2))
|
||||
|
||||
#define T$pop_$r0$ 0xbc01 // pop {r0}
|
||||
#define T$b(im) /* b im */ \
|
||||
(0xde00 | (im & 0xff))
|
||||
#define T$blx(rm) /* blx rm */ \
|
||||
(0x4780 | (rm << 3))
|
||||
#define T$bx(rm) /* bx rm */ \
|
||||
(0x4700 | (rm << 3))
|
||||
#define T$nop /* nop */ \
|
||||
(0x46c0)
|
||||
|
||||
#define T$add_rd_rm(rd, rm) /* add rd, rm */ \
|
||||
(0x4400 | (((rd) & 0x8) >> 3 << 7) | (((rm) & 0x8) >> 3 << 6) | (((rm) & 0x7) << 3) | ((rd) & 0x7))
|
||||
#define T$push_r(r) /* push r... */ \
|
||||
(0xb400 | (((r) & (1 << A$lr)) >> A$lr << 8) | ((r) & 0xff))
|
||||
#define T$pop_r(r) /* pop r... */ \
|
||||
(0xbc00 | (((r) & (1 << A$pc)) >> A$pc << 8) | ((r) & 0xff))
|
||||
#define T$mov_rd_rm(rd, rm) /* mov rd, rm */ \
|
||||
(0x4600 | (((rd) & 0x8) >> 3 << 7) | (((rm) & 0x8) >> 3 << 6) | (((rm) & 0x7) << 3) | ((rd) & 0x7))
|
||||
#define T$ldr_rd_$rn_im_4$(rd, rn, im) /* ldr rd, [rn, #im * 4] */ \
|
||||
(0x6800 | (((im) & 0x1f) << 6) | ((rn) << 3) | (rd))
|
||||
#define T$ldr_rd_$pc_im_4$(rd, im) /* ldr rd, [PC, #im * 4] */ \
|
||||
(0x4800 | ((rd) << 8) | ((im) & 0xff))
|
||||
#define T$cmp_rn_$im(rn, im) /* cmp rn, #im */ \
|
||||
(0x2000 | ((rn) << 8) | ((im) & 0xff))
|
||||
#define T$it$_cd(cd, ms) /* it<ms>, cd */ \
|
||||
(0xbf00 | ((cd) << 4) | (ms))
|
||||
#define T$cbz$_rn_$im(op,rn,im) /* cb<op>z rn, #im */ \
|
||||
(0xb100 | ((op) << 11) | (((im) & 0x40) >> 6 << 9) | (((im) & 0x3e) >> 1 << 3) | (rn))
|
||||
#define T$b$_$im(cond,im) /* b<cond> #im */ \
|
||||
(cond == A$al ? 0xe000 | (((im) >> 1) & 0x7ff) : 0xd000 | ((cond) << 8) | (((im) >> 1) & 0xff))
|
||||
|
||||
#define T1$ldr_rt_$rn_im$(rt, rn, im) /* ldr rt, [rn, #im] */ \
|
||||
(0xf850 | ((im < 0 ? 0 : 1) << 7) | (rn))
|
||||
|
||||
template<class T> T xabs(T _Val)
|
||||
{
|
||||
typedef int BOOL;
|
||||
if(_Val>T(0))return _Val;
|
||||
return -_Val;
|
||||
}
|
||||
|
||||
#define T2$ldr_rt_$rn_im$(rt, rn, im) /* ldr rt, [rn, #im] */ \
|
||||
(((rt) << 12) | xabs(im))
|
||||
|
||||
#define T1$mrs_rd_apsr(rd) /* mrs rd, apsr */ \
|
||||
(0xf3ef)
|
||||
#define T2$mrs_rd_apsr(rd) /* mrs rd, apsr */ \
|
||||
(0x8000 | ((rd) << 8))
|
||||
|
||||
#define T1$msr_apsr_nzcvqg_rn(rn) /* msr apsr, rn */ \
|
||||
(0xf380 | (rn))
|
||||
#define T2$msr_apsr_nzcvqg_rn(rn) /* msr apsr, rn */ \
|
||||
(0x8c00)
|
||||
#define T$msr_apsr_nzcvqg_rn(rn) /* msr apsr, rn */ \
|
||||
(T2$msr_apsr_nzcvqg_rn(rn) << 16 | T1$msr_apsr_nzcvqg_rn(rn))
|
||||
|
||||
static inline bool A$pcrel$r(uint32_t ic) {
|
||||
return (ic & 0x0c000000) == 0x04000000 && (ic & 0xf0000000) != 0xf0000000 && (ic & 0x000f0000) == 0x000f0000;
|
||||
}
|
||||
|
||||
static inline bool T$32bit$i(uint16_t ic) {
|
||||
return ((ic & 0xe000) == 0xe000 && (ic & 0x1800) != 0x0000);
|
||||
}
|
||||
|
||||
static inline bool T$pcrel$cbz(uint16_t ic) {
|
||||
return (ic & 0xf500) == 0xb100;
|
||||
}
|
||||
|
||||
static inline bool T$pcrel$b(uint16_t ic) {
|
||||
return (ic & 0xf000) == 0xd000 && (ic & 0x0e00) != 0x0e00;
|
||||
}
|
||||
|
||||
static inline bool T2$pcrel$b(uint16_t *ic) {
|
||||
return (ic[0] & 0xf800) == 0xf000 && ((ic[1] & 0xd000) == 0x9000 || (ic[1] & 0xd000) == 0x8000 && (ic[0] & 0x0380) != 0x0380);
|
||||
}
|
||||
|
||||
static inline bool T$pcrel$bl(uint16_t *ic) {
|
||||
return (ic[0] & 0xf800) == 0xf000 && ((ic[1] & 0xd000) == 0xd000 || (ic[1] & 0xd001) == 0xc000);
|
||||
}
|
||||
|
||||
static inline bool T$pcrel$ldr(uint16_t ic) {
|
||||
return (ic & 0xf800) == 0x4800;
|
||||
}
|
||||
|
||||
static inline bool T$pcrel$add(uint16_t ic) {
|
||||
return (ic & 0xff78) == 0x4478;
|
||||
}
|
||||
|
||||
static inline bool T$pcrel$ldrw(uint16_t ic) {
|
||||
return (ic & 0xff7f) == 0xf85f;
|
||||
}
|
||||
|
||||
static size_t MSGetInstructionWidthThumb(void *start) {
|
||||
uint16_t *thumb(reinterpret_cast<uint16_t *>(start));
|
||||
return T$32bit$i(thumb[0]) ? 4 : 2;
|
||||
}
|
||||
|
||||
static size_t MSGetInstructionWidthARM(void *start) {
|
||||
return 4;
|
||||
}
|
||||
|
||||
extern "C" size_t MSGetInstructionWidth(void *start) {
|
||||
if ((reinterpret_cast<uintptr_t>(start) & 0x1) == 0)
|
||||
return MSGetInstructionWidthARM(start);
|
||||
else
|
||||
return MSGetInstructionWidthThumb(reinterpret_cast<void *>(reinterpret_cast<uintptr_t>(start) & ~0x1));
|
||||
}
|
||||
|
||||
static size_t SubstrateHookFunctionThumb(SubstrateProcessRef process, void *symbol, void *replace, void **result) {
|
||||
if (symbol == NULL)
|
||||
return 0;
|
||||
printf("SubstrateHookFunctionThumb\n");
|
||||
uint16_t *area(reinterpret_cast<uint16_t *>(symbol));
|
||||
|
||||
unsigned align((reinterpret_cast<uintptr_t>(area) & 0x2) == 0 ? 0 : 1);
|
||||
uint16_t *thumb(area + align);
|
||||
|
||||
uint32_t *arm(reinterpret_cast<uint32_t *>(thumb + 2));
|
||||
uint16_t *trail(reinterpret_cast<uint16_t *>(arm + 2));
|
||||
|
||||
if (
|
||||
(align == 0 || area[0] == T$nop) &&
|
||||
thumb[0] == T$bx(A$pc) &&
|
||||
thumb[1] == T$nop &&
|
||||
arm[0] == A$ldr_rd_$rn_im$(A$pc, A$pc, 4 - 8)
|
||||
) {
|
||||
if (result != NULL)
|
||||
*result = reinterpret_cast<void *>(arm[1]);
|
||||
|
||||
SubstrateHookMemory code(process, arm + 1, sizeof(uint32_t) * 1);
|
||||
|
||||
arm[1] = reinterpret_cast<uint32_t>(replace);
|
||||
|
||||
return sizeof(arm[0]);
|
||||
}
|
||||
|
||||
size_t required((trail - area) * sizeof(uint16_t));
|
||||
|
||||
size_t used(0);
|
||||
while (used < required)
|
||||
used += MSGetInstructionWidthThumb(reinterpret_cast<uint8_t *>(area) + used);
|
||||
used = (used + sizeof(uint16_t) - 1) / sizeof(uint16_t) * sizeof(uint16_t);
|
||||
|
||||
size_t blank((used - required) / sizeof(uint16_t));
|
||||
|
||||
uint16_t backup[used / sizeof(uint16_t)];
|
||||
memcpy(backup, area, used);
|
||||
|
||||
if (MSDebug) {
|
||||
char name[16];
|
||||
sprintf(name, "%p", area);
|
||||
MSLogHexEx(area, used + sizeof(uint16_t), 2, name);
|
||||
}
|
||||
|
||||
if (result != NULL) {
|
||||
|
||||
size_t length(used);
|
||||
for (unsigned offset(0); offset != used / sizeof(uint16_t); ++offset)
|
||||
if (T$pcrel$ldr(backup[offset]))
|
||||
length += 3 * sizeof(uint16_t);
|
||||
else if (T$pcrel$b(backup[offset]))
|
||||
length += 6 * sizeof(uint16_t);
|
||||
else if (T2$pcrel$b(backup + offset)) {
|
||||
length += 5 * sizeof(uint16_t);
|
||||
++offset;
|
||||
} else if (T$pcrel$bl(backup + offset)) {
|
||||
length += 5 * sizeof(uint16_t);
|
||||
++offset;
|
||||
} else if (T$pcrel$cbz(backup[offset])) {
|
||||
length += 16 * sizeof(uint16_t);
|
||||
} else if (T$pcrel$ldrw(backup[offset])) {
|
||||
length += 4 * sizeof(uint16_t);
|
||||
++offset;
|
||||
} else if (T$pcrel$add(backup[offset]))
|
||||
length += 6 * sizeof(uint16_t);
|
||||
else if (T$32bit$i(backup[offset]))
|
||||
++offset;
|
||||
|
||||
unsigned pad((length & 0x2) == 0 ? 0 : 1);
|
||||
length += (pad + 2) * sizeof(uint16_t) + 2 * sizeof(uint32_t);
|
||||
|
||||
uint16_t *buffer(reinterpret_cast<uint16_t *>(mmap(
|
||||
NULL, length, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0
|
||||
)));
|
||||
|
||||
if (buffer == MAP_FAILED) {
|
||||
MSLog(MSLogLevelError, "MS:Error:mmap() = %d", errno);
|
||||
*result = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (false) fail: {
|
||||
munmap(buffer, length);
|
||||
*result = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t start(pad), end(length / sizeof(uint16_t));
|
||||
uint32_t *trailer(reinterpret_cast<uint32_t *>(buffer + end));
|
||||
for (unsigned offset(0); offset != used / sizeof(uint16_t); ++offset) {
|
||||
if (T$pcrel$ldr(backup[offset])) {
|
||||
union {
|
||||
uint16_t value;
|
||||
|
||||
struct {
|
||||
uint16_t immediate : 8;
|
||||
uint16_t rd : 3;
|
||||
uint16_t : 5;
|
||||
};
|
||||
} bits = {backup[offset+0]};
|
||||
|
||||
buffer[start+0] = T$ldr_rd_$pc_im_4$(bits.rd, T$Label(start+0, end-2) / 4);
|
||||
buffer[start+1] = T$ldr_rd_$rn_im_4$(bits.rd, bits.rd, 0);
|
||||
|
||||
// XXX: this code "works", but is "wrong": the mechanism is more complex than this
|
||||
*--trailer = ((reinterpret_cast<uint32_t>(area + offset) + 4) & ~0x2) + bits.immediate * 4;
|
||||
|
||||
start += 2;
|
||||
end -= 2;
|
||||
} else if (T$pcrel$b(backup[offset])) {
|
||||
union {
|
||||
uint16_t value;
|
||||
|
||||
struct {
|
||||
uint16_t imm8 : 8;
|
||||
uint16_t cond : 4;
|
||||
uint16_t /*1101*/ : 4;
|
||||
};
|
||||
} bits = {backup[offset+0]};
|
||||
|
||||
intptr_t jump(bits.imm8 << 1);
|
||||
jump |= 1;
|
||||
jump <<= 23;
|
||||
jump >>= 23;
|
||||
|
||||
buffer[start+0] = T$b$_$im(bits.cond, (end-6 - (start+0)) * 2 - 4);
|
||||
|
||||
*--trailer = reinterpret_cast<uint32_t>(area + offset) + 4 + jump;
|
||||
*--trailer = A$ldr_rd_$rn_im$(A$pc, A$pc, 4 - 8);
|
||||
*--trailer = T$nop << 16 | T$bx(A$pc);
|
||||
|
||||
start += 1;
|
||||
end -= 6;
|
||||
} else if (T2$pcrel$b(backup + offset)) {
|
||||
union {
|
||||
uint16_t value;
|
||||
|
||||
struct {
|
||||
uint16_t imm6 : 6;
|
||||
uint16_t cond : 4;
|
||||
uint16_t s : 1;
|
||||
uint16_t : 5;
|
||||
};
|
||||
} bits = {backup[offset+0]};
|
||||
|
||||
union {
|
||||
uint16_t value;
|
||||
|
||||
struct {
|
||||
uint16_t imm11 : 11;
|
||||
uint16_t j2 : 1;
|
||||
uint16_t a : 1;
|
||||
uint16_t j1 : 1;
|
||||
uint16_t : 2;
|
||||
};
|
||||
} exts = {backup[offset+1]};
|
||||
|
||||
intptr_t jump(1);
|
||||
jump |= exts.imm11 << 1;
|
||||
jump |= bits.imm6 << 12;
|
||||
|
||||
if (exts.a) {
|
||||
jump |= bits.s << 24;
|
||||
jump |= (~(bits.s ^ exts.j1) & 0x1) << 23;
|
||||
jump |= (~(bits.s ^ exts.j2) & 0x1) << 22;
|
||||
jump |= bits.cond << 18;
|
||||
jump <<= 7;
|
||||
jump >>= 7;
|
||||
} else {
|
||||
jump |= bits.s << 20;
|
||||
jump |= exts.j2 << 19;
|
||||
jump |= exts.j1 << 18;
|
||||
jump <<= 11;
|
||||
jump >>= 11;
|
||||
}
|
||||
|
||||
buffer[start+0] = T$b$_$im(exts.a ? A$al : bits.cond, (end-6 - (start+0)) * 2 - 4);
|
||||
|
||||
*--trailer = reinterpret_cast<uint32_t>(area + offset) + 4 + jump;
|
||||
*--trailer = A$ldr_rd_$rn_im$(A$pc, A$pc, 4 - 8);
|
||||
*--trailer = T$nop << 16 | T$bx(A$pc);
|
||||
|
||||
++offset;
|
||||
start += 1;
|
||||
end -= 6;
|
||||
} else if (T$pcrel$bl(backup + offset)) {
|
||||
union {
|
||||
uint16_t value;
|
||||
|
||||
struct {
|
||||
uint16_t immediate : 10;
|
||||
uint16_t s : 1;
|
||||
uint16_t : 5;
|
||||
};
|
||||
} bits = {backup[offset+0]};
|
||||
|
||||
union {
|
||||
uint16_t value;
|
||||
|
||||
struct {
|
||||
uint16_t immediate : 11;
|
||||
uint16_t j2 : 1;
|
||||
uint16_t x : 1;
|
||||
uint16_t j1 : 1;
|
||||
uint16_t : 2;
|
||||
};
|
||||
} exts = {backup[offset+1]};
|
||||
|
||||
int32_t jump(0);
|
||||
jump |= bits.s << 24;
|
||||
jump |= (~(bits.s ^ exts.j1) & 0x1) << 23;
|
||||
jump |= (~(bits.s ^ exts.j2) & 0x1) << 22;
|
||||
jump |= bits.immediate << 12;
|
||||
jump |= exts.immediate << 1;
|
||||
jump |= exts.x;
|
||||
jump <<= 7;
|
||||
jump >>= 7;
|
||||
|
||||
buffer[start+0] = T$push_r(1 << A$r7);
|
||||
buffer[start+1] = T$ldr_rd_$pc_im_4$(A$r7, ((end-2 - (start+1)) * 2 - 4 + 2) / 4);
|
||||
buffer[start+2] = T$mov_rd_rm(A$lr, A$r7);
|
||||
buffer[start+3] = T$pop_r(1 << A$r7);
|
||||
buffer[start+4] = T$blx(A$lr);
|
||||
|
||||
*--trailer = reinterpret_cast<uint32_t>(area + offset) + 4 + jump;
|
||||
|
||||
++offset;
|
||||
start += 5;
|
||||
end -= 2;
|
||||
} else if (T$pcrel$cbz(backup[offset])) {
|
||||
union {
|
||||
uint16_t value;
|
||||
|
||||
struct {
|
||||
uint16_t rn : 3;
|
||||
uint16_t immediate : 5;
|
||||
uint16_t : 1;
|
||||
uint16_t i : 1;
|
||||
uint16_t : 1;
|
||||
uint16_t op : 1;
|
||||
uint16_t : 4;
|
||||
};
|
||||
} bits = {backup[offset+0]};
|
||||
|
||||
intptr_t jump(1);
|
||||
jump |= bits.i << 6;
|
||||
jump |= bits.immediate << 1;
|
||||
|
||||
//jump <<= 24;
|
||||
//jump >>= 24;
|
||||
|
||||
unsigned rn(bits.rn);
|
||||
unsigned rt(rn == A$r7 ? A$r6 : A$r7);
|
||||
|
||||
buffer[start+0] = T$push_r(1 << rt);
|
||||
buffer[start+1] = T1$mrs_rd_apsr(rt);
|
||||
buffer[start+2] = T2$mrs_rd_apsr(rt);
|
||||
buffer[start+3] = T$cbz$_rn_$im(bits.op, rn, (end-10 - (start+3)) * 2 - 4);
|
||||
buffer[start+4] = T1$msr_apsr_nzcvqg_rn(rt);
|
||||
buffer[start+5] = T2$msr_apsr_nzcvqg_rn(rt);
|
||||
buffer[start+6] = T$pop_r(1 << rt);
|
||||
|
||||
*--trailer = reinterpret_cast<uint32_t>(area + offset) + 4 + jump;
|
||||
*--trailer = A$ldr_rd_$rn_im$(A$pc, A$pc, 4 - 8);
|
||||
*--trailer = T$nop << 16 | T$bx(A$pc);
|
||||
*--trailer = T$nop << 16 | T$pop_r(1 << rt);
|
||||
*--trailer = T$msr_apsr_nzcvqg_rn(rt);
|
||||
|
||||
#if 0
|
||||
if ((start & 0x1) == 0)
|
||||
buffer[start++] = T$nop;
|
||||
buffer[start++] = T$bx(A$pc);
|
||||
buffer[start++] = T$nop;
|
||||
|
||||
uint32_t *arm(reinterpret_cast<uint32_t *>(buffer + start));
|
||||
arm[0] = A$add(A$lr, A$pc, 1);
|
||||
arm[1] = A$ldr_rd_$rn_im$(A$pc, A$pc, (trailer - arm) * sizeof(uint32_t) - 8);
|
||||
#endif
|
||||
|
||||
start += 7;
|
||||
end -= 10;
|
||||
} else if (T$pcrel$ldrw(backup[offset])) {
|
||||
union {
|
||||
uint16_t value;
|
||||
|
||||
struct {
|
||||
uint16_t : 7;
|
||||
uint16_t u : 1;
|
||||
uint16_t : 8;
|
||||
};
|
||||
} bits = {backup[offset+0]};
|
||||
|
||||
union {
|
||||
uint16_t value;
|
||||
|
||||
struct {
|
||||
uint16_t immediate : 12;
|
||||
uint16_t rt : 4;
|
||||
};
|
||||
} exts = {backup[offset+1]};
|
||||
|
||||
buffer[start+0] = T1$ldr_rt_$rn_im$(exts.rt, A$pc, T$Label(start+0, end-2));
|
||||
buffer[start+1] = T2$ldr_rt_$rn_im$(exts.rt, A$pc, T$Label(start+0, end-2));
|
||||
|
||||
buffer[start+2] = T1$ldr_rt_$rn_im$(exts.rt, exts.rt, 0);
|
||||
buffer[start+3] = T2$ldr_rt_$rn_im$(exts.rt, exts.rt, 0);
|
||||
|
||||
// XXX: this code "works", but is "wrong": the mechanism is more complex than this
|
||||
*--trailer = ((reinterpret_cast<uint32_t>(area + offset) + 4) & ~0x2) + (bits.u == 0 ? -exts.immediate : exts.immediate);
|
||||
|
||||
++offset;
|
||||
start += 4;
|
||||
end -= 2;
|
||||
} else if (T$pcrel$add(backup[offset])) {
|
||||
union {
|
||||
uint16_t value;
|
||||
|
||||
struct {
|
||||
uint16_t rd : 3;
|
||||
uint16_t rm : 3;
|
||||
uint16_t h2 : 1;
|
||||
uint16_t h1 : 1;
|
||||
uint16_t : 8;
|
||||
};
|
||||
} bits = {backup[offset+0]};
|
||||
|
||||
if (bits.h1) {
|
||||
MSLog(MSLogLevelError, "MS:Error:pcrel(%u):add (rd > r7)", offset);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
unsigned rt(bits.rd == A$r7 ? A$r6 : A$r7);
|
||||
|
||||
buffer[start+0] = T$push_r(1 << rt);
|
||||
buffer[start+1] = T$mov_rd_rm(rt, (bits.h1 << 3) | bits.rd);
|
||||
buffer[start+2] = T$ldr_rd_$pc_im_4$(bits.rd, T$Label(start+2, end-2) / 4);
|
||||
buffer[start+3] = T$add_rd_rm((bits.h1 << 3) | bits.rd, rt);
|
||||
buffer[start+4] = T$pop_r(1 << rt);
|
||||
*--trailer = reinterpret_cast<uint32_t>(area + offset) + 4;
|
||||
|
||||
start += 5;
|
||||
end -= 2;
|
||||
} else if (T$32bit$i(backup[offset])) {
|
||||
buffer[start++] = backup[offset];
|
||||
buffer[start++] = backup[++offset];
|
||||
} else {
|
||||
buffer[start++] = backup[offset];
|
||||
}
|
||||
}
|
||||
|
||||
buffer[start++] = T$bx(A$pc);
|
||||
buffer[start++] = T$nop;
|
||||
|
||||
uint32_t *transfer = reinterpret_cast<uint32_t *>(buffer + start);
|
||||
transfer[0] = A$ldr_rd_$rn_im$(A$pc, A$pc, 4 - 8);
|
||||
transfer[1] = reinterpret_cast<uint32_t>(area + used / sizeof(uint16_t)) + 1;
|
||||
|
||||
if (mprotect(buffer, length, PROT_READ | PROT_EXEC) == -1) {
|
||||
MSLog(MSLogLevelError, "MS:Error:mprotect():%d", errno);
|
||||
return 0;
|
||||
}
|
||||
|
||||
*result = reinterpret_cast<uint8_t *>(buffer + pad) + 1;
|
||||
|
||||
if (MSDebug) {
|
||||
char name[16];
|
||||
sprintf(name, "%p", *result);
|
||||
MSLogHexEx(buffer, length, 2, name);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
{
|
||||
SubstrateHookMemory code(process, area, used);
|
||||
|
||||
if (align != 0)
|
||||
area[0] = T$nop;
|
||||
|
||||
thumb[0] = T$bx(A$pc);
|
||||
thumb[1] = T$nop;
|
||||
|
||||
arm[0] = A$ldr_rd_$rn_im$(A$pc, A$pc, 4 - 8);
|
||||
arm[1] = reinterpret_cast<uint32_t>(replace);
|
||||
|
||||
for (unsigned offset(0); offset != blank; ++offset)
|
||||
trail[offset] = T$nop;
|
||||
}
|
||||
|
||||
if (MSDebug) {
|
||||
char name[16];
|
||||
sprintf(name, "%p", area);
|
||||
MSLogHexEx(area, used + sizeof(uint16_t), 2, name);
|
||||
}
|
||||
|
||||
return used;
|
||||
}
|
||||
|
||||
static size_t SubstrateHookFunctionARM(SubstrateProcessRef process, void *symbol, void *replace, void **result) {
|
||||
if (symbol == NULL)
|
||||
return 0;
|
||||
printf("SubstrateHookFunctionARM\n");
|
||||
uint32_t *area(reinterpret_cast<uint32_t *>(symbol));
|
||||
uint32_t *arm(area);
|
||||
|
||||
const size_t used(8);
|
||||
|
||||
uint32_t backup[used / sizeof(uint32_t)] = {arm[0], arm[1]};
|
||||
|
||||
if (MSDebug) {
|
||||
char name[16];
|
||||
sprintf(name, "%p", area);
|
||||
MSLogHexEx(area, used + sizeof(uint32_t), 4, name);
|
||||
}
|
||||
|
||||
if (result != NULL) {
|
||||
|
||||
if (backup[0] == A$ldr_rd_$rn_im$(A$pc, A$pc, 4 - 8)) {
|
||||
*result = reinterpret_cast<void *>(backup[1]);
|
||||
|
||||
return sizeof(backup[0]);
|
||||
}
|
||||
|
||||
size_t length(used);
|
||||
for (unsigned offset(0); offset != used / sizeof(uint32_t); ++offset)
|
||||
if (A$pcrel$r(backup[offset])) {
|
||||
if ((backup[offset] & 0x02000000) == 0 || (backup[offset] & 0x0000f000 >> 12) != (backup[offset] & 0x0000000f))
|
||||
length += 2 * sizeof(uint32_t);
|
||||
else
|
||||
length += 4 * sizeof(uint32_t);
|
||||
}
|
||||
|
||||
length += 2 * sizeof(uint32_t);
|
||||
|
||||
uint32_t *buffer(reinterpret_cast<uint32_t *>(mmap(
|
||||
NULL, length, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0
|
||||
)));
|
||||
|
||||
if (buffer == MAP_FAILED) {
|
||||
MSLog(MSLogLevelError, "MS:Error:mmap() = %d", errno);
|
||||
*result = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (false) fail: {
|
||||
munmap(buffer, length);
|
||||
*result = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t start(0), end(length / sizeof(uint32_t));
|
||||
uint32_t *trailer(reinterpret_cast<uint32_t *>(buffer + end));
|
||||
for (unsigned offset(0); offset != used / sizeof(uint32_t); ++offset)
|
||||
if (A$pcrel$r(backup[offset])) {
|
||||
union {
|
||||
uint32_t value;
|
||||
|
||||
struct {
|
||||
uint32_t rm : 4;
|
||||
uint32_t : 1;
|
||||
uint32_t shift : 2;
|
||||
uint32_t shiftamount : 5;
|
||||
uint32_t rd : 4;
|
||||
uint32_t rn : 4;
|
||||
uint32_t l : 1;
|
||||
uint32_t w : 1;
|
||||
uint32_t b : 1;
|
||||
uint32_t u : 1;
|
||||
uint32_t p : 1;
|
||||
uint32_t mode : 1;
|
||||
uint32_t type : 2;
|
||||
uint32_t cond : 4;
|
||||
};
|
||||
} bits = {backup[offset+0]}, copy(bits);
|
||||
|
||||
bool guard;
|
||||
if (bits.mode == 0 || bits.rd != bits.rm) {
|
||||
copy.rn = bits.rd;
|
||||
guard = false;
|
||||
} else {
|
||||
copy.rn = bits.rm != A$r0 ? A$r0 : A$r1;
|
||||
guard = true;
|
||||
}
|
||||
|
||||
if (guard)
|
||||
buffer[start++] = A$stmdb_sp$_$rs$((1 << copy.rn));
|
||||
|
||||
buffer[start+0] = A$ldr_rd_$rn_im$(copy.rn, A$pc, (end-1 - (start+0)) * 4 - 8);
|
||||
buffer[start+1] = copy.value;
|
||||
|
||||
start += 2;
|
||||
|
||||
if (guard)
|
||||
buffer[start++] = A$ldmia_sp$_$rs$((1 << copy.rn));
|
||||
|
||||
*--trailer = reinterpret_cast<uint32_t>(area + offset) + 8;
|
||||
end -= 1;
|
||||
} else
|
||||
buffer[start++] = backup[offset];
|
||||
|
||||
buffer[start+0] = A$ldr_rd_$rn_im$(A$pc, A$pc, 4 - 8);
|
||||
buffer[start+1] = reinterpret_cast<uint32_t>(area + used / sizeof(uint32_t));
|
||||
|
||||
if (mprotect(buffer, length, PROT_READ | PROT_EXEC) == -1) {
|
||||
MSLog(MSLogLevelError, "MS:Error:mprotect():%d", errno);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
*result = buffer;
|
||||
|
||||
if (MSDebug) {
|
||||
char name[16];
|
||||
sprintf(name, "%p", *result);
|
||||
MSLogHexEx(buffer, length, 4, name);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
{
|
||||
SubstrateHookMemory code(process, symbol, used);
|
||||
|
||||
arm[0] = A$ldr_rd_$rn_im$(A$pc, A$pc, 4 - 8);
|
||||
arm[1] = reinterpret_cast<uint32_t>(replace);
|
||||
}
|
||||
|
||||
if (MSDebug) {
|
||||
char name[16];
|
||||
sprintf(name, "%p", area);
|
||||
MSLogHexEx(area, used + sizeof(uint32_t), 4, name);
|
||||
}
|
||||
|
||||
return used;
|
||||
}
|
||||
|
||||
static size_t SubstrateHookFunction(SubstrateProcessRef process, void *symbol, void *replace, void **result) {
|
||||
if (MSDebug)
|
||||
MSLog(MSLogLevelNotice, "SubstrateHookFunction(%p, %p, %p, %p)\n", process, symbol, replace, result);
|
||||
if ((reinterpret_cast<uintptr_t>(symbol) & 0x1) == 0)
|
||||
return SubstrateHookFunctionARM(process, symbol, replace, result);
|
||||
else
|
||||
return SubstrateHookFunctionThumb(process, reinterpret_cast<void *>(reinterpret_cast<uintptr_t>(symbol) & ~0x1), replace, result);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
|
||||
#include "SubstrateX86.hpp"
|
||||
|
||||
static size_t MSGetInstructionWidthIntel(void *start) {
|
||||
hde64s decode;
|
||||
return hde64_disasm(start, &decode);
|
||||
}
|
||||
|
||||
static void SubstrateHookFunction(SubstrateProcessRef process, void *symbol, void *replace, void **result) {
|
||||
if (MSDebug)
|
||||
MSLog(MSLogLevelNotice, "MSHookFunction(%p, %p, %p)\n", symbol, replace, result);
|
||||
if (symbol == NULL)
|
||||
return;
|
||||
|
||||
uintptr_t source(reinterpret_cast<uintptr_t>(symbol));
|
||||
uintptr_t target(reinterpret_cast<uintptr_t>(replace));
|
||||
|
||||
uint8_t *area(reinterpret_cast<uint8_t *>(symbol));
|
||||
|
||||
size_t required(MSSizeOfJump(target, source));
|
||||
|
||||
if (MSDebug) {
|
||||
char name[16];
|
||||
sprintf(name, "%p", area);
|
||||
MSLogHex(area, 32, name);
|
||||
}
|
||||
|
||||
size_t used(0);
|
||||
while (used < required) {
|
||||
size_t width(MSGetInstructionWidthIntel(area + used));
|
||||
if (width == 0) {
|
||||
MSLog(MSLogLevelError, "MS:Error:MSGetInstructionWidthIntel(%p) == 0", area + used);
|
||||
return;
|
||||
}
|
||||
|
||||
used += width;
|
||||
}
|
||||
|
||||
size_t blank(used - required);
|
||||
|
||||
if (MSDebug) {
|
||||
char name[16];
|
||||
sprintf(name, "%p", area);
|
||||
MSLogHex(area, used + sizeof(uint16_t), name);
|
||||
}
|
||||
|
||||
uint8_t backup[used];
|
||||
memcpy(backup, area, used);
|
||||
|
||||
if (result != NULL) {
|
||||
|
||||
if (backup[0] == 0xe9) {
|
||||
*result = reinterpret_cast<void *>(source + 5 + *reinterpret_cast<uint32_t *>(backup + 1));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ia32 && backup[0] == 0xff && backup[1] == 0x25) {
|
||||
*result = *reinterpret_cast<void **>(source + 6 + *reinterpret_cast<uint32_t *>(backup + 2));
|
||||
return;
|
||||
}
|
||||
|
||||
size_t length(used + MSSizeOfJump(source + used));
|
||||
|
||||
for (size_t offset(0), width; offset != used; offset += width) {
|
||||
hde64s decode;
|
||||
hde64_disasm(backup + offset, &decode);
|
||||
width = decode.len;
|
||||
//_assert(width != 0 && offset + width <= used);
|
||||
|
||||
#ifdef __LP64__
|
||||
if ((decode.modrm & 0xc7) == 0x05) {
|
||||
if (decode.opcode == 0x8b) {
|
||||
void *destiny(area + offset + width + int32_t(decode.disp.disp32));
|
||||
uint8_t reg(decode.rex_r << 3 | decode.modrm_reg);
|
||||
length -= decode.len;
|
||||
length += MSSizeOfPushPointer(destiny);
|
||||
length += MSSizeOfPop(reg);
|
||||
length += MSSizeOfMove64();
|
||||
} else {
|
||||
MSLog(MSLogLevelError, "MS:Error: Unknown RIP-Relative (%.2x %.2x)", decode.opcode, decode.opcode2);
|
||||
continue;
|
||||
}
|
||||
} else
|
||||
#endif
|
||||
|
||||
if (backup[offset] == 0xe8) {
|
||||
int32_t relative(*reinterpret_cast<int32_t *>(backup + offset + 1));
|
||||
void *destiny(area + offset + decode.len + relative);
|
||||
|
||||
if (relative == 0) {
|
||||
length -= decode.len;
|
||||
length += MSSizeOfPushPointer(destiny);
|
||||
} else {
|
||||
length += MSSizeOfSkip();
|
||||
length += MSSizeOfJump(destiny);
|
||||
}
|
||||
} else if (backup[offset] == 0xeb) {
|
||||
length -= decode.len;
|
||||
length += MSSizeOfJump(area + offset + decode.len + *reinterpret_cast<int8_t *>(backup + offset + 1));
|
||||
} else if (backup[offset] == 0xe9) {
|
||||
length -= decode.len;
|
||||
length += MSSizeOfJump(area + offset + decode.len + *reinterpret_cast<int32_t *>(backup + offset + 1));
|
||||
} else if (
|
||||
backup[offset] == 0xe3 ||
|
||||
(backup[offset] & 0xf0) == 0x70
|
||||
// XXX: opcode2 & 0xf0 is 0x80?
|
||||
) {
|
||||
length += decode.len;
|
||||
length += MSSizeOfJump(area + offset + decode.len + *reinterpret_cast<int8_t *>(backup + offset + 1));
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t *buffer(reinterpret_cast<uint8_t *>(mmap(
|
||||
NULL, length, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0
|
||||
)));
|
||||
|
||||
if (buffer == MAP_FAILED) {
|
||||
MSLog(MSLogLevelError, "MS:Error:mmap() = %d", errno);
|
||||
*result = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
if (false) fail: {
|
||||
munmap(buffer, length);
|
||||
*result = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
uint8_t *current(buffer);
|
||||
|
||||
for (size_t offset(0), width; offset != used; offset += width) {
|
||||
hde64s decode;
|
||||
hde64_disasm(backup + offset, &decode);
|
||||
width = decode.len;
|
||||
//_assert(width != 0 && offset + width <= used);
|
||||
|
||||
#ifdef __LP64__
|
||||
if ((decode.modrm & 0xc7) == 0x05) {
|
||||
if (decode.opcode == 0x8b) {
|
||||
void *destiny(area + offset + width + int32_t(decode.disp.disp32));
|
||||
uint8_t reg(decode.rex_r << 3 | decode.modrm_reg);
|
||||
MSPushPointer(current, destiny);
|
||||
MSWritePop(current, reg);
|
||||
MSWriteMove64(current, reg, reg);
|
||||
} else {
|
||||
MSLog(MSLogLevelError, "MS:Error: Unknown RIP-Relative (%.2x %.2x)", decode.opcode, decode.opcode2);
|
||||
goto copy;
|
||||
}
|
||||
} else
|
||||
#endif
|
||||
|
||||
if (backup[offset] == 0xe8) {
|
||||
int32_t relative(*reinterpret_cast<int32_t *>(backup + offset + 1));
|
||||
if (relative == 0)
|
||||
MSPushPointer(current, area + offset + decode.len);
|
||||
else {
|
||||
MSWrite<uint8_t>(current, 0xe8);
|
||||
MSWrite<int32_t>(current, MSSizeOfSkip());
|
||||
void *destiny(area + offset + decode.len + relative);
|
||||
MSWriteSkip(current, MSSizeOfJump(destiny, current + MSSizeOfSkip()));
|
||||
MSWriteJump(current, destiny);
|
||||
}
|
||||
} else if (backup[offset] == 0xeb)
|
||||
MSWriteJump(current, area + offset + decode.len + *reinterpret_cast<int8_t *>(backup + offset + 1));
|
||||
else if (backup[offset] == 0xe9)
|
||||
MSWriteJump(current, area + offset + decode.len + *reinterpret_cast<int32_t *>(backup + offset + 1));
|
||||
else if (
|
||||
backup[offset] == 0xe3 ||
|
||||
(backup[offset] & 0xf0) == 0x70
|
||||
) {
|
||||
MSWrite<uint8_t>(current, backup[offset]);
|
||||
MSWrite<uint8_t>(current, 2);
|
||||
MSWrite<uint8_t>(current, 0xeb);
|
||||
void *destiny(area + offset + decode.len + *reinterpret_cast<int8_t *>(backup + offset + 1));
|
||||
MSWrite<uint8_t>(current, MSSizeOfJump(destiny, current + 1));
|
||||
MSWriteJump(current, destiny);
|
||||
} else
|
||||
#ifdef __LP64__
|
||||
copy:
|
||||
#endif
|
||||
{
|
||||
MSWrite(current, backup + offset, width);
|
||||
}
|
||||
}
|
||||
|
||||
MSWriteJump(current, area + used);
|
||||
}
|
||||
|
||||
if (mprotect(buffer, length, PROT_READ | PROT_EXEC) == -1) {
|
||||
MSLog(MSLogLevelError, "MS:Error:mprotect():%d", errno);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
*result = buffer;
|
||||
|
||||
if (MSDebug) {
|
||||
char name[16];
|
||||
sprintf(name, "%p", *result);
|
||||
MSLogHex(buffer, length, name);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
{
|
||||
SubstrateHookMemory code(process, area, used);
|
||||
|
||||
uint8_t *current(area);
|
||||
MSWriteJump(current, target);
|
||||
for (unsigned offset(0); offset != blank; ++offset)
|
||||
MSWrite<uint8_t>(current, 0x90);
|
||||
}
|
||||
|
||||
if (MSDebug) {
|
||||
char name[16];
|
||||
sprintf(name, "%p", area);
|
||||
MSLogHex(area, used + sizeof(uint16_t), name);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
_extern void MSHookFunction(void *symbol, void *replace, void **result) {
|
||||
#ifndef __LP64__
|
||||
SubstrateHookFunction(NULL, symbol, replace, result);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(__APPLE__) && defined(__arm__)
|
||||
_extern void _Z14MSHookFunctionPvS_PS_(void *symbol, void *replace, void **result) {
|
||||
return MSHookFunction(symbol, replace, result);
|
||||
}
|
||||
#endif
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
#ifndef __SUBSTRATEHOOK_H__
|
||||
#define __SUBSTRATEHOOK_H__
|
||||
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#define _extern extern "C" __attribute__((__visibility__("default")))
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void MSHookFunction(void *symbol, void *replace, void **result);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
/* Cydia Substrate - Powerful Code Insertion Platform
|
||||
* Copyright (C) 2008-2011 Jay Freeman (saurik)
|
||||
*/
|
||||
|
||||
/* GNU Lesser General Public License, Version 3 {{{ */
|
||||
/*
|
||||
* Substrate is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Substrate 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 Lesser General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
**/
|
||||
/* }}} */
|
||||
|
||||
#ifndef SUBSTRATE_LOG_HPP
|
||||
#define SUBSTRATE_LOG_HPP
|
||||
|
||||
#if 0
|
||||
#include <android/log.h>
|
||||
|
||||
#define MSLog(level, format, ...) ((void)__android_log_print(level, "NNNN", format, __VA_ARGS__))
|
||||
|
||||
#define MSLogLevelNotice ANDROID_LOG_INFO
|
||||
#define MSLogLevelWarning ANDROID_LOG_WARN
|
||||
#define MSLogLevelError ANDROID_LOG_ERROR
|
||||
|
||||
#else
|
||||
|
||||
#define MSLog(level, format, ...) printf(format, __VA_ARGS__)
|
||||
|
||||
#endif
|
||||
|
||||
#endif//SUBSTRATE_LOG_HPP
|
||||
|
|
@ -1,75 +0,0 @@
|
|||
/* Cydia Substrate - Powerful Code Insertion Platform
|
||||
* Copyright (C) 2008-2011 Jay Freeman (saurik)
|
||||
*/
|
||||
|
||||
/* GNU Lesser General Public License, Version 3 {{{ */
|
||||
/*
|
||||
* Substrate is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Substrate 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 Lesser General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
**/
|
||||
/* }}} */
|
||||
|
||||
#define SubstrateInternal
|
||||
#include "CydiaSubstrate.h"
|
||||
#include "SubstrateLog.hpp"
|
||||
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
extern "C" void __clear_cache (void *beg, void *end);
|
||||
|
||||
struct __SubstrateMemory {
|
||||
void *address_;
|
||||
size_t width_;
|
||||
|
||||
__SubstrateMemory(void *address, size_t width) :
|
||||
address_(address),
|
||||
width_(width)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
extern "C" SubstrateMemoryRef SubstrateMemoryCreate(SubstrateAllocatorRef allocator, SubstrateProcessRef process, void *data, size_t size) {
|
||||
if (allocator != NULL) {
|
||||
MSLog(MSLogLevelError, "MS:Error:allocator != %d", 0);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (size == 0)
|
||||
return NULL;
|
||||
|
||||
long page(sysconf(_SC_PAGESIZE)); // Portable applications should employ sysconf(_SC_PAGESIZE) instead of getpagesize
|
||||
|
||||
uintptr_t base(reinterpret_cast<uintptr_t>(data) / page * page);
|
||||
size_t width(((reinterpret_cast<uintptr_t>(data) + size - 1) / page + 1) * page - base);
|
||||
void *address(reinterpret_cast<void *>(base));
|
||||
|
||||
if (mprotect(address, width, PROT_READ | PROT_WRITE | PROT_EXEC) == -1) {
|
||||
MSLog(MSLogLevelError, "MS:Error:mprotect() = %d", errno);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return new __SubstrateMemory(address, width);
|
||||
}
|
||||
|
||||
extern "C" void SubstrateMemoryRelease(SubstrateMemoryRef memory) {
|
||||
if (mprotect(memory->address_, memory->width_, PROT_READ | PROT_WRITE | PROT_EXEC) == -1)
|
||||
MSLog(MSLogLevelError, "MS:Error:mprotect() = %d", errno);
|
||||
|
||||
__clear_cache(reinterpret_cast<char *>(memory->address_), reinterpret_cast<char *>(memory->address_) + memory->width_);
|
||||
|
||||
delete memory;
|
||||
}
|
||||
|
|
@ -1,200 +0,0 @@
|
|||
/* Cydia Substrate - Powerful Code Insertion Platform
|
||||
* Copyright (C) 2008-2011 Jay Freeman (saurik)
|
||||
*/
|
||||
|
||||
/* GNU Lesser General Public License, Version 3 {{{ */
|
||||
/*
|
||||
* Substrate is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Substrate 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 Lesser General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
**/
|
||||
/* }}} */
|
||||
|
||||
#ifndef SUBSTRATE_X86_HPP
|
||||
#define SUBSTRATE_X86_HPP
|
||||
|
||||
#include "Buffer.hpp"
|
||||
|
||||
#ifdef __LP64__
|
||||
static const bool ia32 = false;
|
||||
#else
|
||||
static const bool ia32 = true;
|
||||
#endif
|
||||
|
||||
enum I$r {
|
||||
I$rax, I$rcx, I$rdx, I$rbx,
|
||||
I$rsp, I$rbp, I$rsi, I$rdi,
|
||||
I$r8, I$r9, I$r10, I$r11,
|
||||
I$r12, I$r13, I$r14, I$r15,
|
||||
};
|
||||
|
||||
_disused static bool MSIs32BitOffset(uintptr_t target, uintptr_t source) {
|
||||
intptr_t offset(target - source);
|
||||
return int32_t(offset) == offset;
|
||||
}
|
||||
|
||||
_disused static size_t MSSizeOfSkip() {
|
||||
return 5;
|
||||
}
|
||||
|
||||
_disused static size_t MSSizeOfPushPointer(uintptr_t target) {
|
||||
return uint64_t(target) >> 32 == 0 ? 5 : 13;
|
||||
}
|
||||
|
||||
_disused static size_t MSSizeOfPushPointer(void *target) {
|
||||
return MSSizeOfPushPointer(reinterpret_cast<uintptr_t>(target));
|
||||
}
|
||||
|
||||
_disused static size_t MSSizeOfJump(bool blind, uintptr_t target, uintptr_t source = 0) {
|
||||
if (ia32 || !blind && MSIs32BitOffset(target, source + 5))
|
||||
return MSSizeOfSkip();
|
||||
else
|
||||
return MSSizeOfPushPointer(target) + 1;
|
||||
}
|
||||
|
||||
_disused static size_t MSSizeOfJump(uintptr_t target, uintptr_t source) {
|
||||
return MSSizeOfJump(false, target, source);
|
||||
}
|
||||
|
||||
_disused static size_t MSSizeOfJump(uintptr_t target) {
|
||||
return MSSizeOfJump(true, target);
|
||||
}
|
||||
|
||||
_disused static size_t MSSizeOfJump(void *target, void *source) {
|
||||
return MSSizeOfJump(reinterpret_cast<uintptr_t>(target), reinterpret_cast<uintptr_t>(source));
|
||||
}
|
||||
|
||||
_disused static size_t MSSizeOfJump(void *target) {
|
||||
return MSSizeOfJump(reinterpret_cast<uintptr_t>(target));
|
||||
}
|
||||
|
||||
_disused static void MSWriteSkip(uint8_t *¤t, ssize_t size) {
|
||||
MSWrite<uint8_t>(current, 0xe9);
|
||||
MSWrite<uint32_t>(current, size);
|
||||
}
|
||||
|
||||
_disused static void MSPushPointer(uint8_t *¤t, uintptr_t target) {
|
||||
MSWrite<uint8_t>(current, 0x68);
|
||||
MSWrite<uint32_t>(current, target);
|
||||
|
||||
if (uint32_t high = uint64_t(target) >> 32) {
|
||||
MSWrite<uint8_t>(current, 0xc7);
|
||||
MSWrite<uint8_t>(current, 0x44);
|
||||
MSWrite<uint8_t>(current, 0x24);
|
||||
MSWrite<uint8_t>(current, 0x04);
|
||||
MSWrite<uint32_t>(current, high);
|
||||
}
|
||||
}
|
||||
|
||||
_disused static void MSPushPointer(uint8_t *¤t, void *target) {
|
||||
return MSPushPointer(current, reinterpret_cast<uintptr_t>(target));
|
||||
}
|
||||
|
||||
_disused static void MSWriteCall(uint8_t *¤t, I$r target) {
|
||||
if (target >> 3 != 0)
|
||||
MSWrite<uint8_t>(current, 0x40 | (target & 0x08) >> 3);
|
||||
MSWrite<uint8_t>(current, 0xff);
|
||||
MSWrite<uint8_t>(current, 0xd0 | target & 0x07);
|
||||
}
|
||||
|
||||
_disused static void MSWriteCall(uint8_t *¤t, uintptr_t target) {
|
||||
uintptr_t source(reinterpret_cast<uintptr_t>(current));
|
||||
|
||||
if (ia32 || MSIs32BitOffset(target, source + 5)) {
|
||||
MSWrite<uint8_t>(current, 0xe8);
|
||||
MSWrite<uint32_t>(current, target - (source + 5));
|
||||
} else {
|
||||
MSPushPointer(current, target);
|
||||
|
||||
MSWrite<uint8_t>(current, 0x83);
|
||||
MSWrite<uint8_t>(current, 0xc4);
|
||||
MSWrite<uint8_t>(current, 0x08);
|
||||
|
||||
MSWrite<uint8_t>(current, 0x67);
|
||||
MSWrite<uint8_t>(current, 0xff);
|
||||
MSWrite<uint8_t>(current, 0x54);
|
||||
MSWrite<uint8_t>(current, 0x24);
|
||||
MSWrite<uint8_t>(current, 0xf8);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Type_>
|
||||
_disused static void MSWriteCall(uint8_t *¤t, Type_ *target) {
|
||||
return MSWriteCall(current, reinterpret_cast<uintptr_t>(target));
|
||||
}
|
||||
|
||||
_disused static void MSWriteJump(uint8_t *¤t, uintptr_t target) {
|
||||
uintptr_t source(reinterpret_cast<uintptr_t>(current));
|
||||
|
||||
if (ia32 || MSIs32BitOffset(target, source + 5))
|
||||
MSWriteSkip(current, target - (source + 5));
|
||||
else {
|
||||
MSPushPointer(current, target);
|
||||
MSWrite<uint8_t>(current, 0xc3);
|
||||
}
|
||||
}
|
||||
|
||||
_disused static void MSWriteJump(uint8_t *¤t, void *target) {
|
||||
return MSWriteJump(current, reinterpret_cast<uintptr_t>(target));
|
||||
}
|
||||
|
||||
_disused static void MSWriteJump(uint8_t *¤t, I$r target) {
|
||||
if (target >> 3 != 0)
|
||||
MSWrite<uint8_t>(current, 0x40 | (target & 0x08) >> 3);
|
||||
MSWrite<uint8_t>(current, 0xff);
|
||||
MSWrite<uint8_t>(current, 0xe0 | target & 0x07);
|
||||
}
|
||||
|
||||
_disused static void MSWritePop(uint8_t *¤t, uint8_t target) {
|
||||
if (target >> 3 != 0)
|
||||
MSWrite<uint8_t>(current, 0x40 | (target & 0x08) >> 3);
|
||||
MSWrite<uint8_t>(current, 0x58 | target & 0x07);
|
||||
}
|
||||
|
||||
_disused static size_t MSSizeOfPop(uint8_t target) {
|
||||
return target >> 3 != 0 ? 2 : 1;
|
||||
}
|
||||
|
||||
_disused static void MSWritePush(uint8_t *¤t, I$r target) {
|
||||
if (target >> 3 != 0)
|
||||
MSWrite<uint8_t>(current, 0x40 | (target & 0x08) >> 3);
|
||||
MSWrite<uint8_t>(current, 0x50 | target & 0x07);
|
||||
}
|
||||
|
||||
_disused static void MSWriteAdd(uint8_t *¤t, I$r target, uint8_t source) {
|
||||
MSWrite<uint8_t>(current, 0x83);
|
||||
MSWrite<uint8_t>(current, 0xc4 | target & 0x07);
|
||||
MSWrite<uint8_t>(current, source);
|
||||
}
|
||||
|
||||
_disused static void MSWriteSet64(uint8_t *¤t, I$r target, uintptr_t source) {
|
||||
MSWrite<uint8_t>(current, 0x48 | (target & 0x08) >> 3 << 2);
|
||||
MSWrite<uint8_t>(current, 0xb8 | target & 0x7);
|
||||
MSWrite<uint64_t>(current, source);
|
||||
}
|
||||
|
||||
template <typename Type_>
|
||||
_disused static void MSWriteSet64(uint8_t *¤t, I$r target, Type_ *source) {
|
||||
return MSWriteSet64(current, target, reinterpret_cast<uintptr_t>(source));
|
||||
}
|
||||
|
||||
_disused static void MSWriteMove64(uint8_t *¤t, uint8_t source, uint8_t target) {
|
||||
MSWrite<uint8_t>(current, 0x48 | (target & 0x08) >> 3 << 2 | (source & 0x08) >> 3);
|
||||
MSWrite<uint8_t>(current, 0x8b);
|
||||
MSWrite<uint8_t>(current, (target & 0x07) << 3 | source & 0x07);
|
||||
}
|
||||
|
||||
_disused static size_t MSSizeOfMove64() {
|
||||
return 3;
|
||||
}
|
||||
|
||||
#endif//SUBSTRATE_X86_HPP
|
||||
|
|
@ -1,332 +0,0 @@
|
|||
/*
|
||||
* Hacker Disassembler Engine 64 C
|
||||
* Copyright (c) 2008-2009, Vyacheslav Patkov.
|
||||
* All rights reserved.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "hde64.h"
|
||||
#include "table64.h"
|
||||
|
||||
unsigned int hde64_disasm(const void *code, hde64s *hs)
|
||||
{
|
||||
uint8_t x, c, *p = (uint8_t *)code, cflags, opcode, pref = 0;
|
||||
uint8_t *ht = hde64_table, m_mod, m_reg, m_rm, disp_size = 0;
|
||||
uint8_t op64 = 0;
|
||||
|
||||
memset(hs,0,sizeof(hde64s));
|
||||
char *tmp=(char*)hs;
|
||||
|
||||
for (x = 16; x; x--)
|
||||
switch (c = *p++) {
|
||||
case 0xf3:
|
||||
hs->p_rep = c;
|
||||
pref |= PRE_F3;
|
||||
break;
|
||||
case 0xf2:
|
||||
hs->p_rep = c;
|
||||
pref |= PRE_F2;
|
||||
break;
|
||||
case 0xf0:
|
||||
hs->p_lock = c;
|
||||
pref |= PRE_LOCK;
|
||||
break;
|
||||
case 0x26: case 0x2e: case 0x36:
|
||||
case 0x3e: case 0x64: case 0x65:
|
||||
hs->p_seg = c;
|
||||
pref |= PRE_SEG;
|
||||
break;
|
||||
case 0x66:
|
||||
hs->p_66 = c;
|
||||
pref |= PRE_66;
|
||||
break;
|
||||
case 0x67:
|
||||
hs->p_67 = c;
|
||||
pref |= PRE_67;
|
||||
break;
|
||||
default:
|
||||
goto pref_done;
|
||||
}
|
||||
pref_done:
|
||||
|
||||
hs->flags = (uint32_t)pref << 23;
|
||||
|
||||
if (!pref)
|
||||
pref |= PRE_NONE;
|
||||
|
||||
if ((c & 0xf0) == 0x40) {
|
||||
hs->flags |= F_PREFIX_REX;
|
||||
if ((hs->rex_w = (c & 0xf) >> 3) && (*p & 0xf8) == 0xb8)
|
||||
op64++;
|
||||
hs->rex_r = (c & 7) >> 2;
|
||||
hs->rex_x = (c & 3) >> 1;
|
||||
hs->rex_b = c & 1;
|
||||
if (((c = *p++) & 0xf0) == 0x40) {
|
||||
opcode = c;
|
||||
goto error_opcode;
|
||||
}
|
||||
}
|
||||
|
||||
if ((hs->opcode = c) == 0x0f) {
|
||||
hs->opcode2 = c = *p++;
|
||||
ht += DELTA_OPCODES;
|
||||
} else if (c >= 0xa0 && c <= 0xa3) {
|
||||
op64++;
|
||||
if (pref & PRE_67)
|
||||
pref |= PRE_66;
|
||||
else
|
||||
pref &= ~PRE_66;
|
||||
}
|
||||
|
||||
opcode = c;
|
||||
cflags = ht[ht[opcode / 4] + (opcode % 4)];
|
||||
|
||||
if (cflags == C_ERROR) {
|
||||
error_opcode:
|
||||
hs->flags |= F_ERROR | F_ERROR_OPCODE;
|
||||
cflags = 0;
|
||||
if ((opcode & -3) == 0x24)
|
||||
cflags++;
|
||||
}
|
||||
|
||||
x = 0;
|
||||
if (cflags & C_GROUP) {
|
||||
uint16_t t;
|
||||
t = *(uint16_t *)(ht + (cflags & 0x7f));
|
||||
cflags = (uint8_t)t;
|
||||
x = (uint8_t)(t >> 8);
|
||||
}
|
||||
|
||||
if (hs->opcode2) {
|
||||
ht = hde64_table + DELTA_PREFIXES;
|
||||
if (ht[ht[opcode / 4] + (opcode % 4)] & pref)
|
||||
hs->flags |= F_ERROR | F_ERROR_OPCODE;
|
||||
}
|
||||
|
||||
if (cflags & C_MODRM) {
|
||||
hs->flags |= F_MODRM;
|
||||
hs->modrm = c = *p++;
|
||||
hs->modrm_mod = m_mod = c >> 6;
|
||||
hs->modrm_rm = m_rm = c & 7;
|
||||
hs->modrm_reg = m_reg = (c & 0x3f) >> 3;
|
||||
|
||||
if (x && ((x << m_reg) & 0x80))
|
||||
hs->flags |= F_ERROR | F_ERROR_OPCODE;
|
||||
|
||||
if (!hs->opcode2 && opcode >= 0xd9 && opcode <= 0xdf) {
|
||||
uint8_t t = opcode - 0xd9;
|
||||
if (m_mod == 3) {
|
||||
ht = hde64_table + DELTA_FPU_MODRM + t*8;
|
||||
t = ht[m_reg] << m_rm;
|
||||
} else {
|
||||
ht = hde64_table + DELTA_FPU_REG;
|
||||
t = ht[t] << m_reg;
|
||||
}
|
||||
if (t & 0x80)
|
||||
hs->flags |= F_ERROR | F_ERROR_OPCODE;
|
||||
}
|
||||
|
||||
if (pref & PRE_LOCK) {
|
||||
if (m_mod == 3) {
|
||||
hs->flags |= F_ERROR | F_ERROR_LOCK;
|
||||
} else {
|
||||
uint8_t *table_end, op = opcode;
|
||||
if (hs->opcode2) {
|
||||
ht = hde64_table + DELTA_OP2_LOCK_OK;
|
||||
table_end = ht + DELTA_OP_ONLY_MEM - DELTA_OP2_LOCK_OK;
|
||||
} else {
|
||||
ht = hde64_table + DELTA_OP_LOCK_OK;
|
||||
table_end = ht + DELTA_OP2_LOCK_OK - DELTA_OP_LOCK_OK;
|
||||
op &= -2;
|
||||
}
|
||||
for (; ht != table_end; ht++)
|
||||
if (*ht++ == op) {
|
||||
if (!((*ht << m_reg) & 0x80))
|
||||
goto no_lock_error;
|
||||
else
|
||||
break;
|
||||
}
|
||||
hs->flags |= F_ERROR | F_ERROR_LOCK;
|
||||
no_lock_error:
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
if (hs->opcode2) {
|
||||
switch (opcode) {
|
||||
case 0x20: case 0x22:
|
||||
m_mod = 3;
|
||||
if (m_reg > 4 || m_reg == 1)
|
||||
goto error_operand;
|
||||
else
|
||||
goto no_error_operand;
|
||||
case 0x21: case 0x23:
|
||||
m_mod = 3;
|
||||
if (m_reg == 4 || m_reg == 5)
|
||||
goto error_operand;
|
||||
else
|
||||
goto no_error_operand;
|
||||
}
|
||||
} else {
|
||||
switch (opcode) {
|
||||
case 0x8c:
|
||||
if (m_reg > 5)
|
||||
goto error_operand;
|
||||
else
|
||||
goto no_error_operand;
|
||||
case 0x8e:
|
||||
if (m_reg == 1 || m_reg > 5)
|
||||
goto error_operand;
|
||||
else
|
||||
goto no_error_operand;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_mod == 3) {
|
||||
uint8_t *table_end;
|
||||
if (hs->opcode2) {
|
||||
ht = hde64_table + DELTA_OP2_ONLY_MEM;
|
||||
table_end = ht + sizeof(hde64_table) - DELTA_OP2_ONLY_MEM;
|
||||
} else {
|
||||
ht = hde64_table + DELTA_OP_ONLY_MEM;
|
||||
table_end = ht + DELTA_OP2_ONLY_MEM - DELTA_OP_ONLY_MEM;
|
||||
}
|
||||
for (; ht != table_end; ht += 2)
|
||||
if (*ht++ == opcode) {
|
||||
if (*ht++ & pref && !((*ht << m_reg) & 0x80))
|
||||
goto error_operand;
|
||||
else
|
||||
break;
|
||||
}
|
||||
goto no_error_operand;
|
||||
} else if (hs->opcode2) {
|
||||
switch (opcode) {
|
||||
case 0x50: case 0xd7: case 0xf7:
|
||||
if (pref & (PRE_NONE | PRE_66))
|
||||
goto error_operand;
|
||||
break;
|
||||
case 0xd6:
|
||||
if (pref & (PRE_F2 | PRE_F3))
|
||||
goto error_operand;
|
||||
break;
|
||||
case 0xc5:
|
||||
goto error_operand;
|
||||
}
|
||||
goto no_error_operand;
|
||||
} else
|
||||
goto no_error_operand;
|
||||
|
||||
error_operand:
|
||||
hs->flags |= F_ERROR | F_ERROR_OPERAND;
|
||||
no_error_operand:
|
||||
|
||||
c = *p++;
|
||||
if (m_reg <= 1) {
|
||||
if (opcode == 0xf6)
|
||||
cflags |= C_IMM8;
|
||||
else if (opcode == 0xf7)
|
||||
cflags |= C_IMM_P66;
|
||||
}
|
||||
|
||||
switch (m_mod) {
|
||||
case 0:
|
||||
if (pref & PRE_67) {
|
||||
if (m_rm == 6)
|
||||
disp_size = 2;
|
||||
} else
|
||||
if (m_rm == 5)
|
||||
disp_size = 4;
|
||||
break;
|
||||
case 1:
|
||||
disp_size = 1;
|
||||
break;
|
||||
case 2:
|
||||
disp_size = 2;
|
||||
if (!(pref & PRE_67))
|
||||
disp_size <<= 1;
|
||||
}
|
||||
|
||||
if (m_mod != 3 && m_rm == 4) {
|
||||
hs->flags |= F_SIB;
|
||||
p++;
|
||||
hs->sib = c;
|
||||
hs->sib_scale = c >> 6;
|
||||
hs->sib_index = (c & 0x3f) >> 3;
|
||||
if ((hs->sib_base = c & 7) == 5 && !(m_mod & 1))
|
||||
disp_size = 4;
|
||||
}
|
||||
|
||||
p--;
|
||||
switch (disp_size) {
|
||||
case 1:
|
||||
hs->flags |= F_DISP8;
|
||||
hs->disp.disp8 = *p;
|
||||
break;
|
||||
case 2:
|
||||
hs->flags |= F_DISP16;
|
||||
hs->disp.disp16 = *(uint16_t *)p;
|
||||
break;
|
||||
case 4:
|
||||
hs->flags |= F_DISP32;
|
||||
hs->disp.disp32 = *(uint32_t *)p;
|
||||
}
|
||||
p += disp_size;
|
||||
} else if (pref & PRE_LOCK)
|
||||
hs->flags |= F_ERROR | F_ERROR_LOCK;
|
||||
|
||||
if (cflags & C_IMM_P66) {
|
||||
if (cflags & C_REL32) {
|
||||
if (pref & PRE_66) {
|
||||
hs->flags |= F_IMM16 | F_RELATIVE;
|
||||
hs->imm.imm16 = *(uint16_t *)p;
|
||||
p += 2;
|
||||
goto disasm_done;
|
||||
}
|
||||
goto rel32_ok;
|
||||
}
|
||||
if (op64) {
|
||||
hs->flags |= F_IMM64;
|
||||
hs->imm.imm64 = *(uint64_t *)p;
|
||||
p += 8;
|
||||
} else if (!(pref & PRE_66)) {
|
||||
hs->flags |= F_IMM32;
|
||||
hs->imm.imm32 = *(uint32_t *)p;
|
||||
p += 4;
|
||||
} else
|
||||
goto imm16_ok;
|
||||
}
|
||||
|
||||
|
||||
if (cflags & C_IMM16) {
|
||||
imm16_ok:
|
||||
hs->flags |= F_IMM16;
|
||||
hs->imm.imm16 = *(uint16_t *)p;
|
||||
p += 2;
|
||||
}
|
||||
if (cflags & C_IMM8) {
|
||||
hs->flags |= F_IMM8;
|
||||
hs->imm.imm8 = *p++;
|
||||
}
|
||||
|
||||
if (cflags & C_REL32) {
|
||||
rel32_ok:
|
||||
hs->flags |= F_IMM32 | F_RELATIVE;
|
||||
hs->imm.imm32 = *(uint32_t *)p;
|
||||
p += 4;
|
||||
} else if (cflags & C_REL8) {
|
||||
hs->flags |= F_IMM8 | F_RELATIVE;
|
||||
hs->imm.imm8 = *p++;
|
||||
}
|
||||
|
||||
disasm_done:
|
||||
|
||||
if ((hs->len = (uint8_t)(p-(uint8_t *)code)) > 15) {
|
||||
hs->flags |= F_ERROR | F_ERROR_LENGTH;
|
||||
hs->len = 15;
|
||||
}
|
||||
|
||||
return (unsigned int)hs->len;
|
||||
}
|
||||
|
|
@ -1,112 +0,0 @@
|
|||
/*
|
||||
* Hacker Disassembler Engine 64
|
||||
* Copyright (c) 2008-2009, Vyacheslav Patkov.
|
||||
* All rights reserved.
|
||||
*
|
||||
* hde64.h: C/C++ header file
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _HDE64_H_
|
||||
#define _HDE64_H_
|
||||
|
||||
/* stdint.h - C99 standard header
|
||||
* http://en.wikipedia.org/wiki/stdint.h
|
||||
*
|
||||
* if your compiler doesn't contain "stdint.h" header (for
|
||||
* example, Microsoft Visual C++), you can download file:
|
||||
* http://www.azillionmonkeys.com/qed/pstdint.h
|
||||
* and change next line to:
|
||||
* #include "pstdint.h"
|
||||
*/
|
||||
#include <stdint.h>
|
||||
|
||||
#define F_MODRM 0x00000001
|
||||
#define F_SIB 0x00000002
|
||||
#define F_IMM8 0x00000004
|
||||
#define F_IMM16 0x00000008
|
||||
#define F_IMM32 0x00000010
|
||||
#define F_IMM64 0x00000020
|
||||
#define F_DISP8 0x00000040
|
||||
#define F_DISP16 0x00000080
|
||||
#define F_DISP32 0x00000100
|
||||
#define F_RELATIVE 0x00000200
|
||||
#define F_ERROR 0x00001000
|
||||
#define F_ERROR_OPCODE 0x00002000
|
||||
#define F_ERROR_LENGTH 0x00004000
|
||||
#define F_ERROR_LOCK 0x00008000
|
||||
#define F_ERROR_OPERAND 0x00010000
|
||||
#define F_PREFIX_REPNZ 0x01000000
|
||||
#define F_PREFIX_REPX 0x02000000
|
||||
#define F_PREFIX_REP 0x03000000
|
||||
#define F_PREFIX_66 0x04000000
|
||||
#define F_PREFIX_67 0x08000000
|
||||
#define F_PREFIX_LOCK 0x10000000
|
||||
#define F_PREFIX_SEG 0x20000000
|
||||
#define F_PREFIX_REX 0x40000000
|
||||
#define F_PREFIX_ANY 0x7f000000
|
||||
|
||||
#define PREFIX_SEGMENT_CS 0x2e
|
||||
#define PREFIX_SEGMENT_SS 0x36
|
||||
#define PREFIX_SEGMENT_DS 0x3e
|
||||
#define PREFIX_SEGMENT_ES 0x26
|
||||
#define PREFIX_SEGMENT_FS 0x64
|
||||
#define PREFIX_SEGMENT_GS 0x65
|
||||
#define PREFIX_LOCK 0xf0
|
||||
#define PREFIX_REPNZ 0xf2
|
||||
#define PREFIX_REPX 0xf3
|
||||
#define PREFIX_OPERAND_SIZE 0x66
|
||||
#define PREFIX_ADDRESS_SIZE 0x67
|
||||
|
||||
#pragma pack(push,1)
|
||||
|
||||
typedef struct {
|
||||
uint8_t len;
|
||||
uint8_t p_rep;
|
||||
uint8_t p_lock;
|
||||
uint8_t p_seg;
|
||||
uint8_t p_66;
|
||||
uint8_t p_67;
|
||||
uint8_t rex;
|
||||
uint8_t rex_w;
|
||||
uint8_t rex_r;
|
||||
uint8_t rex_x;
|
||||
uint8_t rex_b;
|
||||
uint8_t opcode;
|
||||
uint8_t opcode2;
|
||||
uint8_t modrm;
|
||||
uint8_t modrm_mod;
|
||||
uint8_t modrm_reg;
|
||||
uint8_t modrm_rm;
|
||||
uint8_t sib;
|
||||
uint8_t sib_scale;
|
||||
uint8_t sib_index;
|
||||
uint8_t sib_base;
|
||||
union {
|
||||
uint8_t imm8;
|
||||
uint16_t imm16;
|
||||
uint32_t imm32;
|
||||
uint64_t imm64;
|
||||
} imm;
|
||||
union {
|
||||
uint8_t disp8;
|
||||
uint16_t disp16;
|
||||
uint32_t disp32;
|
||||
} disp;
|
||||
uint32_t flags;
|
||||
} hde64s;
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* __cdecl */
|
||||
unsigned int hde64_disasm(const void *code, hde64s *hs);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _HDE64_H_ */
|
||||
|
|
@ -1,74 +0,0 @@
|
|||
/*
|
||||
* Hacker Disassembler Engine 64 C
|
||||
* Copyright (c) 2008-2009, Vyacheslav Patkov.
|
||||
* All rights reserved.
|
||||
*
|
||||
*/
|
||||
|
||||
#define C_NONE 0x00
|
||||
#define C_MODRM 0x01
|
||||
#define C_IMM8 0x02
|
||||
#define C_IMM16 0x04
|
||||
#define C_IMM_P66 0x10
|
||||
#define C_REL8 0x20
|
||||
#define C_REL32 0x40
|
||||
#define C_GROUP 0x80
|
||||
#define C_ERROR 0xff
|
||||
|
||||
#define PRE_ANY 0x00
|
||||
#define PRE_NONE 0x01
|
||||
#define PRE_F2 0x02
|
||||
#define PRE_F3 0x04
|
||||
#define PRE_66 0x08
|
||||
#define PRE_67 0x10
|
||||
#define PRE_LOCK 0x20
|
||||
#define PRE_SEG 0x40
|
||||
#define PRE_ALL 0xff
|
||||
|
||||
#define DELTA_OPCODES 0x4a
|
||||
#define DELTA_FPU_REG 0xfd
|
||||
#define DELTA_FPU_MODRM 0x104
|
||||
#define DELTA_PREFIXES 0x13c
|
||||
#define DELTA_OP_LOCK_OK 0x1ae
|
||||
#define DELTA_OP2_LOCK_OK 0x1c6
|
||||
#define DELTA_OP_ONLY_MEM 0x1d8
|
||||
#define DELTA_OP2_ONLY_MEM 0x1e7
|
||||
|
||||
unsigned char hde64_table[] = {
|
||||
0xa5,0xaa,0xa5,0xb8,0xa5,0xaa,0xa5,0xaa,0xa5,0xb8,0xa5,0xb8,0xa5,0xb8,0xa5,
|
||||
0xb8,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xac,0xc0,0xcc,0xc0,0xa1,0xa1,
|
||||
0xa1,0xa1,0xb1,0xa5,0xa5,0xa6,0xc0,0xc0,0xd7,0xda,0xe0,0xc0,0xe4,0xc0,0xea,
|
||||
0xea,0xe0,0xe0,0x98,0xc8,0xee,0xf1,0xa5,0xd3,0xa5,0xa5,0xa1,0xea,0x9e,0xc0,
|
||||
0xc0,0xc2,0xc0,0xe6,0x03,0x7f,0x11,0x7f,0x01,0x7f,0x01,0x3f,0x01,0x01,0xab,
|
||||
0x8b,0x90,0x64,0x5b,0x5b,0x5b,0x5b,0x5b,0x92,0x5b,0x5b,0x76,0x90,0x92,0x92,
|
||||
0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x6a,0x73,0x90,
|
||||
0x5b,0x52,0x52,0x52,0x52,0x5b,0x5b,0x5b,0x5b,0x77,0x7c,0x77,0x85,0x5b,0x5b,
|
||||
0x70,0x5b,0x7a,0xaf,0x76,0x76,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,
|
||||
0x5b,0x5b,0x86,0x01,0x03,0x01,0x04,0x03,0xd5,0x03,0xd5,0x03,0xcc,0x01,0xbc,
|
||||
0x03,0xf0,0x03,0x03,0x04,0x00,0x50,0x50,0x50,0x50,0xff,0x20,0x20,0x20,0x20,
|
||||
0x01,0x01,0x01,0x01,0xc4,0x02,0x10,0xff,0xff,0xff,0x01,0x00,0x03,0x11,0xff,
|
||||
0x03,0xc4,0xc6,0xc8,0x02,0x10,0x00,0xff,0xcc,0x01,0x01,0x01,0x00,0x00,0x00,
|
||||
0x00,0x01,0x01,0x03,0x01,0xff,0xff,0xc0,0xc2,0x10,0x11,0x02,0x03,0x01,0x01,
|
||||
0x01,0xff,0xff,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0xff,0xff,0xff,0xff,0x10,
|
||||
0x10,0x10,0x10,0x02,0x10,0x00,0x00,0xc6,0xc8,0x02,0x02,0x02,0x02,0x06,0x00,
|
||||
0x04,0x00,0x02,0xff,0x00,0xc0,0xc2,0x01,0x01,0x03,0x03,0x03,0xca,0x40,0x00,
|
||||
0x0a,0x00,0x04,0x00,0x00,0x00,0x00,0x7f,0x00,0x33,0x01,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0xff,0xbf,0xff,0xff,0x00,0x00,0x00,0x00,0x07,0x00,0x00,0xff,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,
|
||||
0x00,0x00,0x00,0xbf,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7f,0x00,0x00,
|
||||
0xff,0x40,0x40,0x40,0x40,0x41,0x49,0x40,0x40,0x40,0x40,0x4c,0x42,0x40,0x40,
|
||||
0x40,0x40,0x40,0x40,0x40,0x40,0x4f,0x44,0x53,0x40,0x40,0x40,0x44,0x57,0x43,
|
||||
0x5c,0x40,0x60,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,
|
||||
0x40,0x40,0x64,0x66,0x6e,0x6b,0x40,0x40,0x6a,0x46,0x40,0x40,0x44,0x46,0x40,
|
||||
0x40,0x5b,0x44,0x40,0x40,0x00,0x00,0x00,0x00,0x06,0x06,0x06,0x06,0x01,0x06,
|
||||
0x06,0x02,0x06,0x06,0x00,0x06,0x00,0x0a,0x0a,0x00,0x00,0x00,0x02,0x07,0x07,
|
||||
0x06,0x02,0x0d,0x06,0x06,0x06,0x0e,0x05,0x05,0x02,0x02,0x00,0x00,0x04,0x04,
|
||||
0x04,0x04,0x05,0x06,0x06,0x06,0x00,0x00,0x00,0x0e,0x00,0x00,0x08,0x00,0x10,
|
||||
0x00,0x18,0x00,0x20,0x00,0x28,0x00,0x30,0x00,0x80,0x01,0x82,0x01,0x86,0x00,
|
||||
0xf6,0xcf,0xfe,0x3f,0xab,0x00,0xb0,0x00,0xb1,0x00,0xb3,0x00,0xba,0xf8,0xbb,
|
||||
0x00,0xc0,0x00,0xc1,0x00,0xc7,0xbf,0x62,0xff,0x00,0x8d,0xff,0x00,0xc4,0xff,
|
||||
0x00,0xc5,0xff,0x00,0xff,0xff,0xeb,0x01,0xff,0x0e,0x12,0x08,0x00,0x13,0x09,
|
||||
0x00,0x16,0x08,0x00,0x17,0x09,0x00,0x2b,0x09,0x00,0xae,0xff,0x07,0xb2,0xff,
|
||||
0x00,0xb4,0xff,0x00,0xb5,0xff,0x00,0xc3,0x01,0x00,0xc7,0xff,0xbf,0xe7,0x08,
|
||||
0x00,0xf0,0x02,0x00
|
||||
};
|
||||
|
|
@ -25,6 +25,8 @@ void setNonCompilable(void *method);
|
|||
|
||||
bool setNativeFlag(void *method, bool isNative);
|
||||
|
||||
void *getArtMethod(JNIEnv *env, jobject jmethod);
|
||||
|
||||
static void *getResolvedMethodsAddr(JNIEnv *, jobject);
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
|
|
|||
|
|
@ -21,6 +21,8 @@ static int kAccNative = 0x0100;
|
|||
static int kAccCompileDontBother = 0x01000000;
|
||||
static int kAccFastInterpreterToInterpreterInvoke = 0x40000000;
|
||||
|
||||
static jfieldID fieldArtMethod = NULL;
|
||||
|
||||
static inline uint32_t read32(void *addr) {
|
||||
return *((uint32_t *) addr);
|
||||
}
|
||||
|
|
@ -29,15 +31,19 @@ static inline void write32(void *addr, uint32_t value) {
|
|||
*((uint32_t *) addr) = value;
|
||||
}
|
||||
|
||||
static inline void* readAddr(void *addr) {
|
||||
return *((void**) addr);
|
||||
static inline void *readAddr(void *addr) {
|
||||
return *((void **) addr);
|
||||
}
|
||||
|
||||
void Java_lab_galaxy_yahfa_HookMain_init(JNIEnv *env, jclass clazz, jint sdkVersion) {
|
||||
int i;
|
||||
SDKVersion = sdkVersion;
|
||||
jclass classExecutable;
|
||||
LOGI("init to SDK %d", sdkVersion);
|
||||
switch (sdkVersion) {
|
||||
case __ANDROID_API_R__:
|
||||
classExecutable = (*env)->FindClass(env, "java/lang/reflect/Executable");
|
||||
fieldArtMethod = (*env)->GetFieldID(env, classExecutable, "artMethod", "J");
|
||||
case __ANDROID_API_Q__:
|
||||
case __ANDROID_API_P__:
|
||||
kAccCompileDontBother = 0x02000000;
|
||||
|
|
@ -209,6 +215,10 @@ static int doBackupAndHook(JNIEnv *env, void *targetMethod, void *hookMethod, vo
|
|||
|
||||
static void ensureMethodCached(void *hookMethod, void *backupMethod,
|
||||
void *hookClassResolvedMethods) {
|
||||
if (!backupMethod) {
|
||||
LOGE("ensureMethodCached: backupMethod is null");
|
||||
return;
|
||||
}
|
||||
void *dexCacheResolvedMethods;
|
||||
// then we get the dex method index of the static backup method
|
||||
int methodIndex = read32(
|
||||
|
|
@ -253,6 +263,24 @@ static void ensureMethodCached(void *hookMethod, void *backupMethod,
|
|||
}
|
||||
}
|
||||
|
||||
void *getArtMethod(JNIEnv *env, jobject jmethod) {
|
||||
void *artMethod = NULL;
|
||||
|
||||
if (jmethod == NULL) {
|
||||
return artMethod;
|
||||
}
|
||||
|
||||
if (SDKVersion == __ANDROID_API_R__) {
|
||||
artMethod = (void *) (*env)->GetLongField(env, jmethod, fieldArtMethod);
|
||||
} else {
|
||||
artMethod = (void *) (*env)->FromReflectedMethod(env, jmethod);
|
||||
}
|
||||
|
||||
LOGI("ArtMethod: %p", artMethod);
|
||||
return artMethod;
|
||||
|
||||
}
|
||||
|
||||
jobject Java_lab_galaxy_yahfa_HookMain_findMethodNative(JNIEnv *env, jclass clazz,
|
||||
jclass targetClass, jstring methodName,
|
||||
jstring methodSig) {
|
||||
|
|
@ -285,12 +313,13 @@ jboolean Java_lab_galaxy_yahfa_HookMain_backupAndHookNative(JNIEnv *env, jclass
|
|||
jobject backup) {
|
||||
|
||||
if (!doBackupAndHook(env,
|
||||
(void *) (*env)->FromReflectedMethod(env, target),
|
||||
(void *) (*env)->FromReflectedMethod(env, hook),
|
||||
backup == NULL ? NULL : (void *) (*env)->FromReflectedMethod(env, backup)
|
||||
getArtMethod(env, target),
|
||||
getArtMethod(env, hook),
|
||||
getArtMethod(env, backup)
|
||||
)) {
|
||||
(*env)->NewGlobalRef(env,
|
||||
hook); // keep a global ref so that the hook method would not be GCed
|
||||
if (backup) (*env)->NewGlobalRef(env, backup);
|
||||
return JNI_TRUE;
|
||||
} else {
|
||||
return JNI_FALSE;
|
||||
|
|
@ -300,8 +329,8 @@ jboolean Java_lab_galaxy_yahfa_HookMain_backupAndHookNative(JNIEnv *env, jclass
|
|||
void Java_lab_galaxy_yahfa_HookMain_ensureMethodCached(JNIEnv *env, jclass clazz,
|
||||
jobject hook,
|
||||
jobject backup) {
|
||||
ensureMethodCached((void *) (*env)->FromReflectedMethod(env, hook),
|
||||
backup == NULL ? NULL : (void *) (*env)->FromReflectedMethod(env, backup),
|
||||
ensureMethodCached(getArtMethod(env, hook),
|
||||
getArtMethod(env, backup),
|
||||
getResolvedMethodsAddr(env, hook));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
#define LOGE(...)
|
||||
#else
|
||||
#define LOG_TAG "EdXposed"
|
||||
#ifdef DEBUG
|
||||
#ifndef NDEBUG
|
||||
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)
|
||||
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN,LOG_TAG,__VA_ARGS__)
|
||||
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
|
||||
|
|
|
|||
|
|
@ -8,4 +8,4 @@ include_directories(include src)
|
|||
add_library(riru_edxp SHARED ${SRC_LIST} ${SRC_JNI_LIST})
|
||||
|
||||
find_library(log-lib log)
|
||||
target_link_libraries(riru_edxp yahfa riru xhook substrate android ${log-lib})
|
||||
target_link_libraries(riru_edxp yahfa riru xhook android dobby ${log-lib})
|
||||
|
|
@ -4,7 +4,7 @@
|
|||
#include <art/base/macros.h>
|
||||
#include "logging.h"
|
||||
|
||||
#define JNI_START JNIEnv *env, jclass clazz
|
||||
#define JNI_START JNIEnv *env, [[maybe_unused]] jclass clazz
|
||||
|
||||
ALWAYS_INLINE static void JNIExceptionClear(JNIEnv *env) {
|
||||
if (env->ExceptionCheck()) {
|
||||
|
|
@ -103,3 +103,52 @@ ALWAYS_INLINE static int ClearException(JNIEnv *env) {
|
|||
env->RegisterNatives(class, methods, size); \
|
||||
if (ClearException(env)) LOGE("RegisterNatives " #class);
|
||||
|
||||
class JUTFString {
|
||||
public:
|
||||
inline JUTFString(JNIEnv *env, jstring jstr) : JUTFString(env, jstr, nullptr) {
|
||||
}
|
||||
|
||||
inline JUTFString(JNIEnv *env, jstring jstr, const char *default_cstr) : env_(env),
|
||||
jstr_(jstr) {
|
||||
if (env_ && jstr_) cstr_ = env_->GetStringUTFChars(jstr, nullptr);
|
||||
else cstr_ = default_cstr;
|
||||
}
|
||||
|
||||
inline operator const char *() const { return cstr_; }
|
||||
|
||||
inline operator const std::string() const { return cstr_; }
|
||||
|
||||
inline operator const bool() const { return cstr_ != nullptr; }
|
||||
|
||||
inline auto get() const { return cstr_; }
|
||||
|
||||
inline ~JUTFString() {
|
||||
if (env_ && jstr_) env_->ReleaseStringUTFChars(jstr_, cstr_);
|
||||
}
|
||||
|
||||
JUTFString(JUTFString &&other)
|
||||
: env_(std::move(other.env_)), jstr_(std::move(other.jstr_)),
|
||||
cstr_(std::move(other.cstr_)) {
|
||||
other.cstr_ = nullptr;
|
||||
}
|
||||
|
||||
JUTFString &
|
||||
operator=(JUTFString &&other) {
|
||||
if (&other != this) {
|
||||
env_ = std::move(other.env_);
|
||||
jstr_ = std::move(other.jstr_);
|
||||
cstr_ = std::move(other.cstr_);
|
||||
other.cstr_ = nullptr;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
JNIEnv *env_;
|
||||
jstring jstr_;
|
||||
const char *cstr_;
|
||||
|
||||
JUTFString(const JUTFString &) = delete;
|
||||
|
||||
JUTFString &operator=(const JUTFString &) = delete;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,14 +0,0 @@
|
|||
#ifndef WHALE_ANDROID_ANDROID_BUILD_H_
|
||||
#define WHALE_ANDROID_ANDROID_BUILD_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <sys/system_properties.h>
|
||||
|
||||
static inline int32_t GetAndroidApiLevel() {
|
||||
char prop_value[PROP_VALUE_MAX];
|
||||
__system_property_get("ro.build.version.sdk", prop_value);
|
||||
return atoi(prop_value);
|
||||
}
|
||||
|
||||
#endif // WHALE_ANDROID_ANDROID_BUILD_H_
|
||||
|
|
@ -6,6 +6,7 @@
|
|||
#include <art/runtime/mirror/class.h>
|
||||
#include <android-base/strings.h>
|
||||
#include "runtime.h"
|
||||
#include "config.h"
|
||||
#include "jni_env_ext.h"
|
||||
#include "edxp_context.h"
|
||||
#include "jni/edxp_pending_hooks.h"
|
||||
|
|
@ -23,6 +24,7 @@ namespace art {
|
|||
}
|
||||
|
||||
CREATE_HOOK_STUB_ENTRIES(void *, Constructor, void *thiz, void *intern_table) {
|
||||
LOGI("ConstructorReplace called");
|
||||
if (LIKELY(instance_))
|
||||
instance_->Reset(thiz);
|
||||
else
|
||||
|
|
@ -44,6 +46,15 @@ namespace art {
|
|||
}
|
||||
}
|
||||
|
||||
CREATE_HOOK_STUB_ENTRIES(bool, ShouldUseInterpreterEntrypoint, void *art_method, const void* quick_code) {
|
||||
// TODO check hooked
|
||||
bool hooked = false;
|
||||
if (hooked && quick_code != nullptr) {
|
||||
return false;
|
||||
}
|
||||
return ShouldUseInterpreterEntrypointBackup(art_method, quick_code);
|
||||
}
|
||||
|
||||
public:
|
||||
ClassLinker(void *thiz) : HookedObject(thiz) {}
|
||||
|
||||
|
|
@ -51,7 +62,53 @@ namespace art {
|
|||
return instance_;
|
||||
}
|
||||
|
||||
// @ApiSensitive(Level.MIDDLE)
|
||||
static void Setup(void *handle, HookFunType hook_func) {
|
||||
LOGD("Classlinker hook setup, handle=%p", handle);
|
||||
// TODO: Maybe not compatible with Android 10-
|
||||
int api_level = edxp::GetAndroidApiLevel();
|
||||
size_t OFFSET_classlinker; // Get offset from art::Runtime::RunRootClinits() call in IDA
|
||||
switch(api_level) {
|
||||
case __ANDROID_API_O__:
|
||||
case __ANDROID_API_O_MR1__:
|
||||
#ifdef __LP64__
|
||||
OFFSET_classlinker = 464 / 8;
|
||||
#else
|
||||
OFFSET_classlinker = 284 / 4;
|
||||
#endif
|
||||
break;
|
||||
case __ANDROID_API_P__:
|
||||
#ifdef __LP64__
|
||||
OFFSET_classlinker = 528 / 8;
|
||||
#else
|
||||
OFFSET_classlinker = 336 / 4;
|
||||
#endif
|
||||
break;
|
||||
case __ANDROID_API_Q__:
|
||||
#ifdef __LP64__
|
||||
OFFSET_classlinker = 480 / 8;
|
||||
#else
|
||||
OFFSET_classlinker = 280 / 4;
|
||||
#endif
|
||||
break;
|
||||
default:
|
||||
LOGE("No valid offset for art::Runtime::class_linker_ found. Using Android R.");
|
||||
case __ANDROID_API_R__:
|
||||
#ifdef __LP64__
|
||||
OFFSET_classlinker = 472 / 8;
|
||||
#else
|
||||
OFFSET_classlinker = 276 / 4;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
// ClassLinker* GetClassLinker() but inlined
|
||||
void* cl = reinterpret_cast<void*>(
|
||||
reinterpret_cast<size_t*>(Runtime::Current()->Get()) + OFFSET_classlinker
|
||||
);
|
||||
LOGD("Classlinker object: %p", cl);
|
||||
instance_ = new ClassLinker(cl);
|
||||
|
||||
HOOK_FUNC(Constructor, "_ZN3art11ClassLinkerC2EPNS_11InternTableE",
|
||||
"_ZN3art11ClassLinkerC2EPNS_11InternTableEb"); // 10.0
|
||||
RETRIEVE_FUNC_SYMBOL(SetEntryPointsToInterpreter,
|
||||
|
|
@ -59,9 +116,18 @@ namespace art {
|
|||
|
||||
HOOK_FUNC(FixupStaticTrampolines,
|
||||
"_ZN3art11ClassLinker22FixupStaticTrampolinesENS_6ObjPtrINS_6mirror5ClassEEE");
|
||||
|
||||
// Sandhook will hook ShouldUseInterpreterEntrypoint, so we just skip
|
||||
// edxp::Context::GetInstance()->GetVariant() will not work here, so we use smh dirty hack
|
||||
if (api_level >= __ANDROID_API_R__ && access(edxp::kLibSandHookNativePath.c_str(), F_OK) == -1) {
|
||||
LOGD("Not sandhook, installing _ZN3art11ClassLinker30ShouldUseInterpreterEntrypointEPNS_9ArtMethodEPKv");
|
||||
HOOK_FUNC(ShouldUseInterpreterEntrypoint,
|
||||
"_ZN3art11ClassLinker30ShouldUseInterpreterEntrypointEPNS_9ArtMethodEPKv");
|
||||
}
|
||||
}
|
||||
|
||||
ALWAYS_INLINE void SetEntryPointsToInterpreter(void *art_method) const {
|
||||
LOGD("SetEntryPointsToInterpreter start, thiz=%p, art_method=%p", thiz_, art_method);
|
||||
if (LIKELY(thiz_))
|
||||
SetEntryPointsToInterpreter(thiz_, art_method);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ namespace art {
|
|||
return instance_;
|
||||
}
|
||||
|
||||
// @ApiSensitive(Level.MIDDLE)
|
||||
static void Setup(void *handle, HookFunType hook_func) {
|
||||
HOOK_FUNC(PreZygoteFork, "_ZN3art2gc4Heap13PreZygoteForkEv");
|
||||
RETRIEVE_FUNC_SYMBOL(WaitForGcToComplete,
|
||||
|
|
|
|||
|
|
@ -30,8 +30,9 @@ namespace art {
|
|||
return false;
|
||||
}
|
||||
|
||||
// @ApiSensitive(Level.HIGH)
|
||||
static void DisableHiddenApi(void *handle, HookFunType hook_func) {
|
||||
const int api_level = GetAndroidApiLevel();
|
||||
const int api_level = edxp::GetAndroidApiLevel();
|
||||
if (api_level < __ANDROID_API_P__) {
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ namespace art {
|
|||
public:
|
||||
JNIEnvExt(void *thiz) : HookedObject(thiz) {}
|
||||
|
||||
// @ApiSensitive(Level.MIDDLE)
|
||||
static void Setup(void *handle, HookFunType hook_func) {
|
||||
RETRIEVE_FUNC_SYMBOL(NewLocalRef, "_ZN3art9JNIEnvExt11NewLocalRefEPNS_6mirror6ObjectE");
|
||||
RETRIEVE_FUNC_SYMBOL(DeleteLocalRef, "_ZN3art9JNIEnvExt14DeleteLocalRefEP8_jobject");
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ namespace art {
|
|||
public:
|
||||
Class(void *thiz) : HookedObject(thiz) {}
|
||||
|
||||
// @ApiSensitive(Level.MIDDLE)
|
||||
static void Setup(void *handle, HookFunType hook_func) {
|
||||
RETRIEVE_FUNC_SYMBOL(GetDescriptor, "_ZN3art6mirror5Class13GetDescriptorEPNSt3__112"
|
||||
"basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEE");
|
||||
|
|
|
|||
|
|
@ -11,16 +11,14 @@ namespace art {
|
|||
return;
|
||||
}
|
||||
|
||||
// @ApiSensitive(Level.LOW)
|
||||
// http://androidxref.com/9.0.0_r3/xref/art/runtime/oat_file_manager.cc#637
|
||||
static void DisableOnlyUseSystemOatFiles(void *handle, HookFunType hook_func) {
|
||||
const int api_level = GetAndroidApiLevel();
|
||||
if (api_level == __ANDROID_API_P__) {
|
||||
const int api_level = edxp::GetAndroidApiLevel();
|
||||
if (api_level >= __ANDROID_API_P__) {
|
||||
HOOK_FUNC(SetOnlyUseSystemOatFiles,
|
||||
"_ZN3art14OatFileManager24SetOnlyUseSystemOatFilesEv");
|
||||
}
|
||||
if (api_level == __ANDROID_API_Q__) {
|
||||
HOOK_FUNC(SetOnlyUseSystemOatFiles,
|
||||
"_ZN3art14OatFileManager24SetOnlyUseSystemOatFilesEbb");
|
||||
"_ZN3art14OatFileManager24SetOnlyUseSystemOatFilesEv", // 9 & 11
|
||||
"_ZN3art14OatFileManager24SetOnlyUseSystemOatFilesEbb"); // 10
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -36,10 +36,14 @@ namespace art {
|
|||
return instance_;
|
||||
}
|
||||
|
||||
// @ApiSensitive(Level.LOW)
|
||||
static void Setup(void *handle, HookFunType hook_func) {
|
||||
HOOK_FUNC(Init, "_ZN3art7Runtime4InitEONS_18RuntimeArgumentMapE");
|
||||
RETRIEVE_FUNC_SYMBOL(DeoptimizeBootImage,
|
||||
"_ZN3art7Runtime19DeoptimizeBootImageEv");
|
||||
RETRIEVE_FIELD_SYMBOL(thiz, "_ZN3art7Runtime9instance_E");
|
||||
LOGD("_ZN3art7Runtime9instance_E = %p", thiz);
|
||||
instance_ = new Runtime(thiz);
|
||||
}
|
||||
|
||||
ALWAYS_INLINE void DeoptimizeBootImage() const {
|
||||
|
|
|
|||
|
|
@ -5,15 +5,15 @@
|
|||
#include <dlfcn.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#define __uintval(p) reinterpret_cast<uintptr_t>(p)
|
||||
#define __ptr(p) reinterpret_cast<void *>(p)
|
||||
#define __align_up(x, n) (((x) + ((n) - 1)) & ~((n) - 1))
|
||||
#define __align_down(x, n) ((x) & -(n))
|
||||
#define __page_size 4096
|
||||
#define __page_align(n) __align_up(static_cast<uintptr_t>(n), __page_size)
|
||||
#define __ptr_align(x) __ptr(__align_down(reinterpret_cast<uintptr_t>(x), __page_size))
|
||||
#define __make_rwx(p, n) ::mprotect(__ptr_align(p), \
|
||||
__page_align(__uintval(p) + n) != __page_align(__uintval(p)) ? __page_align(n) + __page_size : __page_align(n), \
|
||||
#define _uintval(p) reinterpret_cast<uintptr_t>(p)
|
||||
#define _ptr(p) reinterpret_cast<void *>(p)
|
||||
#define _align_up(x, n) (((x) + ((n) - 1)) & ~((n) - 1))
|
||||
#define _align_down(x, n) ((x) & -(n))
|
||||
#define _page_size 4096
|
||||
#define _page_align(n) _align_up(static_cast<uintptr_t>(n), _page_size)
|
||||
#define _ptr_align(x) _ptr(_align_down(reinterpret_cast<uintptr_t>(x), _page_size))
|
||||
#define _make_rwx(p, n) ::mprotect(_ptr_align(p), \
|
||||
_page_align(_uintval(p) + n) != _page_align(_uintval(p)) ? _page_align(n) + _page_size : _page_align(n), \
|
||||
PROT_READ | PROT_WRITE | PROT_EXEC)
|
||||
|
||||
typedef void (*HookFunType)(void *, void *, void **);
|
||||
|
|
@ -97,7 +97,7 @@ namespace edxp {
|
|||
|
||||
ALWAYS_INLINE inline static void HookFunction(HookFunType hook_fun, void *original,
|
||||
void *replace, void **backup) {
|
||||
__make_rwx(original, __page_size);
|
||||
_make_rwx(original, _page_size);
|
||||
hook_fun(original, replace, backup);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
#include <sys/types.h>
|
||||
#include <string>
|
||||
#include "art/base/macros.h"
|
||||
#include "android_build.h"
|
||||
#include "utils.h"
|
||||
|
||||
namespace edxp {
|
||||
|
||||
|
|
@ -18,34 +18,30 @@ namespace edxp {
|
|||
# define LP_SELECT(lp32, lp64) (lp32)
|
||||
#endif
|
||||
|
||||
static constexpr auto kInjectDexPath = "/system/framework/edxp.jar:"
|
||||
"/system/framework/eddalvikdx.jar:"
|
||||
"/system/framework/eddexmaker.jar";
|
||||
static const auto kInjectDexPath = "/system/framework/edxp.dex:"
|
||||
"/system/framework/eddalvikdx.dex:"
|
||||
"/system/framework/eddexmaker.dex"_str;
|
||||
|
||||
static constexpr auto kEntryClassName = "com.elderdrivers.riru.edxp.core.Main";
|
||||
static constexpr auto kClassLinkerClassName = "com.elderdrivers.riru.edxp.art.ClassLinker";
|
||||
static constexpr auto kSandHookClassName = "com.swift.sandhook.SandHook";
|
||||
static constexpr auto kSandHookNeverCallClassName = "com.swift.sandhook.ClassNeverCall";
|
||||
static const auto kEntryClassName = "com.elderdrivers.riru.edxp.core.Main"_str;
|
||||
static const auto kClassLinkerClassName = "com.elderdrivers.riru.edxp.art.ClassLinker";
|
||||
static const auto kSandHookClassName = "com.swift.sandhook.SandHook"_str;
|
||||
static const auto kSandHookNeverCallClassName = "com.swift.sandhook.ClassNeverCall"_str;
|
||||
|
||||
static constexpr auto kLibArtName = "libart.so";
|
||||
static constexpr auto kLibFwkName = "libandroid_runtime.so";
|
||||
static constexpr auto kLibWhaleName = "libwhale.edxp.so";
|
||||
static constexpr auto kLibSandHookName = "libsandhook.edxp.so";
|
||||
static const auto kLibArtName = "libart.so"_str;
|
||||
static const auto kLibFwName = "libandroidfw.so"_str;
|
||||
static const auto kLibSandHookName = "libsandhook.edxp.so"_str;
|
||||
static const auto kLibDlName = "libdl.so"_str;
|
||||
static const auto kLibSandHookNativeName = "libsandhook-native.so"_str;
|
||||
|
||||
static const auto kLibBasePath = std::string(LP_SELECT("/system/lib/", "/system/lib64/"));
|
||||
static const auto kLibRuntimeBasePath = std::string(
|
||||
LP_SELECT("/apex/com.android.runtime/lib/", "/apex/com.android.runtime/lib64/"));
|
||||
|
||||
static const auto kLibArtPath =
|
||||
(GetAndroidApiLevel() >= __ANDROID_API_Q__ ? kLibRuntimeBasePath : kLibBasePath) + kLibArtName;
|
||||
|
||||
static const auto kLibWhalePath = kLibBasePath + kLibWhaleName;
|
||||
static const auto kLibBasePath = std::string(
|
||||
LP_SELECT("/system/lib/",
|
||||
"/system/lib64/"));
|
||||
static const auto kLibArtLegacyPath = kLibBasePath + kLibArtName;
|
||||
static const auto kLibSandHookPath = kLibBasePath + kLibSandHookName;
|
||||
static const auto kLibFwPath = kLibBasePath + "libandroidfw.so";
|
||||
static const auto kLibDlPath = kLibBasePath + "libdl.so";
|
||||
static const auto kLibFwkPath = kLibBasePath + kLibFwkName;
|
||||
static const auto kLibSandHookNativePath = kLibBasePath + kLibSandHookNativeName;
|
||||
static const auto kLibFwPath = kLibBasePath + kLibFwName;
|
||||
|
||||
inline const char *const BoolToString(bool b) {
|
||||
inline constexpr const char *const BoolToString(bool b) {
|
||||
return b ? "true" : "false";
|
||||
}
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include <cstdint>
|
||||
|
||||
// @ApiSensitive(Level.MIDDLE)
|
||||
namespace android {
|
||||
|
||||
typedef int32_t status_t;
|
||||
|
|
|
|||
|
|
@ -1,409 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2016 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
|
||||
#include <dirent.h>
|
||||
#include <fcntl.h>
|
||||
#include <grp.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/un.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static const char *kPathPrefixWhitelist[] = {
|
||||
"/data/app/",
|
||||
"/data/app-private/"
|
||||
};
|
||||
|
||||
static const char *kFdPath = "/proc/self/fd";
|
||||
|
||||
// todo stay up to date
|
||||
// Keeps track of all relevant information (flags, offset etc.) of an
|
||||
// open zygote file descriptor.
|
||||
class FileDescriptorInfo {
|
||||
public:
|
||||
// Create a FileDescriptorInfo for a given file descriptor. Returns
|
||||
// |NULL| if an error occurred.
|
||||
static FileDescriptorInfo *createFromFd(int fd) {
|
||||
struct stat f_stat;
|
||||
// This should never happen; the zygote should always have the right set
|
||||
// of permissions required to stat all its open files.
|
||||
if (TEMP_FAILURE_RETRY(fstat(fd, &f_stat)) == -1) {
|
||||
LOGE("Unable to stat fd %d : %s", fd, strerror(errno));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (S_ISSOCK(f_stat.st_mode)) {
|
||||
std::string socket_name;
|
||||
if (!GetSocketName(fd, &socket_name)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!IsWhitelisted(socket_name)) {
|
||||
//LOGE("Socket name not whitelisted : %s (fd=%d)", socket_name.c_str(), fd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return new FileDescriptorInfo(fd);
|
||||
}
|
||||
|
||||
std::string file_path;
|
||||
if (!Readlink(fd, &file_path)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!IsWhitelisted(file_path)) {
|
||||
//LOGE("Not whitelisted : %s", file_path.c_str());
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// We only handle whitelisted regular files and character devices. Whitelisted
|
||||
// character devices must provide a guarantee of sensible behaviour when
|
||||
// reopened.
|
||||
//
|
||||
// S_ISDIR : Not supported. (We could if we wanted to, but it's unused).
|
||||
// S_ISLINK : Not supported.
|
||||
// S_ISBLK : Not supported.
|
||||
// S_ISFIFO : Not supported. Note that the zygote uses pipes to communicate
|
||||
// with the child process across forks but those should have been closed
|
||||
// before we got to this point.
|
||||
if (!S_ISCHR(f_stat.st_mode) && !S_ISREG(f_stat.st_mode)) {
|
||||
LOGE("Unsupported st_mode %d", f_stat.st_mode);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// File descriptor flags : currently on FD_CLOEXEC. We can set these
|
||||
// using F_SETFD - we're single threaded at this point of execution so
|
||||
// there won't be any races.
|
||||
const int fd_flags = TEMP_FAILURE_RETRY(fcntl(fd, F_GETFD));
|
||||
if (fd_flags == -1) {
|
||||
LOGE("Failed fcntl(%d, F_GETFD) : %s", fd, strerror(errno));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// File status flags :
|
||||
// - File access mode : (O_RDONLY, O_WRONLY...) we'll pass these through
|
||||
// to the open() call.
|
||||
//
|
||||
// - File creation flags : (O_CREAT, O_EXCL...) - there's not much we can
|
||||
// do about these, since the file has already been created. We shall ignore
|
||||
// them here.
|
||||
//
|
||||
// - Other flags : We'll have to set these via F_SETFL. On linux, F_SETFL
|
||||
// can only set O_APPEND, O_ASYNC, O_DIRECT, O_NOATIME, and O_NONBLOCK.
|
||||
// In particular, it can't set O_SYNC and O_DSYNC. We'll have to test for
|
||||
// their presence and pass them in to open().
|
||||
int fs_flags = TEMP_FAILURE_RETRY(fcntl(fd, F_GETFL));
|
||||
if (fs_flags == -1) {
|
||||
LOGE("Failed fcntl(%d, F_GETFL) : %s", fd, strerror(errno));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// File offset : Ignore the offset for non seekable files.
|
||||
const off_t offset = TEMP_FAILURE_RETRY(lseek64(fd, 0, SEEK_CUR));
|
||||
|
||||
// We pass the flags that open accepts to open, and use F_SETFL for
|
||||
// the rest of them.
|
||||
static const int kOpenFlags = (O_RDONLY | O_WRONLY | O_RDWR | O_DSYNC | O_SYNC);
|
||||
int open_flags = fs_flags & (kOpenFlags);
|
||||
fs_flags = fs_flags & (~(kOpenFlags));
|
||||
|
||||
return new FileDescriptorInfo(f_stat, file_path, fd, open_flags, fd_flags, fs_flags,
|
||||
offset);
|
||||
}
|
||||
|
||||
bool Detach() const {
|
||||
const int dev_null_fd = open("/dev/null", O_RDWR);
|
||||
if (dev_null_fd < 0) {
|
||||
LOGE("Failed to open /dev/null : %s", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (dup2(dev_null_fd, fd) == -1) {
|
||||
LOGE("Failed dup2 on socket descriptor %d : %s", fd, strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (close(dev_null_fd) == -1) {
|
||||
LOGE("Failed close(%d) : %s", dev_null_fd, strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Reopen() const {
|
||||
if (is_sock) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// NOTE: This might happen if the file was unlinked after being opened.
|
||||
// It's a common pattern in the case of temporary files and the like but
|
||||
// we should not allow such usage from the zygote.
|
||||
const int new_fd = TEMP_FAILURE_RETRY(open(file_path.c_str(), open_flags));
|
||||
|
||||
if (new_fd == -1) {
|
||||
LOGE("Failed open(%s, %d) : %s", file_path.c_str(), open_flags, strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (TEMP_FAILURE_RETRY(fcntl(new_fd, F_SETFD, fd_flags)) == -1) {
|
||||
close(new_fd);
|
||||
LOGE("Failed fcntl(%d, F_SETFD, %x) : %s", new_fd, fd_flags, strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (TEMP_FAILURE_RETRY(fcntl(new_fd, F_SETFL, fs_flags)) == -1) {
|
||||
close(new_fd);
|
||||
LOGE("Failed fcntl(%d, F_SETFL, %x) : %s", new_fd, fs_flags, strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (offset != -1 && TEMP_FAILURE_RETRY(lseek64(new_fd, offset, SEEK_SET)) == -1) {
|
||||
close(new_fd);
|
||||
LOGE("Failed lseek64(%d, SEEK_SET) : %s", new_fd, strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (TEMP_FAILURE_RETRY(dup2(new_fd, fd)) == -1) {
|
||||
close(new_fd);
|
||||
LOGE("Failed dup2(%d, %d) : %s", fd, new_fd, strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
close(new_fd);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const int fd;
|
||||
const struct stat stat;
|
||||
const std::string file_path;
|
||||
const int open_flags;
|
||||
const int fd_flags;
|
||||
const int fs_flags;
|
||||
const off_t offset;
|
||||
const bool is_sock;
|
||||
|
||||
private:
|
||||
FileDescriptorInfo(int pfd) :
|
||||
fd(pfd),
|
||||
stat(),
|
||||
open_flags(0),
|
||||
fd_flags(0),
|
||||
fs_flags(0),
|
||||
offset(0),
|
||||
is_sock(true) {
|
||||
}
|
||||
|
||||
FileDescriptorInfo(struct stat pstat, const std::string &pfile_path, int pfd, int popen_flags,
|
||||
int pfd_flags, int pfs_flags, off_t poffset) :
|
||||
fd(pfd),
|
||||
stat(pstat),
|
||||
file_path(pfile_path),
|
||||
open_flags(popen_flags),
|
||||
fd_flags(pfd_flags),
|
||||
fs_flags(pfs_flags),
|
||||
offset(poffset),
|
||||
is_sock(false) {
|
||||
}
|
||||
|
||||
// Returns true iff. a given path is whitelisted.
|
||||
static bool IsWhitelisted(const std::string &path) {
|
||||
for (size_t i = 0;
|
||||
i < (sizeof(kPathPrefixWhitelist) / sizeof(kPathPrefixWhitelist[0])); ++i) {
|
||||
if (path.compare(0, strlen(kPathPrefixWhitelist[i]), kPathPrefixWhitelist[i]) == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: Call android::base::Readlink instead of copying the code here.
|
||||
static bool Readlink(const int fd, std::string *result) {
|
||||
char path[64];
|
||||
snprintf(path, sizeof(path), "/proc/self/fd/%d", fd);
|
||||
|
||||
// Code copied from android::base::Readlink starts here :
|
||||
|
||||
// Annoyingly, the readlink system call returns EINVAL for a zero-sized buffer,
|
||||
// and truncates to whatever size you do supply, so it can't be used to query.
|
||||
// We could call lstat first, but that would introduce a race condition that
|
||||
// we couldn't detect.
|
||||
// ext2 and ext4 both have PAGE_SIZE limitations, so we assume that here.
|
||||
char *buf = new char[4096];
|
||||
ssize_t len = readlink(path, buf, 4096);
|
||||
if (len == -1) {
|
||||
delete[] buf;
|
||||
return false;
|
||||
}
|
||||
|
||||
result->assign(buf, len);
|
||||
delete[] buf;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Returns the locally-bound name of the socket |fd|. Returns true
|
||||
// iff. all of the following hold :
|
||||
//
|
||||
// - the socket's sa_family is AF_UNIX.
|
||||
// - the length of the path is greater than zero (i.e, not an unnamed socket).
|
||||
// - the first byte of the path isn't zero (i.e, not a socket with an abstract
|
||||
// address).
|
||||
static bool GetSocketName(const int fd, std::string *result) {
|
||||
sockaddr_storage ss;
|
||||
sockaddr *addr = reinterpret_cast<sockaddr *>(&ss);
|
||||
socklen_t addr_len = sizeof(ss);
|
||||
|
||||
if (TEMP_FAILURE_RETRY(getsockname(fd, addr, &addr_len)) == -1) {
|
||||
LOGE("Failed getsockname(%d) : %s", fd, strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
#if PLATFORM_SDK_VERSION <= 23
|
||||
if (addr->sa_family == AF_NETLINK) {
|
||||
(*result) = "@netlink@";
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (addr->sa_family != AF_UNIX) {
|
||||
//LOGE("Unsupported socket (fd=%d) with family %d", fd, addr->sa_family);
|
||||
return false;
|
||||
}
|
||||
|
||||
const sockaddr_un *unix_addr = reinterpret_cast<const sockaddr_un *>(&ss);
|
||||
|
||||
size_t path_len = addr_len - offsetof(struct sockaddr_un, sun_path);
|
||||
// This is an unnamed local socket, we do not accept it.
|
||||
if (path_len == 0) {
|
||||
//LOGE("Unsupported AF_UNIX socket (fd=%d) with empty path.", fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
// This is a local socket with an abstract address, we do not accept it.
|
||||
if (unix_addr->sun_path[0] == '\0') {
|
||||
//LOGE("Unsupported AF_UNIX socket (fd=%d) with abstract address.", fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
// If we're here, sun_path must refer to a null terminated filesystem
|
||||
// pathname (man 7 unix). Remove the terminator before assigning it to an
|
||||
// std::string.
|
||||
if (unix_addr->sun_path[path_len - 1] == '\0') {
|
||||
--path_len;
|
||||
}
|
||||
|
||||
result->assign(unix_addr->sun_path, path_len);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// DISALLOW_COPY_AND_ASSIGN(FileDescriptorInfo);
|
||||
FileDescriptorInfo(const FileDescriptorInfo &);
|
||||
|
||||
void operator=(const FileDescriptorInfo &);
|
||||
};
|
||||
|
||||
// A FileDescriptorTable is a collection of FileDescriptorInfo objects
|
||||
// keyed by their FDs.
|
||||
class FileDescriptorTable {
|
||||
public:
|
||||
// Creates a new FileDescriptorTable. This function scans
|
||||
// /proc/self/fd for the list of open file descriptors and collects
|
||||
// information about them. Returns NULL if an error occurs.
|
||||
static FileDescriptorTable *Create() {
|
||||
DIR *d = opendir(kFdPath);
|
||||
if (d == NULL) {
|
||||
LOGE("Unable to open directory %s: %s", kFdPath, strerror(errno));
|
||||
return NULL;
|
||||
}
|
||||
int dir_fd = dirfd(d);
|
||||
dirent *e;
|
||||
|
||||
std::unordered_map<int, FileDescriptorInfo *> open_fd_map;
|
||||
while ((e = readdir(d)) != NULL) {
|
||||
const int fd = ParseFd(e, dir_fd);
|
||||
if (fd == -1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
FileDescriptorInfo *info = FileDescriptorInfo::createFromFd(fd);
|
||||
if (info == NULL) {
|
||||
continue;
|
||||
}
|
||||
info->Detach();
|
||||
open_fd_map[fd] = info;
|
||||
}
|
||||
|
||||
if (closedir(d) == -1) {
|
||||
LOGE("Unable to close directory : %s", strerror(errno));
|
||||
return NULL;
|
||||
}
|
||||
return new FileDescriptorTable(open_fd_map);
|
||||
}
|
||||
|
||||
// Reopens all file descriptors that are contained in the table.
|
||||
void Reopen() {
|
||||
std::unordered_map<int, FileDescriptorInfo *>::const_iterator it;
|
||||
for (it = open_fd_map_.begin(); it != open_fd_map_.end(); ++it) {
|
||||
const FileDescriptorInfo *info = it->second;
|
||||
if (info != NULL) {
|
||||
info->Reopen();
|
||||
delete info;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
FileDescriptorTable(const std::unordered_map<int, FileDescriptorInfo *> &map)
|
||||
: open_fd_map_(map) {
|
||||
}
|
||||
|
||||
static int ParseFd(dirent *e, int dir_fd) {
|
||||
char *end;
|
||||
const int fd = strtol(e->d_name, &end, 10);
|
||||
if ((*end) != '\0') {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Don't bother with the standard input/output/error, they're handled
|
||||
// specially post-fork anyway.
|
||||
if (fd <= STDERR_FILENO || fd == dir_fd) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
// Invariant: All values in this unordered_map are non-NULL.
|
||||
std::unordered_map<int, FileDescriptorInfo *> open_fd_map_;
|
||||
|
||||
// DISALLOW_COPY_AND_ASSIGN(FileDescriptorTable);
|
||||
FileDescriptorTable(const FileDescriptorTable &);
|
||||
|
||||
void operator=(const FileDescriptorTable &);
|
||||
};
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <android-base/strings.h>
|
||||
#include "base/object.h"
|
||||
|
||||
namespace android {
|
||||
|
||||
// Static whitelist of open paths that the zygote is allowed to keep open.
|
||||
static const char *kPathWhitelist[] = {
|
||||
"/data/app/",
|
||||
"/data/app-private/"
|
||||
};
|
||||
|
||||
class FileDescriptorWhitelist : public edxp::HookedObject {
|
||||
|
||||
public:
|
||||
FileDescriptorWhitelist(void *thiz) : HookedObject(thiz) {}
|
||||
|
||||
static void Setup(void *handle, HookFunType hook_func) {
|
||||
HOOK_FUNC(IsAllowed,
|
||||
"_ZNK23FileDescriptorWhitelist9IsAllowedERKNSt3__112basic_stringIcNS0_11char_traitsIcEENS0_9allocatorIcEEEE");
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
CREATE_HOOK_STUB_ENTRIES(bool, IsAllowed, void *thiz, const std::string &path) {
|
||||
for (const auto &whitelist_path : kPathWhitelist) {
|
||||
if (android::base::StartsWith(path, whitelist_path))
|
||||
return true;
|
||||
}
|
||||
return IsAllowedBackup(thiz, path);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -1,15 +1,12 @@
|
|||
#ifndef _LOGGING_H
|
||||
#define _LOGGING_H
|
||||
|
||||
#include <errno.h>
|
||||
#include "android/log.h"
|
||||
#include <android/log.h>
|
||||
|
||||
#ifndef LOG_TAG
|
||||
#define LOG_TAG "EdXposed"
|
||||
#endif
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#ifdef LOG_DISABLED
|
||||
#define LOGD(...)
|
||||
#define LOGV(...)
|
||||
|
|
@ -17,7 +14,7 @@
|
|||
#define LOGW(...)
|
||||
#define LOGE(...)
|
||||
#else
|
||||
#ifdef DEBUG
|
||||
#ifndef NDEBUG
|
||||
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
|
||||
#else
|
||||
#define LOGD(...)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,31 @@
|
|||
//
|
||||
// Created by loves on 11/13/2020.
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <filesystem>
|
||||
#include "logging.h"
|
||||
#include <sys/system_properties.h>
|
||||
|
||||
namespace edxp {
|
||||
|
||||
static inline int32_t GetAndroidApiLevel() {
|
||||
char prop_value[PROP_VALUE_MAX];
|
||||
__system_property_get("ro.build.version.sdk", prop_value);
|
||||
return atoi(prop_value);
|
||||
}
|
||||
|
||||
inline const std::string operator ""_str(const char *str, std::size_t size) {
|
||||
return {str, size};
|
||||
}
|
||||
|
||||
inline bool path_exists(const std::filesystem::path &path) {
|
||||
try {
|
||||
return std::filesystem::exists(path);
|
||||
} catch (const std::filesystem::filesystem_error &e) {
|
||||
LOGE("%s", e.what());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -12,70 +12,64 @@
|
|||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
#include <android_build.h>
|
||||
#include <logging.h>
|
||||
#include <linux/limits.h>
|
||||
#include <JNIHelper.h>
|
||||
#include <climits>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include "art/runtime/native/native_util.h"
|
||||
#include "config_manager.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace art;
|
||||
#include "utils.h"
|
||||
|
||||
namespace edxp {
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
std::string ConfigManager::RetrieveInstallerPkgName() const {
|
||||
std::string data_test_path = data_path_prefix_ + kPrimaryInstallerPkgName;
|
||||
if (access(data_test_path.c_str(), F_OK) == 0) {
|
||||
LOGI("using installer %s", kPrimaryInstallerPkgName);
|
||||
std::string data_test_path = data_path_prefix_ / kPrimaryInstallerPkgName;
|
||||
if (path_exists(data_test_path)) {
|
||||
LOGI("using installer %s", kPrimaryInstallerPkgName.c_str());
|
||||
return kPrimaryInstallerPkgName;
|
||||
}
|
||||
data_test_path = data_path_prefix_ + kLegacyInstallerPkgName;
|
||||
if (access(data_test_path.c_str(), F_OK) == 0) {
|
||||
LOGI("using installer %s", kLegacyInstallerPkgName);
|
||||
data_test_path = data_path_prefix_ / kLegacyInstallerPkgName;
|
||||
if (path_exists(data_test_path)) {
|
||||
LOGI("using installer %s", kLegacyInstallerPkgName.c_str());
|
||||
return kLegacyInstallerPkgName;
|
||||
}
|
||||
LOGE("no supported installer app found, using default: %s", kPrimaryInstallerPkgName);
|
||||
LOGE("no supported installer app found, using default: %s",
|
||||
kPrimaryInstallerPkgName.c_str());
|
||||
return kPrimaryInstallerPkgName;
|
||||
}
|
||||
|
||||
void ConfigManager::SnapshotBlackWhiteList() {
|
||||
DIR *dir;
|
||||
struct dirent *dent;
|
||||
dir = opendir(whitelist_path_.c_str());
|
||||
if (dir != nullptr) {
|
||||
while ((dent = readdir(dir)) != nullptr) {
|
||||
if (dent->d_type == DT_REG) {
|
||||
const char *fileName = dent->d_name;
|
||||
LOGI(" whitelist: %s", fileName);
|
||||
white_list_default_.emplace_back(fileName);
|
||||
white_list_default_.clear();
|
||||
black_list_default_.clear();
|
||||
try {
|
||||
for (auto &item: fs::directory_iterator(whitelist_path_)) {
|
||||
if (item.is_regular_file()) {
|
||||
const auto &file_name = item.path().filename();
|
||||
LOGI(" whitelist: %s", file_name.c_str());
|
||||
white_list_default_.emplace(file_name);
|
||||
}
|
||||
}
|
||||
closedir(dir);
|
||||
}
|
||||
dir = opendir(blacklist_path_.c_str());
|
||||
if (dir != nullptr) {
|
||||
while ((dent = readdir(dir)) != nullptr) {
|
||||
if (dent->d_type == DT_REG) {
|
||||
const char *fileName = dent->d_name;
|
||||
LOGI(" blacklist: %s", fileName);
|
||||
black_list_default_.emplace_back(fileName);
|
||||
for (auto &item: fs::directory_iterator(blacklist_path_)) {
|
||||
if (item.is_regular_file()) {
|
||||
const auto &file_name = item.path().filename();
|
||||
LOGI(" blacklist: %s", file_name.c_str());
|
||||
black_list_default_.emplace(file_name);
|
||||
}
|
||||
}
|
||||
closedir(dir);
|
||||
} catch (const fs::filesystem_error &e) {
|
||||
LOGE("%s", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
void ConfigManager::UpdateConfigPath(const uid_t user) {
|
||||
if (last_user_ != user) {
|
||||
LOGI("updating config data paths from %u to %u...", last_user_, user);
|
||||
last_user_ = user;
|
||||
}
|
||||
if (LIKELY(last_user_ == user && instance_)) return;
|
||||
|
||||
const char *format = use_prot_storage_ ? "/data/user_de/%u/" : "/data/user/%u/";
|
||||
char buff[PATH_MAX];
|
||||
snprintf(buff, sizeof(buff), format, last_user_);
|
||||
data_path_prefix_ = buff;
|
||||
LOGI("updating config data paths from %u to %u...", last_user_, user);
|
||||
last_user_ = user;
|
||||
|
||||
data_path_prefix_ = use_prot_storage_ ? "/data/user_de" : "/data/user";
|
||||
data_path_prefix_ /= std::to_string(last_user_);
|
||||
|
||||
installer_pkg_name_ = RetrieveInstallerPkgName();
|
||||
base_config_path_ = GetConfigPath("");
|
||||
|
|
@ -83,15 +77,20 @@ namespace edxp {
|
|||
whitelist_path_ = GetConfigPath("whitelist/");
|
||||
use_whitelist_path_ = GetConfigPath("usewhitelist");
|
||||
|
||||
dynamic_modules_enabled_ = access(GetConfigPath("dynamicmodules").c_str(), F_OK) == 0;
|
||||
black_white_list_enabled_ = access(GetConfigPath("blackwhitelist").c_str(), F_OK) == 0;
|
||||
deopt_boot_image_enabled_ = access(GetConfigPath("deoptbootimage").c_str(), F_OK) == 0;
|
||||
resources_hook_enabled_ = access(GetConfigPath("disable_resources").c_str(), F_OK) != 0;
|
||||
no_module_log_enabled_ = access(GetConfigPath("disable_modules_log").c_str(), F_OK) == 0;
|
||||
hidden_api_bypass_enabled_ = access(GetConfigPath("disable_hidden_api_bypass").c_str(), F_OK) != 0;
|
||||
dynamic_modules_enabled_ = path_exists(GetConfigPath("dynamicmodules"));
|
||||
black_white_list_enabled_ = path_exists(GetConfigPath("blackwhitelist"));
|
||||
deopt_boot_image_enabled_ = path_exists(GetConfigPath("deoptbootimage"));
|
||||
resources_hook_enabled_ = path_exists(GetConfigPath("enable_resources"));
|
||||
no_module_log_enabled_ = path_exists(GetConfigPath("disable_modules_log"));
|
||||
hidden_api_bypass_enabled_ =
|
||||
!path_exists(GetConfigPath("disable_hidden_api_bypass"));
|
||||
modules_list_.clear();
|
||||
app_modules_list_.clear();
|
||||
|
||||
UpdateModuleList();
|
||||
|
||||
// use_white_list snapshot
|
||||
use_white_list_snapshot_ = access(use_whitelist_path_.c_str(), F_OK) == 0;
|
||||
use_white_list_snapshot_ = path_exists(use_whitelist_path_);
|
||||
LOGI("data path prefix: %s", data_path_prefix_.c_str());
|
||||
LOGI(" application list mode: %s", BoolToString(black_white_list_enabled_));
|
||||
LOGI(" using whitelist: %s", BoolToString(use_white_list_snapshot_));
|
||||
|
|
@ -105,118 +104,130 @@ namespace edxp {
|
|||
}
|
||||
}
|
||||
|
||||
bool ConfigManager::IsAppNeedHook(const std::string &app_data_dir) {
|
||||
std::string ConfigManager::GetPackageNameFromBaseApkPath(const fs::path &path) {
|
||||
std::vector<std::string> paths(path.begin(), path.end());
|
||||
auto base_apk = paths.back(); // base.apk
|
||||
if (base_apk != "base.apk") return {};
|
||||
paths.pop_back();
|
||||
auto pkg_name_with_obfuscation = paths.back();
|
||||
if (auto pos = pkg_name_with_obfuscation.find('-'); pos != std::string::npos) {
|
||||
return pkg_name_with_obfuscation.substr(0, pos);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
// TODO ignore unrelated processes
|
||||
bool ConfigManager::IsAppNeedHook(const uid_t user, const std::string &package_name) {
|
||||
// zygote always starts with `uid == 0` and then fork into different user.
|
||||
// so we have to check if we are the correct user or not.
|
||||
uid_t user = 0;
|
||||
char package_name[PATH_MAX];
|
||||
if (sscanf(app_data_dir.c_str(), "/data/%*[^/]/%u/%s", &user, package_name) != 2) {
|
||||
if (sscanf(app_data_dir.c_str(), "/data/%*[^/]/%s", package_name) != 1) {
|
||||
package_name[0] = '\0';
|
||||
LOGE("can't parse %s", app_data_dir.c_str());
|
||||
return false; // default to no hooking for safety
|
||||
}
|
||||
}
|
||||
|
||||
if (last_user_ != user) {
|
||||
UpdateConfigPath(user);
|
||||
}
|
||||
UpdateConfigPath(user);
|
||||
|
||||
if (!black_white_list_enabled_) {
|
||||
return true;
|
||||
}
|
||||
bool can_access_app_data = access(base_config_path_.c_str(), F_OK) == 0;
|
||||
bool can_access_app_data = path_exists(base_config_path_);
|
||||
bool use_white_list;
|
||||
if (can_access_app_data) {
|
||||
use_white_list = access(use_whitelist_path_.c_str(), F_OK) == 0;
|
||||
use_white_list = path_exists(use_whitelist_path_);
|
||||
} else {
|
||||
LOGE("can't access config path, using snapshot use_white_list: %s",
|
||||
app_data_dir.c_str());
|
||||
package_name.c_str());
|
||||
use_white_list = use_white_list_snapshot_;
|
||||
}
|
||||
if (strcmp(package_name, kPrimaryInstallerPkgName) == 0
|
||||
|| strcmp(package_name, kLegacyInstallerPkgName) == 0) {
|
||||
if (package_name == kPrimaryInstallerPkgName
|
||||
|| package_name == kLegacyInstallerPkgName) {
|
||||
// always hook installer apps
|
||||
return true;
|
||||
}
|
||||
if (use_white_list) {
|
||||
if (!can_access_app_data) {
|
||||
LOGE("can't access config path, using snapshot white list: %s",
|
||||
app_data_dir.c_str());
|
||||
return !(find(white_list_default_.begin(), white_list_default_.end(),
|
||||
package_name) ==
|
||||
white_list_default_.end());
|
||||
package_name.c_str());
|
||||
return white_list_default_.count(package_name);
|
||||
}
|
||||
std::string target_path = whitelist_path_ + package_name;
|
||||
bool res = access(target_path.c_str(), F_OK) == 0;
|
||||
LOGD("using whitelist, %s -> %d", app_data_dir.c_str(), res);
|
||||
std::string target_path = whitelist_path_ / package_name;
|
||||
bool res = path_exists(target_path);
|
||||
LOGD("using whitelist, %s -> %d", package_name.c_str(), res);
|
||||
return res;
|
||||
} else {
|
||||
if (!can_access_app_data) {
|
||||
LOGE("can't access config path, using snapshot black list: %s",
|
||||
app_data_dir.c_str());
|
||||
return find(black_list_default_.begin(), black_list_default_.end(), package_name) ==
|
||||
black_list_default_.end();
|
||||
package_name.c_str());
|
||||
return black_list_default_.count(package_name) == 0;
|
||||
}
|
||||
std::string target_path = blacklist_path_ + package_name;
|
||||
bool res = access(target_path.c_str(), F_OK) != 0;
|
||||
LOGD("using blacklist, %s -> %d", app_data_dir.c_str(), res);
|
||||
std::string target_path = blacklist_path_ / package_name;
|
||||
bool res = !path_exists(target_path);
|
||||
LOGD("using blacklist, %s -> %d", package_name.c_str(), res);
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
ALWAYS_INLINE bool ConfigManager::IsBlackWhiteListEnabled() const {
|
||||
return black_white_list_enabled_;
|
||||
}
|
||||
|
||||
ALWAYS_INLINE bool ConfigManager::IsDynamicModulesEnabled() const {
|
||||
return dynamic_modules_enabled_;
|
||||
}
|
||||
|
||||
ALWAYS_INLINE bool ConfigManager::IsNoModuleLogEnabled() const {
|
||||
return no_module_log_enabled_;
|
||||
}
|
||||
|
||||
ALWAYS_INLINE bool ConfigManager::IsResourcesHookEnabled() const {
|
||||
return resources_hook_enabled_;
|
||||
}
|
||||
|
||||
ALWAYS_INLINE bool ConfigManager::IsDeoptBootImageEnabled() const {
|
||||
return deopt_boot_image_enabled_;
|
||||
}
|
||||
|
||||
ALWAYS_INLINE bool ConfigManager::IsHiddenAPIBypassEnabled() const {
|
||||
return hidden_api_bypass_enabled_;
|
||||
}
|
||||
|
||||
ALWAYS_INLINE std::string ConfigManager::GetInstallerPackageName() const {
|
||||
return installer_pkg_name_;
|
||||
}
|
||||
|
||||
ALWAYS_INLINE std::string ConfigManager::GetXposedPropPath() const {
|
||||
return kXposedPropPath;
|
||||
}
|
||||
|
||||
ALWAYS_INLINE std::string ConfigManager::GetLibSandHookName() const {
|
||||
return kLibSandHookName;
|
||||
}
|
||||
|
||||
ALWAYS_INLINE std::string ConfigManager::GetLibWhaleName() const {
|
||||
return kLibWhaleName;
|
||||
}
|
||||
|
||||
ALWAYS_INLINE std::string ConfigManager::GetDataPathPrefix() const {
|
||||
return data_path_prefix_;
|
||||
}
|
||||
|
||||
ALWAYS_INLINE std::string ConfigManager::GetConfigPath(const std::string &suffix) const {
|
||||
return data_path_prefix_ + installer_pkg_name_ + "/conf/" + suffix;
|
||||
};
|
||||
|
||||
ConfigManager::ConfigManager() {
|
||||
use_prot_storage_ = GetAndroidApiLevel() >= __ANDROID_API_N__;
|
||||
last_user_ = 0;
|
||||
UpdateConfigPath(last_user_);
|
||||
}
|
||||
|
||||
bool ConfigManager::UpdateModuleList() {
|
||||
if (LIKELY(!modules_list_.empty()) && !IsDynamicModulesEnabled())
|
||||
return true;
|
||||
modules_list_.clear();
|
||||
auto global_modules_list = GetConfigPath("modules.list");
|
||||
if (!path_exists(global_modules_list)) {
|
||||
LOGE("Cannot access path %s", global_modules_list.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (auto last_write_time = fs::last_write_time(global_modules_list);
|
||||
LIKELY(last_write_time < last_write_time_)) {
|
||||
return true;
|
||||
} else {
|
||||
last_write_time_ = last_write_time;
|
||||
}
|
||||
|
||||
std::ifstream ifs(global_modules_list);
|
||||
if (!ifs.good()) {
|
||||
LOGE("Cannot access path %s", global_modules_list.c_str());
|
||||
return false;
|
||||
}
|
||||
std::string module;
|
||||
while (std::getline(ifs, module)) {
|
||||
const auto &module_pkg_name = GetPackageNameFromBaseApkPath(module);
|
||||
modules_list_.emplace_back(std::move(module), std::unordered_set<std::string>{});
|
||||
const auto &module_scope_conf = GetConfigPath(module_pkg_name + ".conf");
|
||||
if (!path_exists(module_scope_conf)) {
|
||||
LOGD("module scope is not set for %s", module_pkg_name.c_str());
|
||||
continue;
|
||||
}
|
||||
std::ifstream ifs_c(module_scope_conf);
|
||||
if (!ifs_c.good()) {
|
||||
LOGE("Cannot access path %s", module_scope_conf.c_str());
|
||||
continue;
|
||||
}
|
||||
auto &scope = modules_list_.back().second;
|
||||
std::string app_pkg_name;
|
||||
while (std::getline(ifs_c, app_pkg_name)) {
|
||||
if (!app_pkg_name.empty())
|
||||
scope.emplace(std::move(app_pkg_name));
|
||||
}
|
||||
scope.insert(module_pkg_name); // Always add module itself
|
||||
LOGD("scope of %s is:\n%s", module_pkg_name.c_str(), ([&scope]() {
|
||||
std::ostringstream join;
|
||||
std::copy(scope.begin(), scope.end(),
|
||||
std::ostream_iterator<std::string>(join, "\n"));
|
||||
return join.str();
|
||||
})().c_str());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ConfigManager::UpdateAppModuleList(const uid_t user, const std::string &pkg_name) {
|
||||
UpdateConfigPath(user);
|
||||
app_modules_list_.clear();
|
||||
for (const auto&[module, scope]: modules_list_) {
|
||||
if (scope.empty() || scope.count(pkg_name)) app_modules_list_.push_back(module);
|
||||
}
|
||||
return !app_modules_list_.empty() || pkg_name == installer_pkg_name_;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -3,71 +3,91 @@
|
|||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <JNIHelper.h>
|
||||
#include "JNIHelper.h"
|
||||
#include <utility>
|
||||
#include <art/runtime/native/native_util.h>
|
||||
#include <filesystem>
|
||||
#include <unordered_set>
|
||||
#include <unordered_map>
|
||||
#include "config.h"
|
||||
|
||||
namespace edxp {
|
||||
|
||||
static constexpr const char *kPrimaryInstallerPkgName = "org.meowcat.edxposed.manager";
|
||||
static constexpr const char *kLegacyInstallerPkgName = "de.robv.android.xposed.installer";
|
||||
static constexpr auto kXposedPropPath = "/system/framework/edconfig.jar";
|
||||
static const std::string kPrimaryInstallerPkgName = "org.meowcat.edxposed.manager";
|
||||
static const std::string kLegacyInstallerPkgName = "de.robv.android.xposed.installer";
|
||||
static const std::string kXposedPropPath = "/system/framework/edconfig.jar";
|
||||
|
||||
class ConfigManager {
|
||||
public:
|
||||
|
||||
static ConfigManager *GetInstance() {
|
||||
if (instance_ == 0) {
|
||||
instance_ = new ConfigManager();
|
||||
inline static ConfigManager *GetInstance() {
|
||||
if (!instance_) {
|
||||
instance_ = std::make_unique<ConfigManager>();
|
||||
}
|
||||
return instance_;
|
||||
return instance_.get();
|
||||
}
|
||||
|
||||
bool IsBlackWhiteListEnabled() const;
|
||||
inline static std::unique_ptr<ConfigManager> ReleaseInstance() {
|
||||
return std::move(instance_);
|
||||
}
|
||||
|
||||
bool IsDynamicModulesEnabled() const;
|
||||
inline auto IsBlackWhiteListEnabled() const { return black_white_list_enabled_; }
|
||||
|
||||
bool IsResourcesHookEnabled() const;
|
||||
inline auto IsDynamicModulesEnabled() const { return dynamic_modules_enabled_; }
|
||||
|
||||
bool IsDeoptBootImageEnabled() const;
|
||||
inline auto IsResourcesHookEnabled() const { return resources_hook_enabled_; }
|
||||
|
||||
bool IsNoModuleLogEnabled() const;
|
||||
inline auto IsDeoptBootImageEnabled() const { return deopt_boot_image_enabled_; }
|
||||
|
||||
bool IsHiddenAPIBypassEnabled() const;
|
||||
inline auto IsNoModuleLogEnabled() const { return no_module_log_enabled_; }
|
||||
|
||||
std::string GetInstallerPackageName() const;
|
||||
inline auto IsHiddenAPIBypassEnabled() const { return hidden_api_bypass_enabled_; }
|
||||
|
||||
std::string GetXposedPropPath() const;
|
||||
inline auto GetInstallerPackageName() const { return installer_pkg_name_; }
|
||||
|
||||
std::string GetLibSandHookName() const;
|
||||
inline auto GetXposedPropPath() const { return kXposedPropPath; }
|
||||
|
||||
std::string GetLibWhaleName() const;
|
||||
inline auto GetLibSandHookName() const { return kLibSandHookName; }
|
||||
|
||||
std::string GetDataPathPrefix() const;
|
||||
inline auto GetDataPathPrefix() const { return data_path_prefix_; }
|
||||
|
||||
std::string GetConfigPath(const std::string &suffix) const;
|
||||
inline auto GetConfigPath(const std::string &suffix) const {
|
||||
return data_path_prefix_ / installer_pkg_name_ / "conf" / suffix;
|
||||
}
|
||||
|
||||
bool IsAppNeedHook(const std::string &app_data_dir);
|
||||
inline auto GetAppModulesList() const { return app_modules_list_; };
|
||||
|
||||
bool UpdateAppModuleList(const uid_t user, const std::string &pkg_name);
|
||||
|
||||
bool IsAppNeedHook(const uid_t user, const std::string &pkg_name);
|
||||
|
||||
bool UpdateModuleList();
|
||||
|
||||
bool hidden_api_bypass_enabled_ = false;
|
||||
private:
|
||||
inline static ConfigManager *instance_;
|
||||
uid_t last_user_ = false;
|
||||
inline static std::unique_ptr<ConfigManager> instance_ = nullptr;
|
||||
uid_t last_user_ = 0;
|
||||
bool use_prot_storage_ = true;
|
||||
std::string data_path_prefix_;
|
||||
std::string installer_pkg_name_;
|
||||
std::string base_config_path_;
|
||||
std::string blacklist_path_;
|
||||
std::string whitelist_path_;
|
||||
std::string use_whitelist_path_;
|
||||
std::filesystem::path data_path_prefix_;
|
||||
std::filesystem::path installer_pkg_name_;
|
||||
std::filesystem::path base_config_path_;
|
||||
std::filesystem::path blacklist_path_;
|
||||
std::filesystem::path whitelist_path_;
|
||||
std::filesystem::path use_whitelist_path_;
|
||||
bool black_white_list_enabled_ = false;
|
||||
bool dynamic_modules_enabled_ = false;
|
||||
bool deopt_boot_image_enabled_ = false;
|
||||
bool no_module_log_enabled_ = false;
|
||||
bool resources_hook_enabled_ = true;
|
||||
bool resources_hook_enabled_ = false;
|
||||
// snapshot at boot
|
||||
bool use_white_list_snapshot_ = false;
|
||||
std::vector<std::string> white_list_default_;
|
||||
std::vector<std::string> black_list_default_;
|
||||
std::unordered_set<std::string> white_list_default_;
|
||||
std::unordered_set<std::string> black_list_default_;
|
||||
bool hidden_api_bypass_enabled_ = false;
|
||||
|
||||
std::vector<std::pair<std::string, std::unordered_set<std::string>>> modules_list_;
|
||||
|
||||
std::vector<std::string> app_modules_list_;
|
||||
|
||||
std::filesystem::file_time_type last_write_time_;
|
||||
|
||||
ConfigManager();
|
||||
|
||||
|
|
@ -76,6 +96,11 @@ namespace edxp {
|
|||
void SnapshotBlackWhiteList();
|
||||
|
||||
std::string RetrieveInstallerPkgName() const;
|
||||
|
||||
static std::string GetPackageNameFromBaseApkPath(const std::filesystem::path &path);
|
||||
|
||||
friend std::unique_ptr<ConfigManager> std::make_unique<ConfigManager>();
|
||||
|
||||
};
|
||||
|
||||
} // namespace edxp
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue