Java安全之Javassist动态编程
Java安全之Javassist动态编程
0x00 前言
在调试CC2链前先来填补知识盲区,先来了解一下Javassist具体的作用。在CC2链会用到Javassist以及PriorityQueue来构造利用链
0x01 Javassist 介绍
Java 字节码以二进制的形式存储在 class 文件中,每一个 class 文件包含一个 Java 类或接口。Javaassist 就是一个用来处理 Java 字节码的类库。
Javassist是一个开源的分析、编辑和创建Java字节码的类库。
0x02 Javassist 使用
这里主要讲一下主要的几个类:
ClassPool
ClassPool:一个基于哈希表(Hashtable)实现的CtClass对象容器,其中键名是类名称,值是表示该类的CtClass对象(Hashtable和Hashmap类似都是实现map接口,hashmap可以接收null的值,但是Hashtable不行)。
常用方法:
static ClassPool getDefault()
返回默认的类池。
ClassPath insertClassPath(java.lang.String pathname)
在搜索路径的开头插入目录或jar(或zip)文件。
ClassPath insertClassPath(ClassPath cp)
ClassPath在搜索路径的开头插入一个对象。
java.lang.ClassLoader getClassLoader()
获取类加载器toClass(),getAnnotations()在 CtClass等
CtClass get(java.lang.String classname)
从源中读取类文件,并返回对CtClass 表示该类文件的对象的引用。
ClassPath appendClassPath(ClassPath cp)
将ClassPath对象附加到搜索路径的末尾。
CtClass makeClass(java.lang.String classname)
创建一个新的public类
CtClass
CtClass表示类,一个CtClass(编译时类)对象可以处理一个class文件,这些CtClass对象可以从ClassPoold的一些方法获得。
常用方法:
void setSuperclass(CtClass clazz)
更改超类,除非此对象表示接口。
java.lang.Class<?> toClass(java.lang.invoke.MethodHandles.Lookup lookup)
将此类转换为java.lang.Class对象。
byte[] toBytecode()
将该类转换为类文件。
void writeFile()
将由此CtClass 对象表示的类文件写入当前目录。
void writeFile(java.lang.String directoryName)
将由此CtClass 对象表示的类文件写入本地磁盘。
CtConstructor makeClassInitializer()
制作一个空的类初始化程序(静态构造函数)。
CtMethod
CtMethod:表示类中的方法。
CtConstructor
CtConstructor的实例表示一个构造函数。它可能代表一个静态构造函数(类初始化器)。
常用方法
void setBody(java.lang.String src)
设置构造函数主体。
void setBody(CtConstructor src, ClassMap map)
从另一个构造函数复制一个构造函数主体。
CtMethod toMethod(java.lang.String name, CtClass declaring)
复制此构造函数并将其转换为方法。
ClassClassPath
该类作用是用于通过 getResourceAsStream() 在 java.lang.Class 中获取类文件的搜索路径。
构造方法:
ClassClassPath(java.lang.Class<?> c)
创建一个搜索路径。
常见方法:
java.net.URL find (java.lang.String classname)
获取指定类文件的URL。
java.io.InputStream openClassfile(java.lang.String classname)
通过获取类文getResourceAsStream()。
代码实例:
ClassPool pool = ClassPool.getDefault();
在默认系统搜索路径获取ClassPool对象。
如果需要修改类搜索的路径需要使用insertClassPath方法进行修改。
pool.insertClassPath(new ClassClassPath(this.getClass()));
将本类所在的路径插入到搜索路径中
toBytecode
package com.demo;
import javassist.*;
import java.io.IOException;
import java.util.Arrays;
public class testssit {
public static void main(String[] args) throws NotFoundException, CannotCompileException, IOException {
ClassPool pool = ClassPool.getDefault();
pool.insertClassPath(new ClassClassPath(demo.class.getClass()));
CtClass ctClass = pool.get("com.demo.test");
ctClass.setSuperclass(pool.get("com.demo.test"));
// System.out.println(ctClass);
byte[] bytes = ctClass.toBytecode();
String s = Arrays.toString(bytes);
System.out.println(s);
}
}

