您的位置:首页 > 编程语言 > Java开发

Protect your java code

2007-05-26 13:27 281 查看
我是Arren :)
我们知道现在很多的Java反编译器,使得所有的java class文件很容易就被编译成源代码,即使使用了混淆器,但是治标不治本,因为对java虚拟机呈现的总是相同的指令,而java的指令系统就注定了反编译的可行性,以及容易性。
编译为本机指令的二进制代码的C语言程序,以及汇编程序,在反汇编的时候相对来说很难,而能够分析反汇编结果的人需要更高的技能,如果要反编译,更难,因为C语言程序编译的结果已经导致所有的原始代码信息丢失(非debug编译方式),要反编译为可读的代码很困难。 当然能够直接分析 机器指令的人可以在不反编译的情况下直接读懂程序,这样的人很少,计算机领域也没有几个。

所以我的保护程序是建立在此基础上。

(1)
正常开发我们的java程序,完成测试任务后, 摘除核心部分,或者要保护的部分,或者由于某种原因而不想公开的部分, 打包成jar文件,这个对于java开发来说是很简单的部分。 得到 kernal.jar,文件内容为:
/kernal/core/util.class
/kernal/core/main.class
/kernal/startup.class

其中manifest文件包含了:
Main-Class:kernal.startup
,只是了此包的可执行类为 kernal.startup.class。 这个类将在此包被加载的时候引导核心程序的执行。

(2)
加密kernal.jar。我们使用RSA算法加密保护一个AES随机密钥,而整个jar文件使用此密钥加密。
加密工具类如下:

/*
* EncryptFile.java
*
* Created on May 24, 2007, 10:41 AM
*
* To change this template, choose Tools | Template Manager
* and open the template in the editor.
*/

package az.pkg.util;

import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.security.Key;
import java.security.KeyStore;
import javax.crypto.Cipher;
import javax.crypto.CipherOutputStream;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;

/**
* Encrypt file as secret.
*
* The private key is in file "{AZ_HOME}/_KERNAL/keys/checker_rsa.jsk".
*
* @author Arren
*/
public class EncryptFile {

/**
* Creates a new instance of EncryptFile
*/
public EncryptFile() {
}

private void combine(Key key) throws FileNotFoundException, IOException, Exception{
//@todo set this array with the file you want to combine.
String file_to_enc="G:/PJ/TT/dist/TT.jar";
//@todo end.

File src_file = new File(file_to_enc);
if(!src_file.exists() || !src_file.isFile()) return;

FileOutputStream fos=null;
try{
System.out.println("Encrypt file:"+src_file.getCanonicalPath());

/// cipher for encrypt content of the file.
KeyGenerator kg = KeyGenerator.getInstance("AES");
SecretKey seckey=kg.generateKey();
Cipher content_cipher=Cipher.getInstance("AES/ECB/PKCS5Padding");
content_cipher.init(Cipher.ENCRYPT_MODE, seckey);

/// Get the Cipher for wrap the secret-key.
System.out.println("Key we use:" + key.getAlgorithm());
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.WRAP_MODE, key);
System.out.println("Key Cipher got:"+cipher.getAlgorithm());
byte[] b_sec_key = cipher.wrap(seckey);

/// Open the destination file.
/// Where do we store the output file.
File ofile=null;
fos=new FileOutputStream(ofile=new File(src_file.getParent(), src_file.getName()+"_enc"));
System.out.println("Output file="+ofile.getCanonicalPath());

/// Store the secret-key in file first.
DataOutputStream dos=new DataOutputStream(fos);
dos.writeInt(b_sec_key.length);
dos.write(b_sec_key);
dos.flush();

/// Create ciphered stream object.
javax.crypto.CipherOutputStream cos = new CipherOutputStream(fos, content_cipher);
System.out.println("Ciphered content stream got");

byte[] buffer = new byte[2048];
int n=0;
n+=combine_child_file(cos, src_file, buffer);
cos.close();
System.out.println(n+" bytes written.");
}
finally{
if(fos!=null){fos.close();}
}
}

