- 浏览: 179512 次
- 性别:
- 来自: 重庆
最新评论
-
way_super:
请问楼主,地图都可以下载了,就是你这里面没有的添加标记的功能, ...
googleMap本地化(离线) -
way_super:
gaoxiangky 写道你好,这个经纬度我解决了,但是我将这 ...
googleMap本地化(离线) -
foreach1025:
...
java源程序加密解决方案(基于Classloader解密) -
cuishuailingcsl:
写的很好,学习了~
java源程序加密解决方案(基于Classloader解密) -
liuchao123:
“保存文件并使用命名source /etc/profile重新 ...
windows上hadoop安装(cygwin等)
源程序加密解决方案
1. 概述:
Java源程序的加密,有如下两种:
1使用混淆器对源码进行混淆,降低反编译工具的作用
2基于classloader的自定义加密、解密运行
1.1. 混淆器加密
1.2. 自定义classloader加密
1.2.1. 原理
原理:java虚拟机的动态加载机制,为classloader加密方案提供了理论基础。在jvm装载运行程序,初始的时候,只装在了必要的类,如java.lang.String等,而应用程序的类并没有一次性装入内存。Jvm解释执行应用程序的过程中,如果发现有未装载的类,则会调用装载正在执行的那个类的classloader来装载,这个过程是一层一层向上,直到顶层的classloader。Jvm启动的时候会装入ExtClassloader,而ExtClassloader又会装载AppClassloader,例如:
Class Hello{ Public static void main(String[] args){ System.out.println(“hello”); HelloMethod.sayHello(); } } Class HelloMethod{ Public static void sayHello(){ System.out.println(“hello in static HelloMethod”); } }
有上面两个类的定义,在执行Hello类的main方法的时候,首先会委托装载Hello类的classloader来装载HelloMethod类,即jvm会委托AppClassloader来装载,但是在AppClassloader的实现的时候,会首先委托装载AppClassloader的classloader来装载,如果上层的classloader无法装载,才会由AppClassloader来装载HelloMethod类。这种模式叫做双亲委托模式。在jvm的所有classloader中都是如此,首先由父classloader加载,失败由自身加载。
Java虚拟机的这种特性,使得我们可以自定义一个classloader,然后由这个classloader来装载应用程序的启动类,然后在启动应用程序,那么当应用程序中有未装载的类的时候,java徐机器逐层向上请求classloader装载新类,那么首先被请求的就是装在应用程序的classloader,即我们自定义的classloader,我们完全可以首先调用自己的加载方法来加载类,如果加载不成功,可以请求父classloader来加载,因为来请求加载的类是完全有可能是系统的类。
在我们使用自定义的classloader的时候,装载自己的程序,那么就可以对装入的字节码进行一定的操作,比如解密。在调用自定义的装载器classloader的时候,首先是要装入被加密之后的文件,通常情况下仍旧已.class为扩展名,在调用defineClass之前对装入的数据解密。
1.2.2. Classloader的两个重要方法
protected Class defineClass(String name, byte[] classData, int offset, int length);
最原子的操作,在调用自定义的classloader加载新类的时候,首先根据自定义规则找到加载的类所存放的位置,然后将数据一byte[]类型读入,进行解密运算时候,调用该方法,以生成一个Class。这是一个比较核心的方法,这个方法是被抽象的Classloader定义为protected访问标记的,只有继承了Classloader这个类才能使用。
Class loadClass(String name, boolean resolve);
Java虚拟机,在装载新类,递归向上查找并调用的方法,在自定义classloader中需要重写,就是判断是否能够自己装载,如果能则自己装载,否则交由系统装载。
2. 源程序加密解决方案
2.1. 自定义classloader加密
加密和解密要是对应的,即使用加密之后的数据,经过解密是需要能够得到原来的数据。
2.1.1. 加密应用程序
为了简单,在这里才用一种简单的加密方法,把得到的需要加密的数据,以字节取,每一个字节加1,对应的解密就是每一个减1。
还是以Hello、HelloMethod类为例子,
BufferedInputStream bis = new BufferedInputStream(new FileInputSteam(“d:/workbench/ciphertool/bin/com/aatest/Hello.class”)); byte[] data = new byte[bis.avialable()]; bis.read(data); bis.close(); for(int I = 0; I < data.length; i++){ data[i] =(byte)( data[i] + 1); } BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(“d:/workbench/ciphertool/bin/com/aatest/Hello.class”)); bos.write(data); bos.close();
将加密对象取出,加密,然后存盘。
2.1.2. 解密运行应用程序
在自定义的classloader接收到加载新类请求的时候,首先读入加密之后的文件,然后解密,最后调用defineClass(name, classData, offset, length)生成类,返回出去。
拦截新类加载请求
package com.cjnetwork.ciphertool.core; import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.jar.JarEntry; import java.util.jar.JarFile; import com.cjnetwork.ciphertool.util.CipherUtil; public class CjClassloader extends ClassLoader { String classpath; Map<String, Class> loadedClassPool = new HashMap<String, Class>(); public CjClassloader(String classpath) { this.classpath = classpath; } @SuppressWarnings("unchecked") @Override public synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { Class claz = null; if (loadedClassPool.containsKey(name)) { claz = this.loadedClassPool.get(name); } else { try { if (claz == null) { claz = super.loadClass(name, false); if (claz != null) { System.out.println("系统加载成功:" + name); } } } catch (ClassNotFoundException e) { System.out.println("系统无法加载:" + name); } try { if (claz == null) { claz = loadByCjClassLoader(name); if (claz != null) { System.out.println("自定义加载成功:" + name); } } } catch (Exception e) { System.out.println("自定义无法加载:" + name); } if (claz != null) { this.loadedClassPool.put(name, claz); } } if (resolve) { resolveClass(claz); } return claz; } /** * * 解密加载 * * * @param name * @return */ @SuppressWarnings("unchecked") private Class loadByCjClassLoader(String name) { Class claz = null; try { byte[] rawData = loadClassData(name); if (rawData != null) { byte[] classData = decrypt(getReverseCypher(this.cjcipher.getKeycode()), rawData); classData = CipherUtil.filter(classData, this.cjcipher); claz = defineClass(name, classData, 0, classData.length); } } catch (Exception e) { claz = null; } return claz; } }
最主要的是集成Classloader,并重写Class loadClass(String name, Boolean resolve)方法,在这个方法中,可以根据需要自己加载需要的文件,并解析生成Class。
解密并返回Class
BufferedInputStream bis = new BufferedInputStream(new FileInputSteam(“d:/workbench/ciphertool/bin/com/aatest/Hello.class”)); byte[] data = new byte[bis.avialable()]; bis.read(data); bis.close(); for(int I = 0; I < data.length; i++){ data[i] =(byte)( data[i] - 1); } Class claz = defineClass(“Hello”, data, 0, data.length);
2.2. 加密自定义classloader
采用以上的方法,就可以将应用程序加密,使得被加密的程序不能被反编译,因为加密之后的class文件已经不是jvm定义的标准class文件,只能通过解密运行程序解密,才能运行。
如果只做到这一步,对于java源程序加密还没有完成。虽然应用程序无法直接反编译,但是自定义的classloader是没有被加密的,它自身是可以被反编译的。理论上,如果得到真正的class文件(即jvm标准的class文件),是可以反编译的java文件,在这里,假设得到class文件就得到了java文件。
如果攻击者将自定义的classloader反编译,得到源码,则攻击者可以再自定义解密运行的同事,将得到的应用程序的字节码存储到本地,那么,攻击者就相当于是跳过了源程序加密解密。例如攻击者在代码Class claz = defineClass(name, classData, offset, length);这句代码前,将classData存储到本地,即攻击者可以再解密运行应用程序的同时,将应用程序的字节码保存,就达到了破解应用程序源代码的效果。
为了描述方便,实例化一个 自定义的classloader,叫做CjClassLoader
这一个漏洞在于,CjClassLoader没有加密,攻击者可以在其中嵌入导出应用程序代码,那么,要解决这个问题,加密CjClassLoader就成了保护应用程序源代码的关键。
试想,如果加密、解密运行程序中,没有CjClassLoader.class文件,或是CjClassLoader.class文件本身也是经过加密的,CjClassLoader类的获得也是通过自己书写的方法动态获取,那么攻击者无法获取到CjClassLoader.class文件,相当于无法获取到CjClassLoader.java文件,那么也就无法再其中加入到处应用程序类文件的代码,那么被加密的应用程序可以认为是安全的。
假设将CjClassLoader.class加密后生成CjClassLoaderEncryptor0.class,那么CjClassLoader是安全了,但理论上攻击者还是可以通过反编译CjClassLoaderEncryptor0来获取CjClassLoader的源码,那么保护CjClassLoaderEncryptor0又成了保护应用程序的关键,注意在CjClassLoaderEncryptor0中存在解密CjClassLoader的密钥,即将密钥硬编码到CjClassLoaderEncryptor0中,这样做是为了防止攻击者直接获取密钥,直接破解最里面一层的加密,至于什么是最里面一层,请继续看后文。
那么如何CjClassLoaderEncryptor0.class的安全性呢,我们同样采取加密的方式,即将CjClassLoaderEncryptor0.class加密,生成CjClassLoaderEncryptor1.class,在解密运行的时候首先动态的生成CjClassLoaderEncryptor1.class,在由CjClassLoaderEncryptor1所定义的类动态的装入CjClassLoaderEncryptor0.class,并且解密生成CjClassLoader,最后使用CjClassLoader装入应用程序,运行。整体上的思路如下:
CjClassLoader.class ——》 CjClassLoaderEncryptor0.class CjClassLoaderEncryptor1.class ——》 CjClassLoaderEncryptor1.class CjClassLoaderEncryptor2.class ——》 CjClassLoaderEncryptor2.class CjClassLoaderEncryptor3.class ——》 CjClassLoaderEncryptor3.class 。。。。。。 CjClassLoaderEncryptorN.class ——》 CjClassLoaderEncryptorN.class
这样的一级一级加密,我们称CjClassLoaderEncryptorN.class为最外层,成CjClassLoaderEncryptor0.class为最里层。除去最外层没有加密,里面的每一层都是加密之后的数据,都是不能直接为jvm所识别的字节码,都是需要通过后一级的解密程序解密之后才能为jvm所识别。
系统装在CjClassLoaderEncryptorN.class,生成CjClassLoaderEncryptorN类,使用反射机制,调用CjClassLoaderEncryptorN类中的方法,这个方法可以动态的装入CjClassLoaderEncryptor(N-1).class,并利用CjClassLoaderEncryptorN中的密钥,解密CjClassLoaderEncryptor(N – 1),然后生成CjClassLoaderEncryptor( N – 1)类,最后调用CjClassLoaderEncryptor(N – 1)中的方法。而CjClassLoaderEncryptor( N – 1)类中的方法,可以动态装入CjClassLoaderEncryptor(N- 2).class文件,并利用CjClassLoaderEncryptor(N – 1)中的密钥,解密CjClassLoaderEncryptor(N – 2),然后生成CjClassLoaderEncryptor(N – 2)类,最后调用方法,被调用的方法可以动态的装入CjClassLoaderEncryptor(N – 3).class。。。。。。。。
CjClassLoaderEncryptorN (密钥N,动态装入,解密,方法调用) CjClassLoaderEncryptor(N–1) CjClassLoaderEncryptor(N-1) (密钥N-1,动态装入,解密,方法调用) CjClassLoaderEncryptor(N–2) CjClassLoaderEncryptor(N-2) (密钥N-2,动态装入,解密,方法调用) CjClassLoaderEncryptor(N-3) ...... CjClassLoaderEncryptor1 (密钥1,动态装入,解密,方法调用) CjClassLoaderEncryptor0 CjClassLoaderEncryptor0 (密钥0,动态装入,解密,方法调用) CjClassLoader
最后使用CjClassLoader解密装载应用程序。
通过这样一个过程的加密CjClassLoader,可以达到保护加密程序本身的目的,这种保护在理论上是可破,但在实际操作中将会变得困难,因为密钥是通过硬编码的方式存储在下一层的封装器中,即CjClassLoaderEncryptor(N-1).class的密钥是放在CjClassLoaderEncryptorN.class中,如果存在CjClassLoaderEncryptor1000.class,那么加密过程将会变得非常复杂。
当然动态生成CjClassLoaderEncryptorN.class的工作,虽然内置了应编码(解密CjClassLoaderEncryptor(N-1)的密钥),但是这样一个过程,是不需要手动实现,利用程序自动生成即可。目前,这个版本的实现中是采用了动态生成CjClassLoaderEncryptorN.java文件,然后调用javac 命令,编译生成class文件。
请记住,这个过程是理论上不安全的,但如果需要加密的应用程序非常的重要,那么可以将加密、解密运行自身的CjClassLoader加密次数增加,以达到更加安全的目的。
2.3. 隐藏自定义classloader
通过上述加密CjClassLoader的方案,可以使得CjClassLoader变得相对安全,但似乎还是有一个问题,即解密运行程序本身的main方法中,会动态的装入CjClassLoaderEncryptorN,然后通过层层调用,最终获取到CjClassLoader类,然后使用CjClassLoader解密装载应用程序,这段代码是没有加密的,攻击者可以不考虑CjClassLoaderEncryptorN开始的层层调用,只需要在最终获取的CjClassLoader解密应用程序之前,将CjClassLoader本地化,即可以获得未经加密的CjClassLoader,这样,就不安全了。
解决这个问题,可以将这段代码中动态获取CjClassLoader类,修改为动态获取CjClassLoader中的Class loadClass(String name, Boolean resovle)方法,然后直接使用获取到的方法,开始加载应用程序。
如此,攻击者就没有办法直接获取到解密之后的CjClassLoader,保护了加密、解密程序。
2.4. 隐藏加密、解密方法
在上述的实现中,CjClassLoader中加密、解密应用程序的方法是被放置于CipherUtil.class文件中,而这个文件是没有被加密的,攻击者是可以直接获取到应用程序加密和解密的方法的,这给应用程序带来了不安全性,是的攻击者不利用解密程序的繁琐解密过程,而自定调用CipherUtil.class中的方法,解密应用程序。
解决这个问题,可以将CipherUtil.class中的加密和解密方法封装到CjClassloader中,因为CjClassloader是没有办法直接得到,所以认为加密解密所用到的方法是安全的。最终在程序中调用的时候不是直接得到CjClassloader类,都是通过CjClassloaderEncrytorN的层层方法调用,而直接获取到需要使用的方法。例如,我们可以在CjClassloaderEncrytorN类中封装了一个Method getEncrytMethod(),如此的方法,这个方法会去调用CjClassloaderEncrytor(N-1)中的同名方法,如此一直调用,直到CjClassloaderEncrytor0.class中,在这个类中直接反射获得CjClassloader中的加密方法,当然这个是比较特殊的,因为在CjClassloaderEncrytor0中时候反射获取CjClassloader中方法的时候,这个反射是需要带参数的,但这个带参数获取也是简单的。
3. bug
异常堆栈过长
经过这种一层一层的CjClassLoader解密运行的源程序,其堆栈是很长的,如果应用程序中,出现异常,答应异常或日志记录将会变得很麻烦,会记录很多无用的堆栈信息。
备注:文中提到的应用程序,指需要被加密的程序。
欢迎评论。
评论
23 楼
foreach1025
2015-10-29
22 楼
cuishuailingcsl
2015-07-07
写的很好,学习了~
21 楼
JetMah
2011-02-28
lovit 写道
如果不考虑跨平台,可以用JNI加解密。也是自定义CLASSLOADER的方法。
楼上的能否说一下这种思路的详细实现方式?
20 楼
lovit
2010-12-28
如果不考虑跨平台,可以用JNI加解密。也是自定义CLASSLOADER的方法。
19 楼
RednaxelaFX
2010-12-27
aabcc 写道
RednaxelaFX 写道
另外也有一些办法,例如说不让用户使用标准的JDK,而必须使用专用的launcher来启动应用(注意到java/java.exe其实就是一个launcher而已,并不是JVM本身);在launcher里多做点手脚,就像保护一般的本地程序一样,例如说让调试器更难连接上来,这就足以使得HotSpot原本支持的class dump方式的一些接口不能直接用了。
学习了,马克一下。
“让调试器更难连接上来” 这个点需要学习一下...
另外忘了赞一下LZ的帖子,思路很清晰。(可惜没有投票权,不然投精了)
嗯例如说在Windows的话IsDebuggerPresent之类的能简单的扛一下,然后如果想办法把这类函数的调用藏起来又能再提高那么一点烦琐程度。总之…嗯就是想办法让拆解的人烦得不想去拆了那就成功了
18 楼
aabcc
2010-12-27
RednaxelaFX 写道
aabcc 写道
RednaxelaFX 写道
xielingjiang 写道
至于class dump,那就是另外关于如何防止class dump的议题了吧,但我想任何一个生产服务器不可能开启这种在开发模式才允许的操作吧?
不,标准的Sun JDK在什么模式都支持class dump。而且还有好几组API支持这个操作,我前面只是给了其中一种的例子。
OH,那就是说Class文件加密然后Classloader层解密加载,不管做得多复杂都是徒劳的? Runtime的class都能被dump出来了,难道只有代码混淆才是 真正有意义的?
这些技巧主要都是用来增加“普通人”的拆解难度的,并不是要“理论上杜绝拆解”。
虽然知道的话做起来不难,但真正知道如何做class dump的人也并不算多,对吧?
另外也有一些办法,例如说不让用户使用标准的JDK,而必须使用专用的launcher来启动应用(注意到java/java.exe其实就是一个launcher而已,并不是JVM本身);在launcher里多做点手脚,就像保护一般的本地程序一样,例如说让调试器更难连接上来,这就足以使得HotSpot原本支持的class dump方式的一些接口不能直接用了。
学习了,马克一下。
“让调试器更难连接上来” 这个点需要学习一下...
另外忘了赞一下LZ的帖子,思路很清晰。(可惜没有投票权,不然投精了)
17 楼
RednaxelaFX
2010-12-27
aabcc 写道
RednaxelaFX 写道
xielingjiang 写道
至于class dump,那就是另外关于如何防止class dump的议题了吧,但我想任何一个生产服务器不可能开启这种在开发模式才允许的操作吧?
不,标准的Sun JDK在什么模式都支持class dump。而且还有好几组API支持这个操作,我前面只是给了其中一种的例子。
OH,那就是说Class文件加密然后Classloader层解密加载,不管做得多复杂都是徒劳的? Runtime的class都能被dump出来了,难道只有代码混淆才是 真正有意义的?
这些技巧主要都是用来增加“普通人”的拆解难度的,并不是要“理论上杜绝拆解”。
虽然知道的话做起来不难,但真正知道如何做class dump的人也并不算多,对吧?
另外也有一些办法,例如说不让用户使用标准的JDK,而必须使用专用的launcher来启动应用(注意到java/java.exe其实就是一个launcher而已,并不是JVM本身);在launcher里多做点手脚,就像保护一般的本地程序一样,例如说让调试器更难连接上来,这就足以使得HotSpot原本支持的class dump方式的一些接口不能直接用了。
16 楼
aabcc
2010-12-27
RednaxelaFX 写道
xielingjiang 写道
至于class dump,那就是另外关于如何防止class dump的议题了吧,但我想任何一个生产服务器不可能开启这种在开发模式才允许的操作吧?
不,标准的Sun JDK在什么模式都支持class dump。而且还有好几组API支持这个操作,我前面只是给了其中一种的例子。
OH,那就是说Class文件加密然后Classloader层解密加载,不管做得多复杂都是徒劳的? Runtime的class都能被dump出来了,难道只有代码混淆才是 真正有意义的?
15 楼
RednaxelaFX
2010-12-27
xielingjiang 写道
至于class dump,那就是另外关于如何防止class dump的议题了吧,但我想任何一个生产服务器不可能开启这种在开发模式才允许的操作吧?
不,标准的Sun JDK在什么模式都支持class dump。而且还有好几组API支持这个操作,我前面只是给了其中一种的例子。
14 楼
xielingjiang
2010-12-27
cjnetwork 写道
xielingjiang 写道
你的class是经过加密了的啊,只有你自定义的classloader可以解密加载啊,即使对方hijack你的classloader也不能读取你的class,因为他不知道密钥是什么。
所以关键点就在于密钥的安全性的问题了,密钥可以多级管理啊,比如银行里面的三级加密体系,如果要很安全,可以用加密机存储你的顶级密钥,如果一般的机房实现,可以把密钥导入到JVM里面或者web服务器的keystore里面啊,这样只有授权的虚拟机才能运行你的程序:)
如果CjClassloader(自定义classloader)只有一个,应用程序的class文件是经过加密的,别人是无法直接读取加密后的应用程序的class文件,但是CjClassloader本身没有加密,CjClassloader自身的不安全性,会使得能够在CjClassloader中加入代码,在CjClassloader使用自定义装入class文件,并获取数据,解密,然后使用解密后的数据定义类(Class),这里的解密后的数据是可以导出的。无论加密算法有多么安全,在CjClassloader中会出现应用程序的非加密数据,因此说是不安全的。
当然如果建立完整的密钥授权机制,即每次获取解密密钥之后,密钥并不存储在本地,那这样的方式,应用程序的安全性是依赖于建立的密钥授权机制(还要除去jvm本身支持的class dump方法这种特殊的情况)。
你的担心无非就是破解者知道了机房服务器的管理员权限,然后把你的classloader修改了然后把解密的内容打印出来。
但是这种情况的发生概率还是非常小的吧(替换class还重启服务器,跟中木马一样)。大部分情况是别人在发布或者传输过程中可以拿着你的class包,然后自己部署反编译,我上面说的这种一机一密的方式就可以防止这种情况的嘛。
至于class dump,那就是另外关于如何防止class dump的议题了吧,但我想任何一个生产服务器不可能开启这种在开发模式才允许的操作吧?
13 楼
cjnetwork
2010-12-27
xielingjiang 写道
你的class是经过加密了的啊,只有你自定义的classloader可以解密加载啊,即使对方hijack你的classloader也不能读取你的class,因为他不知道密钥是什么。
所以关键点就在于密钥的安全性的问题了,密钥可以多级管理啊,比如银行里面的三级加密体系,如果要很安全,可以用加密机存储你的顶级密钥,如果一般的机房实现,可以把密钥导入到JVM里面或者web服务器的keystore里面啊,这样只有授权的虚拟机才能运行你的程序:)
如果CjClassloader(自定义classloader)只有一个,应用程序的class文件是经过加密的,别人是无法直接读取加密后的应用程序的class文件,但是CjClassloader本身没有加密,CjClassloader自身的不安全性,会使得能够在CjClassloader中加入代码,在CjClassloader使用自定义装入class文件,并获取数据,解密,然后使用解密后的数据定义类(Class),这里的解密后的数据是可以导出的。无论加密算法有多么安全,在CjClassloader中会出现应用程序的非加密数据,因此说是不安全的。
当然如果建立完整的密钥授权机制,即每次获取解密密钥之后,密钥并不存储在本地,那这样的方式,应用程序的安全性是依赖于建立的密钥授权机制(还要除去jvm本身支持的class dump方法这种特殊的情况)。
12 楼
xielingjiang
2010-12-27
cjnetwork 写道
xielingjiang 写道
关键点还是建立加密体系,而不是classloader体系。
我倒觉得没必要这么多级的classloader,定义一级classloader就可以了。你只要用对一种加密算法,你的cjClassloader逻辑和公钥可以公开,但是你的私钥不公开,这样不就可以达到加密的目的了吗?
当然也可以用对称加密,比如用最简单的des算法就可以了
我倒觉得没必要这么多级的classloader,定义一级classloader就可以了。你只要用对一种加密算法,你的cjClassloader逻辑和公钥可以公开,但是你的私钥不公开,这样不就可以达到加密的目的了吗?
当然也可以用对称加密,比如用最简单的des算法就可以了
1、CjClassloader中,最后会调用方法Class defineClass(String name, byte[] classData, int offset, int length)来定义一个类,然后返回加载好的类给请求加载请求者,如果不隐藏CjClassloader,则攻击者在这里直接导出解密后的数据classData,即然后了你的程序加密方法,而不论你的算法有多安全。
2、des,是的,我在实现中使用了这个算法加密,但是这种对称密钥的算法,在解密应用程序的时候,是需要使用的,那么,这个密钥(确切的说是生成密钥的密码)是一定会给别人的。例如,你需要在你托管的机房运行你的应用程序,那么那个机房的服务器上是一定需要你的密钥,当然,如果说是在启动程序之后就删除密钥,这种情况除外。
你的class是经过加密了的啊,只有你自定义的classloader可以解密加载啊,即使对方hijack你的classloader也不能读取你的class,因为他不知道密钥是什么。
所以关键点就在于密钥的安全性的问题了,密钥可以多级管理啊,比如银行里面的三级加密体系,如果要很安全,可以用加密机存储你的顶级密钥,如果一般的机房实现,可以把密钥导入到JVM里面或者web服务器的keystore里面啊,这样只有授权的虚拟机才能运行你的程序:)
11 楼
yimlin
2010-12-27
无论你做多少层classloader无非就是增加破解的复杂度而已。
但只要是用java写的复杂度都不算高,如果需要应该考虑采用jni方式,在jni中解密,然后直接调用jnienv的defineClass来定义类。
但即便是这种方式也是可以破解的,不过是需要破解者对c也要熟悉
但只要是用java写的复杂度都不算高,如果需要应该考虑采用jni方式,在jni中解密,然后直接调用jnienv的defineClass来定义类。
但即便是这种方式也是可以破解的,不过是需要破解者对c也要熟悉
10 楼
cjnetwork
2010-12-27
xielingjiang 写道
关键点还是建立加密体系,而不是classloader体系。
我倒觉得没必要这么多级的classloader,定义一级classloader就可以了。你只要用对一种加密算法,你的cjClassloader逻辑和公钥可以公开,但是你的私钥不公开,这样不就可以达到加密的目的了吗?
当然也可以用对称加密,比如用最简单的des算法就可以了
我倒觉得没必要这么多级的classloader,定义一级classloader就可以了。你只要用对一种加密算法,你的cjClassloader逻辑和公钥可以公开,但是你的私钥不公开,这样不就可以达到加密的目的了吗?
当然也可以用对称加密,比如用最简单的des算法就可以了
1、CjClassloader中,最后会调用方法Class defineClass(String name, byte[] classData, int offset, int length)来定义一个类,然后返回加载好的类给请求加载请求者,如果不隐藏CjClassloader,则攻击者在这里直接导出解密后的数据classData,即然后了你的程序加密方法,而不论你的算法有多安全。
2、des,是的,我在实现中使用了这个算法加密,但是这种对称密钥的算法,在解密应用程序的时候,是需要使用的,那么,这个密钥(确切的说是生成密钥的密码)是一定会给别人的。例如,你需要在你托管的机房运行你的应用程序,那么那个机房的服务器上是一定需要你的密钥,当然,如果说是在启动程序之后就删除密钥,这种情况除外。
9 楼
cjnetwork
2010-12-27
RednaxelaFX 写道
换句话说只要这样就绕开了这里说的保护手段了。
classloader加密的方法,确实没有办法绕过jvm自身支持的class dump功能。暂时没有想到可以绕过这一点的加密源程序方案。
8 楼
xielingjiang
2010-12-27
关键点还是建立加密体系,而不是classloader体系。
我倒觉得没必要这么多级的classloader,定义一级classloader就可以了。你只要用对一种加密算法,你的cjClassloader逻辑和公钥可以公开,但是你的私钥不公开,这样不就可以达到加密的目的了吗?
当然也可以用对称加密,比如用最简单的des算法就可以了
我倒觉得没必要这么多级的classloader,定义一级classloader就可以了。你只要用对一种加密算法,你的cjClassloader逻辑和公钥可以公开,但是你的私钥不公开,这样不就可以达到加密的目的了吗?
当然也可以用对称加密,比如用最简单的des算法就可以了
7 楼
RednaxelaFX
2010-12-27
换句话说只要这样就绕开了这里说的保护手段了。
先开上-verbose或-XX:+TraceClassLoading跑一次,可以知道关心的类的全限定名是什么(如果加载的类使用了名字混淆那很容易看出来,没混淆的话则容易通过名字猜测类的作用);然后用上面的办法写个合适的filter就能达到解的目的了。ClassLoader系加密感觉还是不算特别强,主要还是用来防君子。
程序员没有被别人破更多的是看这个程序有没有破的价值——足够有价值的话最容易吸引到相应水平的人来做这种事情。
另外,提到混淆,主要是两类吧:名字混淆,控制流混淆。名字混淆比较常见就不详细说了。控制流混淆的思路也很简单,主要是通过把字节码的某些模式转换为计算上等价、但Java语言无法直接表达的形式。
例如,Java源码里的循环在循环体末尾的地方,操作数栈应该是空的;如果故意在此处留一个值在操作数栈上,很多反编译器就要栽了。再举一例,Java语言里的try块的起始与结束边界处,操作数栈也应该是空的,同样可以用留一个值的方式让反编译器呛着。或者修改ExceptionTable让多个异常处理器(本来应该对于Java语言的catch或finally块)指向同一个位置,这也有效果。
另外很常见的是故意加些无用的swap或者goto之类的。虽说对反编译器有效果,但如果真的是关键代码的话,人肉解读也并不困难,只是烦琐而已——能做到“烦琐”当然也已经有阻吓作用了,还是回到那个问题——这程序到底有没有足够的价值去破。
话说回来,JVM的JIT编译器自身就有跟反编译器很类似的部分,如果把反编译器呛住了的话,同时也很可能会影响JIT编译器的效果,使得程序性能降低。这些都是值得权衡的地方。
当然也有别的混淆方式。例如说,.NET上微软有提供虚拟机指令混淆,就是把原本的MSIL指令的映射关系打乱,使得一般的工具无法直接分析被混淆后的程序。这种是在虚拟机里有钩子直接支持的。
先开上-verbose或-XX:+TraceClassLoading跑一次,可以知道关心的类的全限定名是什么(如果加载的类使用了名字混淆那很容易看出来,没混淆的话则容易通过名字猜测类的作用);然后用上面的办法写个合适的filter就能达到解的目的了。ClassLoader系加密感觉还是不算特别强,主要还是用来防君子。
程序员没有被别人破更多的是看这个程序有没有破的价值——足够有价值的话最容易吸引到相应水平的人来做这种事情。
另外,提到混淆,主要是两类吧:名字混淆,控制流混淆。名字混淆比较常见就不详细说了。控制流混淆的思路也很简单,主要是通过把字节码的某些模式转换为计算上等价、但Java语言无法直接表达的形式。
例如,Java源码里的循环在循环体末尾的地方,操作数栈应该是空的;如果故意在此处留一个值在操作数栈上,很多反编译器就要栽了。再举一例,Java语言里的try块的起始与结束边界处,操作数栈也应该是空的,同样可以用留一个值的方式让反编译器呛着。或者修改ExceptionTable让多个异常处理器(本来应该对于Java语言的catch或finally块)指向同一个位置,这也有效果。
另外很常见的是故意加些无用的swap或者goto之类的。虽说对反编译器有效果,但如果真的是关键代码的话,人肉解读也并不困难,只是烦琐而已——能做到“烦琐”当然也已经有阻吓作用了,还是回到那个问题——这程序到底有没有足够的价值去破。
话说回来,JVM的JIT编译器自身就有跟反编译器很类似的部分,如果把反编译器呛住了的话,同时也很可能会影响JIT编译器的效果,使得程序性能降低。这些都是值得权衡的地方。
当然也有别的混淆方式。例如说,.NET上微软有提供虚拟机指令混淆,就是把原本的MSIL指令的映射关系打乱,使得一般的工具无法直接分析被混淆后的程序。这种是在虚拟机里有钩子直接支持的。
6 楼
冇心人
2010-12-26
自定义的classloader在深入java虚拟机中都有相关的讲解.
5 楼
littleJava
2010-12-26
我记得代码混淆常用的技术就是将类名改掉……
4 楼
onlylau
2010-12-26
kimmking 写道
代码混淆就不错。
我觉得重点是学到关于ClassLoader这个知识点
发表评论
-
hadoop未修复bug6287的解决办法(ttprivte to 0700的bug、setPermission failed)
2012-04-06 17:31 3788hadoop-0.20.2以上版本,若在windows下使用c ... -
hql语句中支持的本地时间函数
2011-11-01 16:17 11714hql语句中支持的本地时间函数 1、UNIX_TIMES ... -
安装Collabnet svn
2011-10-31 11:58 1915安装Collabnet svn 1、下载CollabNetSu ... -
mysql存储过程
2011-10-19 08:44 1074DELIMITER $$ DROP PROCEDURE ... -
com.mysql.jdbc.exceptions.jdbc4.CommunicationsException
2011-06-08 10:35 1788com.mysql.jdbc.exceptions.jdbc4 ... -
PreparedStatement、Statement、ResultSet等一定要手动关闭
2011-05-22 11:23 19554在使用java开发后台应用程序的时候,如果需要使用数 ... -
junit4使用
2010-12-21 17:55 1291junit4 今天为数据的映射写了测试,使用了一下junit ... -
第一次使用java的DES加密
2010-12-16 17:14 3813用了如下的方法使用des加密和解密,但解密后的结果和原来的结果 ...
相关推荐
本文将深入探讨`ClassLoader`的工作原理、加密解密应用程序以及如何防止类被反编译。 首先,让我们理解`ClassLoader`的基本概念。`ClassLoader`是Java中的一个接口,位于`java.lang`包下,它是Java运行时环境的一...
总的来说,"Jar包保护加密解决方案"是一个涵盖开发、部署和运行全过程的技术实践,涉及到Java编程、加密算法、混淆技术、自定义Class Loader以及JVM的深入理解和应用。通过综合运用这些技术,可以有效提高jar包的...
下面将通过一个简单的示例来展示如何使用自定义ClassLoader来实现Java源代码的加密与解密。 1. **创建自定义ClassLoader**: - 自定义ClassLoader需要重写`findClass`方法来处理加密后的类文件。 - 在`findClass`...
独立的应用程序,运行java时,带上参数-agentlib:<动态库文件所在路径>\classloader Tomcat、Jboss等Java application server修改启动脚本, 把执行java的命令行后面加上参数-agentlib:<动态链接库文件所在路径>\...
本工具是对java class文件进行加密保护防止反编译的工具!本工具全面支持linux/unix/windows操作系统。 继推出v1.0版本后,获得了用户大量的支持与的反馈,我们再次推出本v2.0版,对加密算法进行了更大的改进,安全...
本工具是对java class文件进行加密保护防止反编译的工具!本工具全面支持linux/unix/windows操作系统。 继推出v1.0版本后,获得了用户大量的支持与的反馈,我们再次推出本v2.0版,对加密算法进行了更大的改进,安全...
4. **部署和运行**:将加密的类文件和定制的ClassLoader打包在一起,当用户下载并运行应用程序时,ClassLoader会在内存中解密类文件,保证了源代码的隐私。 5. **解密**:在运行时,使用相同的密钥和DES算法对加载...
独立的应用程序,运行java时,带上参数-agentlib:<动态库文件所在路径>\classloader Tomcat、Jboss等Java application server修改启动脚本, 把执行java的命令行后面加上参数-agentlib:<动态链接库文件所在路径>\...
总结起来,Java 类加载器加密是一种增强程序安全性的技术,通过自定义类加载器和解密逻辑,可以在加载类之前对其进行加密,提高代码的保护性。同时,结合反射机制,即使类是加密状态,也能正常执行程序。这种技术常...
《一种无法被Dump的jar包加密保护解决方案》 在当今的软件开发领域,Java因其跨平台的特性及丰富的库资源而广受欢迎。然而,这也带来了代码安全的问题。Java的字节码格式相对简单,容易被反编译工具如JD-GUI解析,...
java编译后的class文件是一种中间字节字文件,很容易被反编译工具反编译,而传统的java源代码保护方法基本都是采用混淆的方式,但这样会带来很多麻烦,而且也不能真正保护class文件,本工具是对class文件进行加密,...
为了解决这个问题,本文探讨了如何在不改变程序原有结构的情况下,利用加密技术来保护Java源代码。 首先,我们探讨为什么需要加密源代码。与C或C++不同,Java的源码可以在不发布的情况下保持安全,但Java的类文件是...
`jenc2.1.gif`可能是关于一个特定加密库或工具的说明图像,该工具可能用于加密和解密Java类文件。通常,这些工具会提供一个图形用户界面(GUI),用户可以通过界面来操作和配置加密参数。 `logo.jpg`可能是该加密...
2. **Class文件加密**:对敏感的Class文件进行加密,然后在运行时通过自定义ClassLoader解密并加载。虽然这增加了反编译的难度,但自定义ClassLoader本身可能会成为攻击目标,一旦解密密钥或算法被破解,加密的Class...
3. **解密代码**:使用特定的ClassLoader,可以在运行时使用加密的类文件并透明解密。 4. **动态生成类**:根据用户需求,生成或定制类的实例。 5. **隔离执行环境**:多个应用程序或Applet可以在同一个JVM中运行,...
JVM代理是一种在JVM启动时或者运行时插入额外功能的方式,它允许开发者在不修改源代码的情况下扩展或修改Java应用程序的行为。通常,JVM代理被用于性能监控、日志记录、动态字节码注入等场景。Java提供了一种标准...
文章提出的技术方案主要依赖于Java软件序列号授权方式,结合类文件加密和动态解密,以及对关键类和代码的混淆处理。 在Java软件序列号授权设计方面,该方案以计算机的唯一标识MAC地址和用户提交信息为基础,通过RSA...
例如,使用自定义的ClassLoader来实现加密和解密过程。 3. **虚拟机层加密**:在Dalvik或ART虚拟机级别实现加密,比如修改Dalvik VM的指令集,使其在执行前需要解密。这种方法较为复杂,但能有效防止针对DEX文件的...