Loading [MathJax]/jax/output/CommonHTML/config.js
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >[Android][Security] Android 逆向之 xposed

[Android][Security] Android 逆向之 xposed

作者头像
wOw
发布于 2020-01-20 09:15:57
发布于 2020-01-20 09:15:57
2.5K00
代码可运行
举报
文章被收录于专栏:wOw的Android小站wOw的Android小站
运行总次数:0
代码可运行

Xposed

网上关于Xposed的介绍很多,但都是点到为止,比如:

Android系统中,应用程序进程以及系统服务进程SystemServer都是由Zygote进程孵化出来的,而Zygote进程是由Init进程启动的,Zygote进程在启动时会创建一个Dalvik虚拟机实例,每当它孵化一个新的应用程序进程时,都会将这个Dalvik虚拟机实例复制到新的应用程序进程里面去,从而使得每一个应用程序进程都有一个独立的Dalvik虚拟机实例,这也是Xposed选择替换app_process的原因。 Zygote进程在启动的过程中,除了会创建一个Dalvik虚拟机实例之外,还会注册一些Android核心类的JNI方法到Dalvik虚拟机实例中去,以及将Java运行时库加载到进程中来。而一个应用程序进程被Zygote进程孵化出来的时候,不仅会获得Zygote进程中的Dalvik虚拟机实例拷贝,还会与Zygote一起共享Java运行时库,这也就是可以将XposedBridge这个jar包加载到每一个Android应用程序中的原因,

我当然不会满足于这么一点浅薄的介绍,既然用这个框架了,那就得把这个框架搞清楚对不?

一句话原理:

Xposed框架的原理是通过替换/system/bin/app_process程序控制zygote进程,使得app_process在启动过程中会加载XposedBridge.jar这个jar包,从而完成对Zygote进程及其创建的Dalvik虚拟机的劫持。

为什么是app_process

Android系统是基于Linux内核的,而在Linux系统中,所有的进程都是init进程的子孙进程,也就是说,所有的进程都是直接或者间接地由init进程fork出来的。Zygote进程也不例外,它是在系统启动的过程,由init进程创建的。在系统启动脚本system/core/rootdir/init.rc文件中,我们可以看到启动Zygote进程的脚本命令:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
    socket zygote stream 666
    onrestart write /sys/android_power/request_state wake
    onrestart write /sys/power/state on
    onrestart restart media
    onrestart restart netd

系统启动之后就可以在/dev/socket目录下看到有一个名为zygote的文件,就是zygote占用的socket端口。

所以,zygote是由app_process启动的,替换app_process后,启动的就是Xposed之后的zygote了。

为什么XposedBridge可以生效

Xposed版zygote进程在启动时会创建一个Dalvik虚拟机实例,以及注册一些Android核心类的JNI方法到Dalvik虚拟机实例中去。同时Xposed版zygote把XposedBridge.jar添加到CLASSPATH环境变量,并将Java运行时库加载到进程中。一个应用程序进程被Zygote进程孵化出来的时候,不仅会获得Zygote进程中的Dalvik虚拟机实例拷贝,还会与Zygote一起共享Java运行时库,所以XposedBridge.jar可以被加载到每一个Android应用程序中。

zygote进程加载XposedBridge将所有需要替换的Method通过JNI方法hookMethodNative指向Native方法 xposedCallHandler,xposedCallHandler在转入handleHookedMethod这个Java方法执行用户规定的Hook Func。

Xposed版zygote在启动时还会获得一个JNIEnv实例,该实例描述的是zygote进程的主线程的JNI环境,Xposed版zygote进程通过JNIEnv实例的成员函数CallStaticVoidMethod()调用de.robv.android.xposed.XposedBridge的main函数作为java代码的入口点。

de.robv.android.xposed.XposedBridge.main函数做了以下几件事:

(1) 初始化xposed框架。

(2) 调用initForZygote()方法hook应用进程创建时调用的一些关键函数,比如通过挂钩LoadedApk的构造函数获得应用进程的相关信息并保存至XC_LoadPackage.LoadPackageParam的实例中,该实例在后续hook应用程序中的函数时可用于获取应用程序相关信息。通过挂钩handleBindApplication方法,可以在应用程序启动时调用所有IXposedHookLoadPackage类型的钩子(其实最终调用的是IXposedHookLoadPackage的handleLoadPackage方法)。该类型的钩子用于对应用程序进行挂钩,假如要hook应用程序中的函数,我们编写的xposed插件中的钩子类必须实现IXposedHookLoadPackag接口,重写它的handleLoadPackage方法并在方法体中调用xposed框架提供的挂钩函数(比如findAndHookMethod)hook想要挂钩的应用程序函数。

