Understanding Network Class Loaders
By Qusay H. Mahmoud, October 2004 |
When Java was first released to the public in 1995 it came with a web browser (HotJava), written in Java, that had the ability to automatically and dynamically download mini-applications (or applets) when it encountered the <Applet> tag in an HTML document. Such Applets are loaded on the fly across the network from remote web servers and run inside the browser's Java Virtual Machine (JVM). The mechanism that enabled such dynamic loading is a class loader, which is one of the cornerstones of Java dynamism. Class loaders are responsible for determining when and how classes can be added to a running Java environment, as well as making sure that important parts of the Java runtime environment are not replaced by impostor code.
The JVM default class loader knows how to load classes from the local file system, but what if you want to develop state-of-the-art applications that are capable of loading classes from remote servers? Class loaders can also be used to ensure safety and security of byte codes; for example, you can develop a custom class loader capable of checking a digital signature before executing untrusted foreign code.
This article provides a tutorial on network class loaders, and:
- Discusses the class loader mechanism
- Offers a flavor of the effort involved in developing network class loaders
- Discusses the security issues surrounding network class loaders
- Shows how to protect network class loaders from loading malicious code
Applications written in statically compiled programming languages, such as C and C++, are compiled into native, machine-specific instructions and saved as an executable file. The process of combining the code into an executable native code is called linking - the merging of separately compiled code with shared library code to create an executable application. This is different in dynamically compiled programming languages such as Java. In Java, the .class
files generated by the Java compiler remain as-is until loaded into the Java Virtual Machine (JVM) -- in other words, the linking process is performed by the JVM at runtime. Classes are loaded into the JVM on an 'as needed' basis. And when a loaded class depends on another class, then that class is loaded as well.
When a Java application is launched, the first class to run (or the entry point into the application) is the one with public static void
method called main()
. This class usually has references to other classes, and all attempts to load the referenced classes are carried out by the class loader.
To get a feeling of this recursive class loading as well as the class loading idea in general, consider the following simple class:
public class HelloApp { public static void main(String argv[]) { System.out.println("Aloha! Hello and Bye"); } } |
If you run this class specifying the -verbose:class
command-line option, so that it prints what classes are being loaded,
you will get an output that looks as follows. Note that this is just a
partial output since the list is too long to show here.
prmpt>java -verbose:class HelloApp [Opened C:\Program Files\Java\jre1.5.0\lib\rt.jar] [Opened C:\Program Files\Java\jre1.5.0\lib\jsse.jar] [Opened C:\Program Files\Java\jre1.5.0\lib\jce.jar] [Opened C:\Program Files\Java\jre1.5.0\lib\charsets.jar] [Loaded java.lang.Object from shared objects file] [Loaded java.io.Serializable from shared objects file] [Loaded java.lang.Comparable from shared objects file] [Loaded java.lang.CharSequence from shared objects file] [Loaded java.lang.String from shared objects file] [Loaded java.lang.reflect.GenericDeclaration from shared objects file] [Loaded java.lang.reflect.Type from shared objects file] [Loaded java.lang.reflect.AnnotatedElement from shared objects file] [Loaded java.lang.Class from shared objects file] [Loaded java.lang.Cloneable from shared objects file] [Loaded java.lang.ClassLoader from shared objects file] [Loaded java.lang.System from shared objects file] [Loaded java.lang.Throwable from shared objects file] . . . [Loaded java.security.BasicPermissionCollection from shared objects file] [Loaded java.security.Principal from shared objects file] [Loaded java.security.cert.Certificate from shared objects file] [Loaded HelloApp from file:/C:/classes/] Aloha! Hello and Bye [Loaded java.lang.Shutdown from shared objects file] [Loaded java.lang.Shutdown$Lock from shared objects file] |
As you can see, the Java runtime classes required by the application class ( HelloApp
) are loaded first.
The Java programming language keeps evolving to make the life of
applications developers easier everyday. This is done by providing APIs
that simplify your life by allowing you to concentrate on business logic
rather than implementation details of fundamental mechanisms. This is
evident by the recent change of J2SE 1.5 to J2SE 5.0 in order to reflect
the maturity of the Java platform.
As of JDK 1.2, a bootstrap class loader that
is built into the JVM is responsible for loading the classes of the
Java runtime. This class loader only loads classes that are found in the
boot classpath, and since these are trusted classes, the validation
process is not performed as for untrusted classes. In addition to the
bootstrap class loader, the JVM has an extension class loader
responsible for loading classes from standard extension APIs, and a
system class loader that loads classes from a general class path as well
as your application classes.
Since there is more than one class loader, they are represented in a
tree whose root is the bootstrap class loader. Each class loader has a
reference to its parent class loader. When a class loader is asked to
load a class, it consults its parent class loader before attempting to
load the item itself. The parent in turn consults its parent, and so on.
So it is only after all the ancestor class loaders cannot find the
class that the current class loader gets involved. In other words, a
delegation model is used.
The java.lang.ClassLoader
is an abstract
class that can be subclassed by applications that need to extend the
manner in which the JVM dynamically loads classes. Constructors in
java.lang.ClassLoader
(and its subclasses) allow
you to specify a parent when you instantiate a new class loader. If you
don't explicitly specify a parent, the virtual machine's system class
loader will be assigned as the default parent. In other words, the
ClassLoader
class uses a delegation model to search for classes and resources. Therefore, each instance of ClassLoader
has an associated parent class loader, so that when requested to find a
class or resources, the task is delegated to its parent class loader
before attempting to find the class or resource itself. The
loadClass()
method of the ClassLoader
performs the following tasks, in order, when called to load a class:
- If a class has already been loaded, it returns it.
- Otherwise, it delegates the search for the new class to the parent class loader.
- If the parent class loader doesn't find the class,
loadClass()
calls the methodfindClass()
to find and load the class.
The finalClass()
method searches for the class in the current class loader if the class wasn't found by the parent class loader.
Developing your own class loaders is an inherently dangerous
undertaking as this can cause no end of security trouble. For this
reason, the Java 2 platform has added useful classes to the core APIs in
order to make developing and using class loaders easier than ever. For
example, the java.security.SecureClassLoader
class extends the ClassLoader
with additional support for defining classes with an associated code
source and permissions which are retrieved by the system policy by
default.
The java.net.URLClassLoader
class, which is a subclass of SecureClassloader
,
can be easily used to load classes and resources from a search path of
URLs referring to directories and JAR files. The URLs will be searched
in the order specified for classes and resources after first searching
in the parent class loader.
The URLClassLoader
can be used to easily
develop an application capable of loading classes and resources from
remote servers. First, you need to define the URLs to be searched for
classes. Any URL that ends with a ' /
' is
assumed to refer to a directory, otherwise the URL is assumed to refer
to a JAR file which will be opened as needed. Once an instance of the
URLClassLoader
is constructed, the loadClass(String name)
method of the ClassLoader
class is used to load the class with the specified name. Once a class
has been loaded, an instance can be created (this means that the
constructor will be invoked). Code Sample 1 shows a sample
implementation. Note, however, that it is recommended to override and
use the findClass()
method instead.
import java.net.*; import java.io.*; public class MyLoader { public static void main (String argv[]) throws Exception { URLClassLoader loader = new URLClassLoader(new URL[] { new URL("http://www.javacourses.com/classes/") }); // Load class from class loader. argv[0] is the name of the class to be loaded Class c = loader.loadClass (argv[0]); // Create an instance of the class just loaded Object o = c.newInstance(); } } |
Tester
class, which is shown in Code Sample 2, has been compiled and the Tester.class
has been uploaded to www.javacourses.com/classes
.
public class Tester { public Tester () { System.out.println ("Hello there"); } public static void main(String argv[]) { System.out.println("Network Class Loaders"); } } |
Now, you can run MyLoader
as follows:
prompt> java MyLoader Tester |
This will load the Tester.class
class from the http://www.javacourses.com/classes/
, and as a result the output will be:
Hello there
When an object is instantiated, the class's constructor was called. In other words, the main()
method wasn't invoked. We will see how to do that shortly.
The URLClassLoader
is flexible and is capable of loading classes from a JAR file. To experiment with this, create a JAR file ( test.jar
) that contains the Tester.class
as follows:
prompt> jar cf test.jar Tester.class |
The test.jar
is already available at http://www.javacourses.com/classes/test.jar
, so to test the code, change the URL in Code Sample 1 to read http://www.javacourses.com/classes/test.jar
, then compile the code and run it as before and you will see the same results.
java.lang.reflect
)can be used to dynamically figure out the capabilities of an object
without necessarily knowing anything about it in advance. As an example,
the reflection APIs can be used to invoke the
main()
method of the loaded class. Code Sample 3 shows a sample implementation.
import java.net.*; import java.io.*; import java.lang.reflect.*; public class MyLoader2 { public static void main (String argv[]) throws Exception { URLClassLoader loader = new URLClassLoader(new URL[] { new URL("http://www.javacourses.com/classes/") }); // Load class from class loader. argv[0] is the name of the class to be loaded Class c = loader.loadClass (argv[0]); Method m = c.getMethod("main", new Class[] {argv.getClass() }); m.setAccessible(true); int mods = m.getModifiers(); if(m.getReturnType() != void.class || !Modifier.isStatic(mods) || !Modifier.isPublic(mods)) { throw new NoSuchMethodException("main"); } try { m.invoke(null, new Object[] { argv }); } catch(IllegalAccessException e) { } } } |
Compile and run the example as shown earlier. The output will be:
Network Class Loaders
which is the message printed by the main()
method.
Since the above application, MyLoader2
,
is capable of loading arbitrary classes from a network then your system
(local disk) is at risk. Those arbitrary classes get interpreted by the
class loader into the JVM, which means that the byte codes -- no matter
how malicious -- will be interpreted in your system. To demonstrate just
one kind of malicious code, consider the following example.
Imagine for a moment that someone is aware of a sensitive file, with the name personal-data.txt
,
on your machine. That someone may write a simple application to delete
that file from your system, or even to email a copy of it across the
network -- perhaps by inviting you to download an "interesting" class of
byte codes. The simple application would resemble that shown here:
import java.io.File; public class MaliciousApp { public static void main(String argv[]) { try { File file = new File("personal-data.txt"); if(file.delete() == true) { System.out.println("File: "+ file + " has been deleted!"); } else { System.out.println("Cannot delete file!"); } } catch(Exception e) { System.out.println("Exception: "+e.getMessage()); } } } |
If your enemy compiled the class and asked you to load it ( java MyLoader2 MaliciousApp
), this code would delete the file personal-data
from your system. This is just one example of security concerns
associated with class loaders; try to think of others. For example, what
happens if an application is able to load its own class loader, or open
socket connections to remote hosts? These are actually some of the
security issues that applets (and therefore class loaders implemented by
web browsers) are concerned with.
You may ask, but wouldn't the Java environment protect against such
malicious code? Built-in safety mechanisms ensure that the system is not
subverted by invalid code, but not malicious code.
One way to protect against such attacks is by using a security
manager, which is not automatically installed when an application is
running. If you wish to apply the same security policy to an application
found on the local file system as to downloaded applets, you can invoke
the Java interpreter with the default security manager as follows:
prompt> java -Djava.security.manager MyLoader2 |
This, however, will give you the following output. As you can see an AccessControlException
is thrown.
Exception in thread "main" java.security.AccessControlException: access denied ( java.lang.RuntimePermission createClassLoader) at java.security.AccessControlContext.checkPermission(Unknown Source) at java.security.AccessController.checkPermission(Unknown Source) at java.lang.SecurityManager.checkPermission(Unknown Source) at java.lang.SecurityManager.checkCreateClassLoader(Unknown Source) at java.lang.ClassLoader.<init>(Unknown Source) at java.security.SecureClassLoader.<init>(Unknown Source) at java.net.URLClassLoader.<init>(Unknown Source) at MyLoader.main(MyLoader.java:8) |
Now, since there is a security manager in place, the URLClassLoader
constructor will first call the security manager's checkCreateClassLoader()
to ensure creation of a class loader is allowed. The default security
manager, however, doesn't allow your application to create its own class
loader. One possible solution is to define a security policy. The Java
environment will adhere to run-time restrictions; therefore, you can
devise an application-level security policy for use with your
application. Such a security policy allows you to state, using
grant
clauses, what sorts of actions a Java program
can and cannot perform. Code Sample 4 shows a sample security policy
that allows code loaded from the local file system to do anything. Using
this policy, the application is allowed to create its own class loader.
A security policy can be specified on the command line using the
-Djava.security.policy= policy-name
command line argument.
grant codeBase "file:/-" { permission java.security.AllPermission; }; |
The application can now be run as follows:
prompt> java -Djava.security.manager -Djava.security.policy=policy.txt MyLoader2 MaliciousApp |
As you can see, both the default security manager as well as the
security policy are being enforced. This means that local code can do
everything (as defined by the AllPermission
in policy.txt
,
but remote code is still being checked by the default security policy,
which means that remote code cannot open local files among other things.
The output of the previous command will be:
Exception: access denied (java.io.FilePermission personal-data.txt delete)
As you can see, an exception is thrown when the loaded class, MaliciousApp
, tries to delete a local file.
URLClassLoader
, check out the JarRunnerapplication, which enables you to run an application that's bundled in a
JAR file by specifying the JAR file's URL on the command line.
The Java 2 platform also introduced the notion of context class
loader. A thread's context class loader is, by default, set to the
context class loader of the thread's parent. The hierarchy of threads is
rooted at the primordial thread (the one that runs the program). The
context class loader of the primordial thread is set to the class loader
that loaded the application. The thread's context class loader will be
the application's class loader. This loader is used by the Java runtime
in the Java Remote Method Invocation (RMI) to load classes on behalf of
the user application. The thread's context class loader can be easily
changed as follows:
String url = "some URL; ClassLoader previous = Thread.currentThread().getContextClassLoader(); // Create a class loader using the URL as the codebase // Use previous as parent class loader to maintain current visibility ClassLoader current = URLClassLoader.newInstance(new URL[]{new URL(url)}, previous); Thread.currentThread().setContextClassLoader(current); |
determine when and how classes can be added to a running Java
environment. This mechanism can be used to load classes dynamically from
local as well as remote file systems. Loading arbitrary classes from
the network, however, introduces the system to new risks that call for
the implementation of security policies. This article provided an
introductory tutorial to class loaders in general and network class
loaders in particular, as well as the security issues that surround
them. The code samples demonstrate how easy it is to develop
applications capable of dynamically loading remote classes, as well as
how to define security policies that can be used in addition to the
default security manager.
Understanding Network Class Loaders的更多相关文章
- Python 黑帽编程大纲(变化中)
Python 黑帽编程大纲(预览版) 教程说明: 本系列教程,采用的大纲母本为<Understanding Network Hacks Attack and Defense with Pytho ...
- Python黑帽编程1.1虚拟机安装和配置 Kali Linux 2016
Python黑帽编程1.1虚拟机安装和配置 Kali Linux 2016 0.1 本系列教程说明 本系列教程,采用的大纲母本为<Understanding Network Hacks Att ...
- Python黑帽编程1.2 基于VS Code构建Python开发环境
Python黑帽编程1.2 基于VS Code构建Python开发环境 0.1 本系列教程说明 本系列教程,采用的大纲母本为<Understanding Network Hacks Atta ...
- Python黑帽编程1.3 Python运行时与包管理工具
Python黑帽编程1.3 Python运行时与包管理工具 0.1 本系列教程说明 本系列教程,采用的大纲母本为<Understanding Network Hacks Attack and ...
- PYTHON黑帽编程1.5 使用WIRESHARK练习网络协议分析
Python黑帽编程1.5 使用Wireshark练习网络协议分析 1.5.0.1 本系列教程说明 本系列教程,采用的大纲母本为<Understanding Network Hacks At ...
- 理解Java NIO
基础概念• 缓冲区操作缓冲区及操作是所有I/O的基础,进程执行I/O操作,归结起来就是向操作系统发出请求,让它要么把缓冲区里的数据排干(写),要么把缓冲区填满(读).如下图• 内核空间.用户空间 上图 ...
- Python 黑客相关电子资源和书籍推荐
原创 2017-06-03 玄魂工作室 玄魂工作室 继续上一次的Python编程入门的资源推荐,本次为大家推荐的是Python网络安全相关的资源和书籍. 在去年的双11送书的时候,其实送过几本Pyth ...
- 加密流量分析cisco
思科ETA主页 https://www.cisco.com/c/en/us/solutions/enterprise-networks/enterprise-network-security/eta. ...
- [knowledge][ETA] Encrypted Traffic Analytics
思科ETA主页 https://www.cisco.com/c/en/us/solutions/enterprise-networks/enterprise-network-security/eta. ...
随机推荐
- Java获取方法参数名、Spring SpEL解析
@Test public void testParse() { //表达式解析 ExpressionParser expressionParser = new SpelExpressionParser ...
- SQL SERVER删除列,报错."由于一个或多个对象访问此列,ALTER TABLE DROP COLUMN ... 失败"
队友给我修改数据的语句.总是执行失败.很纳闷. 如下图: 仔细看了下这个列,并没有什么特殊.如下图: 但其确实有个约束: 'DF__HIS_DRUG___ALL_I__04E4BC85' . 为什么有 ...
- ECMAScript布尔操作符
在ECMAScript中提供了Boolean()转换函数以及三个布尔操作符,这三个布尔操作符分别为逻辑非.逻辑与.逻辑或,这三个操作符通常用作于某些值的求反,比较模式等.学好这一点知识也非常的重要,奠 ...
- QML按键事件处理
QML提供了对应的按键处理方法,我们接下来实现一个通过键盘上的方向键来移动文本,代码如下: import QtQuick 2.4 import QtQuick.Controls 1.3 import ...
- Java Web开发中的名词解释
1.JVM Java虚拟机,class文件的运行时环境,就好比软件运行在操作系统一样,java要运行在JVM中才行,这也是Java之所以支持扩平台的基础. 2.Servlet/JSP 是满足一定接口需 ...
- JS中判断JSON数据是否存在某字段的方法 JavaScript中判断json中是否有某个字段
方式一 !("key" in obj) 方式二 obj.hasOwnProperty("key") //obj为json对象. 实例: var jsonwor ...
- 微信video标签全屏无法退出bug
安卓(android)微信里面video播放视频,会被强制全屏,播放完毕后还有腾讯推荐的视频,非常讨厌..强制被全屏无法解决,但是视频播放完毕后退出播放器可以解决.方法就是视频播放完毕后,用音频aud ...
- FolderBrowserDialog(文件夹浏览对话框)
1.选择数据库目录,在此处不需要新建文件夹,因此屏蔽新建文件夹按钮. C#代码 FolderBrowserDialog df = new FolderBrowserDialog(); //设置文件浏览 ...
- 为WPF版的GridControl控件添加行序号功能
废话不多数,先上效果图和代码: 包装GridControl控件 cs using DevExpress.Xpf.Data; using DevExpress.Xpf.Grid; using Syste ...
- JS的this本质
1.this究竟为何物? 1.1 全局上下文(Global context ) 在全局运行上下文中(在任何函数体外部),this 指代全局对象window,无论是否在严格模式下. alert(this ...