免责申明:
本文只讨论技术,项目不用于任何利益行为,如因此产生的纠纷,本人概不负责。
前言
在阅读本文之前,假设你的手机已经root,并且已经成功安装好了 XposedInstaller。
正文
就我知道的,微信自动抢红包存在着两种方式,一种是利用安卓帮助残障用户使用android设备和应用的AccessibilityService进行模拟点击(本文不做讨论),第二种方式是直接hook微信本身的方法进行自动抢。第一种方式受限太多,比如手机本身性能等,并且抢的速度在不考虑手机性能的情况下第二种会快一些。
我们需要一个方法去实现 IXposedHookLoadPackage 这个接口,重写它内部提供的方法。此间我们会用到的是微信的包地址(com.tencent.mm)和数据库地址(com.tencent.wcdb.database.SQLiteDatabase)这里先给出。
首先第一步我们需要插入数据库进行操作,获取一些必要的信息:
1)hook数据库insert操作,打印是否成功插入
2)判断插入的数据是否是发送过来的消息
3)判断是否是红包消息类型
4)处理红包消息
//hook数据库插入操作
private void hookDatabaseInsert(final XC_LoadPackage.LoadPackageParam loadPackageParam) {
Class findClassIfExists = XposedHelpers.findClassIfExists(WECHAT_DATABASE_PACKAGE_NAME, loadPackageParam.classLoader);
if (findClassIfExists == null) {
XposedBridge.log("hook数据库insert操作:未找到类com.tencent.wcdb.database.SQLiteDatabase");
return;
}
XposedHelpers.findAndHookMethod(findClassIfExists, "insertWithOnConflict", new Object[]{String.class, String.class, ContentValues.class, Integer.TYPE, new XC_MethodHook() {
/* access modifiers changed from: protected */
public void afterHookedMethod(XC_MethodHook.MethodHookParam methodHookParam) throws Throwable {
Integer asInteger;
//打印插入的信息
XposedBridge.log("------------------------insert start---------------------\n\n");
XposedBridge.log("param args1:" + ((String) methodHookParam.args[0]));
XposedBridge.log("param args1:" + ((String) methodHookParam.args[1]));
ContentValues contentValues = (ContentValues) methodHookParam.args[2];
XposedBridge.log("param args3 contentValues:");
for (Map.Entry next : contentValues.valueSet()) {
if (next.getValue() != null) {
XposedBridge.log(((String) next.getKey()) + "---------" + next.getValue().toString());
} else {
XposedBridge.log(((String) next.getKey()) + "---------null");
}
}
XposedBridge.log("------------------------insert over---------------------\n\n");
//判断插入的数据是否是发送过来的消息
String str = (String) methodHookParam.args[0];
XposedBridge.log("消息类型:" + contentValues.getAsInteger("type"));
if (!TextUtils.isEmpty(str) && str.equals("message") && (asInteger = contentValues.getAsInteger("type")) != null && asInteger.intValue() == 436207665) {
//处理红包消息
XposedInit.this.handleLuckyMoney(contentValues, loadPackageParam);
}
}
}});
}
其次需要在红包出现的界面做一些操作:
1)hook 微信主界面的onCreate方法,获得主界面对象
2)hook领取红包页面的onCreate方法,打印Intent中的参数(只起到调试作用)
3)hook红包界面初始化“开”按钮的方法,在该方法完成后自动点击开按钮领取红包
public void handleLoadPackage(final XC_LoadPackage.LoadPackageParam loadPackageParam) {
//调用hook数据库插入
hookDatabaseInsert(loadPackageParam);
//**************红包专用开始**************
//hook微信主界面的onCreate方法,获得主界面对象
XposedHelpers.findAndHookMethod("com.tencent.mm.ui.LauncherUI", loadPackageParam.classLoader, "onCreate", new Object[]{Bundle.class, new XC_MethodHook() {
/* access modifiers changed from: protected */
public void afterHookedMethod(XC_MethodHook.MethodHookParam methodHookParam) throws Throwable {
XposedBridge.log("com.tencent.mm.ui.LauncherUI onCreated\n");
Activity unused = XposedInit.launcherUiActivity = (Activity) methodHookParam.thisObject;
}
}});
//hook领取红包页面的onCreate方法,打印Intent中的参数(只起到调试作用)
XposedHelpers.findAndHookMethod("com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyReceiveUI", loadPackageParam.classLoader, "onCreate", new Object[]{Bundle.class, new XC_MethodHook() {
/* access modifiers changed from: protected */
public void afterHookedMethod(XC_MethodHook.MethodHookParam methodHookParam) throws Throwable {
Activity activity = (Activity) methodHookParam.thisObject;
String stringExtra = activity.getIntent().getStringExtra("key_native_url");
String stringExtra2 = activity.getIntent().getStringExtra("key_username");
int intExtra = activity.getIntent().getIntExtra("key_way", 0);
XposedBridge.log("key_native_url: " + stringExtra + "\n");
XposedBridge.log("key_way: " + intExtra + "\n");
XposedBridge.log("key_username: " + stringExtra2 + "\n");
}
}});
//hook红包界面初始化“开”按钮的方法,在该方法完成后自动点击“开”按钮领取红包
XposedHelpers.findAndHookMethod("com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyReceiveUI", loadPackageParam.classLoader, "d", new Object[]{Integer.TYPE, Integer.TYPE, String.class, XposedHelpers.findClass("com.tencent.mm.ab.l", loadPackageParam.classLoader), new XC_MethodHook() {
/* access modifiers changed from: protected */
public void afterHookedMethod(XC_MethodHook.MethodHookParam methodHookParam) throws Throwable {
XposedBridge.log("com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyReceiveUI: Method d called\n");
((Button) XposedHelpers.findField(methodHookParam.thisObject.getClass(), "kMG").get(methodHookParam.thisObject)).performClick();
}
}});
}
//**************红包专用结束**************
最后到了最关键的一步:
1)从插入的数据库中获得nativeurl
2)启动红包页面
3)至于抢的操作Xposed内置的 callStaticMethod 已经帮我们完成了
//处理红包消息
public void handleLuckyMoney(ContentValues contentValues, XC_LoadPackage.LoadPackageParam loadPackageParam) throws Exception {
String asString = contentValues.getAsString("talker");
//从插入的数据库中获得nativeurl
String asString2 = contentValues.getAsString("content");
if (!asString2.startsWith("<msg")) {
asString2 = asString2.substring(asString2.indexOf("<msg"));
}
String string = new XmlToJson.Builder(asString2).build().toJson().getJSONObject(NotificationCompat.CATEGORY_MESSAGE).getJSONObject("appmsg").getJSONObject("wcpayinfo").getString("nativeurl");
XposedBridge.log("nativeurl: " + string + "\n");
//启动红包页面
if (launcherUiActivity != null) {
XposedBridge.log("call method com.tencent.mm.bm.d b, start LuckyMoneyReceiveUI\n");
Intent intent = new Intent();
intent.putExtra("key_way", 1);
intent.putExtra("key_native_url", string);
intent.putExtra("key_username", asString);
XposedHelpers.callStaticMethod(XposedHelpers.findClass("com.tencent.mm.bg.d", loadPackageParam.classLoader), "b", new Object[]{launcherUiActivity, "luckymoney", ".ui.LuckyMoneyReceiveUI", intent});
return;
}
XposedBridge.log("launcherUiActivity == null\n");
}
技巧: 获取每个页面的关键类信息,可以通过DDMS进行进程追踪(动手google或者百度)或者通过UI工具去实现查找,当然这里我介绍一种最最最最不折磨人的方式各大应用市场搜TopActivity(神器啊!想了解这个的同学可以去gayhub找源码看)。
写在最后
学之深,则越发觉得世界之大,你我皆如井底之蛙,奋斗不息,方达彼岸。
不定期更新博客,期待和大家一起进步。