Skip to content

Commit

Permalink
Improved search
Browse files Browse the repository at this point in the history
Added support for SerializationUtils
  • Loading branch information
TheAndrey committed Jul 30, 2023
1 parent 67d4ea9 commit 61c99a0
Show file tree
Hide file tree
Showing 6 changed files with 113 additions and 43 deletions.
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Project
group=me.theandrey
version=1.0
version=1.1
minecraft.version=1.7.10

# Repositories
Expand Down
51 changes: 28 additions & 23 deletions src/main/java/me/theandrey/objectstream/Config.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,27 +10,32 @@

public final class Config {

public static final Set<String> excludeClass = new HashSet<>();

public static void load(File file) {
Configuration config = new Configuration(file);
config.load();

String[] classNames = config.getStringList("ExcludeClass", "general", new String[0],
"Список полных названий классов для которых не будет применяться исправление");

excludeClass.clear();
excludeClass.addAll(filterStringList(classNames));

if (config.hasChanged()) {
config.save();
}
}

private static List<String> filterStringList(String[] strings) {
return Arrays.stream(strings)
.map(String::trim)
.distinct()
.collect(Collectors.toList());
}
public static final Set<String> excludeClass = new HashSet<>();
public static final Set<String> excludeMethods = new HashSet<>();

public static void load(File file) {
Configuration config = new Configuration(file);
config.load();

String[] classNames = config.getStringList("ExcludeClass", "general", new String[0],
"List of full class name for which the inheritance fix will not apply");
String[] methodList = config.getStringList("ExcludeMethods", "general", new String[0],
"List of methods in the format 'class_name#method_name' to which the fix will not apply.");

excludeClass.clear();
excludeClass.addAll(filterStringList(classNames));
excludeMethods.clear();
excludeMethods.addAll(filterStringList(methodList));

if (config.hasChanged()) {
config.save();
}
}

private static List<String> filterStringList(String[] strings) {
return Arrays.stream(strings)
.map(String::trim)
.distinct()
.collect(Collectors.toList());
}
}
29 changes: 22 additions & 7 deletions src/main/java/me/theandrey/objectstream/ObjectInputStreamMock.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,32 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import org.apache.commons.lang3.SerializationUtils;

/**
* Заглушка класса {@link ObjectInputStream}
*/
public final class ObjectInputStreamMock extends ObjectInputStream {
public class ObjectInputStreamMock extends ObjectInputStream {

public ObjectInputStreamMock(InputStream in) throws IOException {
throw new SecurityException("Not available due security reasons");
}
public ObjectInputStreamMock(InputStream in) throws IOException {
throw new SecurityException("Not available due security reasons");
}

public ObjectInputStreamMock() throws IOException, SecurityException {
throw new SecurityException("Not available due security reasons");
}
public ObjectInputStreamMock() throws IOException, SecurityException {
throw new SecurityException("Not available due security reasons");
}

/**
* @see SerializationUtils#deserialize(InputStream)
*/
public static <T> T deserialize(InputStream stream) {
throw new SecurityException("Not available due security reasons");
}

/**
* @see SerializationUtils#deserialize(byte[])
*/
public static <T> T deserialize(byte[] bytes) {
throw new SecurityException("Not available due security reasons");
}
}
4 changes: 4 additions & 0 deletions src/main/java/me/theandrey/objectstream/asm/ASMHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,8 @@ public static Type getObjectType(String name) {
return Type.getType('L' + name.replace('.', '/') + ';');
}

public static String className(ClassNode node) {
return node.name.replace('/', '.');
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import me.theandrey.objectstream.Config;

@IFMLLoadingPlugin.SortingIndex(2000)
@IFMLLoadingPlugin.TransformerExclusions("me.theandrey.objectstream.asm.")
@IFMLLoadingPlugin.TransformerExclusions("me.theandrey.objectstream.")
public class LoadingPlugin implements IFMLLoadingPlugin {

public LoadingPlugin() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,30 +24,30 @@ public class ObjectInputStreamTransformer implements IClassTransformer {

static final Logger LOGGER = LogManager.getLogger();
private final Type findType;
private final Type utilsType;
private final Type replaceType;

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

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

ClassNode node = ASMHelper.readClass(bytes);
List<MethodNode> unsafeMethods = scanMethods(node);
boolean superCheck = checkSuperClass(node);

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

if (!unsafeMethods.isEmpty()) {
LOGGER.warn("SECURITY ALERT: Detected usage of ObjectInputStream in class '{}'", name);
for (MethodNode method : unsafeMethods) {
LOGGER.warn("Method: {} {}", name, method.name + method.desc);
return ASMHelper.writeClass(node, 0);
}

return ASMHelper.writeClass(node, 0);
}

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

for (MethodNode method : node.methods) {
if (Config.excludeMethods.contains(ASMHelper.className(node) + '#' + method.name)) {
continue;
}

ListIterator<AbstractInsnNode> it = method.instructions.iterator();
boolean foundNew = false; // Найден оператор NEW

Expand All @@ -86,10 +90,52 @@ private List<MethodNode> scanMethods(ClassNode node) {
invoke.owner = replaceType.getInternalName(); // Заглушка
foundNew = false; // Завершаем замену блока
}

} else if (next.getOpcode() == Opcodes.INVOKESTATIC) {
MethodInsnNode invoke = ((MethodInsnNode)next);

if ("deserialize".equals(invoke.name) && invoke.owner.equals(utilsType.getInternalName())) {
invoke.owner = replaceType.getInternalName(); // Заглушка

if (!found.contains(method)) {
found.add(method);
}
}
}
}
}

return found;
}

private boolean checkSuperClass(ClassNode node) {
String name = ASMHelper.className(node);
if (!Config.excludeClass.contains(name) && node.superName.equals(findType.getInternalName())) {
node.superName = replaceType.getInternalName();

// Замена вызова конструктора родителя
for (MethodNode method : node.methods) {
if (method.name.equals("<init>")) {
ListIterator<AbstractInsnNode> it = method.instructions.iterator();

while (it.hasNext()) {
AbstractInsnNode next = it.next();

if (next.getOpcode() == Opcodes.INVOKESPECIAL) {
MethodInsnNode invoke = ((MethodInsnNode)next);

if ("<init>".equals(invoke.name) && invoke.owner.equals(findType.getInternalName())) {
invoke.owner = replaceType.getInternalName();
}
}
}
}
}

LOGGER.warn("SECURITY ALERT: Detected extending of ObjectInputStream in '{}'", name);
return true;
}

return false;
}
}

0 comments on commit 61c99a0

Please sign in to comment.