安卓跨进程点击事件的解决方案

2016-08-01   出处: 搜狗测试  作/译者: sunny

问题背景

最近在做安卓自动化的时候,发现使用instrument自带的Instrumention.sendPointerSync向其他应用程序发送点击事件的时候,没有效果,而且报出错误:

Permission denied,injecting event from pid  XXX XXX uid  to window XXX owned by uid XXX.

简单翻译过来就是:从一个应用程序向另外一个应用程序发送事件,因为两个程序的uid不一致,导致权限不够。

 

错误原因

下面是我从网上找到的关于这个错误原因的答案,原文可参考:http://www.java123.net/984624.html

  一、分析 android.app.Instrumentation的sendpointerSync的实现原理

(1)android.app.Instrumentation

   

    (2)android.hardware.input.InputManager

    

    (3)android.hardware.input.IInputManager

   

最后是调用Binder.transect进行跨进程调用。被调用者是系统服务,可追朔到InputManagerService的injectInputEvent。

 (4)/frameworks/base/services/java/com/android/server/input/InputManagerService.java

    

   在这个函数中,终于发现了我们要找的SecurityException INPUT_EVENT_INJECTION_PERMISSION_DENIED是jni层的nativeInjectInputEvent

(4)/frameworks/base/services/jni/com_android_server_input_InputManagerService.cpp

    

   (6)/frameworks/base/services/input/InputDispatcher.cpp

    

    终于发现了INPUT_EVENT_INJECTION_PERMISSION_DENIED踪迹,继续查看checkInjectionPermission:

    

    查看系统日志,发现是造成Permission验证失败的原因是:当前windowHandle(被测app)->owneruid与注入者(instrument)->injectoruid不一致。并且windowHandle(被测app)的owneruid竟然是1000(系统账户)!

 因为从1.5后android系统做了限制,不允许跨进程注入,这个方法只能在自己这个程序内用,home出去就不行了。


解决方案

跨进程点击常用的解决方案如下:

(1)第一种只用adb命令

命令很简单,比如说点击事件: input  tap 100 100 (点击 坐标 100,100)

 

(2)直接往linux底层/dev/input/event*写事件

第一、分析android界面捕获事件的流程。

用户在屏幕上点击一下后,程序里面的OnClickListener是怎样收到这个事件的。

大致流程如下:户点击-(硬件驱动部分)硬件产生一个中断,往/dev/input/event*写入一个相应的信号->jni部分,android循环读取/dev/input/event*的事件,再分发给WindowManagerServer,最后再发到相应的ViewGroup和View。这里可以通过往/dev/input/event*写信号的方式,来达到模拟事件的目的,接下来关心的就是信号的协议了。

第二、按键协议分析

通过adb shell getevent 命令可以获取事件流信息。如下图所示:


BS_MT_TRACKING_ID //是用来追踪一次点击的标识,每次点击累加,与最后的ABS_MT_TRACKING_ID和SYN_REPORT 形成一个完整的点击事件。第二个参数就是点击的x坐标,第三个是y坐标。

得到这些值后,我们就可以组织我们的点击事件了,注意这里输出的都是16进制的,使用命令时要使用十进制的数字。每种手机的事件流可能不太一样,我们适配了红米note的事件流,如下图所示:

    HM NOTE 1LTEW

    sendevent /dev/input/event2 3 57 710

    sendevent /dev/input/event2 3 53 x

    sendevent /dev/input/event2 3 54 y

    sendevent /dev/input/event2 0 0 0

    sendevent /dev/input/event2 3 57 4294967295

    sendevent /dev/input/event2 0 0 0

  第三、sendevent原理分析

我们使用了sendevent命令进行点击。翻看安卓的sendevent源码我们可以看到,其实现就是对event文件进行写入操作。那么我们也可以做同样的事情。具体的jni代码实现如下。在实现中,为了实现快速的点击,我只是第一次写事件时进行文件的打开操作,之后都用同一个文件句柄进行写入。用完了之后再通过jni接口释放掉该句柄。 具体的源码实现请参考安卓源码:

https://github.com/android/platform_system_core/blob/master/toolbox/sendevent.c

 第四、封装sendevent注入命令

实现点击操作:

(4)通过hook方法绕过权限验证

  通过之前的原因分析,我们可以看出实在checkInjectpermission这个函数的时候验证失败导致的,checkInjectpermission函数中还调用了hasInjectPermission函数,hasInjectPermission的的函数实现如下:

哎呀,我去,到这里才发现injectorUid==0的话,这个权限检查就过了。。。。。。

那么Uid为0代表什么呢?UID 为0 代表着是root账号,是最高权限。

 

那么通过hook方式通过权限检查的思想就是:hook中native InjectInputEvent,,将uid的值改为0,这样就能够通过里面hasInjectPermission的权限验证了。

代码实现如下:

   

这种需要手机连着电脑,在pc端执行monkey命令,并不是很方便,因此不推荐

除了以上介绍的几种方法之外,还有一种方法,不过我还没尝试,有兴趣的可以试试,就是以父进程的方式启动另外你需要注入点击事件的app,这样的话,它就是你的子进程,就可以随机进行点击。Ps之前讲的这些方法都需要root权限,小编比较推荐使用hook的方式。



声明:本文为本站编辑转载,文章版权归原作者所有。文章内容为作者个人观点,本站只提供转载参考(依行业惯例严格标明出处和作译者),目的在于传递更多专业信息,普惠测试相关从业者,开源分享,推动行业交流和进步。 如涉及作品内容、版权和其它问题,请原作者及时与本站联系(QQ:1017718740),我们将第一时间进行处理。本站拥有对此声明的最终解释权!欢迎大家通过新浪微博(@测试窝)或微信公众号(测试窝)关注我们,与我们的编辑和其他窝友交流。
675° /6752 人阅读/0 条评论 发表评论

登录 后发表评论