Xposed框架实战

介绍

 

一个强大的hook框架

可以编写自定义的模块来hook各种应用的api 从而达到劫持的效果(修改imei 修改地理位置等)

 

什么是hook


hook本质就是劫持函数调用

Hook的难点在于寻找函数的入口点、替换函数,这就涉及到函数的连接与加载机制。

基本原理

 

xposed 原理就是修改系统的关键文件,然后当APP调用系统API时,首先经过xposed,而这些基于xposed的模块就可以选择性的在App调用这些api的时候做一些自己想要的事

情,或者修改返回的结果。

这样app在运行的时候效果就会改变,但app本身并没有被破坏,只是调用系统api的时候,Android系统的表现发生了变化,Xposed作为Java层的重要Hook手段

在一些模拟的场合发挥了重要的作用,比如需要测试模拟的地理位置,就可以Hook掉相关的接口,返回我们需要的值即可,

Xposed的底层原理是通过替换/system/bin/app_precesss 程序控制zygote进程,使得它在系统启动的过程中会加载Xposed framework的一个jar文件即XposedBridge.jar,从而完成

对Zygote进程及其创建的Dalvik虚拟机的劫持,并且能够允许开发者独立的替代任何class,例如framework本身,系统UI又或者随意的一个app。

zygote进程是Android中所有进程的父进程,是init进程之后的第一个进程,所有的进程都是由zygote进程孵化出来,zygote进程会完成虚拟机的初始化,库的加载,

预置类库的加载和初始化等等操作,而以后如果需要开启新的应用程序,zygote进程便会Fork一份出来,从而使应用程序共享了已经加载的资源等等,加快了启动速度,也提高了

性能。而Xposed由于劫持了zygote进程,就相当于劫持了所有的应用程序,从而可以实现Hook掉目标程序达到自己想要的目的

 

开源地址

Xposed源码地址为:https://github.com/rovo89 
Xposed文件下载地址为:http://dl-xda.xposed.info/framework/

Xposed

The native part of the Xposed framework (mainly the modified app_process binary).

XposedBridge

The Java part of the Xposed framework.

XposedTools

Xposed框架打包工具,用于生成不同平台不同android版本的框架文件

XposedInstaller

Xposed傻瓜式安装工具

框架安装

方式一

1:root

2:刷入recovery

3:刷入xposed framework 

方式二

1:root

2:安装XposedInstall

3:使用XposedInstall进行安装

 

方式三

 

使用VirtualXposed 构建沙箱环境

VirtualXposed  基于 VirtualApp 框架

VirtualApp(以下称VA)是一个App虚拟化引擎(简称VA)。VirtualApp创建了一个虚拟空间,

你可以在虚拟空间内任意的安装、启动和卸载APK,这一切都与外部隔离,如同一个沙盒。运行在VA中的APK无需在Android系统中安装即可运行

由于沙盒的存在 Xposed可以很容易安装在这个沙盒里面 

VirtualXposed  = VirtualApp + XposedFramework 

 

 

Xposed的攻与防 -----

Xposed的攻其实就是hook掉目标应用的过程,此处选择微信作为hook对象,实现简单的自动抢红包功能

一:搭建hook环境

1:创建普通studio工程

2:gradle引入xposedBridge工具

      provided 'de.robv.android.xposed:api:81'

      provided 'de.robv.android.xposed:api:81:sources'

  source是源码引入,方便阅读bridge源码

3:创建MainHook类

    MainHook继承自IXposedHookLoadPackage,IXposedHookZygoteInit 并实现handleLoadPackage,initZygote方法

4:在assets目录下创建xposed_init文件并写入MainHook的全局路径

此处即完成了一个基础hook环境的搭建,MainHook的handleLoadPackage和initZygote方法就是hook旅程的出发点

 

二:认识xposedBridge工具

 

AndroidAppHelper

包含有关当前应用程序的各种方法