private int combine_child_file(OutputStream os, File file, byte[] buffer) throws Exception {
FileInputStream fis = null;
int buf_sz =buffer.length;
int n=0, r=0;
try{
fis=new FileInputStream(file);
while(-1!=(r = fis.read(buffer))){
os.write(buffer, 0, r);
os.flush();
n+=r;
}
}finally{
if(fis!=null) fis.close();
}
return n;
}

public static void main(String [] args){
String home=System.getenv("AZ_HOME");
System.out.println("AZ_HOME:"+home);
File _kernal = new File(home, "_KERNAL");

File jks = new File(_kernal, "keys/checker_rsa.jks");
FileInputStream fis = null;
try{
KeyStore store = KeyStore.getInstance("JKS");
fis =new FileInputStream(jks);
store.load(fis, "howtofuckyou".toCharArray());
Key key=store.getKey("checker", "howtofuckyou".toCharArray());

new EncryptFile().combine(key);

}catch(Exception e){
if(fis!=null){
try {fis.close();
} catch (IOException ex) {
ex.printStackTrace();
}
}
System.out.println("failed to combine files:"+e.getMessage());
}
}
}

加密的结果是一个文件,因为使用了RSA算法保护密钥,因此即使拿到此文件也无法获取其中的内容。 将此文件命名为 resource, 并和java程序的其他部分一起打包, 形成testproject.jar, 内容如下:
/test/pj/aaa.class
/test/pj/bbb.class
/resource

现在kernal.jar成了整体程序的一个资源文件,且是加密的。
其中比如 test.aaa.class调用了: org.az.io.LibLoader.load();
启动核心部分。

(3)
加密包加载程序。
是一个Java程序,一个类, 如下:

/*
* FuckYou.java
*
* Created on May 23, 2007, 9:59 PM
*
* To change this template, choose Tools | Template Manager
* and open the template in the editor.
*/

package az.jni;

import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.util.jar.Attributes;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;
import java.util.jar.Manifest;
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.NoSuchPaddingException;

