FakeXposed最强屏蔽Xposed、Root检测,自定义maps、文件重定向等支持Android5~14

admin 2026-04-16 04:09:03 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: FakeXposed是一款通用多功能Xposed隐藏工具,通过NativeHook与JavaHook结合实现双向屏蔽检测,支持Android5-14系统。核心功能包括自定义maps文件屏蔽、文件重定向、权限控制、JNI监控等,提供进程内动态配置修改能力。该项目开源且支持多版本Xposed框架,适用于移动安全防护场景。 综合评分: 85 文章分类: 移动安全,逆向分析,安全工具,免杀,红队


cover_image

FakeXposed最强屏蔽Xposed、Root检测,自定义maps、文件重定向等支持Android5~14

哆啦安全

2023年10月6日 23:15 四川

在小说阅读器读本章

去阅读

以下文章来源于sanfengAndroid逆向安全 ,作者sanfengAndroid

sanfengAndroid逆向安全 .

主要分享Android源码、逆向、安全、脱壳、开源项目解析等,第一时间分享个人的开源项目,开源地址 https://github.com/sanfengAndroid

FakeXposed[1]

一个通用多功能的 Xposed 隐藏器,采用 NativeJava 结合来做到双向屏蔽检测,提供高度自由化为每个应用配置不同属性。它不仅仅局限于屏蔽 Xposed 检测,还提供更多更加高级的功能,如 maps 文件自定义屏蔽各种检测、完整的文件重定向功能、访问权限控制、JNI 方法监控、动态符号查找屏蔽 dlsym 等等,还可以提供给其它模块在进程内动态添加或修改配置。开源地址 https://github.com/sanfengAndroid/FakeXposed[2]

原理简要介绍

  • Native Hook 使用我的另一开源项目 fake-linker[3],Java Hook 使用 Xposed 框架,大部分功能都是由 Native Hook 来完成,Xposed 不限于原版 XposedEdXposedVirtualXposed等等
  • 内部提供 堆栈类应用环境变量全局系统属性Android Global属性Runtime.exec拦截文件访问/重定向等符号拦截,各种属性的隐藏和修改,下面我将简单介绍一些原理,代码都是基于 Android 最新源码主分支,旧分支一些变化不是太大自行分析即可

类/堆栈类隐藏

  • Hook Class.forName()ClassLoader.loadClass()Throwable.getStackTrace() 方法,判断隐藏类加载则抛出异常或删除该元素。目前我在测试 EdXposed 中只有部分情况会走该回调,可能框架处理了有关部分

应用/组件隐藏

  • 使用动态代理 PackageManagerActivityManagerActivityTaskManager屏蔽常见会使用到获取其它应用属性的方法,如:getInstalledPackagesgetInstalledApplicationsgetRunningServicesgetTasks等等。应用进程本身就是通过 Bindersystem_server 服务进程通信进程内只存在一个 IBinger 对象,因此非常适合使用动态代理,这里屏蔽掉几乎所有能够访问其它应用的方式,具体查看源码HookSystemComponent[4]。
  • PackageManager 源码在 ActivityThread.getPackageManager[5]
  static volatile IPackageManager sPackageManager;
  public static IPackageManager getPackageManager() {
    if (sPackageManager != null) {
        return sPackageManager;
    }
    final IBinder b = ServiceManager.getService("package");
    sPackageManager = IPackageManager.Stub.asInterface(b);
    return sPackageManager;
  }

