生活随笔
收集整理的這篇文章主要介紹了
进击的Android Hook 注入术《三》
小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
目錄(?) [-]
繼續(xù) Component Injection 原理 示例二 comdemohost comdemoinject 繞過(guò)ClassLoader雙親委托 輸出 最后
繼續(xù) 在《二》詳細(xì)介紹了通過(guò)ptrace實(shí)現(xiàn)注入的技術(shù)方案,在這個(gè)章節(jié)里,我再介紹一種Android上特有的注入技術(shù),我命其名為——Component Injection。顧名思義,這種方式是跟Android的組件相關(guān)的,詳細(xì)見(jiàn)下面敘述。
Component Injection 原理 在android的開(kāi)發(fā)者文檔里,對(duì)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.
從描述上可以發(fā)現(xiàn),當(dāng)兩個(gè)應(yīng)用,它們簽名同樣且具備相同的shareduserID,它們之間只有一個(gè)組件的android:process是相同的,那么這兩個(gè)組件之間的互動(dòng)可以發(fā)生在同一個(gè)進(jìn)程里。這里所說(shuō)的同一個(gè)進(jìn)程,其實(shí)就是進(jìn)程注入的效果的了。
示例二 示例二同樣包含兩部分代碼,分別是com.demo.host和com.demo.inject,它們的代碼都非常簡(jiǎn)單,如下所示:
com.demo.host 先看看host的manifest.xml的配置
[html] view plain
copy < 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 > ?? 關(guān)鍵代碼
[java] view plain
copy package ?com.demo.host;???? import ?android.app.Activity;??import ?android.content.ContentResolver;??import ?android.net.Uri;??import ?android.os.Bundle;??import ?android.util.Log;???? ? ? ? ? ?? 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;?? ????}?? ?? ????@Override ?? ????protected ?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一啟動(dòng),就馬上調(diào)用ContentResolver的query,這個(gè)正是Inject里的ContentProvider組件。
com.demo.inject manifest.xml
[html] view plain
copy < 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 > ?? 關(guān)鍵代碼
[java] view plain
copy <span?style="white-space:pre" >????</span>@Override ?? ????public ?Cursor?query(Uri?arg0,?String[]?arg1,?String?arg2,?String[]?arg3,?String?arg4)?{?? ?? ????????final ?Timer?timer?=?new ?Timer("demo" );?? ????????timer.schedule(new ?TimerTask()?{?? ?? ????????????@Override ?? ????????????public ?void ?run()?{?? ????????????????try ?{?? ????????????????????Log.i("TTT" ,?">>>>>>>>>>>>>I?am?in,?I?am?a?bad?boy!!!!<<<<<<<<<<<<<<\n" );?? ?????????????????????? ????????????????????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中,當(dāng)query被調(diào)用后,會(huì)等待5s,然后通過(guò)反射調(diào)用host的MainActivity.setA方法,修改打印的數(shù)值。
繞過(guò)ClassLoader雙親委托 細(xì)心的朋友會(huì)發(fā)現(xiàn),inject的代碼中,獲取MainActivity的Class,并不是直接通過(guò)Class.forName("com.demo.host.MainActivity")獲取到,而是先獲取到全局Context(即Application對(duì)象),然后再調(diào)用其ClassLoader來(lái)間接獲取得的,為什么要這樣呢?我我們知道,Java中每個(gè)class都是通過(guò)雙親委托機(jī)制加載的,這方面的內(nèi)容可以參考http://blog.csdn.net/xyang81/article/details/7292380,下面我畫(huà)出示意圖:
當(dāng)我們嘗試在DemoContentProvider通過(guò)Class.forNmae尋找MainActivity時(shí),必然會(huì)拋ClassNotFoundException。唯一可行的方案是找到host的PathClassLoader,然后通過(guò)這個(gè)ClassLoader尋找MainActivity。我們需要尋找的變量需要滿(mǎn)足如下條件:
這個(gè)變量必須由host產(chǎn)生的; 這個(gè)變量必須是全局的,而且其引用會(huì)保存在BootClassLoader(也就是Android SDK中的某個(gè)引用); 可以通過(guò)反射機(jī)制讀取到; 很自然的,想到了host的Application對(duì)象。通過(guò)閱讀源碼,發(fā)現(xiàn)可以通過(guò)下面的方式讀取到Application對(duì)象:
如果是System_Process,可以通過(guò)如下方式獲取 [java] view plain
copy Context?context?=?ActivityThread.mSystemContext?? 如果是非System_Process(即普通的Android進(jìn)程),可以通過(guò)如下方式獲取 [java] view plain
copy Context?context?=?((ApplicationThread)RuntimeInit.getApplicationObject()).app_obj.this $0 ?? 輸出 理解了上述的原理之后,我們?cè)倏纯词纠妮敵?
[plain] view plain
copy I/TTT?????(??633):?com.demo.inject?starts.?? I/TTT?????(??633):?com.demo.host?starts?? I/TTT?????(??633):?1?? I/TTT?????(??633):?2?? I/TTT?????(??633):?3?? I/TTT?????(??633):?4?? I/TTT?????(??633):?5?? I/TTT?????(??633):?>>>>>>>>>>>>>I?am?in,?I?am?a?bad?boy!!!!<<<<<<<<<<<<<<?? I/TTT?????(??633):?998?? I/TTT?????(??633):?999?? I/TTT?????(??633):?1000?? I/TTT?????(??633):?1001?? I/TTT?????(??633):?1002?? I/TTT?????(??633):?1003?? 從前二行就可以看出,這兩個(gè)組件都是運(yùn)行在同一個(gè)進(jìn)程的。從第5秒開(kāi)始,打印的數(shù)據(jù)開(kāi)始發(fā)生變化,證明我們的注入邏輯生效了。 文中的示例代碼,大家可以到https://github.com/boyliang/Component_Injection下載
最后 ComponentInjection的好處是不需要ROOT權(quán)限,但其使用限制也非常多。但如果跟MaskterKey漏洞結(jié)合起來(lái)用,那效果還是相當(dāng)驚艷的。我們知道,Zygote進(jìn)程會(huì)接收來(lái)自system_process的命令,其中比較關(guān)鍵的信息有uid, gid, gids, classpath, runtime-init等等,這些信息是決定了Zygote子進(jìn)程的加載容器以及所從屬的uid。
通過(guò)MasterKey漏洞我們可以偽造系統(tǒng)的Setting包,Setting與system_process的配置正好符合我所說(shuō)的ComponentInjection條件,因此利用這種方式,可以注入到system_process進(jìn)程,進(jìn)而控制傳遞給Zygote的參數(shù)。其中classpath和runtime-init是加載容器的配置,classpath是指向一個(gè)dex文件的路徑,runtime-init是其main函數(shù)所在的類(lèi)名,通過(guò)指定每個(gè)App的加載容器,就可以很巧妙的控制了所有普通用戶(hù)的進(jìn)程的環(huán)境。
LBE 曾經(jīng)就是利用這種技術(shù)實(shí)現(xiàn)主動(dòng)防御的,更詳細(xì)的介紹可訪問(wèn)http://safe.baidu.com/2013-10/lbe-root.html,不過(guò)這個(gè)文章分析得并不到位,最關(guān)鍵的環(huán)節(jié)即ComponentInjection并沒(méi)有提及,結(jié)合的我分享,算是做一個(gè)完美的補(bǔ)充吧。
這一章節(jié)里,介紹了一種Android特有的注入技術(shù),通過(guò)一些小技巧繞過(guò)了Java的雙父委托機(jī)制。而且找到了可以輕松找到Application對(duì)象的方法,這個(gè)對(duì)象在Android開(kāi)發(fā)中可以是至關(guān)重要的。在接下來(lái)的《四》里,我會(huì)詳細(xì)介紹如何利用JNI獲取JNIEnv指針,再通過(guò)JNI找到DexCloassLoader加載DEX文件。
原文地址:http://blog.csdn.net/l173864930/article/details/38459449
總結(jié)
以上是生活随笔 為你收集整理的进击的Android Hook 注入术《三》 的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
如果覺(jué)得生活随笔 網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔 推薦給好友。