reorganize builtin plugins

Signed-off-by: pengzhile <pengzhile@gmail.com>
This commit is contained in:
pengzhile 2021-11-30 19:24:10 +08:00
parent cf57cd2015
commit a380bf126f
14 changed files with 209 additions and 66 deletions

View File

@ -1,4 +1,4 @@
# ja-netfilter v1.1.0
# ja-netfilter v1.1.1
### A javaagent lib for network filter

View File

@ -6,7 +6,7 @@
<groupId>io.zhile.research</groupId>
<artifactId>ja-netfilter</artifactId>
<version>1.1.0</version>
<version>1.1.1</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

View File

@ -1,7 +1,7 @@
package io.zhile.research.ja.netfilter;
import io.zhile.research.ja.netfilter.commons.DebugInfo;
import io.zhile.research.ja.netfilter.transformers.MyTransformer;
import io.zhile.research.ja.netfilter.plugin.MyTransformer;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
@ -28,12 +28,20 @@ public class Dispatcher implements ClassFileTransformer {
}
public void addTransformers(List<MyTransformer> transformers) {
if (null == transformers) {
return;
}
for (MyTransformer transformer : transformers) {
addTransformer(transformer);
}
}
public void addTransformers(MyTransformer[] transformers) {
if (null == transformers) {
return;
}
addTransformers(Arrays.asList(transformers));
}
@ -54,7 +62,7 @@ public class Dispatcher implements ClassFileTransformer {
for (MyTransformer transformer : transformers) {
classFileBuffer = transformer.transform(className, classFileBuffer, order++);
}
} catch (Exception e) {
} catch (Throwable e) {
DebugInfo.output("Transform class failed: " + e.getMessage());
}
} while (false);

View File