因此只需要使用反射修改 sPackageManager 静态变量即可

  • ActivityManager 源码 AndroidO 以上在 ActivityManager.IActivityManagerSingleton[6],AndroidO 以下在 ActivityManagerNative.gDefault,都是一个单例对象
  private&nbsp;static&nbsp;final&nbsp;Singleton<IActivityManager>&nbsp;IActivityManagerSingleton&nbsp;=
  &nbsp;&nbsp;new&nbsp;Singleton<IActivityManager>()&nbsp;{
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@Override
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;protected&nbsp;IActivityManager&nbsp;create()&nbsp;{
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;final&nbsp;IBinder&nbsp;b&nbsp;=&nbsp;ServiceManager.getService(Context.ACTIVITY_SERVICE);
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;final&nbsp;IActivityManager&nbsp;am&nbsp;=&nbsp;IActivityManager.Stub.asInterface(b);
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;am;
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
  &nbsp;&nbsp;};

同样反射修改 Singleton 里面的对象即可

  • ActivityTaskManagerAndroidQ 以上新增的一个服务,修改方法同 ActivityManager

环境变量修改

  • Java 调用 System.getenv()System.getenv(String) 源码如下
  public&nbsp;static&nbsp;java.util.Map<String,String>&nbsp;getenv()&nbsp;{
  &nbsp;&nbsp;SecurityManager&nbsp;sm&nbsp;=&nbsp;getSecurityManager();
  &nbsp;&nbsp;if&nbsp;(sm&nbsp;!=&nbsp;null)&nbsp;{
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sm.checkPermission(new&nbsp;RuntimePermission("getenv.*"));
  &nbsp;&nbsp;}

  &nbsp;&nbsp;return&nbsp;ProcessEnvironment.getenv();
  }
  public&nbsp;static&nbsp;String&nbsp;getenv(String&nbsp;name)&nbsp;{
  &nbsp;&nbsp;if&nbsp;(name&nbsp;==&nbsp;null)&nbsp;{
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;throw&nbsp;new&nbsp;NullPointerException("name&nbsp;==&nbsp;null");
  &nbsp;&nbsp;}

  &nbsp;&nbsp;return&nbsp;Libcore.os.getenv(name);
  }

其最终调用两个函数 Libcore.os.environ()Libcore.os.getenv(String)

  public&nbsp;final&nbsp;class&nbsp;Libcore&nbsp;{
  &nbsp;&nbsp;private&nbsp;Libcore()&nbsp;{&nbsp;}
  &nbsp;&nbsp;public&nbsp;static&nbsp;final&nbsp;Os&nbsp;rawOs&nbsp;=&nbsp;new&nbsp;Linux();
  &nbsp;&nbsp;public&nbsp;static&nbsp;volatile&nbsp;Os&nbsp;os&nbsp;=&nbsp;new&nbsp;BlockGuardOs(rawOs);
  &nbsp;&nbsp;...
  }

  public&nbsp;final&nbsp;class&nbsp;Linux&nbsp;implements&nbsp;Os&nbsp;{
  &nbsp;&nbsp;Linux()&nbsp;{&nbsp;}
  &nbsp;&nbsp;public&nbsp;native&nbsp;String&nbsp;getenv(String&nbsp;name);
  &nbsp;&nbsp;public&nbsp;native&nbsp;String[]&nbsp;environ();
  &nbsp;&nbsp;...
  }

不同版本 Libcore.os 的实现对象类名不一样,但是区别很小,而 native 中访问到是 libc 导出变量 environ、导出函数 getenv,因此通过 Native Hook 拦截 getenv 函数即可拦截对应 Java System.getenv(String)调用,而 System.getenv() 调用是直接使用 environ 变量,因此暂时采用Java Hook替换该Map对象,通常情况下应用是很少使用到 System.getenv 非系统环境变量的,一些软件检测才会使用,因此后续可能会直接修改 environ 变量中的值

全局属性 SystemProperties 修改

  • Java 反射使用 SystemProperties.get 系列方法
  @NonNull
  @SystemApi
  public&nbsp;static&nbsp;String&nbsp;get(@NonNull&nbsp;String&nbsp;key,&nbsp;@Nullable&nbsp;String&nbsp;def)&nbsp;{
  &nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(TRACK_KEY_ACCESS)&nbsp;onKeyAccess(key);
  &nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;native_get(key,&nbsp;def);
  }
  ...
  @FastNative
  @UnsupportedAppUsage(maxTargetSdk&nbsp;=&nbsp;Build.VERSION_CODES.P)
  private&nbsp;static&nbsp;native&nbsp;String&nbsp;native_get(String&nbsp;key,&nbsp;String&nbsp;def);

调用 android_os_SystemProperties.cpp[7] 中的方法

  template<typename&nbsp;Functor>
  void&nbsp;ReadProperty(const&nbsp;prop_info*&nbsp;prop,&nbsp;Functor&&&nbsp;functor)
  {
  #if&nbsp;defined(__BIONIC__)
  &nbsp;&nbsp;&nbsp;&nbsp;auto&nbsp;thunk&nbsp;=&nbsp;[](void*&nbsp;cookie,
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;const&nbsp;char*&nbsp;/*name*/,
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;const&nbsp;char*&nbsp;value,
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;uint32_t&nbsp;/*serial*/)&nbsp;{
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;std::forward<Functor>(*static_cast<Functor*>(cookie))(value);
  &nbsp;&nbsp;&nbsp;&nbsp;};
  &nbsp;&nbsp;&nbsp;&nbsp;__system_property_read_callback(prop,&nbsp;thunk,&nbsp;&functor);
  #else
  &nbsp;&nbsp;&nbsp;&nbsp;LOG(FATAL)&nbsp;<<&nbsp;"fast&nbsp;property&nbsp;access&nbsp;supported&nbsp;only&nbsp;on&nbsp;device";
  #endif
  }

  template<typename&nbsp;Functor>
  void&nbsp;ReadProperty(JNIEnv*&nbsp;env,&nbsp;jstring&nbsp;keyJ,&nbsp;Functor&&&nbsp;functor)
  {
  &nbsp;&nbsp;&nbsp;&nbsp;ScopedUtfChars&nbsp;key(env,&nbsp;keyJ);
  &nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(!key.c_str())&nbsp;{
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return;
  &nbsp;&nbsp;&nbsp;&nbsp;}
  #if&nbsp;defined(__BIONIC__)
  &nbsp;&nbsp;&nbsp;&nbsp;const&nbsp;prop_info*&nbsp;prop&nbsp;=&nbsp;__system_property_find(key.c_str());
  &nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(!prop)&nbsp;{
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return;
  &nbsp;&nbsp;&nbsp;&nbsp;}
  &nbsp;&nbsp;&nbsp;&nbsp;ReadProperty(prop,&nbsp;std::forward<Functor>(functor));
  #else
  &nbsp;&nbsp;&nbsp;&nbsp;std::forward<Functor>(functor)(
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;android::base::GetProperty(key.c_str(),&nbsp;"").c_str());
  #endif
  }

  jstring&nbsp;SystemProperties_getSS(JNIEnv*&nbsp;env,&nbsp;jclass&nbsp;clazz,&nbsp;jstring&nbsp;keyJ,
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;jstring&nbsp;defJ)
  {
  &nbsp;&nbsp;&nbsp;&nbsp;jstring&nbsp;ret&nbsp;=&nbsp;defJ;
  &nbsp;&nbsp;&nbsp;&nbsp;ReadProperty(env,&nbsp;keyJ,&nbsp;[&](const&nbsp;char*&nbsp;value&nbsp;"&")&nbsp;{
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(value[0])&nbsp;{
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ret&nbsp;=&nbsp;env->NewStringUTF(value);
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
  &nbsp;&nbsp;&nbsp;&nbsp;});
  &nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(ret&nbsp;==&nbsp;nullptr&nbsp;&&&nbsp;!env->ExceptionCheck())&nbsp;{
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ret&nbsp;=&nbsp;env->NewStringUTF("");&nbsp;&nbsp;//&nbsp;Legacy&nbsp;behavior
  &nbsp;&nbsp;&nbsp;&nbsp;}
  &nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;ret;
  }

  int&nbsp;register_android_os_SystemProperties(JNIEnv&nbsp;*env)
  {
  &nbsp;&nbsp;&nbsp;&nbsp;const&nbsp;JNINativeMethod&nbsp;method_table[]&nbsp;=&nbsp;{
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;"native_get",
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;",
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(void*)&nbsp;SystemProperties_getSS&nbsp;},
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;"native_get_int",&nbsp;"(Ljava/lang/String;I)I",
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(void*)&nbsp;SystemProperties_get_integral<jint>&nbsp;},
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;"native_get_long",&nbsp;"(Ljava/lang/String;J)J",
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(void*)&nbsp;SystemProperties_get_integral<jlong>&nbsp;},
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;"native_get_boolean",&nbsp;"(Ljava/lang/String;Z)Z",
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(void*)&nbsp;SystemProperties_get_boolean&nbsp;},
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;"native_find",
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"(Ljava/lang/String;)J",
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(void*)&nbsp;SystemProperties_find&nbsp;},
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;"native_get",
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"(J)Ljava/lang/String;",
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(void*)&nbsp;SystemProperties_getH&nbsp;},
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;"native_get_int",&nbsp;"(JI)I",
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(void*)&nbsp;SystemProperties_get_integralH<jint>&nbsp;},
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;"native_get_long",&nbsp;"(JJ)J",
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(void*)&nbsp;SystemProperties_get_integralH<jlong>&nbsp;},
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;"native_get_boolean",&nbsp;"(JZ)Z",
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(void*)&nbsp;SystemProperties_get_booleanH&nbsp;},
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;"native_set",&nbsp;"(Ljava/lang/String;Ljava/lang/String;)V",
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(void*)&nbsp;SystemProperties_set&nbsp;},
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;"native_add_change_callback",&nbsp;"()V",
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(void*)&nbsp;SystemProperties_add_change_callback&nbsp;},
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;"native_report_sysprop_change",&nbsp;"()V",
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(void*)&nbsp;SystemProperties_report_sysprop_change&nbsp;},
  &nbsp;&nbsp;&nbsp;&nbsp;};
  &nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;RegisterMethodsOrDie(env,&nbsp;"android/os/SystemProperties",
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;method_table,&nbsp;NELEM(method_table));
  }

而它调用了 libc.so 中的 __system_property_find__system_property_read_callback,在低版本中获取属性也使用到了 __system_property_get 方法,因此采用 Native Hook 以上这三个方法,这里也要注意不同版本在不同动态库中实现

Android Global 属性修改

  • 采用 Java Hook Global.getString 方法修改

