JavaSec基础 反射修改Final修饰的属性及绕过高版本反射限制

反射重拾

半年没碰java了

先写点基础回忆一下

反射弹计算器

public class Test {
    public static void main(String[] args) throws Exception {
        Class<?> clazz = Class.forName("java.lang.Runtime");
        clazz.getDeclaredMethod("exec", String.class).
                invoke(clazz.getDeclaredMethod("getRuntime").invoke(clazz),"calc");

    }
}
public class Test {
    public static void main(String[] args) throws Exception {
        Class.forName("java.lang.ProcessBuilder").getDeclaredMethod("start")
                .invoke(Class.forName("java.lang.ProcessBuilder")
                        .getConstructor(String[].class)
                .newInstance(new String[][]{{"calc"}}));

    }
}
import java.lang.reflect.Method;
import java.util.Map;

public class Test {
    public static void main(String[] args) throws Exception {

        Class clazz = Class.forName("java.lang.ProcessImpl");
        Method start = clazz.getDeclaredMethod("start", String[].class, Map.class, String.class, ProcessBuilder.Redirect[].class, boolean.class);
        start.setAccessible(true);
        start.invoke(clazz, new String[]{"calc"}, null, null, null, false);

    }
}

修改Final

步骤如下

获取字段 -> 修改修饰符 -> 修改值

MyClass

public class MyClass {
    private final int password = 114514;

    @Override
    public String toString() {
        return "MyClass{" +
                "password='" + password + '\'' +
                '}';
    }
}

Test

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;

public class Test {
    public static void main(String[] args) throws Exception {
        Class<?> clazz = Class.forName("MyClass");
        Field fieldPassword = clazz.getDeclaredField("password");
        fieldPassword.setAccessible(true);

        Field modifiers = Field.class.getDeclaredField("modifiers");
        modifiers.setAccessible(true);
        modifiers.setInt(fieldPassword,fieldPassword.getModifiers() & ~Modifier.FINAL);

        MyClass myClass = new MyClass();
        fieldPassword.set(myClass,1919810);

        System.out.println(myClass);// MyClass{password='114514'}
        System.out.println(fieldPassword.get(myClass)); // 1919810

    }
}

为什么修改了之后我们输出的仍然是原先的值呢?

这是因为java编译器对final修饰属性进行的内联优化 即编译时将final的值直接放到了引用他的地方,即使通过反射修改了该属性 也没啥用

java会对如下final修饰的类型进行优化

byte short int long float double boolean char LiteralString(直接双引号括起来的字符串)

new 的String比较特殊 可以被有效修改 其余类型的包装类也是如此

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

public class Test {
    public static void main(String[] args) throws Exception {

        Field fieldPassword = Class.forName("MyClass").getDeclaredField("password");
        Method getDeclaredFields0 = Class.class.getDeclaredMethod("getDeclaredFields0", boolean.class);
        getDeclaredFields0.setAccessible(true);

        Field modifiers = null;
        Field[] fields = (Field[]) getDeclaredFields0.invoke(Field.class, false);
        for (Field f: fields){
            if (f.toString().contains("modifiers")) {
                modifiers = f;
            break;
            }
        }
        modifiers.setAccessible(true);


        int recoverNum = fieldPassword.getModifiers();
        modifiers.setInt(fieldPassword,0);

        MyClass myClass = new MyClass();
        System.out.println(myClass);
        fieldPassword.set(myClass,"1919810");
        modifiers.set(fieldPassword,recoverNum);
        System.out.println(myClass);
        System.out.println(fieldPassword.get(myClass));

    }
}

高版本下不能通过getDeclaredFiled获取Field的属性

但是可以通过getDeclaredFileds0来获得

这里没有考虑高版本绕过反射限制 采用的添加启动参数

--add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.lang.reflect=ALL-UNNAMED

后面学完绕过高版本反射限制之后会补充

绕过高版本反射限制

jdk 17 下运行

import java.lang.reflect.Method;
import java.util.Map;

public class HighVersionBypass {
    public static void main(String[] args) throws Exception {
        Class clazz = Class.forName("java.lang.ProcessImpl");
        Method start = clazz.getDeclaredMethod("start", String[].class, Map.class, String.class, ProcessBuilder.Redirect[].class, boolean.class);
        start.setAccessible(true);
        start.invoke(clazz, new String[]{"calc"}, null, null, null, false);
    }
}

