基于xposed的android自动化测试技术

-- 基于xposed框架的android app自动化测试技术
【官网】:http://repo.xposed.info/

应用场景

xposed用来做一些自动化操作以经被慢慢普及了,通常对于一个app要自动化测试或者自动化营销类的方案,如果使用adb, uiautomator,辅助服务之类的往往受制于机型,需要考虑兼容性以及繁杂的元素操作及模拟过程。此时xposed框架则非常合适。但xposed在一定程度上对编程,逆向,反编译,模拟组包等有要求。

基础资源

Android, root环境,匹配的xposed框架。

使用须知

由于xposed框架的异常强大,请勿使用xposed框架从事非法活动,法网恢恢疏而不漏,具体案件请自行百度了解。

配置步骤

【Xposed简介】

可以在不修改APK的情况下影响程序的运行,比如:
直接把APP的界面改成自己想要的样子,去掉界面里不喜欢的东西,
自动抢红包,消息防撤回,步数修改等等;简直酷得不行,网上有
很多插件作者开发出来的优秀插件,随手打开Xposed Installer下载
就有很多:

插件用起来是挺爽的,不过呢,因为Xposed拥有最高权限,如果不法分子
在插件里植入了恶意代码,比如登录劫持,偷偷采集你的账号密码发送到
他们的手里,如果涉及到了金钱,就很恐怖啦,所以在使用Xposed插件的时候,
尽量选那些开源的,并进行代码review,看是否存在恶意代码,再进行安装体验
(开源不一定就没问题,之前有个抢外卖红包的开源项目在里面加了一段挖矿代码,
令人窒息的操作!)

大概简述下Xposed的原理吧,后面有一节会专门研究源码~

Android基于Linux,第一个启动的进程自然是init进程,该进程会
启动所有Android进程的父进程——Zygote(孵化)进程,该进程的启动配置在
/init.rc脚本中,而Zygote进程对应的执行文件是/system/bin/app_process
该文件完成类库的加载以及一些函数的调用工作。在Zygote进程创建后,
再fork出SystemServer进程和其他进程。

而Xposed Framework呢,就是用自己实现的app_process替换掉了系统原本
提供的app_process,加载一个额外的jar包,然后入口从原来的:
com.android.internal.osZygoteInit.main()被替换成了:
de.robv.android.xposed.XposedBridge.main()
然后创建的Zygote进程就变成Hook的Zygote进程了,而后面Fork出来的进程
也是被Hook过的。这个Jar包在
/data/data/de.rbov.android.xposed.installer/bin/XposedBridge.jar

大概原理就是这样,源码我还没去撸,后面会研究一波,有说错的再回来改。
另外使用Xposed模块是需要Root权限的.

【xposed基础知识】


IXposedHookLoadPackage接口:App被加载的时候调用,用于App应用的Hook
回调方法是:handleLoadPackage(final XC_LoadPackage.LoadPackageParam lpparam)

XC_LoadPackage.LoadPackageParam:包含与正在加载的应用程序的有关信息。

 

XposedHelpers.findAndHookMethod(Hook的类,classLoader,方法名,参数,回调对象)
Hook一个方法的时候使用,回调对象XC_MethodHook()需重写两个方法
beforeHookedMethod(MethodHookParam param):方法调用前执行
afterHookedMethod(MethodHookParam param) 方法调用后执行
注:可以调用param.setResult()设置方法的返回值!

MethodHookParam:包含与调用方法有关的信息

 

比较关注的是这个thisObject,代表调用该方法的对象实例,如果是静态方法
的话,返回一个Null,比如这里调用onCreate()方法的是MainActivity,获得
的自然是MainActivity实例。

接着是获取成员变量,分为私有与非私有变量,非私有直接调用下述方法
即可获得class

Class c = lpparam.classLoader.loadClass("com.coderpig.cpwechatxposed.MainActivity");

Field field = c.getField("tv");

如果是私有,则需要先设置访问权限(setAccessible)

Class c = lpparam.classLoader.loadClass("com.coderpig.cpwechatxposed.MainActivity");

Field field = c.getDeclaredField("tv");

field.setAccessible(true);

接着调用获得该对象

TextView tv = (TextView) field.get(param.thisObject);

tv.setText("贪玩难约");

IXposedHookZygoteInit:在Zygote启动时调用,用于系统服务的Hook
回调方法initZygote()

IXposedHookInitPackageResources:在资源布局初始化时会回被执行(inflate方法)
回调方法:handleInitPackageResources(XC_InitPackageResources.InitPackageResourcesParam resparam)
InitPackageResourcesParam包含两个参数,包名和XResource(资源相关)

 

有了这个XResource对象,就可以拿到布局资源树了,通过重写hookLayout方法,

 

LayoutInflatedParam,里面这个view就是布局资源树了,你可以拿到遍历,拿到某个
特定控件,然后做一些骚操作。

XposeHelpers提供了一些辅助方法

callMethod(Object obj,String methodName, Object… args):在APP中调用特定方法;
参数依次是:调用方法的所在类,调用方法名,方法参数

findClass(String className,ClassLoader classLoader):获取class类实例
参数依次是类名,类加载器