toClass
public class Hello {
public void say() {
System.out.println("Hello");
}
}
public class Test {
public static void main(String[] args) throws Exception {
ClassPool cp = ClassPool.getDefault();//在默认系统搜索路径获取ClassPool对象。
CtClass cc = cp.get("Hello"); //获取hello类的
CtMethod m = cc.getDeclaredMethod("say"); //获取hello类的say方法
m.insertBefore("{ System.out.println(\"Hello.say():\"); }");//在正文的开头插入字节码
Class c = cc.toClass();//将此类转换为java.lang.Class对象
Hello h = (Hello)c.newInstance(); //反射创建对象并进行强转
h.say();调用方法say
}
}
0x02 一些小想法
按照我的理解来说就是可以去将类和字节码进行互相转换。那么按照这个思路来延申的话,我们可以做到什么呢?我首先想到的可能就是webshell的一些免杀,例如说Jsp的最常见的一些webshell,都是采用Runtime,ProcessBuilder这两个类去进行构造,执行命令。按照WAF的惯性这些设备肯定是把这些常见的执行命令函数给拉入黑名单里面去。那么如果说可以转换成字节码的话呢?字节码肯定是不会被杀的。如果说这时候将Runtime这个类转换成字节码,内嵌在Jsp中,后面再使用Javassist来将字节码还原成类的话,如果转换的几个方法没被杀的话,是可以实现过WAF的。当然这些也只是我的一些臆想,因为Javassist并不是JDK中自带的,实现的话后面可以再研究一下。但是类加载器肯定是可以去加载字节码,然后实现执行命令的。这里只是抛砖引玉,更多的就不细说了。如果有更好的想法也可以提出来一起去交流。
0x03 想法实现
这里可以来思考一个问题,该怎么样才能动态传入参数去执行呢?那能想到的肯定是反射。如果我们用上面的思路,把全部代码都转换成字节码的话,其实就没有多大意义了。因为全是固定死的东西,他也只会执行并且得到同一个执行结果。
我在这里能想到的就是将部分在代码里面固定死的代码给转换成字节码,然后再使用反射的方式去调用。
public class test {
public static void main(String[] args) {
String string ="java.lang.Runtime";
byte[] bytes1 = string.getBytes();
System.out.println(Arrays.toString(bytes1));
}
}
获取结果:
[106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 80, 114, 111, 99, 101, 115, 115, 73, 109, 112, 108]
现在已经是把结果给获取到了,但是我们需要知道字节码怎么样还原为String类型。
在后面翻阅资料的时候,发现String的构造方法就直接能执行,来看看他的官方文档。

使用bytes去构造一个新的String
代码:
public class test {
public static void main(String[] args) {
byte[] bytes = new byte[]{106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 82, 117, 110, 116, 105, 109, 101};
String s = new String(bytes);
System.out.println(s);
}
}

public class test {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, IOException {
byte[] b1 = new byte[]{106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 82, 117, 110, 116, 105, 109, 101};
String run = new String(b1);
String command = "ipconfig";
Class aClass = Class.forName(run);
Constructor declaredConstructor = aClass.getDeclaredConstructor();
declaredConstructor.setAccessible(true);
Object o = declaredConstructor.newInstance();
Method exec = aClass.getMethod("exec", String.class);
Process process = (Process) exec.invoke(o,command);
InputStream inputStream = process.getInputStream(); //获取输出的数据
String ipconfig = IOUtils.toString(inputStream,"gbk"); //字节输出流转换为字符
System.out.println(ipconfig);
}
}

