今天下午遇到一个小游戏,游戏内容很简单,就是疯狂地点屏幕,然后看在短时间内次数最多,一般来说我对这种游戏都是不太感冒的,游戏脑残,容易手抽筋,不过突然心血来潮,想试着让页面自动点击来获取高分数,见有点时间,就把页面发到电脑上来了(页面太low了我不想发上来)。
稍微分析发现,这个页面其实是由h5的canvas来承担主要的游戏功能的,包括点击,界面切换等等,都是从一个canvas中来的,那首先想到的就是直接获取到canvas的dom对象,然后就发送点击事件即可。
打开 chrome 的console,我们可以这里进行一些js的调试,先获取canvas的dom对象,可以使用document来做
1 | var gameCanvas=document.getElementById("gameCanvas") |
运行后,的确message的alert是弹出来了,但canvas里啥动静都没有,难道canvas不是通过click来接收事件吗?
后来我又想,是不是点击的坐标有要求,由于直接操作dom不方便,下面使用JQuery,因为页面本来就有jquery,所以也是方便
1 | $("#gameCanvas").on("click", function(e) { |
运行后发现,console的确打出来了,坐标也对了,但是canvas还是像死了一样。
看来通过js触发canvas的点击事件走不通,其实最好是有一个第三方能够模拟鼠标的点击来给浏览器发送点击的事件,后来找到了chrome的extension,但extension也是需要页面的支持,还是不能完美地模拟地鼠标事件点击。
那没办法了,浏览器不行那系统去模拟总行了吧,由于我使用的是Fedora,鼠标事件模拟点击这种事应该是很容易实现的,最终我找到了xdotool这个工具(可以dnf/下载)。
使用系统的鼠标事件模拟,那思路就很简单了,找到浏览器的window,然后向浏览器发送点击的消息就可以了。xdotool提供了比较全面的功能,可以模拟键盘与鼠标的事件(其实说是模拟,就是帮你把需要外设传进入的数据模拟了,直接发到系统当中,由于系统不知道来源,它也不关心,表象就是鼠标和键盘的动作如我们设定般活动),我写了一个脚本,去模拟这个事件。
1 |
|
脚本不难,大概意思就是找到名为Google Chrome的window,然后向这个window的(300,300)的坐标发送一个鼠标左键的消息。通过–window的参数,可以直接选定把消息发送到哪一个窗口,由于我们需要的是页面的点击而不是浏览器本身的点击,所以鼠标的位置需要有一点偏移,才有可能命中我们需要操作的页面(全屏例外)。
整个的操作就这样子了。
在电脑上可以这么操作,但是有手机上能有办法可以这么搞吗。当然是可以的,大android就是来源于linux,当然也能这样子操作,不过使用的方法有点不一样,事实上,android本身就有直接向驱动发消息的能力,当然,前提是先root。
直接adb shell进去后,使用input命令就可以向驱动发送各种模拟的事件,比方说想发送一个点击home的事件,就可以
1 | input keyevent "KEYCODE_BACK" |
假如需要发送一个点击事件,就可以
1 | input tag x y |
需要说明的是,这些事件都可以选择性地发送到某一位置,如屏幕(touchscreen),触摸板(touchpad)等,详情可查看 input 命令。
既然我们已经知道如何向设备里发消息了,那就很简单了,只是修改一下命令而已
1 | while [ true ]; do input tap 100 300; done |
如此,便可……但是……input命令的效率奇差,差不多要2s才能执行完一条指令, 这样子还怎么玩?
android中存在另一个命令,sendevent,可以直接向设备发消息,这个命令不知道能不能提高点击的效率。由于sendevent需要知道具体的设备文件等,所以我先执行getevent,查找一下设备的情况,我手机当前的设备情况如下:
因为我们需要使用点击的事件,所以需要向/dev/input/event1的设备发送消息。然后这时候点击一下屏幕,getevent会把当前的发送的消息命令都列出来。
这是一次快速点击(按下,抬起)的动作事件,为了好分析,我们使用getevent -l 将后面的三串数据的代表意义给打出来,这一次点击出现
点了几次的结果如上图,第一个EV_ABS,EV_SYN等就类似于协议一样的东西,用来描述绝对坐标,这里当然指的是屏幕的。
类似于ABS_MT_TRACKING_ID应该就是一个点击的序列,接着两个ABS_MT_POSITION_X与ABS_MT_POSITION_Y明显是坐标了,跟着的两个ABS_MT_PRESSURE和ABS_MT_TOUCH_MAJOR应该是与压力感应有关的,然后通过几次的点击测试,发现后面两个信号总是在抬起手指来时才触发,应该是与抬起有关的,大概的分析就到这里了,有需要可以参考(http://source.android.com/devices/input/touch-devices.html)。
我们要做的就是把这些量给重放,通过归纳,可以得出单点触摸时点击的事件最简单有:
1 | ABS_MT_TRACKING_ID (自增,0039) |
这几个。而释放的就有:
1 | ABS_MT_TRACKING_ID (值为-1,0039) |
信号量对应关系:
1 | EV_ABS (十六进制,0003) |
它们的值也已经标到后边了,括号内第一个含义为它们的类型与取值,第二个为这个量本身的值,由于我们是使用了getevent -l才能看到这些有含义的量,但最终使用的时候还是得用回数值。万事俱备了,那么我们只需要将这些重新放出去就可以了,我们在具体输入的时候可以直接使用回十进制,即把0039等数值转为十进制,设置点击的坐标为(300,800),得到以下命令
1 | sendevent /dev/input/event1 3 57 10000 |
可以选择把脚本写在电脑上,然后通过adb shell去执行,也可以直接进入adb shell,然后把脚本写在手机里执行,都可以
1 |
|
效果如下
嗯……还是不尽人意,不过……关我鸟事
1 | [link] |
附:
ABS_MT_POSITION_X: (必须) 报告触碰的X坐标。
ABS_MT_POSITION_Y: (必须) 报告触碰的Y坐标。
ABS_MT_PRESSURE: (可选) 报告触碰的压力大小或者信号强度。
ABS_MT_TOUCH_MAJOR: (可选) 报告触碰的代表性区域, 或者触碰的最长的尺寸。
ABS_MT_TOUCH_MINOR: (可选) 报告触碰的最小的尺寸。 如果ABS_MT_TOUCH_MAJOR报告的是区域测量,则不使用。
ABS_MT_WIDTH_MAJOR: (可选) 报告触碰本身的代表性区域,或者触碰本身的最大尺寸。 如果触碰的尺寸不知道则不使用。
ABS_MT_WIDTH_MINOR: (可选) 报告触碰本身的最小尺寸。 如果触碰的尺寸不知道则不使用。
ABS_MT_ORIENTATION: (可选) 报告触碰的方向。
ABS_MT_DISTANCE: (可选) 报告触碰本身和表面的距离。
ABS_MT_TOOL_TYPE: (可选) 报告触碰是MT_TOOL_FINGER还是MT_TOOL_PEN.
ABS_MT_TRACKING_ID: (可选) 报告触碰的跟踪ID。跟踪ID是一个非负的任意整数,用来分辨多个同时的操作。例如,当多个手指触碰设备,在手指还在屏幕上时每个手指绑定一个独立的跟踪ID,当手指离开屏幕后,跟踪ID可能被重新使用。
ABS_MT_SLOT: (可选) 报告触碰的slot ID,在使用Linux多点触碰协议B的情况下使用。查看Linnux多点触碰协议文档获取详细信息。