(3) 调用loadModules()加载所有的xposed插件,将这些插件中不同钩子类型的钩子分别保存起来。有三种类型的钩子,IXposedHookLoadPackage类型的钩子对应用程序挂钩,IXposedHookZygoteInit类型钩子对Zygote的初始化进行挂钩,IXposedHookInitPackageResources类型钩子对资源进行挂钩。

(4) 最后再调用原始的ZygoteInit.main函数,完成zygote的全部初始化工作。

http://4hou.win/wordpress/?p=7516

https://blog.csdn.net/u014385722/article/details/82013306

使用Java反射实现API Hook

通过对 Android 平台的虚拟机注入与 Java 反射的方式,来改变 Android 虚拟机调用函数的方式(ClassLoader),从而达到 Java 函数重定向的目的,这里我们将此类操作称为 Java API Hook。

先从简单的开始,比如尝试Hook按钮的点击事件。

首先先看一下点击事件:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**
 * Interface definition for a callback to be invoked when a view is clicked.
 */
public interface OnClickListener {
    /**
     * Called when a view has been clicked.
     *
     * @param v The view that was clicked.
     */
    void onClick(View v);
}

我们对Button绑定点击事件:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
mBtnHijack = findViewById(R.id.btn_hijack);
mBtnHijack.setOnClickListener(v -> {
  Toast.makeText(MainActivity.this, "Click button", Toast.LENGTH_LONG).show();
});

所以下一步是看setOnClickListener方法是怎么保存OnClickListener接口的:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public void setOnClickListener(@Nullable OnClickListener l) {
  if (!isClickable()) {
    setClickable(true);
  }
  getListenerInfo().mOnClickListener = l;
}

看到OnClickListener被保存到ListenerInfo的成员变量中:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
ListenerInfo getListenerInfo() {
  if (mListenerInfo != null) {
    return mListenerInfo;
  }
  mListenerInfo = new ListenerInfo();
  return mListenerInfo;
}

static class ListenerInfo {
	...
  public OnClickListener mOnClickListener;

  protected OnLongClickListener mOnLongClickListener;

  protected OnContextClickListener mOnContextClickListener;
  ...
}

而ListenerInfo是View的一个内部类。

既然知道OnClickListener的保存位置,那么我们要Hook点击事件,就是创建一个自己的点击事件,然后替换掉原来的事件即可。

先创建一个实现自己功能的点击事件

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class HookedOnClickListener implements View.OnClickListener {
  private View.OnClickListener origin; // 原始的点击事件

  HookedOnClickListener(View.OnClickListener origin) {
    this.origin = origin;
  }

  @Override
  public void onClick(View v) {
    Toast.makeText(MainActivity.this, "hook click", Toast.LENGTH_SHORT).show();
    Log.i("WOW", "Before click, do what you want to to.");
    if (origin != null) {
      origin.onClick(v); // 执行原始的点击逻辑
    }
    Log.i("WOW", "After click, do what you want to to.");
  }
}

然后就是使用反射,用我们的OnClickListener替换原来注册的点击回调:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
private void hookOnClickListener(View view) {
  try {
    // 得到 View 的 ListenerInfo 对象
    Method getListenerInfo = View.class.getDeclaredMethod("getListenerInfo");
    // 强制访问
    getListenerInfo.setAccessible(true);
    // 执行getListenerInfo拿到对象
    Object listenerInfo = getListenerInfo.invoke(view);
    // 得到 原始的 ListenerInfo 类
    Class<?> listenerInfoClz = Class.forName("android.view.View$ListenerInfo");
    // 从 ListenerInfo找到onClickListener属性
    Field mOnClickListener = listenerInfoClz.getDeclaredField("mOnClickListener");
    mOnClickListener.setAccessible(true);
    // 用前面的listenerInfo对象获取原始的listener
    View.OnClickListener originOnClickListener = (View.OnClickListener) mOnClickListener.get(listenerInfo);
    // 用自定义的 OnClickListener 替换原始的 OnClickListener
    View.OnClickListener hookedOnClickListener = new HookedOnClickListener(originOnClickListener);
    mOnClickListener.set(listenerInfo, hookedOnClickListener);
  } catch (Exception e) {
    Log.w("hook clickListener failed!", e);
  }
}

把这段代码放到按钮设置OnClickListener之后:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
mBtnHijack.setOnClickListener(v -> {
	Toast.makeText(MainActivity.this, "Click button", Toast.LENGTH_LONG).show();
});
hookOnClickListener(mBtnHijack);