它包含了获取当前应用的包信息,进程信息,ApplicationInfo。Application,SharedPreferences等对象的方法

 

XModuleResources

XResForwarder

XResources

实现对资源的hook

 

IXposedHookCmdInit(hide)

在对java命令行初始化时进行hook后回调

 

IXposedHookInitPackageResources

在应用的资源文件被完成底层hook后调用,此时你可以进一步hook应用资源

 

IXposedHookLoadPackage

在应用程序源码被完成底层hook后调用,此时你可以进一步hook应用的api及各种源码

 

IXposedHookZygoteInit

该接口在zygote进程被xposed 底层hook的时候后被调用,在该接口的实现中,你可以初始化一些必要的类

也可以在该回调中对android框架的一些东西进行hook

 

XposedBridge

包含Xposed的核心逻辑,提供本地的初始化和各种回调,还提供了各种hook的方法 

比如

log()提供log输出能力

hookAllMethods()hook方法

hookAllConstructors()hook构造方法

invokeOriginalMethod()调用应用的原始方法

 

XposedHelpers

工具类,帮助简化一些流程,对一些hook过程中使用的常用功能方法进行封装

 

XSharedPreferences

提供一个没有兼容性差别的读取Preferences的工具

当hook某个应用的某个方法时,就可以通过xposedbridge jar包里的XSharedPreferences类去加载指定路径下得xml文件来获取返回数据。所以XSharedPreferences不需要写进你编写的插件模块,只需编译即可。在gradle里要把compile或者implementation换成provided。

在主界面编写插件功能,一般都是通过Sharedprefences读写本地数据。XSharedPreferences是在重启手机后,initZygote时,通过XSharedPreferences去加载这个xml文件,把数据都读进内存。需要返回数据时就去map里去查找数据返回数据。

 

三:分析目标应用特征

分析流程:

 

四:开始hook之旅

查找开包界面

 

打开红包领取界面执行

adb shell dumpsys activity activities 列出当前Acitivyt堆栈