命令执行成功。
那么这就是一段完整的代码,但是还有些地方处理得不是很好,比如:
Method exec = aClass.getMethod("exec", String.class);
这里是反射获取exec方法,这里的exec是固定的。exec这个对于一些设备来说也是严杀的。
那么在这里就可以来处理一下,也转换成字节码。
转换后的字节码:
[101, 120, 101, 99]
改进一下代码:
public class test {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, IOException {
String command = "ipconfig";
byte[] b1 = new byte[]{106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 82, 117, 110, 116, 105, 109, 101};
String run = new String(b1);
byte[] b2 = new byte[]{101, 120, 101, 99};
String cm = new String(b2);
Class aClass = Class.forName(run);
Constructor declaredConstructor = aClass.getDeclaredConstructor();
declaredConstructor.setAccessible(true);
Object o = declaredConstructor.newInstance();
Method exec = aClass.getMethod(cm, String.class);
Process process = (Process) exec.invoke(o,command);
InputStream inputStream = process.getInputStream(); //获取输出的数据
String ipconfig = IOUtils.toString(inputStream,"gbk"); //字节输出流转换为字符
System.out.println(ipconfig);
}
}
实际中运用就别用啥ipconfig和command这些来命名了,这些都是一些敏感词。这里只是为了方便理解。
在真实情况下应该是request.getInputStream()来获取输入的命令的。那么这里也还需要注意传输的时候进行加密,不然流量肯定也是过不了设备的。
0x04 结尾
其实后面这些内容是跑偏题了,因为是后面突然才想到的这么一个东西。所以将他给记录下来。
Java安全之Javassist动态编程的更多相关文章
- 使用javassist进行动态编程
今天在研究dubbo时,发现一个新的知识点,可以使用javassist包进行动态编程,hibernate也使用该包进行编程.晚上百度了很多资料,将它的特性以代码的形式展现出来. package com ...
- Java动态编程初探——Javassist
最近需要通过配置生成代码,减少重复编码和维护成本.用到了一些动态的特性,和大家分享下心得. 我们常用到的动态特性主要是反射,在运行时查找对象属性.方法,修改作用域,通过方法名称调用方法等.在线的应用不 ...
- 【java编程-Javassist】秒懂Java动态编程(Javassist研究)
作者:ShuSheng007 来源:CSDN 原文:https://blog.csdn.net/ShuSheng0007/article/details/81269295 版权声明:本文为博主原创文章 ...
- Java动态编程---动态代理
java中动态编程用到的技术有:反射(动态代理),javassist和ASM,这几种动态编程方法相比较,在性能上Javassist高于反射,但低于ASM,因为Javassist增加了一层抽象.在实现成 ...
- java 动态代理 和动态编程
概述 代理分两种技术,一种是jdk代理(机制就是反射,只对接口操作),一种就是字节码操作技术.前者不能算技术,后者算是新的技术.未来将有大的动作或者较为广泛的应用和变革,它可以实现代码自我的编码(人工 ...
- C# 4动态编程新特性与DLR剖析
=================================================== 注:很久没有发文了,贴一篇新文吧.从Word直接贴过来的,没仔细排版,诸位海涵.有关DLR和C# ...
- Java Proxy和CGLIB动态代理原理
动态代理在Java中有着广泛的应用,比如Spring AOP,Hibernate数据查询.测试框架的后端mock.RPC,Java注解对象获取等.静态代理的代理关系在编译时就确定了,而动态代理的代理关 ...
- java学习笔记15--多线程编程基础2
本文地址:http://www.cnblogs.com/archimedes/p/java-study-note15.html,转载请注明源地址. 线程的生命周期 1.线程的生命周期 线程从产生到消亡 ...
- java:java静态代理与动态代理简单分析
java静态代理与动态代理简单分析 转载自:http://www.cnblogs.com/V1haoge/p/5860749.html 1.动态代理(Dynamic Proxy) 代理分为静态代理和动 ...
随机推荐
- nginx的gzip压缩
随着nginx的发展,越来越多的网站使用nginx,因此nginx的优化变得越来越重要,今天我们来看看nginx的gzip压缩到底是怎么压缩的呢? gzip(GNU-ZIP)是一种压缩技术.经过gzi ...
- if __name__ == ‘__main__‘
if __name__ == '__main__': def_test() 作为程序的入口,当函数被调用时会从此处开始运行 如被导入的模块内没写 if __name__ == '__main__',则 ...
- 11.QT-ffmpeg+QAudioOutput实现音频播放器
1.前言 由于QAudioOutput支持的输入数据必须是原始数据,所以播放mp3,WAV,AAC等格式文件,需要解封装后才能支持播放. 而在QT中,提供了QMediaPlayer ...
- Kubernetes-5:搭建企业级私有仓库Harbor
搭建企业级私有仓库Harbor 安装需求 python版本 >= 2.7 Docker引擎版本 >= 1.10 docker-compose版本 >= 1.6.0 安装环境 一.Py ...
- 实践案例丨基于 Raft 协议的分布式数据库系统应用
摘要:简单介绍Raft协议的原理.以及存储节点(Pinetree)如何应用 Raft实现复制的一些工程实践经验. 1.引言 在华为分布式数据库的工程实践过程中,我们实现了一个计算存储分离. 底层存储基 ...
- [LeetCode] 46. 全排列(回溯)
###题目 给定一个没有重复数字的序列,返回其所有可能的全排列. 示例: 输入: [1,2,3] 输出: [ [1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], ...
- [程序员代码面试指南]递归和动态规划-最小编辑代价(DP)
问题描述 输入 原字符串StrOrg,目标字符串StrTarget,插入.删除.替换的编辑代价ic,dc,rc.输出将原字符串编辑成目标字符串的最小代价. 解题思路 状态表示 dp[i][j]表示把s ...
- springcloud一些博文记录
自己搭建完整demo后看的一个系列博文https://my.oschina.net/u/3375733/following 现在看着这个系列的后期文章估计出差前不会看了 一个博主的系列的git地址和博 ...
- 面向对象技术-原文转载自jingwhale
面向对象技术 --原文转载自jingwhale,文章底部附原文链接 对象是面向对象的程序设计的核心,它由描述状态的属性(变量)和用来实现对象行为的方法(函数)组成,完成了从数据模型到处理模型的结合与统 ...
- defer 延迟调用
1. 延迟调用 defer 的用法很简单,只要在后面跟一个函数的调用,就能实现将这个 xxx 函数的调用延迟到当前函数执行完后再执行. defer xxx() 这是一个很简单的例子,可以很快帮助 ...