zoukankan      html  css  js  c++  java
  • Xposed模块开发基本方法记录

      由于某些课程实验的要求,需要通过xposed框架对某应用进行hook操作,笔者选用了开源且免费的xposed框架进行实现。虽然网上存在一些利用xposed实现特定功能的文章资源,但大多均将xposed模块的构建作为一个小节内容一笔带过,而且介绍的内容随着考虑的因素、使用的编辑环境不同也大有区别,使得笔者在实际构建过程中往往提心吊胆,出现了错误也不知道如何去改正。故而这里特将笔者最近摸索和学习到的简单xposed构建的知识记录,以供查阅和参考。

    Xposed 框架

      想要使用Xposed模块实现特定的功能,必须依赖Xposed框架,正如 apk 的运行需要Android系统提供支持一样,Xposed模块任务的完成也离不开Xposed框架的支持,Xposed框架提供了Xposed模块运行所必需的环境和功能接口,同时也可以对系统上已安装的模块进行禁用、卸载等管理。故而在想通过Xposed模块完成一定任务之前,必须安装Xposed框架。

      Xposed框架Installer下载地址:http://repo.xposed.info/module/de.robv.android.xposed.installer,安装的过程可参考网络资料。

      注意:由于Xposed框架安装时需要将系统文件夹/system/bin的部分文件进行替换和备份,故而手机需要具备root权限

    Xposed 模块

      下面以Android Studio 3.0.1为例,总结下简单的Xposed模块的编写所需的配置和注意事项。

      项目创建

      Xposed模块实际上是作为一种较为特殊的apk安装在系统上,由于Xposed 模块的功能通常借助于其他应用和系统资源实现,其一般没有应用界面,故而创建一个没有活动的项目即可。

      A.在Android Studio中,点击 file -> new -> new project 进行新项目的创建。指定应用名称、公司域名、存储位置和包名等信息,这里需要注意应用名和包名这两个名字;

      

      B.勾选 Phone and Tablet 选项,选择该 Xposed 模块( 实际就是apk)将要运行的 Android 环境所对应的 API。注意这里的API指定的是Android环境与apk之间交互的接口,只有该API与之后apk实际运行的Android环境相匹配,apk才能正确的安装和运行。如笔者的 Xposed 模块将要运行在 Android 4.4上,则应该选择 Android 4.4 对应的API 19.

      

      C.在接下来的界面中,选择“ add no activity ”即不添加活动( Xposed 模块不需要活动 ),即可完成项目的创建。

      项目配置

      Xposed模块被视为一种特殊的apk,故而在创建项目之外,还需要针对其进行 Xposed 模块相关的配置。

      A. 切换目录结构。点击界面左上方的竖式 Project,展开项目界面,将项目结构从 Android 切换至 Project 模式,方便之后的编辑;

      

      B. 添加 Xposed API依赖。Xposed 模块的功能借助 Xposed 框架实现,Xposed 模块通过对应的 Xposed 框架 API 来使用 Xposed 框架提供的功能。想要使用 Xposed 框架 API,则必须提供对应的库( 名为 XposedBridge API jar)的路径等信息。(以下信息来自 Using the Xposed Framework API )

        *针对 Android Studio

        Android Studio 中使用gradle进行项目的构建,故而想要使用 Xposed 框架 API 对应的库,则需要在 gradle 的配置文件中进行指定。在 应用名(test) -> app -> build.gradle 中加入以下内容:

    1      repositories {  //通常新项目的 build.gradle 中不包含有repositories块,直接在空白处增加1-3行即可
    2          jcenter();       }
    3  
    4      dependencies {  //通常 build.gradle 文件中已包含dependncies块,所以只需将第6行的内容添加进已有的 dependencies 中即可
    5          provided 'de.robv.android.xposed:api:53'
    6        provided 'de.robv.android.xposed:api:53:sources'   //该行是可选的,用于下载API相关的文档等信息
    7  }   

        其中,第2行表示将 jcenter 作为代码仓库,可在上面引用开源项目,而第 6 行则指定引用项目的项目名和版本信息。

        第6行的声明需要注意两点:  

        a) 使用 provided 关键字而不是 compile,后者会将引用的 Xposed 框架 API 类打包至生成的 apk 中,而这些类在安装好的 Xposed 框架中是已经存在的,所以可能会产生冲突,而 provided 关键字则仅保留对 API 的引用,实现具体功能的类则由安装好的 Xposed 框架提供;     

        b)使用系统对应的 Xposed 框架 api 版本,不同的 Xposed 框架 API 在不同的 Android 环境中发挥作用。如Android 4.x 环境则只能选择 API 53。

        

        *针对 eclipse

        eclipse 环境下无法通过指定 jcenter 中的开源项目来实现对其的引用,所以必须在实际的项目中手动添加所需引用的库文件 XposedBridge API jar 的路径,在这里手动下载所需的 API 版本,将其复制到项目的文件夹下( 注意不要放置在项目本身已经有的 lib / libs 文件夹下,原因与Android Studio环境下使用 provided 而不是 compile 一致 ),选中 jar 包 右键 -> Build Path -> add to build path 即可。

      

      C. Android Studio 环境下需要禁用 Instant run 。在 File -> Settings -> Build, Execution, Deployment -> Instant Run 中取消其勾选,否则源程序中的类会由一个 stub 应用加载,而不是直接包含在 apk 文件中,而 Xposed 框架目前无法处理这样的情况。

      D. 修改 AndroidManifest.xml 文件中的属性。在 应用名(test) -> app -> src -> main 文件夹中找到 AndroidManifest.xml,修改其中的属性,使得我们最终生成的 apk 能够被 Xposed 框架识别。请保证最终文件的部分内容为以下格式。

        <application
                android:allowBackup="true"
                android:icon="@mipmap/ic_launcher"
                android:label="@string/app_name"
                android:roundIcon="@mipmap/ic_launcher_round"
                android:supportsRtl="true"
                android:theme="@style/AppTheme" >    // App相关的信息
    
        <meta-data
                android:name="xposedmodule"    //标志该 apk 为一个 Xposed 模块,供 Xposed 框架识别
                android:value="true" />
        <meta-data
                android:name="xposedminversion"   
                android:value="53" />       //xposed最小版本号,请与(2)步中选用的api版本保持一致
        <meta-data
                android:name="xposeddescription"
                android:value="hook a function" /> //针对该模块的描述,会在Xposed框架的模块管理界面中显示出来,便于模块管理
    
        </application>

      完成上述步骤后,则可将项目文件打包成 apk ( Android Studio 下打包 apk 的简单步骤可以参见这里),并在手机中安装。安装成功后,在 Xposed 框架的模块管理界面会出现该模块对应的管理项,证明对 Xposed 模块的配置是成功的。勾选对应的模块,并重启设备,重启之后该模块则开始发挥功能。当然目前我们并没有实现任何功能。

      

      简单示例

      在完成 Xposed 框架的配置后可在该项目中进行框架功能的编写。在 应用名(test) -> app -> src -> main -> java 目录下,可以看到 Android Studio 已经自动根据我们在创建项目时指定的包名生成了一个包( package )的条目,如笔者项目中的包名为 xposed.test(见创建项目时的界面截图),选中该包右键 -> new -> java  ->class ,创建自己的一个 class ,如 test.class 。用户可在该 class 中实现简单的xposed hook功能。

      ( 以下部分不涉及具体的实现原理,仅提供函数的简单功能的描述,更多关于Xposed 框架API的功能可在这里查看)

      一个 Xposed 模块可以有多个不同的入口,其中实现的方法既可以在 Android 系统启动时被调用,也可在一个应用程序包被加载时被调用,想要在不同的时期被调用,则该功能类需要实现不同的接口。

        Xposed公共接口
        package:de.robv.android.xposed.IXposedHookZygoteInit       
        interface :IXposedHookZygoteInit   //在Android系统启动时被调用,作用于初始的zygote进程,可用于实现应用于所有应用的hook
    
        package:de.robv.android.xposed.IXposedHookLoadPackage
        interface:IXposedHookLoadPackage  //当指定应用被加载时被调用,一般用于hook特定应用的方法
    
        package:de.robv.android.xposed.IXposedHookInitPackageResources
        interface:IXposedHookInitPackageResources //指定应用的资源进行初始化时被调用,一般用于资源的替换

       

      以实现 IXposedHookLoadPackage 接口的模块为例,实现一个简单的hook功能。

      通常使用 findAndHookMethod 方法来定位待 hook 的类中的方法(官方解释在这里)

        findAndHookMethod(String className, ClassLoader classLoader, String methodName, Object... parameterTypesAndCallback)
         className:被hook的方法所在类的完整名称,包括 包名 + 类名
         classLoader:可通过 lpparam.classLoader 获得
          methodName:被hook的方法的名,注意如有混淆,则应该用混淆后的名字
          object... 与为被hook的方法的参数对应
          Callback  :指定该方法被调用时,需要被执行的回调

      例如,想要hook如下代码,即hook包 com.test.example 中的类 a 下的 b 方法

        package com.test.example
        public class a{
             public int b(String s, int i, MyClass m) 
             {}
        }    

      则对应的 findAndHookMethod方法应该写作

       findAndHookMethod("com.test.example.a", lpparam.classLoader,"b", String.class, int.class, "com.test.example.MyClass", new XC_MethodHook() {
       @Override
       protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
         do somthing
       }
     });
        这里需要注意:
        (1)对于待hook函数中的java自带类型的参数,如 int 和 String,则应使用 int.class 和 String.class 作为findAndHookMethod的参数
        (2)若待Hook函数中包含有形如 MyClass 这样的自定义类型的参数,则直接使用该类型的完整路径名即可,如 "com.test.example.MyClass" 

      在 findAndHookMethod 方法的参数中,直接定义了一个 XC_MethodHook 回调,其中一般有两个方法可供使用者重新定义。

       @Override
          protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
              // 该方法在被hook函数之前被调用
          }
         @Override
          protected void afterHookedMethod(MethodHookParam param) throws Throwable {
              // 该方法在被hook函数之后被调用
          }

      以下代码实现了微信的一个日志函数的简单 hook 。

     1 package xposed.xposed_wechat_log;  //实现的类所在的包
     2 
     3 import android.util.Log;       //系统提供的Log方法的包
     4 import static de.robv.android.xposed.XposedHelpers.findAndHookMethod;  //findAndHookMethod方法所在的包
     5 import de.robv.android.xposed.IXposedHookLoadPackage;           
     6 import de.robv.android.xposed.XC_MethodHook;
     7 import de.robv.android.xposed.callbacks.XC_LoadPackage.LoadPackageParam;
     8 
     9 public class test implements IXposedHookLoadPackage {  //test类实现了IXposedHookLoadPackage接口
    10     @Override
    11     public void handleLoadPackage(LoadPackageParam lpparam) throws Throwable {  // handleLoadPackage 在任意包被加载时被调用,参数lpparam,包含有加载的 app 的信息
    12         if ( lpparam.packageName.equals("com.tencent.mm")) {             // 可通过 lpparam 中包含的包信息对包进行筛选,如这里表示 com.tencent.mm 包加载时,才执行下一步操作
    13     findAndHookMethod("com.tencent.mm.sdk.platformtools.x",lpparam.classLoader, "d" , String.class, String.class, Object[].class,
    14             new XC_MethodHook() {
    15                 @Override
    16                 protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
    17                     String str0  = (String) param.args[0];              //可通过 param.args[i]获得 hook 方法的参数
    18                     String str1  = (String) param.args[1];
    19                     Object[] obj2= (Object[]) param.args[2];
    20                     String  str = obj2 == null ? str1 : String.format(str1, obj2);
    21 
    22                     if( str==null)
    23                         str = "";
    24 
    25                     Log.e( "Xposed_hook_d"+str0,str);
    26                     super.beforeHookedMethod(param);
    27                 }
    28             });
    29      } 
    30   } 
    31 }
    simple_hook

      

      完成上述模块功能的编写后,需在 应用名(test) -> app -> src -> main 目录下新建一个 assets 目录,并在该目录下建立一个名为 exposed_init 的文本文件。该文件中记录模块中所有实现了Xposed 功能接口的类的完整路径名,每一行书只写一个这样的路径。

      如在上述实例模块中,test 类实现了接口 IXposedHookLoadPackage,则 exposed_init 文件中应该有以下内容:

        xposed.test.test  //其中 xposed.test 为包名,test为实现接口的类名

      所有实现了 Xposed 接口的类均要在该文件中记录,以供 Xposed 框架进行处理。

      之后即可打包完成的项目,将其安装至设备上进行验证了。

    参考资料:

      1. [TUTORIAL]Xposed module development详细的xposed模块开发示例

      2. Using the Xposed Framework API关于如何在模块中使用Xposed框架提供的API

      3.  Development tutorial简单的xposed模块示例

  • 相关阅读:
    git(1)-git关联GitHub-windows-转载
    jenkins(4)-jenkins配置邮件通知
    jenkins(3)-linux下安装jenkins(yum install方式)
    【PAT甲级】1090 Highest Price in Supply Chain (25 分)(DFS)
    【PAT甲级】1087 All Roads Lead to Rome (30 分)(MAP【int,string】,邻接表,DFS,模拟,SPFA)
    【PAT甲级】1018 Public Bike Management (30 分)(DFS,SPFA)
    Educational Codeforces Round 61 (Rated for Div. 2) G(线段树,单调栈)
    Atcoder Grand Contest 032C(欧拉回路,DFS判环)
    Educational Codeforces Round 62 (Rated for Div. 2)E(染色DP,构造,思维,组合数学)
    Atcoder Grand Contest 031C(构造,思维,异或,DFS)
  • 原文地址:https://www.cnblogs.com/yhjoker/p/8653020.html
Copyright © 2011-2022 走看看