/**
* This class is wrapped in "commons-util".
* for more detailed information please @see "org.az.io.LibLoader".
*
* This file is extract files been encrypted by "az.pkg.util.EncryptFile".
*
* @author Arren
*/
public class FuckYou extends ClassLoader {

/**
* Creates a new instance of FuckYou
*/
public FuckYou() {
super(FuckYou.class.getClassLoader());
System.out.println("[FuckYou] when you read this, i'm begin to fuck you :)");
try{
init();
}catch(Exception e){
System.out.println("[FuckYou] i failed to fuck you :"+e.getMessage());
}finally{
Runtime.getRuntime().gc();
}
}

private void init() throws IOException, InvalidKeyException, CertificateException, NoSuchAlgorithmException, NoSuchPaddingException, Exception{

/// Get the encrypted stream.
InputStream in = getClass().getResourceAsStream("/resource");
if(in!=null){
System.out.println("[FuckYou] resource got : "+in);
}else{
System.out.println("[FuckYou] no resource. finished.");
return;
}

/// Load certificate to encrypt the resource.
/// this byte array is the stream of the certificate.
byte[] _file_content={
(byte)0x30,(byte)0x82,(byte)0x1,(byte)0xe5,(byte)0x30,(byte)0x82,(byte)0x1,(byte)0x4e,(byte)0xa0,(byte)0x3,(byte)0x2,(byte)0x1,(byte)0x2,(byte)0x2,(byte)0x4,(byte)0x46,(byte)0x55,(byte)0xa,(byte)0x27,(byte)0x30,(byte)0xd,(byte)0x6,(byte)0x9,(byte)0x2a,(byte)0x86,(byte)0x48,(byte)0x86,(byte)0xf7,(byte)0xd,(byte)0x1,(byte)0x1,(byte)0x5,(byte)0x5,(byte)0x0,(byte)0x30,(byte)0x36,(byte)0x31,(byte)0xb,(byte)0x30,(byte)0x9,(byte)0x6,(byte)0x3,(byte)0x55,(byte)0x4,(byte)0x6,(byte)0x13,(byte)0x2,(byte)0x43,(byte)0x4e,(byte)0x31,(byte)0xb,(byte)0x30,(byte)0x9,(byte)0x6,(byte)0x3,(byte)0x55,(byte)0x4,(byte)0xa,(byte)0x13,(byte)0x2,(byte)0x61,(byte)0x7a,(byte)0x31,(byte)0x1a,(byte)0x30,(byte)0x18,(byte)0x6,(byte)0x3,(byte)0x55,(byte)0x4,(byte)0x3,(byte)0x13,(byte)0x11,(byte)0x61,(byte)0x7a,(byte)0x2e,(byte)0x64,(byte)0x6f,(byte)0x63,(byte)0x6d,(byte)0x61,(byte)0x6e,(byte)0x2e,(byte)0x63,(byte)0x68,(byte)0x65,(byte)0x63,(byte)0x6b,(byte)0x65,(byte)0x72,(byte)0x30,(byte)0x20,(byte)0x17,(byte)0xd,(byte)0x30,(byte)0x37,(byte)0x30,(byte)0x35,(byte)0x32,(byte)0x34,(byte)0x30,(byte)0x33,(byte)0x34,(byte)0x34,(byte)0x33,(byte)0x39,(byte)0x5a,(byte)0x18,(byte)0xf,(byte)0x32,(byte)0x31,(byte)0x30,(byte)0x37,(byte)0x30,(byte)0x34,(byte)0x33,(byte)0x30,(byte)0x30,(byte)0x33,(byte)0x34,(byte)0x34,(byte)0x33,(byte)0x39,(byte)0x5a,(byte)0x30,(byte)0x36,(byte)0x31,(byte)0xb,(byte)0x30,(byte)0x9,(byte)0x6,(byte)0x3,(byte)0x55,(byte)0x4,(byte)0x6,(byte)0x13,(byte)0x2,(byte)0x43,(byte)0x4e,(byte)0x31,(byte)0xb,(byte)0x30,(byte)0x9,(byte)0x6,(byte)0x3,(byte)0x55,(byte)0x4,(byte)0xa,(byte)0x13,(byte)0x2,(byte)0x61,(byte)0x7a,(byte)0x31,(byte)0x1a,(byte)0x30,(byte)0x18,(byte)0x6,(byte)0x3,(byte)0x55,(byte)0x4,(byte)0x3,(byte)0x13,(byte)0x11,(byte)0x61,(byte)0x7a,(byte)0x2e,(byte)0x64,(byte)0x6f,(byte)0x63,(byte)0x6d,(byte)0x61,(byte)0x6e,(byte)0x2e,(byte)0x63,(byte)0x68,(byte)0x65,(byte)0x63,(byte)0x6b,(byte)0x65,(byte)0x72,(byte)0x30,(byte)0x81,(byte)0x9f,(byte)0x30,(byte)0xd,(byte)0x6,(byte)0x9,(byte)0x2a,(byte)0x86,(byte)0x48,(byte)0x86,(byte)0xf7,(byte)0xd,(byte)0x1,(byte)0x1,(byte)0x1,(byte)0x5,(byte)0x0,(byte)0x3,(byte)0x81,(byte)0x8d,(byte)0x0,(byte)0x30,(byte)0x81,(byte)0x89,(byte)0x2,(byte)0x81,(byte)0x81,(byte)0x0,(byte)0xa0,(byte)0x67,(byte)0x10,(byte)0x49,(byte)0x8c,(byte)0x1f,(byte)0x6b,(byte)0xea,(byte)0xb6,(byte)0x4c,(byte)0xcd,(byte)0xbd,(byte)0xba,(byte)0x8e,(byte)0x29,(byte)0xbd,(byte)0x65,(byte)0x3e,(byte)0x15,(byte)0x3b,(byte)0x8c,(byte)0x38,(byte)0xd2,(byte)0x72,(byte)0x8a,(byte)0x7c,(byte)0x61,(byte)0x9e,(byte)0x53,(byte)0x74,(byte)0xe7,(byte)0xab,(byte)0x37,(byte)0xff,(byte)0x8e,(byte)0x42,(byte)0x78,(byte)0x85,(byte)0xd1,(byte)0x23,(byte)0x9f,(byte)0xc0,(byte)0xef,(byte)0xc4,(byte)0xc6,(byte)0xa0,(byte)0x7c,(byte)0xb9,(byte)0x68,(byte)0xc2,(byte)0xa8,(byte)0x5a,(byte)0x3c,(byte)0xff,(byte)0x5c,(byte)0x4d,(byte)0xf,(byte)0xaa,(byte)0x57,(byte)0x16,(byte)0x6c,(byte)0x3c,(byte)0x50,(byte)0xc3,(byte)0x45,(byte)0xc9,(byte)0x21,(byte)0x7d,(byte)0x12,(byte)0x81,(byte)0x60,(byte)0x91,(byte)0x36,(byte)0xa0,(byte)0xd,(byte)0x8c,(byte)0xff,(byte)0xb4,(byte)0xf4,(byte)0xc4,(byte)0xf3,(byte)0x9d,(byte)0x9b,(byte)0xe7,(byte)0xef,(byte)0xd5,(byte)0x9c,(byte)0x60,(byte)0xad,(byte)0x40,(byte)0x4b,(byte)0xc0,(byte)0x8b,(byte)0x4b,(byte)0xc9,(byte)0x5e,(byte)0x24,(byte)0xac,(byte)0xbe,(byte)0x54,(byte)0x2e,(byte)0x2f,(byte)0x3a,(byte)0x68,(byte)0xa3,(byte)0xf1,(byte)0x6,(byte)0xf6,(byte)0x4b,(byte)0x90,(byte)0x9c,(byte)0x1b,(byte)0xff,(byte)0xcc,(byte)0x71,(byte)0x90,(byte)0x88,(byte)0x6,(byte)0x37,(byte)0x4e,(byte)0x9b,(byte)0x26,(byte)0xf0,(byte)0xd3,(byte)0xb,(byte)0x7d,(byte)0xd9,(byte)0x93,(byte)0x2,(byte)0x3,(byte)0x1,(byte)0x0,(byte)0x1,(byte)0x30,(byte)0xd,(byte)0x6,(byte)0x9,(byte)0x2a,(byte)0x86,(byte)0x48,(byte)0x86,(byte)0xf7,(byte)0xd,(byte)0x1,(byte)0x1,(byte)0x5,(byte)0x5,(byte)0x0,(byte)0x3,(byte)0x81,(byte)0x81,(byte)0x0,(byte)0x60,(byte)0x26,(byte)0xf0,(byte)0x37,(byte)0xf9,(byte)0x8c,(byte)0x91,(byte)0x28,(byte)0x2c,(byte)0xae,(byte)0x7a,(byte)0x6a,(byte)0x90,(byte)0xe7,(byte)0x44,(byte)0x6a,(byte)0x37,(byte)0x27,(byte)0xd0,(byte)0x11,(byte)0xcc,(byte)0x1c,(byte)0x69,(byte)0x48,(byte)0x20,(byte)0x1f,(byte)0x11,(byte)0x7b,(byte)0x61,(byte)0x4a,(byte)0x46,(byte)0x86,(byte)0x89,(byte)0xe4,(byte)0x73,(byte)0x42,(byte)0x85,(byte)0xc6,(byte)0xc6,(byte)0x3b,(byte)0x14,(byte)0x7,(byte)0xe7,(byte)0x3c,(byte)0x3c,(byte)0x24,(byte)0xec,(byte)0x30,(byte)0xe7,(byte)0x51,(byte)0x48,(byte)0x1a,(byte)0x12,(byte)0x2a,(byte)0x95,(byte)0xca,(byte)0x53,(byte)0x5f,(byte)0xf,(byte)0xf8,(byte)0xfc,(byte)0xd5,(byte)0xc9,(byte)0xfc,(byte)0xe1,(byte)0xd,(byte)0xee,(byte)0x69,(byte)0xec,(byte)0x8e,(byte)0xed,(byte)0x8f,(byte)0xfc,(byte)0x1b,(byte)0x74,(byte)0xf0,(byte)0x40,(byte)0x6d,(byte)0xd8,(byte)0x3e,(byte)0xf0,(byte)0x88,(byte)0xa0,(byte)0xd6,(byte)0xac,(byte)0x18,(byte)0x72,(byte)0xe0,(byte)0x52,(byte)0x9f,(byte)0x73,(byte)0xd9,(byte)0x92,(byte)0xfa,(byte)0x27,(byte)0xe2,(byte)0x31,(byte)0xc9,(byte)0xef,(byte)0x4,(byte)0x4c,(byte)0x4f,(byte)0xb9,(byte)0x6f,(byte)0xcd,(byte)0x3b,(byte)0x1d,(byte)0x33,(byte)0x63,(byte)0x2a,(byte)0x15,(byte)0xa5,(byte)0x1b,(byte)0xd8,(byte)0x6,(byte)0x1e,(byte)0xa,(byte)0x1a,(byte)0x66,(byte)0xe9,(byte)0x4,(byte)0xcf,(byte)0xfd,(byte)0x3f,(byte)0xac,(byte)0xce,(byte)0xde,(byte)0xbe
};

/**We generate the certificate with its binary content,
* we use cert's publick decrypt the file.
**/
CertificateFactory cf=CertificateFactory.getInstance("X.509");
Certificate cert=cf.generateCertificate(new ByteArrayInputStream(_file_content));
System.out.println("[FuckYou] certificate got.");
Cipher key_cipher = Cipher.getInstance("RSA");
key_cipher.init(Cipher.UNWRAP_MODE, cert);

/// Data reader, used to read key -size.
DataInputStream dis = new DataInputStream(in);

/// Read a integer value,
/// this is the size of the secret-key.
int key_sz=dis.readInt();
if(key_sz<0 || key_sz>2048) throw new java.lang.Exception("Illegal file: key size invalid.");
byte[] key=new byte[key_sz];
if(key_sz!=in.read(key)) throw new java.lang.Exception("Illegal file: file size too small.");

/// now the key we got is encrypted by the RSA key,
/// we must decrypt it to use.
Key sec_key = key_cipher.unwrap(key, "AES", Cipher.SECRET_KEY);
java.util.Arrays.fill(key, 0, key_sz, (byte)0);
key = null;
System.out.println("[FuckYou] secret key got");

/// ok
/// we got the secret key.
Cipher file_cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
file_cipher.init(Cipher.DECRYPT_MODE, sec_key);
CipherInputStream cis = new CipherInputStream(in, file_cipher);
System.out.println("[FuckYou] cipher stream got");

// /// write the file to disk,
// /// or use it in memory.
// FileOutputStream fos=null;
// try{
// fos=new FileOutputStream(new File("g:/xxx.jar"));
// byte[] buffer = new byte[2048];
// int r=0;
// while(-1!=(r=cis.read(buffer))) fos.write(buffer, 0, r);
// }finally{
// if(fos!=null){fos.close(); fos=null;}
// }
System.out.println("[FuckYou] load module from resources");
try{
load_jar_file(cis, true);
}finally{
cis.close();
System.out.println("[FuckYou] module loaded.");
}
}

private void load_jar_file(InputStream jar_file, boolean load_or_write) throws IOException, Exception{
JarInputStream jis = new JarInputStream(jar_file);
String tmp=null;
final int MAX_SIZE = 1024*128; long size=0;
byte [] buffer = new byte[MAX_SIZE];

/// Read each entry in the jar file,
/// every entry should be an .class object.
JarEntry entry=jis.getNextJarEntry();
while(entry!=null){
if(!entry.isDirectory() && (tmp=entry.getName()).endsWith(".class")){
/// Check entry size.
size=entry.getSize();
if(size<64 || size>MAX_SIZE){
System.out.println("[FuckYou] module component is too large. ignored.");
}
else if(load_or_write){
int p=tmp.indexOf(".class");
load_class(jis, tmp.substring(0, p).replace('/','.'), buffer, MAX_SIZE);
// }else{
// write_class(jis, tmp, buffer);
}
}
entry=jis.getNextJarEntry();
}

if(load_or_write){
System.out.println("[FuckYou] trying to bootstrap module...");
/// if Main-Class defined,
/// we create an instance of it.
Manifest mf=jis.getManifest();
if(mf!=null){
Attributes attrs=mf.getMainAttributes();
String mc=attrs.getValue(Attributes.Name.MAIN_CLASS);
if(mc!=null){
System.out.println("[FuckYou] found entry point :"+mc);
try{
Class cls=Class.forName(mc, true, this);
System.out.println("[FuckYou] got class object:"+cls);
Method m=cls.getDeclaredMethod( "main", new Class[]{String[].class});
System.out.println("[FuckYou] found 'main' method:"+m);
m.invoke(null, (Object)null);
System.out.println("[FuckYou] 'main' method invoked.");
}catch(Exception e){
e.printStackTrace();
System.out.println("[FuckYou] failed to bootstrap module by main():"+e.getMessage());
}
}else{
System.out.println("[FuckYou] no entry point. no need to bootstrap.");
}
}//.if.
}
}

/**
* Load a class definition from stream
**/
private void load_class(InputStream cls_stream, String cls_name, byte[] buffer, int bl ) throws Exception{
System.out.println("[FuckYou] find component :"+cls_name);
int off=0, r=0;
while(-1!=(r=cls_stream.read(buffer, off, bl-off))){
off+=r;
}
System.out.println("[FuckYou] component size: "+String.valueOf(off));
Class cls = super.defineClass(cls_name, buffer, 0, off);
}
//
// private void write_class(InputStream cls_stream, String cls_name, byte[] buffer ) throws Exception{
// File file = new File(cls_name);
// File dir = file.getParentFile();
// if(!dir.exists()) dir.mkdirs();
// FileOutputStream fos = null;
// try{
// fos = new FileOutputStream(file);
// int r=0;
// while(-1!=(r=cls_stream.read(buffer))) fos.write(buffer, 0, r);
// }
// finally{
// if(fos!=null){fos.close();}
// }
// }
}

