0%

Android Hook native层获取系统输入

在上一篇文章 Android 外部 imgui 绘制 (二)–补充补充说明了 ue4 游戏下触摸输入的获取方法。本篇介绍一种更为通用的获取系统输入的方法.

原理

在 Android 系统中获取系统输入的原理是读取 "/dev/input/event" 下的文件,监听是否有系统输入。然后将不同的输入进行加工,然后分发.
在安卓系统中输入事件被分成了

1
2
3
KeyEvent(键盘输入)
MotionEvent(触摸输入)
...

参考
/frameworks/native/libs/input/Input.cpp

每一种 Event 在分发之前都会经过初始化的流程,以触摸事件为例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
1345  void InputConsumer::initializeMotionEvent(MotionEvent* event, const InputMessage* msg) {
1346 uint32_t pointerCount = msg->body.motion.pointerCount;
1347 PointerProperties pointerProperties[pointerCount];
1348 PointerCoords pointerCoords[pointerCount];
1349 for (uint32_t i = 0; i < pointerCount; i++) {
1350 pointerProperties[i].copyFrom(msg->body.motion.pointers[i].properties);
1351 pointerCoords[i].copyFrom(msg->body.motion.pointers[i].coords);
1352 }
1353
1354 ui::Transform transform;
1355 transform.set({msg->body.motion.dsdx, msg->body.motion.dtdx, msg->body.motion.tx,
1356 msg->body.motion.dtdy, msg->body.motion.dsdy, msg->body.motion.ty, 0, 0, 1});
1357 event->initialize(msg->body.motion.eventId, msg->body.motion.deviceId, msg->body.motion.source,
1358 msg->body.motion.displayId, msg->body.motion.hmac, msg->body.motion.action,
1359 msg->body.motion.actionButton, msg->body.motion.flags,
1360 msg->body.motion.edgeFlags, msg->body.motion.metaState,
1361 msg->body.motion.buttonState, msg->body.motion.classification, transform,
1362 msg->body.motion.xPrecision, msg->body.motion.yPrecision,
1363 msg->body.motion.xCursorPosition, msg->body.motion.yCursorPosition,
1364 msg->body.motion.displayWidth, msg->body.motion.displayHeight,
1365 msg->body.motion.downTime, msg->body.motion.eventTime, pointerCount,
1366 pointerProperties, pointerCoords);
1367 }

注意到,event->initialize 就是初始化一个 MotionEvent.
而这个函数 (InputConsumer::initializeMotionEvent) 就是需要 Hook 的函数.
参考
/frameworks/native/libs/input/InputTransport.cpp

过程

随便观察一个 app 进程的 maps, 过滤得到 libinput.so 的路径.

libinput

这个 libinput.so 就包含上面 InputConsumer::initializeMotionEvent 的代码.

拉到电脑上,ida 打开.

ida
得到它的导出函数符号是

1
_ZN7android13InputConsumer21initializeMotionEventEPNS_11MotionEventEPKNS_12InputMessageE

用 Dobby 进行 hook

1
2
3
4
5
6
7
8
9
static void hook_input()
{
void *sym_input = DobbySymbolResolver(NULL, "_ZN7android13InputConsumer21initializeMotionEventEPNS_11MotionEventEPKNS_12InputMessageE");
if (NULL != sym_input)
{
LOGD("_ZN7android13InputConsumer21initializeMotionEventEPNS_11MotionEventEPKNS_12InputMessageE:%llx", (unsigned long long)sym_input);
DobbyHook(sym_input, (void *)new_input, (void **)&ori_input);
}
}

在 new_input 就可以得到系统输入了.

1
2
3
4
5
6
7
8
9
10
11
12
static void new_input(void *thiz, void *a2, void *a3)
{
if (NULL == ori_input)
{
LOGD("failed to get original input");
return;
}
ori_input(thiz, a2, a3);
// LOGD("new_input");
LOGD("action:%d", AMotionEvent_getAction((AInputEvent *)thiz));
return;
}

log

如果出现崩溃等 bug, 请检查是否是 DobbySymbolResolver 的问题,请指定第一个参数为 libinput.so 的路径
例如:

1
void *sym_input = DobbySymbolResolver("system/lib/libinput.so","_ZN7android13InputConsumer21initializeMotionEventEPNS_11MotionEventEPKNS_12InputMessageE");