Runtime.exec 拦截

  • 源码分析如下
  public&nbsp;class&nbsp;Runtime{
  &nbsp;&nbsp;public&nbsp;Process&nbsp;exec(String[]&nbsp;cmdarray,&nbsp;String[]&nbsp;envp,&nbsp;File&nbsp;dir)
  &nbsp;&nbsp;&nbsp;&nbsp;throws&nbsp;IOException&nbsp;{
  &nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;new&nbsp;ProcessBuilder(cmdarray)
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.environment(envp)
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.directory(dir)
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.start();
  &nbsp;&nbsp;}
  }

  public&nbsp;final&nbsp;class&nbsp;ProcessBuilder{
  &nbsp;&nbsp;public&nbsp;Process&nbsp;start()&nbsp;throws&nbsp;IOException&nbsp;{
  &nbsp;&nbsp;&nbsp;&nbsp;...
  &nbsp;&nbsp;&nbsp;&nbsp;try&nbsp;{
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;ProcessImpl.start(cmdarray,
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;environment,
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dir,
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;redirects,
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;redirectErrorStream);
  &nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;catch&nbsp;(IOException&nbsp;|&nbsp;IllegalArgumentException&nbsp;e)&nbsp;{
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;...
  &nbsp;&nbsp;}
  }
  final&nbsp;class&nbsp;ProcessImpl&nbsp;{
  &nbsp;&nbsp;static&nbsp;Process&nbsp;start(String[]&nbsp;cmdarray,
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;java.util.Map<String,String>&nbsp;environment,
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;dir,
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ProcessBuilder.Redirect[]&nbsp;redirects,
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;boolean&nbsp;redirectErrorStream)
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;throws&nbsp;IOException
  &nbsp;&nbsp;{
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert&nbsp;cmdarray&nbsp;!=&nbsp;null&nbsp;&&&nbsp;cmdarray.length&nbsp;>&nbsp;0;

  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;Convert&nbsp;arguments&nbsp;to&nbsp;a&nbsp;contiguous&nbsp;block;&nbsp;it's&nbsp;easier&nbsp;to&nbsp;do
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;...
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;复制环境变量

  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;FileInputStream&nbsp;&nbsp;f0&nbsp;=&nbsp;null;
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;FileOutputStream&nbsp;f1&nbsp;=&nbsp;null;
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;FileOutputStream&nbsp;f2&nbsp;=&nbsp;null;

  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;try&nbsp;{
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(redirects&nbsp;==&nbsp;null)&nbsp;{
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;std_fds&nbsp;=&nbsp;new&nbsp;int[]&nbsp;{&nbsp;-1,&nbsp;-1,&nbsp;-1&nbsp;};
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;else&nbsp;{
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;重定向流
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;...
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}

  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;new&nbsp;UNIXProcess
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(toCString(cmdarray[0]),
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;argBlock,&nbsp;args.length,
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;envBlock,&nbsp;envc[0],
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;toCString(dir),
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;std_fds,
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;redirectErrorStream);
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;finally&nbsp;{
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;In&nbsp;theory,&nbsp;close()&nbsp;can&nbsp;throw&nbsp;IOException
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;(although&nbsp;it&nbsp;is&nbsp;rather&nbsp;unlikely&nbsp;to&nbsp;happen&nbsp;here)
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;try&nbsp;{&nbsp;if&nbsp;(f0&nbsp;!=&nbsp;null)&nbsp;f0.close();&nbsp;}
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;finally&nbsp;{
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;try&nbsp;{&nbsp;if&nbsp;(f1&nbsp;!=&nbsp;null)&nbsp;f1.close();&nbsp;}
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;finally&nbsp;{&nbsp;if&nbsp;(f2&nbsp;!=&nbsp;null)&nbsp;f2.close();&nbsp;}
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
  }

  final&nbsp;class&nbsp;UNIXProcess&nbsp;extends&nbsp;Process&nbsp;{
  &nbsp;&nbsp;private&nbsp;native&nbsp;int&nbsp;forkAndExec(byte[]&nbsp;prog,
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;byte[]&nbsp;argBlock,&nbsp;int&nbsp;argc,
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;byte[]&nbsp;envBlock,&nbsp;int&nbsp;envc,
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;byte[]&nbsp;dir,
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int[]&nbsp;fds,
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;boolean&nbsp;redirectErrorStream)
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;throws&nbsp;IOException;

  &nbsp;&nbsp;UNIXProcess(final&nbsp;byte[]&nbsp;prog,
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;final&nbsp;byte[]&nbsp;argBlock,&nbsp;final&nbsp;int&nbsp;argc,
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;final&nbsp;byte[]&nbsp;envBlock,&nbsp;final&nbsp;int&nbsp;envc,
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;final&nbsp;byte[]&nbsp;dir,
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;final&nbsp;int[]&nbsp;fds,
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;final&nbsp;boolean&nbsp;redirectErrorStream)
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;throws&nbsp;IOException&nbsp;{

  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pid&nbsp;=&nbsp;forkAndExec(prog,
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;argBlock,&nbsp;argc,
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;envBlock,&nbsp;envc,
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dir,
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fds,
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;redirectErrorStream);

  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;try&nbsp;{
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;doPrivileged(new&nbsp;PrivilegedExceptionAction<Void>()&nbsp;{
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;Void&nbsp;run()&nbsp;throws&nbsp;IOException&nbsp;{
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;initStreams(fds);
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;null;
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}});
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;catch&nbsp;(PrivilegedActionException&nbsp;ex)&nbsp;{
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;throw&nbsp;(IOException)&nbsp;ex.getException();
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
  }

通过跟踪 Runtime.exec() -> ProcessBuilder.start() -> ProcessImpl.start() -> new UNIXProcess() -> UNIXProcess.forkAndExec() 最终执行 UNIXProcess.forkAndExec 产生子进程,继续跟踪 native

  JNIEXPORT&nbsp;jint&nbsp;JNICALL
  UNIXProcess_forkAndExec(JNIEnv&nbsp;*env,
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;jobject&nbsp;process,
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;jbyteArray&nbsp;prog,
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;jbyteArray&nbsp;argBlock,&nbsp;jint&nbsp;argc,
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;jbyteArray&nbsp;envBlock,&nbsp;jint&nbsp;envc,
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;jbyteArray&nbsp;dir,
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;jintArray&nbsp;std_fds,
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;jboolean&nbsp;redirectErrorStream)
  {
  &nbsp;&nbsp;&nbsp;&nbsp;...
  &nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;上面设置环境变量,重定向输入输出流
  &nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;startChild关键函数启动子进程
  &nbsp;&nbsp;&nbsp;&nbsp;resultPid&nbsp;=&nbsp;startChild(c);
  &nbsp;&nbsp;&nbsp;&nbsp;assert(resultPid&nbsp;!=&nbsp;0);

  &nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(resultPid&nbsp;<&nbsp;0)&nbsp;{
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;throwIOException(env,&nbsp;errno,&nbsp;START_CHILD_SYSTEM_CALL&nbsp;"&nbsp;failed");
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;goto&nbsp;Catch;
  &nbsp;&nbsp;&nbsp;&nbsp;}

  &nbsp;&nbsp;&nbsp;&nbsp;restartableClose(fail[1]);&nbsp;fail[1]&nbsp;=&nbsp;-1;&nbsp;/*&nbsp;See:&nbsp;WhyCantJohnnyExec&nbsp;*/

  &nbsp;&nbsp;&nbsp;&nbsp;switch&nbsp;(readFully(fail[0],&nbsp;&errnum,&nbsp;sizeof(errnum)))&nbsp;{
  &nbsp;&nbsp;&nbsp;&nbsp;case&nbsp;0:&nbsp;break;&nbsp;/*&nbsp;Exec&nbsp;succeeded&nbsp;*/
  &nbsp;&nbsp;&nbsp;&nbsp;case&nbsp;sizeof(errnum):
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;waitpid(resultPid,&nbsp;NULL,&nbsp;0);
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;throwIOException(env,&nbsp;errnum,&nbsp;"Exec&nbsp;failed");
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;goto&nbsp;Catch;
  &nbsp;&nbsp;&nbsp;&nbsp;default:
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;throwIOException(env,&nbsp;errno,&nbsp;"Read&nbsp;failed");
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;goto&nbsp;Catch;
  &nbsp;&nbsp;&nbsp;&nbsp;}

  &nbsp;&nbsp;&nbsp;&nbsp;fds[0]&nbsp;=&nbsp;(in&nbsp;[1]&nbsp;!=&nbsp;-1)&nbsp;?&nbsp;in&nbsp;[1]&nbsp;:&nbsp;-1;
  &nbsp;&nbsp;&nbsp;&nbsp;fds[1]&nbsp;=&nbsp;(out[0]&nbsp;!=&nbsp;-1)&nbsp;?&nbsp;out[0]&nbsp;:&nbsp;-1;
  &nbsp;&nbsp;&nbsp;&nbsp;fds[2]&nbsp;=&nbsp;(err[0]&nbsp;!=&nbsp;-1)&nbsp;?&nbsp;err[0]&nbsp;:&nbsp;-1;

  Finally:
  #if&nbsp;START_CHILD_USE_CLONE
  &nbsp;&nbsp;&nbsp;&nbsp;free(c->clone_stack);
  #endif

  &nbsp;&nbsp;&nbsp;&nbsp;/*&nbsp;Always&nbsp;clean&nbsp;up&nbsp;the&nbsp;child's&nbsp;side&nbsp;of&nbsp;the&nbsp;pipes&nbsp;*/
  &nbsp;&nbsp;&nbsp;&nbsp;closeSafely(in&nbsp;[0]);
  &nbsp;&nbsp;&nbsp;&nbsp;closeSafely(out[1]);
  &nbsp;&nbsp;&nbsp;&nbsp;closeSafely(err[1]);

  &nbsp;&nbsp;&nbsp;&nbsp;/*&nbsp;Always&nbsp;clean&nbsp;up&nbsp;fail&nbsp;descriptors&nbsp;*/
  &nbsp;&nbsp;&nbsp;&nbsp;closeSafely(fail[0]);
  &nbsp;&nbsp;&nbsp;&nbsp;closeSafely(fail[1]);

  &nbsp;&nbsp;&nbsp;&nbsp;releaseBytes(env,&nbsp;prog,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pprog);
  &nbsp;&nbsp;&nbsp;&nbsp;releaseBytes(env,&nbsp;argBlock,&nbsp;pargBlock);
  &nbsp;&nbsp;&nbsp;&nbsp;releaseBytes(env,&nbsp;envBlock,&nbsp;penvBlock);
  &nbsp;&nbsp;&nbsp;&nbsp;releaseBytes(env,&nbsp;dir,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;c->pdir);

  &nbsp;&nbsp;&nbsp;&nbsp;free(c->argv);
  &nbsp;&nbsp;&nbsp;&nbsp;free(c->envv);
  &nbsp;&nbsp;&nbsp;&nbsp;free(c);

  &nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(fds&nbsp;!=&nbsp;NULL)
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(*env)->ReleaseIntArrayElements(env,&nbsp;std_fds,&nbsp;fds,&nbsp;0);

  &nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;resultPid;

  Catch:
  &nbsp;&nbsp;&nbsp;&nbsp;/*&nbsp;Clean&nbsp;up&nbsp;the&nbsp;parent's&nbsp;side&nbsp;of&nbsp;the&nbsp;pipes&nbsp;in&nbsp;case&nbsp;of&nbsp;failure&nbsp;only&nbsp;*/
  &nbsp;&nbsp;&nbsp;&nbsp;closeSafely(in&nbsp;[1]);
  &nbsp;&nbsp;&nbsp;&nbsp;closeSafely(out[0]);
  &nbsp;&nbsp;&nbsp;&nbsp;closeSafely(err[0]);
  &nbsp;&nbsp;&nbsp;&nbsp;goto&nbsp;Finally;
  }

最关键函数 startChild 继续跟踪

  static&nbsp;pid_t
  startChild(ChildStuff&nbsp;*c)&nbsp;{
  #if&nbsp;START_CHILD_USE_CLONE
  #define&nbsp;START_CHILD_CLONE_STACK_SIZE&nbsp;(64&nbsp;*&nbsp;1024)
  &nbsp;&nbsp;&nbsp;&nbsp;/*
  &nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;See&nbsp;clone(2).
  &nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;Instead&nbsp;of&nbsp;worrying&nbsp;about&nbsp;which&nbsp;direction&nbsp;the&nbsp;stack&nbsp;grows,&nbsp;just
  &nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;allocate&nbsp;twice&nbsp;as&nbsp;much&nbsp;and&nbsp;start&nbsp;the&nbsp;stack&nbsp;in&nbsp;the&nbsp;middle.
  &nbsp;&nbsp;&nbsp;&nbsp;*/
  &nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;((c->clone_stack&nbsp;=&nbsp;malloc(2&nbsp;*&nbsp;START_CHILD_CLONE_STACK_SIZE))&nbsp;==&nbsp;NULL)
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/*&nbsp;errno&nbsp;will&nbsp;be&nbsp;set&nbsp;to&nbsp;ENOMEM&nbsp;*/
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;-1;
  &nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;clone(childProcess,
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;c->clone_stack&nbsp;+&nbsp;START_CHILD_CLONE_STACK_SIZE,
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CLONE_VFORK&nbsp;|&nbsp;CLONE_VM&nbsp;|&nbsp;SIGCHLD,&nbsp;c);
  #else
  &nbsp;&nbsp;#if&nbsp;START_CHILD_USE_VFORK
  &nbsp;&nbsp;&nbsp;&nbsp;/*
  &nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;We&nbsp;separate&nbsp;the&nbsp;call&nbsp;to&nbsp;vfork&nbsp;into&nbsp;a&nbsp;separate&nbsp;function&nbsp;to&nbsp;make
  &nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;very&nbsp;sure&nbsp;to&nbsp;keep&nbsp;stack&nbsp;of&nbsp;child&nbsp;from&nbsp;corrupting&nbsp;stack&nbsp;of&nbsp;parent,
  &nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;as&nbsp;suggested&nbsp;by&nbsp;the&nbsp;scary&nbsp;gcc&nbsp;warning:
  &nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;&nbsp;warning:&nbsp;variable&nbsp;'foo'&nbsp;might&nbsp;be&nbsp;clobbered&nbsp;by&nbsp;'longjmp'&nbsp;or&nbsp;'vfork'
  &nbsp;&nbsp;&nbsp;&nbsp;*/
  &nbsp;&nbsp;&nbsp;&nbsp;volatile&nbsp;pid_t&nbsp;resultPid&nbsp;=&nbsp;vfork();
  &nbsp;&nbsp;#else
  &nbsp;&nbsp;&nbsp;&nbsp;/*
  &nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;From&nbsp;Solaris&nbsp;fork(2):&nbsp;In&nbsp;Solaris&nbsp;10,&nbsp;a&nbsp;call&nbsp;to&nbsp;fork()&nbsp;is
  &nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;identical&nbsp;to&nbsp;a&nbsp;call&nbsp;to&nbsp;fork1();&nbsp;only&nbsp;the&nbsp;calling&nbsp;thread&nbsp;is
  &nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;replicated&nbsp;in&nbsp;the&nbsp;child&nbsp;process.&nbsp;This&nbsp;is&nbsp;the&nbsp;POSIX-specified
  &nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;behavior&nbsp;for&nbsp;fork().
  &nbsp;&nbsp;&nbsp;&nbsp;*/
  &nbsp;&nbsp;&nbsp;&nbsp;pid_t&nbsp;resultPid&nbsp;=&nbsp;fork();
  &nbsp;&nbsp;#endif
  &nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(resultPid&nbsp;==&nbsp;0)
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;子进程处理对应命令
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;childProcess(c);
  &nbsp;&nbsp;&nbsp;&nbsp;assert(resultPid&nbsp;!=&nbsp;0);&nbsp;&nbsp;/*&nbsp;childProcess&nbsp;never&nbsp;returns&nbsp;*/
  &nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;resultPid;
  #endif&nbsp;/*&nbsp;!&nbsp;START_CHILD_USE_CLONE&nbsp;*/
  }

调用 clonevforkfork 函数产生子进程然后调用 childProcess(c) 处理命令,这里使用哪一个函数产生子进程不是重点,我们关心的是子进程如何执行命令

  static&nbsp;int
  childProcess(void&nbsp;*arg)
  {
  &nbsp;&nbsp;&nbsp;&nbsp;const&nbsp;ChildStuff*&nbsp;p&nbsp;=&nbsp;(const&nbsp;ChildStuff*)&nbsp;arg;

  &nbsp;&nbsp;&nbsp;&nbsp;/*&nbsp;Close&nbsp;the&nbsp;parent&nbsp;sides&nbsp;of&nbsp;the&nbsp;pipes.
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Closing&nbsp;pipe&nbsp;fds&nbsp;here&nbsp;is&nbsp;redundant,&nbsp;since&nbsp;closeDescriptors()
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;would&nbsp;do&nbsp;it&nbsp;anyways,&nbsp;but&nbsp;a&nbsp;little&nbsp;paranoia&nbsp;is&nbsp;a&nbsp;good&nbsp;thing.&nbsp;*/
  &nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;((closeSafely(p->in[1])&nbsp;&nbsp;&nbsp;==&nbsp;-1)&nbsp;||
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(closeSafely(p->out[0])&nbsp;&nbsp;==&nbsp;-1)&nbsp;||
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(closeSafely(p->err[0])&nbsp;&nbsp;==&nbsp;-1)&nbsp;||
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(closeSafely(p->fail[0])&nbsp;==&nbsp;-1))
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;goto&nbsp;WhyCantJohnnyExec;

  &nbsp;&nbsp;&nbsp;&nbsp;/*&nbsp;Give&nbsp;the&nbsp;child&nbsp;sides&nbsp;of&nbsp;the&nbsp;pipes&nbsp;the&nbsp;right&nbsp;fileno's.&nbsp;*/
  &nbsp;&nbsp;&nbsp;&nbsp;/*&nbsp;Note:&nbsp;it&nbsp;is&nbsp;possible&nbsp;for&nbsp;in[0]&nbsp;==&nbsp;0&nbsp;*/
  &nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;((moveDescriptor(p->in[0]&nbsp;!=&nbsp;-1&nbsp;?&nbsp;&nbsp;p->in[0]&nbsp;:&nbsp;p->fds[0],
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;STDIN_FILENO)&nbsp;==&nbsp;-1)&nbsp;||
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(moveDescriptor(p->out[1]!=&nbsp;-1&nbsp;?&nbsp;p->out[1]&nbsp;:&nbsp;p->fds[1],
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;STDOUT_FILENO)&nbsp;==&nbsp;-1))
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;goto&nbsp;WhyCantJohnnyExec;

  &nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(p->redirectErrorStream)&nbsp;{
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;((closeSafely(p->err[1])&nbsp;==&nbsp;-1)&nbsp;||
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(restartableDup2(STDOUT_FILENO,&nbsp;STDERR_FILENO)&nbsp;==&nbsp;-1))
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;goto&nbsp;WhyCantJohnnyExec;
  &nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;else&nbsp;{
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(moveDescriptor(p->err[1]&nbsp;!=&nbsp;-1&nbsp;?&nbsp;p->err[1]&nbsp;:&nbsp;p->fds[2],
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;STDERR_FILENO)&nbsp;==&nbsp;-1)
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;goto&nbsp;WhyCantJohnnyExec;
  &nbsp;&nbsp;&nbsp;&nbsp;}

  &nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(moveDescriptor(p->fail[1],&nbsp;FAIL_FILENO)&nbsp;==&nbsp;-1)
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;goto&nbsp;WhyCantJohnnyExec;

  &nbsp;&nbsp;&nbsp;&nbsp;/*&nbsp;close&nbsp;everything&nbsp;*/
  &nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(closeDescriptors()&nbsp;==&nbsp;0)&nbsp;{&nbsp;/*&nbsp;failed,&nbsp;&nbsp;close&nbsp;the&nbsp;old&nbsp;way&nbsp;*/
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;max_fd&nbsp;=&nbsp;(int)sysconf(_SC_OPEN_MAX);
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;fd;
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for&nbsp;(fd&nbsp;=&nbsp;FAIL_FILENO&nbsp;+&nbsp;1;&nbsp;fd&nbsp;<&nbsp;max_fd;&nbsp;fd++)
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(restartableClose(fd)&nbsp;==&nbsp;-1&nbsp;&&&nbsp;errno&nbsp;!=&nbsp;EBADF)
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;goto&nbsp;WhyCantJohnnyExec;
  &nbsp;&nbsp;&nbsp;&nbsp;}

  &nbsp;&nbsp;&nbsp;&nbsp;/*&nbsp;change&nbsp;to&nbsp;the&nbsp;new&nbsp;working&nbsp;directory&nbsp;*/
  &nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(p->pdir&nbsp;!=&nbsp;NULL&nbsp;&&&nbsp;chdir(p->pdir)&nbsp;<&nbsp;0)
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;goto&nbsp;WhyCantJohnnyExec;

  &nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(fcntl(FAIL_FILENO,&nbsp;F_SETFD,&nbsp;FD_CLOEXEC)&nbsp;==&nbsp;-1)
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;goto&nbsp;WhyCantJohnnyExec;
  &nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;最终调用&nbsp;JDK_execvpe&nbsp;执行命令
  &nbsp;&nbsp;&nbsp;&nbsp;JDK_execvpe(p->argv[0],&nbsp;p->argv,&nbsp;p->envv);

  WhyCantJohnnyExec:
  &nbsp;&nbsp;&nbsp;&nbsp;/*&nbsp;We&nbsp;used&nbsp;to&nbsp;go&nbsp;to&nbsp;an&nbsp;awful&nbsp;lot&nbsp;of&nbsp;trouble&nbsp;to&nbsp;predict&nbsp;whether&nbsp;the
  &nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;child&nbsp;would&nbsp;fail,&nbsp;but&nbsp;there&nbsp;is&nbsp;no&nbsp;reliable&nbsp;way&nbsp;to&nbsp;predict&nbsp;the
  &nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;success&nbsp;of&nbsp;an&nbsp;operation&nbsp;without&nbsp;*trying*&nbsp;it,&nbsp;and&nbsp;there's&nbsp;no&nbsp;way
  &nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;to&nbsp;try&nbsp;a&nbsp;chdir&nbsp;or&nbsp;exec&nbsp;in&nbsp;the&nbsp;parent.&nbsp;&nbsp;Instead,&nbsp;all&nbsp;we&nbsp;need&nbsp;is&nbsp;a
  &nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;way&nbsp;to&nbsp;communicate&nbsp;any&nbsp;failure&nbsp;back&nbsp;to&nbsp;the&nbsp;parent.&nbsp;&nbsp;Easy;&nbsp;we&nbsp;just
  &nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;send&nbsp;the&nbsp;errno&nbsp;back&nbsp;to&nbsp;the&nbsp;parent&nbsp;over&nbsp;a&nbsp;pipe&nbsp;in&nbsp;case&nbsp;of&nbsp;failure.
  &nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;The&nbsp;tricky&nbsp;thing&nbsp;is,&nbsp;how&nbsp;do&nbsp;we&nbsp;communicate&nbsp;the&nbsp;*success*&nbsp;of&nbsp;exec?
  &nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;We&nbsp;use&nbsp;FD_CLOEXEC&nbsp;together&nbsp;with&nbsp;the&nbsp;fact&nbsp;that&nbsp;a&nbsp;read()&nbsp;on&nbsp;a&nbsp;pipe
  &nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;yields&nbsp;EOF&nbsp;when&nbsp;the&nbsp;write&nbsp;ends&nbsp;(we&nbsp;have&nbsp;two&nbsp;of&nbsp;them!)&nbsp;are&nbsp;closed.
  &nbsp;&nbsp;&nbsp;&nbsp;*/
  &nbsp;&nbsp;&nbsp;&nbsp;{
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;errnum&nbsp;=&nbsp;errno;
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;restartableWrite(FAIL_FILENO,&nbsp;&errnum,&nbsp;sizeof(errnum));
  &nbsp;&nbsp;&nbsp;&nbsp;}
  &nbsp;&nbsp;&nbsp;&nbsp;restartableClose(FAIL_FILENO);
  &nbsp;&nbsp;&nbsp;&nbsp;_exit(-1);
  &nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;0;&nbsp;&nbsp;/*&nbsp;Suppress&nbsp;warning&nbsp;"no&nbsp;return&nbsp;value&nbsp;from&nbsp;function"&nbsp;*/
  }

  static&nbsp;void
  JDK_execvpe(const&nbsp;char&nbsp;*file,
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;const&nbsp;char&nbsp;*argv[],
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;const&nbsp;char&nbsp;*const&nbsp;envp[])
  {
  &nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(envp&nbsp;==&nbsp;NULL&nbsp;||&nbsp;(char&nbsp;**)&nbsp;envp&nbsp;==&nbsp;environ)&nbsp;{
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;execvp(file,&nbsp;(char&nbsp;**)&nbsp;argv);
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return;
  &nbsp;&nbsp;&nbsp;&nbsp;}

  &nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(*file&nbsp;==&nbsp;'\0')&nbsp;{
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;errno&nbsp;=&nbsp;ENOENT;
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return;
  &nbsp;&nbsp;&nbsp;&nbsp;}

  &nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(strchr(file,&nbsp;'/')&nbsp;!=&nbsp;NULL)&nbsp;{
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;execve_with_shell_fallback(file,&nbsp;argv,&nbsp;envp);
  &nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;else&nbsp;{
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/*&nbsp;We&nbsp;must&nbsp;search&nbsp;PATH&nbsp;(parent's,&nbsp;not&nbsp;child's)&nbsp;*/
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;char&nbsp;expanded_file[PATH_MAX];
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;filelen&nbsp;=&nbsp;strlen(file);
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;sticky_errno&nbsp;=&nbsp;0;
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;const&nbsp;char&nbsp;*&nbsp;const&nbsp;*&nbsp;dirs;
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for&nbsp;(dirs&nbsp;=&nbsp;parentPathv;&nbsp;*dirs;&nbsp;dirs++)&nbsp;{
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;const&nbsp;char&nbsp;*&nbsp;dir&nbsp;=&nbsp;*dirs;
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;dirlen&nbsp;=&nbsp;strlen(dir);
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(filelen&nbsp;+&nbsp;dirlen&nbsp;+&nbsp;1&nbsp;>=&nbsp;PATH_MAX)&nbsp;{
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;errno&nbsp;=&nbsp;ENAMETOOLONG;
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;continue;
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;memcpy(expanded_file,&nbsp;dir,&nbsp;dirlen);
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;memcpy(expanded_file&nbsp;+&nbsp;dirlen,&nbsp;file,&nbsp;filelen);
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;expanded_file[dirlen&nbsp;+&nbsp;filelen]&nbsp;=&nbsp;'\0';
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;execve_with_shell_fallback(expanded_file,&nbsp;argv,&nbsp;envp);
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/*&nbsp;There&nbsp;are&nbsp;3&nbsp;responses&nbsp;to&nbsp;various&nbsp;classes&nbsp;of&nbsp;errno:
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;return&nbsp;immediately,&nbsp;continue&nbsp;(especially&nbsp;for&nbsp;ENOENT),
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;or&nbsp;continue&nbsp;with&nbsp;"sticky"&nbsp;errno.
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;From&nbsp;exec(3):
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;If&nbsp;permission&nbsp;is&nbsp;denied&nbsp;for&nbsp;a&nbsp;file&nbsp;(the&nbsp;attempted
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;execve&nbsp;returned&nbsp;EACCES),&nbsp;these&nbsp;functions&nbsp;will&nbsp;continue
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;searching&nbsp;the&nbsp;rest&nbsp;of&nbsp;the&nbsp;search&nbsp;path.&nbsp;&nbsp;If&nbsp;no&nbsp;other
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;file&nbsp;is&nbsp;found,&nbsp;however,&nbsp;they&nbsp;will&nbsp;return&nbsp;with&nbsp;the
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;global&nbsp;variable&nbsp;errno&nbsp;set&nbsp;to&nbsp;EACCES.
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*/
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;switch&nbsp;(errno)&nbsp;{
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;case&nbsp;EACCES:
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sticky_errno&nbsp;=&nbsp;errno;
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/*&nbsp;FALLTHRU&nbsp;*/
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;case&nbsp;ENOENT:
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;case&nbsp;ENOTDIR:
  #ifdef&nbsp;ELOOP
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;case&nbsp;ELOOP:
  #endif
  #ifdef&nbsp;ESTALE
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;case&nbsp;ESTALE:
  #endif
  #ifdef&nbsp;ENODEV
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;case&nbsp;ENODEV:
  #endif
  #ifdef&nbsp;ETIMEDOUT
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;case&nbsp;ETIMEDOUT:
  #endif
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;break;&nbsp;/*&nbsp;Try&nbsp;other&nbsp;directories&nbsp;in&nbsp;PATH&nbsp;*/
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;default:
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return;
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(sticky_errno&nbsp;!=&nbsp;0)
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;errno&nbsp;=&nbsp;sticky_errno;
  &nbsp;&nbsp;&nbsp;&nbsp;}
  }

  static&nbsp;void
  execve_with_shell_fallback(const&nbsp;char&nbsp;*file,
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;const&nbsp;char&nbsp;*argv[],
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;const&nbsp;char&nbsp;*const&nbsp;envp[])
  {
  #if&nbsp;START_CHILD_USE_CLONE&nbsp;||&nbsp;START_CHILD_USE_VFORK
  &nbsp;&nbsp;&nbsp;&nbsp;/*&nbsp;shared&nbsp;address&nbsp;space;&nbsp;be&nbsp;very&nbsp;careful.&nbsp;*/
  &nbsp;&nbsp;&nbsp;&nbsp;execve(file,&nbsp;(char&nbsp;**)&nbsp;argv,&nbsp;(char&nbsp;**)&nbsp;envp);
  &nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(errno&nbsp;==&nbsp;ENOEXEC)
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;execve_as_traditional_shell_script(file,&nbsp;argv,&nbsp;envp);
  #else
  &nbsp;&nbsp;&nbsp;&nbsp;/*&nbsp;unshared&nbsp;address&nbsp;space;&nbsp;we&nbsp;can&nbsp;mutate&nbsp;environ.&nbsp;*/
  &nbsp;&nbsp;&nbsp;&nbsp;environ&nbsp;=&nbsp;(char&nbsp;**)&nbsp;envp;
  &nbsp;&nbsp;&nbsp;&nbsp;execvp(file,&nbsp;(char&nbsp;**)&nbsp;argv);
  #endif
  }

通过上面分析,最终调用 JDK_execvpe -> execve_with_shell_fallback -> execve/execvp 执行命令,实际测试执行 Runtime.exec 最终执行到 execvp 中。由于 fork 子进程后会继承父进程的环境,因此也可通过 Native Hook 来拦截该函数,但是实际测试中如果拦截 execvp 会导致子进程一直无法结束,从而导致卡住,这里原因暂时不明,有知道的可以留言告诉我。因此还是老实采用 Java Hook 更底层方法 java.lang.UNIXProcess 构造方法,对于低版本 Hook java.lang.ProcessManager.exec 方法。基于此提供命令,参数替换,以及固定输入、输出、错误流。具体源码查看 HookRuntime[8]

文件重定向/访问控制(核心)

  • 文件重定向/黑名单:Native Hook 与 IO 有关的方法,由于我们使用的 PLT Hook 因此要尽可能的包含全部函数

  • openat__openatopenfopenlibc 函数,Java 中的 File 使用调用到 Libcore.os 中,这与上面分析环境变量类似,因此只需要 Hook libc 中的 IO 函数即可,查看代码hook_io[9]。

  • syscall 函数,自己实现 软中断系统调用 的无法拦截,其 inline Hook 框架也无法拦截,只能通过修改内核或动态查找 软中断系统调用 然后再 Hook,这种极个别情况忽略,查看代码 hook_syscall[10]

  • exec 簇执行函数,它会传入可执行文件路径,也需要重定向,查看代码 hook_exec[11]

  • 与时间相关函数 utimesutimelutimes,查看代码 hook_time[12]

  • 与文件访问路径相关函数 chdirlinkat等,查看代码 hook_unistd[13]

  • 与文件状态相关函数 fchmodatfstatatstat等,查看代码 hook_stat[14]

  • maps 文件过滤,基于文件重定向,当要访问 maps 文件时将修改掉需要过滤的数据然后将它重定向到缓存路径,查看代码 io_redirect[15]

  • 动态加载函数 dlopenandroid_dlopen_ext,查看代码 hook_dlfcn[16]

  • 文件权限控制

  • stat fstatataccess 等函数,查看代码 hook_stat[17]

符号隐藏

  • Native Hook dlsym 函数,屏蔽一些符号查找和重定向 libc 库中的函数到 Hook 模块 中,查看代码 hook_dlfcn[18]

系统共享库查找

  1. 上面 Hook 的 native 方法都是 libc 中的导出方法,要让 Native Hook 生效我们则需要重定位那些已经加载过的动态库,其中系统中最主要使用的 Libcore 库,我们通过查找 Android.bp(旧版本 Android.mk)来查找共享库名称,如果没找到名称则目录一级一级的向上继续查找,下面与最新版 Libcore 为例,其它版本类似
  • 如上面频繁使用的 libcore_io_Linux.cpp 为例,源码路径在 libcore/luni/src/main/native/libcore_io_Linux.cpp,它所属的编译模块 Android.bp(libcore/luni/src/main/native/Android.bp),有关配置如下
  package&nbsp;{
  &nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;http://go/android-license-faq
  &nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;A&nbsp;large-scale-change&nbsp;added&nbsp;'default_applicable_licenses'&nbsp;to&nbsp;import
  &nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;the&nbsp;below&nbsp;license&nbsp;kinds&nbsp;from&nbsp;"libcore_luni_license":
  &nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;&nbsp;&nbsp;SPDX-license-identifier-Apache-2.0
  &nbsp;&nbsp;&nbsp;&nbsp;default_applicable_licenses:&nbsp;["libcore_luni_license"],
  }

  filegroup&nbsp;{
  &nbsp;&nbsp;&nbsp;&nbsp;name:&nbsp;"luni_native_srcs",
  &nbsp;&nbsp;&nbsp;&nbsp;visibility:&nbsp;[
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"//libcore",
  &nbsp;&nbsp;&nbsp;&nbsp;],
  &nbsp;&nbsp;&nbsp;&nbsp;srcs:&nbsp;[
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"ExecStrings.cpp",
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"IcuUtilities.cpp",
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"JniConstants.cpp",
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"JniException.cpp",
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"NetworkUtilities.cpp",
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"Register.cpp",
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"ZipUtilities.cpp",
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"android_system_OsConstants.cpp",
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"cbigint.cpp",
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"java_lang_StringToReal.cpp",
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"java_lang_invoke_MethodHandle.cpp",
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"java_lang_invoke_VarHandle.cpp",
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"libcore_math_NativeBN.cpp",
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"libcore_icu_ICU.cpp",
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"libcore_io_AsynchronousCloseMonitor.cpp",
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;这里包含我们需要拦截的源代码
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"libcore_io_Linux.cpp",
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"libcore_io_Memory.cpp",
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"libcore_util_NativeAllocationRegistry.cpp",
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"org_apache_harmony_xml_ExpatParser.cpp",
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"sun_misc_Unsafe.cpp",
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"valueOf.cpp",
  &nbsp;&nbsp;&nbsp;&nbsp;],
  }

  filegroup&nbsp;{
  &nbsp;&nbsp;&nbsp;&nbsp;name:&nbsp;"libandroidio_srcs",
  &nbsp;&nbsp;&nbsp;&nbsp;visibility:&nbsp;[
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"//libcore",
  &nbsp;&nbsp;&nbsp;&nbsp;],
  &nbsp;&nbsp;&nbsp;&nbsp;srcs:&nbsp;[
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"AsynchronousCloseMonitor.cpp",
  &nbsp;&nbsp;&nbsp;&nbsp;],
  }

这里没有找到模块名称,则继续向上级目录查找编译脚本,上层找到 libcore/luni/Android.bp,配置如下

  package&nbsp;{
  &nbsp;&nbsp;default_applicable_licenses:&nbsp;["libcore_luni_license"],
  }

  //&nbsp;Added&nbsp;automatically&nbsp;by&nbsp;a&nbsp;large-scale-change
  //&nbsp;http://go/android-license-faq
  license&nbsp;{
  &nbsp;&nbsp;&nbsp;&nbsp;name:&nbsp;"libcore_luni_license",
  &nbsp;&nbsp;&nbsp;&nbsp;visibility:&nbsp;[":__subpackages__"],
  &nbsp;&nbsp;&nbsp;&nbsp;license_kinds:&nbsp;[
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"SPDX-license-identifier-Apache-2.0",
  &nbsp;&nbsp;&nbsp;&nbsp;],
  &nbsp;&nbsp;&nbsp;&nbsp;license_text:&nbsp;[
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"license.html",
  &nbsp;&nbsp;&nbsp;&nbsp;],
  }

还是没找到名称继续往上查找 libcore/Android.bp

  license&nbsp;{
  &nbsp;&nbsp;&nbsp;&nbsp;name:&nbsp;"libcore_license",
  &nbsp;&nbsp;&nbsp;&nbsp;visibility:&nbsp;[":__subpackages__"],
  &nbsp;&nbsp;&nbsp;&nbsp;license_kinds:&nbsp;[
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"SPDX-license-identifier-Apache-2.0",
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"SPDX-license-identifier-BSD",
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"SPDX-license-identifier-GPL",
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"SPDX-license-identifier-GPL-2.0",
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"SPDX-license-identifier-LGPL",
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"SPDX-license-identifier-MIT",
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"SPDX-license-identifier-OpenSSL",
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"SPDX-license-identifier-Unicode-DFS",
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"SPDX-license-identifier-W3C",
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"legacy_unencumbered",
  &nbsp;&nbsp;&nbsp;&nbsp;],
  &nbsp;&nbsp;&nbsp;&nbsp;license_text:&nbsp;[
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"LICENSE",
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"NOTICE",
  &nbsp;&nbsp;&nbsp;&nbsp;],
  }

  build&nbsp;=&nbsp;[
  &nbsp;&nbsp;&nbsp;&nbsp;"JavaLibrary.bp",
  &nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;这里有两个编译脚本
  &nbsp;&nbsp;&nbsp;&nbsp;"NativeCode.bp",
  ]

  genrule&nbsp;{
  &nbsp;&nbsp;&nbsp;&nbsp;name:&nbsp;"notices-for-framework-stubs-gen",
  &nbsp;&nbsp;&nbsp;&nbsp;tool_files:&nbsp;[
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"NOTICE",
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"ojluni/NOTICE",
  &nbsp;&nbsp;&nbsp;&nbsp;],
  &nbsp;&nbsp;&nbsp;&nbsp;cmd:&nbsp;"cp&nbsp;-f&nbsp;$(location&nbsp;NOTICE)&nbsp;$(genDir)/NOTICES/libcore-NOTICE&nbsp;&&&nbsp;cp&nbsp;-f&nbsp;$(location&nbsp;ojluni/NOTICE)&nbsp;$(genDir)/NOTICES/ojluni-NOTICE",
  &nbsp;&nbsp;&nbsp;&nbsp;out:&nbsp;[
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"NOTICES/libcore-NOTICE",
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"NOTICES/ojluni-NOTICE",
  &nbsp;&nbsp;&nbsp;&nbsp;],
  }

  java_library&nbsp;{
  &nbsp;&nbsp;&nbsp;&nbsp;name:&nbsp;"art-notices-for-framework-stubs-jar",
  &nbsp;&nbsp;&nbsp;&nbsp;visibility:&nbsp;[
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"//art/build/sdk",
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"//frameworks/base",
  &nbsp;&nbsp;&nbsp;&nbsp;],
  &nbsp;&nbsp;&nbsp;&nbsp;java_resources:&nbsp;[
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;":notices-for-framework-stubs-gen",
  &nbsp;&nbsp;&nbsp;&nbsp;],
  &nbsp;&nbsp;&nbsp;&nbsp;sdk_version:&nbsp;"core_current",
  }

查看 NativeCode.bp

  cc_library_shared&nbsp;{
  &nbsp;&nbsp;&nbsp;&nbsp;name:&nbsp;"libjavacore",
  &nbsp;&nbsp;&nbsp;&nbsp;visibility:&nbsp;[
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"//art/build/apex",
  &nbsp;&nbsp;&nbsp;&nbsp;],
  &nbsp;&nbsp;&nbsp;&nbsp;apex_available:&nbsp;[
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"com.android.art",
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"com.android.art.debug",
  &nbsp;&nbsp;&nbsp;&nbsp;],
  &nbsp;&nbsp;&nbsp;&nbsp;defaults:&nbsp;[
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"core_native_default_flags",
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"core_native_default_libs",
  &nbsp;&nbsp;&nbsp;&nbsp;],
  &nbsp;&nbsp;&nbsp;&nbsp;srcs:&nbsp;[
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;":luni_native_srcs",
  &nbsp;&nbsp;&nbsp;&nbsp;],
  &nbsp;&nbsp;&nbsp;&nbsp;shared_libs:&nbsp;[
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"libandroidio",
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"libbase",
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"libcrypto",
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"libicu",
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"libexpat",
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"libnativehelper",
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"libz",
  &nbsp;&nbsp;&nbsp;&nbsp;],
  &nbsp;&nbsp;&nbsp;&nbsp;static_libs:&nbsp;[
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"libziparchive",
  &nbsp;&nbsp;&nbsp;&nbsp;],
  }

最终找到该名称为 libjavacore.so

  1. 根据代码位置猜测,或者直接在 maps 文件里面查找哪些已经加载的可疑的库,目前查找到系统有关的库包含如下几个
  • libjavacore.so 与文件重定向、文件状态、exec 执行有关
  • libnativehelper.so 与动态加载有关
  • libnativeloader.so Android 7 以上动态加载有关
  • libart.so 与文件重定向、动态加载有关
  • libopenjdk.so 与文件重定向、文件状态有关
  • libopenjdkjvm.so 与文件访问有关
  • libandroid_runtime.so 与文件访问有关
  • libcutils.so 与 SystemProperties 访问有关
  1. 如果有遗漏的库可以调用 NativeHook.relinkLibrary() 重新重定位该库

其它模块调用

查看 FakeXposed[19] 说明文档

软件使用截图

  • 软件状态
  • 应用配置,长按开启/关闭
  • 对应功能配置

参考资料

[1]

FakeXposed: https://github.com/sanfengAndroid/FakeXposed

[3]

fake-linker: https://github.com/sanfengAndroid/fake-linker

[4]

HookSystemComponent: https://github.com/sanfengAndroid/FakeXposed/blob/main/app/src/main/java/com/sanfengandroid/xp/hooks/HookSystemComponent.java

[5]

ActivityThread.getPackageManager: https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/app/ActivityThread.java;l=2185;bpv=0;bpt=0

[6]

ActivityManager.IActivityManagerSingleton: https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/app/ActivityManager.java;drc=master;l=4553?q=ActivityManager&ss=android%2Fplatform%2Fsuperproject

[7]

android_os_SystemProperties.cpp: https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/jni/android_os_SystemProperties.cpp

[8]

HookRuntime: https://github.com/sanfengAndroid/FakeXposed/blob/main/app/src/main/java/com/sanfengandroid/xp/hooks/HookRuntime.java

[9]

hook_io: https://github.com/sanfengAndroid/FakeXposed/blob/main/app/src/main/cpp/hook/hook_io.cpp

[10]

hook_syscall: https://github.com/sanfengAndroid/FakeXposed/blob/main/app/src/main/cpp/hook/hook_syscall.cpp

[11]

hook_exec: https://github.com/sanfengAndroid/FakeXposed/blob/main/app/src/main/cpp/hook/hook_exec.cpp

[12]

hook_time: https://github.com/sanfengAndroid/FakeXposed/blob/main/app/src/main/cpp/hook/hook_time.cpp

[13]

hook_unistd: https://github.com/sanfengAndroid/FakeXposed/blob/main/app/src/main/cpp/hook/hook_unistd.cpp

[14]

hook_stat: https://github.com/sanfengAndroid/FakeXposed/blob/main/app/src/main/cpp/hook/hook_stat.cpp

[15]

io_redirect: https://github.com/sanfengAndroid/FakeXposed/blob/main/app/src/main/cpp/hook/io_redirect.cpp

[16]

hook_dlfcn: https://github.com/sanfengAndroid/FakeXposed/blob/main/app/src/main/cpp/hook/hook_dlfcn.cpp

[17]

hook_stat: https://github.com/sanfengAndroid/FakeXposed/blob/main/app/src/main/cpp/hook/hook_stat.cpp

[18]

hook_dlfcn: https://github.com/sanfengAndroid/FakeXposed/blob/main/app/src/main/cpp/hook/hook_dlfcn.cpp

[19]

FakeXposed: https://github.com/sanfengAndroid/FakeXposed

项目案例

推荐阅读

编译官方frida源码

魔改frida到绕过检测的思路


免责声明:

本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。

任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。

本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我

本文转载自:哆啦安全 《FakeXposed最强屏蔽Xposed、Root检测,自定义maps、文件重定向等支持Android5~14》

评论:0   参与:  0