攻防:如何防止动态hook绕过jni签名校验
攻
我們知道jni校驗簽名也不可靠,可以被動態(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);這時無論java層還是jni層,當(dāng)獲取getPackageManager()時,它的mPM都是已經(jīng)被代理的對象,這樣當(dāng)執(zhí)行g(shù)etPackageInfo()函數(shù)(實際上是執(zhí)行mPM的對應(yīng)函數(shù))就會返回設(shè)置好的signature,而不是當(dāng)前app的簽名,這樣就繞過了。
防
那么怎么防止這種手段?
那就是每次使用getPackageManager()時都檢查一下它是否被代理。代碼如下:
Proxy本身提供了一個函數(shù)isProxyClass來檢查當(dāng)前對象的類是否是代理類。
我們將mPM對象獲取到,用isProxyClass驗證它的class即可。
那么這個這就涉及到了動態(tài)代理proxy的原理了。
動態(tài)代理
首先,動態(tài)代理一定需要一個接口,就是說代理的類必須實現(xiàn)某個接口,否則無法代理該類。比如上面的mPM就是實現(xiàn)接口android.content.pm.IPackageManager
這是為什么?這也與動態(tài)代理的原理有關(guān)。
簡單來說,當(dāng)我們設(shè)置動態(tài)代理后,實際上會自動生成一個類
public final class $Proxy0 extends Proxy implements XXXXX {public $Proxy0(InvocationHandler paramInvocationHandler) throws {super(paramInvocationHandler);}... }這樣就一下子清晰了,因為實現(xiàn)了同一個接口,所以可以設(shè)置給原對象而不產(chǎn)生問題。
但是它又繼承了Proxy類,而且可以看到構(gòu)造函數(shù)中將之前創(chuàng)建的InvocationHandler也傳進來了,這樣就可以調(diào)用到原對象的函數(shù)了。
它的詳細代碼就不展示和一一解釋了,簡單來說就是它實現(xiàn)了接口,在每個函數(shù)中再去執(zhí)行InvocationHandler的invoke,就實現(xiàn)了代理。
這也是為什么動態(tài)代理一定需要一個接口的原因。
Proxy.newProxyInstance()函數(shù)就是創(chuàng)建一個$Proxy0這個類的對象,然后設(shè)置給原對象,就代理上了。
那么isProxyClass的原理就清晰了,我們只要知道這個對象是不是繼承Proxy即可。代碼:
public static boolean isProxyClass(Class<?> cl) {return Proxy.class.isAssignableFrom(cl) && proxyClassCache.containsValue(cl); }可以看到使用了isAssignableFrom,那么再來說一說這個函數(shù)。
isAssignableFrom和instanceof
這兩個作用類似,從例子上看:B extends A
A.class.isAssignableFrom(B.class); 表示A是B的父類,注意兩邊都是Class
b instanceOf A 判斷A是否是b對象的類。這里b是B類的對象。A是B的父類,所以也一樣是b對象的類
關(guān)注公眾號:BennuCTech,發(fā)送“HookSignHandler”獲取完整源碼
總結(jié)
以上是生活随笔為你收集整理的攻防:如何防止动态hook绕过jni签名校验的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: flutter web:lottie j
- 下一篇: java中try-catch另外一种用法