这篇文章上次修改于 786 天前,可能其部分内容已经发生变化,如有疑问可询问作者。
背景
手机需要打验证码
被否定的方案
- 基于无障碍服务的模拟滑动 被否原因——需要额外安装软件
- 基于shell执行sendevent命令 被否原因——滑动不流畅
最终方案
- 基于shell向输入设备写入数据流
记录
首先进入shell然后执行getevent -lt
此时点击屏幕或者滑动屏幕,可以看到一系列输出
add device 1: /dev/input/event1
name: "hisi_on"
add device 2: /dev/input/event0
name: "hisi_gpio_key"
add device 3: /dev/input/event2
name: "fingerprint"
add device 4: /dev/input/event3
name: "HI6250_hi6555c_CARD Headset Jack"
add device 5: /dev/input/event4
name: "cyttsp5_mt"
could not get driver version for /dev/input/mice, Not a typewriter
[ 109.609577] /dev/input/event4: EV_ABS ABS_MT_TRACKING_ID 00000024
[ 109.609577] /dev/input/event4: EV_ABS ABS_MT_POSITION_X 000003d6
[ 109.609577] /dev/input/event4: EV_ABS ABS_MT_POSITION_Y 000002c7
[ 109.609577] /dev/input/event4: EV_ABS ABS_MT_PRESSURE 00000060
[ 109.609577] /dev/input/event4: EV_SYN SYN_REPORT 00000000
[ 109.619927] /dev/input/event4: EV_ABS ABS_MT_TRACKING_ID ffffffff
[ 109.619927] /dev/input/event4: EV_SYN SYN_REPORT 00000000
这个时候能看到具体输入设备是什么,比如可能是/dev/input/event4
不同手机对应的可能不同
那么现在改成这个命令getevent -lt /dev/input/event4
,输出就是下面这样
[ 169.833199] EV_ABS ABS_MT_TRACKING_ID 00000031
[ 169.833199] EV_ABS ABS_MT_POSITION_X 0000034d
[ 169.833199] EV_ABS ABS_MT_POSITION_Y 00000368
[ 169.833199] EV_ABS ABS_MT_PRESSURE 000000d9
[ 169.833199] EV_SYN SYN_REPORT 00000000
[ 169.858516] EV_ABS ABS_MT_TRACKING_ID ffffffff
[ 169.858516] EV_SYN SYN_REPORT 00000000
既然可以getevent
那么自然也能sendevent
,具体参考文末的通过sendevent实现多点连续滑动
有了前面的输出结果,那么对应的就可以转换成sendevent
命令,重现刚才的事件
use: sendevent device type code value
注意sendevent
的参数是10进制
type code value
对应的定义数值可以在系统内核源代码中的input.h
文件找到,好在大部分都是一致的
将命令转换为sendevent指令,并放置于shell执行,可行,但是会和通过sendevent实现多点连续滑动
评论中说到那样,一卡一卡的
即使向同一个shell中写入执行命令,依然难以解决这个问题
查阅sendevent.c
源码,最终将数据写入设备的代码是
ret = write(fd, &event, sizeof(event));
event
结构体的定义如下
struct input_event {
struct timeval time;
__u16 type;
__u16 code;
__s32 value;
};
struct timeval {
time_t tv_sec; /* seconds */
suseconds_t tv_usec; /* microseconds */
};
typedef unsigned long time_t;
typedef unsigned long suseconds_t;
如此一来就知道写入对应的数据类型了
为了准确获取数据,通过cat获取数据 -> cat /dev/input/event4 > /sdcard/tmp.bin
数据如下(16进制):
4C C7 B9 60 00 00 00 00 42 C9 05 00 00 00 00 00 03 00 39 00 36 00 00 00
4C C7 B9 60 00 00 00 00 42 C9 05 00 00 00 00 00 03 00 35 00 C9 02 00 00
4C C7 B9 60 00 00 00 00 42 C9 05 00 00 00 00 00 03 00 36 00 D0 02 00 00
4C C7 B9 60 00 00 00 00 42 C9 05 00 00 00 00 00 03 00 3A 00 E2 00 00 00
4C C7 B9 60 00 00 00 00 42 C9 05 00 00 00 00 00 00 00 00 00 00 00 00 00
4C C7 B9 60 00 00 00 00 77 4E 06 00 00 00 00 00 03 00 39 00 FF FF FF FF
4C C7 B9 60 00 00 00 00 77 4E 06 00 00 00 00 00 00 00 00 00 00 00 00 00
结合之前获取的输出简单总结如下,另外可以知道写入的数据是小端
时间(整数部分) | 时间(小数部分) | type | code | value |
---|---|---|---|---|
4字节 | 4字节 | 2字节 | 2字节 | 4字节 |
时间整数和小数部分后面都有4字节的0,emm不知道怎么来的
最终解析和生成二进制流的代码如下
import time
import struct
def parse(raw: bytes):
if len(raw) != 24: return
ts_1, _, ts_2, _, _type, _code, _value = struct.unpack('<LLLLHHI', raw)
print(f'{ts_1}.{ts_2}', _type, _code, _value)
def generate(_type: int, _code: int, _value: int):
ts_1, ts_2 = f'{time.time()}'.split('.')
return struct.pack('<LLLLHHI', int(ts_1), 0, int(ts_2), 0, _type, _code, _value)
line = '4C C7 B9 60 00 00 00 00 42 C9 05 00 00 00 00 00 03 00 39 00 36 00 00 00'
raw = binascii.a2b_hex(''.join(line.split()))
parse(raw)
raw = generate(3, 57, 54)
print(binascii.b2a_hex(raw, ' ').decode('utf-8').upper())
可以进行上述转换后,现在可以将生成的文件通过cat
写入/dev/input/event4
来完成模拟滑动了
已有 4 条评论
在androdi8.1 nexus 6p 设备中复现时, 发现device是/dev/input/event0 看源码时发现event是一样的http://androidxref.com/8.1.0_r33/xref/external/toybox/toys/android/sendevent.c
但是获取到的时间戳是85739.316801, 它这是基于那个时间的偏移量吗
@dangerous 那个时间是开机到当前的秒数https://stackoverflow.com/questions/34385733/convert-timestamp-obtained-from-adb-shell-getevent-to-hhmmss-sss-format-using
另外使用cat 往/dev/input/event0写的时候 报错 cat: xwrite: Invalid argument,是姿势不对吗ヾ(≧∇≦*)ゝ
@dangerous 一般是数据格式不对,可以cat对应的设备 把数据定向到一个文件里面,然后看看对应的内容是怎么构成的