JVM-12-远程执行代码
2018年10月8日大约 3 分钟
整体思路
如果远程系统需要执行一段代码,但是我们现在又没有入口。
可以通过这种方式。
实际上对于日志信息,可能某个调用信息没有打印,也可以通过这个方式。
代码实现
核心代码
- ByteUtils.java
package org.jvm;
/**
* Bytes数组处理工具
*/
public class ByteUtils {
public static int bytes2Int(byte[] b, int start, int len) {
int sum = 0;
int end = start + len;
for(int i = start; i > 8 * i) & 0xff);
}
return b;
}
public static String bytes2String(byte[] b, int start, int len) {
return new String(b, start, len);
}
public static byte[] string2Bytes(String str) {
return str.getBytes();
}
public static byte[] bytesReplace(byte[] originalBytes, int offset, int len,
byte[] replaceBytes) {
byte[] newBytes = new byte[originalBytes.length + (replaceBytes.length - len)];
System.arraycopy(originalBytes, 0, newBytes, 0, offset);
System.arraycopy(replaceBytes, 0, newBytes, offset, replaceBytes.length);
System.arraycopy(originalBytes, offset + len, newBytes, offset + replaceBytes.length, originalBytes.length - offset - len);
return newBytes;
}
}
- ClassModifier.java
package org.jvm;
/**
* 修改Class文件,暂时只提供修改常量池常量的功能
*/
public class ClassModifier {
private static final int CONSTANT_POOL_COUNT_INDEX = 8;
private static final int CONSTANT_Utf8_info = 1;
private static final int[] CONSTANT_ITEM_LENGTH = {-1,-1,-1,5,5,9,9,3,3,5,5,5,5};
private static final int u1 = 1;
private static final int u2 = 2;
private byte[] classByte;
public ClassModifier(byte[] classByte){
this.classByte = classByte;
}
public byte[] modifyUTF8Constant(String oldStr, String newStr){
int cpc = getConstantPoolCount();
int offset = CONSTANT_POOL_COUNT_INDEX + u2;
for(int i = 0; i loadByte(byte[] classByte){
return defineClass(null,classByte,0,classByte.length);
}
}
- JavaClassExecuter.java
public class JavaClassExecuter {
@SuppressWarnings({ "unused", "rawtypes", "unchecked" })
public static String execute(byte[] classByte){
HackSystem.clearBuffer();
ClassModifier cm=new ClassModifier(classByte);
byte[] modiBytes=cm.modifyUTF8Constant("java/lang/System", "test/HackSystem");
HotSwapClassLoader loader=new HotSwapClassLoader();
Class clazz=loader.loadByte(modiBytes);
try{
Method method=clazz.getMethod("main", new Class[]{String[].class});
method.invoke(null, new String[]{null});
}catch(Throwable e){
e.printStackTrace(HackSystem.out);
}
return HackSystem.getBufferString();
}
}
测试类
package org.jvm;
/**
* 测试类,在此类中打印想要在页面看到的内容,System.out输出的内容会存在HackSystem的字节数组输出流中
*/
public class TestClass {
public static void main(String[] args) {
System.out.println("-----this is test class out println----");
}
}
3、jsp页面
- index.jsp
替换步骤
1、将 ByteUtils ClassModifier HackSystem HotSwapClassloader JavaClassExecuter TestClass 这六个.java文件上传到服务器通过javac进行编译成.class 文件
2、将编译好的TestClass放在/opt目录中
3、在tomcat的项目位置的WEB-INF/classes/中新建org/jvm文件夹,再将编译好的 ByteUtils ClassModifier HackSystem HotSwapClassloader JavaClassExecuter 放在WEB-INF/classes/org/jvm中
4、将 index.jsp 放在项目中能访问到的位置,如项目的根路径中
5、在浏览器中访问jsp页面即可。
参考资料
《深入理解 jvm》
贡献者
binbin.hou