背景
一般我们安卓开发会有两套签名(debug和release),而调用QQ登录时,QQ会检测目标应用的签名信息,这样就会导致在调试时无法正常登录的问题
解决方案
通过Xposed注入QQ,在QQ检测签名信息时,强制返回一个release签名信息,以使在调试时仍能正常使用QQ登录功能
具体实现方式
参考bin大的https://github.com/L-JINBIN/ApkSignatureKiller,这是他在mt管理器里去除签名校验的代码,核心原理就是替换签名信息,不过他这个只能用于检测自身签名信息,而且还需要修改应用,英雌我们需要适配下,通过Xposed将该代码注入到QQ里,以下是我修改后的核心代码:
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.Signature;
import android.text.TextUtils;
import com.mhook.dialog.tool.Debug;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;
import de.robv.android.xposed.XC_MethodHook;
import louis.framework.util.ByteUtil;
/**
* Created by ASUS on 2020/10/26.
*/
public class AppSignaturesHook extends XC_MethodHook implements InvocationHandler {
public static final String TAG="AppSignaturesHook";
private static final int GET_SIGNATURES = 0x00000040;
private Object base;
private Map<String,byte[][]> pkgSignMaps=new HashMap<>();
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if ("getPackageInfo".equals(method.getName())) {
String pkgName = (String) args[0];
Integer flag = (Integer) args[1];
if(pkgSignMaps.containsKey(pkgName)){
byte[][] sign=pkgSignMaps.get(pkgName);
if ((flag & GET_SIGNATURES) != 0&&sign!=null&&sign.length>0) {
PackageInfo info = (PackageInfo) method.invoke(base, args);
info.signatures = new Signature[sign.length];
for (int i = 0; i < info.signatures.length; i++) {
info.signatures[i] = new Signature(sign[i]);
}
Debug.LogI(TAG,"Replace "+pkgName+" sign success.");
return info;
}
}
}
return method.invoke(base, args);
}
private void hook(Context context) {
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");
this.base = sPackageManager;
Object proxy = Proxy.newProxyInstance(
iPackageManagerInterface.getClassLoader(),
new Class<?>[]{
iPackageManagerInterface},
this);
// 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);
Debug.LogI(TAG,"PmsHook success.");
} catch (Exception e) {
Debug.LogE(TAG,"PmsHook failed.");
e.printStackTrace();
}
}
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
switch (param.method.getName()){
case "attach":
attach(param);
break;
}
}
private static AppSignaturesHook instance;
public static AppSignaturesHook getInstance(Map<String,String> hexSignMaps){
if(instance==null){
instance=new AppSignaturesHook(hexSignMaps);
}
return instance;
}
private AppSignaturesHook(Map<String,String> hexSignMaps) {
//do init code
pkgSignMaps.clear();
for (String pkg: hexSignMaps.keySet()){
if(TextUtils.isEmpty(pkg))continue;
String hexSign=hexSignMaps.get(pkg);
if(TextUtils.isEmpty(hexSign))continue;
Debug.LogI(TAG,"Parse "+pkg+" hexSign:"+hexSign);
try {
pkgSignMaps.put(pkg,parseSign(hexSign));
} catch (IOException e) {
e.printStackTrace();
Debug.LogI(TAG,"Parse "+pkg+" failed:"+e.getMessage());
}
}
}
private byte[][] parseSign(String data) throws IOException {
DataInputStream is = new DataInputStream(new ByteArrayInputStream(ByteUtil.hexString2byte(data)));
byte[][] sign = new byte[is.read() & 0xFF][];
for (int i = 0; i < sign.length; i++) {
sign[i] = new byte[is.readInt()];
is.readFully(sign[i]);
}
return sign;
}
private void attach(MethodHookParam param) {
Context context= (Context) param.args[0];
hook(context);
}
}
- 调用
findAndHookMyMethod(Application.class, "attach", Context.class, AppSignaturesHook.getInstance(signMap));
成品
- 可关注公众号"对话框取消"获取
- 或者酷安获取https://www.coolapk.com/apk/277278