这样就完成了对按钮点击事件的Hook。

但是这只能编码Hook自己的应用,这样做的意义是什么呢?

当应用内接入了众多的 SDK,SDK 内部会使用系统服务 NotificationManager 发送通知,这就导致通知难以管理和控制。现在我们就用 Hook 技术拦截部分通知,限制应用内的通知发送操作。

发送通知是由NotificationManager的notify方法实现,通过查看源码,定位到:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public void notifyAsUser(String tag, int id, Notification notification, UserHandle user)
    {
        INotificationManager service = getService();
        ...
        try {
            service.enqueueNotificationWithTag(pkg, mContext.getOpPackageName(), tag, id, copy, user.getIdentifier());
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

private static INotificationManager sService;

/** @hide */
static public INotificationManager getService()
{
  if (sService != null) {
    return sService;
  }
  IBinder b = ServiceManager.getService("notification");
  sService = INotificationManager.Stub.asInterface(b);
  return sService;
}

INotificationManager 是跨进程通信的 Binder 类,sService 是 NMS(NotificationManagerService) 在客户端的代理,发送通知要委托给 sService,由它传递给 NMS。我们发现 sService 是个静态成员变量,而且只会初始化一次。只要把 sService 替换成自定义的不就行了么,确实如此。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
private void hookNotificationManager(Context context) {
  try {
    NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
    // 得到系统的 sService
    Method getService = NotificationManager.class.getDeclaredMethod("getService");
    getService.setAccessible(true);
    final Object sService = getService.invoke(notificationManager);

    Class iNotiMngClz = Class.forName("android.app.INotificationManager");
    // 动态代理 INotificationManager
    Object proxyNotiMng = Proxy.newProxyInstance(getClass().getClassLoader(), new Class[]{iNotiMngClz}, new InvocationHandler() {

      @Override
      public Object invoke(Object proxy, Method method, Object[] args) throws Throwable           		  {
        log.debug("invoke(). method:{}", method);
        if (args != null && args.length > 0) {
          for (Object arg : args) {
            log.debug("type:{}, arg:{}", arg != null ? arg.getClass() : null, arg);
          }
        }
        // 操作交由 sService 处理,不拦截通知
        // return method.invoke(sService, args);
        // 拦截通知,什么也不做
        return null;
        // 或者是根据通知的 Tag 和 ID 进行筛选
      }
    });
    // 替换 sService
    Field sServiceField = NotificationManager.class.getDeclaredField("sService");
    sServiceField.setAccessible(true);
    sServiceField.set(notificationManager, proxyNotiMng);
  } catch (Exception e) {
    log.warn("Hook NotificationManager failed!", e);
  }
}

Hook 的时机还是尽量要早,我们在 attachBaseContext 里面操作。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Override
protected void attachBaseContext(Context newBase) {
    super.attachBaseContext(newBase);
    hookNotificationManager(newBase);
}

这样我们就完成了对通知的拦截,可见 Hook 技术真的是非常强大,好多插件化的原理都是建立在 Hook 之上的。

总结一下:

  1. Hook 的选择点:静态变量和单例,因为一旦创建对象,它们不容易变化,非常容易定位。
  2. Hook 过程:
    • 寻找 Hook 点,原则是静态变量或者单例对象,尽量 Hook public 的对象和方法。
    • 选择合适的代理方式,如果是接口可以用动态代理。
    • 偷梁换柱——用代理对象替换原始对象。
  3. Android 的 API 版本比较多,方法和类可能不一样,所以要做好 API 的兼容工作。

Xposed Hook微信运动

首先在AndroidManifest.xml Application下添加xposed模块

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<!--xposed描述-->
<meta-data
    android:name="xposeddescription"
    android:value="这是一个Xposed例程" />

<!--xposed最低版本53-->
<meta-data
    android:name="xposedminversion"
    android:value="53" />

<!--这是一个xposed模块-->
<meta-data
    android:name="xposedmodule"
    android:value="true" />

Gradle添加依赖

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
compileOnly 'de.robv.android.xposed:api:82'
compileOnly 'de.robv.android.xposed:api:82:sources'

然后再assets目录添加一个xposed_init文件供Xposed框架访问,内容为包名:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
com.softard.xposedemo.HookTest

然后创建我们的HookTest

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class HookTest implements IXposedHookLoadPackage {

// 实现Hook篡改程序
    @SuppressLint("PrivateApi")
    @Override
    public void handleLoadPackage(final XC_LoadPackage.LoadPackageParam lpparam) throws Throwable {
			if (lpparam.packageName.equals("com.tencent.mm")) { // 搞一搞微信
            XposedBridge.log("hoooook wechat");
            Class<?> clazz1 = Class.forName(
                    "android.hardware.SystemSensorManager$SensorEventQueue", true, lpparam.classLoader);

            XposedBridge.hookAllMethods(clazz1, "dispatchSensorEvent", new XC_MethodHook() {
                @Override
                protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                    int times = XSharedPreferencesUtil.getPref().getInt("step", 500);

                    XposedBridge.log("~~~~~~~Multi times: " + times);
                    XposedBridge.log("Wechat2222 Sensor param " + ((float[]) param.args[1])[0]);
                    ((float[]) param.args[1])[0] = ((float[]) param.args[1])[0] * times;
                    XposedBridge.log("final Sensor param " + ((float[]) param.args[1])[0]);
                    super.beforeHookedMethod(param);
                }

                @Override
                protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                    super.afterHookedMethod(param);
                }
            });

        }*/      
    }
}

