xposed的使用实践

在测试移动端app的时候,抓包过程中可能都会遇到app客户端做了sign,导致无法修改数据包的情况,这个时候一般我们就需要反编译客户端,查找到sign的生成算法,破解算法后,再模拟请求。但这样成本老高了,客户端再做了代码混淆,很难分析。这个时候,可以使用xposed,hook劫持一些关键函数的返回值,让客户端生成我们期待的请求包。

xposed 可以在不修改APK的情况下影响程序运行(修改系统)的框架服务,基于它可以制作出许多功能强大的模块,且在功能不冲突的情况下同时运作。

 

实践测试。

我们以想看app测试。

https://app.mi.com/details?id=com.xiangkan.android&ref=search

首先反编译客户端,下载后的apk进行解压。

 

 

 提取其中dex文件

放到dex2jar.20目录下,记得去下载官方最新版的dex2jar,低版本啥也还原不出来

命令:

D:\downloads\dex2jar-2.0>d2j-dex2jar classes.dex

  还原成jar包

然后使用jd-gui打开,4个dex全部还原成jar包

通过app抓包

查看个人信息的请求为:

https://feed.browser.miui.com/api/oauth/bindinfo

其返回结果为

 

 

由于有requestsign,修改分包会报错,分析请求中userId参数,那么这里可能存在越权遍历信息的问题。

通过jd-gui中搜索*bindinfo(注意有个*)

 

 

找到定义的抽象接口,其中有个抽象 getBindInfo函数,定义了请求地址。查询这个函数发现它在UserInfoEditActivity.class中被调用。

 

 其中有个getUserId函数,在实体类User中定义

 

 

那么我只要hook这个功函数,劫持它的返回结果为我们测试的userid,即可验证是否存在越权。

 

接下来才是正题

 xposed 下载地址:

https://forum.xda-developers.com/t/official-xposed-for-lollipop-marshmallow-nougat-oreo-v90-beta3-2018-01-29.3034811/

授权root权限后激活即可。顺便提一下我的环境是小米4,android6.0,miui10开发版

idea新建项目,选择empty activity,创建成功后,

1.在AndroidManifest.xml中添加如下代码

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.xkxposeddemo">

<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">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>

<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<meta-data
android:name="xposedmodule"
android:value="true" /> <!--告诉xposed框架这是一个xposed模块-->
<meta-data
android:name="xposeddescription"
android:value="这是一个Xposed例程" /> <!--模块描述-->
<meta-data
android:name="xposedminversion" android:value="54" /><!--模块支持的最低版本-->
</application>


</manifest>

  

2.下载XposedBridgeApi-54.jar,app目录下创建一个lib目录,注意是lib目录,不是放到libs目录去,把jar复制进去

链接:https://pan.baidu.com/s/1WNSr4iJSZxXXvWe87vOGjg
提取码:61uw

bulid.gradle加入

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'androidx.appcompat:appcompat:1.0.0-beta01'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.2'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test:runner:1.1.0-alpha4'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0-alpha4'
    compileOnly files('lib/XposedBridgeApi-54.jar')
}

  

gradle刷新一下

 

4.创建HookTest.class

测试内容为:

package com.example.xkxposeddemo;

import java.lang.reflect.Array;
import java.security.PublicKey;
import java.util.Arrays;
import java.util.Map;

import android.util.Log;
import android.widget.Toast;
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 {
    private static final String HOOK_APP_NAME = "com.example.xkxposeddemo";

    public void handleLoadPackage(XC_LoadPackage.LoadPackageParam lpparam) throws Throwable {
        XposedBridge.log("Hook初体验:" + lpparam.processName + ":" + lpparam.packageName);
    }
}

  

5.main目录下创建assets文件夹,放入文件xposed_init内容为 包名.HookTest

6.运行安装app,xposed模块中出现你写的app说明xposed已经识别出这是个xopsed模块,如果没有,自己去看看xposed有没有激活成功,或者配置错误。选中后,重启设备

