基于微信红包插件的原理实现android任何APP自动发送评论(已开源)
背景
地址:https://github.com/huijimuhe/postman
核心就是android的AccessibilityService,回复功能api需要23以上版本才行。
其实很像在做单元测试。你可以有n种方式实现发帖功能,这只是一个比较邪火的方式,亲测过一次,可行。这里我以网易新闻客户端举例。
模拟你在手机端的物理动作:选择新闻-》回复-》退回新闻列表-》进入下一个新闻-》回复-》退回新闻列表刷新-》进入-》回复....
做的不精细,只是探究到底可不可行。你可以用在任何APP中自动发消息,只要没有验证码。
你要拿来玩,请抱着一颗开心的心情。
原理
直接在github上开源的微信红包插件改的,红包插件项目和你需要了解的几篇文章在这里
https://github.com/geeeeeeeeek/WeChatLuckyMoney
http://www.xuebuyuan.com/2061597.html
http://www.xuebuyuan.com/2061595.html
http://developer.android.com/training/accessibility/service.html
package com.huijimuhe.pman.services; import android.accessibilityservice.AccessibilityService;
import android.content.ComponentName;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.preference.PreferenceManager;
import android.util.Log;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo; import com.huijimuhe.pman.utils.PowerUtil; import java.util.ArrayList;
import java.util.List; public class PostService extends AccessibilityService implements SharedPreferences.OnSharedPreferenceChangeListener { private static final String TAG = "PostService"; private static final String MAIN_ACT = "MainActivity";
private static final String DETAIL_ACT = "NewsPageActivity";
private static final String BASE_ACT = "BaseActivity"; private static final int MSG_BACK = 159;
private static final int MSG_REFRESH_NEW_LIST = 707;
private static final int MSG_READ_NEWS = 19;
private static final int MSG_POST_COMMENT = 211;
private static final int MSG_REFRESH_COMPLETE = 22;
private static final int MSG_FINISH_COMMENT = 59; private String currentActivityName = MAIN_ACT;
private HandlerEx mHandler = new HandlerEx(); private boolean mIsMutex = false;
private int mReadCount = 0;
private List<String> readedNews = new ArrayList<>();
private PowerUtil powerUtil;
private SharedPreferences sharedPreferences; /**
* AccessibilityEvent
*
* @param event 事件
*/
@Override
public void onAccessibilityEvent(AccessibilityEvent event) { if (sharedPreferences == null) return; setCurrentActivityName(event);
watchMain(event);
watchBasic(event);
watchDetail(event);
} private void watchMain(AccessibilityEvent event) {
//新闻列表
if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED && currentActivityName.contains(MAIN_ACT)) {
if (mReadCount > 4) {
//如果读取完了都没有新的就刷新
Log.d(TAG, "新闻已读取完,需要刷新列表");
//需要刷新列表了
mHandler.sendEmptyMessage(MSG_REFRESH_NEW_LIST);
} else {
mHandler.sendEmptyMessage(MSG_READ_NEWS);
}
}
} private void watchDetail(AccessibilityEvent event) {
if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED && currentActivityName.contains(DETAIL_ACT)) {
//添加评论
mHandler.sendEmptyMessage(MSG_POST_COMMENT);
}
} private void watchBasic(AccessibilityEvent event) {
if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED && currentActivityName.contains(BASE_ACT)) {
Log.d(TAG, "进入非新闻页,即将退出");
mHandler.sendEmptyMessage(MSG_BACK);
mHandler.sendEmptyMessage(MSG_BACK);
}
} private void refreshList() {
List<AccessibilityNodeInfo> nodes = getRootInActiveWindow().findAccessibilityNodeInfosByViewId("android:id/list");
for (AccessibilityNodeInfo node : nodes) {
//页面是否加载完成
if (node == null) return;
//执行刷新
node.performAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
}
//重新开始读取新闻
mHandler.sendEmptyMessage(MSG_REFRESH_COMPLETE);
} private void enterDetailAct() { //获取列表items
List<AccessibilityNodeInfo> nodes = getRootInActiveWindow().findAccessibilityNodeInfosByViewId("com.netease.newsreader.activity:id/perfect_item"); for (AccessibilityNodeInfo node : nodes) {
//页面是否加载完成
if (node == null) return; //获取列表item的标题
List<AccessibilityNodeInfo> titles = node.findAccessibilityNodeInfosByViewId("com.netease.newsreader.activity:id/title"); for (AccessibilityNodeInfo title : titles) { //检查是否已读取
if (!readedNews.contains(title.getText().toString())) {
//点击读取该新闻
readedNews.add(title.getText().toString());
node.getParent().performAction(AccessibilityNodeInfo.ACTION_CLICK);
Log.d(TAG, "进入新闻:" + title.getText().toString());
mReadCount++;
//进入一个就停止
return;
}
}
}
} private void postComment() {
//激活输入框
List<AccessibilityNodeInfo> nodes = getRootInActiveWindow().findAccessibilityNodeInfosByViewId("com.netease.newsreader.activity:id/mock_reply_edit");
for (AccessibilityNodeInfo node : nodes) { //页面是否加载完成
if (node == null) return; node.performAction(AccessibilityNodeInfo.ACTION_CLICK);
} //输入内容
List<AccessibilityNodeInfo> editNodes = getRootInActiveWindow().findAccessibilityNodeInfosByViewId("com.netease.newsreader.activity:id/reply_edit");
for (AccessibilityNodeInfo node : editNodes) { //页面是否加载完成
if (node == null) return; Bundle arguments = new Bundle();
arguments.putCharSequence(AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, "抽烟的人最讨厌了");
node.performAction(AccessibilityNodeInfo.ACTION_SET_TEXT, arguments);
} // //回复按钮
// List<AccessibilityNodeInfo> postNodes = getRootInActiveWindow().findAccessibilityNodeInfosByViewId("com.netease.newsreader.activity:id/reply");
// for (AccessibilityNodeInfo node : postNodes) {
// //页面是否加载完成
// if (node == null) return;
// node.performAction(AccessibilityNodeInfo.ACTION_CLICK);
// } //退出
mHandler.sendEmptyMessage(MSG_FINISH_COMMENT); Log.d(TAG, "评论已发表");
} /**
* 设置当前页面名称
*
* @param event
*/
private void setCurrentActivityName(AccessibilityEvent event) { if (event.getEventType() != AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
return;
} try {
ComponentName componentName = new ComponentName(event.getPackageName().toString(), event.getClassName().toString()); getPackageManager().getActivityInfo(componentName, 0);
currentActivityName = componentName.flattenToShortString();
Log.d(TAG, "<--pkgName-->" + event.getPackageName().toString());
Log.d(TAG, "<--className-->" + event.getClassName().toString());
Log.d(TAG, "<--currentActivityName-->" + currentActivityName);
} catch (PackageManager.NameNotFoundException e) {
currentActivityName = MAIN_ACT;
}
} @Override
public void onDestroy() {
this.powerUtil.handleWakeLock(false);
super.onDestroy();
} @Override
public void onInterrupt() { } @Override
public void onServiceConnected() {
super.onServiceConnected();
this.watchFlagsFromPreference();
} /**
* 屏幕是否常亮
*/
private void watchFlagsFromPreference() {
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
sharedPreferences.registerOnSharedPreferenceChangeListener(this); this.powerUtil = new PowerUtil(this);
Boolean watchOnLockFlag = sharedPreferences.getBoolean("pref_watch_on_lock", false);
this.powerUtil.handleWakeLock(watchOnLockFlag);
} @Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
if (key.equals("pref_watch_on_lock")) {
Boolean changedValue = sharedPreferences.getBoolean(key, false);
this.powerUtil.handleWakeLock(changedValue);
}
} /**
* 处理机
*/
private class HandlerEx extends Handler {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
//后退
case MSG_BACK:
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
performGlobalAction(GLOBAL_ACTION_BACK);
}
}, 1000);
break;
//结束评论
case MSG_FINISH_COMMENT:
for (int i = 0; i < 4; i++) {
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
performGlobalAction(GLOBAL_ACTION_BACK);
}
}, 2000 +i*500);
}
break;
//刷新列表
case MSG_REFRESH_NEW_LIST:
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
refreshList();
}
}, 3000);
break;
//结束刷新
case MSG_REFRESH_COMPLETE:
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
mReadCount = 0;
enterDetailAct();
}
}, 3000);
break;
//进入新闻页
case MSG_READ_NEWS:
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
enterDetailAct();
}
}, 3000);
break;
//发送评论
case MSG_POST_COMMENT:
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
postComment();
}
}, 3000);
break;
}
}
}
}
在开始写代码前,你应该至少阅读了之前几篇文章和微信红包插件的代码,然后还应该掌握用Android Device Monitor查看UI树的工具使用。(最近开始研究iOS逆向,这个确实比reveal和cycript方便太多)
粗略实现步骤
1.manifest中申明服务
<service
android:name=".services.PostService"
android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
<intent-filter>
<action android:name="android.accessibilityservice.AccessibilityService"/>
</intent-filter>
<meta-data android:name="android.accessibilityservice"
android:resource="@xml/accessible_service_config"/>
</service>
2.设定你需要监控的app包名来过滤,在/res/xml/accessible_service_config.xml中
<accessibility-service
xmlns:android="http://schemas.android.com/apk/res/android"
android:description="@string/app_description"
android:accessibilityEventTypes="typeWindowStateChanged|typeWindowContentChanged"
android:accessibilityFeedbackType="feedbackAllMask"
android:packageNames="com.netease.newsreader.activity"
android:notificationTimeout="10"
android:settingsActivity="com.huijimuhe.pman.activities.SettingsActivity"
android:accessibilityFlags="flagIncludeNotImportantViews|flagDefault"
android:canRetrieveWindowContent="true"/>
比如网易的,android:packageNames="com.netease.newsreader.activity"
3.在AccessibleService中实现对事件的监听
@Override
public void onAccessibilityEvent(AccessibilityEvent event) { if (sharedPreferences == null) return; setCurrentActivityName(event);
watchMain(event);
watchBasic(event);
watchDetail(event);
}
/**
* 设置当前页面名称
*
* @param event
*/
private void setCurrentActivityName(AccessibilityEvent event) { if (event.getEventType() != AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
return;
} try {
ComponentName componentName = new ComponentName(event.getPackageName().toString(), event.getClassName().toString()); getPackageManager().getActivityInfo(componentName, 0);
currentActivityName = componentName.flattenToShortString();
Log.d(TAG, "<--pkgName-->" + event.getPackageName().toString());
Log.d(TAG, "<--className-->" + event.getClassName().toString());
Log.d(TAG, "<--currentActivityName-->" + currentActivityName);
} catch (PackageManager.NameNotFoundException e) {
currentActivityName = MAIN_ACT;
}
}
4.监控是否是新闻列表,可以设定个页面刷新阀值
//新闻列表
if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED && currentActivityName.contains(MAIN_ACT)) {
if (mReadCount > 4) {
//如果读取完了都没有新的就刷新
Log.d(TAG, "新闻已读取完,需要刷新列表");
//需要刷新列表了
mHandler.sendEmptyMessage(MSG_REFRESH_NEW_LIST);
} else {
mHandler.sendEmptyMessage(MSG_READ_NEWS);
}
}
5.监控是否是新闻详情
private void watchDetail(AccessibilityEvent event) {
if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED && currentActivityName.contains(DETAIL_ACT)) {
//添加评论
mHandler.sendEmptyMessage(MSG_POST_COMMENT);
}
}
6监控是否广告或其他专题,不做操作
private void watchBasic(AccessibilityEvent event) {
if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED && currentActivityName.contains(BASE_ACT)) {
Log.d(TAG, "进入非新闻页,即将退出");
mHandler.sendEmptyMessage(MSG_BACK);
mHandler.sendEmptyMessage(MSG_BACK);
}
}
7.回复评论
private void postComment() {
//激活输入框
List<AccessibilityNodeInfo> nodes = getRootInActiveWindow().findAccessibilityNodeInfosByViewId("com.netease.newsreader.activity:id/mock_reply_edit");
for (AccessibilityNodeInfo node : nodes) { //页面是否加载完成
if (node == null) return; node.performAction(AccessibilityNodeInfo.ACTION_CLICK);
} //输入内容
List<AccessibilityNodeInfo> editNodes = getRootInActiveWindow().findAccessibilityNodeInfosByViewId("com.netease.newsreader.activity:id/reply_edit");
for (AccessibilityNodeInfo node : editNodes) { //页面是否加载完成
if (node == null) return; Bundle arguments = new Bundle();
arguments.putCharSequence(AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, "抽烟的人最讨厌了");
node.performAction(AccessibilityNodeInfo.ACTION_SET_TEXT, arguments);
} //退出
mHandler.sendEmptyMessage(MSG_FINISH_COMMENT); Log.d(TAG, "评论已发表");
}
总体思路是通过postDelay来实现操作的间隔,其他的请自己阅读代码,我只测试了下思路是否可行就没有继续延伸下去了。
大家不要留言说我简单事情做那么复杂。用物理方式(现在回头看倒觉得很像单元测试)实现回复,真实性是100%,发贴机你要倒腾一个别人家服务器看不出作弊的,估计更费劲吧。
如果你觉得python写脚本很酷或者直接用fiddler抓包然后写个发帖器都行。我这还有个用Tesseract-OCR做验证码识别的winform。
做这个只是当时觉得红包插件原理很酷,可以有点其他玩法,我也确实倒腾了一个,也开源了https://github.com/huijimuhe/focus
要是开开脑洞,比如不停的微信给欠债老板发消息让还钱啥的,这种插件倒是很能气死他,哈哈哈哈。
要搞什么推广(尤其是卖面膜的)应该靠金主,而不是这个,哈哈哈哈。
P.S.
自己在做独立开发,希望广结英豪,尤其是像我一样脑子短路不用react硬拼anroid、ios原生想干点什么的朋友。App独立开发群533838427
微信公众号『懒文』-->lanwenapp<--
基于微信红包插件的原理实现android任何APP自动发送评论(已开源)的更多相关文章
- 仿各种APP将文章DOM转JSON并在APP中以列表显示(android、ios、php已开源)
背景 一直以来都想实现类似新闻客户端.鲜城等文章型app的正文显示,即在web editor下编辑后存为json,在app中解析json并显示正文. 网上搜过,没找到轮子.都是给的思路,然后告知是公司 ...
- 发布了Android的App,我要开源几个组件!
做了一款App,本来是毕业设计但是毕业的时候还没有做完,因为大部分时间都改论文去了,你们都懂的.现在毕业了在工作之余把App基本上做完了.为什么说基本上呢,因为我觉得还有很多功能还没实现,还要很多bu ...
- android黑科技系列——微信抢红包插件原理解析和开发实现
一.前言 自从几年前微信添加抢红包的功能,微信的电商之旅算是正式开始正式火爆起来.但是作为Android开发者来说,我们在抢红包的同时意识到了很多问题,就是手动去抢红包的速度慢了,当然这些有很多原因导 ...
- Android通过辅助功能实现抢微信红包原理简单介绍
简书文章:https://www.jianshu.com/p/e1099a94b979 附抢红包开源项目地址,代码已全改为Kotlin了,已适配到最新微信7.0.5版本,如果对你有所帮助赏个star吧 ...
- Android中微信抢红包插件原理解析和开发实现
一.前言 自从去年中微信添加抢红包的功能,微信的电商之旅算是正式开始正式火爆起来.但是作为Android开发者来说,我们在抢红包的同时意识到了很多问题,就是手动去抢红包的速度慢了,当然这些有很多原因导 ...
- [Android进阶]学习AccessibilityService实现微信抢红包插件
在你的手机更多设置或者高级设置中,我们会发现有个无障碍的功能,很多人不知道这个功能具体是干嘛的,其实这个功能是为了增强用户界面以帮助残障人士,或者可能暂时无法与设备充分交互的人们 它的具体实现是通过A ...
- Android进阶——学习AccessibilityService实现微信抢红包插件
在你的手机更多设置或者高级设置中,我们会发现有个无障碍的功能,很多人不知道这个功能具体是干嘛的,其实这个功能是为了增强用户界面以帮助残障人士,或者可能暂时无法与设备充分交互的人们 它的具体实现是通过A ...
- Android基于代理的插件化思路分析
前言 正常的App开发流程基本上是这样的:开发功能-->测试--->上线,上线后发现有大bug,紧急修复---->发新版本---->用户更新----->bug修复.从发现 ...
- Python自动抢红包,超详细教程,再也不会错过微信红包了!
目录: 0 引言 1 环境 2 需求分析 3 前置准备 4 抢红包流程回顾 5 代码梳理 6 后记 0 引言 提到抢红包,就不得不提Xposed框架,它简直是个抢红包的神器,但使用Xposed框架有一 ...
随机推荐
- Eclipse和MyEclipse 手动设置 Java代码 注释模板
一.目的 1. 为什么需要注释规范? 注释规范对于程序员而言尤为重要,有以下几个原因: 一个软件的生命周期中,80%的花费在于维护. 几乎没有任何一个软件,在其整个生命周期中,均由最初的开发人员来维 ...
- javascript 内部对象(1)——Math 对象
Math是javascript中的内部对象之一,主要用于处理数学方面的任务,是一种静态对象.和其他动态对象如Date.String等不同的是它没有构造函数Math(),可以直接使用属性和方法. 例如使 ...
- SSAS CUBE TEST CASES
经过周末两天和今天的努力,基本上完成并修复了一些bug并且集成到我的MSBIHelper项目中去,可以进行数据测试了.效果图如下: 可以帮助开发人员快速生成等值的Tsql和mdx查询,辅助测试人员快速 ...
- Hadoop_FileInputFormat分片
Hadoop学习笔记总结 01. InputFormat和OutFormat 1. 整个MapReduce组件 InputFormat类和OutFormat类都是抽象类. 可以实现文件系统的读写,数据 ...
- runv kill 流程分析
1.runv/kill.go Action: func(context *cli.Context) 该函数做的工作很简单,就是通过grpc客户端,发送一个grpc请求而已,如下: c.Signal(n ...
- 《TCP/IP 详解 卷一》读书笔记-----Ping&Traceroute
1.ping是用于测试对方主机是否可达的命令,其实本质上就是echo类型的ICMP报文.同时,ping还能用于计算RTT(round-trip time),即两台主机间的往返时延. 2.随着网络安全意 ...
- 最小生成树 kruskal hdu 5723 Abandoned country
题目链接:hdu 5723 Abandoned country 题目大意:N个点,M条边:先构成一棵最小生成树,然后这个最小生成树上求任意两点之间的路径长度和,并求期望 /************** ...
- pixel art之 hqx 算法
在去年的时候,偶然看到hqx算法. 一个高质量的插值放大算法. 与双线性插值等插值算法相比,这个算法放大后对人眼保护相对比较好. 没有双线性插值看起来模糊,固然,也抽空把算法简单优化了一下. 官网及代 ...
- [转载]ExtJs4 笔记(4) Ext.XTemplate 模板
作者:李盼(Lipan)出处:[Lipan] (http://www.cnblogs.com/lipan/)版权声明:本文的版权归作者与博客园共有.转载时须注明本文的详细链接,否则作者将保留追究其法律 ...
- (转) C#多线程赛跑实例
专于:http://blog.csdn.net/lidatgb/article/details/8363035 结合上篇<多线程的基础>,这次我们写一个多线程的赛跑实例,内容很简单:超人和 ...