基于微信红包插件的原理实现android任何APP自动发送评论(已开源)

来源:互联网 发布:聚宝盆软件官方下载 编辑:程序博客网 时间:2024/05/16 16:55

(转载)http://www.cnblogs.com/matoo/p/5452553.html

背景

地址: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;            }        }    }}
View Code

 

在开始写代码前,你应该至少阅读了之前几篇文章和微信红包插件的代码,然后还应该掌握用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<--


阅读全文
0 0
原创粉丝点击