virtualapp-启动VA中安装的app

1.新进程启动流程

com.hgy.ndk新安装在VA上的app启动为例:

LaunchpadAdapter.onBindViewHolder设置了点击VA已安装app监听事件,如下代码:

  public void onBindViewHolder(ViewHolder holder, int position) {
        ...
        holder.itemView.setOnClickListener(v -> {
            if (mAppClickListener != null) {
                mAppClickListener.onAppClick(position, data);
            }
        });
        ...
  }

—>在HomeActivity中设置了mAppClickListener,如下代码:

mLaunchpadAdapter.setAppClickListener((pos, data) -> {
            if (!data.isLoading()) {
                ...
                mPresenter.launchApp(data);
            }
        });

—>调用到HomePresenterImpl.launchApp函数,传入参数为AppData(实际为PackageAppData), AppData可能包含以下参数:

icon
name = "ndk"
packageName = "com.hgy.ndk"
isFirstOpen = true
isLoading = false
userid默认为0

—>这内部可能涉及到权限申请等延迟操作,但最后都会调用到VActivityManager.launchApp函数,它主要是构造一个intent, intent的主要内容差不多如下:

 Intent intentToResolve = new Intent(Intent.ACTION_MAIN);
 intentToResolve.setPackage("com.hgy.ndk");
 intentToResolve.addCategory(Intent.CATEGORY_LAUNCHER);
 final Intent intent = new Intent(intentToResolve);
 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
 intent.setClassName("com.hgy.ndk", "com.hgy.ndk.MainActivity"); // intent.setClassName(info.packageName, info.name);

很明显,是增加以下两个属性:

 <action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />

—>然后跨进程从VPMS中取得ActivityInfo数据,并和intent一起传到VAMSstartActivity中,代码如下:

public int startActivity(Intent intent, int userId) {
	// ...
	  ActivityInfo info = VirtualCore.get().resolveActivityInfo(intent, userId);
	  return startActivity(intent, info, null, null, null, 0, userId);
}
---> VAMS的startActivity:
public int startActivity(Intent intent, ActivityInfo info, ...) {
 	return mActivityStack.startActivityLocked(userId, intent, info, resultTo, options, resultWho, requestCode, VBinder.getCallingUid());  
}

