Android 绕过反射黑名单的方法
限制原理
Google从AndroidP开始引入了针对非公开API的限制,这一点可以从Native相关的源码中找到限制的原理,从而从中找到解决办法,不过非必要原因不太建议去挑战这种限制,毕竟不清楚在后续的版本中会不会做限制,维护起来挺麻烦的。
在Native层有几个访问级别:
classHiddenApiAccessFlags{ public: enumApiList{ kWhitelist=0, kLightGreylist, kDarkGreylist, kBlacklist, }; }
另外还有几个对应的响应级别:
enumAction{ kAllow,//通过 kAllowButWarn,//通过,但日志警告 kAllowButWarnAndToast,//通过,且日志警告和弹窗 kDeny//拒绝访问 };
这里介绍一下网上的一些解决方式,此外,还可以把我们调用了反射方法的类的类加载器设置为系统类加载器,这样就可以绕过Native层的限制了。
系统类伪装
黑名单在系统中有一个fn_caller_is_trusted的条件:如果调用者是系统类,那么就允许被调用。即如果我们能以系统类的身份去反射,那么就能畅通无阻:
- 首先通过反射API拿到getDeclaredMethod方法。getDeclaredMethod是public的,不存在问题;这个通过反射拿到的方法网上称之为元反射方法。
- 然后通过刚刚的元反射方法去反射调用getDeclardMethod。这里我们就实现了以系统身份去反射的目的——反射相关的API都是系统类,因此我们的元反射方法也是被系统类加载的方法;所以我们的元反射方法调用的getDeclardMethod会被认为是系统调用的,可以反射任意的方法。
伪代码如下:
//公开API,无问题 MethodmetaGetDeclaredMethod=Class.class.getDeclaredMethod("getDeclardMethod"); //系统类通过反射使用隐藏API,检查直接通过。 MethodhiddenMethod=metaGetDeclaredMethod.invoke(hiddenClass,"hiddenMethod","hiddenMethod参数列表"); //正确找到Method直接反射调用 hiddenMethod.invoke
豁免条件
隐藏API的调用有「豁免」条件,即只要它是豁免的,则即使它在黑名单中,也会被放行。这种方式暴露给了Java层,因此可以通过VMRuntime.setHiddenApiExemptions方法来实现。再结合上面这个方法,我们只需要通过「元反射」来反射调用VMRuntime.setHiddenApiExemptions就能将我们自己要使用的隐藏API全部都豁免掉了。另外系统在检查豁免时是通过方法签名进行前缀匹配的,而Java方法签名都是L开头的,因此我们可以把直接传个L进去,那么所有的隐藏API全部被赦免了!
源码直接参考网上大佬的开源项目:FreeReflection。
publicfinalclassBootstrapClass{ privatestaticfinalStringTAG="BootstrapClass"; privatestaticObjectsVmRuntime; privatestaticMethodsetHiddenApiExemptions; static{ if(SDK_INT>=Build.VERSION_CODES.P){ try{ MethodforName=Class.class.getDeclaredMethod("forName",String.class); MethodgetDeclaredMethod=Class.class.getDeclaredMethod("getDeclaredMethod",String.class,Class[].class); Class>vmRuntimeClass=(Class>)forName.invoke(null,"dalvik.system.VMRuntime"); MethodgetRuntime=(Method)getDeclaredMethod.invoke(vmRuntimeClass,"getRuntime",null); setHiddenApiExemptions=(Method)getDeclaredMethod.invoke(vmRuntimeClass,"setHiddenApiExemptions",newClass[]{String[].class}); sVmRuntime=getRuntime.invoke(null); }catch(Throwablee){ Log.w(TAG,"reflectbootstrapfailed:",e); } } } /** *makethemethodexemptedfromhiddenAPIcheck. * *@parammethodthemethodsignatureprefix. *@returntrueifsuccess. */ publicstaticbooleanexempt(Stringmethod){ returnexempt(newString[]{method}); } /** *makespecificmethodsexemptedfromhiddenAPIcheck. * *@parammethodsthemethodsignatureprefix,suchas"Ldalvik/system","Landroid"oreven"L" *@returntrueifsuccess */ publicstaticbooleanexempt(String...methods){ if(sVmRuntime==null||setHiddenApiExemptions==null){ returnfalse; } try{ setHiddenApiExemptions.invoke(sVmRuntime,newObject[]{methods}); returntrue; }catch(Throwablee){ returnfalse; } } /** *MakeallhiddenAPIexempted. * *@returntrueifsuccess. */ publicstaticbooleanexemptAll(){ returnexempt(newString[]{"L"}); } }
以上就是Android绕过反射黑名单的方法的详细内容,更多关于Android绕过反射黑名单的资料请关注毛票票其它相关文章!