双亲委派模型:
实现自己的类加载器,并能对字节码实现加密
import java.io.*;
import java.lang.reflect.*;
public class ShuffleClassLoader extends ClassLoader
{
private static int mask=0xff;
private boolean compile(String fileStub) throws IOException
{
Process p=Runtime.getRuntime().exec("javac "+fileStub+".java");
try
{
p.waitFor();
}
catch (InterruptedException ie)
{
System.out.println(ie);
}
int ret=p.exitValue();
if (ret==0)
{
File file=new File(fileStub+".class");
FileInputStream fin=new FileInputStream(file);
FileOutputStream fout=new FileOutputStream(fileStub+"_sec.class");
int temp=-1;
while ((temp=fin.read())!=-1)
{
temp=temp^mask;
fout.write(temp);
}
fin.close();
fout.close();
file.delete();
new File(fileStub+"_sec.class").renameTo(file);
}
return ret==0;
}
private byte[] read(String fileStub) throws IOException
{
ByteArrayOutputStream bos=new ByteArrayOutputStream();
try(FileInputStream fin=new FileInputStream(fileStub+".class"))
{
int temp=-1;
while ((temp=fin.read())!=-1)
{
temp=temp^mask;
bos.write(temp);
}
return bos.toByteArray();
}
}
protected Class<?> findClass(String name) throws ClassNotFoundException
{
Class clazz=null;
String fileStub=name.replace(".","/");
String javaFileName=fileStub+".java";
String classFileName=fileStub+".class";
File javaFile=new File(javaFileName);
File classFile=new File(classFileName);
if (javaFile.exists()&&(!classFile.exists()
||javaFile.lastModified()>classFile.lastModified()))
{
try
{
//调用compile()方法,得到.class文件
if (!compile(fileStub)||!classFile.exists())
{
throw new ClassNotFoundException(javaFileName);
}
}
catch (IOException ex)
{
ex.printStackTrace();
}
}
if (classFile.exists())
{
try
{
//调用read()方法,把二进制文件转换为字节数组
byte[] raw=read(fileStub);
//调用defineClass()方法,把字节数组转换为Class实例
clazz=defineClass(name,raw,0,raw.length);
}
catch (IOException ie)
{
ie.printStackTrace();
}
}
if (clazz==null)
{
throw new ClassNotFoundException(name);
}
return clazz;
}
public static void main(String[] args) throws Exception
{
if (args.length<1)
{
System.out.println("缺少目标类,请安如下格式:");
System.out.println("java ShuffleClassLoader ClassName");
}
String progClass=args[0];
String[] progArgs=new String[args.length-1];
System.arraycopy(args,1,progArgs,0,progArgs.length);
ShuffleClassLoader scl=new ShuffleClassLoader();
Class<?> clazz=scl.findClass(progClass);
Method main=clazz.getMethod("main",(new String[0]).getClass());
Object argsArray[]={progArgs};
main.invoke(null,argsArray);
}
}
用这个自定义的类加载器可以编译源代码,即.java文件,生成.class文件,而且只能用这个自定义的类加载器运行.class文件。
例如对于下面的Hello.java
public class Hello
{
public static void main(String[] args)
{
System.out.println("Hello World!");
}
}
输入java ShuffleClassLoader Hello命令可以得到Hello.class文件,并输出"Hello World"。
如果直接输入 java Hello,输出如下:
Error: A JNI error has occurred, please check your installation and try again
Exception in thread "main" java.lang.ClassFormatError: Incompatible magic value 889275713 in class file Hello
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:763)
at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
at java.net.URLClassLoader.defineClass(URLClassLoader.java:467)
at java.net.URLClassLoader.access$100(URLClassLoader.java:73)
at java.net.URLClassLoader$1.run(URLClassLoader.java:368)
at java.net.URLClassLoader$1.run(URLClassLoader.java:362)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:361)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
at sun.launcher.LauncherHelper.checkAndLoadMain(LauncherHelper.java:495)
贴一点ClassLoader类的源码:
public Class<?> loadClass(String name) throws ClassNotFoundException {
return loadClass(name, false);
}
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// First, check if the class has already been loaded
Class<?> c = findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
try {
if (parent != null) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
if (c == null) {
// If still not found, then invoke findClass in order
// to find the class.
long t1 = System.nanoTime();
c = findClass(name);
// this is the defining class loader; record the stats
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}
protected Class<?> findClass(String name) throws ClassNotFoundException {
throw new ClassNotFoundException(name);
}
protected final Class<?> defineClass(String name, byte[] b, int off, int len)
throws ClassFormatError
{
return defineClass(name, b, off, len, null);
}
protected final Class<?> defineClass(String name, byte[] b, int off, int len,
ProtectionDomain protectionDomain)
throws ClassFormatError
{
protectionDomain = preDefineClass(name, protectionDomain);
String source = defineClassSourceLocation(protectionDomain);
Class<?> c = defineClass1(name, b, off, len, protectionDomain, source);
postDefineClass(c, protectionDomain);
return c;
}
protected final Class<?> defineClass(String name, java.nio.ByteBuffer b,
ProtectionDomain protectionDomain)
throws ClassFormatError
{
int len = b.remaining();
// Use byte[] if not a direct ByteBufer:
if (!b.isDirect()) {
if (b.hasArray()) {
return defineClass(name, b.array(),
b.position() + b.arrayOffset(), len,
protectionDomain);
} else {
// no array, or read-only array
byte[] tb = new byte[len];
b.get(tb); // get bytes out of byte buffer.
return defineClass(name, tb, 0, len, protectionDomain);
}
}
protectionDomain = preDefineClass(name, protectionDomain);
String source = defineClassSourceLocation(protectionDomain);
Class<?> c = defineClass2(name, b, b.position(), len, protectionDomain, source);
postDefineClass(c, protectionDomain);
return c;
}