此类是 核心程序的加载类,其中包含了解密/resource用的RSA公钥,即字节流 byte[] _file_content。关于把任意文件转换成 字节数组定义形式,我提供了工具类。

(4)
保护Fuckyou.class文件。我们对此文件进行加密以及编码。打开文件流为 fis, 加密输出为 writer:

private static void encrypt(InputStream fis, int l, Writer writer) throws Exception{
writer.write("byte[] _file_content={ ");
int i=0, a=0, code=0;
if(-1!=(a=fis.read())){
code = a ^ 0x11;
writer.write(" (byte)0x"); writer.write(Integer.toHexString(code));
while(-1!=(a=fis.read())){
code = a ^ 0x11;
writer.write(",(byte)0x"); writer.write(Integer.toHexString(code));
}
}
writer.write(" }; ");
}

输出为加密并编码的 byte数组定义文件。 注意到,我们使用了简单的字节异或操作来加密。这样子比较简单。方法类似可替换。

(5)
至此,核心程序kernal.jar已经加密,而加密包引导程序也已经准备好。注意:(2),(3),(4)步骤中的类都是我们加密工具的一部分,因此普通用户只需要完成步骤(1)的任务即可,所以程序是通用的。

(6)
引导加密程序,我们需要使用C语言代码,调用JNI。

