万物皆可Hook!重新捡起Hook神器-Xposed框架

2019-11-05 12:30:18 浏览数 (1)

作者: Lateautumn4lin

来源:云爬虫技术研究笔记

首先注意!!这个Hook不是邓紫棋要给你唱的Hook哦!

而是在程序界流传的强大秘技-Hook函数,Hook原意是指钩子,它表示的就是在某个函数的上下文做自定义的处理来实现我们想要的黑科技。 在很多技术领域都存在的这种Hook技术,比如下面这些:

  • PythonWeb框架中,如DjangoFlask都存在这种Hook技术,可以在请求的上下文应用的上下文做自定义操作。
  • Scrapy框架中,可以自定义MiddlerWare,在请求解析的时候做自定操作。
  • K8S编排框架中,我们也可以在执行某些函数的上下文中插入Hook函数,这也是和Web框架同理

而今天我们讲解的是关于AndroidHook技术,而有一款神器能够帮助我们快速地开发Hook模块,也就是Xposed框架。

其实网上的关于Xposed模块编写的教程可谓是一抓一大把。但由于时间的推移,很多工具和方法都发生了变化(如Eclipse退出安卓编程舞台,AndroidStudio 不断升级导致其一些设置也随之变化等)也正因此,网上的教程往往有一些时限性,比如现如今 provide 这个关键字已经被舍弃了却仍有人在用,还有些说要把jar包放到lib文件夹而非libs文件夹……种种错误或者落伍的教程对新手产生了很大的误导。

之前也搞过一阵子Xposed框架,而今天在重新部署环境的时候参考某些教程的时候也遇到了很多的坑,所以想重新结合最新的配套工具写个小教程,主要讲解的以下两个方面:

  • Xposed框架介绍以及原理
  • Xposed框架实战

Xposed框架介绍以及原理

XposedGithubrovo89大佬设计的一个针对Android平台的动态劫持项目,通过替换/system/bin/app_process程序控制Zygote进程,使得app_process在启动过程中会加载XposedBridge.jar这个jar包,从而完成对Zygote进程及其创建的Dalvik虚拟机的劫持。

因为Xposed工作原理是在/system/bin目录下替换文件,在install的时候需要root权限,但是运行时不需要root权限。

看到这里很多人会很懵,什么是Zygote?简单来说在Android系统中,应用程序进程都是由Zygote进程孵化出来的,而Zygote进程是由Init进程启动的。Zygote进程在启动时会创建一个Dalvik虚拟机实例,每当它孵化一个新的应用程序进程时,都会将这个Dalvik虚拟机实例复制到新的应用程序进程里面去,而一个应用程序进程被Zygote进程孵化出来的时候,不仅会获得Zygote进程中的Dalvik虚拟机实例拷贝,还会与Zygote一起共享Java运行时库。这也就是可以将XposedBridge这个jar包加载到每一个Android应用程序中的原因。XposedBridge有一个私有的Native(JNI)方法hookMethodNative,这个方法也在app_process中使用。这个函数提供一个方法对象利用JavaReflection机制来对内置方法覆写。。。。等等这些都会借鉴各路大神的思路和分析,总而言之,就是从底层替换方法,可以让我们在不修改APK源码的情况下,通过自己编写的模块来影响程序运行的框架服务,实现类似于自动抢红包、微信消息自动回复等功能。

其实,从本质上来讲,Xposed模块也是一个Android程序。但与普通程序不同的是,想要让写出的Android程序成为一个``Xposed 模块,要额外多完成以下四个硬性任务:

硬性任务清单

1、让手机上的xposed框架知道我们安装的这个程序是个xposed模块。

2、模块里要包含有xposed的API的jar包,以实现下一步的hook操作

3、这个模块里面要有对目标程序进行hook操作的方法。

4、要让手机上的xposed框架知道,我们编写的xposed模块中,哪一个方法是实现hook操作的。

这就引出我即将要介绍的四大件(与前四步一一对照):

四大件

1、AndroidManifest.xml

2、XposedBridgeApi-xx.jar 与 build.gradle

3、实现hook操作的具体代码

4、xposed_Init

牢记以上四大件,按照顺序一个一个实现,就能完成我们的第一个Xposed模块编写。以上的原理我们大致就介绍这么多,下面我们实战开始吧!

Xposed框架实战

1. 迈开第一步,新建项目并编辑AndroidManifest.xml

我们使用的IDEAndroid Studio,首先打开AndroidStudio(以版本3.4.2为例,还在用老版本的请升级),建立一个工程,提示我们选择“Activity”,那就选一个Empty Activity吧。(这个是模块的界面,随意选择即可)。

