diff --git a/README.md b/README.md index c5151f2..49efa86 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# ja-netfilter v1.1.0 +# ja-netfilter v1.1.1 ### A javaagent lib for network filter diff --git a/pom.xml b/pom.xml index d34ede2..cd7c9b2 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.zhile.research ja-netfilter - 1.1.0 + 1.1.1 UTF-8 diff --git a/src/main/java/io/zhile/research/ja/netfilter/Dispatcher.java b/src/main/java/io/zhile/research/ja/netfilter/Dispatcher.java index 5fbee17..1d83da6 100644 --- a/src/main/java/io/zhile/research/ja/netfilter/Dispatcher.java +++ b/src/main/java/io/zhile/research/ja/netfilter/Dispatcher.java @@ -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 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); diff --git a/src/main/java/io/zhile/research/ja/netfilter/Initializer.java b/src/main/java/io/zhile/research/ja/netfilter/Initializer.java index f96250e..5532f14 100644 --- a/src/main/java/io/zhile/research/ja/netfilter/Initializer.java +++ b/src/main/java/io/zhile/research/ja/netfilter/Initializer.java @@ -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()); } diff --git a/src/main/java/io/zhile/research/ja/netfilter/Launcher.java b/src/main/java/io/zhile/research/ja/netfilter/Launcher.java index 9fb5de3..3729d5f 100644 --- a/src/main/java/io/zhile/research/ja/netfilter/Launcher.java +++ b/src/main/java/io/zhile/research/ja/netfilter/Launcher.java @@ -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; } diff --git a/src/main/java/io/zhile/research/ja/netfilter/transformers/MyTransformer.java b/src/main/java/io/zhile/research/ja/netfilter/plugin/MyTransformer.java similarity index 78% rename from src/main/java/io/zhile/research/ja/netfilter/transformers/MyTransformer.java rename to src/main/java/io/zhile/research/ja/netfilter/plugin/MyTransformer.java index 5b2d6f8..6053739 100644 --- a/src/main/java/io/zhile/research/ja/netfilter/transformers/MyTransformer.java +++ b/src/main/java/io/zhile/research/ja/netfilter/plugin/MyTransformer.java @@ -1,4 +1,4 @@ -package io.zhile.research.ja.netfilter.transformers; +package io.zhile.research.ja.netfilter.plugin; public interface MyTransformer { String getHookClassName(); diff --git a/src/main/java/io/zhile/research/ja/netfilter/plugin/PluginEntry.java b/src/main/java/io/zhile/research/ja/netfilter/plugin/PluginEntry.java index 1c9bf75..b5bfd62 100644 --- a/src/main/java/io/zhile/research/ja/netfilter/plugin/PluginEntry.java +++ b/src/main/java/io/zhile/research/ja/netfilter/plugin/PluginEntry.java @@ -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"; } diff --git a/src/main/java/io/zhile/research/ja/netfilter/plugin/PluginManager.java b/src/main/java/io/zhile/research/ja/netfilter/plugin/PluginManager.java index b08a60c..dcbe405 100644 --- a/src/main/java/io/zhile/research/ja/netfilter/plugin/PluginManager.java +++ b/src/main/java/io/zhile/research/ja/netfilter/plugin/PluginManager.java @@ -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 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> getAllPluginClasses(Instrumentation inst, File currentDirectory) { + List> 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) Class.forName(entryClass)); + } catch (Throwable e) { + DebugInfo.output("Load plugin failed: " + e.getMessage()); + } + } + } while (false); + + return classes; + } + + private void addPluginEntry(Class 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() + "}"); + } } diff --git a/src/main/java/io/zhile/research/ja/netfilter/filters/DNSFilter.java b/src/main/java/io/zhile/research/ja/netfilter/plugins/dns/DNSFilter.java similarity index 70% rename from src/main/java/io/zhile/research/ja/netfilter/filters/DNSFilter.java rename to src/main/java/io/zhile/research/ja/netfilter/plugins/dns/DNSFilter.java index dfe0baf..8596524 100644 --- a/src/main/java/io/zhile/research/ja/netfilter/filters/DNSFilter.java +++ b/src/main/java/io/zhile/research/ja/netfilter/plugins/dns/DNSFilter.java @@ -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 ruleList; + + public static void setRules(List 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; } diff --git a/src/main/java/io/zhile/research/ja/netfilter/plugins/dns/DNSFilterPlugin.java b/src/main/java/io/zhile/research/ja/netfilter/plugins/dns/DNSFilterPlugin.java new file mode 100644 index 0000000..6cf8113 --- /dev/null +++ b/src/main/java/io/zhile/research/ja/netfilter/plugins/dns/DNSFilterPlugin.java @@ -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 transformers = new ArrayList<>(); + + @Override + public void init(List 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 getTransformers() { + return transformers; + } +} diff --git a/src/main/java/io/zhile/research/ja/netfilter/transformers/InetAddressTransformer.java b/src/main/java/io/zhile/research/ja/netfilter/plugins/dns/InetAddressTransformer.java similarity index 76% rename from src/main/java/io/zhile/research/ja/netfilter/transformers/InetAddressTransformer.java rename to src/main/java/io/zhile/research/ja/netfilter/plugins/dns/InetAddressTransformer.java index f95410b..920dfe7 100644 --- a/src/main/java/io/zhile/research/ja/netfilter/transformers/InetAddressTransformer.java +++ b/src/main/java/io/zhile/research/ja/netfilter/plugins/dns/InetAddressTransformer.java @@ -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 rules; + + public InetAddressTransformer(List 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)); diff --git a/src/main/java/io/zhile/research/ja/netfilter/transformers/HttpClientTransformer.java b/src/main/java/io/zhile/research/ja/netfilter/plugins/url/HttpClientTransformer.java similarity index 73% rename from src/main/java/io/zhile/research/ja/netfilter/transformers/HttpClientTransformer.java rename to src/main/java/io/zhile/research/ja/netfilter/plugins/url/HttpClientTransformer.java index 60c4303..4be940d 100644 --- a/src/main/java/io/zhile/research/ja/netfilter/transformers/HttpClientTransformer.java +++ b/src/main/java/io/zhile/research/ja/netfilter/plugins/url/HttpClientTransformer.java @@ -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 rules; + + public HttpClientTransformer(List 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); diff --git a/src/main/java/io/zhile/research/ja/netfilter/filters/URLFilter.java b/src/main/java/io/zhile/research/ja/netfilter/plugins/url/URLFilter.java similarity index 66% rename from src/main/java/io/zhile/research/ja/netfilter/filters/URLFilter.java rename to src/main/java/io/zhile/research/ja/netfilter/plugins/url/URLFilter.java index 68ac5cd..37d53cc 100644 --- a/src/main/java/io/zhile/research/ja/netfilter/filters/URLFilter.java +++ b/src/main/java/io/zhile/research/ja/netfilter/plugins/url/URLFilter.java @@ -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 ruleList; + + public static void setRules(List 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; } diff --git a/src/main/java/io/zhile/research/ja/netfilter/plugins/url/URLFilterPlugin.java b/src/main/java/io/zhile/research/ja/netfilter/plugins/url/URLFilterPlugin.java new file mode 100644 index 0000000..faa143f --- /dev/null +++ b/src/main/java/io/zhile/research/ja/netfilter/plugins/url/URLFilterPlugin.java @@ -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 transformers = new ArrayList<>(); + + @Override + public void init(List 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 getTransformers() { + return transformers; + } +}