通用 java 层签名爆破
本文最后更新于 1035 天前,其中的信息可能已经有所发展或是发生改变。

因为有些游戏为了限制你上硬核渠道,或者对你分包的投放进行限制,会有一些简单的签名校验逻辑,要求渠道想分包必须依赖 cp 的重新签名,所以需要技术做一下通用的签名校验逻辑 hook 脚本

1. 签名校验代码一般是怎么写的

封装一个函数,从 PackageManager 获取签名

public static int getSignature(Context context) {
        PackageManager pm = context.getPackageManager();
        PackageInfo pi;
        StringBuilder sb = new StringBuilder();
        // 获取签名信息
        try {
            pi = pm.getPackageInfo(context.getPackageName(), PackageManager.GET_SIGNATURES);
            Signature[] signatures = pi.signatures;
            for (Signature signature : signatures) {
                sb.append(signature.toCharsString());
            }
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
        }
        return sb.toString().hashCode();
    }

运行的时候校验签名是否还是本公司的签名

int signature = getSignature(getApplicationContext());
if(!MD5Util.getMD5(String.valueOf(signature)).equals("本公司的签名md5")){
        // 被篡改了,执行退出,或者默默的封号
}

2. 代理 PMS

  1. 一般 java 层的校验最后都是调用到 PackageManager,本质上都是通过 binder 和 PMS 通信调用 PMS 的函数,所以我们只需要代理 PMS ,hook 和包相关的信息即可
  2. 首先 建立一个代理对象 PmsHookBinderInvocationHandler
public class PmsHookBinderInvocationHandler implements InvocationHandler {

    private Object base;

    //应用正确的签名信息
    private String oldSign;
    private String oldPackageName;

    public PmsHookBinderInvocationHandler(Object base, String sign, String appPkgName, int hashCode) {

        this.base = base;

    }

    public void setOldSign(String oldSign) {
        this.oldSign = oldSign;
    }

    public void setOldPackageName(String oldPackageName) {
        this.oldPackageName = oldPackageName;
    }

    @Override

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Log.i("ZKHOOK", method.getName());
        if ("getPackageInfo".equals(method.getName())) {
            String pkgName = (String) args[0];
            Integer flag = (Integer) args[1];
            if (flag == PackageManager.GET_SIGNATURES && oldPackageName.equals(pkgName)) {
                Signature sign = new Signature(oldSign);
                PackageInfo info = (PackageInfo) method.invoke(base, args);
                info.signatures[0] = sign;
                return info;
            }
        }
        return method.invoke(base, args);
    }

}
  1. 用代理对象替换实际的对象,然后运行,看代码要替换2个地方的 pm,
ActivityThread 里面的 sPackageManager
PackageManager 里面的 mPM
  public static void hookPMS(Context context, String signed, 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);
            // 准备好代理对象, 用来替换原始的对象
            PmsHookBinderInvocationHandler hookBinderInvocationHandler = new PmsHookBinderInvocationHandler(sPackageManager, 0);
            hookBinderInvocationHandler.setOldPackageName(context.getPackageName());
            hookBinderInvocationHandler.setOldSign(signed);
            Class<?> iPackageManagerInterface = Class.forName("android.content.pm.IPackageManager");
            Object proxy = Proxy.newProxyInstance(
                    iPackageManagerInterface.getClassLoader(),
                    new Class<?>[]{iPackageManagerInterface},
                    hookBinderInvocationHandler);
            // 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("ZKHOOK", "hook pms error:" + Log.getStackTraceString(e));
        }
    }

    public static void hookPMS(Context context){
        String oldSign = 
        hookPMS(context, oldSign, context.getPackageName(), 0);
    }

3. 在 cp 的 application 逻辑中运行 hookPMS 函数(这一步可以脚本化)

反编译的基础知识就不说了,找到 cp 的 application,在 onCreate 生命周期添加 smali 逻辑,同时把上面的 java 代码转成 smali ,丢进 cp 的包里面

# virtual methods
.method public onCreate()V
    .locals 0

    .prologue
    .line 12


    .line 13
    invoke-static {p0}, Lcom/hook/ServiceManagerWraper;->hookPMS(Landroid/content/Context;)V

    .line 14
    return-void
.end method

到这里整个 hook 逻辑就完成了,回编译重新签名。基于java 层的签名校验就解决了

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