package com.lody.virtual;
import android.hardware.Camera;
import android.os.Binder;
import android.os.Build;
import android.os.Process;
import com.lody.virtual.client.VClientImpl;
import com.lody.virtual.client.core.VirtualCore;
import com.lody.virtual.client.env.VirtualRuntime;
import com.lody.virtual.client.ipc.VActivityManager;
import com.lody.virtual.helper.proto.AppSetting;
import com.lody.virtual.helper.utils.VLog;
import com.lody.virtual.os.VUserHandle;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import dalvik.system.DexFile;
/**
* VirtualApp Native Project
*/
public class IOHook {
private static final String TAG = IOHook.class.getSimpleName();
private static Map<String, AppSetting> sDexOverrideMap;
private static Method gOpenDexFileNative;
private static Method gCameraNativeSetup;
private static int gCameraMethodType;
static {
try {
System.loadLibrary("iohook");
} catch (Throwable e) {
VLog.e(TAG, VLog.getStackTraceString(e));
}
}
static {
String methodName =
Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT ? "openDexFileNative" : "openDexFile";
for (Method method : DexFile.class.getDeclaredMethods()) {
if (method.getName().equals(methodName)) {
gOpenDexFileNative = method;
break;
}
}
if (gOpenDexFileNative == null) {
throw new RuntimeException("Unable to find method : " + methodName);
}
gOpenDexFileNative.setAccessible(true);
// TODO: Collect the methods of custom ROM.
try {
gCameraNativeSetup = Camera.class.getDeclaredMethod("native_setup", Object.class, int.class, String.class);
gCameraMethodType = 1;
} catch (NoSuchMethodException e) {
// ignore
}
if (gCameraNativeSetup == null) {
try {
gCameraNativeSetup = Camera.class.getDeclaredMethod("native_setup", Object.class, int.class, int.class, String.class);
gCameraMethodType = 2;
} catch (NoSuchMethodException e) {
}
}
if (gCameraNativeSetup == null) {
try {
gCameraNativeSetup = Camera.class.getDeclaredMethod("native_setup", Object.class, int.class);
gCameraMethodType = 3;
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
if (gCameraNativeSetup == null) {
Method[] methods = Camera.class.getDeclaredMethods();
for (Method method : methods) {
if ("native_setup".equals(method.getName())) {
gCameraNativeSetup = method;
VLog.w("native_setup", "native_setup:" + Arrays.toString(method.getParameterTypes()));
break;
}
}
}
if (gCameraNativeSetup != null) {
gCameraNativeSetup.setAccessible(true);
}
}
public static void startDexOverride() {
List<AppSetting> appSettings = VirtualCore.get().getAllApps();
sDexOverrideMap = new HashMap<>(appSettings.size());
for (AppSetting info : appSettings) {
try {
sDexOverrideMap.put(new File(info.apkPath).getCanonicalPath(), info);
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static String getRedirectedPath(String origPath) {
try {
return nativeGetRedirectedPath(origPath);
} catch (Throwable e) {
VLog.e(TAG, VLog.getStackTraceString(e));
}
return origPath;
}
public static String restoreRedirectedPath(String origPath) {
try {
return nativeRestoreRedirectedPath(origPath);
} catch (Throwable e) {
VLog.e(TAG, VLog.getStackTraceString(e));
}
return origPath;
}
public static void redirect(String origPath, String newPath) {
try {
nativeRedirect(origPath, newPath);
} catch (Throwable e) {
VLog.e(TAG, VLog.getStackTraceString(e));
}
}
public static void reversed(String origPath, String newPath) {
try {
nativeReversedRedirect(origPath, newPath);
} catch (Throwable e) {
VLog.e(TAG, VLog.getStackTraceString(e));
}
}
public static void hook() {
try {
nativeHook(Build.VERSION.SDK_INT);
} catch (Throwable e) {
VLog.e(TAG, VLog.getStackTraceString(e));
}
}
public static void hookNative() {
Method[] methods = {gOpenDexFileNative, gCameraNativeSetup};
try {
nativeHookNative(methods, VirtualCore.get().getHostPkg(), VirtualRuntime.isArt(), Build.VERSION.SDK_INT, gCameraMethodType);
} catch (Throwable e) {
VLog.e(TAG, VLog.getStackTraceString(e));
}
}
public static void onKillProcess(int pid, int signal) {
VLog.e(TAG, "killProcess: pid = %d, signal = %d.", pid, signal);
if (pid == android.os.Process.myPid()) {
VLog.e(TAG, VLog.getStackTraceString(new Throwable()));
}
}
public static int onGetCallingUid(int originUid) {
int callingPid = Binder.getCallingPid();
if (callingPid == Process.myPid()) {
return VClientImpl.get().getBaseVUid();
}
if (callingPid == VirtualCore.get().getSystemPid()) {
return Process.SYSTEM_UID;
}
int vuid = VActivityManager.get().getUidByPid(callingPid);
if (vuid != -1) {
return VUserHandle.getAppId(vuid);
}
VLog.d(TAG, "Unknown uid: " + callingPid);
return VClientImpl.get().getBaseVUid();
}
public static void onOpenDexFileNative(String[] params) {
String dexOrJarPath = params[0];
String outputPath = params[1];
VLog.d(TAG, "DexOrJarPath = %s, OutputPath = %s.", dexOrJarPath, outputPath);
try {
String canonical = new File(dexOrJarPath).getCanonicalPath();
AppSetting info = sDexOverrideMap.get(canonical);
if (info != null && !info.dependSystem) {
outputPath = info.getOdexFile().getPath();
params[1] = outputPath;
}
} catch (IOException e) {
e.printStackTrace();
}
}
private static native void nativeHookNative(Object method, String hostPackageName, boolean isArt, int apiLevel, int cameraMethodType);
private static native void nativeMark();
private static native String nativeRestoreRedirectedPath(String redirectedPath);
private static native String nativeGetRedirectedPath(String orgPath);
private static native void nativeRedirect(String orgPath, String newPath);
private static native void nativeReversedRedirect(String orgPath, String newPath);
private static native void nativeHook(int apiLevel);
public static int onGetUid(int uid) {
return VClientImpl.get().getBaseVUid();
}
}