在之前的一篇文章中已經介紹了 Android中的應用啟動流程,這個流程一定要理解透徹,這樣我們才可以進行后續的Hook操作,在之前還介紹了Android中如何Hook系統的剪切板服務實現方法的攔截效果,實現原理就是:
1、先找到Hook點,這個一般是分析源碼來得到,而一般的Hook點都是靜態變量或者是單例方法。
2、構造一個需要攔截的代理對象,需要的條件是代理的對象必須實現一個接口,其次就是需要獲取到原始對象實例
有了這兩步就是用反射機制,把代理對象替換源對象即可,然后在InvocationHandler的invoke回調方法中進行制定方法的攔截。
那么有了之前的Hook知識點基礎和啟動流程分析,今天我們就要開始說Hook系統AMS服務的知識點的了,因為這個服務和應用啟動相關的,所以一定要理解了應用啟動流程。然后按照之前Hook系統的剪切板服務之后,在任何Hook操作之前步得先找到Hook點,那下面就開始找這個點。
還是按照之前的邏輯,如果我們想攔截方法,肯定是Hook本地化服務對象,比如之前Hook掉系統的剪切板的方法,肯定是Hook掉IClipboard對象,當時還記得找到的Hook點步驟思路是:
1、首先通過反射從ServiceManager中獲取到Clipboard的遠端Binder對象
2、Hook掉這個Binder對象,攔截他的queryLocalInterface方法,在這個方法中再次Hook掉本地化服務對象
3、然后在攔截本地化服務對象的具體實現方法即可
在這個過程中我們發現需要做兩次Hook操作,因為我們攔截的對象是本地化服務對象,我們實際在使用服務的時候也是這個對象,而在步得先Hook掉服務對應的遠端Binder對象,然后在Hook掉服務對象即可。
那么今天我們介紹如何Hook掉系統的AMS服務,來實現攔截Activity的啟動流程,那么我們依然可以采用上面的這種方式來進行操作,但是我們還可以采用另外一種更方便的技術來做操作,在上面的三個步驟中,會發現次Hook其實是可以省略的,因為我們在Hook掉AMS服務的時候,可以發現在系統中某一個地方,有AMS本地化實際對象IActivityManager,我們只要找到他就可以先獲取對象,然后在通過反射進行Hook代理對象的重置即可。
所以在之前剪切板服務沒有這么做,是因為我們并沒有發現系統中有一個地方可以去獲取IClipboardManager本地化實際對象,所以在以后的Hook操作中,一定要記住這點,如果能夠通過分析Android源碼,獲取服務的本地化對象那么就可以直接Hook即可,如果不行,那只能通過兩次Hook操作了。
下面我們通過源碼來找到AMS本地化實際對象
我們在前面一篇文章中介紹了Android中應用的啟動流程,當時有這張圖:
當時分析的時候也說了,這個是整個應用啟動的處遠程通信的地方,從圖上可以清晰的看到,這里的對象之間的關系,那么我們其實就是需要Hook本地端的中間者,也就是ActivityManagerProxy對象,那么我們如何去查找這個對象呢?因為我們的目的就是要攔截startActivity方法,那么可以通過這個方法跟進源碼,這里就不在說明了,在之前的啟動流程中已經講解的非常清楚了,我們終會跟進到ActivityManagerNative類中的getDefault方法:
而這里的gDefault是Singleton類型實現單例模式功能的:
好了,這里發現其實內部就是先從ServiceManager中獲取到遠程的AMS的Binder對象,然后在轉化成本地操作的實際對象,其實就是ActivityManagerProxy類型的,那么這里我們可以省略Hook遠端的Binder對象操作了,同時會發現這里的方法和變量都是static類型的,那么對于反射來說就非常有利了,之前也說過在Hook技術中:對于單例方法和static變量以及static方法好用反射了!
那么我們就可以這么弄:
1、使用反射機制獲取到AMS的本地化對象
2、然后在使用動態代理技術,生成一個代理對象
3、后把代理對象在重置回去即可
在之前介紹動態代理生成代理對象也說到,只要符合兩個規則即可:
1》有原始對象,這里正好是步中獲取到的本地化對象
2》原始對象必須實現接口,這里也正好符合,因為ActivityManagerProxy是實現了IActivityManager接口類型的
下面就開始操作吧:
這里我們可以攔截startActivity的方法:
我們運行程序,但是需要需要注意哦,一般會先把Hook操作就是上面的反射工作放到開始的地方,好是Application的attachBaseContext方法中,因為這個時機是早的。
哈哈,看到了,這里我們攔截成功了,而且可以獲取攔截的參數,從中可以知道啟動的Activity信息了。
到這里我們就已經實現可以攔截系統中Activity啟動的流程了,那么下面咋們就來實踐一下,我們做一個案例,就是啟動一個沒有在AndroidManifest.xml中聲明的Activity:
這個思路可以是這樣,上面已經攔截了啟動Activity流程,也得到了啟動參數intent信息,那么就在這里,我們可以自己構造一個假的Activity信息的intent,然后啟動:
然后我們啟動activity的代碼:
這里啟動的是TargetActivity,但是需要在AndroidManifest.xml中聲明StubActivity:
然后啟動,會發現沒有報錯,而是啟動成功了StubActivity了。
那么這里其實想想應該沒問題的,雖然我們代碼中啟動的是TargetActivity,但是我們進行攔截然后替換成了StubActivity,而StubActivity在AndroidManifest.xml中聲明了,也不會報錯,所以運行成功了。
從上面的簡單例子可以看到,我們沒有在AndroidManifest.xml中聲明TargetActivity,運行也沒有報錯的,原因是因為我們替換了StubActivity,而StubActivity聲明了,運行也是不會報錯的。那么如果我們把StubActivity的聲明去了,運行會報錯嗎?
答案是肯定會報錯的,而且報錯的信息是:StubActivity沒有聲明,而不是TargetActivity沒有聲明,因為我們已經替換了。那么這里就引出第二個問題了,關于Activity啟動的遠端操作具體如何?
這個就需要去看ActivityManagerService源碼了,在這里會做Activity啟動前的校驗工作,而這個是運行在遠端的system_server進程中的。有的同學說那繼續在這里Hook呀,要是把這個校驗工作也給替換了,就可以實現真正意義上無需聲明Activity就可以啟動了,做當然可以做,但是不是本文重點,因為這個校驗工作實在system_server進程中的,如果要Hook的話,就需要注入到進程中,而注入system_server系統進程是在這里講到:Android中通過系統進程注入攔截應用行為。
上面的一個例子中會發現雖然攔截了,也替換了啟動參數信息,但是發現然并卵,因為我們終想啟動的還是TargetActivity,而不是替換的StubActivity,所以這里還需要在后面把StubActivity給換回來。那么這里就要去分析Activity校驗之后開始啟動的邏輯了,而這里就要涉及到之前文章分析的啟動流程中第二個遠程通信:
這個通信其實是為了Activity的生命周期工作,而在這個過程中和上面的AMS的通信不一樣,這里的遠端服務是在應用進程中,而本地服務實在系統進程中,也就是說系統進程會發送一些生命周期命令給應用進程,而具體實際的處理邏輯實在應用中的。
那么我們就又要開始進行Hook了,按照之前的邏輯,這里我們需要Hook掉ApplicationThreadProxy即可,但是這里不需要這么弄,因為還有一種更為方便的路徑,就是在終的處理地方進行攔截,這個就要分析ActivityThread類了,因為ApplicationThread類就是在這里定義,也就說具體處理邏輯就在這里,而在之前分析流程的時候介紹過了,其實終的處理都是在ActivityThread中的Handler中:
看到了,在這里就是我們好的還原時機,那么我們就需要Hook掉這個Handler機制了,但是我們在之前介紹了Handler源碼內部機制解析 知道,終處理消息dispatchMessage方法的處理邏輯:
1、如果傳遞的Message本身就有callback,那么直接使用Message對象的callback方法;
2、如果Handler類的成員變量mCallback存在,那么首先執行這個mCallback回調;
3、如果mCallback的回調返回true,那么表示消息已經成功處理;直接結束。
4、如果mCallback的回調返回false,那么表示消息沒有處理完畢,會繼續使用Handler類的handleMessage方法處理消息
有了這個邏輯,那么我們如果要攔截Handler的handleMessage方法,只需要構造一個變量mCallback即可:
這里我們就可以自定義一個Callback,然后設置到Handler的變量中,后在自定義的Callback中處理消息實現攔截:
到這里我們就可以看一下實現的效果了:
看到了,這里就可以完全的實現了沒有聲明的TargetActivity的啟動,但是這里還是需要StubActivity的聲明,而對于StubActivity也叫作代理Activity,后續如果在啟動Activity都可以把他當做代理Activity,而這個代理大的作用就是欺騙AMS,讓AMS檢測通過即可,
好了,下面咋們就來總結一下攔截流程:
1、首先通過源碼分析得知,在ActivityManagerNative類中有一個靜態方法和靜態變量維護這一個AMS本地化對象
2、那么就可以使用反射機制獲取到這個AMS本地化對象,也就是ActivityManagerProxy。
3、而我們正好想要攔截的方法都是在這個對象中,所以只需要Hook掉這個對象即可,因為使用動態代理生成代理對象必須符合兩個規則:
1》有原始對象,這里就是ActivityManagerProxy對象
2》原始對象必須實現一個接口,正好ActivityManagerProxy對象實現了IActivityManager接口
那么這里既可以生成一個ActivityManagerProxy的代理對象,然后在使用反射把這個對象重置回去。
4、然后就可以在InvocationHandler中進行方法攔截了,本文中只對啟動方法做了攔截操作
5、我們攔截方法成功之后,就實現了一個簡單的功能,對啟動的Activity進行調換,也就是通過修改參數實現的。
6、后我們要完全實現真正意義上的攔截,還得在后把原始Activity給替換回來。這里還需要攔截系統處理Activity的生命周期邏輯的Handler機制,這里主要借助Handler本身的處理消息機制,構造一個回調,然后重置變量即可。
在這整個過程中,我們其實可以發現,首先咋們得“貍貓換太子”,騙過AMS的檢測,然后在后生不知鬼不覺的在換回來,整個過程AMS完全無感知:
到這里我們就實現了Android中無需聲明Activity就可以啟動的效果,那么這個有什么用呢?
1、現在很多應用有時候會集成微信和支付寶支付功能,但是這時候就需要在AndroidManifest.xml中聲明一些Activity,而惡心的是,有些市場在審核個人開發者提交的app的時候,如果有支付功能是不能審核通過的,這個應該也是為了防止惡意扣費吧,那么對于個人開發者就是沒轍了?想想路子還是有的:
1》可以先上一個沒有支付功能的,先到市場再說,然后在自己的app中自升級帶有支付功能的即可,完全繞過市場審核了,但是這種方式是需要自升級工作。
2》采用插件化開發,把支付功能SDK做成動態加載,這樣市場在掃描包的時候是找不到指定支付api就可以的,同時還得把AndroidManifest.xml中的支付Activity給隱藏起來躲避檢測,那么如何隱藏就用到了這里的技術了,咋們可以自定義一個代理假的Activity,然后通過這種方式啟動真正的支付Activity即可。
2、上面也提到了,在插件化開發中處理Activity的生命周期問題,也是可以采用這種方式去做處理的。
通過之前的兩篇文章了解了如何Hook掉系統的服務,規則和方法都是大同小異,主要有兩種情況:
1、如果在系統中找不到指定服務的本地化對象,即沒法使用反射機制獲取,就需要進行兩次Hook工作,先Hook遠程服務的Binder對象,在Hook掉本地化服務對象攔截指定方法。像之前的剪切板服務ClipboardManager。
2、如果能夠直接使用反射機制獲取到本地化服務對象,那么只需要一次Hook操作即可完成攔截,像這里的AMS服務。
關于Android中的Hook技術其實全局可以分為兩種:
1、種是獲取root權限,利用進程注入技術,修改指定函數指針,達到攔截效果,這種方式可以攔截系統所有的服務。對系統所有應用有效果。
2、無需root權限,利用反射機制和動態代理技術,達到攔截效果,這種方式只能對本應用有效果。
本站文章版權歸原作者及原出處所有 。內容為作者個人觀點, 并不代表本站贊同其觀點和對其真實性負責,本站只提供參考并不構成任何投資及應用建議。本站是一個個人學習交流的平臺,網站上部分文章為轉載,并不用于任何商業目的,我們已經盡可能的對作者和來源進行了通告,但是能力有限或疏忽,造成漏登,請及時聯系我們,我們將根據著作權人的要求,立即更正或者刪除有關內容。本站擁有對此聲明的最終解釋權。