-
-
[原创]逆向篇三:解决Flutter应用不能点击问题
-
发表于: 2022-9-15 20:51 17748
-
由于我们的设备改变了原先系统的布局,将应用的window平移缩小,然后留出其他区域用于其他功能布局。导致IMS也需要修改适配,那么鼠标事件作为IMS的一部分,自然也做了响应的修改,导致了我们对flutter应用点击的不兼容。
拓展:大致介绍下IMS的工作流程,IMS分为java层和native层,java层InputManagerService通过jni函数nativeInit创建NativeInputManager用于java层和native层通讯,NativeInputManager构造时会创建InputManager,而这个InputManager对象就保存有InputReader和InputDispatcher。InputReader负责事件的读取,然后扔给InputDispatcher,InputDispatcher把事件分发给对应的window。接下里就是应用内部自己的事件处理。
我们修改的代码就在InputDispatcher.cpp里,处理鼠标事件的源码
处理鼠标事件的函数
查找下源码的这个0x2002事件
可以看到到MotionEntry对象的souce值为0x2002,结合源码可以看到0x2002为鼠标事件,当为鼠标事件的时候,把source的值改为0x5002,然而我们并没有在源码中看到这个0x5002是代表什么事件,那么大概率是离职小伙伴的自定义事件。
从上面截图也可以看到有几个action的事件被return false或者标记上清除,那么接下来我们看看action,根据frameworks源码java层和native层的定义可以看到这个action是一一对应的,并且根据名称很容易知道,这几个是什么行为
/framework/src/main/java/org/robolectric/shadows/NativeAndroidInput.java
frameworks/native/include/android/input.h
所以结合上面的解读,我们就知道了在鼠标事件时,把鼠标事件的source值改为0x5002,并且,如果是hover(悬停)相关的行为要么就返回不让继续往下执行事件分发,要么就直接清理掉。
为了更好的验证问题,我自己动手写了一个简单的flutter应用,主要代码如下:
app界面
然后用pixel进行调试,点击底部五个的按钮
点击事件堆栈:
根据堆栈信息发现flutter事件分发的起点是flutter/bin/cache/pkg/sky_engine/lib/ui/hooks.dart的_dispatchPointerDataPacket方法
然后切换我们的设备,并且根据pixel的堆栈信息,逐个在方法入口处打下断点,看看是哪步导致没有往下走
发现在up事件时找到了在调用这个sweep方法时state为空,被return了。那再看看正常手机是什么情况
这里可以看到正常的手机这个state对象是不为空的,那么接下来看看这个state是什么时候被添加,或者是什么时候被移除了。
通过AndroidStudio的搜索引用工具,发现有三处remove和一处put,那么很简单,这几处都下个断点。看看真机是怎么走的。
pixel确实在Down事件的时候,添加了这个pointer对应的state。
那再看看我们的设备是什么情况,一通调试下来,发现并不会进这个add方法,于是跟着堆栈往上找,发现Down事件时这isPointerAllowed方法返回了false导致不去添加这个state。
这个isPointerAllowed为什么返回false呢?继续进入这个方法内部
于是我们发现这个event.buttons的值为0,不匹配所有case的情况,进入default分支,返回false。看到这个buttons=0,我们立马联想到源码里的buttonState强制赋值为0的情况,这个也对应了flutter中state获取到为空的情况。就是下图的源码
接下来,我想通过ida把这个值强制改掉,重新生成so,然后推进我们的设备。
通过AndroidStudio导出设备中这个模块对应的so(libinputfinger.so),然后打开ida,搜索导出函数
convertMouse2Touch,结合源码进行解读。
entry[21]和entry[27]地址相差6个指针
从结构体看也是差6个指针位
● 结合上面两张源码和ida的伪代码可以看到entry[21] = 20482 =
0x5002,所以这里是对source字段进行赋值;
● bottomState和source字段在内存空间的布局相差6个指针,那么entry[27] = 0即为对bottomState进行赋值;
● entry->policyFlags
|= 0没有任何意义,则被编译器优化了;
既然知道了entry[27]就是对bottomState进行赋值,那么我们来改改看。
可以看到entry[27]对应的是这行汇编代码,改它!!
然后到这里我遇到了一个问题,ida的keypatch不支持苹果M1
MAX的Macbook。既然这样那就直接改源码,然后把libinputfinger.so推进去替换掉。
改完以后初始化环境,然后编译一下这个模块
然后把libinputfinger.so push进手机,接下来重启手机,再次打开我们自己编写的flutter应用测试
然后我们设备的这个方法就进来了,flutter也确实可以点击了,然后我们去验证一下其他原生点击和webView的点击。
经过一番测试发现其他都正常的,但是WebView不能点击跳转了。由于WebView也是原生事件转换成js的事件的,所以这几个hover事件不给肯定也会产生影响。所以把这三个hover事件也给分发下去。
重新编译libinputfinger.so并推入手机,然后WebView的世界也恢复正常了。
总结:通过编写flutter应用,然后在onTap(flutter的点击回调)事件做调试,查看正常手机的堆栈,可以很容易的找到flutter点击事件的源头,然后在整个堆栈都下断,再由我们的设备来触发点击,就能找到和真机的区别在bottonState的值导致了down事件时没有把事件添加进去,然后up事件时发现容器内没有down事件的记录,就抛弃了这次up事件,进而导致flutter没有点击事件的回调。在翻看源码的时候我们也发现这个值被强制赋值成0,那么把它改掉,问题就能解决了,至于后面的WebView也是一样只要做一个简单的尝试,看看鼠标连上手机后是否正常点击,然后我们源码把哪几个事件丢弃了,还原一下。
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)