Skip to content

Commit ce3e711

Browse files
committed
Improved search
Added support for SerializationUtils
1 parent 3619129 commit ce3e711

File tree

6 files changed

+113
-43
lines changed

6 files changed

+113
-43
lines changed

gradle.properties

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Project
22
group=me.theandrey
3-
version=1.0
3+
version=1.1
44
minecraft_version=1.12.2
55

66
# Repositories

src/main/java/me/theandrey/objectstream/Config.java

+28-23
Original file line numberDiff line numberDiff line change
@@ -10,27 +10,32 @@
1010

1111
public final class Config {
1212

13-
public static final Set<String> excludeClass = new HashSet<>();
14-
15-
public static void load(File file) {
16-
Configuration config = new Configuration(file);
17-
config.load();
18-
19-
String[] classNames = config.getStringList("ExcludeClass", "general", new String[0],
20-
"Список полных названий классов для которых не будет применяться исправление");
21-
22-
excludeClass.clear();
23-
excludeClass.addAll(filterStringList(classNames));
24-
25-
if (config.hasChanged()) {
26-
config.save();
27-
}
28-
}
29-
30-
private static List<String> filterStringList(String[] strings) {
31-
return Arrays.stream(strings)
32-
.map(String::trim)
33-
.distinct()
34-
.collect(Collectors.toList());
35-
}
13+
public static final Set<String> excludeClass = new HashSet<>();
14+
public static final Set<String> excludeMethods = new HashSet<>();
15+
16+
public static void load(File file) {
17+
Configuration config = new Configuration(file);
18+
config.load();
19+
20+
String[] classNames = config.getStringList("ExcludeClass", "general", new String[0],
21+
"List of full class name for which the inheritance fix will not apply");
22+
String[] methodList = config.getStringList("ExcludeMethods", "general", new String[0],
23+
"List of methods in the format 'class_name#method_name' to which the fix will not apply.");
24+
25+
excludeClass.clear();
26+
excludeClass.addAll(filterStringList(classNames));
27+
excludeMethods.clear();
28+
excludeMethods.addAll(filterStringList(methodList));
29+
30+
if (config.hasChanged()) {
31+
config.save();
32+
}
33+
}
34+
35+
private static List<String> filterStringList(String[] strings) {
36+
return Arrays.stream(strings)
37+
.map(String::trim)
38+
.distinct()
39+
.collect(Collectors.toList());
40+
}
3641
}

src/main/java/me/theandrey/objectstream/ObjectInputStreamMock.java

+22-7
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,32 @@
33
import java.io.IOException;
44
import java.io.InputStream;
55
import java.io.ObjectInputStream;
6+
import org.apache.commons.lang3.SerializationUtils;
67

78
/**
89
* Заглушка класса {@link ObjectInputStream}
910
*/
10-
public final class ObjectInputStreamMock extends ObjectInputStream {
11+
public class ObjectInputStreamMock extends ObjectInputStream {
1112

12-
public ObjectInputStreamMock(InputStream in) throws IOException {
13-
throw new SecurityException("Not available due security reasons");
14-
}
13+
public ObjectInputStreamMock(InputStream in) throws IOException {
14+
throw new SecurityException("Not available due security reasons");
15+
}
1516

16-
public ObjectInputStreamMock() throws IOException, SecurityException {
17-
throw new SecurityException("Not available due security reasons");
18-
}
17+
public ObjectInputStreamMock() throws IOException, SecurityException {
18+
throw new SecurityException("Not available due security reasons");
19+
}
20+
21+
/**
22+
* @see SerializationUtils#deserialize(InputStream)
23+
*/
24+
public static <T> T deserialize(InputStream stream) {
25+
throw new SecurityException("Not available due security reasons");
26+
}
27+
28+
/**
29+
* @see SerializationUtils#deserialize(byte[])
30+
*/
31+
public static <T> T deserialize(byte[] bytes) {
32+
throw new SecurityException("Not available due security reasons");
33+
}
1934
}

src/main/java/me/theandrey/objectstream/asm/ASMHelper.java

+4
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,8 @@ public static Type getObjectType(String name) {
2424
return Type.getType('L' + name.replace('.', '/') + ';');
2525
}
2626

27+
public static String className(ClassNode node) {
28+
return node.name.replace('/', '.');
29+
}
30+
2731
}

src/main/java/me/theandrey/objectstream/asm/LoadingPlugin.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import net.minecraftforge.fml.relauncher.IFMLLoadingPlugin;
77

