Java类加载器( 死磕7)
【正文】Java类加载器( CLassLoader )死磕7:
基于加密的自定义网络加载器
本小节目录
7.1. 加密传输Server端的源码
7.2. 加密传输Client端的源码
7.3. 使用亦或实现简单加密和解密算法
7. 网络加密SafeClassLoader的源码
7.5. SafeSocketLoader的使用
众所周知,java代码很容易被反编译,如果你需要把自己的代码进行加密,可以先将编译后的代码用某种加密算法加密,然后结合自己的网络类加载器,进行加密后的安全传输。
客户端接收到加密后的字节码后,负责将这段加密后的代码还原。
1.1.1. 加密传输Server端的源码
和文件传输Server端的源码基本一致,只有一行代码的差别。
简单粗暴,直接上码。
package com.crazymakercircle.classLoader;
import com.crazymakercircle.config.SystemConfig;
import com.crazymakercircle.util.DeEnCode;
import com.crazymakercircle.util.IOUtil;
import com.crazymakercircle.util.Logger;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
/**
* 文件传输Server端<br>
*/
public class SafeSocketServer {
ServerSocket serverSocket = null;
static String filePath = null;
public SafeSocketServer() throws Exception {
serverSocket = new ServerSocket(SystemConfig.SOCKET_SERVER_PORT);
this.filePath = SystemConfig.CLASS_SERVER_PATH;
startServer();
}
/**
* 启动服务端
* 使用线程处理每个客户端传输的文件
*
* @throws Exception
*/
public void startServer() {
while (true) {
// server尝试接收其他Socket的连接请求,server的accept方法是阻塞式的
Logger.info("server listen at:" + SystemConfig.SOCKET_SERVER_PORT);
Socket socket = null;
try {
socket = serverSocket.accept();
// 每接收到一个Socket就建立一个新的线程来处理它
new Thread(new Task(socket)).start();
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* 处理客户端传输过来的文件线程类
*/
class Task implements Runnable {
private Socket socket;
private DataInputStream dis;
private FileOutputStream fos;
public Task(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try {
dis = new DataInputStream(socket.getInputStream());
// 文件名和长度
String fileName = dis.readUTF();
DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
sendFile(fileName, dos);
} catch (Exception e) {
e.printStackTrace();
} finally {
IOUtil.closeQuietly(fos);
IOUtil.closeQuietly(dis);
IOUtil.closeQuietly(socket);
}
}
private void sendFile(String fileName, DataOutputStream dos) throws Exception {
fileName=classNameToPath(fileName);
fileName = SafeSocketServer.filePath + File.separator + fileName;
File file = new File(fileName);
if (!file.exists()) {
throw new Exception("file not found! :"+fileName);
}
long fileLen = file.length();
dos.writeLong(fileLen);
dos.flush();
byte one= (byte) 0xff;
FileInputStream fis = new FileInputStream(file);
// 开始传输文件
Logger.info("======== 开始传输文件 ========");
byte[] bytes = new byte[1024];
int length = 0;
long progress = 0;
while ((length = fis.read(bytes, 0, bytes.length)) != -1) {
DeEnCode.encode(bytes,length);
dos.write(bytes, 0, length);
dos.flush();
progress += length;
Logger.info("| " + (100 * progress / fileLen) + "% |");
}
Logger.info("======== 文件传输成功 ========");
}
}
private String classNameToPath(String className) {
return className.replace('.', '/') + ".class";
}
public static void main(String[] args) {
try {
SafeSocketServer socketServer = new SafeSocketServer();
socketServer.startServer();
} catch (Exception e) {
e.printStackTrace();
}
}
}
和文件传输Server端的源码基本一致,只有一行代码的差别。这个类仅仅增加的一行是:
DeEnCode.encode(bytes,length);
其目的是,在发送字节码之前,使用定义的加密函数,进行字节码加密。
源码比较长,建议运行main函数,先将服务端的源码跑起来,然后再阅读代码,这样阅读起来更加容易懂。
另外,在使用基于网络的类加载器之前,一定要确保服务端的代码先执行。否则客户端会报错。
案例路径:com.crazymakercircle.classLoader.SafeSocketServer
案例提示:无编程不创客、无案例不学习。一定要跑案例哦
运行的结果是:
<clinit> |> 开始加载配置文件到SystemConfig
loadFromFile |> load properties: /system.properties
startServer |> server listen at:18899
看到以上结果,表示服务端开始启动。监听了18899端口,等待客户端的连接。
1.1.2. 加密传输Client端的源码
客户端的工作:
建立和服务器的TCP连接后,首先做的第一步工作,是发送文件名称给服务器端。
然后阻塞,直到服务器的数据过来。客户端开始接受服务器传输过来的数据。接受数据的工作由函数receivefile()完成。
整个的数据的读取工作分为两步,先读取文件的大小,然后读取传输过来的文件内容。
在传输文件内容的字节码时,需要对字节码进行解密。
简单粗暴,直接上源码。
/**
* 文件传输Client端
*/
public class SafeSocketClient {
private Socket client;
private FileInputStream fis;
private DataOutputStream dos;
/**
* 构造函数<br/>
* 与服务器建立连接
*
* @throws Exception
*/
public SafeSocketClient() throws IOException {
this.client = new Socket(
SystemConfig.SOCKET_SERVER_IP,
SystemConfig.SOCKET_SERVER_PORT
);
Logger.info("Cliect[port:" + client.getLocalPort() + "] 成功连接服务端");
}
/**
*向服务端去取得文件
*
* @throws Exception
*/
public byte[] getFile(String fileName) throws Exception {
byte[] result = null;
try {
dos = new DataOutputStream(client.getOutputStream());
// 文件名和长度
dos.writeUTF(fileName);
dos.flush();
DataInputStream dis = new DataInputStream(client.getInputStream());
result = receivefile(dis);
Logger.info("文件接收成功,File Name:" + fileName);
} catch (Exception e) {
e.printStackTrace();
} finally {
IOUtil.closeQuietly(fis);
IOUtil.closeQuietly(dos);
IOUtil.closeQuietly(client);
}
return result;
}
public byte[] receivefile(DataInputStream dis) throws Exception {
int fileLength = (int) dis.readLong();
ByteArrayOutputStream bos = new ByteArrayOutputStream(fileLength);
long startTime = System.currentTimeMillis();
Logger.info("block IO 传输开始:");
// 开始接收文件
byte[] bytes = new byte[1024];
int length = 0;
while ((length = dis.read(bytes, 0, bytes.length)) != -1) {
DeEnCode.decode(bytes,length);
bos.write(bytes, 0, length);
bos.flush();
}
Logger.info(" Size:" + IOUtil.getFormatFileSize(fileLength));
long endTime = System.currentTimeMillis();
Logger.info("block IO 传输毫秒数:" + (endTime - startTime));
bos.flush();
byte[] result = bos.toByteArray();
IOUtil.closeQuietly(bos);
return result;
}
}
与前面的基础案例SafeSocketClient 相比,只有一行代码的差别。这个类仅仅增加的一行是:
DeEnCode.decode(bytes,length);
其目的是,在接受字节码之后,使用定义的解密函数,进行字节码解密。
案例路径:com.crazymakercircle.classLoader.SafeSocketClient
此案例类没法独立运行,因为没有运行的入口。只能作为基础类,供其他类调用。
下面介绍一下加密和解密的算法。
1.1.3. 使用亦或实现简单加密和解密算法
加密和解密算法有很多,这里为为了演示方便,使用亦或的方式,实现简单加密和解密算法。
简单粗暴,直接上代码:
public class DeEnCode {
private static final String key0 = "FECOI()*&<MNCXZPKL";
private static final Charset charset = Charset.forName("UTF-8");
private static byte[] keyBytes = key0.getBytes(charset);
public static void encode(byte[] b, int length) {
for (int i = 0, size =length; i < size; i++) {
for (byte kb : keyBytes) {
b[i] = (byte) (b[i] ^ kb);
}
}
}
public static void decode(byte[] dee, int length) {
for (int i = 0, size =length; i < size; i++) {
for (byte kb : keyBytes) {
dee[i] = (byte) (dee[i] ^ kb);
}
}
}
public static void main(String[] args) {
String s = "you are right ok 测试";
byte[] sb = s.getBytes();
encode(sb,sb.length);
decode(sb, sb.length);
Logger.info(new String(sb));
}
}
案例路径:com.crazymakercircle.util.DeEnCode
无编程不创客、无案例不学习。一定要跑案例哦
运行上面的main方法,测试一下加密和解密。结果是:
main |> you are right ok 测试
从结果可以看到,解密后的数据和加密前的数据是一致的。说明这组算法是没有问题的。
1.1.4. 网络加密SafeClassLoader的源码
与前面的网络类加载器SocketClassLoader,只有一行之差。
源码如下:
public class SafeClassLoader extends ClassLoader {
public SafeClassLoader() {
}
protected Class<?> findClass(String name)
throws ClassNotFoundException {
Logger.info("findClass name = " + name);
byte[] classData = null;
try {
SafeSocketClient client = new SafeSocketClient();
classData = client.getFile(name);
} catch (Exception e) {
e.printStackTrace();
throw new ClassNotFoundException();
}
if (classData == null) {
throw new ClassNotFoundException();
} else {
return defineClass(name, classData, 0, classData.length);
}
}
}
1.1.5. SafeSocketLoader的使用
简单粗暴,先上代码:
package com.crazymakercircle.classLoaderDemo.net;
import com.crazymakercircle.classLoader.SafeClassLoader;
import com.crazymakercircle.config.SystemConfig;
import com.crazymakercircle.petStore.pet.IPet;
import com.crazymakercircle.util.ClassLoaderUtil;
import com.crazymakercircle.util.Logger;
public class SafeNetDemo {
public static void testLoader() {
try {
SafeClassLoader classLoader = new SafeClassLoader();
String className = SystemConfig.PET_DOG_CLASS;
Class dogClass = classLoader.loadClass(className);
Logger.info("显示dogClass的ClassLoader =>");
ClassLoaderUtil.showLoader4Class(dogClass);
IPet pet = (IPet) dogClass.newInstance();
pet.sayHello();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
testLoader();
}
}
案例路径:com.crazymakercircle.classLoaderDemo.base.SafeSocketDemo
案例提示:无编程不创客、无案例不学习。一定要跑案例哦
运行的结果,与前面的SocketLoaderDemo结果是相同的,这里不在赘述。
源码:
代码工程: classLoaderDemo.zip
下载地址:在疯狂创客圈QQ群文件共享。
疯狂创客圈:如果说Java是一个武林,这里的聚集一群武痴, 交流编程体验心得
QQ群链接:疯狂创客圈QQ群
无编程不创客,无案例不学习。 一定记得去跑一跑案例哦
类加载器系列全目录
8. 高级案例1:使用ASM技术,结合类加载器,解密AOP原理
Java类加载器( 死磕7)的更多相关文章
- Java类加载器(死磕 1-2)
Java类加载器( CLassLoader ) 死磕 1.2: 导入 & 类加载器分类 本小节目录 1.导入 1.1. 从class文件的载入开始 1.2. 什么是类加载器 2. JA ...
- Java类加载器(死磕5)
Java类加载器( CLassLoader ) 死磕5: 自定义一个文件系统classLoader 本小节目录 5.1. 自定义类加载器的基本流程 5.2. 入门案例:自定义文件系统类加载器 5 ...
- Java类加载器( 死磕9)
[正文]Java类加载器( CLassLoader ) 死磕9: 上下文加载器原理和案例 本小节目录 9.1. 父加载器不能访问子加载器的类 9.2. 一个宠物工厂接口 9.3. 一个宠物工厂管理 ...
- Java类加载器( 死磕8)
[正文]Java类加载器( CLassLoader ) 死磕 8: 使用ASM,和类加载器实现AOP 本小节目录 8.1. ASM字节码操作框架简介 8.2. ASM和访问者模式 8.3. 用于增 ...
- Java类加载器( 死磕 6)
[正文]Java类加载器( CLassLoader )死磕 6: 自定义网络类加载器 本小节目录 6.1. 自定义网络类加载器的类设计 6.2. 文件传输Server端的源码 6.3. 文件传输C ...
- Java类加载器( 死磕 4)
[正文]Java类加载器( CLassLoader ) 死磕 之4: 神秘的双亲委托机制 本小节目录 4.1. 每个类加载器都有一个parent父加载器 4.2. 类加载器之间的层次关系 4.3. ...
- Java类加载器(死磕3)
[正文]Java类加载器( CLassLoader ) 死磕3: 揭秘 ClassLoader抽象基类 本小节目录 3.1. 类的加载分类:隐式加载和显示加载 3.2. 加载一个类的五步工作 3. ...
- java笔记--理解java类加载器以及ClassLoader类
类加载器概述: java类的加载是由虚拟机来完成的,虚拟机把描述类的Class文件加载到内存,并对数据进行校验,解析和初始化,最终形成能被java虚拟机直接使用的java类型,这就是虚拟机的类加载机制 ...
- java类加载器深入研究
看了下面几篇关于类的加载器的文章,豁然开朗.猛击下面的地址开始看吧. Java类加载原理解析 深入探讨 Java 类加载器 分析BootstrapClassLoader/ExtClassLo ...
随机推荐
- 标准C程序设计七---71
Linux应用 编程深入 语言编程 标准C程序设计七---经典C11程序设计 以下内容为阅读: <标准C程序设计>(第7版) 作者 ...
- Linux 之 网络相关设置
网络相关设置 参考教程:[千峰教育] 命令: ping: 作用:通常用于检测网络设备的连通性. 格式:ping IP/域名 选项:-c,指定方式测试数据包的次数 实例:ping www.baidu.c ...
- AC日记——城市 洛谷 P1401
题目描述 N(2<=n<=200)个城市,M(1<=m<=40000)条无向边,你要找T(1<=T<=200)条从城市1到城市N的路,使得最长的边的长度最小,边不能 ...
- 天梯赛 - L2-002 链表去重
GG思密达,第二个测试点的三分怎么也拿不上,我还是比较熟悉指针,用指针来写~,写完去上概率论 题目链接:https://www.patest.cn/contests/gplt/L2-002 #incl ...
- SPOJ 1825 Free tour II (树的点分治)
题目链接 Free tour II 题意:有$N$个顶点的树,节点间有权值, 节点分为黑点和白点. 找一条最长路径使得 路径上黑点数量不超过K个 这是树的点分治比较基本的题,涉及树上启发式合并……仰望 ...
- django使用logging记录日志
django使用logging记录日志,我没有用这方式去记录日志,主要还是项目小的原因吧, 有机会遇见大项目的话可以回头研究. 配置setting.py配置文件 import logging impo ...
- How to Use Dtrace Tracing Ruby Executing
http://googya.github.io/blog/categories/dtrace/ 最近看了点关于Dtrace的东西,它是个通用型的工具,但我主要集中于分析ruby程序的执行上面.关于操作 ...
- CSRF攻击 & XSS攻击
之前有几篇文章写了 SQL注入类问题: http://www.cnblogs.com/charlesblc/p/5987951.html (介绍) http://www.cnblogs.com/cha ...
- C#使用CurrentUICulture切换语言
1. 创建2个窗口 2. 窗口1属性Localizable设置为True,Language选择英语(美国) 然后把窗口1中控件的Text由中文编辑成英文,Form2一样设置. 此时,Form1 ...
- PS 如何使用钢笔工具
1.钢笔工具属于矢量绘图工具,其优点是可以勾画平滑的曲线,在缩放或者变形之后仍能保持平滑效果. 2.钢笔工具画出来的矢量图形称为路径,路径是矢量的路径允许是不封闭的开放状,如果把起点与终点重合绘制就可 ...