从0开始学微信逆向开发(android版)——第一栏
首先给大家普及一下啥叫微信逆向开发,以及微信逆向开发能做什么:
场景1:小明是做微商的,他每天需要加很多很多的好友。然后他会通过微信的各种渠道去添加好友,比如(附近的人、摇一摇、漂流瓶、手机号搜索等),这样小明每天会花费很多的时间在重复并且枯燥无趣的加人的操作上,如果设计一款程序,帮助小明很容易的去加人呢?
场景2:小花是做在线教育的,她建了很多群,每天在群里给学院们讲课。她讲课的时候需要先在一个群里面发送自己的语音,然后长按语音挨个的转发到其他的学员群里面。如果设计一款程序。能自动的把语音批量发给小花提前设置好的所有群呢?
场景3:小强在一个500强的企业做微信大客户营销,他每天需要操控几百上千个微信号,与客户互动,沟通,推销自己公司的产品。如果设计一款程序,自动把很多手机同步化操作,写好脚本一键操控几百上千个微信号呢?
好了,上面介绍了几个场景,大家大概也对微信逆向开发能做什么略知一二了。微信目前在国内是占有率最高的社交软件,其延伸的功能也是数不胜数,我这里只教大家方法,至于还有哪些有趣的功能,大家可以发挥自己的想象。
首先给大家介绍一下逆向需要用到哪些技术:
1、基于android的模拟点击,滑动等。
2、基于xposed框架的hook操作。
3、反编译工具的使用如:jadx-gui 、apk-tool等
4、Android Device Monitor的使用
由于是第一章,我们先从一个简单的demo来讲起,今天给大家讲解如何自动添加附近的人。
首先我们要用到的功能包含上面的 1和4,后面几章会陆续和大家讲到如何反编译 如何使用xposed模块。今天主要教大家如何使用Android Device Monitor和基于android辅助类的 模拟点击功能。
首先打开微信:
然后在android studio中进入Android Device Monitor
然后再里面找到com.tencent.mm包,点击上方的Dump View
然后可以看见打开的界面是这样的。在中间的屏显区域点击你想要查看的元素(这个感觉和浏览器调试比较类似。)
今天我们要做附近的人的功能,则在微信主界面首先查看“发现”
然后我们可以看见发现拥有text和resource-id 这两个值我们在后面会用到。其他的元素以此类推。
好了,后面来教大家如何使用android的辅助类来实现模拟点击。
基础使用
1. 创建服务类
编写自己的服务类,需要继承AccessibilityService类.其中要实现onAccessibilityEvent(AccessibilityEvent event)及onInterruput()两个重要的方法:
public class AutoService extends AccessibilityService implements View.OnClickListener { @Overridepublic void onAccessibilityEvent(final AccessibilityEvent event) { //handle } @Overridepublic void onInterrupt() { } @Overrideprotected void onServiceConnected() { } }
2. 声明服务
像其他Service服务一样,需要在AndroidManifest.xml中声明.除此之外,该服务还必须配置以下两项:
· 配置<intent-filter>,其name为固定的android.accessibilityservice.AccessibilityService
· 声明BIND_ACCESSIBILITY_SERVICE权限,以便系统能够绑定该服务(4.1版本后要求)
注意:任何一点配置错误,系统都检测不到该服务,因此其固定配置如下:
<service android:enabled="true" android:exported="true" android:label="@string/app_name" android:name=".AutoService" 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/envelope_service_config"/>
</service>
3. 配置服务类
在AndroidManifest.xml声明了该服务之后,接下来就是需要对该服务进行一些参数设置了.该服务能够被配置用来接受指定类型的事件,监听指定package,检索窗口内容,获取事件类型的时间等等.目前有两种配置方法:
1. 4.0之后提供了可以通过<meta-data>标签进行配置
2. 通过setServiceInfo()进行配置
1. 通过<meta-data>进行配置
在manifest生命的servce中提供一个meta-data标签,然后通过android:resource指定相应的配置文件(在res目录下创建xml文件,并在其中创建配置文件
envelope_service_config.xml):
<?xml version="1.0" encoding="utf-8"?><accessibility-service xmlns:android="http://schemas.android.com/apk/res/android" android:accessibilityEventTypes="typeNotificationStateChanged|typeWindowStateChanged|typeWindowContentChanged" android:accessibilityFeedbackType="feedbackGeneric" android:accessibilityFlags="" android:canRetrieveWindowContent="true" android:description="@string/app_name" android:notificationTimeout="100" android:packageNames="com.tencent.mm,com.huawei.android.launcher" />
4. 启动服务
当我们做完以上操作,便可将app安装到手机.安装成功后,在设置->辅助功能中便可以找到我们的服务.该服务默认处在关闭状态,需要手动开启.
5. 获取事件信息
上面我们说道,onAccessibilityEvent(AccessibilityEvent event)是该服务的核心方法,其中参数event封装来自界面相关事件的信息,比如我们可以获得该事件的事件类型,进而根据起类型选择不同的处理方式:
6. 获取窗口内容
仅仅知道事件的信息是不够的,我们还希望通过事件来获取发出该事件(事件源)的信息,比如Button按钮被点击时它的text.一个服务可以配置为可以检索窗口内容,即获取窗口内容.整个窗口内容本质上是关于AccessibilityWindowInfo和AccessibilityNodeInfo的树结构,我称之为内容树.(类似View Tree,但由不完全相同)
需要注意,该服务可能配置了只检测了部分事件,而不是全部事件,这就意味着,当内容树发生变化后,该服务可能并不知道,即该服务无法及时的了解当前的内容树是否发生了变化.比如说,你的服务只检测了点击事件,但是此时界面的输入焦点已经变化,这样整个结点树也发生了变化,但是你的服务却不知道,此时你在结点中拿到的窗口内容可能已经不是最新的了.因此,如果你想及时的获知当前窗口的内容,那么就在配置的时候,设置监听全部事件.
正如上面所提到的,要想获取窗口内容,,在配置AccessibilityService时需设置canRetrieveWindowContent为true.之后,便可以通过AccessibilityEvent.getSource(),findFocus(int),getWindow()或者getRootInActiveWindow()获取窗口内容.
7. 服务的生命周期
要理解该中服务的生命周期只需要记住以下三点即可:
· 该种服务完全由系统管理,并遵循已有的服务周期.
· 开启一个服务只能由用户在设置中打开,而关闭则只能由用户在设置中关闭或者服务本身通过diableSelf()方法关闭(当然,现在有些第三放软件也可以强制关闭该类型服务)
· 系统绑定该服务之后,会调用onServiceConnected()方法,这个方法可以被重写,在其中,你可以做一些初始化的操作.
下面贴上核心代码
public void onAccessibilityEvent(final AccessibilityEvent event) { int eventType = event.getEventType(); switch (status) { case "dzh_zt1"://点击发现 countI = 0; openDelay(1000, "发现", "dzh_zt2"); break; case "dzh_zt2"://d点击附近的人 openDelay(1000, "附近的人","dzh_xb1"); break; case "dzh_xb1"://如果sex有值则进行筛选 if (event.getClassName().equals("com.tencent.mm.plugin.nearby.ui.NearbyFriendsUI") && eventType == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) { if (sex != null) { AccessibilityNodeInfo sexInfo = getRootInActiveWindow(); recursionFun(sexInfo, "android.widget.TextView", "更多"); status="dzh_xb2"; } } case "dzh_xb2"://根据sex点击筛选条件 if (event.getClassName().equals("com.tencent.mm.plugin.nearby.ui.NearbyFriendsUI") && eventType == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) { if (sex != null) { AccessibilityNodeInfo sexInfo = getRootInActiveWindow(); List<AccessibilityNodeInfo> list = sexInfo.findAccessibilityNodeInfosByText(sex); list.get(0).performAction(AccessibilityNodeInfo.ACTION_CLICK); list.get(0).getParent().performAction(AccessibilityNodeInfo.ACTION_CLICK); status="dzh_zt3"; } } break; case "dzh_zt3"://附近的人界面 开始选人打招呼 nearPersonList(event,eventType); break; case "dzh_zt4"://打招呼界面 nearPersonSeayHello(event,eventType); break; case "dzh_zt5"://打招呼详情界面 sayHelloDetails(event,eventType); break; } }