Exception in thread "main" java.lang.reflect.InaccessibleObjectException: Unable to make static java.lang.Process java.lang.ProcessImpl.start(java.lang.String[],java.util.Map,java.lang.String,java.lang.ProcessBuilder$Redirect[],boolean) throws java.io.IOException accessible: module java.base does not "opens java.lang" to unnamed module @3b07d329

可以通过Unsafe类进行反射

Unsafe是位于sun.misc包下的一个类,主要提供一些用于执行低级别、不安全操作的方法,如直接访问系统内存资源、自主管理内存资源等,这些方法在提升Java运行效率、增强Java语言底层资源操作能力方面起到了很大的作用。但由于Unsafe类使Java语言拥有了类似C语言指针一样操作内存空间的能力,这无疑也增加了程序发生相关指针问题的风险。在程序中过度、不正确使用Unsafe类会使得程序出错的概率变大,使得Java这种安全的语言变得不再“安全”,因此对Unsafe的使用一定要慎重。

那么如何获得一个Unsafe对象呢

Unsafe提供静态方法getUnsafe获取Unsafe实例,当且仅当调用getUnsafe方法的类为引导类加载器所加载时才合法,否则抛出SecurityException异常

那么从getUnsafe方法的使用限制条件出发,通过Java命令行命令-Xbootclasspath/a把调用Unsafe相关方法的类A所在jar包路径追加到默认的bootstrap路径中,使得A被引导类加载器加载,从而通过Unsafe.getUnsafe方法安全的获取Unsafe实例。

挺麻烦 一般用反射

image-20240108230806895

Field field = Unsafe.class.getDeclaredField("theUnsafe");
field.setAccessible(true);
Unsafe unsafe = (Unsafe)field.get(null);

再看看setAccessible的实现

image-20240108224820204

传入true的时候会调用checkCanSetAccessible

跟进去 一路跟到

private boolean checkCanSetAccessible(Class<?> caller,
                                          Class<?> declaringClass,
                                          boolean throwExceptionIfDenied) {
        if (caller == MethodHandle.class) {
            throw new IllegalCallerException();   // should not happen
        }

        Module callerModule = caller.getModule();
        Module declaringModule = declaringClass.getModule();

        if (callerModule == declaringModule) return true;
        if (callerModule == Object.class.getModule()) return true;
        if (!declaringModule.isNamed()) return true;

        String pn = declaringClass.getPackageName();
        int modifiers;
        if (this instanceof Executable) {
            modifiers = ((Executable) this).getModifiers();
        } else {
            modifiers = ((Field) this).getModifiers();
        }

        // class is public and package is exported to caller
        boolean isClassPublic = Modifier.isPublic(declaringClass.getModifiers());
        if (isClassPublic && declaringModule.isExported(pn, callerModule)) {
            // member is public
            if (Modifier.isPublic(modifiers)) {
                return true;
            }

            // member is protected-static
            if (Modifier.isProtected(modifiers)
                && Modifier.isStatic(modifiers)
                && isSubclassOf(caller, declaringClass)) {
                return true;
            }
        }

        // package is open to caller
        if (declaringModule.isOpen(pn, callerModule)) {
            return true;
        }

        if (throwExceptionIfDenied) {
            // not accessible
            String msg = "Unable to make ";
            if (this instanceof Field)
                msg += "field ";
            msg += this + " accessible: " + declaringModule + " does not \"";
            if (isClassPublic && Modifier.isPublic(modifiers))
                msg += "exports";
            else
                msg += "opens";
            msg += " " + pn + "\" to " + callerModule;
            InaccessibleObjectException e = new InaccessibleObjectException(msg);
            if (printStackTraceWhenAccessFails()) {
                e.printStackTrace(System.err);
            }
            throw e;
        }
        return false;
    }

image-20240108225324126

如果调用类和目标类是同一个module 则可以修改

我们可以通过Unsafe#getAndSetObject来修改module

image-20240108225609564

三个参数对应 操作对象 偏移 值

image-20240108230223185

