http://blog.csdn.net/yzzst/article/details/47659987
XPosed是与Cydia其名的工具,它能够让Android设备在没有修改源码的情况下修改系统中的API运行结果。我们通常称之为:God Mode(上帝模式)。
之前享大家分享了Xposed的基础,Xposed的作用和最简单的用法。那么,它的原理和它的内部构造是如何构成的?下面,我们从Github上看看,rovo89大神是如何制作的。
rovo89的github地址:https://github.com/rovo89
在主页上我们看到了,xposed其实主要是由三个项目来组成的,如下图所示;
三个分别是:
项目 | 说明 |
---|---|
Xposed | Xposed框架的native部分(主要是改性app_process二进制文件) |
XposedInstaller | Xposed框架的Android端本地管理,环境架构搭建,以及第三方module资源下载的工具。 |
XposedBridge | Xposed向开发者提供的API与相应的工具类库 |
XposedInstaller的构成
Xposed项目使我们最常用的项目,当然,他也是构造Xposed的核心部分。(也许你会说,其实是xposed项目更重要,它主要是替换app_process,ok我们后面再说它)。
如下图所示,是我们在XPosedInstaller apk中见到的,安装xposed框架的界面。
InstallerFragment我们能够在其中找到install方法,其中主要就是针对使用不同方式的将自定义的app_process文件替换掉系统的app_process文件。
<code class="hljs cs has-numbering"> <span class="hljs-comment">/** * xposed install * @return 安装成功返回true,否则false */</span> <span class="hljs-keyword">private</span> boolean <span class="hljs-title">install</span>() { <span class="hljs-comment">// 获取安装的方式,直接写入 or 使用 recovery进行安装</span> final <span class="hljs-keyword">int</span> installMode = getInstallMode(); <span class="hljs-comment">// 检查获取Root权限</span> <span class="hljs-keyword">if</span> (!startShell()) <span class="hljs-keyword">return</span> <span class="hljs-keyword">false</span>; List<String> messages = <span class="hljs-keyword">new</span> LinkedList<String>(); boolean showAlert = <span class="hljs-keyword">true</span>; <span class="hljs-keyword">try</span> { messages.add(getString(R.<span class="hljs-keyword">string</span>.sdcard_location, XposedApp.getInstance().getExternalFilesDir(<span class="hljs-keyword">null</span>))); messages.add(<span class="hljs-string">""</span>); messages.add(getString(R.<span class="hljs-keyword">string</span>.file_copying, <span class="hljs-string">"Xposed-Disabler-Recovery.zip"</span>)); <span class="hljs-comment">// 把Xposed-Disabler-Recovery.zip文件 从asset copy到sdcard中</span> <span class="hljs-keyword">if</span> (AssetUtil.writeAssetToSdcardFile(<span class="hljs-string">"Xposed-Disabler-Recovery.zip"</span>, <span class="hljs-number">00644</span>) == <span class="hljs-keyword">null</span>) { messages.add(<span class="hljs-string">""</span>); messages.add(getString(R.<span class="hljs-keyword">string</span>.file_extract_failed, <span class="hljs-string">"Xposed-Disabler-Recovery.zip"</span>)); <span class="hljs-keyword">return</span> <span class="hljs-keyword">false</span>; } <span class="hljs-comment">// 将编译后的app_process二进制文件,从asset文件夹中,copy到/data/data/de.robv.android.xposed.installer/bin/app_process下</span> File appProcessFile = AssetUtil.writeAssetToFile(APP_PROCESS_NAME, <span class="hljs-keyword">new</span> File(XposedApp.BASE_DIR + <span class="hljs-string">"bin/app_process"</span>), <span class="hljs-number">00700</span>); <span class="hljs-keyword">if</span> (appProcessFile == <span class="hljs-keyword">null</span>) { showAlert(getString(R.<span class="hljs-keyword">string</span>.file_extract_failed, <span class="hljs-string">"app_process"</span>)); <span class="hljs-keyword">return</span> <span class="hljs-keyword">false</span>; } <span class="hljs-keyword">if</span> (installMode == INSTALL_MODE_NORMAL) { <span class="hljs-comment">// 普通安装模式</span> <span class="hljs-comment">// 重新挂载/system为rw模式</span> messages.add(getString(R.<span class="hljs-keyword">string</span>.file_mounting_writable, <span class="hljs-string">"/system"</span>)); <span class="hljs-keyword">if</span> (mRootUtil.executeWithBusybox(<span class="hljs-string">"mount -o remount,rw /system"</span>, messages) != <span class="hljs-number">0</span>) { messages.add(getString(R.<span class="hljs-keyword">string</span>.file_mount_writable_failed, <span class="hljs-string">"/system"</span>)); messages.add(getString(R.<span class="hljs-keyword">string</span>.file_trying_to_continue)); } <span class="hljs-comment">// 查看原有的app_process文件是否已经备份,如果没有备份,现将原有的app_process文件备份一下</span> <span class="hljs-keyword">if</span> (<span class="hljs-keyword">new</span> File(<span class="hljs-string">"/system/bin/app_process.orig"</span>).exists()) { messages.add(getString(R.<span class="hljs-keyword">string</span>.file_backup_already_exists, <span class="hljs-string">"/system/bin/app_process.orig"</span>)); } <span class="hljs-keyword">else</span> { <span class="hljs-keyword">if</span> (mRootUtil.executeWithBusybox(<span class="hljs-string">"cp -a /system/bin/app_process /system/bin/app_process.orig"</span>, messages) != <span class="hljs-number">0</span>) { messages.add(<span class="hljs-string">""</span>); messages.add(getString(R.<span class="hljs-keyword">string</span>.file_backup_failed, <span class="hljs-string">"/system/bin/app_process"</span>)); <span class="hljs-keyword">return</span> <span class="hljs-keyword">false</span>; } <span class="hljs-keyword">else</span> { messages.add(getString(R.<span class="hljs-keyword">string</span>.file_backup_successful, <span class="hljs-string">"/system/bin/app_process.orig"</span>)); } mRootUtil.executeWithBusybox(<span class="hljs-string">"sync"</span>, messages); } <span class="hljs-comment">// 将项目中的自定义app_process文件copy覆盖系统的app_process,修改权限</span> messages.add(getString(R.<span class="hljs-keyword">string</span>.file_copying, <span class="hljs-string">"app_process"</span>)); <span class="hljs-keyword">if</span> (mRootUtil.executeWithBusybox(<span class="hljs-string">"cp -a "</span> + appProcessFile.getAbsolutePath() + <span class="hljs-string">" /system/bin/app_process"</span>, messages) != <span class="hljs-number">0</span>) { messages.add(<span class="hljs-string">""</span>); messages.add(getString(R.<span class="hljs-keyword">string</span>.file_copy_failed, <span class="hljs-string">"app_process"</span>, <span class="hljs-string">"/system/bin"</span>)); <span class="hljs-keyword">return</span> <span class="hljs-keyword">false</span>; } <span class="hljs-keyword">if</span> (mRootUtil.executeWithBusybox(<span class="hljs-string">"chmod 755 /system/bin/app_process"</span>, messages) != <span class="hljs-number">0</span>) { messages.add(<span class="hljs-string">""</span>); messages.add(getString(R.<span class="hljs-keyword">string</span>.file_set_perms_failed, <span class="hljs-string">"/system/bin/app_process"</span>)); <span class="hljs-keyword">return</span> <span class="hljs-keyword">false</span>; } <span class="hljs-keyword">if</span> (mRootUtil.executeWithBusybox(<span class="hljs-string">"chown root:shell /system/bin/app_process"</span>, messages) != <span class="hljs-number">0</span>) { messages.add(<span class="hljs-string">""</span>); messages.add(getString(R.<span class="hljs-keyword">string</span>.file_set_owner_failed, <span class="hljs-string">"/system/bin/app_process"</span>)); <span class="hljs-keyword">return</span> <span class="hljs-keyword">false</span>; } } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (installMode == INSTALL_MODE_RECOVERY_AUTO) { <span class="hljs-comment">// 自动进入Recovery</span> <span class="hljs-keyword">if</span> (!prepareAutoFlash(messages, <span class="hljs-string">"Xposed-Installer-Recovery.zip"</span>)) <span class="hljs-keyword">return</span> <span class="hljs-keyword">false</span>; } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (installMode == INSTALL_MODE_RECOVERY_MANUAL) { <span class="hljs-comment">// 手动进入Recovery</span> <span class="hljs-keyword">if</span> (!prepareManualFlash(messages, <span class="hljs-string">"Xposed-Installer-Recovery.zip"</span>)) <span class="hljs-keyword">return</span> <span class="hljs-keyword">false</span>; } File blocker = <span class="hljs-keyword">new</span> File(XposedApp.BASE_DIR + <span class="hljs-string">"conf/disabled"</span>); <span class="hljs-keyword">if</span> (blocker.exists()) { messages.add(getString(R.<span class="hljs-keyword">string</span>.file_removing, blocker.getAbsolutePath())); <span class="hljs-keyword">if</span> (mRootUtil.executeWithBusybox(<span class="hljs-string">"rm "</span> + blocker.getAbsolutePath(), messages) != <span class="hljs-number">0</span>) { messages.add(<span class="hljs-string">""</span>); messages.add(getString(R.<span class="hljs-keyword">string</span>.file_remove_failed, blocker.getAbsolutePath())); <span class="hljs-keyword">return</span> <span class="hljs-keyword">false</span>; } } <span class="hljs-comment">// copy XposedBridge.jar 到私有目录 XposedBridge.jar是Xposed提供的jar文件,负责在Native层与FrameWork层进行交互</span> messages.add(getString(R.<span class="hljs-keyword">string</span>.file_copying, <span class="hljs-string">"XposedBridge.jar"</span>)); File jarFile = AssetUtil.writeAssetToFile(<span class="hljs-string">"XposedBridge.jar"</span>, <span class="hljs-keyword">new</span> File(JAR_PATH_NEWVERSION), <span class="hljs-number">00644</span>); <span class="hljs-keyword">if</span> (jarFile == <span class="hljs-keyword">null</span>) { messages.add(<span class="hljs-string">""</span>); messages.add(getString(R.<span class="hljs-keyword">string</span>.file_extract_failed, <span class="hljs-string">"XposedBridge.jar"</span>)); <span class="hljs-keyword">return</span> <span class="hljs-keyword">false</span>; } mRootUtil.executeWithBusybox(<span class="hljs-string">"sync"</span>, messages); showAlert = <span class="hljs-keyword">false</span>; messages.add(<span class="hljs-string">""</span>); <span class="hljs-keyword">if</span> (installMode == INSTALL_MODE_NORMAL) { offerReboot(messages); } <span class="hljs-keyword">else</span> { offerRebootToRecovery(messages, <span class="hljs-string">"Xposed-Installer-Recovery.zip"</span>, installMode); } <span class="hljs-keyword">return</span> <span class="hljs-keyword">true</span>; } <span class="hljs-keyword">finally</span> { <span class="hljs-comment">// 删除busybox 工具库</span> AssetUtil.removeBusybox(); <span class="hljs-keyword">if</span> (showAlert) showAlert(TextUtils.<span class="hljs-keyword">join</span>(<span class="hljs-string">"\n"</span>, messages).trim()); } }</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li><li>32</li><li>33</li><li>34</li><li>35</li><li>36</li><li>37</li><li>38</li><li>39</li><li>40</li><li>41</li><li>42</li><li>43</li><li>44</li><li>45</li><li>46</li><li>47</li><li>48</li><li>49</li><li>50</li><li>51</li><li>52</li><li>53</li><li>54</li><li>55</li><li>56</li><li>57</li><li>58</li><li>59</li><li>60</li><li>61</li><li>62</li><li>63</li><li>64</li><li>65</li><li>66</li><li>67</li><li>68</li><li>69</li><li>70</li><li>71</li><li>72</li><li>73</li><li>74</li><li>75</li><li>76</li><li>77</li><li>78</li><li>79</li><li>80</li><li>81</li><li>82</li><li>83</li><li>84</li><li>85</li><li>86</li><li>87</li><li>88</li><li>89</li><li>90</li><li>91</li><li>92</li><li>93</li><li>94</li><li>95</li><li>96</li><li>97</li><li>98</li><li>99</li><li>100</li><li>101</li><li>102</li><li>103</li><li>104</li><li>105</li><li>106</li><li>107</li><li>108</li><li>109</li><li>110</li><li>111</li><li>112</li><li>113</li><li>114</li><li>115</li><li>116</li><li>117</li><li>118</li><li>119</li><li>120</li><li>121</li><li>122</li><li>123</li><li>124</li><li>125</li><li>126</li></ul>
ok,我们看完了代码,发现所有的工作都是为了app_process文件的替换。那么,系统中这个app_process是做什么的?为什么我们需要替换?替换成什么样?替换后对于我们么来说有什么帮助呢?
Xposed原理
我们在android的源码中的init.rc文件可以看到
<code class="hljs livecodeserver has-numbering">service zygote /<span class="hljs-keyword">system</span>/bin/app_process -Xzygote /<span class="hljs-keyword">system</span>/bin –zygote –start-<span class="hljs-keyword">system</span>-server <span class="hljs-built_in">socket</span> zygote stream <span class="hljs-number">666</span> onrestart <span class="hljs-built_in">write</span> /sys/android_power/request_state wake onrestart <span class="hljs-built_in">write</span> /sys/power/state <span class="hljs-command"><span class="hljs-keyword">on</span></span> onrestart restart media onrestart restart netd</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li></ul>
app_process是andriod app的启动程序(具体形式是zygote fork()调用一个 app_process作为Android app的载体)
Xposed的实现方案
针对Hook的不同进程来说又可以分为全局Hook与单个应用程序进程Hook,我们知道在Android系统中,应用程序进程都是由Zygote进程孵化出来的,而Zygote进程是由Init进程启动的。
Zygote进程在启动时会创建一个Dalvik虚拟机实例,每当它孵化一个新的应用程序进程时,都会将这个Dalvik虚拟机实例复制到新的应用程序进程里面去,从而使得每一个应用程序进程都有一个独立的Dalvik虚拟机实例。所以如果选择对Zygote进程Hook,则能够达到针对系统上所有的应用程序进程Hook,即一个全局Hook。如下图所示:
/*
* @author zhoushengtao(周圣韬)
* @since 2015年8月18 日 19:41:32
* @weixin stchou_zst
* @blog http://blog.csdn.net/yzzst
* @交流学习QQ群:341989536
* @私人QQ:445914891
/