Skip to content

Commit

Permalink
feat: add feature to detect runtime generated classes (#64)
Browse files Browse the repository at this point in the history
  • Loading branch information
algomaster99 authored Aug 25, 2023
1 parent 19b7597 commit a74461d
Show file tree
Hide file tree
Showing 10 changed files with 117 additions and 21 deletions.
15 changes: 15 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,21 @@
<artifactId>log4j-core</artifactId>
<version>2.20.0</version>
</dependency>
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm</artifactId>
<version>9.5</version>
</dependency>
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm-tree</artifactId>
<version>9.5</version>
</dependency>
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm-util</artifactId>
<version>9.5</version>
</dependency>
</dependencies>
</dependencyManagement>

Expand Down
12 changes: 12 additions & 0 deletions terminator-commons/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,18 @@
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
</dependency>
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm</artifactId>
</dependency>
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm-tree</artifactId>
</dependency>
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm-util</artifactId>
</dependency>
</dependencies>

<build>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package io.github.algomaster99.terminator.commons.fingerprint.classfile;

import java.util.Objects;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.Opcodes;

public class RuntimeClass {

private RuntimeClass() {}

/**
* Proxy classes are not synthetic classes, but they are runtime generated.
*/
public static boolean isProxyClass(byte[] classfileBytes) {
ClassReader reader = new ClassReader(classfileBytes);
return Objects.equals(reader.getSuperName(), "java/lang/reflect/Proxy");
}

public static boolean isSynthetic(byte[] classfileBytes) {
ClassReader reader = new ClassReader(classfileBytes);
return (reader.getAccess() & Opcodes.ACC_SYNTHETIC) != 0;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package io.github.algomaster99.terminator.commons.fingerprint.classfile;

import static org.assertj.core.api.Assertions.assertThat;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.util.TraceClassVisitor;

public class ClassfileTest {

private static final Path TEST_RESOURCES = Path.of("src/test/resources");

@Nested
class IsRuntimeGeneratedClass {
private static final Path CLASSFILE = TEST_RESOURCES.resolve("classfile");

@Test
void isAProxyClass_true() throws IOException {
Path proxy39 = CLASSFILE.resolve("$Proxy39.class");
Path proxy40 = CLASSFILE.resolve("$Proxy40.class");

assertThat(RuntimeClass.isProxyClass(Files.readAllBytes(proxy39))).isTrue();
assertThat(RuntimeClass.isProxyClass(Files.readAllBytes(proxy40))).isTrue();
}

@Test
void isProxyClass_false() throws IOException {
Path maven = CLASSFILE.resolve("Maven.class");

assertThat(RuntimeClass.isProxyClass(Files.readAllBytes(maven))).isFalse();
}

@Test
void isSyntheticClass_true() throws IOException {
Path synthetic = CLASSFILE.resolve("$Proxy39.class");

byte[] unmodifiedBytes = Files.readAllBytes(synthetic);
assertThat(RuntimeClass.isSynthetic(unmodifiedBytes)).isFalse();

byte[] modifiedBytes = makeClassfileSynthetic(Files.readAllBytes(synthetic));
assertThat(RuntimeClass.isSynthetic(modifiedBytes)).isTrue();
}

private static byte[] makeClassfileSynthetic(byte[] classfileBytes) {
ClassNode classNode = new ClassNode();
ClassReader classReader = new ClassReader(classfileBytes);
classReader.accept(classNode, 0);
classNode.access |= Opcodes.ACC_SYNTHETIC;

ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
classNode.accept(new TraceClassVisitor(writer, null));
return writer.toByteArray();
}
}
}
Binary file not shown.
Binary file not shown.
Binary file not shown.
20 changes: 0 additions & 20 deletions watchdog-agent/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -30,26 +30,6 @@
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>net.bytebuddy</groupId>
<artifactId>byte-buddy-dep</artifactId>
<version>1.14.6</version>
</dependency>
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm</artifactId>
<version>9.5</version>
</dependency>
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm-tree</artifactId>
<version>9.5</version>
</dependency>
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm-util</artifactId>
<version>9.5</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import static io.github.algomaster99.terminator.commons.fingerprint.classfile.HashComputer.computeHash;

import io.github.algomaster99.terminator.commons.fingerprint.classfile.RuntimeClass;
import io.github.algomaster99.terminator.commons.fingerprint.provenance.Provenance;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.Instrumentation;
Expand Down Expand Up @@ -33,6 +34,9 @@ public byte[] transform(

private static byte[] isLoadedClassWhitelisted(String className, byte[] classfileBuffer) {
Map<String, List<Provenance>> fingerprints = options.getFingerprints();
if (RuntimeClass.isProxyClass(classfileBuffer) || RuntimeClass.isSynthetic(classfileBuffer)) {
return classfileBuffer;
}
if (INTERNAL_PACKAGES.stream().anyMatch(className::startsWith)) {
return classfileBuffer;
}
Expand Down
2 changes: 1 addition & 1 deletion watchdog-agent/src/test/java/AgentTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ void sorald_0_8_5_shouldExitWith_1() throws IOException, InterruptedException {
Process p = pb.start();
int exitCode = p.waitFor();

assertThat(exitCode).isEqualTo(1);
assertThat(exitCode).isEqualTo(0);
}

private static void deleteContentsOfFile(String file) throws InterruptedException, IOException {
Expand Down

0 comments on commit a74461d

Please sign in to comment.