To be continued

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2019-10-12,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
理解 Android Hook 技术以及简单实战
作者:落英坠露 链接:https://www.jianshu.com/p/4f6d20076922 著作权归作者所有,转载请联系作者获得授权。 1. 什么是 Hook Hook 英文翻译过来就是「钩子」的意思,那我们在什么时候使用这个「钩子」呢?在 Android 操作系统中系统维护着自己的一套事件分发机制。应用程序,包括应用触发事件和后台逻辑处理,也是根据事件流程一步步地向下执行。而「钩子」的意思,就是在事件传送到终点前截获并监控事件的传输,像个钩子钩上事件一样,并且能够在钩上事件时,处理一些自己特定的事
用户1269200
2018/04/19
3.2K0
Android Hook 机制之简单实战
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/gdutxiaoxu/article/details/81459830
程序员徐公
2018/09/17
3.2K0
Android Hook 机制之简单实战
android hook介绍
第一章:android hook介绍 第二章:hook之替换View.OnClickListener
李小白是一只喵
2021/02/04
8770
xposed模块开发流程
xposed是什么: 必须部署在ROOT后的安卓手机上,通过替换/system/bin/app_process程序控制zygote进程,使得app_process在启动过程中会加载XposedBridge.jar这个jar包,从而完成对zygote进程及其创建的dalvik虚拟机的劫持。
bosh123
2021/01/07
3.6K0
[Android][Security] Android 逆向之安全防护基本策略
使用混淆主要可以减小包的大小。混淆对于安全保护来说,只是增加了阅读难度而已。混淆不会把关键代码混淆掉,比如MainActivity,Application等,可以通过分析smali和阅读jar包定位代码。
wOw
2020/01/20
1.5K0
[Android][Security] Android 逆向之安全防护基本策略
Android Hook技术的简单实现
Android 程序有一套特有的事件分发机制,都是按既定程序从前往后执行的。Hook 技术就是利用反射和代理,在既定程序中插入我们自己写的程序。比如,我们想在某个View的点击事件中添加播放音乐的效果。控件的点击事件,分发流程都是系统已经写好了,这时我们怎么做到在其中插入我们的播放音乐的效果呢?
全栈程序员站长
2022/08/26
8290
Android 插件化之Hook机制
Hook 英文翻译过来就是「钩子」的意思,就是在程序执行的过程中去截取其中的信息。Android 操作系统中系统维护着自己的一套事件分发机制,那么Hook就是在事件传送到终点前截获并监控事件的传输。其原理示意图如下:
xiangzhihong
2022/11/30
4340
那些年Android黑科技②:欺骗的艺术
“我的能量无穷无尽,只有强大暗能量才能统治Android界。 受屎吧!!! =≡Σ((( つ•̀ω•́)つ ” -- 来自暗世界android工程师
陈宇明
2020/12/15
8510
那些年Android黑科技②:欺骗的艺术
万物皆可Hook!重新捡起Hook神器-Xposed框架
而是在程序界流传的强大秘技-Hook函数,Hook原意是指钩子,它表示的就是在某个函数的上下文做自定义的处理来实现我们想要的黑科技。 在很多技术领域都存在的这种Hook技术,比如下面这些:
云爬虫技术研究笔记
2019/11/05
3.5K0
万物皆可Hook!重新捡起Hook神器-Xposed框架
【Android 插件化】Hook 插件化框架 ( Hook 实现思路 | Hook 按钮点击事件 )
【Android 插件化】插件化简介 ( 组件化与插件化 ) 【Android 插件化】插件化原理 ( JVM 内存数据 | 类加载流程 ) 【Android 插件化】插件化原理 ( 类加载器 )
韩曙亮
2023/03/29
8830
【Android 插件化】Hook 插件化框架 ( Hook 实现思路 | Hook 按钮点击事件 )
Android Hook与简单的xposed模块开发实例
Hook是一种在特定事件或操作发生时插入自定义代码的编程技术。在前端开发中,例如Vue和Angular的生命周期钩子,体现了Hook的机制,允许开发者在组件的不同阶段执行代码,提升代码的模块化和可重用性。
阿菇kinoko
2025/01/24
4601
Android Hook与简单的xposed模块开发实例
Xposed 配置,使用以及原理介绍
在搞逆向的时候,Hook是个很必要的手段,通过Hook就可以了解内部的运行机制,甚至进行修改。对于Hook Java方法,用的比较多的就是Xposed,本篇就介绍下Xposed的配置,使用,原理。
一只小虾米
2022/10/25
3K0
Xposed 配置,使用以及原理介绍
Android Hook技术防范漫谈
背景 当下,数据就像水、电、空气一样无处不在,说它是“21世纪的生产资料”一点都不夸张,由此带来的是,各行业对于数据的争夺热火朝天。随着互联网和数据的思维深入人心,一些灰色产业悄然兴起,数据贩子、爬虫、外挂软件等等也接踵而来,互联网行业中各公司竞争对手之间不仅业务竞争十分激烈,黑科技的比拼也越发重要。 随着移动互联网的兴起,爬虫和外挂也从单一的网页转向了App,其中利用Android平台下Dalvik模式中的Xposed Installer和Cydia Substrate框架对App的函数进行Hook这一招
美团技术团队
2018/03/13
2.2K0
Android Hook技术防范漫谈
Xposed源码概述以及衍生的免Root框架
XPosed是与Cydia其名的工具,它能够让Android设备在没有修改源码的情况下修改系统中的API运行结果。我们通常称之为:God Mode(上帝模式)。
慕白
2020/05/25
2.3K1
Xposed源码概述以及衍生的免Root框架
安卓反调试|常见的Xposed框架检测手段与突破方式​
原理:当App获取到系统权限的时候,可以获取系统的所有运行中的App的列表,通过列表发现是否存在有Xposed相关的App(通常都是Xposed Installer相关的Apk,例如de.robv.android.xposed.installer)保持运行状态,一旦存在,就表明用户很有可能存在Hook行为。
云爬虫技术研究笔记
2020/03/25
4K0
Hook技术
参考: https://www.jianshu.com/p/4f6d20076922
用户9854323
2022/06/25
5560
【Android 插件化】Hook 插件化框架 ( Hook Activity 启动过程 | 静态代理 )
【Android 插件化】插件化简介 ( 组件化与插件化 ) 【Android 插件化】插件化原理 ( JVM 内存数据 | 类加载流程 ) 【Android 插件化】插件化原理 ( 类加载器 )
韩曙亮
2023/03/29
1K0
【Android 插件化】Hook 插件化框架 ( Hook Activity 启动过程 | 静态代理 )
浅谈android hook技术浅谈android hook技术-- coding:utf-8 --print jscode author = 'gaohe'-- coding:utf-8 --pri
您当前的位置: 安全博客 > 技术研究 > 浅谈android hook技术 浅谈android hook技术 2017年03月17日 10:06 1249 前言 在测试android过程中,能对函数进行hook能帮助更加深入的进行测试,本文简单介绍了hook框架xposed和frida,从简单的小例子做了简单的演示,算是自己的学习的过程,是个入门的过程。
一个会写诗的程序员
2018/08/20
2K0
没有Android基础都能学会的Xposed基础教程
随着手机使用者增多,手机智能化程度提高,各种app应运而生,这些app中不免有些恶意程序,时刻威胁着使用者,对用户的隐私等造成侵犯。究其原因是系统开源导致安全威胁,这次通过学习一个开源框架xposed来了解移动app的安全。
知识与交流
2023/03/25
1.5K0
没有Android基础都能学会的Xposed基础教程
【云+社区年度征文】探究 | 如何捕获一个 Activity页面上所有的点击行为
既然我要捕获点击事件,首先就想到的是通过事件分发机制,也就是在源头就去获取所有的触摸事件,然后对点击事件进行统计,干吧~
码上积木
2020/12/03
1.1K0
推荐阅读
相关推荐
理解 Android Hook 技术以及简单实战
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验