上面的参数除了ActivityInfo数据和intent,其余全为null或0。
—>传入ActivityStack.startActivityLocked中还有个参数是callingUid,默认是9999, 代码如下:

    // eg: 第一次点击 callingUid = Constants.OUTSIDE_APP_UID 9999
    int startActivityLocked(int userId, Intent intent, ActivityInfo info, IBinder resultTo, Bundle options,
                            String resultWho, int requestCode, int callingUid) { // 173
        synchronized (mHistory) {
            // 把不存活的task从mHistory中干掉
            optimizeTasksLocked();
        }
		...
        if (reuseTask == null || reuseTask.isFinishing()) { // eg: 新的app启动task会走这个条件
            return startActivityInNewTaskLocked(mLauncherFlags, userId, intent, info, options, callingUid);
        }
        ...

其中ActivityStack.mHistory保存了正在运行的TaskRecord记录, 对应系统ActivityStackArrayList<TaskRecord> mTaskHistory = new ArrayList<>();
—> 上面函数有一堆逻辑判断,但都是查找最近有没有打开过这个app。
新的app启动可以忽略这个函数,直接进入ActivityStack.startActivityInNewTaskLocked,其中mLauncherFlags = 0, 代码如下:

  private int startActivityInNewTaskLocked(...) {
  ActivityRecord targetRecord = newActivityRecord(intent, info, null); // 1 
  final Intent destIntent = startActivityProcess(userId, targetRecord, intent, info, callingUid); // 2
   ...
 }
  1. 创建一个ActivityRecord targetRecordtargetRecord内部存储了前面提到的ActivityInfo数据和intent,并设置了targetRecord.component=ComponentInfo{com.hgy.ndk/com.hgy.ndk.MainActivity}
  2. 调用ActivityStack.startActivityProcess, 它主要调用两个函数, 代码如下:
private Intent startActivityProcess(int userId, ActivityRecord targetRecord, Intent intent, ActivityInfo info, int callingUid) {
// 3
 ProcessRecord targetApp = mService.startProcessIfNeedLocked(info.processName, userId, info.packageName, -1, callingUid);
 // 4
 return getStartStubActivityIntentInner(intent, targetApp.is64bit, targetApp.vpid, userId, targetRecord, info);
}
  1. 调用VAMS.startProcessIfNeedLocked

VAMS.startProcessIfNeedLocked根据包名首先取得 PackageSetting psApplicationInfo info数据, 这些数据在安装时就会被保存。
然后创建一个新的ProcessRecord app,ProcessRecord 记录了 ApplicationInfo infovuid等信息。并通过mProcessNamesmPidsSelfLocked记录app, 最后调用initProcess创建新进程, 关键代码如下:

   app = new ProcessRecord(info, processName, vuid, vpid, callingUid, is64bit);
   mProcessNames.put(app.processName, app.vuid, app);
   mPidsSelfLocked.add(app);
   if (initProcess(app)) {
       return app;

—>由VAMSinitProcess发起的启动新进程:p0(第一个启动是:p0,依次+1), 代码如下:

 private boolean initProcess(ProcessRecord app) {
 	...
    Bundle extras = new Bundle();
    extras.putParcelable("_VA_|_client_config_", app.getClientConfig());
    Bundle res = ProviderCall.callSafely(app.getProviderAuthority(), "_VA_|_init_process_", null, extras);

app.getProviderAuthority()对应AndroidManifest.xml:p0-:p99的100个provider
它会先进入VirtualCore.startup流程,做一些hook系统函数设置,这个在后续分析。
之后进入ShadowContentProvidercall初始化, 取得由ProcessRecord转换的clientConfig,并设置到VClient中, 同时把VClient 对象和pid绑定到Bundle返回到VAMS中, 代码如下:

	public Bundle call(String method, String arg, Bundle extras) {
		if ("_VA_|_init_process_".equals(method)) {
			return initProcess(extras);
		}
	}
	--->
    private Bundle initProcess(Bundle extras) { 
        VirtualCore.get().waitStartup(); // 等街startup完成
        extras.setClassLoader(ClientConfig.class.getClassLoader());
        ClientConfig clientConfig = extras.getParcelable("_VA_|_client_config_");// 取得由ProcessRecord转换的clientConfig
        VClient client = VClient.get();
        client.initProcess(clientConfig); // 将clientConfig设置到VClient中
        Bundle res = new Bundle();
        BundleCompat.putBinder(res, "_VA_|_client_", client.asBinder()); // 将"_VA_|_client_"和client绑定
        res.putInt("_VA_|_pid_", Process.myPid()); // 将"_VA_|_pid_"和pid绑定
        return res;
    }

---->返回到VAMS中的initProcess函数, 此函数后续把:po传过来的pid, IVClientIApplicationThread都赋值给ProcessRecord, 代码如下:

  app.pid = res.getInt("_VA_|_pid_"); // 得到pid
  IBinder clientBinder = BundleCompat.getBinder(res, "_VA_|_client_");// 得到IClient.IBinder对象
  return attachClient(app, clientBinder);
  
  --->attachClient实现如下:
   private boolean attachClient(final ProcessRecord app, final IBinder clientBInder) { // 734
        IVClient client = IVClient.Stub.asInterface(clientBInder);
      	...
        app.client = client;
        // 对应IApplicationThread
        app.appThread = ApplicationThreadCompat.asInterface(client.getAppThread());
       ...
        return true;
    }

其中client.getAppThread()是跨进程在po中调用。

  1. 调用ActivityThread.getStartStubActivityIntentInner

---->返回后调用getStartStubActivityIntentInner, 设置targetIntent, targetIntent设置的代码如下:

Intent targetIntent = new Intent();
targetIntent.setClassName("io.busniess.va", "com.lody.virtual.client.stub.ShadowActivity$P0")
targetIntent.setType("com.hgy.ndk/com.hgy.ndk.MainActivity")
StubActivityRecord saveInstance = new StubActivityRecord(intent, info, userId, targetRecord);
saveInstance.saveToIntent(targetIntent); // 把saveInstance中数据保存到targetIntent

可以看出targetIntent 记录了 前面提到的intentActivityInfo infouserIdtargetRecord所有数据。

继续回到上层函数startActivityInNewTaskLocked, 它会继续对返回的targetIntent 增加flags,主要内容差不多如下:

  private int startActivityInNewTaskLocked(...) {
        ActivityRecord targetRecord = newActivityRecord(intent, info, null);
        // destIntent即前面提到的targetIntent
        final Intent destIntent = startActivityProcess(userId, targetRecord, intent, info, callingUid);
        destIntent.addFlags(0);// launcherFlags = 0
        destIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        destIntent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
        destIntent.addFlags(Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED)
        destIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
        destIntent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
        VirtualCore.get().getContext().startActivity(destIntent);
   }

最后进入系统的startActivity,因为targetIntent.setClassName("io.busniess.va", "com.lody.virtual.client.stub.ShadowActivity$P0"),所以它会在po进程中启动startActivity

但是这里有个问题,因为设置的是 "com.lody.virtual.client.stub.ShadowActivity$P0",那理论上启动的是ShadowActivity,而不是我们原始的com.hgy.ndk.MainActivity了,这里可以提前解释下,在后面的HCallbackStubLAUNCH_ACTIVITY中有如下代码:

   ActivityThread.ActivityClientRecord.intent.set(r, intent);
   ActivityThread.ActivityClientRecord.activityInfo.set(r, info);

其中intent是原始的,这里可以贴下改变前面的值:
在这里插入图片描述
调用 ActivityThread.ActivityClientRecord.intent.set(r, intent)改变后:
在这里插入图片描述
也就是在启动activity前,又换回原始的intentinfo数据,从而正常启动。

在lulubox4.3的插件化中有类似的实现: 位于plugin-base-cmplugin-host-libraryAndroidHack.java

这时VA中设置的hook函数开始准备拦截了。

2.hook 函数处理

hook函数框架可以参考InvocationStubManager相关

2.0 预先处理的hook函数

startActivity后到HCallbackStubLAUNCH_ACTIVITY之前要处理几个函数的hook:

2.0.1 addPackageDependency

mInitialApplication = LoadedApk.makeApplication中会调用getClassLoadergetClassLoader内部会调用addPackageDependency,如下图:
在这里插入图片描述
将参数args从"com.hgy.ndk"换成“io.busniess.va”再传入调用:

        public Object call(Object who, Method method, Object... args) throws Throwable {
            MethodParameterUtils.replaceFirstAppPkg(args); // args由原始的包名转换成io.busniess.va
            return method.invoke(who, args);
        }
2.0.2 startService

如果是va自身的service就直接使用系统的,否则和VAMSstartActivity类似,使用VAMS中的startService,先自己起一个子进程p0(如果p0被占用就依此类推),再 intent.setClassName(com.lody.virtual.client.stub.ShadowService$P0, serviceName);绑定到子进程。

2.0.3 getContentProvider

根据名字判断如果是va自身的contentprovider就直接使用系统的,否则和前面的一样,使用VAMS中的initProcess,先自己起一个子进程p0(如果p0被占用就依此类推), 然后把getContentProvider的第二个参数name设置为io.busniess.vatools.virtual_stub_${vpid}

2.0.4 getRunningAppProcesses

首先取得运行进程list, 遍历, 如果是va的app进程,则更新RunningAppProcessInfo中的数据为va变化的数据:

 if (processName != null) {
                           info.importanceReasonCode = 0;
                           info.importanceReasonPid = 0;
                           info.importanceReasonComponent = null;
                           info.processName = processName;
                       }
                       info.pkgList = pkgList.toArray(new String[0]);
                       info.uid = vuid;

2.1 client进程的HCallbackStub

ActivityThread 有个很重要的变量,final H mH = new H();, 它的实现如下:

  private class H extends Handler {
  public void handleMessage(Message msg) {
            if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
            switch (msg.what) {
                case LAUNCH_ACTIVITY: {
                ...
                }
  }

简单的说就是处理各种消息, HCallbackStub就是设置了ActivityThread.Hcallback函数,实现handleMessage拦截。

2.1.1 LAUNCH_ACTIVITY消息

首先到达的LAUNCH_ACTIVITY消息,其中 Msg.object就是ActivityClientRecord r对象

  Intent stubIntent;
 if (BuildCompat.isPie()) {
     stubIntent = LaunchActivityItem.mIntent.get(r);
} else {
     stubIntent = ActivityThread.ActivityClientRecord.intent.get(r);
  }

这里得到的stubIntent就是我们前面VAMSstartActivity传出的destIntent, 如下所示:
在这里插入图片描述
所以就可以在这里把VAMS中绑定的StubActivityRecordintent(原始的)ActivityInfo info都取出来,前面流程提到过如何绑定, 代码如下:

 StubActivityRecord saveInstance = new StubActivityRecord(stubIntent);

// intent是原始的,没有调用setClassName改变的
Intent intent = saveInstance.intent;
 IBinder token;
 if (BuildCompat.isPie()) { //android 9.0
    token = ClientTransaction.mActivityToken.get(msg.obj);
 } else {
     token = ActivityThread.ActivityClientRecord.token.get(r);
 }
ActivityInfo info = saveInstance.info;

这时还会判断:p0进程是否已初始化,如果没有就尝试重启p0进程,代码如下:

if (VClient.get().getClientConfig() == null) { // 理论上不会成立,除非px进程挂了,因为clientConfig会在call时赋值给VClient
     InstalledAppInfo installedAppInfo = VirtualCore.get().getInstalledAppInfo(info.packageName, 0);
     // 让VAMS再次initProcess,重启:px进程
     VActivityManager.get().processRestarted(info.packageName, info.processName, saveInstance.userId);
     getH().sendMessageAtFrontOfQueue(Message.obtain(msg));// 重发消息
     return false;
}

继续就会进行到比较重要的一步,调用 VClient.get().bindApplication,然后重新触发LAUNCH_ACTIVITY消息。之后调用

2.1.1.1 bindApplication初始化

VClient.get().bindApplication总是在主UI线程中调用bindApplicationNoCheckbindApplicationNoCheck主要完成了以下任务:

  1. setupUncaughtHandler 设置异常捕获,由外部VCore设置CrashHandler,内部默认没有设置。
  2. fixInstalledProviders会检查我们的provider,如果authority不是io.busniess.va.virtual_stub_开头,则利用ProviderHook生成provider代理取代原始的provider。理论上都是以io.busniess.va.virtual_stub_开头。
  3. VDeviceManager.get().applyBuildProp设置虚拟的设备信息。
  4. VirtualCore.mainThreadmInitialApplication变量设置为null。
  5. 设置InstalledAppInfo,ApplicationInfo, List<ProviderInfo>等基础数据到VClient中。
  6. 通过VirtualRuntime.setupRuntime改变进程的argv[0] 参数为进程名(com.hgy.ndk), 设置app名字为进程名(com.hgy.ndk)。
    原始的app名字为io.busniess.va:p0
  7. 设置AlarmManager.mTargetSdkVersion 为被安装apk的targetSdkVersion,原始值为va的targetSdkVersion
  8. 设置系统缓存的临时目录为/data/data/io.busniess.vatools/virtual/data/user/userId/packageName/cache, userId为0,1,…。
  9. 调用startIORelocaterstartIORelocater的作用就是可以在java层设置路径白名单,黑名单,要替换的名单,然后把这些路径列表传到NDK层,并hook所有的带路径的libc函数以及linker下的dlopen函数,并在hook函数中对路径做相应放过,禁止,替换。
  10. 调用launchEngine替换java native函数对应的NDK jni函数
  11. 设置codeCacheDir/data/data/io.busniess.va/virtual/data/user/0/com.hgy.ndk/code_cache
  12. AppBindData mBoundApplication传入ActivityThread.mBoundApplication对象,即 设置appInfoprocessNameinstrumentationNameinfo等到对象, 其中info由临时创建的context获取。
  13. 设置LoadedApk.mSecurityViolationfalse
  14. 调用VMRuntime.setTargetSdkVersion设置 targetSdkVersion
  15. 调用AppCallback.beforeStartApplication对外接口,可以看出传出的context为前面临时创建的。
  16. 利用LoadedApk.makeApplication创建mInitialApplication,并设置到ActivityThread.mInitialApplication中。
  17. 利用ContextFixerContextImpl.mBasePackageNameContextImplKitkat.mOpPackageNameContentResolverJBMR2.mPackageName的包名(原始为com.hgy.ndk)改为io.busniess.va
  18. 调用AppCallback.beforeApplicationCreate对外接口。
  19. 调用 mInstrumentation.callApplicationOnCreate会首先把ActivityThread.mInstrumentation替换成VClient的mInstrumentation
  20. 调用AppCallback.afterApplicationCreate对外接口。
2.1.1.2 startIORelocater重定位

仍以com.hgy.ndk为例,逻辑:

在deviceConfig.enable时触发
 NativeEngine.redirectFile("/sys/class/net/wlan0/address",“/data/data/io.busniess.va/virtual/data/user/0/system/wifiMacAddress”);
 NativeEngine.redirectFile("/sys/class/net/eth0/address",“/data/data/io.busniess.va/virtual/data/user/0/system/wifiMacAddress”);
 NativeEngine.redirectFile("/sys/class/net/wifi/address",“/data/data/io.busniess.va/virtual/data/user/0/system/wifiMacAddress”);
 NativeEngine.redirectFile("/proc/stat", "/data/data/io.busniess.vatools/virtual/proc/stat");
 NativeEngine.forbid("/proc/" + info.pid + "/maps", true);
 NativeEngine.forbid("/proc/" + info.pid + "/cmdline", true);
 NativeEngine.redirectDirectory("/tmp/", "/data/data/io.busniess.va/virtual/data/user/0/com.hgy.ndk/cache/");
 NativeEngine.redirectDirectory("/data/data/com.hgy.ndk","/data/data/io.busniess.va/virtual/data/user/0/com.hgy.ndk/");
 NativeEngine.redirectDirectory("/data/user/0/com.hgy.ndk","/data/data/io.busniess.va/virtual/data/user/0/com.hgy.ndk/");
 // user_de在android7.0以后
NativeEngine.redirectDirectory("/data/user_de/0/com.hgy.ndk","/data/data/io.busniess.va/virtual/data/user_de/0/com.hgy.ndk/");
if (appLibConfig == SettingConfig.AppLibConfig.UseOwnLib) {// 默认不成立
NativeEngine.redirectDirectory("/data/data/com.hgy.ndk/lib/","/data/data/io.busniess.va/virtual/data/app/com.hgy.ndk/lib/");
NativeEngine.redirectDirectory("/data/user/0/com.hgy.ndk/lib/","/data/data/io.busniess.va/virtual/data/app/com.hgy.ndk/lib/");
 }
NativeEngine.redirectDirectory("/data/data/io.busniess.va/virtual/data/user/0/com.hgy.ndk/lib","/data/data/io.busniess.va/virtual/data/app/com.hgy.ndk/lib/");

 NativeEngine.whitelist("/data/data/io.busniess.va/virtual/data/app/com.hgy.ndk/lib");
 NativeEngine.whitelist("/data/user/0/com.hgy.ndk/lib/");

最后调用NativeEngine.enableIORedirect()

关键函数是NativeEngine.redirectFileNativeEngine.redirectDirectoryNativeEngine.forbidNativeEngine.whitelistNativeEngine.enableIORedirect
四者 用于区分传入的是文件和文件夹在于路径最后有没有/

NativeEngine.forbid最终调到NDK的jni_nativeIOForbidjni_nativeIOForbid会把传入的路径统一加入到forbidden_items数组中,forbidden_items数组由PathItem构成, 代码如下:

typedef struct PathItem {
    char *path;
    bool is_folder;
    size_t size;
} PathItem;

--->
    PathItem &item = forbidden_items[forbidden_item_count];
    item.path = strdup(path);
    item.size = strlen(path);
    item.is_folder = (path[strlen(path) - 1] == '/'); // 根据最后有没有斜杠判断是否为文件夹

NativeEngine.whitelist最终调到NDK的jni_nativeIOWhitelistjni_nativeIOWhitelist把传入的路径统一加入到keep_items数组中,forbidden_items数组同样由PathItem构成,和上面的加入类似。

NativeEngine.redirectFileNativeEngine.redirectDirectory都是把原始文件(夹)和要重定位的文件(夹)路径放在REDIRECT_LISTS列表中。

最后由NativeEngine.enableIORedirect来触发:

NativeEngine.enableIORedirect首先会把REDIRECT_LISTS排序(第一个字符串从长到短), 然后遍历REDIRECT_LISTS,并调用NativeEngine.nativeIORedirect,再调到NDK的jni_nativeIORedirect把传入的原始路径和要替换的路径统一加入到replace_items数组中, 代码如下:

typedef struct ReplaceItem {
    char *orig_path;
    size_t orig_size;
    char *new_path;
    size_t new_size;
    bool is_folder;
} ReplaceItem;
--->
  ReplaceItem &item = replace_items[replace_item_count];
    item.orig_path = strdup(orig_path);
    item.orig_size = strlen(orig_path);
    item.new_path = strdup(new_path);
    item.new_size = strlen(new_path);
    item.is_folder = (orig_path[strlen(orig_path) - 1] == '/');

最后调用NDK的jni_nativeEnableIORedirect, 传入参数基本如下:

soPath = /data/app/io.busniess.va-2/lib/x86/libv++.so
soPath64 = /data/app/io.busniess.va-2/lib/x86/libv++_64.so
nativePath = /data/data/io.busniess.va/virtual/.native/data/data/io.busniess64.va/virtual/.native

jni_nativeEnableIORedirect在内部调用IOUniformer::startUniformer,先把前面传入参数使用setenv保存,再调用startIOHookhook所有的包含路径的c库函数,这些函数在调用的时候,可能会替换路径为新路径。由于hook的是libc的函数,java层和虚拟机的文件访问最终也会调用到这里,从而受到影响, 代码如下:

void startIOHook(int api_level) {
    void *handle = dlopen("libc.so", RTLD_NOW);
    if (handle) {
...
        HOOK_SYMBOL(handle, faccessat);
        HOOK_SYMBOL(handle, __openat);
        HOOK_SYMBOL(handle, fchmodat);
		...
#ifdef __arm__
        if (!relocate_linker()) {
            findSyscalls("/system/bin/linker", on_found_linker_syscall_arm);
        }
#endif
#endif
        dlclose(handle);
    }
}

HOOK_SYMBOL内部调用了MSHookFunction,也就是著名的Cydia的hook库, 代码如下:

#define HOOK_SYMBOL(handle, func) hook_function(handle, #func, (void*) new_##func, (void**) &orig_##func)
--->
void hook_function(void *handle, const char *symbol, void *new_func, void **old_func) {
    void *addr = dlsym(handle, symbol);
    MSHookFunction(addr, new_func, old_func);
}

faccessat为例,代码如下:

HOOK_DEF(int, faccessat, int dirfd, const char *pathname, int mode, int flags) {
    char temp[PATH_MAX];
    const char *relocated_path = relocate_path(pathname, temp, sizeof(temp));
    if (relocated_path && !(mode & W_OK && isReadOnly(relocated_path))) {
        return syscall(__NR_faccessat, dirfd, relocated_path, mode, flags);
    }
    errno = EACCES;
    return -1;
}

内部relocate_path是关键的替换路径函数,代码如下:

const char *relocate_path(const char *path, char *const buffer, const size_t size) {
    const char *result = relocate_path_internal(path, buffer, size);
    return result;
}
--->relocate_path_internal代码:

relocate_path_internal逻辑如下:

  1. 使用canonicalize_path把路径转成规范的路径,比如传入的路径可能有//这种重复斜杠或./
  2. keep_items(白名单,对应上层的 NativeEngine.whitelist)中遍历,看是否符合,如符合,直接返回原始路径。
  3. forbidden_items(黑名单,对应上层的 NativeEngine.forbid)中遍历,看是否符合,如符合,直接返回NULL
  4. replace_items(替换名单, 对应上层的NativeEngine.redirectFileNativeEngine.redirectDirectory)中遍历,看是否符合,如符合,这里会分几种情况,不过最后总是返回被替换的路径。一般会把被替换的路径保存到前面传入的temp中。

比较函数代码如下:

bool match_path(bool is_folder, size_t size, const char *item_path, const char *path, size_t path_len) {
    if (is_folder) {
        if (path_len < size) {
            // ignore the last '/'
            return strncmp(item_path, path, size - 1) == 0;
        } else {
            return strncmp(item_path, path, size) == 0;
        }
    } else {
        return strcmp(item_path, path) == 0;
    }
}

relocate_linker的作用是:从/proc/self/maps中取得/system/bin/linker的基地址,再遍历/system/bin/linker文件的symbol查找dlopen的文件偏移,再加上基地址做hook, 不同版本的linkerdlopen对应的symbol不同,这个可以通过IDA来查看确认。

startIORelocater的作用是可以在java层设置路径白名单,黑名单,要替换的名单,然后把这些路径列表传到NDK层,并hook所有的带路径的libc函数以及linker下的dlopen函数,并在hook中对路径做相应放过,禁止,替换。

2.1.1.3 launchEngine

NativeEngine.launchEngine会调用jni_nativeLaunchEngine, 传入参数如下:

1> method:java层的native函数列表:

  1. gOpenDexFileNative: DexFile.javaopenDexFileNative函数地址
  2. gCameraNativeSetup: Camera.javanative_setup函数地址
  3. gAudioRecordNativeCheckPermission: AudioRecord.javanative_check_permission函数地址
  4. gMediaRecorderNativeSetup: MediaRecorder.javanative_setup函数地址
  5. gAudioRecordNativeSetup: AudioRecord.javanative_setup函数地址

2> VirtualCore.get().getHostPkg:va的包名io.busniess.va
3> isArt:是否为art
4> Build.VERSION.SDK_INT: 设备版本号
5> gCameraMethodType: gCameraNativeSetup函数参数列表中String参数的位置 + 0x10
6> gAudioRecordMethodType: 区分gAudioRecordNativeSetup的版本,9个参数为1,10个参数为2

jni_nativeLaunchEngine内部做了一套hook native 函数的封装, 逻辑如下:

  1. 自定义一个空的hook函数mark, 并RegisterNatives绑定NativeEngine.nativeMarkmark。然后遍历计算NativeEngine.nativeMarkmark的偏移,参考函数measureNativeOffset
void measureNativeOffset(JNIEnv *env, bool isArt) {

    jmethodID markMethod = env->GetStaticMethodID(nativeEngineClass, "nativeMark", "()V");

    size_t start = (size_t) markMethod;
    size_t target = (size_t) mark;

    int offset = 0;
    bool found = false;
    while (true) {
        if (*((size_t *) (start + offset)) == target) {
            found = true;
            break;
        }
        offset += 4;
        if (offset >= 100) {
            ALOGE("Error: Cannot find the jni function offset.");
            break;
        }
    }
    if (found) {
        patchEnv.native_offset = offset;
        }
}
  1. 以hook getCallingUid为例,首先取得原始的getCallingUid地址
public static final native int getCallingUid()

--->得到native函数getCallingUid的地址
jclass binderClass = env->FindClass("android/os/Binder");
jmethodID getCallingUid = env->GetStaticMethodID(binderClass, "getCallingUid", "()I");

然后调用hookJNIMethod做hook, vmGetJNIFunction通过相同的偏移量得到ndk层中原始jni函数地址vmUseJNIFunction替换为新的jni函数地址。

 hookJNIMethod(getCallingUid,
                      (void *) new_getCallingUid,
                      (void **) &patchEnv.orig_getCallingUid
 ---> 
void hookJNIMethod(jmethodID method, void *new_jni_func, void **orig_jni_func) {
    *orig_jni_func = vmGetJNIFunction(method);
    vmUseJNIFunction(method, new_jni_func);
}

---> vmGetJNIFunction得到原始的jni函数地址
void *vmGetJNIFunction(jmethodID method) {
    void **funPtr = (void **) (reinterpret_cast<size_t>(method) + patchEnv.native_offset);
    return *funPtr;
}
--->vmUseJNIFunction替换为新的jni函数地址
void vmUseJNIFunction(jmethodID method, void *jniFunction) {
    void **funPtr = (void **) (reinterpret_cast<size_t>(method) + patchEnv.native_offset);
    *funPtr = jniFunction;
}

jni_nativeLaunchEngine主要完成了以下任务:

  1. hook Binder.javagetCallingUid,调用NativeEngine. onGetCallingUid返回自定义的Uid
  2. hook DexFile.javaopenDexFileNative, 调用NativeEngine. onOpenDexFileNative
  3. hook Camera.javanative_setup, 替换它的String参数为传入的包名
  4. hook MediaRecorder.javanative_setup, 替换它的String参数为传入的包名
  5. hook AudioRecord.javanative_setup, 替换它的String参数为传入的包名
  6. hook Runtime.java中的nativeLoad, 替换它的文件路径

launchEngine的作用是替换java native函数对应的ndk层jni函数,具体如上所述。

2.2.x进程中hook 函数处理

2.2.1. IActivityManager.overridePendingTransition

VClient.bindApplication调用之前,默认返回0,之后判断va中是否使用系统已安装的apk进行安装(默认是),如果是,则放过

        public Object call(Object who, Method method, Object... args) throws Throwable {
            if (!VClient.get().isAppUseOutsideAPK()) {
                return 0;
            }
            return super.call(who, method, args);
        }
2.2.2. IActivityManager.getRecentTasks

判断取得的 List<ActivityManager.RecentTaskInfo>是否有我们自定义的TaskInfo,如果有,就强制修改List,再返回, 修改如下:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
       info.topActivity = taskInfo.topActivity;
        info.baseActivity = taskInfo.baseActivity;    
  }

    info.origActivity = taskInfo.baseActivity;
     info.baseIntent = taskInfo.baseIntent;
            
2.2.3. IActivityManager.getContentProvider

这个函数首次触发是在前面提到的initProcessProviderCall.callSafely创建新进程:p0, 这时默认是直接返回的:

        public Object call(Object who, Method method, Object... args) throws Throwable {
            int nameIdx = getProviderNameIndex(); // nameIndex = 1
            String name = (String) args[nameIdx]; // name = io.busniess.va.virtual_stub_0
            if ((name.startsWith(StubManifest.STUB_CP_AUTHORITY)
                    || name.startsWith(StubManifest.STUB_CP_AUTHORITY_64BIT)
                    || name.equals(getConfig().get64bitHelperAuthority()))
                    || name.equals(getConfig().getBinderProviderAuthority())) {
                return method.invoke(who, args);
                ...

// todo,待续.

之后进入了新进程:p0 的相关hook

2.3.Os.getuid

取得原始系统分配的uid,初始使用系统分配的uid,在初始化完成后,使用ProcessRecord中的vuid, 这个也可以从后面打印的日志看出。

        public Object afterCall(Object who, Method method, Object[] args, Object result) throws Throwable {
            int uid = (int) result;
            return NativeEngine.onGetUid(uid);
        }
   -->
    public static int onGetUid(int uid) {
        if (!VClient.get().isAppRunning()) {// 在点击启动va中的apk开始时,isAppRunning=false
            return uid;
        }
        return VClient.get().getBaseVUid();// 取得ProcessRecord中的vuid
    }

2.1. IPackageManager.getApplicationInfo

默认返回我们VPMS中创建的ApplicationInfo, 如果我们没有安装过,但是属于以下三类:

  1. va自身
  2. 系统app
  3. 使用外部包的app, 其中3的解释如下
    比如内部微信调用QQ分享,但是内部没有QQ,如果没用addVisibleOutsidePackage,那么提示没有安装QQ,如果用了addVisibleOutsidePackage,则属于外部包,启动外部的QQ
    这三类都会通过ComponentFixer.fixOutsideApplicationInfoApplicationInfo.uid设置为9999。
        public Object call(Object who, Method method, Object... args) throws Throwable {
            String pkg = (String) args[0]; // eg: com.hgy.ndk
            int flags = (int) args[0]; // 1024
            if (getHostPkg().equals(pkg)) { // getHostPkg = io.busniess.va
                return method.invoke(who, args);
            }
            int userId = VUserHandle.myUserId(); // eg: userId = 0
            ApplicationInfo info = VPackageManager.get().getApplicationInfo(pkg, flags, userId);
            if (info != null) {
                return info; // 默认返回我们VPMS中创建的ApplicationInfo
            }
            info = (ApplicationInfo)method.invoke(who, args);
            // isVisiblePackage表明了三类可见app,1.va自身 2.系统app 3.使用外部包的app, 其中3的解释如下
            // 外部安装了应用,但是内部没有安装(双开),内部应用在调用外面的应用,需要先addVisibleOutsidePackage,否则会相当于没有安装
            if (info == null || !isVisiblePackage(info)) {
                return null;
            }
            ComponentFixer.fixOutsideApplicationInfo(info);
            return info;
        }

3. 打印hook的函数

我们可以把HookInvocationHandler.invoke稍改造下,让它所有的hook函数都打印出日志:

        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            MethodProxy methodProxy = getMethodProxy(method.getName());
            if (methodProxy != null) {
                methodProxy.setInvocationloggingCondition(LogInvocation.Condition.ALWAYS);
            }

VirtualCore.startup中加入等待断点:

public void startup(Context context, SettingConfig config) throws Throwable {
...
		 detectProcessType();
         if (isVAppProcess()) {
             VLog.e(TAG, "startup0");
             android.os.Debug.waitForDebugger();
             VLog.e(TAG, "startup1");
         }
}

点击VA中安装的APK启动,附加断点,把Logcat切到当前的io.busniess.va:px日志,观察它的整个hook函数列表:
在附加之前,我们的:x服务进程,它的日志如下:

08-26 02:24:13.594 3006-3006/io.busniess.va:x I/MethodInvocationStub: IActivityManager.getActivityDisplayId(android.os.BinderProxy@640bff7) => 0
08-26 02:24:13.603 3006-3006/io.busniess.va:x I/MethodInvocationStub: IActivityManager.setTaskDescription(android.os.BinderProxy@640bff7, TaskDescription Label: null Icon: null colorPrimary: -1644826) => void
08-26 02:24:13.605 3006-3006/io.busniess.va:x I/MethodInvocationStub: IActivityManager.overridePendingTransition(android.os.BinderProxy@640bff7, io.busniess.va, 0, 0) => void
08-26 02:24:13.616 3006-3006/io.busniess.va:x I/MethodInvocationStub: IActivityManager.getActivityOptions(android.os.BinderProxy@640bff7) => null
08-26 02:24:13.620 3006-3006/io.busniess.va:x I/MethodInvocationStub: IActivityManager.getActivityOptions(android.os.BinderProxy@640bff7) => null
08-26 02:24:13.622 3006-3006/io.busniess.va:x I/MethodInvocationStub: IActivityManager.checkPermission(android.permission.INTERACT_ACROSS_USERS, 3006, 10058) => -1
08-26 02:24:13.623 3006-3006/io.busniess.va:x I/MethodInvocationStub: IActivityManager.checkPermission(android.permission.INTERACT_ACROSS_USERS_FULL, 3006, 10058) => -1
08-26 02:24:13.646 3006-3006/io.busniess.va:x I/MethodInvocationStub: IActivityManager.activityResumed(android.os.BinderProxy@640bff7) => void
08-26 02:24:13.825 3006-3006/io.busniess.va:x I/MethodInvocationStub: IActivityManager.activityIdle(android.os.BinderProxy@640bff7, {1.0 310mcc260mnc en_US ldltr sw392dp w392dp h713dp 440dpi nrml long port finger -keyb/v/h dpad/v s.6}, false) => void
08-26 02:24:13.992 3006-3030/io.busniess.va:x I/MethodInvocationStub: IActivityManager.getRecentTasks(2147483647, 3, 0) => [android.app.ActivityManager$RecentTaskInfo@4d02da, android.app.ActivityManager$RecentTaskInfo@644d30b, android.app.ActivityManager$RecentTaskInfo@91d65e8]

附加后,它的新增日志为:

08-26 02:25:22.163 3006-3030/io.busniess.va:x I/MethodInvocationStub: IActivityManager.getContentProvider(android.app.ActivityThread$ApplicationThread@b14e927, io.busniess.va.virtual_stub_0, 0, false) => android.app.IActivityManager$ContentProviderHolder@14401
08-26 02:25:23.659 3006-3006/io.busniess.va:x I/MethodInvocationStub: IActivityManager.removeContentProvider(android.os.BinderProxy@1eb94e7, false) => void
08-26 02:25:23.714 3006-3030/io.busniess.va:x I/MethodInvocationStub: IActivityManager.startActivity(android.app.ActivityThread$ApplicationThread@b14e927, io.busniess.va, Intent { typ=com.hgy.ndk/com.hgy.ndk.MainActivity flg=0x18290000 cmp=io.busniess.va/com.lody.virtual.client.stub.ShadowActivity$P0 (has extras) }, com.hgy.ndk/com.hgy.ndk.MainActivity, null, null, -1, 0, null, null) => 0
08-26 02:25:23.726 3006-3006/io.busniess.va:x I/MethodInvocationStub: IActivityManager.activityPaused(android.os.BinderProxy@640bff7) => void
08-26 02:25:23.728 3006-3018/io.busniess.va:x I/MethodInvocationStub: IActivityManager.getRecentTasks(2147483647, 3, 0) => [android.app.ActivityManager$RecentTaskInfo@d3d1f3d, android.app.ActivityManager$RecentTaskInfo@8b91332, android.app.ActivityManager$RecentTaskInfo@5529883, android.app.ActivityManager$RecentTaskInfo@6f50a00]
08-26 02:25:23.743 3006-3018/io.busniess.va:x I/MethodInvocationStub: IActivityManager.startActivity(android.app.ActivityThread$ApplicationThread@b14e927, io.busniess.va, Intent { typ=com.hgy.ndk/com.hgy.ndk.MainActivity flg=0x18290000 cmp=io.busniess.va/com.lody.virtual.client.stub.ShadowActivity$P0 (has extras) }, com.hgy.ndk/com.hgy.ndk.MainActivity, null, null, -1, 0, null, null) => 0
08-26 02:25:23.779 3006-3031/io.busniess.va:x I/MethodInvocationStub: IActivityManager.getRecentTasks(2147483647, 3, 0) => [android.app.ActivityManager$RecentTaskInfo@b617239, android.app.ActivityManager$RecentTaskInfo@a61307e, android.app.ActivityManager$RecentTaskInfo@79c39df, android.app.ActivityManager$RecentTaskInfo@8a0c62c]
08-26 02:25:23.780 3006-3031/io.busniess.va:x I/MethodInvocationStub: IActivityManager.broadcastIntent(android.app.ActivityThread$ApplicationThread@b14e927, Intent { act=virtual.intent.action.APP_LAUNCHED (has extras) }, null, null, -1, null, null, null, -1, null, false, false, 0) => 0
08-26 02:25:23.895 3006-3018/io.busniess.va:x I/MethodInvocationStub: IActivityManager.getRecentTasks(2147483647, 3, 0) => [android.app.ActivityManager$RecentTaskInfo@addf8f5, android.app.ActivityManager$RecentTaskInfo@4dbd68a, android.app.ActivityManager$RecentTaskInfo@5b494fb, android.app.ActivityManager$RecentTaskInfo@86a1918]
08-26 02:25:24.031 3006-3030/io.busniess.va:x I/MethodInvocationStub: IActivityManager.getRecentTasks(2147483647, 3, 0) => [android.app.ActivityManager$RecentTaskInfo@9622f71, android.app.ActivityManager$RecentTaskInfo@ba8d156, android.app.ActivityManager$RecentTaskInfo@52d85d7, android.app.ActivityManager$RecentTaskInfo@e73aec4, android.app.ActivityManager$RecentTaskInfo@61951ad]
08-26 02:25:24.033 3006-3030/io.busniess.va:x I/MethodInvocationStub: IActivityManager.broadcastIntent(android.app.ActivityThread$ApplicationThread@b14e927, Intent { act=virtual.intent.action.APP_LAUNCHED (has extras) }, null, null, -1, null, null, null, -1, null, false, false, 0) => 0
08-26 02:25:24.079 3006-3018/io.busniess.va:x I/MethodInvocationStub: IActivityManager.getRecentTasks(2147483647, 3, 0) => [android.app.ActivityManager$RecentTaskInfo@878ace2, android.app.ActivityManager$RecentTaskInfo@8daa873, android.app.ActivityManager$RecentTaskInfo@5ef330, android.app.ActivityManager$RecentTaskInfo@da65ba9, android.app.ActivityManager$RecentTaskInfo@d78b52e]
08-26 02:25:24.316 3006-3006/io.busniess.va:x I/MethodInvocationStub: IActivityManager.finishActivity(android.os.BinderProxy@640bff7, 0, null, false) => true
08-26 02:25:24.328 3006-3006/io.busniess.va:x I/MethodInvocationStub: IActivityManager.activityStopped(android.os.BinderProxy@640bff7, Bundle[{android:viewHierarchyState=Bundle[{android:views={16908290=android.view.AbsSavedState$1@32d58cf}}]}], null, null) => void

对于被启动的进程com.hgy.ndk(io.busniess.va:p0),它的日志如下:

08-26 02:25:22.150 3032-3032/io.busniess.va:p0 I/MethodInvocationStub: Os.getuid(ul) => 10058
08-26 02:25:22.161 3032-3032/io.busniess.va:p0 I/MethodInvocationStub: Os.getuid(ul) => 10058
08-26 02:25:22.163 3032-3032/io.busniess.va:p0 I/MethodInvocationStub: IActivityManager.publishContentProviders(android.app.ActivityThread$ApplicationThread@6fbd30e, [android.app.IActivityManager$ContentProviderHolder@e5b512f]) => void
08-26 02:25:22.165 3032-3032/io.busniess.va:p0 I/MethodInvocationStub: IActivityManager.removeContentProvider(android.os.BinderProxy@2ec0c41, false) => void
08-26 02:25:23.746 3032-3032/com.hgy.ndk I/MethodInvocationStub: IActivityManager.getRunningAppProcesses(ul) => [android.app.ActivityManager$RunningAppProcessInfo@446f18, android.app.ActivityManager$RunningAppProcessInfo@b8ccd71, android.app.ActivityManager$RunningAppProcessInfo@7f21756]
08-26 02:25:23.755 3032-3032/com.hgy.ndk I/MethodInvocationStub: Os.getuid(ul) => 10001
08-26 02:25:23.757 3032-3032/com.hgy.ndk I/MethodInvocationStub: IPackageManager.getApplicationInfo(com.hgy.ndk, 1024, 0) => ApplicationInfo{d0c4bde com.hgy.ndk}
08-26 02:25:23.757 3032-3032/com.hgy.ndk I/MethodInvocationStub: Os.getuid(ul) => 10001
08-26 02:25:23.757 3032-3032/com.hgy.ndk I/MethodInvocationStub: Os.getuid(ul) => 10001
08-26 02:25:23.757 3032-3032/com.hgy.ndk I/MethodInvocationStub: IPackageManager.performDexOptIfNeeded(com.hgy.ndk, x86) => false
08-26 02:25:23.757 3032-3032/com.hgy.ndk I/MethodInvocationStub: IActivityManager.addPackageDependency(com.hgy.ndk) => void
08-26 02:25:23.758 3032-3032/com.hgy.ndk I/MethodInvocationStub: Os.stat(/data/app/com.hgy.ndk-1/base.apk) => StructStat[st_atime=1566480905,st_blksize=4096,st_blocks=3512,st_ctime=1566480906,st_dev=64800,st_gid=1000,st_ino=16584,st_mode=33188,st_mtime=1566480905,st_nlink=1,st_rdev=0,st_size=1796475,st_uid=1000]
08-26 02:25:23.758 3032-3032/com.hgy.ndk I/MethodInvocationStub: Os.stat(/data/app/com.hgy.ndk-1/base.apk) => StructStat[st_atime=1566480905,st_blksize=4096,st_blocks=3512,st_ctime=1566480906,st_dev=64800,st_gid=1000,st_ino=16584,st_mode=33188,st_mtime=1566480905,st_nlink=1,st_rdev=0,st_size=1796475,st_uid=1000]
08-26 02:25:23.759 3032-3032/com.hgy.ndk I/MethodInvocationStub: Os.stat(/vendor/lib) => StructStat[st_atime=1499889343,st_blksize=4096,st_blocks=8,st_ctime=1499889343,st_dev=64768,st_gid=2000,st_ino=1583,st_mode=16877,st_mtime=1499889343,st_nlink=4,st_rdev=0,st_size=4096,st_uid=0]
08-26 02:25:23.759 3032-3032/com.hgy.ndk I/MethodInvocationStub: Os.stat(/system/lib) => StructStat[st_atime=1499889365,st_blksize=4096,st_blocks=16,st_ctime=1566472418,st_dev=64768,st_gid=0,st_ino=889,st_mode=16877,st_mtime=1566472418,st_nlink=5,st_rdev=0,st_size=8192,st_uid=0]
08-26 02:25:23.759 3032-3032/com.hgy.ndk I/MethodInvocationStub: Os.stat(/data/app/com.hgy.ndk-1/lib/x86) => StructStat[st_atime=1566480905,st_blksize=4096,st_blocks=8,st_ctime=1566480906,st_dev=64800,st_gid=1000,st_ino=16586,st_mode=16877,st_mtime=1566480905,st_nlink=2,st_rdev=0,st_size=4096,st_uid=1000]
08-26 02:25:23.759 3032-3032/com.hgy.ndk I/MethodInvocationStub: Os.stat(/vendor/lib) => StructStat[st_atime=1499889343,st_blksize=4096,st_blocks=8,st_ctime=1499889343,st_dev=64768,st_gid=2000,st_ino=1583,st_mode=16877,st_mtime=1499889343,st_nlink=4,st_rdev=0,st_size=4096,st_uid=0]
08-26 02:25:23.760 3032-3032/com.hgy.ndk I/MethodInvocationStub: Os.stat(/system/lib) => StructStat[st_atime=1499889365,st_blksize=4096,st_blocks=16,st_ctime=1566472418,st_dev=64768,st_gid=0,st_ino=889,st_mode=16877,st_mtime=1566472418,st_nlink=5,st_rdev=0,st_size=8192,st_uid=0]
08-26 02:25:23.761 3032-3032/com.hgy.ndk I/MethodInvocationStub: Os.stat(/data/app/com.hgy.ndk-1/base.apk) => StructStat[st_atime=1566480905,st_blksize=4096,st_blocks=3512,st_ctime=1566480906,st_dev=64800,st_gid=1000,st_ino=16584,st_mode=33188,st_mtime=1566480905,st_nlink=1,st_rdev=0,st_size=1796475,st_uid=1000]
08-26 02:25:23.768 3032-3032/com.hgy.ndk I/MethodInvocationStub: Os.getuid(ul) => 10001
08-26 02:25:23.769 3032-3032/com.hgy.ndk I/MethodInvocationStub: IPackageManager.getPackageInfo(com.hgy.ndk, 0, 0) => PackageInfo{a3889b7 com.hgy.ndk}
08-26 02:25:23.769 3032-3032/com.hgy.ndk I/MethodInvocationStub: Os.getuid(ul) => 10001
08-26 02:25:23.778 3032-3032/com.hgy.ndk I/MethodInvocationStub: IActivityManager.getTaskForActivity(android.os.BinderProxy@a58f8af, false) => 19
08-26 02:25:23.780 3032-3032/com.hgy.ndk I/MethodInvocationStub: Os.getuid(ul) => 10001
08-26 02:25:23.780 3032-3032/com.hgy.ndk I/MethodInvocationStub: Os.getuid(ul) => 10001
08-26 02:25:23.782 3032-3032/com.hgy.ndk I/MethodInvocationStub: Os.fstat(FileDescriptor[26]) => StructStat[st_atime=1566480905,st_blksize=4096,st_blocks=3512,st_ctime=1566480906,st_dev=64800,st_gid=1000,st_ino=16584,st_mode=33188,st_mtime=1566480905,st_nlink=1,st_rdev=0,st_size=1796475,st_uid=1000]
08-26 02:25:23.782 3032-3032/com.hgy.ndk I/MethodInvocationStub: Os.fstat(FileDescriptor[26]) => StructStat[st_atime=1566480905,st_blksize=4096,st_blocks=3512,st_ctime=1566480906,st_dev=64800,st_gid=1000,st_ino=16584,st_mode=33188,st_mtime=1566480905,st_nlink=1,st_rdev=0,st_size=1796475,st_uid=1000]
08-26 02:25:23.783 3032-3032/com.hgy.ndk I/MethodInvocationStub: Os.fstat(FileDescriptor[26]) => StructStat[st_atime=1566480905,st_blksize=4096,st_blocks=3512,st_ctime=1566480906,st_dev=64800,st_gid=1000,st_ino=16584,st_mode=33188,st_mtime=1566480905,st_nlink=1,st_rdev=0,st_size=1796475,st_uid=1000]
08-26 02:25:23.787 3032-3032/com.hgy.ndk I/MethodInvocationStub: IActivityManager.getActivityDisplayId(android.os.BinderProxy@a58f8af) => 0
08-26 02:25:23.787 3032-3032/com.hgy.ndk I/MethodInvocationStub: Os.getuid(ul) => 10001
08-26 02:25:23.788 3032-3032/com.hgy.ndk I/MethodInvocationStub: Os.getuid(ul) => 10001
08-26 02:25:23.788 3032-3032/com.hgy.ndk I/MethodInvocationStub: Os.getuid(ul) => 10001
08-26 02:25:23.789 3032-3032/com.hgy.ndk I/MethodInvocationStub: Os.getuid(ul) => 10001
08-26 02:25:23.789 3032-3032/com.hgy.ndk I/MethodInvocationStub: Os.getuid(ul) => 10001
08-26 02:25:23.791 3032-3032/com.hgy.ndk I/MethodInvocationStub: Os.getuid(ul) => 10001
08-26 02:25:23.791 3032-3032/com.hgy.ndk I/MethodInvocationStub: IUserManager.getProfiles(0, false) => []
08-26 02:25:23.793 3032-3032/com.hgy.ndk I/MethodInvocationStub: IActivityManager.setTaskDescription(android.os.BinderProxy@a58f8af, TaskDescription Label: null Icon: null colorPrimary: -16743049) => void
08-26 02:25:23.795 3032-3032/com.hgy.ndk I/MethodInvocationStub: IActivityManager.getTaskForActivity(android.os.BinderProxy@a58f8af, true) => 19
08-26 02:25:23.795 3032-3032/com.hgy.ndk I/MethodInvocationStub: Os.getuid(ul) => 10001
08-26 02:25:23.796 3032-3032/com.hgy.ndk I/MethodInvocationStub: IUserManager.getProfiles(0, false) => []
08-26 02:25:23.796 3032-3032/com.hgy.ndk I/MethodInvocationStub: IActivityManager.setTaskDescription(android.os.BinderProxy@a58f8af, TaskDescription Label: ndk Icon: android.graphics.Bitmap@800eaa1 colorPrimary: 0) => void
08-26 02:25:23.797 3032-3032/com.hgy.ndk I/MethodInvocationStub: IActivityManager.getRequestedOrientation(android.os.BinderProxy@a58f8af) => -1
08-26 02:25:23.809 3032-3032/com.hgy.ndk I/MethodInvocationStub: IPackageManager.getActivityInfo(ComponentInfo{com.hgy.ndk/com.hgy.ndk.MainActivity}, 128, 0) => ActivityInfo{dda98f1 com.hgy.ndk.MainActivity}
08-26 02:25:23.812 3032-3032/com.hgy.ndk I/MethodInvocationStub: Os.getuid(ul) => 10001
08-26 02:25:23.812 3032-3032/com.hgy.ndk I/MethodInvocationStub: Os.getuid(ul) => 10001
08-26 02:25:23.813 3032-3032/com.hgy.ndk I/MethodInvocationStub: IActivityManager.checkPermission(android.permission.INTERACT_ACROSS_USERS, 3032, 10001) => -1
08-26 02:25:23.813 3032-3032/com.hgy.ndk I/MethodInvocationStub: Os.getuid(ul) => 10001
08-26 02:25:23.814 3032-3032/com.hgy.ndk I/MethodInvocationStub: IActivityManager.checkPermission(android.permission.INTERACT_ACROSS_USERS_FULL, 3032, 10001) => -1
08-26 02:25:23.814 3032-3032/com.hgy.ndk I/MethodInvocationStub: Os.getuid(ul) => 10001
08-26 02:25:23.814 3032-3032/com.hgy.ndk I/MethodInvocationStub: IAccessibilityManager.addClient(android.view.accessibility.AccessibilityManager$1@76ed32d, 0) => 0
08-26 02:25:23.872 3032-3032/com.hgy.ndk I/MethodInvocationStub: IPackageManager.getPackageInfo(com.dts.freefireth, 0, 0) => null
08-26 02:25:23.874 3032-3032/com.hgy.ndk I/MethodInvocationStub: IActivityManager.getActivityOptions(android.os.BinderProxy@a58f8af) => null
08-26 02:25:23.874 3032-3032/com.hgy.ndk I/MethodInvocationStub: IActivityManager.getActivityOptions(android.os.BinderProxy@a58f8af) => null
08-26 02:25:23.876 3032-3032/com.hgy.ndk I/MethodInvocationStub: Os.getuid(ul) => 10001
08-26 02:25:23.880 3032-3032/com.hgy.ndk I/MethodInvocationStub: IWindowManager.openSession(android.view.WindowManagerGlobal$1@ee7b73a, android.view.inputmethod.InputMethodManager$1@358dfeb, android.view.inputmethod.InputMethodManager$ControlledInputConnectionWrapper@bd32148) => android.view.IWindowSession$Stub$Proxy@69dc51d
08-26 02:25:23.886 3032-3032/com.hgy.ndk I/MethodInvocationStub: IGraphicsStats.requestBufferForProcess(com.hgy.ndk, android.os.Binder@97f6163) => {ParcelFileDescriptor: FileDescriptor[32]}
08-26 02:25:23.894 3032-3032/com.hgy.ndk I/MethodInvocationStub: IWindowSession.addToDisplay(android.view.ViewRootImpl$W@7ad160, 0, WM.LayoutParams{(0,0)(fillxfill) ty=1 fl=#81810100 wanim=0x103045b needsMenuKey=2}, 4, 0, Rect(0, 0 - 0, 0), Rect(0, 0 - 0, 0), Rect(0, 0 - 0, 0), uninitialized) => 3
08-26 02:25:23.896 3032-3032/com.hgy.ndk I/MethodInvocationStub: IActivityManager.activityResumed(android.os.BinderProxy@a58f8af) => void
08-26 02:25:23.897 3032-3032/com.hgy.ndk I/MethodInvocationStub: Os.getuid(ul) => 10001
08-26 02:25:23.901 3032-3032/com.hgy.ndk I/MethodInvocationStub: IActivityManager.activityPaused(android.os.BinderProxy@a58f8af) => void
08-26 02:25:23.920 3032-3032/com.hgy.ndk I/MethodInvocationStub: IWindowSession.relayout(android.view.ViewRootImpl$W@7ad160, 0, WM.LayoutParams{(0,0)(fillxfill) sim=#20 ty=1 fl=#81810100 wanim=0x103045b needsMenuKey=2}, 1080, 2028, 0, 0, Rect(0, 0 - 0, 0), Rect(0, 0 - 0, 0), Rect(0, 66 - 0, 132), Rect(0, 0 - 0, 0), Rect(0, 66 - 0, 132), Rect(0, 0 - 0, 0), {1.0 ?mcc?mnc ?locale ?layoutDir ?swdp ?wdp ?hdp ?density ?lsize ?long ?orien ?uimode ?night ?touch ?keyb/?/? ?nav/?}, Surface(name=null)/@0xbbb6953) => 7
08-26 02:25:24.030 3032-3032/com.hgy.ndk I/MethodInvocationStub: IActivityManager.getTaskForActivity(android.os.BinderProxy@d9c3789, false) => 20
08-26 02:25:24.033 3032-3032/com.hgy.ndk I/MethodInvocationStub: Os.getuid(ul) => 10001
08-26 02:25:24.034 3032-3032/com.hgy.ndk I/MethodInvocationStub: Os.getuid(ul) => 10001
08-26 02:25:24.034 3032-3032/com.hgy.ndk I/MethodInvocationStub: IActivityManager.getActivityDisplayId(android.os.BinderProxy@d9c3789) => 0
08-26 02:25:24.035 3032-3032/com.hgy.ndk I/MethodInvocationStub: Os.getuid(ul) => 10001
08-26 02:25:24.037 3032-3032/com.hgy.ndk I/MethodInvocationStub: Os.getuid(ul) => 10001
08-26 02:25:24.038 3032-3032/com.hgy.ndk I/MethodInvocationStub: Os.getuid(ul) => 10001
08-26 02:25:24.042 3032-3032/com.hgy.ndk I/MethodInvocationStub: Os.getuid(ul) => 10001
08-26 02:25:24.042 3032-3032/com.hgy.ndk I/MethodInvocationStub: IUserManager.getProfiles(0, false) => []
08-26 02:25:24.042 3032-3032/com.hgy.ndk I/MethodInvocationStub: IActivityManager.setTaskDescription(android.os.BinderProxy@d9c3789, TaskDescription Label: null Icon: null colorPrimary: -16743049) => void
08-26 02:25:24.043 3032-3032/com.hgy.ndk I/MethodInvocationStub: IActivityManager.getTaskForActivity(android.os.BinderProxy@d9c3789, true) => 20
08-26 02:25:24.043 3032-3032/com.hgy.ndk I/MethodInvocationStub: Os.getuid(ul) => 10001
08-26 02:25:24.043 3032-3032/com.hgy.ndk I/MethodInvocationStub: IUserManager.getProfiles(0, false) => []
08-26 02:25:24.044 3032-3032/com.hgy.ndk I/MethodInvocationStub: IActivityManager.setTaskDescription(android.os.BinderProxy@d9c3789, TaskDescription Label: ndk Icon: android.graphics.Bitmap@90947af colorPrimary: 0) => void
08-26 02:25:24.059 3032-3032/com.hgy.ndk I/MethodInvocationStub: IActivityManager.getRequestedOrientation(android.os.BinderProxy@d9c3789) => -1
08-26 02:25:24.061 3032-3032/com.hgy.ndk I/MethodInvocationStub: IPackageManager.getActivityInfo(ComponentInfo{com.hgy.ndk/com.hgy.ndk.MainActivity}, 128, 0) => ActivityInfo{887c345 com.hgy.ndk.MainActivity}
08-26 02:25:24.072 3032-3032/com.hgy.ndk I/MethodInvocationStub: IPackageManager.getPackageInfo(com.dts.freefireth, 0, 0) => null
08-26 02:25:24.073 3032-3032/com.hgy.ndk I/MethodInvocationStub: IActivityManager.getActivityOptions(android.os.BinderProxy@d9c3789) => null
08-26 02:25:24.074 3032-3032/com.hgy.ndk I/MethodInvocationStub: IActivityManager.getActivityOptions(android.os.BinderProxy@d9c3789) => null
08-26 02:25:24.074 3032-3032/com.hgy.ndk I/MethodInvocationStub: Os.getuid(ul) => 10001
08-26 02:25:24.078 3032-3032/com.hgy.ndk I/MethodInvocationStub: IWindowSession.addToDisplay(android.view.ViewRootImpl$W@35f9e4a, 0, WM.LayoutParams{(0,0)(fillxfill) ty=1 fl=#81810100 wanim=0x103045b needsMenuKey=2}, 4, 0, Rect(0, 0 - 0, 0), Rect(0, 0 - 0, 0), Rect(0, 0 - 0, 0), uninitialized) => 3
08-26 02:25:24.079 3032-3032/com.hgy.ndk I/MethodInvocationStub: IActivityManager.activityResumed(android.os.BinderProxy@d9c3789) => void
08-26 02:25:24.188 3032-3032/com.hgy.ndk I/MethodInvocationStub: IWindowSession.relayout(android.view.ViewRootImpl$W@35f9e4a, 0, WM.LayoutParams{(0,0)(fillxfill) sim=#20 ty=1 fl=#81810100 wanim=0x103045b needsMenuKey=2}, 1080, 2028, 0, 0, Rect(0, 0 - 0, 0), Rect(0, 0 - 0, 0), Rect(0, 66 - 0, 132), Rect(0, 0 - 0, 0), Rect(0, 66 - 0, 132), Rect(0, 0 - 0, 0), {1.0 ?mcc?mnc ?locale ?layoutDir ?swdp ?wdp ?hdp ?density ?lsize ?long ?orien ?uimode ?night ?touch ?keyb/?/? ?nav/?}, Surface(name=null)/@0x3f9d511) => 7
08-26 02:25:24.252 3032-3032/com.hgy.ndk I/MethodInvocationStub: IInputMethodManager.windowGainedFocus(android.view.inputmethod.InputMethodManager$1@358dfeb, android.view.ViewRootImpl$W@35f9e4a, 260, 32, -2122252032, android.view.inputmethod.EditorInfo@c711676, null) => InputBindResult{null com.android.inputmethod.latin/.LatinIME sequence:29 userActionNotificationSequenceNumber:1}
08-26 02:25:24.270 3032-3032/com.hgy.ndk I/MethodInvocationStub: IActivityManager.activityIdle(android.os.BinderProxy@d9c3789, {1.0 310mcc260mnc en_US ldltr sw392dp w392dp h713dp 440dpi nrml long port finger -keyb/v/h dpad/v s.6}, false) => void
08-26 02:25:24.271 3032-3032/com.hgy.ndk I/MethodInvocationStub: IActivityManager.activityIdle(android.os.BinderProxy@a58f8af, {1.0 310mcc260mnc en_US ldltr sw392dp w392dp h713dp 440dpi nrml long port finger -keyb/v/h dpad/v s.6}, false) => void
08-26 02:25:24.306 3032-3032/com.hgy.ndk I/MethodInvocationStub: IWindowSession.relayout(android.view.ViewRootImpl$W@7ad160, 0, null, 1080, 2160, 8, 0, Rect(0, 0 - 1080, 2160), Rect(0, 0 - 0, 0), Rect(0, 66 - 0, 132), Rect(0, 66 - 0, 132), Rect(0, 66 - 0, 132), Rect(0, 0 - 0, 0), {1.0 310mcc260mnc en_US ldltr sw392dp w392dp h713dp 440dpi nrml long port finger -keyb/v/h dpad/v}, Surface(name=null)/@0xbbb6953) => 5
08-26 02:25:24.326 3032-3032/com.hgy.ndk I/MethodInvocationStub: IActivityManager.activityStopped(android.os.BinderProxy@a58f8af, Bundle[{android:viewHierarchyState=Bundle[{android:views={16908290=android.view.AbsSavedState$1@9ae7868, 2131165190=android.support.v7.widget.Toolbar$SavedState@692481, 2131165192=android.view.AbsSavedState$1@9ae7868, 2131165198=android.view.AbsSavedState$1@9ae7868, 2131165232=android.view.AbsSavedState$1@9ae7868}}], android:fragments=android.app.FragmentManagerState@2aa0926}], null, null) => void

其中参数为null会被截取打印成ul,如Os.getuid(ul) => 10058

参考 VirtualApp之Activity栈

  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
简介VirtualApp是一个App虚拟化引擎(简称VA)。VirtualApp已兼容Android 0(8.0 Preview)。VirtualApp在你的App内创建一个虚拟空间,你可以在虚拟空间内任意的安装启动和卸载APK,这一切都与外部隔离,如同一个沙盒。运行在VA的APK无需在外部安装,即VA支持免安装运行APK。VA目前被广泛应用于双开/多开、应用市场、模拟定位、一键改机、隐私保护、游戏修改、自动化测试、无感知热更新等技术领域,但它决不仅限于此,Android本身就是一个极其开放的平台,免安装运行APK这一Feature打开了无限可能--------这都取决于您的想象力。申明当您需要将VirtualApp用于商业用途时,请务必联系QQ:10890 购买商业授权。您如果未经授权将VirtualAppApp模块作为您自己的App用于牟利或上传软件市场,我们取证后将直接报警(侵犯著作权罪)。购买商业授权是对我们最大的支持和认可,我们将投入更多精力和时间来不断完善优化VirtualApp,作为购买商业授权的回报,您可以获得未开放的商业版本和1vs1的支持(技术、运营、预警)!同时我们也支持基于VirtualAppAPP订制开发,请联系:QQ:10890 洽谈。请注意VirtualApp代码的更新频率非常快(以小时为单位),每一次代码的更新都有可能修复重大BUG,所以请 watch 本项目,并注意随时更新代码,以免给您带来损失!已支持的加固(不断更新)360加固腾讯加固梆梆加固梆梆企业版(12306客户端 Pass)爱加密百度加固娜迦加固乐变加固网易易盾通付盾(已支持的加固均可通过VA来脱壳,本技术不公开)在VA使用Google服务VA支持运行官方的Google服务套件,同时我们也提供了对MicroG的支持。您可以通过在VA安装MicroG来支持Google服务,这样,即使外部没有Google服务,用户也可以在VA享受Google服务。MicroG套件可在此下载:Download MicroGMicroG的必要模块:Services CoreServices Framework ProxyStore如果您需要在VA使用官方的Google服务套件(外部已安装的前提下),则可以通过 GmsSupport.installGms(userId) 来安装。注意,您不能同时安装MicroGms和官方的Gms。使用说明前往你的Application并添加如下代码:@Override     protected void attachBaseContext(Context base) {         super.attachBaseContext(base);         try {             VirtualCore.getCore().startup(base);         } catch (Throwable e) {             e.printStackTrace();         }     }安装App:VirtualCore.getCore().installApp({APK PATH}, flags);启动App:VirtualCore.getCore().uninstallApp({PackageName});该App的基本信息:VirtualCore.getCore().findApp({PackageName});

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值