PM代理对抗签名校验
Created At :
Views 👀 :
原理
1 2 3 4 5 6 7 8 9 10
| private void getSignature() { try { PackageInfo packageInfo = getPackageManager().getPackageInfo(getPackageName(), PackageManager.GET_SIGNATURES); Log.i(SHARK, "len:"+packageInfo.signatures.length); if (packageInfo.signatures != null) { Log.i(SHARK, "sig:"+packageInfo.signatures[0].toCharsString()); } } catch (Exception e) { } }
|
使用PMS可以很容易的获取到apk的签名信息
通过查看getPackageManager方法,我们可以发现其实是调用了mBase的getPackageManager方法,mBase是一个Context(attachBaseContext方法中引入),最终调用mBase.getPackageManager的其实是Context的子类Contextimpl,查看getPackageInfo方法可以发现,最后获得包的信息是通过mPM(IPackageManager),所以只需要将mPM替换即可
替换mPM
查看getPackageManager方法
1 2 3 4 5 6 7 8 9 10 11 12
| ... static volatile IPackageManager sPackageManager; ... public static IPackageManager getPackageManager() { if (sPackageManager != null) { return sPackageManager; } final IBinder b = ServiceManager.getService("package"); sPackageManager = IPackageManager.Stub.asInterface(b); return sPackageManager; }
|
其实就是返回了sPackageManager
就需要用替换掉ActivityThread的静态变量sPackageManager,ApplicationPackageManager对象中的mPM变量
动态代理HookPMS
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| package cn.n1ng.hookpm;
import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.Signature; import android.util.Log;
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method;
public class PmsHookBinderInvocationHandler implements InvocationHandler { private Object base;
public final static String N1 = "n2ng";
private String SIGN; private String appPkgName = "";
public PmsHookBinderInvocationHandler(Object base, String sign, String appPkgName, int hashCode) { try { this.base = base; this.SIGN = sign; this.appPkgName = appPkgName; } catch (Exception e) { Log.d(N1, "error:"+ Log.getStackTraceString(e)); } }
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Log.i(N1, method.getName()); if("getPackageInfo".equals(method.getName())){ String pkgName = (String)args[0]; Integer flag = (Integer)args[1]; if(flag == PackageManager.GET_SIGNATURES && appPkgName.equals(pkgName)){ Signature sign = new Signature(SIGN); PackageInfo info = (PackageInfo) method.invoke(base, args); info.signatures[0] = sign; return info; } } return method.invoke(base, args); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| package cn.n1ng.hookpm;
import android.content.Context; import android.content.pm.PackageManager; import android.util.Log;
import java.lang.reflect.Field; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy;
public class ServerManagerWraper { public final static String N1 = "n2ng";
public static void hookPMS(Context context, String signed, String appPkgName, int hashCode) { try { Class<?> activityThreadClass = Class.forName("android.app.ActivityThread"); Method currentActivityThreadMethod = activityThreadClass.getDeclaredMethod("currentActivityThread"); Object currentActivityThread = currentActivityThreadMethod.invoke(null); Field sPackageManagerField = activityThreadClass.getDeclaredField("sPackageManager"); sPackageManagerField.setAccessible(true); Object sPackageManager = sPackageManagerField.get(currentActivityThread); Class<?> iPackageManagerInterface = Class.forName("android.content.pm.IPackageManager"); Object proxy = Proxy.newProxyInstance( iPackageManagerInterface.getClassLoader(), new Class<?>[]{iPackageManagerInterface}, new PmsHookBinderInvocationHandler(sPackageManager, signed, appPkgName, 0)); sPackageManagerField.set(currentActivityThread, proxy); PackageManager pm = context.getPackageManager(); Field mPmField = pm.getClass().getDeclaredField("mPM"); mPmField.setAccessible(true); mPmField.set(pm, proxy); } catch (Exception e) { Log.d(N1, "hook pms error:" + Log.getStackTraceString(e)); } }
public static void hookPMS(Context context) { String Sign = "111111"; hookPMS(context, Sign, "cn.n1ng.hookpm", 0); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| package cn.n1ng.hookpm;
import androidx.appcompat.app.AppCompatActivity;
import android.app.Application; import android.content.Context; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.os.Bundle; import android.util.Log;
public class MainActivity extends AppCompatActivity {
private void getSignature() { try { PackageInfo packageInfo = getPackageManager().getPackageInfo(getPackageName(), PackageManager.GET_SIGNATURES); Log.i("n2ng", "len:"+packageInfo.signatures.length); if (packageInfo.signatures != null) { Log.i("n2ng ", "sig:"+packageInfo.signatures[0].toCharsString()); } } catch (Exception e) { Log.i("n2ng","catch error"); } } protected void attachBaseContext(Context newBase){ ServerManagerWraper.hookPMS(newBase); super.attachBaseContext(newBase); }
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); getSignature(); }
}
|
先看ServerManagerWraper类中的hookPMS方法,他先通过反射获取到全局的ActivityThread对象,然后通过ActivityThread的静态方法currentActivityThread获得当前的ActivityThread,然后获取ActivityThread中的初始的sPackageManager,再创建一个代理,指定实现iPackageManagerInterface接口,以及一个InvocationHandler用于处理代理对象的方法调用,这里传进去的就是PmsHookBinderInvocationHandler(实现了InvocationHandler接口),主要实现了覆盖原签名的作用,通过context.getPackageManager获得ApplicationPackageManager对象,最后将sPackageManager和mPM替换
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| 2024-05-30 16:21:07.797 19251-19251 n2ng cn.n1ng.hookpm I getServiceInfo 2024-05-30 16:21:10.785 19251-19251 n2ng cn.n1ng.hookpm I hasSystemFeature 2024-05-30 16:21:13.882 19251-19251 n2ng cn.n1ng.hookpm I getActivityInfo 2024-05-30 16:21:13.943 19251-19251 n2ng cn.n1ng.hookpm I getActivityInfo 2024-05-30 16:21:14.047 19251-19284 n2ng cn.n1ng.hookpm I getPackageInfo 2024-05-30 16:21:14.063 19251-19284 n2ng cn.n1ng.hookpm I getApplicationInfo 2024-05-30 16:21:14.159 19251-19284 n2ng cn.n1ng.hookpm I notifyPackageUse 2024-05-30 16:21:14.283 19251-19284 n2ng cn.n1ng.hookpm I notifyDexLoadWithStatus 2024-05-30 16:21:14.395 19251-19284 n2ng cn.n1ng.hookpm I notifyDexLoadWithStatus 2024-05-30 16:21:14.496 19251-19284 n2ng cn.n1ng.hookpm I notifyDexLoadWithStatus 2024-05-30 16:21:14.595 19251-19284 n2ng cn.n1ng.hookpm I notifyDexLoadWithStatus 2024-05-30 16:21:14.633 19251-19284 n2ng cn.n1ng.hookpm I getPackageInfo 2024-05-30 16:21:14.645 19251-19284 n2ng cn.n1ng.hookpm I getPackageInfo 2024-05-30 16:21:14.654 19251-19284 n2ng cn.n1ng.hookpm I getApplicationInfo 2024-05-30 16:21:14.724 19251-19284 n2ng cn.n1ng.hookpm I notifyPackageUse 2024-05-30 16:21:17.461 19251-19251 n2ng cn.n1ng.hookpm I getPackageInfo 2024-05-30 16:21:17.471 19251-19251 n2ng cn.n1ng.hookpm I len:1 2024-05-30 16:21:17.472 19251-19251 n2ng cn.n1ng.hookpm I sig:111111
|
实战过签名校验
以52pj正己安卓逆向课程的教程demo为例
先将上面的hookpm编译为dex,然后放进apk包中并改名为classes3.dex
接下来需要修改hookpmdex中的签名数据以及包名,先找到ServiceManagerWraper,将const string v0的值改为apk原包的十六进制签名值(可以通过np管理器的查看签名功能获取),然后修改const string v1为包名,这里是com.zj.wuaipojie,保存并退出
最后还需要调用hookPMS方法,在classes.dex中搜索attachBaseContext,找到在MainApplication类下的attachBaseContext,在invoke-super步骤前添加调用hookPMS的smali代码,这样就过掉了普通的签名校验
1 2
| //调用了zhengji.Hook.ServiceManagerWraper类下的hookPMS方法 invoke-static {p1}, Lzhengji/Hook/ServiceManagerWraper;->hookPMS(Landroid/content/Context;)V
|
参考文章
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 3049155267@qq.com