之前跟那些20年的单身狗抢红包是屡战屡败。再加上最近群里面发红包发的厉害,又想到快要过年了,到时候还不知道群里要发好多红包,所以我将之前在网上宕的一份微信抢红包的代码修改了一下,实现了QQ抢红包!可以支持抢QQ拼手气红包,普通红包,口令红包,现在再也不怕20年单身手速的人跟我抢红包了!
先看测试效果图:
1. 抢QQ口令红包:
可以看见,只要红包一发出,自动填写口令并发出,帮你将红包抢到手!
2. 抢QQ拼手气红包:
拼手气红包也是一样,只要红包一发出,自动帮你把红包抢到手,是不是很爽的感觉?
3. 抢QQ好友发送的红包:
只要好友或者群里的人把红包一发出,就会第一时间让你抢到红包!
所以只要在群里面开启插件,抢红包从来都是百发百中!
好了废话不多说了,也不吹嘘有多牛多好了,下面直接给大家上代码:
MainActivity:
/*MainActivity中的代码基本没改变:*/
public class MainActivity extends AppCompatActivity {
private final Intent mAccessibleIntent = new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS);
private Button switchPlugin;
/*onCreate*/
@Overrideprotected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
switchPlugin = (Button) findViewById(R.id.button_accessible);
updateServiceStatus();
}
/*开启插件的按钮*/
public void onButtonClicked(View view) {startActivity(mAccessibleIntent);}
@Overrideprotected void onResume() {
super.onResume();
updateServiceStatus();
}
}
private void updateServiceStatus() {
boolean serviceEnabled = false;
AccessibilityManager accessibilityManager = (AccessibilityManager) getSystemService(Context.ACCESSIBILITY_SERVICE);
List accessibilityServices = accessibilityManager.getEnabledAccessibilityServiceList(AccessibilityServiceInfo.FEEDBACK_GENERIC);
for (AccessibilityServiceInfo info : accessibilityServices) {
if (info.getId().equals(getPackageName() + "/.QQHongbaoService")) {
serviceEnabled = true;
break;
}
}
if (serviceEnabled) {
switchPlugin.setText("关闭插件");
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
} else {
switchPlugin.setText("开启插件");
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);}
}
}
这里是MainActivity中的全部代码,是不是很少的样子,主要是实现了一个按钮去开启ACCESSIBILITY_SERVICE。这个插件主要就是借助AccessibilityService这个服务来实现。所以剩下的代码就都在这个服务中了!
QQHongbaoService:
public class QQHongbaoService extends AccessibilityService {
private static final String WECHAT_OPEN_EN = "Open";
private static final String WECHAT_OPENED_EN = "You've opened";
private final static String QQ_DEFAULT_CLICK_OPEN = "点击拆开";
private final static String QQ_HONG_BAO_PASSWORD = "口令红包";
private final static String QQ_CLICK_TO_PASTE_PASSWORD = "点击输入口令";
private boolean mLuckyMoneyReceived;
private String lastFetchedHongbaoId = null;
private long lastFetchedTime = 0;
private static final int MAX_CACHE_TOLERANCE = 5000;
private AccessibilityNodeInfo rootNodeInfo;
private List mReceiveNode;
@TargetApi(Build.VERSION_CODES.KITKAT)
public void recycle(AccessibilityNodeInfo info) {
if (info.getChildCount() == 0) {
if (info.getText() != null && info.getText().toString().equals(QQ_CLICK_TO_PASTE_PASSWORD)) {
info.getParent().performAction(AccessibilityNodeInfo.ACTION_CLICK);
}
if (info.getClassName().toString().equals("android.widget.Button") && info.getText().toString().equals("发送")) {
info.performAction(AccessibilityNodeInfo.ACTION_CLICK);
}
} else {
for (int i = 0; i < info.getChildCount(); i++) {
if (info.getChild(i) != null) {
recycle(info.getChild(i));
}
}
}
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
@Override
public void onAccessibilityEvent(AccessibilityEvent event) {
this.rootNodeInfo = event.getSource();
if (rootNodeInfo == null) {
return;
}
mReceiveNode = null;
checkNodeInfo();
/* 如果已经接收到红包并且还没有戳开 */
if (mLuckyMoneyReceived && (mReceiveNode != null)) {
int size = mReceiveNode.size();
if (size > 0) {
String id = getHongbaoText(mReceiveNode.get(size - 1));
long now = System.currentTimeMillis();
if (this.shouldReturn(id, now - lastFetchedTime))
return;
lastFetchedHongbaoId = id;
lastFetchedTime = now;
AccessibilityNodeInfo cellNode = mReceiveNode.get(size - 1);
if (cellNode.getText().toString().equals("口令红包已拆开")) {
return;
}
cellNode.getParent().performAction(AccessibilityNodeInfo.ACTION_CLICK);
if (cellNode.getText().toString().equals(QQ_HONG_BAO_PASSWORD)) {
AccessibilityNodeInfo rowNode = getRootInActiveWindow();
if (rowNode == null) {
Log.e(TAG, "noteInfo is null");
return;
} else {
recycle(rowNode);
}
}
mLuckyMoneyReceived = false;
}
}
}
/**
* 检查节点信息
*/
private void checkNodeInfo() {
if (rootNodeInfo == null) {
return;
}
/* 聊天会话窗口,遍历节点匹配“点击拆开”,“口令红包”,“点击输入口令” */
List nodes1 = this.findAccessibilityNodeInfosByTexts(this.rootNodeInfo, new String[]{
QQ_DEFAULT_CLICK_OPEN, QQ_HONG_BAO_PASSWORD, QQ_CLICK_TO_PASTE_PASSWORD, "发送"});
if (!nodes1.isEmpty()) {
String nodeId = Integer.toHexString(System.identityHashCode(this.rootNodeInfo));
if (!nodeId.equals(lastFetchedHongbaoId)) {
mLuckyMoneyReceived = true;
mReceiveNode = nodes1;
}
return;
}
}
/**
* 将节点对象的id和红包上的内容合并
* 用于表示一个唯一的红包
*
* @param node 任意对象
* @return 红包标识字符串
*/
private String getHongbaoText(AccessibilityNodeInfo node) {
/* 获取红包上的文本 */
String content;
try {
AccessibilityNodeInfo i = node.getParent().getChild(0);
content = i.getText().toString();
} catch (NullPointerException npe) {
return null;
}
return content;
}
/**
* 判断是否返回,减少点击次数
* 现在的策略是当红包文本和缓存不一致时,戳
* 文本一致且间隔大于MAX_CACHE_TOLERANCE时,戳
*
* @param id 红包id
* @param duration 红包到达与缓存的间隔
* @return 是否应该返回
*/
private boolean shouldReturn(String id, long duration) {
// ID为空
if (id == null) return true;
// 名称和缓存不一致
if (duration < MAX_CACHE_TOLERANCE && id.equals(lastFetchedHongbaoId)) {
return true;
}
return false;
}
/**
* 批量化执行AccessibilityNodeInfo.findAccessibilityNodeInfosByText(text).
* 由于这个操作影响性能,将所有需要匹配的文字一起处理,尽早返回
*
* @param nodeInfo 窗口根节点
* @param texts 需要匹配的字符串们
* @return 匹配到的节点数组
*/
private List findAccessibilityNodeInfosByTexts(AccessibilityNodeInfo nodeInfo, String[] texts) {
for (String text : texts) {
if (text == null) continue;
List nodes = nodeInfo.findAccessibilityNodeInfosByText(text);
if (!nodes.isEmpty()) {
if (text.equals(WECHAT_OPEN_EN) && !nodeInfo.findAccessibilityNodeInfosByText(WECHAT_OPENED_EN).isEmpty()) {
continue;
}
return nodes;
}
}
return new ArrayList<>();
}
@Override
public void onInterrupt() {
}
}
QQHongbaoService的全部代码也在这里,代码不多。首先,在这个服务中主要是通过findAccessibilityNodeInfosByText这个方法去获我们需要的节点;然后,用performAction(AccessibilityNodeInfo.ACTION_CLICK) 这个方法去点击红包节点,关键思路大概就是这样!
另外如果是口令红包,我们需要先按照上面的步骤将红包戳开,然后通过performAction(AccessibilityNodeInfo.ACTION_CLICK)去点击输入口令,最后再通过点击去发送即可实现!
QQHongbaoService需要在AndroidManifest.xml文件中注册,
注册的节点如下图:
总体来看,只是将微信抢红包的代码做了少量的修改,在这里要感谢各位大神对微信抢红包源码的贡献!最后也希望这篇文章能给大家有所帮助,在抢红包大战中虐死单身狗,再也不怕你20年的单身手速了!!!
-------------------------------------------------------------------------------------------------------------------
Android微信抢红包外挂 源代码
版权声明:本文为博主原创文章,未经博主允许不得转载。
目录(?)[+]
CodeBoy微信抢红包外挂
效果预览
源码下载地址:https://github.com/lendylongli/qianghongbao
apk下载地址 : 百度云下载 本地下载
前言
Codeboy微信抢红包是我在2015年春节过年期间编写的一个开源的兴趣项目,只要是将整个核心抢红包的流程编写出来,至于再复杂的一些操作就没深入研究。当这个项目发布后,也是反应挺大的,很多网友也找到我了与交流,也有做淘宝的人给钱让我去增加一些功能,当然我是拒绝的。
作者声明
在这里,我声明一下,我所做的是自己有兴趣的事情,只是通过开源的方式让大家去学习相关技术,并不是为了营利,而我也知道淘宝上有人直接拿我的应用去售卖,这些都是没经过我的允许,我也没有半点收益,我留下联系方式是为了方便开发者之间的讨论与学习,所以请商业合作的与小白不要加我QQ,谢谢。
技术详述
一开始大家都会觉得做一个Android外挂会汲取很多东西或者底层的东西,但当发现Android里有一个叫AccessibilityService
的服务时,一切都变得很简单。
关于AccessibilityService
先看看官网的介绍Accessibility
Many Android users have different abilities that require them to interact with their Android devices in different ways. These include users who have visual, physical or age-related limitations that prevent them from fully seeing or using a touchscreen, and users with hearing loss who may not be able to perceive audible information and alerts...
Android官网详解accessibility
上面大概的意思就是Accessibility是一个辅助服务,主要是面向一些使用Android手机的用户有相关障碍(年龄、视觉、听力、身体等),这个功能可以更容易使用手机,可以帮用户在点击屏幕或者显示方面得到帮助等等。接下来就是查找相关API,看能做到哪个地步。
Accessibility相关API描述
当然accessibility
除了可以辅助点击界面的事件外,还可以用作自动化测试,或者一键返回,是一个非常强大与实用的功能,具体实例可以看我另一个App虚拟按键助手
请往下载 GooglePlay市场 或 应用宝。
关于抢红包的流程
在有以上的一些关于辅助服务的基础知识后,我们就可以分析怎样自动化抢红包。 大家使用过微信都知道,如果不是在微信的可见界面范围(在桌面或者在使用其它应用时),在收到新的消息,就会在通知栏提醒用户。而在微信的消息列表界面,就不会弹出通知栏,所以可以区分这两种情况。然后抓取相关关键字作进一步处理。
1、在非微信消息列表界面,收到通知消息的事件,判断通知栏里的文本是否有[微信红包]的关键字,有则可以判断为用户收到红包的消息(当然,你可以故意发一条包括这个关键字的文本消息去整蛊你的朋友)。然后,我们就自动化触发这个消息的意图事件(Intent
);
2、在通知栏跳进微信界面后,是去到com.tencent.mm.ui.LauncherUI
这个Activity
界面。我们知道,红包的消息上,包括了关键字领取红包
或者View
的id
,那我们就根据这个关键字找到相应的View
,然后再触发ACTION_CLICK
(点击事件);
3、在点击红包后,会跳到com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyReceiveUI
这个拆红包的Activity
,当然老方法,找关键字拆红包
或id
,然后触发自动化点击事件。
这样就可以完成整个自动化完成抢红包的流程了,所以核心就是找关键字,然后模拟用户点击事件,就这么简单。以下详细说一下代码的实现。
以下是通过DDMS
工具里的Dump View Hierarchy For UI Automator
去分析微信UI结构。
使用AccessibilityService去一步步监听微信的动作
1、新建一个继承AccessibilityService
的类,如QiangHongBaoService
,然后在AndroidManifest.xml
里声明组件,如下
在meta-data里声明的是辅助配置,这个是Android4.0之后才支持的写法,在4.0之前的系统要在代码里声明。
2、在res/xml
目录下生成辅助服务的配置文件qianghongbao_service_config.xml
android:description
这个是设置服务的描述,在用户授权的界面可以看到。
android:accessibilityEventTypes
这个是配置要监听的辅助事件,我们只需要用到typeNotificationStateChanged
(通知变化事件)、typeWindowStateChanged
(界面变化事件)
android:packageNames
这个是要监听应用的包名,如果要监听多个应用,则用,
去分隔,这里我们只需要监听微信的就可以了
android:accessibilityFeedbackType
这个是设置反馈方式
FeedbackType | 描述 |
---|---|
feedbackSpoken | 语音反馈 |
feedbackHaptic | 触感反馈 |
feedbackAudible | 表示声音(不是语音)反馈 |
feedbackVisual | 视觉反馈 |
feedbackGeneric | 通用反馈 |
feedbackAllMask | 所有以上的反馈 |
详细看AccessibilityServiceInfo类文档描述
3、在以上都配置好后,我们就可以在QiangHongBaoService
这个服务里进行编码了,要做的就是将整个UI跳转流程与逻辑串联起来。
@Override
public void onAccessibilityEvent(AccessibilityEvent event) {
//接收事件,如触发了通知栏变化、界面变化等
}
@Override
protected boolean onKeyEvent(KeyEvent event) {
//接收按键事件
return super.onKeyEvent(event);
}
@Override
public void onInterrupt() {
//服务中断,如授权关闭或者将服务杀死
}
@Override
protected void onServiceConnected() {
super.onServiceConnected();
//连接服务后,一般是在授权成功后会接收到
}
具体内容请看源码
其它
如何防止外挂
在了解整个核心后,获取事件不外乎就是通过文本与id判断,那么就可以将文本改为图标方式,将id改为动态id(每次显示都是随机生成),这样一来就可以提高外挂的门槛。
如何发红包会安全点
现在抢红包就看谁的外挂工具反应够快,如何去干扰这些外挂,其实也有点小技巧,就是在发红包前,发送文本[微信红包]
,可以导致部分外挂工具失效。
版权归作者所有,转载请注明出处:http://www.happycodeboy.com/index.php/archives/10/
关于作者
英文名:Leon
个人博客:http://www.happycodeboy.com
Email:[email protected]
QQ:730395591