PM代理对抗签名校验

  1. 原理
    1. 替换mPM
  2. 动态代理HookPMS
  3. 实战过签名校验

原理

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
//ActivityThread.java
...
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
//PmsHookBinderinvocationHandler
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());
//查看是否是getPackageInfo方法
if("getPackageInfo".equals(method.getName())){
String pkgName = (String)args[0];
Integer flag = (Integer)args[1];
//是否是获取我们需要hook apk的签名
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
//ServerManagerWraper
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 {
// 获取全局的ActivityThread对象
Class<?> activityThreadClass = Class.forName("android.app.ActivityThread");
Method currentActivityThreadMethod =
activityThreadClass.getDeclaredMethod("currentActivityThread");
Object currentActivityThread = currentActivityThreadMethod.invoke(null);
// 获取ActivityThread里面原始的sPackageManager
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));
// 1. 替换掉ActivityThread里面的 sPackageManager 字段
sPackageManagerField.set(currentActivityThread, proxy);
// 2. 替换 ApplicationPackageManager里面的 mPM对象
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
//Main
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

💰

×

Help us with donation