攻防:如何防止动态hook绕过jni签名校验
攻
我們知道jni校驗(yàn)簽名也不可靠,可以被動(dòng)態(tài)hook繞過。代碼如下:
class HookSignHandler(var base : Any) : InvocationHandler {companion object{internal var signature = "xxx"fun hook(context: Context){try{var activityThreadClass = Class.forName("android.app.ActivityThread")var currentActicityThreadMethod = activityThreadClass.getDeclaredMethod("currentActivityThread")var currentActivityThread = currentActicityThreadMethod.invoke(null)var sPackageManagerField = activityThreadClass.getDeclaredField("sPackageManager")sPackageManagerField.isAccessible = true;var sPackageManager = sPackageManagerField.get(currentActivityThread)var iPackageManagerInterface = Class.forName("android.content.pm.IPackageManager")var proxy = Proxy.newProxyInstance(iPackageManagerInterface.classLoader, arrayOf(iPackageManagerInterface), HookSignHandler(sPackageManager))sPackageManagerField.set(currentActivityThread, proxy)var pm = context.packageManagervar mPMField = pm.javaClass.getDeclaredField("mPM")mPMField.isAccessible = truemPMField.set(pm, proxy)}catch (e : Exception){Log.e("hook", "hook", e)}}}override fun invoke(proxy: Any?, method: Method, args: Array<out Any>): Any {if("getPackageInfo".equals(method.name)){var flag = args[1] as Intif(flag == PackageManager.GET_SIGNATURES){var sign = Signature(signature)var info = method.invoke(base, *args) as PackageInfoinfo.signatures[0] = signreturn info}}return method.invoke(base, *args)}}只要得到了簽名的signature,并且在application中添加
HookSignHandler.Companion.hook(this);這時(shí)無論java層還是jni層,當(dāng)獲取getPackageManager()時(shí),它的mPM都是已經(jīng)被代理的對(duì)象,這樣當(dāng)執(zhí)行g(shù)etPackageInfo()函數(shù)(實(shí)際上是執(zhí)行mPM的對(duì)應(yīng)函數(shù))就會(huì)返回設(shè)置好的signature,而不是當(dāng)前app的簽名,這樣就繞過了。
防
那么怎么防止這種手段?
那就是每次使用getPackageManager()時(shí)都檢查一下它是否被代理。代碼如下:
Proxy本身提供了一個(gè)函數(shù)isProxyClass來檢查當(dāng)前對(duì)象的類是否是代理類。
我們將mPM對(duì)象獲取到,用isProxyClass驗(yàn)證它的class即可。
那么這個(gè)這就涉及到了動(dòng)態(tài)代理proxy的原理了。
動(dòng)態(tài)代理
首先,動(dòng)態(tài)代理一定需要一個(gè)接口,就是說代理的類必須實(shí)現(xiàn)某個(gè)接口,否則無法代理該類。比如上面的mPM就是實(shí)現(xiàn)接口android.content.pm.IPackageManager
這是為什么?這也與動(dòng)態(tài)代理的原理有關(guān)。
簡(jiǎn)單來說,當(dāng)我們?cè)O(shè)置動(dòng)態(tài)代理后,實(shí)際上會(huì)自動(dòng)生成一個(gè)類
public final class $Proxy0 extends Proxy implements XXXXX {public $Proxy0(InvocationHandler paramInvocationHandler) throws {super(paramInvocationHandler);}... }這樣就一下子清晰了,因?yàn)閷?shí)現(xiàn)了同一個(gè)接口,所以可以設(shè)置給原對(duì)象而不產(chǎn)生問題。
但是它又繼承了Proxy類,而且可以看到構(gòu)造函數(shù)中將之前創(chuàng)建的InvocationHandler也傳進(jìn)來了,這樣就可以調(diào)用到原對(duì)象的函數(shù)了。
它的詳細(xì)代碼就不展示和一一解釋了,簡(jiǎn)單來說就是它實(shí)現(xiàn)了接口,在每個(gè)函數(shù)中再去執(zhí)行InvocationHandler的invoke,就實(shí)現(xiàn)了代理。
這也是為什么動(dòng)態(tài)代理一定需要一個(gè)接口的原因。
Proxy.newProxyInstance()函數(shù)就是創(chuàng)建一個(gè)$Proxy0這個(gè)類的對(duì)象,然后設(shè)置給原對(duì)象,就代理上了。
那么isProxyClass的原理就清晰了,我們只要知道這個(gè)對(duì)象是不是繼承Proxy即可。代碼:
public static boolean isProxyClass(Class<?> cl) {return Proxy.class.isAssignableFrom(cl) && proxyClassCache.containsValue(cl); }可以看到使用了isAssignableFrom,那么再來說一說這個(gè)函數(shù)。
isAssignableFrom和instanceof
這兩個(gè)作用類似,從例子上看:B extends A
A.class.isAssignableFrom(B.class); 表示A是B的父類,注意兩邊都是Class
b instanceOf A 判斷A是否是b對(duì)象的類。這里b是B類的對(duì)象。A是B的父類,所以也一樣是b對(duì)象的類
關(guān)注公眾號(hào):BennuCTech,發(fā)送“HookSignHandler”獲取完整源碼
總結(jié)
以上是生活随笔為你收集整理的攻防:如何防止动态hook绕过jni签名校验的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: flutter web:lottie j
- 下一篇: java中try-catch另外一种用法