`

Understanding Network Class Loaders

    博客分类:
  • java
阅读更多

原文:http://www.oracle.com/technetwork/articles/javase/classloaders-140370.html

另外其它中文参考资料:http://www.iteye.com/topic/136427

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
Motivation

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.

 

Class Loaders in the Java 2 Platform

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 Class

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:

  1. If a class has already been loaded, it returns it.
  2. Otherwise, it delegates the search for the new class to the parent class loader.
  3. If the parent class loader doesn't find the class, loadClass() calls the method findClass() 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.

 

Using Class Loaders

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.

 

A Simple Network 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.

 

Code Sample 1: MyLoader.java
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();

  }
}

 

Now, let's assume that the Tester class, which is shown in Code Sample 2, has been compiled and the Tester.class has been uploaded to www.javacourses.com/classes .

 

Code Sample 2: Tester.java
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.

 

Loading Classes from JAR Files

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.

 

Using the Reflection APIs
The reflection APIs ( 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.

 

Code Sample 3: MyLoader2.java
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.

 

Network Class Loaders and Security Issues

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.

 

Code Sample 4: policy.txt
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.

 

JarRunner
For another example demonstrating the use of URLClassLoader , check out the JarRunner application, 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.

 

Context Class Loaders

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);

 

 

Conclusion
Class loaders are one of the cornerstones of Java dynamics; they 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.
分享到:
评论

相关推荐

    2015-11-ClassLoaders-Selajev.pdf

    针对Java ClassLoaders的学习文章,让你全面掌握classloader的特性

    Dynamic Class Loading in the JavaTM Virtual Machine

    Class loaders are a powerful mechanism for dynamically loading software components on the Java platform. They are unusual in supporting all of the following features: laziness, type-safe linkage, user...

    Linkers & Loaders

    Linkers & Loaders

    Assemblers_and_loaders.pdf

    Assemblers_and_loaders.pdf

    Linkers and Loaders.pdf

    Linkers and Loaders 英文PDF 版本,带目录标签。可以添加注释

    ioncube_loaders_win_vc6_x86_ipf

    标题 "ioncube_loaders_win_vc6_x86_ipf" 指的是一款专为Windows环境、基于Visual C++ 6编译器构建,并适用于Intel Pentium 4(IPF)处理器的PHP扩展——ionCube Loaders。ionCube Loaders是PHP的一个关键组件,它...

    loaders.css纯CSS3超酷Loading加载动画

    loaders.css是一款使用纯CSS3 Loading加载指示器特效。该loading指示器特效共有11种不同的效果,都是使用CSS3 animation动画来完成。使用该Loading加载指示器特效需要在页面中引入loaders.css或loaders.min.css文件...

    cobbler_loaders.tar

    cobbler相关程序文件

    Linkers and Loaders

    《Linkers and Loaders》是计算机科学领域的一本经典著作,由知名计算机科学家Lawrence Kratz和Stephen Levine合著。这本书深入浅出地探讨了链接器(Linkers)和加载器(Loaders)的工作原理,以及它们在程序执行...

    linkers and loaders.pdf

    链接和加载,每个真正的程序员必备的基本知识

    linkers_and_loaders.pdf

    《Linkers and Loaders》是计算机科学领域一本经典著作,由美国计算机科学家Lawrence E. Graff著述。这本书深入浅出地探讨了链接器、加载器以及它们在程序执行过程中的作用,对理解操作系统如何管理和执行代码至关...

    linkers and loaders-中文版.pdf_loader_linker_

    《链接器与加载器》是理解计算机系统执行过程中的关键环节之一。链接器(Linker)和加载器(Loader)是程序编译和执行过程中的两个重要组件,它们在将源代码转化为可执行程序的过程中起着至关重要的作用。...

    11.4_loaders.rar

    标题中的"11.4_loaders.rar"似乎指的是一个关于SG11加密加载器的版本更新或特定章节的压缩文件,而"SG11加密"是这个话题的核心。描述提到SG11加密在不同的操作系统环境下需要加载对应的PHP扩展文件,这涉及到PHP的...

    Linkers & Loaders 英文版原著

    不错的系统学习文档,详细描述乐elf文件格式的原理 对想更深入了解软件的同学有很大的帮助

    vue-loaders:Vue + loaders.css

    Vue装载机 + vueNPM $ npm install -S vue-loadersCDN umd: : esm / mjs: ://unpkg.... 对于捆绑&lt; link rel =" stylesheet " href =" https://unpkg.com/vue-loaders/dist/vue-loaders.css " &gt;...

    linkers and loaders

    linkers and loaders,深入理解连接器和加载器的经典书籍。

    ioncube_loaders_lin_x86-64.zip

    ioncube_loaders_lin_x86-64.zip

    Linkers & Loaders write by John R. Levine

    《Linkers & Loaders》是John R. Levine撰写的一本经典著作,主要探讨了在Linux环境中,链接器和加载器的工作原理与实现细节。这本书深入浅出地解释了编译过程中至关重要的两个环节:链接和加载,对于理解程序是如何...

Global site tag (gtag.js) - Google Analytics