進擊的Android注入術《三》,進擊android《三》
繼續在《二》詳細介紹了通過ptrace實現注入的技術方案,在這個章節裡,我再介紹一種Android上特有的注入技術,我命其名為——Component Injection。顧名思義,這種方式是跟Android的組件相關的,詳細見下面敘述。
Component Injection原理在android的開發人員文檔裡,對android:process的描述是這樣的:
android:process
The name of a process where all components of the application should run. Each component can override this default by setting its own process attribute.
By default, Android creates a process for an application when the first of its components needs to run. All components then run in that process. The name of the default process matches the package name set by the <manifest> element.
By setting this attribute to a process name that's shared with another application, you can arrange for components of both applications to run in the same process — but only if the two applications also share a user ID and be signed with the same certificate.
If the name assigned to this attribute begins with a colon (':'), a new process, private to the application, is created when it's needed. If the process name begins with a lowercase character, a global process of that name is created. A global process can be shared with other applications, reducing resource usage.
從描述上可以發現,當兩個應用,它們簽名同樣且具備相同的shareduserID,它們之間只有一個組件的android:process是相同的,那麼這兩個組件之間的互動可以發生在同一個進程裡。這裡所說的同一個進程,其實就是進程注入的效果的了。
樣本二樣本二同樣包含兩部分代碼,分別是com.demo.host和com.demo.inject,它們的代碼都非常簡單,如下所示:
com.demo.host先看看host的manifest.xml的配置
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.demo.host" android:sharedUserId="com.demo" android:versionCode="1" android:versionName="1.0" > <application android:name=".DemoApplication" android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:process="com.demo" android:theme="@style/AppTheme" > <activity android:name=".MainActivity" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="9" /></manifest>
關鍵代碼
package com.demo.host;import android.app.Activity;import android.content.ContentResolver;import android.net.Uri;import android.os.Bundle;import android.util.Log;/** * * @author boyliang * */public final class MainActivity extends Activity {private static int sA = 1;public static void setA(int a) {sA = a;}public static int getA() {return sA;}@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);ContentResolver resolver = getContentResolver();Uri uri = Uri.parse("content://demo_contentprovider");resolver.query(uri, null, null, null, null);new Thread() {public void run() {while (true) {Log.i("TTT", "" + getA());setA(getA() + 1);try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}};}.start();}}host一啟動,就馬上調用ContentResolver的query,這個正是Inject裡的ContentProvider組件。
com.demo.injectmanifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.demo.inject" android:sharedUserId="com.demo" android:versionCode="1" android:versionName="1.0" > <application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:process="com.demo" android:theme="@style/AppTheme" > <provider android:name=".DemoContentProvider" android:authorities="demo_contentprovider" android:exported="false" /> </application> <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="9" /></manifest>
關鍵代碼
<span style="white-space:pre"></span>@Overridepublic Cursor query(Uri arg0, String[] arg1, String arg2, String[] arg3, String arg4) {final Timer timer = new Timer("demo");timer.schedule(new TimerTask() {@Overridepublic void run() {try {Log.i("TTT", ">>>>>>>>>>>>>I am in, I am a bad boy!!!!<<<<<<<<<<<<<<\n");//Class<?> MainActivity_class = Class.forName("com.demo.host.MainActivity");Context context = ContexHunter.getContext();ClassLoader classloader = context.getClass().getClassLoader();Class<?> MainActivity_class = classloader.loadClass("com.demo.host.MainActivity");Method setA_method = MainActivity_class.getDeclaredMethod("setA", int.class);setA_method.invoke(null, 998);} catch (Exception e) {e.printStackTrace();}timer.cancel();}}, 5000);return null;}inject中,當query被調用後,會等待5s,然後通過反射調用host的MainActivity.setA方法,修改列印的數值。
繞過ClassLoader雙親委託細心的朋友會發現,inject的代碼中,擷取MainActivity的Class,並不是直接通過Class.forName("com.demo.host.MainActivity")擷取到,而是先擷取到全域Context(即Application對象),然後再調用其ClassLoader來間接擷取得的,為什麼要這樣呢?我我們知道,Java中每個class都是通過雙親委託機制載入的,這方面的內容可以參考http://blog.csdn.net/xyang81/article/details/7292380,下面我畫出:
當我們嘗試在DemoContentProvider通過Class.forNmae尋找MainActivity時,必然會拋ClassNotFoundException。唯一可行的方案是找到host的PathClassLoader,然後通過這個ClassLoader尋找MainActivity。我們需要尋找的變數需要滿足如下條件:
- 這個變數必須由host產生的;
- 這個變數必須是全域的,而且其引用會儲存在BootClassLoader(也就是Android SDK中的某個引用);
- 可以通過反射機制讀取到;
很自然的,想到了host的Application對象。通過閱讀源碼,發現可以通過下面的方式讀取到Application對象:
- 如果是System_Process,可以通過如下方式擷取
Context context = ActivityThread.mSystemContext
- 如果是非System_Process(即普通的Android進程),可以通過如下方式擷取
Context context = ((ApplicationThread)RuntimeInit.getApplicationObject()).app_obj.this$0
輸出理解了上述的原理之後,我們再看看樣本的輸出:
I/TTT ( 633): com.demo.inject starts.I/TTT ( 633): com.demo.host startsI/TTT ( 633): 1I/TTT ( 633): 2I/TTT ( 633): 3I/TTT ( 633): 4I/TTT ( 633): 5I/TTT ( 633): >>>>>>>>>>>>>I am in, I am a bad boy!!!!<<<<<<<<<<<<<<I/TTT ( 633): 998I/TTT ( 633): 999I/TTT ( 633): 1000I/TTT ( 633): 1001I/TTT ( 633): 1002I/TTT ( 633): 1003
從前二行就可以看出,這兩個組件都是運行在同一個進程的。從第5秒開始,列印的資料開始發生變化,證明我們的注入邏輯生效了。文中的範例程式碼,大家可以到https://github.com/boyliang/Component_Injection下載最後ComponentInjection的好處是不需要ROOT許可權,但其使用限制也非常多。但如果跟MaskterKey漏洞結合起來用,那效果還是相當驚豔的。我們知道,Zygote進程會接收來自system_process的命令,其中比較關鍵的資訊有uid, gid, gids, classpath, runtime-init等等,這些資訊是決定了Zygote子進程的載入容器以及所從屬的uid。
通過MasterKey漏洞我們可以偽造系統的Setting包,Setting與system_process的配置正好符合我所說的ComponentInjection條件,因此利用這種方式,可以注入到system_process進程,進而控制傳遞給Zygote的參數。其中classpath和runtime-init是載入容器的配置,classpath是指向一個dex檔案的路徑,runtime-init是其main函數所在的類名,通過指定每個App的載入容器,就可以很巧妙的控制了所有普通使用者的進程的環境。
LBE 曾經就是利用這種技術實現主動防禦的,更詳細的介紹可訪問http://safe.baidu.com/2013-10/lbe-root.html,不過這個文章分析得並不到位,最關鍵的環節即ComponentInjection並沒有提及,結合的我分享,算是做一個完美的補充吧。
這一章節裡,介紹了一種Android特有的注入技術,通過一些小技巧繞過了Java的雙父委託機制。而且找到了可以輕鬆找到Application對象的方法,這個對象在Android開發中可以是至關重要的。在接下來的《四》裡,我會詳細介紹如何利用JNI擷取JNIEnv指標,再通過JNI找到DexCloassLoader載入DEX檔案。
對於進擊的巨人的問題 知道的來
山奧組或同鄉組的情況:
阿尼結晶、胡佛被巨人群包圍生死不明、萊那因為鎧巨皮膚死亡的幾率幾乎不存在。
尤尼爾為救胡佛和萊那衝進巨人群中,生死不明。三人失蹤在牆壁外面,下落不明。
綁架艾倫的目的,記得好像是說需亞艾倫的力量吧!具體的沒解釋。
為什麼能變成巨人?
巨人化有兩種類型,第一種以艾倫為典型,注入了巨人化藥劑。
第二種以尤尼爾為典型,被變成巨人後恢複回人類。
根據調查兵團的推測,無腦巨人很有可能本來是人類。被獸型巨人變成了巨人!
艾倫的爸爸之後去哪了?
下落不明!老爸是巨人化研究者,如果沒死肯定是在為了對抗巨人做準備,個人想法。
牆壁裡面為什麼會有巨人?
山奧組的話,調查兵團的說法是瑪利亞淪陷時,混進來的。
無腦巨人的話,是獸型巨人爬牆進去以後出現的,
所以推測看你老家村的村民被變成了巨人。
康尼證言,一巨人臉型貌似康尼老媽。
現在漫畫還沒演到解密呢!仍然出於加密模式,
只透露了赫裡斯塔的身世,最近漫畫會透漏相關的秘密。
進擊的巨人11集以後的劇情
哎,看他們說的複雜的,其實是以前的人類製造的一種病毒,會讓人巨人化,但是失敗了,感染了很多人,沒有意識,而艾倫他們是注入了成功的病毒,那些吃人的巨人其實是以前被感染的人 艾倫變成巨人後攻擊那女的,然後就被某某人說教了就恢複意識了,然後拿石頭堵住了洞,就這麼簡單