7.勾选掉Instan Run中的所有选项

8.运行app如果出现

 

 说明hook成功,如果没有出现,仔细检查以上步骤是否正确,比如包名是不是写错了?比如jar文件是不是放到libs文件夹去了?还是找不到错误的话,xposed日志模块里有日志信息,自行审计一下。

 

好,以上只是验证xposed hook测试成功,接下里,是实战测试。前面已经分析过了,我们要hook的函数是getUserId,那么重新编写HookTest

package com.example.xkxposeddemo;

import java.lang.reflect.Array;
import java.security.PublicKey;
import java.util.Arrays;
import java.util.Map;

import android.os.SystemClock;
import android.util.Log;
import android.widget.Toast;
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;

import static de.robv.android.xposed.XposedHelpers.findAndHookMethod;

public class HookTest implements IXposedHookLoadPackage {
    private static final String HOOK_APP_NAME = "com.xiangkan.android";
    Class class_hook;

    public void handleLoadPackage(final XC_LoadPackage.LoadPackageParam lpparam) throws Throwable {
        //我们要hook的包名
        if(!lpparam.packageName.equals(HOOK_APP_NAME))
            return;
        XposedBridge.log("Xposed 加载的 app: " + lpparam.packageName);
        //创建一个线程,用于实时的动态发现要hook的类
        new Thread(new Runnable(){
            @Override
            public void run() {
                String className = "com.bikan.coordinator.router.account.entity.User";
                //监听是否触发到了需要的hook类名
                while(true) {
                    SystemClock.sleep(50);
                    try {
                        class_hook = lpparam.classLoader.loadClass(className);
                        XposedBridge.log("Find Class: " + className);
                        break;
                    } catch (Exception e) {
                        Log.e("Find Class error", "ERROR: run: " + e.getMessage());
                    }
                }
                //发现指定的类名后,第三个参数写你要hook函数名,后边的跟对应的函数参数类别,比如String.class,如果没有参数不要即可。
                findAndHookMethod(className, lpparam.classLoader, "getUserId", new XC_MethodHook(){

                    //hook函数,劫持函数返回结果
                    protected void afterHookedMethod(MethodHookParam param)
                            throws Throwable {
                        //真实的id
                        String trueResult=(String) param.getResult();
                        XposedBridge.log("trueUserID:"+trueResult);
                        String result = "1496838";
                        param.setResult(result );
                        XposedBridge.log("hook ----ok,nowUserId:"+result);
                    }

                });

            }
        }).start();
    }
}

  

 注意以上代码中的红色部分,你需要填写你hook的包名,类名,函数名,以及hook的结果。

安装app后,重启设备,打开想看app,再次抓包,发现userId已经变成了我们hook的值,而请求也正确的返回了结果,可惜的是,此处验证并不存在越权问题。

 

 

 

 logcat中的日志

 

 

 

测试时发现,只hook了com.bikan.coordinator.router.account.entity.User中的getUserId只修改了bindInfo接口中的userId,如果想修改所有接口中的userId可以修改com.bikan.reading.model.User中的,这样能更快捷的测试其他接口是否存在越权。

 以上是对afterHookedMethod的使用介绍,这是劫持返回结果,但很多时候我们还要劫持的是函数输入内容接下来介绍的就是beforeHookedMethod

//第三个参数是要hook的函数名,byte[].class是要hook的函数参数类型
findAndHookMethod(className, lpparam.classLoader, "a",byte[].class, new XC_MethodHook(){

                    //hook函数之前劫持输入参数内容
                    protected void beforeHookedMethod(MethodHookParam param)
                            throws Throwable {
//param.args[0]对应的参数位置 param.args[0]=strToByteArray("<script>alert(1)</script>"); } });

 也是一样的,需要的关键内容,hook需要的类、函数、参数。

以上就是xposed的测试实践,有不对之处还希望能够指点,感谢

posted @ 2021-02-05 09:43  妇愁者纞萌  阅读(694)  评论(0编辑  收藏  举报