Some modifications to specify custom patching file and some work towards dynamic init with view to re-enabling tests

This commit is contained in:
rick.parker 2023-04-04 11:53:54 +01:00
parent 11aaa018c0
commit e09c6473ad
5 changed files with 86 additions and 34 deletions

View File

@ -30,7 +30,7 @@ dependencies {
testImplementation('log4j:apache-log4j-extras:1.2.17') {
exclude group: 'log4j', module: 'log4j'
}
testImplementation group: 'com.ea.agentloader', name: 'ea-agent-loader', version: '1.0.3'
// testImplementation group: 'com.ea.agentloader', name: 'ea-agent-loader', version: '1.0.3'
testImplementation "com.google.guava:guava:$guava_version"
}

View File

@ -2,11 +2,20 @@
package net.gredler.aegis4j;
import java.io.IOException;
import java.io.InputStream;
import java.lang.instrument.Instrumentation;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.TreeMap;
import java.util.stream.Collectors;
/**
@ -20,6 +29,8 @@ import java.util.stream.Collectors;
*/
public final class AegisAgent {
private static Instrumentation instrumentation;
/**
* Supports static attach (via -javaagent parameter at JVM startup).
*
@ -27,7 +38,25 @@ public final class AegisAgent {
* @param instr instrumentation services
*/
public static void premain(String args, Instrumentation instr) {
Patcher.start(instr, toBlockList(args));
instrumentation = instr;
if(args.trim().equalsIgnoreCase("dynamic")) return;
Path path = null;
boolean started = false;
for(String arg: args.split(";")) {
if(started) throw new IllegalArgumentException("ERROR: argument ordering means patching already started");
String normalisedaArg = arg.trim().toLowerCase();
if(normalisedaArg.isEmpty() || normalisedaArg.startsWith("block=") || normalisedaArg.startsWith("unblock=")) {
try {
Patcher.start(instr, toBlockList(normalisedaArg, path), getModificationsInputStream(path));
started = true;
} catch (IOException e) {
e.printStackTrace();
}
} else if(normalisedaArg.startsWith("path=")) {
path= Paths.get(arg.trim().substring(5));
}
}
if(!started) throw new IllegalArgumentException("ERROR: patching not started");
}
/**
@ -37,7 +66,11 @@ public final class AegisAgent {
* @param instr instrumentation services
*/
public static void agentmain(String args, Instrumentation instr) {
Patcher.start(instr, toBlockList(args));
premain(args, instr);
}
static void dynamicLoad(String args) {
agentmain(args, instrumentation);
}
/**
@ -47,9 +80,8 @@ public final class AegisAgent {
* @param args agent arguments
* @return the block list derived from the agent arguments
*/
protected static Set< String > toBlockList(String args) {
Set< String > all = new HashSet<String>(Arrays.<String>asList("jndi", "rmi", "process", "httpserver", "serialization", "unsafe", "scripting", "jshell"));
protected static Set<String> toBlockList(String args, Path path) {
Set<String> all = loadFeaturesFromModifications(path);
if (args == null || args.trim().isEmpty()) {
// no arguments provided by user
return all;
@ -89,7 +121,6 @@ public final class AegisAgent {
* @throws IllegalArgumentException if any unrecognized feature names are included in the comma-delimited feature list
*/
private static Set<String> split(String values, Set<String> all) {
Set<String> features = Arrays.asList(values.split(","))
.stream()
.map(String::trim)
@ -103,4 +134,25 @@ public final class AegisAgent {
return Collections.unmodifiableSet(features);
}
private static Set<String> loadFeaturesFromModifications(Path path) {
Properties props = new Properties();
try {
props.load(getModificationsInputStream(path));
} catch (IOException e) {
e.printStackTrace();
}
Set<String> features = new HashSet<String>();
for (String key : props.stringPropertyNames()) {
int first = key.indexOf('.');
String feature = key.substring(0, first).toLowerCase();
features.add(feature);
}
return Collections.unmodifiableSet(features);
}
public static InputStream getModificationsInputStream(Path path) throws IOException {
if(path != null) return path.toUri().toURL().openStream();
return AegisAgent.class.getResourceAsStream("mods.properties");
}
}

View File

@ -3,6 +3,7 @@
package net.gredler.aegis4j;
import java.io.IOException;
import java.io.InputStream;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.Instrumentation;
import java.lang.instrument.UnmodifiableClassException;
@ -38,8 +39,8 @@ public final class Patcher implements ClassFileTransformer {
*
* @param block the features to block
*/
public Patcher(Set< String > block) {
modifications = loadModifications(block);
public Patcher(Set<String> block, InputStream inputStream) {
modifications = loadModifications(inputStream, block);
}
/**
@ -48,10 +49,9 @@ public final class Patcher implements ClassFileTransformer {
* @param instr the instrumentation instance to add a new patcher to
* @param block the features to block
*/
public static void start(Instrumentation instr, Set< String > block) {
public static void start(Instrumentation instr, Set<String> block, InputStream inputStream) {
System.out.println("Aegis4j patching starting");
Patcher patcher = new Patcher(block);
Patcher patcher = new Patcher(block ,inputStream);
instr.addTransformer(patcher, true);
for (String className : patcher.modifications.keySet()) {
@ -74,7 +74,6 @@ public final class Patcher implements ClassFileTransformer {
}
private byte[] patch(String className, byte[] classBytes) {
List<Modification> mods = modifications.get(className);
if (mods == null || mods.isEmpty()) {
return null;
@ -109,11 +108,10 @@ public final class Patcher implements ClassFileTransformer {
}
}
private static Map< String, List< Modification > > loadModifications(Set< String > block) {
private static Map<String, List<Modification>> loadModifications(InputStream inputStream, Set<String> block) {
Properties props = new Properties();
try {
props.load(AegisAgent.class.getResourceAsStream("mods.properties"));
props.load(inputStream);
} catch (IOException e) {
e.printStackTrace();
}

View File

@ -33,6 +33,8 @@ public class AegisAgentCommandLineTest {
testStaticAttach(jar, "block=jndi", "");
testStaticAttach(jar, "unblock=serialization", "");
testStaticAttach(jar, "block=serialization", "Java serialization blocked by aegis4j");
testStaticAttach(jar, "block=serialization;", "Java serialization blocked by aegis4j");
testStaticAttach(jar, ";block=serialization", "ERROR: argument ordering means patching already started");
}
private static void testStaticAttach(String jar, String config, String expectedErr) throws Exception {

View File

@ -20,7 +20,6 @@ import java.util.jar.JarEntry;
import java.util.jar.JarOutputStream;
import java.util.jar.Manifest;
import com.ea.agentloader.AgentLoader;
import com.google.common.base.Charsets;
import com.google.common.io.ByteStreams;
import org.junit.jupiter.api.function.Executable;
@ -136,7 +135,8 @@ public final class TestUtils {
jvm.loadAgent(createAgentJar(), options);
jvm.detach();
*/
AgentLoader.loadAgentClass(AegisAgent.class.getName(), options);
//AgentLoader.loadAgentClass(AegisAgent.class.getName(), options);
AegisAgent.dynamicLoad(options);
}
public static byte[] inputStreamReadAllBytes(InputStream stream) throws IOException {