也就是要修改当前的module 为java.base

我们可以通过Unsafe的staticFieldOffset 或 objectFieldOffset来查找偏移量

在此 我们需要将HighVersionBypass类的module属性改为java.base

此处的getAndSetObject可以用putObject替换

image-20240108233434581

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/305992.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

记录汇川:H5U与Fctory IO测试5

主程序&#xff1a; 子程序&#xff1a; IO映射 子程序&#xff1a; 自动程序 Fctory IO配置&#xff1a; 触摸屏如下&#xff1a; 实际动作如下&#xff1a; Fctory IO测试5

力扣:18.四数之和

一、做题链接&#xff1a;18. 四数之和 - 力扣&#xff08;LeetCode&#xff09; 二、题目分析 1.做这一道题之前本博主建议先看上一篇《三数之和》 2.题目分析 给你一个由 n 个整数组成的数组 nums &#xff0c;和一个目标值 target 。请你找出并返回满足下述全部条件且不重…

java连接池、C3P0、Druid德鲁伊连接池技术

java线程池 连接池C3P0Druid 连接池 概念&#xff1a;其实就是一个容器(集合)&#xff0c;存放数据库连接的容器。当系统初始化好后&#xff0c;容器被创建&#xff0c;容器中会申请一些连接对象&#xff0c;当用户来访问数据库时&#xff0c;从容器中获取连接对象&#xff0c…

信息论与编码期末复习——计算题+基础汇总(二)

个人名片&#xff1a; &#x1f981;作者简介&#xff1a;一名喜欢分享和记录学习的在校大学生 &#x1f42f;个人主页&#xff1a;妄北y &#x1f427;个人QQ&#xff1a;2061314755 &#x1f43b;个人邮箱&#xff1a;2061314755qq.com &#x1f989;个人WeChat&#xff1a;V…

微信小程序连接数据库与WXS的使用

&#x1f389;&#x1f389;欢迎来到我的CSDN主页&#xff01;&#x1f389;&#x1f389; &#x1f3c5;我是Java方文山&#xff0c;一个在CSDN分享笔记的博主。&#x1f4da;&#x1f4da; &#x1f31f;推荐给大家我的专栏《微信小程序开发实战》。&#x1f3af;&#x1f3a…

基于Springboot+vue高校宿舍管理系统(前后端分离)

该项目完全免费 高校宿舍管理系统采用前后端分离的架构方式&#xff0c;是为学校宿舍管理打造的一套系统,可以让管理者更为便捷地处理学生公寓问题,从而大大提高管理效率,让学生公寓的资源合理分配,事半功倍,进而改善了学生公寓管理。 系统分为三种角色&#xff0c;分别是系统…

这些开源自动化测试框架,会用等于白嫖一个w

作者&#xff1a;黑马测试 链接&#xff1a;https://www.zhihu.com/question/19923336/answer/2585952461 来源&#xff1a;知乎 著作权归作者所有。商业转载请联系作者获得授权&#xff0c;非商业转载请注明出处。 随着计算机技术人员的大量增加&#xff0c;通过编写代码来…

【开源】基于JAVA+Vue+SpringBoot的大学计算机课程管理平台

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 实验课程档案模块2.2 实验资源模块2.3 学生实验模块 三、系统设计3.1 用例设计3.2 数据库设计3.2.1 实验课程档案表3.2.2 实验资源表3.2.3 学生实验表 四、系统展示五、核心代码5.1 一键生成实验5.2 提交实验5.3 批阅实…

mysql进阶-视图

目录 1. 用途 2. 语法 2.1 创建或替换视图 2.2 修改视图 2.3 查看视图&#xff1a; 2.4 删除视图&#xff1a; 3. 其他 3.1 操作视图 3.2 迁移数据库 1. 用途 视图可以理解为一个复杂查询的简称&#xff0c;它可以帮助我们简化查询&#xff0c;主要用于报表查询:例如…

BigDecimal使用记录

在公司经费这块用到了BigDecimal类&#xff0c;特此整理记录一下。 一、BigDecimal简介&#xff1a; float和double类型的主要设计目标是为了科学计算和工程计算。他们执行二进制浮点运算&#xff0c;这是为了在广域数值范围上提供较为精确的快速近似计算而精心设计的。然而&a…

