rename package && separate builtin plugins

Signed-off-by: pengzhile <pengzhile@gmail.com>
This commit is contained in:
pengzhile 2021-12-06 10:49:40 +08:00
parent 7057cd3394
commit e622c8fed4
35 changed files with 111 additions and 381 deletions

View File

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

View File

@ -4,9 +4,9 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>io.zhile.research</groupId>
<groupId>com.ja-netfilter</groupId>
<artifactId>ja-netfilter</artifactId>
<version>1.1.6</version>
<version>1.2.0</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
@ -48,8 +48,8 @@
</manifest>
<manifestEntries>
<Built-By>neo</Built-By>
<Premain-Class>io.zhile.research.ja.netfilter.Launcher</Premain-Class>
<Main-Class>io.zhile.research.ja.netfilter.Launcher</Main-Class>
<Premain-Class>com.janetfilter.core.Launcher</Premain-Class>
<Main-Class>com.janetfilter.core.Launcher</Main-Class>
<Can-Redefine-Classes>true</Can-Redefine-Classes>
<Can-Retransform-Classes>true</Can-Retransform-Classes>
</manifestEntries>

View File

@ -1,28 +1,21 @@
package io.zhile.research.ja.netfilter;
package com.janetfilter.core;
import io.zhile.research.ja.netfilter.commons.DebugInfo;
import io.zhile.research.ja.netfilter.plugin.MyTransformer;
import com.janetfilter.core.commons.DebugInfo;
import com.janetfilter.core.plugin.MyTransformer;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.security.ProtectionDomain;
import java.util.*;
public class Dispatcher implements ClassFileTransformer {
private static Dispatcher INSTANCE;
public final class Dispatcher implements ClassFileTransformer {
private final Set<String> classSet = new TreeSet<>();
private final Map<String, List<MyTransformer>> transformerMap = new HashMap<>();
public static synchronized Dispatcher getInstance() {
if (null == INSTANCE) {
INSTANCE = new Dispatcher();
}
return INSTANCE;
}
public void addTransformer(MyTransformer transformer) {
List<MyTransformer> transformers = transformerMap.computeIfAbsent(transformer.getHookClassName(), k -> new ArrayList<>());
String className = transformer.getHookClassName();
classSet.add(className.replace('/', '.'));
List<MyTransformer> transformers = transformerMap.computeIfAbsent(className, k -> new ArrayList<>());
transformers.add(transformer);
}
@ -46,7 +39,7 @@ public class Dispatcher implements ClassFileTransformer {
}
public Set<String> getHookClassNames() {
return transformerMap.keySet();
return classSet;
}
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classFileBuffer) throws IllegalClassFormatException {

View File

@ -0,0 +1,27 @@
package com.janetfilter.core;
import java.io.File;
public final class Environment {
private final File baseDir;
private final File agentFile;
private final File pluginsDir;
public Environment(File agentFile) {
this.agentFile = agentFile;
baseDir = agentFile.getParentFile();
pluginsDir = new File(baseDir, "plugins");
}
public File getBaseDir() {
return baseDir;
}
public File getAgentFile() {
return agentFile;
}
public File getPluginsDir() {
return pluginsDir;
}
}

View File

@ -1,18 +1,18 @@
package io.zhile.research.ja.netfilter;
package com.janetfilter.core;
import io.zhile.research.ja.netfilter.commons.ConfigDetector;
import io.zhile.research.ja.netfilter.commons.ConfigParser;
import io.zhile.research.ja.netfilter.commons.DebugInfo;
import io.zhile.research.ja.netfilter.models.FilterConfig;
import io.zhile.research.ja.netfilter.plugin.PluginManager;
import com.janetfilter.core.commons.ConfigDetector;
import com.janetfilter.core.commons.ConfigParser;
import com.janetfilter.core.commons.DebugInfo;
import com.janetfilter.core.models.FilterConfig;
import com.janetfilter.core.plugin.PluginManager;
import java.io.File;
import java.lang.instrument.Instrumentation;
import java.util.Set;
public class Initializer {
public static void init(String args, Instrumentation inst, File currentDirectory) {
File configFile = ConfigDetector.detect(currentDirectory, args);
public static void init(String args, Instrumentation inst, Environment environment) {
File configFile = ConfigDetector.detect(environment.getBaseDir(), args);
if (null == configFile) {
DebugInfo.output("Could not find any configuration files.");
} else {
@ -25,14 +25,15 @@ public class Initializer {
DebugInfo.output(e.getMessage());
}
PluginManager.getInstance().loadPlugins(inst, currentDirectory);
Dispatcher dispatcher = new Dispatcher();
new PluginManager(dispatcher, environment).loadPlugins(inst);
inst.addTransformer(Dispatcher.getInstance(), true);
inst.addTransformer(dispatcher, true);
Set<String> classSet = Dispatcher.getInstance().getHookClassNames();
Set<String> classSet = dispatcher.getHookClassNames();
for (Class<?> c : inst.getAllLoadedClasses()) {
String name = c.getName();
if (!classSet.contains(name.replace('.', '/'))) {
if (!classSet.contains(name)) {
continue;
}

View File

@ -1,6 +1,6 @@
package io.zhile.research.ja.netfilter;
package com.janetfilter.core;
import io.zhile.research.ja.netfilter.commons.DebugInfo;
import com.janetfilter.core.commons.DebugInfo;
import java.io.File;
import java.lang.instrument.Instrumentation;
@ -9,7 +9,7 @@ import java.net.URL;
import java.util.jar.JarFile;
public class Launcher {
private static final String VERSION = "v1.1.6";
private static final String VERSION = "v1.2.0";
public static void main(String[] args) {
printUsage();
@ -26,16 +26,15 @@ public class Launcher {
return;
}
File currentFile = new File(jarURI.getPath());
File currentDirectory = currentFile.getParentFile();
File agentFile = new File(jarURI.getPath());
try {
inst.appendToBootstrapClassLoaderSearch(new JarFile(currentFile));
inst.appendToBootstrapClassLoaderSearch(new JarFile(agentFile));
} catch (Throwable e) {
DebugInfo.output("ERROR: Can not access ja-netfilter jar file.");
return;
}
Initializer.init(args, inst, currentDirectory); // for some custom UrlLoaders
Initializer.init(args, inst, new Environment(agentFile)); // for some custom UrlLoaders
}
private static void printUsage() {
@ -45,7 +44,7 @@ public class Launcher {
"\n\n" +
" A javaagent lib for network filter :)\n" +
"\n" +
" https://github.com/pengzhile/ja-netfilter\n" +
" https://github.com/ja-netfilter/ja-netfilter\n" +
"\n" +
" ============================================================================ \n\n";
@ -59,7 +58,7 @@ public class Launcher {
return url.toURI();
}
String resourcePath = "/442fcf28466515a81d5434931496ffa64611cc8e.txt";
String resourcePath = "/b7e909d6ba41ae03fb85af5b8ba702709f5798cf.txt";
url = Launcher.class.getResource(resourcePath);
if (null == url) {
throw new Exception("Can not locate resource file.");

View File

@ -1,6 +1,6 @@
package io.zhile.research.ja.netfilter.commons;
package com.janetfilter.core.commons;
import io.zhile.research.ja.netfilter.utils.StringUtils;
import com.janetfilter.core.utils.StringUtils;
import java.io.File;

View File

@ -1,7 +1,7 @@
package io.zhile.research.ja.netfilter.commons;
package com.janetfilter.core.commons;
import io.zhile.research.ja.netfilter.models.FilterRule;
import io.zhile.research.ja.netfilter.utils.StringUtils;
import com.janetfilter.core.models.FilterRule;
import com.janetfilter.core.utils.StringUtils;
import java.io.BufferedReader;
import java.io.File;

View File

@ -1,6 +1,6 @@
package io.zhile.research.ja.netfilter.commons;
package com.janetfilter.core.commons;
import io.zhile.research.ja.netfilter.utils.DateUtils;
import com.janetfilter.core.utils.DateUtils;
public class DebugInfo {
private static final boolean DEBUG = "1".equals(System.getenv("JANF_DEBUG")) || "1".equals(System.getProperty("janf.debug"));

View File

@ -1,6 +1,6 @@
package io.zhile.research.ja.netfilter.enums;
package com.janetfilter.core.enums;
import io.zhile.research.ja.netfilter.rulers.*;
import com.janetfilter.core.rulers.*;
public enum RuleType {
PREFIX(new PrefixRuler()),

View File

@ -1,4 +1,4 @@
package io.zhile.research.ja.netfilter.models;
package com.janetfilter.core.models;
import java.util.ArrayList;
import java.util.List;

View File

@ -1,6 +1,6 @@
package io.zhile.research.ja.netfilter.models;
package com.janetfilter.core.models;
import io.zhile.research.ja.netfilter.enums.RuleType;
import com.janetfilter.core.enums.RuleType;
import java.util.HashMap;
import java.util.Map;

View File

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

View File

@ -1,4 +1,4 @@
package io.zhile.research.ja.netfilter.plugin;
package com.janetfilter.core.plugin;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
@ -6,7 +6,7 @@ import java.io.InputStream;
import java.util.jar.JarFile;
import java.util.zip.ZipEntry;
public class PluginClassLoader extends ClassLoader {
public final class PluginClassLoader extends ClassLoader {
private final JarFile jarFile;
public PluginClassLoader(JarFile jarFile) {

View File

@ -1,11 +1,12 @@
package io.zhile.research.ja.netfilter.plugin;
package com.janetfilter.core.plugin;
import io.zhile.research.ja.netfilter.models.FilterRule;
import com.janetfilter.core.Environment;
import com.janetfilter.core.models.FilterRule;
import java.util.List;
public interface PluginEntry {
default void init(List<FilterRule> filterRules) {
default void init(Environment environment, List<FilterRule> filterRules) {
// get plugin config
}

View File

@ -1,11 +1,10 @@
package io.zhile.research.ja.netfilter.plugin;
package com.janetfilter.core.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.plugins.dns.DNSFilterPlugin;
import io.zhile.research.ja.netfilter.plugins.url.URLFilterPlugin;
import io.zhile.research.ja.netfilter.utils.StringUtils;
import com.janetfilter.core.Dispatcher;
import com.janetfilter.core.Environment;
import com.janetfilter.core.commons.DebugInfo;
import com.janetfilter.core.models.FilterConfig;
import com.janetfilter.core.utils.StringUtils;
import java.io.File;
import java.lang.instrument.Instrumentation;
@ -15,22 +14,19 @@ import java.util.List;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
public class PluginManager {
private static final String PLUGINS_DIR = "plugins";
public final class PluginManager {
private static final String ENTRY_NAME = "JANF-Plugin-Entry";
private static PluginManager INSTANCE;
private final Dispatcher dispatcher;
private final Environment environment;
public static synchronized PluginManager getInstance() {
if (null == INSTANCE) {
INSTANCE = new PluginManager();
}
return INSTANCE;
public PluginManager(Dispatcher dispatcher, Environment environment) {
this.dispatcher = dispatcher;
this.environment = environment;
}
public void loadPlugins(Instrumentation inst, File currentDirectory) {
for (Class<? extends PluginEntry> klass : getAllPluginClasses(inst, currentDirectory)) {
public void loadPlugins(Instrumentation inst) {
for (Class<? extends PluginEntry> klass : getAllPluginClasses(inst)) {
try {
addPluginEntry(klass);
} catch (Throwable e) {
@ -39,13 +35,11 @@ public class PluginManager {
}
}
private List<Class<? extends PluginEntry>> getAllPluginClasses(Instrumentation inst, File currentDirectory) {
private List<Class<? extends PluginEntry>> getAllPluginClasses(Instrumentation inst) {
List<Class<? extends PluginEntry>> classes = new ArrayList<>();
classes.add(DNSFilterPlugin.class);
classes.add(URLFilterPlugin.class);
do {
File pluginsDirectory = new File(currentDirectory, PLUGINS_DIR);
File pluginsDirectory = environment.getPluginsDir();
if (!pluginsDirectory.exists() || !pluginsDirectory.isDirectory()) {
break;
}
@ -84,8 +78,8 @@ public class PluginManager {
private void addPluginEntry(Class<? extends PluginEntry> entryClass) throws Exception {
PluginEntry pluginEntry = entryClass.newInstance();
pluginEntry.init(FilterConfig.getBySection(pluginEntry.getName()));
Dispatcher.getInstance().addTransformers(pluginEntry.getTransformers());
pluginEntry.init(environment, FilterConfig.getBySection(pluginEntry.getName()));
dispatcher.addTransformers(pluginEntry.getTransformers());
DebugInfo.output("Plugin loaded: {name=" + pluginEntry.getName() + ", version=" + pluginEntry.getVersion() + ", author=" + pluginEntry.getAuthor() + "}");
}

View File

@ -1,4 +1,4 @@
package io.zhile.research.ja.netfilter.rulers;
package com.janetfilter.core.rulers;
public class EqualICRuler implements Ruler {
@Override

View File

@ -1,4 +1,4 @@
package io.zhile.research.ja.netfilter.rulers;
package com.janetfilter.core.rulers;
public class EqualRuler implements Ruler {
@Override

View File

@ -1,4 +1,4 @@
package io.zhile.research.ja.netfilter.rulers;
package com.janetfilter.core.rulers;
public class KeywordICRuler implements Ruler {
@Override

View File

@ -1,4 +1,4 @@
package io.zhile.research.ja.netfilter.rulers;
package com.janetfilter.core.rulers;
public class KeywordRuler implements Ruler {
@Override

View File

@ -1,4 +1,4 @@
package io.zhile.research.ja.netfilter.rulers;
package com.janetfilter.core.rulers;
public class PrefixICRuler implements Ruler {
@Override

View File

@ -1,4 +1,4 @@
package io.zhile.research.ja.netfilter.rulers;
package com.janetfilter.core.rulers;
public class PrefixRuler implements Ruler {
@Override

View File

@ -1,4 +1,4 @@
package io.zhile.research.ja.netfilter.rulers;
package com.janetfilter.core.rulers;
import java.util.regex.Pattern;

View File

@ -1,4 +1,4 @@
package io.zhile.research.ja.netfilter.rulers;
package com.janetfilter.core.rulers;
public interface Ruler {
default boolean test(String rule, String content) {

View File

@ -1,4 +1,4 @@
package io.zhile.research.ja.netfilter.rulers;
package com.janetfilter.core.rulers;
public class SuffixICRuler implements Ruler {
@Override

View File

@ -1,4 +1,4 @@
package io.zhile.research.ja.netfilter.rulers;
package com.janetfilter.core.rulers;
public class SuffixRuler implements Ruler {
@Override

View File

@ -1,4 +1,4 @@
package io.zhile.research.ja.netfilter.utils;
package com.janetfilter.core.utils;
import java.text.DateFormat;
import java.text.ParseException;

View File

@ -1,4 +1,4 @@
package io.zhile.research.ja.netfilter.utils;
package com.janetfilter.core.utils;
public class StringUtils {
public static boolean isEmpty(String str) {

View File

@ -1,50 +0,0 @@
package io.zhile.research.ja.netfilter.plugins.dns;
import io.zhile.research.ja.netfilter.commons.DebugInfo;
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 List<FilterRule> ruleList;
public static void setRules(List<FilterRule> rules) {
ruleList = rules;
}
public static String testQuery(String host) throws IOException {
if (null == host || null == ruleList) {
return null;
}
for (FilterRule rule : ruleList) {
if (!rule.test(host)) {
continue;
}
DebugInfo.output("Reject dns query: " + host + ", rule: " + rule);
throw new java.net.UnknownHostException();
}
return host;
}
public static Object testReachable(InetAddress n) throws IOException {
if (null == n || null == ruleList) {
return null;
}
for (FilterRule rule : ruleList) {
if (!rule.test(n.getHostName())) {
continue;
}
DebugInfo.output("Reject dns reachable test: " + n.getHostName() + ", rule: " + rule);
return false;
}
return null;
}
}

View File

@ -1,42 +0,0 @@
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,67 +0,0 @@
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";
}
@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);
for (MethodNode m : node.methods) {
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/plugins/dns/DNSFilter", "testQuery", "(Ljava/lang/String;)Ljava/lang/String;", false));
list.add(new InsnNode(POP));
m.instructions.insert(list);
continue;
}
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/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));
LabelNode label1 = new LabelNode();
list.add(new JumpInsnNode(IF_ACMPEQ, label1));
list.add(new InsnNode(ICONST_0));
list.add(new InsnNode(IRETURN));
list.add(label1);
m.instructions.insert(list);
}
}
ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
node.accept(writer);
return writer.toByteArray();
}
}

View File

@ -1,50 +0,0 @@
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";
}
@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);
for (MethodNode mn : node.methods) {
if ("openServer".equals(mn.name) && "()V".equals(mn.desc)) {
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/plugins/url/URLFilter", "testURL", "(Ljava/net/URL;)Ljava/net/URL;", false));
list.add(new InsnNode(POP));
mn.instructions.insert(list);
}
}
ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
node.accept(writer);
return writer.toByteArray();
}
}

View File

@ -1,34 +0,0 @@
package io.zhile.research.ja.netfilter.plugins.url;
import io.zhile.research.ja.netfilter.commons.DebugInfo;
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 List<FilterRule> ruleList;
public static void setRules(List<FilterRule> rules) {
ruleList = rules;
}
public static URL testURL(URL url) throws IOException {
if (null == url || null == ruleList) {
return null;
}
for (FilterRule rule : ruleList) {
if (!rule.test(url.toString())) {
continue;
}
DebugInfo.output("Reject url: " + url + ", rule: " + rule);
throw new SocketTimeoutException("connect timed out");
}
return url;
}
}

View File

@ -1,42 +0,0 @@
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;
}
}