/*
* LibLoader.java
*
* Created on May 24, 2007, 9:42 AM
*
* To change this template, choose Tools | Template Manager
* and open the template in the editor.
*/

package org.az.io;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;

/**
*
* @author Arren
*/
public class LibLoader {

/*** Cache the specified stream to a file on disk,
* later Java system could load it as library.
***/
private static String keep_file(String to, InputStream from){
FileOutputStream fos=null;
try{
File file=new File(System.getProperty("java.io.tmpdir"),to);
fos=new FileOutputStream(file);
int a=0;
while(-1!=(a=from.read())) fos.write(a);
fos.close(); fos=null;

String path = file.getCanonicalPath();
return path;
}catch(Exception e){
return to;
}finally{
if(fos!=null){
try {
fos.close();
} catch (IOException ex) {
System.out.println("[commons-util] failed to close the lib-file.");
}
}
}//finally.
}

static{
String path = null;
try{
InputStream is=LibLoader.class.getResourceAsStream("/libloader.dll");
path = keep_file("libldr.dll", is);
is.close();
/// Load the library.
System.load(path);
}catch(Exception e){
System.out.println("[commons-util] failed to load system library | " + e.getMessage());
}
}//.static.

private static LibLoader _instance = new LibLoader();
/**
* Creates a new instance of LibLoader
*/
private LibLoader() {
}

public synchronized static void load(){
try{
System.out.println("[commons-util] spawn libs...");
_instance.spawn( _instance.getClass().getClassLoader());
}catch(Exception e){
System.out.println("[commons-util] failed to spawn more libs | "+e.getMessage());
}finally{
System.out.println("[commons-util] complete.");
}
}

private native void spawn(Object loader);
}