Python接口自动化 —— 什么是接口测试、为什么要做接口测试(详解)

什么是接口测试 接口测试是测试系统组件间接口的一种测试。接口测试主要用于检测外部系统与系统之间以及内部各个子系统之间的交互点。测试的重点是要检查数据的交换&#xff0c;传递和控制管理过程&#xff0c;以及系统间的相互逻辑依赖关系等。  一般来说&#xff0c;测试接…

mysql基础-常用函数汇总

目录 1. 查询技巧 2. 时间函数 2.1 now() 2.2 current_date() 2.3 时间差timestampdiff&#xff08;&#xff09;与datediff&#xff08;&#xff09; 2.4 其他时间函数 3. 字符函数 3.1 截取函数 3.2 分割函数 3.3 left与right函数 3.4 其他函数 4. 数字函数 5. …

2024年跨境电商上半年营销日历最全整理

2024年伊始&#xff0c;跨境电商开启新一轮的营销竞技&#xff0c;那么首先需要客户需求&#xff0c;节假日与用户需求息息相关&#xff0c;那么接下来小编为大家整理2024上半年海外都有哪些节日和假期&#xff1f;跨境卖家如何见针对营销日历选品&#xff0c;助力卖家把握2024…

JAVA基础学习笔记-day15-File类与IO流

JAVA基础学习笔记-day15-File类与IO流 1. java.io.File类的使用1.1 概述1.2 构造器1.3 常用方法1、获取文件和目录基本信息2、列出目录的下一级3、File类的重命名功能4、判断功能的方法5、创建、删除功能 2. IO流原理及流的分类2.1 Java IO原理2.2 流的分类2.3 流的API 3. 节点…

图像分类任务的可视化脚本,生成类别json字典文件

1. 前言 之前的图像分类任务可视化&#xff0c;都是在train脚本里&#xff0c; 用torch中dataloader将图片和类别加载&#xff0c;然后利用matplotlib库进行可视化。 如这篇文章中&#xff1a;CNN 卷积神经网络对染色血液细胞分类(blood-cells) 在分类任务中&#xff0c;必定…

在Qt通过查询数据库将查询的结果展示到QTableView控件上

要在Qt中通过查询数据库将查询结果展示到QTableView&#xff0c;你需要遵循以下步骤&#xff1a; 1.设置数据库连接&#xff1a; 首先&#xff0c;确保你已经安装了Qt的MySQL数据库驱动。 在你的主窗口类中&#xff0c;创建一个QSqlDatabase实例并打开数据库连接。 使用QSqlD…

MySQL8.0 升级

将 MySQL8.0.30 升级到 MySQL8.0.32 备份旧数据 rootLAPTOP-FPIQJ438:/data/backup# xtrabackup --backup --userroot --password123456 --socket/tmp/mysql.sock --target-dir/data/backup/ 2024-01-08T16:46:38.98768708:00 0 [Note] [MY-011825] [Xtrabackup] recognized s…

【YOLOv8新玩法】姿态评估寻找链接切割点

学习《OpenCV应用开发&#xff1a;入门、进阶与工程化实践》一书 做真正的OpenCV开发者&#xff0c;从入门到入职&#xff0c;一步到位&#xff01; 前言 Hello大家好&#xff0c;今天给大家分享一下如何基于深度学习模型训练实现工件切割点位置预测&#xff0c;主要是通过对…

stm32的规则采样与注入采样的理解

规则与注入转换 在STM32中&#xff0c;规则采样&#xff08;Regular Conversion&#xff09;和注入采样&#xff08;Injected Conversion&#xff09;是用于模数转换的两种不同模式。 规则采样&#xff08;Regular Conversion&#xff09;&#xff1a;规则采样是STM32中最常用…

【python】TCP测速程序

一、服务端 下面是一个简单的 Python 服务端程序的示例&#xff0c;使用标准库中的 socket 模块来建立一个 TCP 服务器。该服务器接收客户端的连接请求&#xff0c;客户端发送一定大小的数据流以测试 TCP 带宽。 实际场景中带宽测试可能需要更复杂的逻辑来确保测试的准确性。 …