* Hist #1: ActivityRecord{cd28b16 u0 com.tencent.mm/.plugin.luckymoney.ui.LuckyMoneyReceiveUI t84}

    packageName=com.tencent.mm processName=com.tencent.mm

    launchedFromUid=10207 launchedFromPackage=com.tencent.mm userId=0

    app=ProcessRecord{cd4bb91 8317:com.tencent.mm/u0a207}

    Intent { cmp=com.tencent.mm/.plugin.luckymoney.ui.LuckyMoneyReceiveUI (has extras) }

    frontOfTask=false task=TaskRecord{93dd6f5 #84 A=com.tencent.mm U=0 StackId=1 sz=2}

    taskAffinity=com.tencent.mm

    realActivity=com.tencent.mm/.plugin.luckymoney.ui.LuckyMoneyReceiveUI

    baseDir=/data/app/com.tencent.mm-dlS_3EalixJL7hZ2pCnECw==/base.apk

    dataDir=/data/user/0/com.tencent.mm

    stateNotNeeded=false componentSpecified=true mActivityType=0

    compat={480dpi} labelRes=0x7f090296 icon=0x7f0205b2 theme=0x7f0c0021

    mLastReportedConfigurations:

     mGlobalConfig={1.0 ?mcc?mnc [zh_CN] ldltr sw360dp w360dp h732dp 480dpi nrml long port finger -keyb/v/h -nav/h appBounds=Rect(00 -

     mOverrideConfig={1.0 ?mcc?mnc [zh_CN] ldltr sw360dp w360dp h732dp 480dpi nrml long port finger -keyb/v/h -nav/h appBounds=Rect(00

    CurrentConfiguration={1.0 ?mcc?mnc [zh_CN] ldltr sw360dp w360dp h732dp 480dpi nrml long port finger -keyb/v/h -nav/h appBounds=Rect(

    taskDescription: iconFilename=null label="null" primaryColor=ff212121

      backgroundColor=fffafafa

      statusBarColor=ff000000

      navigationBarColor=ffe8e8e8

    launchFailed=false launchCount=1 lastLaunchTime=-6s434ms

    haveState=false icicle=null

    state=RESUMED stopped=false delayedResume=false finishing=false

    keysPaused=false inHistory=true visible=true sleeping=false idle=true mStartingWindowState=STARTING_WINDOW_NOT_SHOWN

    fullscreen=false noDisplay=false immersive=false launchMode=1

    frozenBeforeDestroy=false forceNewConfig=false

    mActivityType=APPLICATION_ACTIVITY_TYPE

    waitingVisible=false nowVisible=true lastVisibleTime=-6s33ms

    resizeMode=RESIZE_MODE_FORCE_RESIZABLE_PORTRAIT_ONLY

    mLastReportedMultiWindowMode=false mLastReportedPictureInPictureMode=false

    maxAspectRatio=2.4

可以看出当前红包领取界面的名称是 

LuckyMoneyReceiveUI 

下面开始反编译微信源码 获取LuckyMoneyReceiveUI 的调用逻辑

此处使用的工具是dex-tools和jd-gui-windows

dex-tools用于将微信apk反编译为jar文件

jd-gui-windows用于查看及全局检索jar代码

最终反编译结果如图

 

开始全局搜索LuckyMoneyReceiveUI 

从搜索结果可以看出来 LuckyMoneyReceiveUI 的搜索结果并不多 上图橙色部位的代码应该就是调起红包界面的方法

由此可知 调用打开红包界面的类为 com.tencent.mm.bs.d.class 的 b(Context paramContext, String paramString1, String paramString2, Intent paramIntent)方法

b方法中需要三个参数

参数1:context或者Activity

参数2/参数3:固定值 “luckmoney” “.ui.LucyMonkeyReceiveUI”

参数4:Intent 包含key_way , key_native_url , key_username

那么现在进一步的目标就很了然了  

我们要构造这个Intent 而构造这个Intent 需要三个参数 

 

思路是这样

1:参数一定是跟对方发过来的红包有关系,或者说是包含在红包消息的数据包里面

2:红包消息在接收后一定是要存入到数据库里面 而且一般都是即时存入的

3:找到存数据库的地方 把数据先打印出来看看

 

OK 全局搜索SQLiteDatabase

 

 看到一个wcdb的包名

wcdb是一个微信开源的数据库组件,怀疑数据库读写就是在这个里面 

从wcdb包下找到SQLiteDatabase这个类 查找insert方法 发现最终均调用insertWithOnConflict方法

 

编写代码hook该方法 ,打印数据查看红包参数

 

 

打印出来的信息如下所示

2019-01-25 17:14:23.762 24115-24201/? I/Xposed: param args0:message

2019-01-25 17:14:23.762 24115-24201/? I/Xposed: param args1:msgId

2019-01-25 17:14:23.762 24115-24201/? I/Xposed: param args3 contentValues:

2019-01-25 17:14:23.762 24115-24201/? I/Xposed: bizClientMsgId:

2019-01-25 17:14:23.762 24115-24201/? I/Xposed: talker:wxid_k8t6na2oonzj12

2019-01-25 17:14:23.762 24115-24201/? I/Xposed: flag:0

2019-01-25 17:14:23.762 24115-24201/? I/Xposed: bizChatId:1

2019-01-25 17:14:23.762 24115-24201/? I/Xposed: msgId:18

2019-01-25 17:14:23.762 24115-24201/? I/Xposed: type:436207665

2019-01-25 17:14:23.762 24115-24201/? I/Xposed: content:<msg>

        <appmsg appid="" sdkver="">

            <des><![CDATA[我给你发了一个红包,赶紧去拆!]]></des>

            <url><![CDATA[https://wxapp.tenpay.com/mmpayhb/wxhb_personalreceive?showwxpaytitle=1&msgtype=1&channelid=1&sendid=1000039501201901257013102408071&ver=6&sign=c991c6bad638bfb55fb98642165bbf4986b24239959b02dd7c47059ade626999f9b45c7ded338738a7d11f200fee14ae884072321a1959c65d4cfbc1c4eb197e124209129eca952c1e211e98ef112417]]></url>

            <type><![CDATA[2001]]></type>

            <title><![CDATA[微信红包]]></title>

            <thumburl><![CDATA[https://wx.gtimg.com/hongbao/1800/hb.png]]></thumburl>

            <wcpayinfo>

                <templateid><![CDATA[7a2a165d31da7fce6dd77e05c300028a]]></templateid>

                <url><![CDATA[https://wxapp.tenpay.com/mmpayhb/wxhb_personalreceive?showwxpaytitle=1&msgtype=1&channelid=1&sendid=1000039501201901257013102408071&ver=6&sign=c991c6bad638bfb55fb98642165bbf4986b24239959b02dd7c47059ade626999f9b45c7ded338738a7d11f200fee14ae884072321a1959c65d4cfbc1c4eb197e124209129eca952c1e211e98ef112417]]></url>

                <iconurl><![CDATA[https://wx.gtimg.com/hongbao/1800/hb.png]]></iconurl>

                <receivertitle><![CDATA[恭喜发财,大吉大利]]></receivertitle>

                <sendertitle><![CDATA[恭喜发财,大吉大利]]></sendertitle>

                <scenetext><![CDATA[微信红包]]></scenetext>

                <senderdes><![CDATA[查看红包]]></senderdes>

                <receiverdes><![CDATA[领取红包]]></receiverdes>

                <nativeurl><![CDATA[wxpay://c2cbizmessagehandler/hongbao/receivehongbao?msgtype=1&channelid=1&sendid=1000039501201901257013102408071&sendusername=wxid_k8t6na2oonzj12&ver=6&sign=c991c6bad638bfb55fb98642165bbf4986b24239959b02dd7c47059ade626999f9b45c7ded338738a7d11f200fee14ae884072321a1959c65d4cfbc1c4eb197e124209129eca952c1e211e98ef112417]]></nativeurl>

                <sceneid><![CDATA[1002]]></sceneid>

                <innertype><![CDATA[0]]></innertype>

                <paymsgid><![CDATA[1000039501201901257013102408071]]></paymsgid>

                <scenetext>微信红包</scenetext>

                <locallogoicon><![CDATA[c2c_hongbao_icon_cn]]></locallogoicon>

                <invalidtime><![CDATA[1548494063]]></invalidtime>

                <broaden />

            </wcpayinfo>

        </appmsg>

        <fromusername><![CDATA[wxid_k8t6na2oonzj12]]></fromusername>

    </msg>

从这个红包数据里面我们可以得到几个信息

1:红包数据的type 是 type:436207665 这里可以用来判断数据是一个红包还是一个普通消息

2: key_native_url , key_username 这三个都有了 key_username 其实就是talker  key_native_url就是native_url

只剩一个key_way 不知道是啥但是我们可以hook原先的方法把这个key_way 打印出来看 多次尝试之后 得知这个key_way其实是一个固定值1

数据都拿到了 我们就可以直接调用开红包的方法了 调用该方法需要用到一个context 这里我们直接使用AndroidAppHelper提供的当前的Application的Context

if (AndroidAppHelper.currentApplication() != null) {

    Intent paramau = new Intent();

    paramau.putExtra("key_way"1);

    paramau.putExtra("key_native_url", nativeUrlString);

    paramau.putExtra("key_username", talker);

    XposedHelpers.callStaticMethod(XposedHelpers.findClass("com.tencent.mm.bs.d"lpparam.classLoader), "b"AndroidAppHelper.currentApplication(), "luckymoney"".ui.LuckyMoneyReceiveUI", paramau);

else {

    XposedBridge.log("launcherUiActivity == null" "\n");

}

这个时候红包界面就弹出了 

 

下一步就是点击红包界面上的按钮 领取红包

还是查看LuckyMoneyReceiveUI 的代码 找到界面View初始化的地方

protected final void initView()

{

  this.lOd = findViewById(a.f.lucky_money_receive_ll);

  this.lNh = ((ImageView)findViewById(a.f.lucky_money_receive_sender_avatar));

  this.lNY = ((TextView)findViewById(a.f.lucky_money_receive_sender_nickname));

  this.iLJ = ((TextView)findViewById(a.f.lucky_money_receive_tips));

  this.lmK = ((TextView)findViewById(a.f.lucky_money_receive_wishing));

  this.lNZ = ((Button)findViewById(a.f.lucky_money_recieve_open));

  this.lOc = ((TextView)findViewById(a.f.lucky_money_recieve_check_detail));

  this.lOa = findViewById(a.f.lucky_money_recieve_check_detail_ll);

  this.lOb = ((ImageView)findViewById(a.f.lucky_money_bottom_decoration));

  this.lQd = ((ImageView)findViewById(a.f.lucky_money_recieve_close_btn));

  this.lQd.setOnClickListener(new View.OnClickListener()

  {

    public final void onClick(View paramAnonymousView)

    {

      LuckyMoneyReceiveUI.this.finish();

    }

  });

  this.maxSize = ((int)(com.tencent.mm.cc.a.ab(this.mController.uOU, a.d.lucky_money_goldstyle_envelop_wishing_textsize) * 1.375F));

  this.textSize = com.tencent.mm.cc.a.aa(this.mController.uOU, a.d.lucky_money_goldstyle_envelop_wishing_textsize);

  if (this.textSize > this.maxSize) {}

  for (int i = this.maxSize;; i = this.textSize)

  {

    this.textSize = i;

    this.lmK.setTextSize(0this.textSize);

    this.mController.contentView.setVisibility(8);

    this.tipDialog = com.tencent.mm.ui.base.h.b(this.mController.uOU, getString(a.i.loading_tips), truenew DialogInterface.OnCancelListener()

    {

      public final void onCancel(DialogInterface paramAnonymousDialogInterface)

      {

        if ((LuckyMoneyReceiveUI.a(LuckyMoneyReceiveUI.this) != null) && (LuckyMoneyReceiveUI.a(LuckyMoneyReceiveUI.this).isShowing())) {

          LuckyMoneyReceiveUI.a(LuckyMoneyReceiveUI.this).dismiss();

        }

        LuckyMoneyReceiveUI.this.lVC.bgm();

        if ((LuckyMoneyReceiveUI.b(LuckyMoneyReceiveUI.this).getVisibility() == 8) || (LuckyMoneyReceiveUI.c(LuckyMoneyReceiveUI.this).getVisibility() == 4))

        {

          com.tencent.mm.sdk.platformtools.y.i("MicroMsg.LuckyMoneyReceiveUI""user cancel & finish");

          LuckyMoneyReceiveUI.this.finish();

        }

      }

    });

    bga();

    return;

  }

}

关注一行 

  this.lNZ = ((Button)findViewById(a.f.lucky_money_recieve_open));

傻子都能看出来“LNZ”按钮对象就是 打开红包的按钮 剩下的就是点击这个按钮

从源码可以看出来LNZ按钮在初始化之后并没有立刻设置onClick事件。因此我们找到设置事件的方法 这个方法被混淆后方法名称是c  没错 hook它

public final boolean c(int paramInt1, int paramInt2, final String paramString, m paramm)

 {

   boolean bool2 = true;

   int i = 0;

   final boolean bool1;

   if ((paramm instanceof ag)) {

     if ((paramInt1 == 0) && (paramInt2 == 0))

     {

       this.lTc = ((ag)paramm);

       com.tencent.mm.plugin.report.service.h.nHW.f(11701new Object[] { Integer.valueOf(5), Integer.valueOf(tv(this.lTc.lRd)), Integer.valueOf(bgW()), Integer.valueOf(0), Integer.valueOf(1) });

       paramString = new com.tencent.mm.plugin.wallet_core.model.y();

       paramString.field_mNativeUrl = this.lVM;

       paramString.field_hbType = this.lTc.lRd;

       paramString.field_hbStatus = this.lTc.cet;

       paramString.field_receiveStatus = this.lTc.ceu;

       com.tencent.mm.plugin.wallet_core.model.o.bWd().a(paramString);

       if (this.lTc.ceu == 2)

       {

         b(new w(this.lVK, 110this.lVM, "v1.0"), false);

         bool1 = bool2;

       }

     }

   }

   for (;;)

   {

     return bool1;

     if ((this.tipDialog != null) && (this.tipDialog.isShowing())) {

       this.tipDialog.hide();

     }

     com.tencent.mm.plugin.luckymoney.b.o.a(this.lNh, this.lTc.lRh, this.lTc.lRB);

     com.tencent.mm.plugin.luckymoney.b.o.a(this.mController.uOU, this.lNY, this.lTc.lSU);

     if ((this.lTc.ceu != 1) && (this.lTc.cet != 4) && (this.lTc.cet != 5) && (this.lTc.cet != 1))

     {

       if (!bk.bk(this.lTc.lRe))

       {

         this.iLJ.setText(this.lTc.lRe);

         this.iLJ.setVisibility(0);

       }

       if (!bk.bk(this.lTc.lMy))

       {

         com.tencent.mm.plugin.luckymoney.b.o.a(this.mController.uOU, this.lmK, this.lTc.lMy);

         this.lmK.setVisibility(0);

       }

       this.lNZ.setOnClickListener(new View.OnClickListener()

       {

         public final void onClick(View paramAnonymousView)

         {

           com.tencent.mm.plugin.report.service.h.nHW.f(11701new Object[] { Integer.valueOf(5), Integer.valueOf(LuckyMoneyReceiveUI.tw(LuckyMoneyReceiveUI.d(LuckyMoneyReceiveUI.this).lRd)), Integer.valueOf(LuckyMoneyReceiveUI.e(LuckyMoneyReceiveUI.this)), Integer.valueOf(0), Integer.valueOf(2) });

           paramAnonymousView = LuckyMoneyReceiveUI.this;

           paramAnonymousView.b(new ad(paramAnonymousView.lTc.msgType, paramAnonymousView.lTc.bvt, paramAnonymousView.lTc.lNs, paramAnonymousView.lTc.ces, com.tencent.mm.plugin.luckymoney.b.o.bgq(), com.tencent.mm.model.q.GC(), paramAnonymousView.getIntent().getStringExtra("key_username"), "v1.0"paramAnonymousView.lTc.lSY), false);

           com.tencent.mm.plugin.luckymoney.b.o.c(paramAnonymousView.lNZ);

         }

       });

 

Class<?> luckyMoneyReceiveUI = XposedHelpers.findClass("com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyReceiveUI"loadPackageParam.classLoader);

XposedBridge.hookAllMethods(luckyMoneyReceiveUI, "c"new XC_MethodHook() {

    @Override

    protected void beforeHookedMethod(MethodHookParam param) throws Throwable {

        super.beforeHookedMethod(param);

    }

 

    @Override

    protected void afterHookedMethod(MethodHookParam param) throws Throwable {

        super.afterHookedMethod(param);

        if (!isAll) {

            return;

        }

        Field buttonField = XposedHelpers.findField(param.thisObject.getClass(), "lNZ");

        final Button kaiButton = (Button) buttonField.get(param.thisObject);

        XposedBridge.log("kaiButton: " + kaiButton + "\n");

        kaiButton.performClick();

    }

});

 

到此为止 全部完成 录个视频测试一下

 

 

Xposed的攻与防 -----防

转载自 https://blog.coderstory.cn/about-xposed/

1.尝试加载xposed的类,如果能加载则表示已经安装了

XposedHelpers类中存在fieldCache methodCache constructorCache 这三个静态成员,都是hashmap类型,凡是需要被hook的且已经被找到的对象都会被缓存到这三个map里面。

我们通过便利这三个map来找到相关hook信息。

方法a是检测xposed到底改了什么东西存放到a中可以收集相关信息并上报。

public void b(){

    try{

        Object localObject = ClassLoader.getSystemClassLoader()

                .loadClass("de.robv.android.xposed.XposedHelpers").newInstance();

        // 如果加载类失败 则表示当前环境没有xposed

        if (localObject != null){

            a(localObject, "fieldCache");

            a(localObject, "methodCache");

            a(localObject, "constructorCache");

        }

        return;

    }

    catch (Throwable localThrowable) {}

}

 

方法a是检测xposed到底改了什么东西存放到a中可以收集相关信息并上报。

private void a(Object arg5, String arg6) {

    try {

        // 从XposedHelpers中读取相关的hook信息

        Field v0_1 = arg5.getClass().getDeclaredField(arg6);

        v0_1.setAccessible(true);

        Set v0_2 = v0_1.get(arg5).keySet();

        if(v0_2 == null) {

            return;

        }

        if(v0_2.isEmpty()) {

            return;

        }

        Iterator v1 = v0_2.iterator();

        // 排除无关紧要的类

        while(v1.hasNext()) {

            Object v0_3 = v1.next();

            if(v0_3 == null) {

                continue;

            }

            if(((String)v0_3).length() <= 0) {

                continue;

            }

            if(((String)v0_3).toLowerCase().startsWith("android.support")) {

                continue;

            }

            if(((String)v0_3).toLowerCase().startsWith("javax.")) {

                continue;

            }

            if(((String)v0_3).toLowerCase().startsWith("android.webkit")) {

                continue;

            }

            if(((String)v0_3).toLowerCase().startsWith("java.util")) {

                continue;

            }

            if(((String)v0_3).toLowerCase().startsWith("android.widget")) {

                continue;

            }

            if(((String)v0_3).toLowerCase().startsWith("sun.")) {

                continue;

            }

            this.a.add(v0_3);

        }

    }

    catch(Throwable v0) {

        v0.printStackTrace();

    }

}


2.检测xposed相关文件
检测XposedBridge.jar这个文件和de.robv.android.xposed.XposedBridge
XposedBridge.jar存放在framework里面,de.robv.android.xposed.XposedBridge是开发xposed框架使用的主要接口。
在这个方法里读取了maps这个文件,在linux内核中,这个文件存储了进程映射了的内存区域和访问权限。在这里我们可以看到这个进程加载了那些文件。
如果进程加载了xposed相关的so库或者jar则表示xposed框架已注入。

public static boolean a(String paramString){

   try{

     Object localObject = new HashSet();

     // 读取maps文件信息

     BufferedReader localBufferedReader =

               new BufferedReader(new FileReader("/proc/" Process.myPid() + "/maps"));

     for (;;){

       // 遍历查询关键词

       String str = localBufferedReader.readLine();

       if (str == null) {

         break;

       }

       if ((str.endsWith(".so")) || (str.endsWith(".jar"))) {

         ((Set)localObject).add(str.substring(str.lastIndexOf(" ") + 1));

       }

     }

     localBufferedReader.close();

     localObject = ((Set)localObject).iterator();

     while (((Iterator)localObject).hasNext()){

       boolean bool = ((String)((Iterator)localObject).next()).contains(paramString);

       if (bool) {

         return true;

       }

     }

   }

   catch (Exception paramString) {}

   return false;

 }

 

3.检测堆栈信息
如果你的手机安装了xposed框架,那么你在查看app崩溃时候的堆栈信息,一定能在顶层找到xposed的类信息,
既然xposed想改你的信息,那么在调用栈里面肯定是有它的身影的。比如出现de.robv.android.xposed.XposedBridge这个类,方法被hook的时候调用栈里会出现de.robv.android.xposed.XposedBridge.handleHookedMethod 和de.robv.android.xposed.XposedBridge.invokeOriginalMethodNative这些xposed的方法,在dalvik.system.NativeStart.main方法后出现de.robv.android.xposed.XposedBridge.main调用

try {

        throw new Exception("");

    catch (Exception localException) {

        StackTraceElement[] arrayOfStackTraceElement = localException.getStackTrace();

        int m = arrayOfStackTraceElement.length;

        int i = 0;

        int j;

        // 遍历整个堆栈查询xposed相关信息 检测 ZygoteInit 是否出现了2次

        for (int k = 0; i < m; k = j) {

            StackTraceElement localStackTraceElement = arrayOfStackTraceElement[i];

            j = k;

            if (localStackTraceElement.getClassName()

                    .equals("com.android.internal.os.ZygoteInit")) {

                k += 1;

                j = k;

                if (k == 2) {

                    return true;

                }

            }

            if (localStackTraceElement.getClassName().equals(e)) {

                return true;

            }

            i += 1;

        }

    }

    return false;

}

try{

    throw new Exception("");

}

catch(Exception localException){

    arrayOfStackTraceElement = localException.getStackTrace();

    j = arrayOfStackTraceElement.length;

    i = 0;

}

for(;;){

    boolean bool1 = bool2;

    if (i < j) {

        if (arrayOfStackTraceElement[i].getClassName()

                .equals("de.robv.android.xposed.XposedBridge")) {

            bool1 = true;

        }

    else {

        return bool1;

    }

    i += 1;

}



4.检测方法是否被篡改
Modifier.isNative方法判断是否是native修饰的方法,
xposedhook方法的时候,会把方法转位native方法,我们检测native方法中不该为native的方法来达到检测的目的,
比如getDeviceId,getMacAddress这些方法如果是native则表示已经被篡改了。

public static boolean a(String paramString1, String paramString2, Class... paramVarArgs){

   try{

     // 判断方法是不是用native修饰的

     boolean bool = Modifier.isNative(Class.forName(paramString1)

                  .getDeclaredMethod(paramString2, paramVarArgs).getModifiers());

     if (bool) {

       return true;

     }

   }catch (Exception paramString1){

     paramString1.printStackTrace();

   }

   return false;

 }


5.检测包名
这个是最简单也是最不靠谱的方法,因为只要禁止app读取应用列表就没办法了。

if (((ApplicationInfo)localObject).packageName.equals("de.robv.android.xposed.installer")){

      Log.wtf("HookDetection""Xposed found on the system.");

    }

}


反制xposed

通过反射重写xposed设置,直接禁用
这个方法是酷安里中抄来的。重写application,在onCreate中写入

try {

     Field v0_1 = ClassLoader.getSystemClassLoader()

                 .loadClass("de.robv.android.xposed.XposedBridge")

                 .getDeclaredField("disableHooks");

     v0_1.setAccessible(true);

     v0_1.set(nullBoolean.valueOf(true));

 }catch(Throwable v0) {

 }

 

Xposed 在测试领域的应用

1:渗透测试

2:构造调试数据

3:自动化测试埋点

4:脚本录制

5:构造异常场景

6:app建模

 

本文仅做本人学习记录所用,感谢以下作者

https://blog.coderstory.cn/about-xposed/(摘抄Xposed防御部分段落)

https://blog.csdn.net/xiao_nian/article/details/79391417(提供抢红包思路及摘抄部分代码)

本文所描述的抢红包功能仅限学习交流Xposed使用,由该功能造成的法律问题由使用者承担

 

  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值