番外篇-根据前文的文件上传系统实现一个Java在线运行工具

首先,我们要先对前文的上传系统进行修改,首先是前端

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title></title>
</head>
<body>
        <textarea rows="100%" cols="100%" id="java">
        </textarea>
<input type="button" value="提交" onclick="uploads()">
</body>
<script src="js/jquery.js"></script>
<script>
    var uploads=function () {
            var form = new FormData();
            form.append("java",document.getElementById("java").value);
            $.ajax({
                type: "POST",
                url: "/test/upload",
                data:form,
                async: true,
                processData: false,
                contentType: false,
                success: function (result) {
                },
                error: function (e) {
                }
            });
    }
</script>
</html>

在这里插入图片描述
上传成功,因为字段不符,后台会报错,不要紧,查看缓存文件
在这里插入图片描述
成功获取Java文件,修改上传代码,准备调用javac进行编译获取.class文件

		parse(request.getInputStream());
        request.getInputStream().close();
        write("java","上传文件/"+"test.java");
        new File("缓存文件/"+hm.get("java")).delete();

在这里插入图片描述
jdk8以下想要调用javac需要到jdk中提取tools.jar,请依照如何查看Javac源码自行查找添加

protected void doPost(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException {
        parse(request.getInputStream());
        request.getInputStream().close();
        write("java","上传文件/test.java");
        new File("缓存文件/"+hm.get("java")).delete();
        Main.compile(new String[]{"上传文件/test.java"});
    }

在这里插入图片描述
准备修改.class文件,将对应的常量池符号修改为我们自己定义的包装类,最后将所有打印信息返回给浏览器,Javap随意打开一段包含system.out.println的class文件

Constant pool:
   #1 = Methodref          #6.#15         // java/lang/Object."<init>":()V
   #2 = Fieldref           #16.#17        // java/lang/System.out:Ljava/io/PrintStream;
   #3 = String             #18            // xx
   #4 = Methodref          #19.#20        // java/io/PrintStream.println:(Ljava/lang/String;)V
   #5 = Class              #21            // test
   #6 = Class              #22            // java/lang/Object
   #7 = Utf8               <init>
   #8 = Utf8               ()V
   #9 = Utf8               Code
  #10 = Utf8               LineNumberTable
  #11 = Utf8               main
  #12 = Utf8               ([Ljava/lang/String;)V
  #13 = Utf8               SourceFile
  #14 = Utf8               test.java
  #15 = NameAndType        #7:#8          // "<init>":()V
  #16 = Class              #23            // java/lang/System
  #17 = NameAndType        #24:#25        // out:Ljava/io/PrintStream;
  #18 = Utf8               xx
  #19 = Class              #26            // java/io/PrintStream
  #20 = NameAndType        #27:#28        // println:(Ljava/lang/String;)V
  #21 = Utf8               test
  #22 = Utf8               java/lang/Object
  #23 = Utf8               java/lang/System
  #24 = Utf8               out
  #25 = Utf8               Ljava/io/PrintStream;
  #26 = Utf8               java/io/PrintStream
  #27 = Utf8               println
  #28 = Utf8               (Ljava/lang/String;)V
{
  public test();
    descriptor: ()V
    flags: (0x0001) ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 2: 0

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=1, args_size=1
         0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
         3: ldc           #3                  // String xx
         5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
         8: return
      LineNumberTable:
        line 4: 0
        line 5: 8
}
SourceFile: "test.java"

这是我们准备替换system的类

import java.io.ByteArrayOutputStream;
import java.io.PrintStream;

public class sys {
    public static ByteArrayOutputStream bos=new ByteArrayOutputStream();

    public final static PrintStream out=new PrintStream(bos);

    public void println(String s){
        out.println(s);
    }
    public void println(){
        out.println("\n");
    }
    public String getArr(){
        out.flush();
        return new String(bos.toByteArray());
    }
}

还是老样子,javap反编译,查看不同

Constant pool:
   #1 = Methodref          #6.#20         // java/lang/Object."<init>":()V
   #2 = Fieldref           #21.#22        // sys.out:Ljava/io/PrintStream;
   #3 = String             #23            //
   #4 = Methodref          #24.#25        // java/io/PrintStream.println:(Ljava/lang/String;)V
   #5 = Class              #26            // Bean
   #6 = Class              #27            // java/lang/Object
   #7 = Utf8               <init>
   #8 = Utf8               ()V
   #9 = Utf8               Code
  #10 = Utf8               LineNumberTable
  #11 = Utf8               LocalVariableTable
  #12 = Utf8               this
  #13 = Utf8               LBean;
  #14 = Utf8               main
  #15 = Utf8               ([Ljava/lang/String;)V
  #16 = Utf8               args
  #17 = Utf8               [Ljava/lang/String;
  #18 = Utf8               SourceFile
  #19 = Utf8               Bean.java
  #20 = NameAndType        #7:#8          // "<init>":()V
  #21 = Class              #28            // sys
  #22 = NameAndType        #29:#30        // out:Ljava/io/PrintStream;
  #23 = Utf8
  #24 = Class              #31            // java/io/PrintStream
  #25 = NameAndType        #32:#33        // println:(Ljava/lang/String;)V
  #26 = Utf8               Bean
  #27 = Utf8               java/lang/Object
  #28 = Utf8               sys
  #29 = Utf8               out
  #30 = Utf8               Ljava/io/PrintStream;
  #31 = Utf8               java/io/PrintStream
  #32 = Utf8               println
  #33 = Utf8               (Ljava/lang/String;)V
{
  public Bean();
    descriptor: ()V
    flags: (0x0001) ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 1: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   LBean;

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=1, args_size=1
         0: getstatic     #2                  // Field sys.out:Ljava/io/PrintStream;
         3: ldc           #3                  // String
         5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
         8: return
      LineNumberTable:
        line 3: 0
        line 4: 8
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       9     0  args   [Ljava/lang/String;
}

所以我们需要修改

	#23 = Utf8               java/lang/System

	#28 = Utf8               sys

由于java/lang/System属于utf-8字段,它在.class中保存的形式基本确定,为

01 00 10 6A 61 76 61 2F 6C 61 6E 67 2F 53 79 73 74 65 6D
         j  a  v  a  /  l  a  n  g  /  S  y  s  t  e  m

所以我们只需要循环遍历,将其替换为

01 00 03 73 79 73
	     s  y  s

我们先手动修改一下字节码文件
在这里插入图片描述
test原本的代码

public class test {
    public static void main(String[] args) {
        System.out.println("xx");
    }
}

运行
在这里插入图片描述
成功,开始写替换程序

话不多说,直接上代码

import java.io.FileInputStream;

/**
 * 2019-10-19 15:33
 * 贺驰宇
 */
public class tool {
    /**
     * system字节码
     */
    static byte[] system = new byte[]{
            0x01,
            0x00, 0x10,
            0x6A, 0x61, 0x76, 0x61, 0x2F, 0x6C, 0x61, 0x6E, 0x67, 0x2F, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6D
    };
    /**
     * sys字节码
     */
    static byte[] sys = new byte[]{
            0x01,
            0x00, 0x03,
            0x73, 0x79, 0x73
    };

    /**
     * 通用的单个字节码替换方法
     * @param file        需要替换的class文件位置
     * @param replace     即将被替换的字节码
     * @param replacement 替换的字节码
     * @return 最终生成的字节码
     */
    public static byte[] systemToSys(String file, byte[] replace, byte[] replacement) {
        //当前class对象与被替换对象的匹配度
        int size = 0;
        //替换对象和被替换对象的差值
        int reduceSize = replace.length - replacement.length;
        try {
            //获取class字节码
            FileInputStream fis = new FileInputStream(file);
            byte[] bytes = fis.readAllBytes();
            fis.close();
            //根据差值,决定最终生成的字节码大小
            byte[] cache = new byte[bytes.length - reduceSize];
            for (int i = 0; i < bytes.length; i++) {
                //是否完全匹配
                if (size == replace.length) {
                    //需要替换的字节码的前半段长度
                    int beginIndex = i - replace.length;
                    int j;
                    //进行循环插入
                    for (j = 0; j < beginIndex; j++)
                        cache[j] = bytes[j];
                    for (; j < beginIndex + replacement.length; j++)
                        cache[j] = replacement[j - beginIndex];
                    for (; j < bytes.length - reduceSize; j++)
                        cache[j] = bytes[j + reduceSize];
                    return cache;
                }
                //是否匹配
                if (replace[size] == bytes[i]) {
                    size++;
                } else {
                    size = 0;
                    if (replace[size] == bytes[i])
                        size++;
                }
            }
            return bytes;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    public static void main(String[] args) {
        new tool().systemToSys("xx.class", system, sys);
    }
}

由于类加载我在之前的虚拟机类加载机制(二)类加载的过程已经写过了,稍作更改

public class TestClassLoader extends ClassLoader {
    private byte[] bytes;

    public TestClassLoader(byte[] bytes) {
        super(sys.class.getClassLoader());
        this.bytes = bytes;
    }

    public Class<?> findClass(String name) {
        return defineClass(name, bytes, 0, bytes.length);
    }
}
protected void doPost(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException {
        sys.bos.reset();
        parse(request.getInputStream());
        request.getInputStream().close();
        write("java","上传文件/test.java");
        new File("缓存文件/"+hm.get("java")).delete();
        Main.compile(new String[]{"上传文件/test.java"});
        byte[] bytes=tool.systemToSys("上传文件/test.class",tool.system,tool.sys);
        try {
            Class clzz=new TestClassLoader(bytes).findClass("test");
            Method m =clzz.getMethod("main",String[].class);
            m.invoke(null,new Object[]{new String[]{}});
        } catch (Exception e) {
            e.printStackTrace();
        }
        response.getWriter().println(sys.getArr());
    }

在这里插入图片描述
成功

好的,您想了解有关自定义几何体的内容,我很乐意为您解答。首先,让我们了解一下什么是几何体。 在Cesium中,几何体是由一些点、线和三角形组成的图形。几何体可以在地球上显示各种形状的物体,如建筑、飞机、汽车等。Cesium提供了一些内置的几何体,如BoxGeometry、CylinderGeometry、SphereGeometry等,但是有时候我们需要展示一些特殊形状的物体,这时候就需要自定义几何体了。 下面是一个简单的自定义几何体的例子: ```javascript var geometry = new Cesium.Geometry({ attributes: { position: new Cesium.GeometryAttribute({ componentDatatype: Cesium.ComponentDatatype.DOUBLE, componentsPerAttribute: 3, values: new Float64Array([ 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0 ]) }) }, indices: new Uint16Array([ 0, 1, 2, 0, 2, 3 ]), primitiveType: Cesium.PrimitiveType.TRIANGLES }); ``` 这个例子中,我们创建了一个由四个点组成的矩形,并用这些点的索引定义了两个三角形。这个几何体可以用来在地球上显示一个矩形。 接下来,让我们逐步了解这个例子中的代码。首先是Cesium.GeometryAttribute。 Cesium.GeometryAttribute是几何体属性的容器。在这个例子中,我们定义了一个名为position的属性,它有三个分量:x、y和z。这个属性使用的数据类型是Cesium.ComponentDatatype.DOUBLE,表示每个分量有一个双精度浮点数。componentsPerAttribute表示每个属性有几个分量。在这个例子中,每个属性都有三个分量。最后,我们用一个Float64Array数组来定义这个属性的值。 接下来是indices,它定义了几何体使用哪些点来组成三角形。在这个例子中,我们定义了两个三角形,每个三角形使用三个顶点。在indices数组中,我们用顶点在attributes数组中的索引来定义每个三角形。 最后,我们定义了几何体的primitiveType,它表示几何体的类型。在这个例子中,我们使用的是三角形类型,所以primitiveType为Cesium.PrimitiveType.TRIANGLES。 希望这个例子可以帮助您更好地理解自定义几何体的实现
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值