2、快速运行模板的Xposed模块

我们可以把项目查看方式设置为Project模式,以方便查看。然后在 “项目名称/app/src/main/”目录下找到AndroidManifest.xml,打开这个文件,并在指定位置插入以下三段代码:

代码语言:javascript复制
<meta-data
          android:name="xposedmodule"
            android:value="true" />
        <meta-data
            android:name="xposeddescription"
            android:value="微信hook" />
        <meta-data
            android:name="xposedminversion"
            android:value="53" />

效果如图:

插入代码之后,我们可以点击Run运行App

不过,此时会出现如图提示,也就是缺少Device设备来运行这个App

下一步我们要把手机连接Android Studio,连接的办法很多,包括通过USB连接(物理连接)Wifi连接(也就是网络连接),我们为了节省方法,就采用物理连接,Ps: 有关于远程连接可以参考这篇文章,连接好我们的实体机之后我们点击这里

我们等待Android Studio连接手机,连接好我们就可以看到在Logcat选项里面看到我们的手机运行的日志报告。

有如图所示的日志打印之后我们就会发现我们就可以运行了,点击Run之后会提示我们的手机安装我们刚才刚写的Apk,不过我的手机提示安装时验证超时,不能直接安装,苦恼,以后选手机也要选个正常的。关于Android Studio安装Apk失败的原因可以参考这篇文章,既然我们不能直接安装Apk,我们就使用adb直接来安装

安装好应用之后我们在Xposed框架中勾选我们刚才的模块,然后我们重启一下Xposed框架,就可以啦

这一步只是说明Xposed框架已经认出了我们写的程序。但先别高兴太早——虽然框架已经觉得他是一个Xposed模块了,但我们自己心里清楚,这个模块还啥都不会干呢。下一步,我们让这个模块长点本事。

3、搞定XposedBridgeApi-xx.jar 与 build.gradle

我们知道,Xposed模块主要功能是用来Hook其他程序的各种函数。但是,如何让前一步中的那个“一穷二白”的模块长本事呢?那就要引入 XposedBridgeApi.jar 这个包,你可以理解为一把兵器,模块有了这把宝刀才能施展出Hook本领。很多以前的老教程都需要手动下载诸如XposedBridgeApi-54.jarXposedBridgeApi-82.jarjar包,然后手工导入到libs目录里,才能走下一步道路,而这些jar没有官方的渠道来安装,通常只是一个传一个的,都不知道变成了什么版本。其实在最新的AndroidStudio 3.1以后,我们完全不用这么麻烦,只需要多写一行代码,就让AndroidStuido自动给我们配置XposedBridgeApi.jar!下面操作开始:

“项目名称/app/src/main/”目录下找到build.gradle,在图示位置加上:

代码语言:javascript复制
repositories {

    jcenter()

}
compileOnly 'de.robv.android.xposed:api:82'

compileOnly 'de.robv.android.xposed:api:82:sources'

这句代码是告诉AndroidStuido使用jcenter作为代码仓库,从这个仓库里远程寻找 de.robv.android.xposed:api:82这个API。这个网上很少有Xposed教程介绍它的!(我们不用自己找XposedBridgeApi.jar了。注意!此处要用compileOnly这个修饰符!网上有些写的是provide,但是现在已经停用了!坑人啊!)

写完之后, build.gradle会提示文件已经修改,是否同步。点击 “sync now”,同步即可:

等待依赖构建完成

【Ps:如果网络不通,或者同步不畅,就不要进行第三步的repositories { jcenter()}这个步骤了,改做这个步骤:手动下载XposedBridgeApi-82.jar,拖放到“项目名称/app/libs/”里面(不是网上说的单独建立lib文件夹,那是很久以前的故事了!),然后右键“Add As Library”自行添加这个jar包。而compileOnly ‘de.robv.android.xposed:api:82′compileOnly ‘de.robv.android.xposed:api:82:sources’这两句仍然照常添加。】

好了,我们现在已经搞好了所有的准备工作。下一步,就要开始“施展刀法”(编写hook代码)了。

4、实现hook操作的具体代码

在“施展刀法”(编写hook代码)之前,我们先要立一个靶子。在界面上画一个按钮,并在MainAcitiviy里写代码如下:

代码语言:javascript复制
package com.example.wx;

import android.support.v7.app.AppCompatActivity;

import android.os.Bundle;

import android.view.View;

import android.widget.Button;

import android.widget.Toast;