注意其中的 private native void spawn(Object loader) 方法。
创建C语言动态库项目,使用如下程序:libloader.c,

#include "LibLoader.h"

//@author : arren zhang.

const char * CLASS_NAME="az/jni/FuckYou";
typedef char byte;

// The encrypted "az.jni.Fuckyou" class.
byte _file_content[]={
(byte)0xa3,(byte)0x97,(byte)0xd3, ...
};

/**
Define a class in JVM space.
This class will work for us.

@param loader The ClassLoader object used to load this new defined class.
@return The new defined Class.
**/
jclass defineMyClass(JNIEnv * env, jobject loader){
char * buf=_file_content;
int sz=sizeof(_file_content), i=0;
for(i=0;i<sz;i++){
buf[i]=buf[i]^0x11;
}
jclass cls=(*env)->DefineClass(env, CLASS_NAME, loader, buf, sz);
return cls;
}

JNIEXPORT void JNICALL Java_org_apache_commons_io_LibLoader_spawn
(JNIEnv * env, jobject obj, jobject cls_loader)
{
/// Load the class, and create new instance of it.
jclass cls = defineMyClass(env, cls_loader);
jmethodID m_init=(*env)->GetMethodID(env, cls, "<init>", "()V");
if(m_init){
jobject new_obj= (*env)->NewObject(env, cls, m_init);
}

/// Exception check
jthrowable err=(*env)->ExceptionOccurred(env);
if(err){
jint r=(*env)->Throw(env, err);
printf("commons-util: exception rethrowed:%d ", r);
}
return;
}

编译结果为libloader.dll. 我们把此文件和LibLoader .class一起打包成libloader.jar。这个文件就是提供给最终程序的。
至此,保护程序的唯一入口就是 libloader.dll文件,也是保护程序的弱点,但是所有的安全程序都有一个弱点,即使全部使用 RSA加密,也存在一个问题,你如何存放RSA私钥,如果你加密此私钥,那么你加密此私钥的密钥又放在何处? 所以最后总是有一个弱点。
当然可以用硬件之类的保护此弱点,但我在此介绍方法而已。当然攻破C程序难度不小,相对安全。

LibLoader.class 和libloader.dll一起打包后得到, libloader.jar.

(7)最后发布程序
至此我们得到的文件包括:
testproject.jar, 其中包含了加密过的kernal.jar。
libloader.jar,加密程序,可以通用。

这就是所有了,保护程序其实是和程序主体部分分离的,只需要在发布之前进行保护。当然如果你已经公布了核心部分的代码,那保护就没有必要了:)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