@ -21,7 +21,7 @@ public class Initializer {
try {
FilterConfig.setCurrent(new FilterConfig(ConfigParser.parse(configFile)));
} catch (Exception e) {
} catch (Throwable e) {
DebugInfo.output(e.getMessage());
}

View File

@ -19,7 +19,7 @@ public class Launcher {
URI jarURI;
try {
jarURI = getJarURI();
} catch (Exception e) {
} catch (Throwable e) {
DebugInfo.output("ERROR: Can not locate ja-netfilter jar file.");
return;
}

View File

@ -1,4 +1,4 @@
package io.zhile.research.ja.netfilter.transformers;
package io.zhile.research.ja.netfilter.plugin;
public interface MyTransformer {
String getHookClassName();

View File

@ -1,7 +1,6 @@
package io.zhile.research.ja.netfilter.plugin;
import io.zhile.research.ja.netfilter.models.FilterRule;
import io.zhile.research.ja.netfilter.transformers.MyTransformer;
import java.util.List;
@ -12,6 +11,8 @@ public interface PluginEntry {
String getName();
String getAuthor();
default String getVersion() {
return "v1.0.0";
}

View File

@ -3,14 +3,15 @@ package io.zhile.research.ja.netfilter.plugin;
import io.zhile.research.ja.netfilter.Dispatcher;
import io.zhile.research.ja.netfilter.commons.DebugInfo;
import io.zhile.research.ja.netfilter.models.FilterConfig;
import io.zhile.research.ja.netfilter.transformers.HttpClientTransformer;
import io.zhile.research.ja.netfilter.transformers.InetAddressTransformer;
import io.zhile.research.ja.netfilter.transformers.MyTransformer;
import io.zhile.research.ja.netfilter.plugins.dns.DNSFilterPlugin;
import io.zhile.research.ja.netfilter.plugins.url.URLFilterPlugin;
import io.zhile.research.ja.netfilter.utils.StringUtils;
import java.io.File;
import java.lang.instrument.Instrumentation;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
@ -29,46 +30,63 @@ public class PluginManager {
}
public void loadPlugins(Instrumentation inst, File currentDirectory) {
File pluginsDirectory = new File(currentDirectory, PLUGINS_DIR);
if (!pluginsDirectory.exists() || !pluginsDirectory.isDirectory()) {
return;
}
File[] pluginFiles = pluginsDirectory.listFiles((d, n) -> n.endsWith(".jar"));
if (null == pluginFiles) {
return;
}
Dispatcher.getInstance().addTransformers(new MyTransformer[]{ // built-in transformers
new HttpClientTransformer(),
new InetAddressTransformer()
});
for (File pluginFile : pluginFiles) {
for (Class<? extends PluginEntry> klass : getAllPluginClasses(inst, currentDirectory)) {
try {
JarFile jarFile = new JarFile(pluginFile);
Manifest manifest = jarFile.getManifest();
String entryClass = manifest.getMainAttributes().getValue(ENTRY_NAME);
if (StringUtils.isEmpty(entryClass)) {
continue;
}
PluginClassLoader classLoader = new PluginClassLoader(jarFile);
Class<?> klass = Class.forName(entryClass, false, classLoader);
if (!Arrays.asList(klass.getInterfaces()).contains(PluginEntry.class)) {
continue;
}
inst.appendToBootstrapClassLoaderSearch(jarFile);
PluginEntry pluginEntry = (PluginEntry) Class.forName(entryClass).newInstance();
pluginEntry.init(FilterConfig.getBySection(pluginEntry.getName()));
Dispatcher.getInstance().addTransformers(pluginEntry.getTransformers());
DebugInfo.output("Plugin loaded: {name=" + pluginEntry.getName() + ", version=" + pluginEntry.getVersion() + "}");
} catch (Exception e) {
DebugInfo.output("Load plugin failed: " + e.getMessage());
addPluginEntry(klass);
} catch (Throwable e) {
DebugInfo.output("Init plugin failed: " + e.getMessage());
}
}
}
private List<Class<? extends PluginEntry>> getAllPluginClasses(Instrumentation inst, File currentDirectory) {
List<Class<? extends PluginEntry>> classes = new ArrayList<>();
classes.add(DNSFilterPlugin.class);
classes.add(URLFilterPlugin.class);
do {
File pluginsDirectory = new File(currentDirectory, PLUGINS_DIR);
if (!pluginsDirectory.exists() || !pluginsDirectory.isDirectory()) {
break;
}
File[] pluginFiles = pluginsDirectory.listFiles((d, n) -> n.endsWith(".jar"));
if (null == pluginFiles) {
break;
}
for (File pluginFile : pluginFiles) {
try {
JarFile jarFile = new JarFile(pluginFile);
Manifest manifest = jarFile.getManifest();
String entryClass = manifest.getMainAttributes().getValue(ENTRY_NAME);
if (StringUtils.isEmpty(entryClass)) {
continue;
}
PluginClassLoader classLoader = new PluginClassLoader(jarFile);
Class<?> klass = Class.forName(entryClass, false, classLoader);
if (!Arrays.asList(klass.getInterfaces()).contains(PluginEntry.class)) {
continue;
}
inst.appendToBootstrapClassLoaderSearch(jarFile);
classes.add((Class<? extends PluginEntry>) Class.forName(entryClass));
} catch (Throwable e) {
DebugInfo.output("Load plugin failed: " + e.getMessage());
}
}
} while (false);
return classes;
}
private void addPluginEntry(Class<? extends PluginEntry> entryClass) throws Exception {
PluginEntry pluginEntry = entryClass.newInstance();
pluginEntry.init(FilterConfig.getBySection(pluginEntry.getName()));
Dispatcher.getInstance().addTransformers(pluginEntry.getTransformers());
DebugInfo.output("Plugin loaded: {name=" + pluginEntry.getName() + ", version=" + pluginEntry.getVersion() + ", author=" + pluginEntry.getAuthor() + "}");
}
}

View File

@ -1,21 +1,25 @@
package io.zhile.research.ja.netfilter.filters;
package io.zhile.research.ja.netfilter.plugins.dns;
import io.zhile.research.ja.netfilter.commons.DebugInfo;
import io.zhile.research.ja.netfilter.models.FilterConfig;
import io.zhile.research.ja.netfilter.models.FilterRule;
import java.io.IOException;
import java.net.InetAddress;
import java.util.List;
public class DNSFilter {
private static final String SECTION_NAME = "DNS";
private static List<FilterRule> ruleList;
public static void setRules(List<FilterRule> rules) {
ruleList = rules;
}
public static String testQuery(String host) throws IOException {
if (null == host) {
if (null == host || null == ruleList) {
return null;
}
for (FilterRule rule : FilterConfig.getBySection(SECTION_NAME)) {
for (FilterRule rule : ruleList) {
if (!rule.test(host)) {
continue;
}
@ -28,11 +32,11 @@ public class DNSFilter {
}
public static Object testReachable(InetAddress n) throws IOException {
if (null == n) {
if (null == n || null == ruleList) {
return null;
}
for (FilterRule rule : FilterConfig.getBySection(SECTION_NAME)) {
for (FilterRule rule : ruleList) {
if (!rule.test(n.getHostName())) {
continue;
}

View File

@ -0,0 +1,42 @@
package io.zhile.research.ja.netfilter.plugins.dns;
import io.zhile.research.ja.netfilter.models.FilterRule;
import io.zhile.research.ja.netfilter.plugin.MyTransformer;
import io.zhile.research.ja.netfilter.plugin.PluginEntry;
import java.util.ArrayList;
import java.util.List;
public class DNSFilterPlugin implements PluginEntry {
private final List<MyTransformer> transformers = new ArrayList<>();
@Override
public void init(List<FilterRule> filterRules) {
transformers.add(new InetAddressTransformer(filterRules));
}
@Override
public String getName() {
return "DNS";
}
@Override
public String getAuthor() {
return "neo";
}
@Override
public String getVersion() {
return "v1.0.0";
}
@Override
public String getDescription() {
return "ja-netfilter core: dns plugin";
}
@Override
public List<MyTransformer> getTransformers() {
return transformers;
}
}

View File

@ -1,12 +1,22 @@
package io.zhile.research.ja.netfilter.transformers;
package io.zhile.research.ja.netfilter.plugins.dns;
import io.zhile.research.ja.netfilter.models.FilterRule;
import io.zhile.research.ja.netfilter.plugin.MyTransformer;
import jdk.internal.org.objectweb.asm.ClassReader;
import jdk.internal.org.objectweb.asm.ClassWriter;
import jdk.internal.org.objectweb.asm.tree.*;
import java.util.List;
import static jdk.internal.org.objectweb.asm.Opcodes.*;
public class InetAddressTransformer implements MyTransformer {
private final List<FilterRule> rules;
public InetAddressTransformer(List<FilterRule> rules) {
this.rules = rules;
}
@Override
public String getHookClassName() {
return "java/net/InetAddress";
@ -14,6 +24,8 @@ public class InetAddressTransformer implements MyTransformer {
@Override
public byte[] transform(String className, byte[] classBytes, int order) throws Exception {
DNSFilter.setRules(rules);
ClassReader reader = new ClassReader(classBytes);
ClassNode node = new ClassNode(ASM5);
reader.accept(node, 0);
@ -22,7 +34,7 @@ public class InetAddressTransformer implements MyTransformer {
if ("getAllByName".equals(m.name) && "(Ljava/lang/String;Ljava/net/InetAddress;)[Ljava/net/InetAddress;".equals(m.desc)) {
InsnList list = new InsnList();
list.add(new VarInsnNode(ALOAD, 0));
list.add(new MethodInsnNode(INVOKESTATIC, "io/zhile/research/ja/netfilter/filters/DNSFilter", "testQuery", "(Ljava/lang/String;)Ljava/lang/String;", false));
list.add(new MethodInsnNode(INVOKESTATIC, "io/zhile/research/ja/netfilter/plugins/dns/DNSFilter", "testQuery", "(Ljava/lang/String;)Ljava/lang/String;", false));
list.add(new InsnNode(POP));
m.instructions.insert(list);
@ -32,7 +44,7 @@ public class InetAddressTransformer implements MyTransformer {
if ("isReachable".equals(m.name) && "(Ljava/net/NetworkInterface;II)Z".equals(m.desc)) {
InsnList list = new InsnList();
list.add(new VarInsnNode(ALOAD, 0));
list.add(new MethodInsnNode(INVOKESTATIC, "io/zhile/research/ja/netfilter/filters/DNSFilter", "testReachable", "(Ljava/net/InetAddress;)Ljava/lang/Object;", false));
list.add(new MethodInsnNode(INVOKESTATIC, "io/zhile/research/ja/netfilter/plugins/dns/DNSFilter", "testReachable", "(Ljava/net/InetAddress;)Ljava/lang/Object;", false));
list.add(new VarInsnNode(ASTORE, 4));
list.add(new InsnNode(ACONST_NULL));
list.add(new VarInsnNode(ALOAD, 4));

View File

@ -1,12 +1,22 @@
package io.zhile.research.ja.netfilter.transformers;
package io.zhile.research.ja.netfilter.plugins.url;
import io.zhile.research.ja.netfilter.models.FilterRule;
import io.zhile.research.ja.netfilter.plugin.MyTransformer;
import jdk.internal.org.objectweb.asm.ClassReader;
import jdk.internal.org.objectweb.asm.ClassWriter;
import jdk.internal.org.objectweb.asm.tree.*;
import java.util.List;
import static jdk.internal.org.objectweb.asm.Opcodes.*;
public class HttpClientTransformer implements MyTransformer {
private final List<FilterRule> rules;
public HttpClientTransformer(List<FilterRule> rules) {
this.rules = rules;
}
@Override
public String getHookClassName() {
return "sun/net/www/http/HttpClient";
@ -14,6 +24,8 @@ public class HttpClientTransformer implements MyTransformer {
@Override
public byte[] transform(String className, byte[] classBytes, int order) throws Exception {
URLFilter.setRules(rules);
ClassReader reader = new ClassReader(classBytes);
ClassNode node = new ClassNode(ASM5);
reader.accept(node, 0);
@ -23,7 +35,7 @@ public class HttpClientTransformer implements MyTransformer {
InsnList list = new InsnList();
list.add(new VarInsnNode(ALOAD, 0));
list.add(new FieldInsnNode(GETFIELD, "sun/net/www/http/HttpClient", "url", "Ljava/net/URL;"));
list.add(new MethodInsnNode(INVOKESTATIC, "io/zhile/research/ja/netfilter/filters/URLFilter", "testURL", "(Ljava/net/URL;)Ljava/net/URL;", false));
list.add(new MethodInsnNode(INVOKESTATIC, "io/zhile/research/ja/netfilter/plugins/url/URLFilter", "testURL", "(Ljava/net/URL;)Ljava/net/URL;", false));
list.add(new InsnNode(POP));
mn.instructions.insert(list);

View File

@ -1,22 +1,26 @@
package io.zhile.research.ja.netfilter.filters;
package io.zhile.research.ja.netfilter.plugins.url;
import io.zhile.research.ja.netfilter.commons.DebugInfo;
import io.zhile.research.ja.netfilter.models.FilterConfig;
import io.zhile.research.ja.netfilter.models.FilterRule;
import java.io.IOException;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.util.List;
public class URLFilter {
private static final String SECTION_NAME = "URL";
private static List<FilterRule> ruleList;
public static void setRules(List<FilterRule> rules) {
ruleList = rules;
}
public static URL testURL(URL url) throws IOException {
if (null == url) {
if (null == url || null == ruleList) {
return null;
}
for (FilterRule rule : FilterConfig.getBySection(SECTION_NAME)) {
for (FilterRule rule : ruleList) {
if (!rule.test(url.toString())) {
continue;
}

View File

@ -0,0 +1,42 @@
package io.zhile.research.ja.netfilter.plugins.url;
import io.zhile.research.ja.netfilter.models.FilterRule;
import io.zhile.research.ja.netfilter.plugin.MyTransformer;
import io.zhile.research.ja.netfilter.plugin.PluginEntry;
import java.util.ArrayList;
import java.util.List;
public class URLFilterPlugin implements PluginEntry {
private final List<MyTransformer> transformers = new ArrayList<>();
@Override
public void init(List<FilterRule> filterRules) {
transformers.add(new HttpClientTransformer(filterRules));
}
@Override
public String getName() {
return "URL";
}
@Override
public String getAuthor() {
return "neo";
}
@Override
public String getVersion() {
return "v1.0.0";
}
@Override
public String getDescription() {
return "ja-netfilter core: url plugin";
}
@Override
public List<MyTransformer> getTransformers() {
return transformers;
}
}