public class MainActivity extends AppCompatActivity {

    private Button button;

    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);

        button = (Button) findViewById(R.id.button);

        button.setOnClickListener(new View.OnClickListener() {

            public void onClick(View v) {

                Toast.makeText(MainActivity.this, toastMessage(), Toast.LENGTH_SHORT).show();

            }

        });

    }

    public String toastMessage() {

        return "我未被劫持";

    }

}

而在页面布置的文件中,也就是activity_main.xml中增加如下红框的代码

这个靶子很简单:MainActivity界面有个按钮,点击按钮后会弹出一个toast提示,该提示的内容由toastMessage()方法提供,而toastMessage()的返回值为“我未被劫持”

现在,我们已经做好了我们的App了,下面我们正式开始“施展刀法”(编写hook代码) 来hook我们的MainActivity并修改这个类的toastMessage()方法,让它的返回值为“你已被劫持”

5、在MainActivity的同级路径下新建一个类“HookTest.java”

代码语言:javascript复制
package com.example.wx;

import de.robv.android.xposed.IXposedHookLoadPackage;

import de.robv.android.xposed.XC_MethodHook;

import de.robv.android.xposed.XposedBridge;

import de.robv.android.xposed.XposedHelpers;

import de.robv.android.xposed.callbacks.XC_LoadPackage;

public class HookTest implements IXposedHookLoadPackage {

    public void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {

        if (loadPackageParam.packageName.equals("com.example.root.xposd_hook_new")) {

            XposedBridge.log(" has Hooked!");

            Class clazz = loadPackageParam.classLoader.loadClass(

                    "com.example.root.xposd_hook_new.MainActivity");

            XposedHelpers.findAndHookMethod(clazz, "toastMessage", new XC_MethodHook() {

                protected void beforeHookedMethod(MethodHookParam param) throws Throwable {

                    super.beforeHookedMethod(param);

                    //XposedBridge.log(" has Hooked!");

                }

                protected void afterHookedMethod(MethodHookParam param) throws Throwable {

                    param.setResult("你已被劫持");

                }

            });

        }

    }

}

由代码可知,我们是通过IXposedHookLoadPackage接口中的handleLoadPackage方法来实现Hook并篡改程序的输出结果的。代码中“com.example.wx”是目标程序的包名,”com.example.wx.MainActivity”是想要Hook的类,“toastMessage”是想要Hook的方法。我们在afterHookedMethod方法(用来定义Hook了目标方法之后的操作)中,修改了toastMessage()方法的返回值为“你已被劫持”。在完成代码编写之前,说一下为什么要Hook toastMessage这个方法,我们先用Jadx查看一下我们Apk的源代码

这个源码是没有经过混淆的,所以我们可以看到这个源码和我们之前写的一样,我们根据项目结构可以判断出我们需要Hook的函数。

OK,以上用来hook的代码编写完毕,让我们进行下一步操作。

6、最后一步,添加入口点

右键点击 “main ”文件夹 , 选择new –> Folder –>Assets Folder,新建assets文件夹:

然后右键点击 assets文件夹, new–> file,文件名为xposed_init(文件类型选text),并在其中写上入口类的完整路径(就是自己编写的那一个Hook类),这样,Xposed框架就能够从这个xposed_init读取信息来找到模块的入口,然后进行Hook操作了:

好了,曙光就在前面!最后选择禁用Instant Run:单击 File -> Settings -> Build, Execution, Deployment -> Instant Run,把勾全部去掉。 我们重新之前的安装Xposed模块的方法,运行模块,点击,奇迹出现~

大功告成!!!

结语

从上面的实战中我们可以发现Hook的基本原理以及步骤,重新看看我们之前说的四大步,Hook的关键其实我们需要知道针对哪个模块的哪个方法进行Hook

像我们这个例子很简单,没有特意的进行代码混淆以及程序入口改写等等,我们寻找还是很简单的,一般市面上的App都是有很多反Xposed的行为,我们其实要学习的还有很多,这个小教程就当做个小入门吧。

下一篇文章和最近工作上的需求有关系,针对的是2019.10.28之后搜狗微信关闭了在某些公众号内搜索的功能,所以我们想要获取最新的公众号文章就不能采取搜狗微信这个渠道了,网上有很多教程都在谈论其他的方法,相比较来说,还是Hook这个渠道是最实际的,我们将会在之后的文章里详细谈论,大家可以期待一下~

注意:项目已经完成,想要获得源码可以关注下面的微信号,回复“hook入门”即可获得项目地址以及现成的Apk

0 人点赞