java unsafe 告警,Java安全之Unsafe类
搜索热词
Java安全之Unsafe类
0x00 前言
前面使用到的一些JNI编程和Javaagent等技术,其实在安全里面的运用非常的有趣和微妙,这个已经说过很多次。后面还会发现一些比较有意思的技术,比如ASM和Unsafe这些。这下面就先来讲解Unsafe这个类的使用和实际当中的一些运用场景。
0x01 Unsafe概述
Unsafe是位于sun.misc包下的一个类,主要提供一些用于执行低级别、不安全操作的方法,如直接访问系统内存资源、自主管理内存资源等,这些方法在提升Java运行效率、增强Java语言底层资源操作能力方面起到了很大的作用。使用该类可以获取到底层的控制权,该类在sun.misc包,默认是BootstrapClassLoader加载的。
来看一下下面的两张图
Unsafe类是一个不能被继承的类且不能直接通过new的方式创建Unsafe类实例。
这里可以看到该构造方法是private所以说不能直接new该对象,里面有一个getUnsafe()会返回Unsafe的实例。
@CallerSensitive
public static Unsafe getUnsafe() {
// ----- 这里去获取当前类的ClassLoader加载器
Class var0 = Reflection.getCallerClass();
// ----- 判断var0是不是BootstrapClassLoader
if (!VM.isSystemDomainLoader(var0.getClassLoader())) {
// ----- 否:抛出SecurityException异常
throw new SecurityException("Unsafe");
} else {
// ----- 是:返回unsafe对象
return theUnsafe;
}
}
这里是调用了isSystemDomainLoader来判断是否为Bootstrap类加载器,如果是,可以正常获取Unsafe实例,否则会抛出安全异常。
public static boolean isSystemDomainLoader(ClassLoader var0) {
// ----- 重点是在这里:
// --- 当结果为true时:说明var0是Bootstrap类加载器,
// -- 当结果为false时:说明var0是Extension || App || Custom 等类加载器
// ----- 所以回到getUnsafe()函数,当这个函数返回false时,会直接抛异常,不允许加载Unsafe
return var0 == null;
}
可以来测试一下
package com.UNsafe;
import sun.misc.Unsafe;
public class test {
public static void main(String[] args) {
Unsafe unsafe = Unsafe.getUnsafe();
int i = unsafe.addressSize();
}
}
0x02 Unsafe调用
前面说到Unsafe该类功能去进行直接调用,那么这时候就会想到我们的反射机制。可以利用反射去直接调用。在这里面也有两种方式去进行反射调用。
调用方式一:
因为该类将他的实例化定义在theUnsafe成员变量里面,所以可以使用反射直接获取该变量的值。
package com.UNsafe;
import sun.misc.Unsafe;
import java.lang.reflect.Field;
public class test {
public static void main(String[] args) throws ClassNotFoundException,NoSuchFieldException,IllegalAccessException {
Class> aClass = Class.forName("sun.misc.Unsafe");
Field theUnsafe = aClass.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
Unsafe o = (Unsafe)theUnsafe.get(null);
int i = o.addressSize();
System.out.println(i);
}
}
结果:
8
调用方式二:
还有种方式就是反射调用getUnsafe()方法,该方法会直接返回UNsafe实例对象。那么可以反射获取该构造方法的实例,然后调用该方法
package com.UNsafe;
import sun.misc.Unsafe;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
public class test {
public static void main(String[] args) throws ClassNotFoundException,IllegalAccessException,NoSuchMethodException,InvocationTargetException,InstantiationException {
Class> aClass = Class.forName("sun.misc.Unsafe");
Constructor> declaredConstructor = aClass.getDeclaredConstructor();
declaredConstructor.setAccessible(true);
Unsafe o = (Unsafe)declaredConstructor.newInstance();
int i = o.addressSize();
System.out.println(i);
}
}
结果:
8
0x03 Unsafe功能
操作内存
public native long allocateMemory(long bytes);
//分配内存,相当于C++的malloc函数
public native long reallocateMemory(long address,long bytes);
//扩充内存
public native void freeMemory(long address);
//释放内存
public native void setMemory(Object o,long offset,long bytes,byte value);
//在给定的内存块中设置值
public native void copyMemory(Object srcBase,long srcOffset,Object destBase,long destOffset,long bytes);
//内存拷贝
public native Object getObject(Object o,long offset);
//获取给定地址值,忽略修饰限定符的访问限制。与此类似操作还有: getInt,getDouble,getLong,getChar等
public native void putObject(Object o,Object x);
//为给定地址设置值,忽略修饰限定符的访问限制,与此类似操作还有: putInt,putDouble,putLong,putChar等
public native byte getByte(long address);
//获取给定地址的byte类型的值(当且仅当该内存地址为allocateMemory分配时,此方法结果为确定的)
public native void putByte(long address,byte x);
//为给定地址设置byte类型的值(当且仅当该内存地址为allocateMemory分配时,此方法结果才是确定的)
获取系统信息
public native int addressSize();
//返回系统指针的大小。返回值为4(32位系统)或 8(64位系统)。
public native int pageSize();
//内存页的大小,此值为2的幂次方。
线程调度
public native void unpark(Object thread);
// 终止挂起的线程,恢复正常.java.util.concurrent包中挂起操作都是在LockSupport类实现的,其底层正是使用这两个方法
public native void park(boolean isAbsolute,long time);
// 线程调用该方法,线程将一直阻塞直到超时,或者是中断条件出现。
@Deprecated
public native void monitorEnter(Object o);
//获得对象锁(可重入锁)
@Deprecated
public native void monitorExit(Object o);
//释放对象锁
@Deprecated
public native boolean tryMonitorEnter(Object o);
//尝试获取对象锁
操作对象
// 传入一个Class对象并创建该实例对象,但不会调用构造方法
public native Object allocateInstance(Class> cls) throws InstantiationException;
// 获取字段f在实例对象中的偏移量
public native long objectFieldOffset(Field f);
// 返回值就是f.getDeclaringClass()
public native Object staticFieldBase(Field f);
// 静态属性的偏移量,用于在对应的Class对象中读写静态属性
public native long staticFieldOffset(Field f);
// 获得给定对象偏移量上的int值,所谓的偏移量可以简单理解为指针指向该变量;的内存地址,
// 通过偏移量便可得到该对象的变量,进行各种操作
public native int getInt(Object o,long offset);
// 设置给定对象上偏移量的int值
public native void putInt(Object o,int x);
// 获得给定对象偏移量上的引用类型的值
public native Object getObject(Object o,long offset);
// 设置给定对象偏移量上的引用类型的值
public native void putObject(Object o,Object x););
// 设置给定对象的int值,使用volatile语义,即设置后立马更新到内存对其他线程可见
public native void putIntVolatile(Object o,int x);
// 获得给定对象的指定偏移量offset的int值,使用volatile语义,总能获取到最新的int值。
public native int getIntVolatile(Object o,long offset);
// 与putIntVolatile一样,但要求被操作字段必须有volatile修饰
public native void putOrderedInt(Object o,int x);
这里allocateInstance 这个方法很有意思,可以不调用该构造方法,然后去获取一个传入对象的实例。
那么在安全中会怎么去使用到该方法呢?假设一个场景,某个类的构造方法被HOOK了,该构造方法是private修饰也不能直接去进行new该对象。如果这时候不能使用 反射的机制去进行一个调用,那么这时候就可以使用到该方法进行绕过。
小案例:
定义一个Persion类,并且构造方法为private修饰。
package com.demo2;
import java.io.Serializable;
public class Person implements Serializable {
private String name;
private int age;
public String getName() {
return name;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
",age=" + age +
'}';
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
private Person() {
}
private Person(String name,int age) {
this.name = name;
this.age = age;
}
}
编写调用测试代码:
package com.UNsafe;
import com.demo2.Person;
import sun.misc.Unsafe;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class test {
public static void main(String[] args) throws ClassNotFoundException,InstantiationException {
Class> aClass = Class.forName("sun.misc.Unsafe");
Constructor> declaredConstructor = aClass.getDeclaredConstructor();
declaredConstructor.setAccessible(true);
Unsafe unsafe = (Unsafe)declaredConstructor.newInstance();
Person person = (Person)unsafe.allocateInstance(Person.class);
person.setAge(20);
person.setName("nice0e3");
System.out.println(person);
}
}
执行结果:
Person{name='nice0e3',age=20}
不采用反射和new的反射调用构造方法。
Class相关操作
//静态属性的偏移量,用于在对应的Class对象中读写静态属性
public native long staticFieldOffset(Field f);
//获取一个静态字段的对象指针
public native Object staticFieldBase(Field f);
//判断是否需要初始化一个类,通常在获取一个类的静态属性的时候(因为一个类如果没初始化,它的静态属性也不会初始化)使用。 当且仅当ensureClassInitialized方法不生效时返回false
public native boolean shouldBeInitialized(Class> c);
//确保类被初始化
public native void ensureClassInitialized(Class> c);
//定义一个类,可用于动态创建类,此方法会跳过JVM的所有安全检查,默认情况下,ClassLoader(类加载器)和ProtectionDomain(保护域)实例来源于调用者
public native Class> defineClass(String name,byte[] b,int off,int len,ClassLoader loader,ProtectionDomain protectionDomain);
//定义一个匿名类,可用于动态创建类
public native Class> defineAnonymousClass(Class> hostClass,byte[] data,Object[] cpPatches);
这里面的defineClass方法也很有意思,在前面的学习中应该会对defineClass方法有比较深刻的印象,比如命令执行 Java的webshell工具实现、还有jsp的一些免杀都会利用到ClassLoader的defineClass这个方法去将字节码给还原成一个类。那么在这里的这个defineClass的作用上面也说明了,也是可以去定义一个匿名类,并且可以动态去进行一个创建。假设一个场景ClassLoader.defineClass不可用后就可以使用Unsafe.defineClass。
动态加载类案例
package com.UNsafe;
import com.demo2.Person;
import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.NotFoundException;
import sun.misc.Unsafe;
import javax.xml.soap.SAAJResult;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.security.CodeSource;
import java.security.ProtectionDomain;
import java.security.cert.Certificate;
public class test {
public static void main(String[] args) throws ClassNotFoundException,InstantiationException,CannotCompileException,IOException,NotFoundException {
String AbstractTranslet="com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet";
String Classname ="com.nice0e3.Commandtest";
ClassPool classPool= ClassPool.getDefault();
classPool.appendClassPath(AbstractTranslet);
CtClass payload=classPool.makeClass("com.nice0e3.Commandtest");
payload.setSuperclass(classPool.get(AbstractTranslet));
payload.makeClassInitializer().setBody("java.lang.Runtime.getRuntime().exec(\"calc\");");
byte[] bytes=payload.toBytecode();
//获取系统加载器
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
//创建默认保护域
ProtectionDomain protectionDomain = new ProtectionDomain(new CodeSource(null,(Certificate[]) null),null,systemClassLoader,null);
Class> aClass = Class.forName("sun.misc.Unsafe");
Constructor> declaredConstructor = aClass.getDeclaredConstructor();
declaredConstructor.setAccessible(true);
Unsafe unsafe = (Unsafe)declaredConstructor.newInstance();
Class> aClass1 = unsafe.defineClass(Classname,bytes,bytes.length,protectionDomain);
Object o = aClass1.newInstance();
}
}
在JDK 11版本以后就移除了该方法。但是前面说到的defineAnonymousClass方法还是存在也可以进行使用。
参考文章
https://www.cnblogs.com/rickiyang/p/11334887.html
https://javasec.org/javase/Unsafe/
0x04 结尾
在这里面由上面的案例可以看出来,结合了分析利用链的时候学习的Javassist动态生成类,然后去做转换成字节码Unsafe去进行加载,这其实也能想到一些有趣的利用场景。
总结
以上是编程之家为你收集整理的Java安全之Unsafe类全部内容,希望文章能够帮你解决Java安全之Unsafe类所遇到的程序开发问题。
如果觉得编程之家网站内容还不错,欢迎将编程之家网站推荐给程序员好友。
本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
java unsafe 告警,Java安全之Unsafe类相关推荐
- OpenJDK1.8 :java/lang/NoSuchMethodError‘: Method sun.misc.Unsafe.defineClass(Ljava/lang/String;[BII)
记录一个OpenJDK1.8的一个BUG : Crash日志 报错信息 : Event: 0.078 Thread 0x00007f1160055800 Exception <a 'java/l ...
- java unsafe park_在sun.misc.Unsafe.park(本机方法)中等待
我的一个应用程序在负载下运行一段时间后挂起,有人知道是什么会导致jstack中的此类输出: "scheduler-5" prio=10 tid=0x00007f49481d0000 ...
- java memory copy_Java Unsafe.copyMemory java.lang.IllegalArgumentException
我有来自Unsafe的copyMemory的问题. 我花了2天时间解决它但没有结果. 下面给出的代码总是以"IllegalArgumentException"结尾. 你能帮助我并说 ...
- java加载并运行虚拟机_《深入理解Java虚拟机》- Java虚拟机是如何加载Java类的?...
Java虚拟机是如何加载Java类的? 这个问题也就是面试常问到的Java类加载机制.在年初面试百战之后,菜鸟喜鹊也是能把这流程倒背如流啊!但是,也只是字面上的背诵,根本就是像上学时背书考试一样. ...
- java 阈值 告警_处理Java异常告警最佳实践
1.整理出一份常见 Java 错误类型清单,与各项目组进行沟通调研后,根据线上应用系统日常使用的异常频次,对告警进行分类设计: 2.在设计告警发送内容时,重点考虑如何将告警核心或关键内容直接发送给后续 ...
- Java学习总结:58(Collections工具类)
Collections工具类 Java提供了一个集合的工具类--Collections,这个工具类可以实现List.Set.Map集合的操作.Collections类的常用方法如下: No. 方法 类 ...
- java异常(机制和捕捉(常见异常类))详解 +练习题
Java 中的异常处理机制 1.什么是异常 异常:程序在运行过程中产生的不正常情况. 程序在运行的时候,发生了一些不被预期的事件,从而没有按照我们编写的代码执行,这就是异常. 异常是Java中的错误, ...
- java读取ES配置生成ES管理类,获取ES连接
java读取ES配置生成ES管理类,获取ES连接 1.Elasticsearch是基于Lucene开发的一个分布式全文检索框架,向Elasticsearch中存储和从Elasticsearch中查询, ...
- java类接口的区别_【Java基础】java接口和类的区别-瑶瑶吖的回答
基本语法区别 Java中接口和抽象类的定义语法分别为interface与abstract关键字. 抽象类:在Java中被abstract关键字修饰的类称为抽象类,被abstract关键字修饰的方法称为 ...
最新文章
- python在线读-Python电子书免费分享
- python九九乘法口诀表-python打出九九乘法口诀表
- 【转帖】计算机世界:后DRM时代的数字音乐博弈
- python基础语法教程:行与缩进
- JVM调优:CMS使用的算法
- Nginx实战部署常用功能演示(超详细版),绝对给力~~~
- Java ClassLoader findSystemClass()方法与示例
- OpenCV掩码、blending、改变对比度和亮度、随机发生器和绘图
- 身份证号有效性检验代码 (python)
- PRML中文版(马春鹏)勘误表
- html项目_趣图:我是一个 HTML 开发者,你们看看我做的这个项目咋样?
- Android 热补丁实践之路
- JOHNSON算法:流水作业最优调度问题
- 前端菜鸟浅谈Web前端开发技术
- winhex查看mysql_使用WINHEX查看innodb的BTree高度
- 带宽、传输速率、吞吐量的概念区别
- 微信小程序学习日记7
- 八代及以上笔记本发热降频的一般处理办法
- 根据ParentId生成树状结构这po事
- GPS接收机(四)--基于STM32和GU620的GPS接收机搭建