88
@IFMLLoadingPlugin.SortingIndex(2000)
9-
@IFMLLoadingPlugin.TransformerExclusions("me.theandrey.objectstream.asm.")
9+
@IFMLLoadingPlugin.TransformerExclusions("me.theandrey.objectstream.")
1010
public class LoadingPlugin implements IFMLLoadingPlugin {
1111

1212
public LoadingPlugin() {

src/main/java/me/theandrey/objectstream/asm/ObjectInputStreamTransformer.java

+57-11
Original file line numberDiff line numberDiff line change
@@ -24,30 +24,30 @@ public class ObjectInputStreamTransformer implements IClassTransformer {
2424

2525
static final Logger LOGGER = LogManager.getLogger();
2626
private final Type findType;
27+
private final Type utilsType;
2728
private final Type replaceType;
2829

2930
public ObjectInputStreamTransformer() {
3031
findType = Type.getType(ObjectInputStream.class);
32+
utilsType = ASMHelper.getObjectType("org.apache.commons.lang3.SerializationUtils");
3133
replaceType = ASMHelper.getObjectType("me.theandrey.objectstream.ObjectInputStreamMock");
3234
}
3335

3436
@Override
3537
public byte[] transform(String name, String transformedName, @Nullable byte[] bytes) {
36-
if (bytes == null || bytes.length == 0 || Config.excludeClass.contains(name)) {
37-
return bytes;
38-
}
38+
if (bytes != null && bytes.length > 0) {
39+
ClassNode node = ASMHelper.readClass(bytes);
3940

40-
ClassNode node = ASMHelper.readClass(bytes);
41+
List<MethodNode> unsafeMethods = scanMethods(node);
42+
boolean superCheck = checkSuperClass(node);
4143

42-
List<MethodNode> unsafeMethods = scanMethods(node);
44+
if (!unsafeMethods.isEmpty() || superCheck) {
45+
for (MethodNode method : unsafeMethods) {
46+
LOGGER.warn("SECURITY ALERT: Detected usage of ObjectInputStream in '{}#{}'", name, method.name);
47+
}
4348

44-
if (!unsafeMethods.isEmpty()) {
45-
LOGGER.warn("SECURITY ALERT: Detected usage of ObjectInputStream in class '{}'", name);
46-
for (MethodNode method : unsafeMethods) {
47-
LOGGER.warn("Method: {} {}", name, method.name + method.desc);
49+
return ASMHelper.writeClass(node, 0);
4850
}
49-
50-
return ASMHelper.writeClass(node, 0);
5151
}
5252

5353
return bytes;
@@ -61,6 +61,10 @@ private List<MethodNode> scanMethods(ClassNode node) {
6161
List<MethodNode> found = new ArrayList<>();
6262

6363
for (MethodNode method : node.methods) {
64+
if (Config.excludeMethods.contains(ASMHelper.className(node) + '#' + method.name)) {
65+
continue;
66+
}
67+
6468
ListIterator<AbstractInsnNode> it = method.instructions.iterator();
6569
boolean foundNew = false; // Найден оператор NEW
6670

@@ -86,10 +90,52 @@ private List<MethodNode> scanMethods(ClassNode node) {
8690
invoke.owner = replaceType.getInternalName(); // Заглушка
8791
foundNew = false; // Завершаем замену блока
8892
}
93+
94+
} else if (next.getOpcode() == Opcodes.INVOKESTATIC) {
95+
MethodInsnNode invoke = ((MethodInsnNode)next);
96+
97+
if ("deserialize".equals(invoke.name) && invoke.owner.equals(utilsType.getInternalName())) {
98+
invoke.owner = replaceType.getInternalName(); // Заглушка
99+
100+
if (!found.contains(method)) {
101+
found.add(method);
102+
}
103+
}
89104
}
90105
}
91106
}
92107

93108
return found;
94109
}
110+
111+
private boolean checkSuperClass(ClassNode node) {
112+
String name = ASMHelper.className(node);
113+
if (!Config.excludeClass.contains(name) && node.superName.equals(findType.getInternalName())) {
114+
node.superName = replaceType.getInternalName();
115+
116+
// Замена вызова конструктора родителя
117+
for (MethodNode method : node.methods) {
118+
if (method.name.equals("<init>")) {
119+
ListIterator<AbstractInsnNode> it = method.instructions.iterator();
120+
121+
while (it.hasNext()) {
122+
AbstractInsnNode next = it.next();
123+
124+
if (next.getOpcode() == Opcodes.INVOKESPECIAL) {
125+
MethodInsnNode invoke = ((MethodInsnNode)next);
126+
127+
if ("<init>".equals(invoke.name) && invoke.owner.equals(findType.getInternalName())) {
128+
invoke.owner = replaceType.getInternalName();
129+
}
130+
}
131+
}
132+
}
133+
}
134+
135+
LOGGER.warn("SECURITY ALERT: Detected extending of ObjectInputStream in '{}'", name);
136+
return true;
137+
}
138+
139+
return false;
140+
}
95141
}

0 commit comments

Comments
 (0)