findMethodExact:通过反射查找类的成员方法(setAccessible(true)设置非私有)

findConstructorExact:通过反射查找构造函数(同样可设置可访问下性)

findAndHookXXX:查找并Hook

setXxx:通过反射设置对象数据成员的值

setStaticXxx:通过反射设置静态变量的值

XposedBridge.log(“日志内容”):输入日志和写入到/data/xposed/debug.log
Xposed Installer日志那里可以看到!

内部类:通过$符号链接内部类

只能Hook方法与构造方法,不能Hook接口和抽象方法

 


【逆向分析手段】
<故意插入异常>
获取异常的堆栈信息,比如在一个反编译的环境中可以加入:
try {
                    throw new NullPointerException(); // 故意抛出一个异常以便打印堆栈信息
                } catch (Exception e) {
                    XposedLogUtils.log("securityCheckHook:" + Log.getStackTraceString(e)); 
                }
<从正常开发人员选用的组件的可能性出发>
比如,如果对于一个弹框警告,肯定会继承"android.app.Dialog"类,弹框显示的时候肯定会调用其"show"方法,我们只需要hook住"android.app.Dialog"类的"show"方法:
findAndHookMethod(Dialog.class, "show", new XC_MethodHook() {
            @Override
            protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                super.afterHookedMethod(param);
                try {
                    throw new NullPointerException(); // 故意抛出一个异常以便打印堆栈信息
                } catch (Exception e) {
                    XposedLogUtils.log("securityCheckHook:" + Log.getStackTraceString(e)); // 打印堆栈信息分析代码的调用逻辑
                }
            }
        })
<全局搜索>
反编译代码中全局搜索
<存在一些假.so>
都是以so文件结尾,但是其实有些它是假的so库,应该属于apk包之类的,将so文件重命名为.zip文件,解压之后通过反编译就可以看到对应的代码
<广撒网的hook,确定流程关系>
当查看代码分析不清楚流程时,可以尝试将对应类的全部方法都hook住,然后打印方法的调用流程,这样就能知道对应操作的方法调用流程
<反射,获得类的成员对象>
当实在找不到对应的反编译代码时,可以通过反射将对应类的所有字段(包括字段名称和类型)、方法(包括方法名称、传入参数、返回参数)都打印出来,这样对类的结构能有一个大致的印象(之前一直以为账单动态库是so库,无法反编译,就是通过这种方式分析账单页面的)
<从界面正常业务逻辑,UI交互出发>
hierarchy view是一个很好的查看布局的工具,我们只有对界面的布局有所了解之后,才能更好的找到突破点
<猜测,验证,排除,缩小范围>
最后一点,一定要大胆猜想和尝试,通过不断的打印日志来验证自己的猜想,这样才能找到对应的解决办法。
【进程间通讯】
1.)由于其他方式需要在AndroidManefest.xml中注册,只有广播可以动态注册,因此可以使用广播.
   <注册广播,用于接收>
    pluginReceiver = new PluginBroadcast();
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(PluginBroadcast.com.eg.android.AlipayGphone.info);
        registerReceiver(pluginReceiver, intentFilter);

    在onDesdroy中释放:        unregisterReceiver(pluginReceiver);
   <发送广播>
    Intent broadCastIntent = new Intent();
                    broadCastIntent.putExtra("consultSetAmountResString", consultSetAmountResString);
                    broadCastIntent.putExtra("cookieStr", cookieStr);
                    broadCastIntent.setAction(PluginBroadcast.INTENT_FILTER_ACTION);
                    Activity activity = (Activity) param.thisObject;
                    activity.sendBroadcast(broadCastIntent);



常见问题

快速入门

【示例学习,仅用于研究,请勿它用】


一:手机安装xposedinstaller 程序。左上菜单栏,点击框架栏,下载需要版本xposed框架,安装成功后重启手机(安装xposed框架需要root权限)。

二:在Android项目新建lib文件夹,将手机xposed框架对应版本jar包放入lib目录,add to buildpath(注意不要直接放到libs目录,会出问题)

三:在AndroidManifest appication节点内加入如下配置:

        <meta-data

            android:name="xposedmodule"

            android:value="true" />

        <meta-data

            android:name="xposeddescription"

            android:value="XposedTest" />

        <meta-data

            android:name="xposedminversion"

            android:value="87" />

xposedminversion 是兼容最低版本

 

四:新建一个类实现IXposedHookLoadPackage接口,实现handleLoadPackage方法。(这个方法用于在加载应用程序的包的时候执行用户的操作)

 

五:在assets目录下新建xposed_init文件,配置入口类(第四步创建的类)。

 

六:示例:通过劫持setonclicklistener,将微信登录页面的更多按钮改变为更少。

判断要hook的应用,通过findAndHookMethod   hook指定方法,重写XC_MethodHook的两个方法beforeHookedMethodafterHookedMethod,这两个方法会在原始的方法的之前和之后执行。

通过XposedBridge.log();输出日志,日志可以在xposedinstaller,菜单栏日志 里查看。

 

:代码写完后安装到手机,在xposinstalller,左上打开菜单,点击模块,可以看到刚安装的模块,勾选后重启手机即可运行。


参考资料