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

news2024/9/21 14:48:57

反射重拾

半年没碰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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Aging:浙大学者研究发现,多吃这类抗氧化饮食,延缓衰老

撰文 | 宋文法 衰老&#xff0c;是一个复杂、多阶段、渐进的过程&#xff0c;发生在生命的整个过程。随着时间的流逝&#xff0c;人体的器官、肌肉会逐渐衰老&#xff0c;一些疾病也伴随着年龄的增长而发生&#xff0c;包括癌症、糖尿病、心血管疾病等。 衰老过程是由体内自由基…

autoxjs 安卓爬虫自动化

autoxjs 安卓爬虫自动化 我这里只是测试请勿用于违法的 我这里是小红书 文章目录 autoxjs 安卓爬虫自动化前言一、自动刷直播间并且抓取商品已经粉丝数量等&#xff1f;总结 前言 欢迎来到AutoXJS的世界&#xff0c;这是一个充满创新、挑战和技术探索的领域。在这个引领未来的…

【huggingface】【pytorch-image-models】timm框架中使用albumentations库数据增广

文章目录 一、前言二、实操2.1 声明库2.2 定义你的数据增广算子2.3 加入其中 一、前言 问题是这样的&#xff0c;在使用timm框架训练时&#xff0c;发现数据增广不够&#xff0c;想用Albumentations库的数据增广&#xff0c;怎么把后者嵌入到前者的训练中。 其实也是比较简单…

CHS_02.1.4+操作系统体系结构 二

CHS_02.1.4操作系统体系结构 二 操作系统的结构 上篇文章我们只介绍过宏内核 也就是大内核以及微内核分层结构的操作系统模块化是一种很经典的程序设计思想宏内核和微内核外核 操作系统的结构 上篇文章我们只介绍过宏内核 也就是大内核以及微内核 今年大纲又增加了分层结构 模块…

unity编辑器Scene界面输出位置及路径

工程Asset下新建Editor文件夹&#xff1b; Editor文件夹下新建脚本LogPosition using System.Collections; using System.Collections.Generic; using UnityEditor; using UnityEngine; public class LogPosition : EditorWindow {//最终输出的数据.static string logtext;//增…

GCF:在线市场异质治疗效果估计的广义因果森林

英文题目&#xff1a;GCF: Generalized Causal Forest for Heterogeneous Treatment Effects Estimation in Online Marketplace 中文题目&#xff1a;GCF&#xff1a;在线市场异质治疗效果估计的广义因果森林 单位&#xff1a;滴滴&美团 时间&#xff1a;2022 论文链接…

[java]JAVA中文版API手册 -jdk_api_1.8

有mac和win版本 链接&#xff1a;https://pan.baidu.com/s/14WGXJYBICeSxgg6OxBVGRQ 提取码&#xff1a;c03p

MCU、MPU、SOC简介

文章目录 前言一、MCU二、MPU三、SOC总结 前言 随着处理器技术的不断发展&#xff0c;CPU(Central Processing Unit)的发展逐渐出现三种分支&#xff0c;分别是MCU(Micro Controller Unit&#xff0c;微控制器单元) 和MPU&#xff08;Micro Processor Unit&#xff0c;微处理器…

软考高级系统架构设计师考试经验分享

文章目录 1. 软考介绍&#xff08;1&#xff09;什么是软考&#xff08;2&#xff09;软考的作用&#xff08;3&#xff09;软考各科目的难度&#xff08;4&#xff09;考试时间&#xff08;5&#xff09;考试形式 2.系统架构设计师备考经验&#xff08;1&#xff09;辅导资料&…

数据结构与算法(十)深度优先搜索与广度优先搜索

广度优先搜索 广度优先搜索&#xff1a;从一个顶点出发&#xff08;由开始时顶点创造顺序优先决定&#xff09;&#xff0c;访问所有没有被访问过的临节点。然后在从被访问过的节点出发&#xff0c;重复之前的操作 如下为一个图 从1出发&#xff0c;先后访问2 3&#xff0c;之后…

String的(toCharArray\split)方法*

题目 class Solution {public int firstUniqChar(String s) {int[] sum new int[26];char[] num s.toCharArray();for(int i0;i<num.length;i) {sum[num[i]-a];}for(int j0;j<num.length;j) {if(sum[num[j]-a] 1) {return j;}}return -1; } }题目 …

我的1827创作纪念日

机缘 习惯性早上打开电脑&#xff0c;看看CSDN上的资讯&#xff0c;了解行业动态、当前新的技术和大佬的分享。自己动手写应该是2019 年 01 月 08 日&#xff0c;当时应该是在用安装和使用Oracle&#xff0c;遇到一些问题&#xff0c;写下第一篇博客 Oracle存储过程常见问题及…

【RK3399 PCIE调试——硬件信息资源获取】

一、1、 硬件接口 二、2、 PCB原理图 三、 官网地址&#xff1a; https://t.rock-chips.com/portal.php 相关资料和固件烧写可参考资料下载菜单

欧拉图及其应用

什么是欧拉图 提到欧拉图就要谈到哥尼斯堡七桥问题&#xff0c;最初有这样的一个问题的&#xff1a;18世纪中叶&#xff0c;东普鲁士哥尼斯堡城有一条贯穿全城的普雷格尔河&#xff0c;河中有两个岛&#xff0c;通过七座桥彼此相连&#xff0c;如下图所示 问题是这样的&…

(非常全面的干货)Python接口自动化测试框架实战开发

一丶叙述 1.项目介绍 整个项目分为四个部分&#xff1a;接口基础丶接口开发丶Unittest与接口测试结合以及接口自动化框架从设计到开发 接口基础包括&#xff1a;HTTP接口 / 常见接口 / 接口工具 / 接口基础知识 接口开发&#xff1a;通过Django来开发get/post接口 Unittes…

“器官短缺”将被打破 基因编辑猪成为人类的“二师兄”

器官移植被称为生命之灯。但是&#xff0c;受制于传统观念及对人体器官捐献意义的不了解&#xff0c;人体器官捐献的数量&#xff0c;还远远达不到需求。目前&#xff0c;全国有近30万的患者在等待器官移植&#xff0c;但每年只有近一万的患者能真正得到器官移植&#xff0c;缺…

银联扫码第三方支付接口申请:开启便捷支付新时代

随着移动支付的普及&#xff0c;越来越多的商家开始接受微信、支付宝等第三方支付平台的付款方式。然而&#xff0c;作为国内最大的银行卡组织&#xff0c;银联也在不断拓展其业务范围&#xff0c;推出了自己的扫码支付接口。本文将为您详细介绍银联扫码第三方支付接口的申请流…

【Azure 架构师学习笔记】- Azure Databricks (5) - Unity Catalog 简介

本文属于【Azure 架构师学习笔记】系列。 本文属于【Azure Databricks】系列。 接上文 【Azure 架构师学习笔记】- Azure Databricks (4) - 使用Azure Key Vault 管理ADB Secret 前言 DataBricks Unity Catalog&#xff08;UC&#xff09;是一个统一的对数据资产治理的解决方案…

【算法Hot100系列】有效的数独

💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学习,不断总结,共同进步,活到老学到老导航 檀越剑指大厂系列:全面总结 jav…

GPS 模拟器

GPS 工具包&#xff1a;https://www.ni.com/es/support/downloads/software-products/download.gnss-test-toolkit.html#333303 GPS-SDR-SIM&#xff1a;https://github.com/osqzss/gps-sdr-sim GPS LabVIEW &#xff1a;http://mikioblog.dolphinsystem.jp/2017/08/gps-sdr-si…