所以我有一个类加载器 (MyClassLoader),它在内存中维护一组“特殊”类。这些特殊类被动态编译并存储在 MyClassLoader 内部的字节数组中。当 MyClassLoader 被请求一个类时,它首先检查它的 specialClasses 是否在委托(delegate)给系统类加载器之前,字典包含它。它看起来像这样:
class MyClassLoader extends ClassLoader {
Map<String, byte[]> specialClasses;
public MyClassLoader(Map<String, byte[]> sb) {
this.specialClasses = sb;
}
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
if (specialClasses.containsKey(name)) return findClass(name);
else return super.loadClass(name);
}
@Override
public Class findClass(String name) {
byte[] b = specialClasses.get(name);
return defineClass(name, b, 0, b.length);
}
}
如果我想对 specialClasses 执行转换(例如检测) , 我可以通过修改 byte[] 来做到这一点在我打电话之前 defineClass()在上面。
我还想转换系统类加载器提供的类,但系统类加载器似乎没有提供任何访问原始 byte[] 的方法。它提供的类,并给了我 Class直接对象。
我可以使用 -javaagent检测加载到 JVM 中的所有类,但这会增加我不想检测的类的开销;我真的只希望对 MyClassLoader 加载的类进行检测。
byte[]父类加载器提供的类,所以我可以在定义自己的副本之前检测它们? byte[] 的位置而言。来自,以便 MyClassLoader 可以检测和定义它自己的所有系统类(对象、字符串等)的副本?编辑:
所以我尝试了另一种方法:
-javaagent , 捕获 byte[]加载的每个类并将其存储在哈希表中,以类名作为键。理论上,这会让 MyClassLoader 定义它自己的系统类版本,并带有检测。但是,它失败了
java.lang.SecurityException: Prohibited package name: java.lang
显然 JVM 不喜欢我定义 java.lang我自己上课,即使它(理论上)应该来自同一个 byte[]引导加载的类应该来自的来源。继续寻找解决方案。
编辑2:
我为这个问题找到了一个(非常粗略的)解决方案,但如果有人比我更了解 Java 类加载/检测的复杂性,可以想出一些不那么粗略的方法,那就太棒了。
最佳答案
所以我找到了解决这个问题的方法。这不是一个非常优雅的解决方案,它会在代码审查时引起很多愤怒的电子邮件,但它似乎有效。基本要点是:
使用 java.lang.instrumentation和一个 -javaagent存储 Instrumentation稍后使用的对象
class JavaAgent {
private JavaAgent() {}
public static void premain(String agentArgs, Instrumentation inst) {
System.out.println("Agent Premain Start");
Transformer.instrumentation = inst;
inst.addTransformer(new Transformer(), inst.isRetransformClassesSupported());
}
}
添加 Transformer到 Instrumentation仅适用于标记的类。有点像
public class Transformer implements ClassFileTransformer {
public static Set<Class<?>> transformMe = new Set<>()
public static Instrumentation instrumentation = null; // set during premain()
@Override
public byte[] transform(ClassLoader loader,
String className,
Class<?> classBeingRedefined,
ProtectionDomain protectionDomain,
byte[] origBytes) {
if (transformMe.contains(classBeingRedefined)) {
return instrument(origBytes, loader);
} else {
return null;
}
}
public byte[] instrument(byte[] origBytes) {
// magic happens here
}
}
在类加载器中,显式标记每个加载的类(甚至是加载委托(delegate)给父类的类),方法是将其放置在 transformMe 中。在询问 Instrumentation 之前改造它
public class MyClassLoader extends ClassLoader{
public Class<?> instrument(Class<?> in){
try{
Transformer.transformMe.add(in);
Transformer.instrumentation.retransformClasses(in);
Transformer.transformMe.remove(in);
return in;
}catch(Exception e){ return null; }
}
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
return instrument(super.loadClass(name));
}
}
...瞧! MyClassLoader 加载的每个类被 instrument() 转化方法,包括所有系统类,如 java.lang.Object和 friend ,而所有由默认 ClassLoader 加载的类都保持不变。
我已经尝试使用内存分析 instrument()方法,它插入回调 Hook 以跟踪检测字节码中的内存分配,并可以确认 MyClassLoad类在它们的方法运行时触发回调(甚至系统类),而“普通”类则没有。
胜利!
当然,这是糟糕的代码。无处不在的共享可变状态、非本地副作用、全局变量,以及您可能想象到的一切。可能也不是线程安全的。但它表明这样的事情是可能的,您可以确实有选择地检测类的字节码,甚至是系统类,作为自定义 ClassLoader 操作的一部分,同时让程序的“其余部分”保持不变。
如果其他人有任何想法如何让这段代码不那么糟糕,我会很高兴听到。我想不出一个办法:
Instrumentation 仅 通过retransformClasses()按需提供的乐器类(class)并且不以其他方式加载仪器类Class<?> 中存储一些元数据允许 Transformer 的对象在没有全局可变哈希表查找的情况下判断它是否应该被转换。Instrumentation.retransformClass() 的情况下转换系统类方法。如前所述,任何动态 defineClass 的尝试一个byte[]进入 java.lang.*由于 ClassLoader.java 中的硬编码检查,类失败。如果有人能找到解决这些问题中的任何一个的方法,它就会变得不那么粗略。无论如何,我猜测能够检测(例如用于分析)某些子系统(即您感兴趣的子系统),同时保持 JVM 的其余部分不变(没有检测开销)将对其他人有用我,就在这里。
关于java - Java Classloader 能否重写系统类(仅它们的副本)的字节码?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13032918/
我正在使用的第三方API的文档状态:"[O]urAPIonlyacceptspaddedBase64encodedstrings."什么是“填充的Base64编码字符串”以及如何在Ruby中生成它们。下面的代码是我第一次尝试创建转换为Base64的JSON格式数据。xa=Base64.encode64(a.to_json) 最佳答案 他们说的padding其实就是Base64本身的一部分。它是末尾的“=”和“==”。Base64将3个字节的数据包编码为4个编码字符。所以如果你的输入数据有长度n和n%3=1=>"=="末尾用于填充n%
我真的很习惯使用Ruby编写以下代码:my_hash={}my_hash['test']=1Java中对应的数据结构是什么? 最佳答案 HashMapmap=newHashMap();map.put("test",1);我假设? 关于java-等价于Java中的RubyHash,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/22737685/
我正在尝试使用boilerpipe来自JRuby。我看过guide从JRuby调用Java,并成功地将它与另一个Java包一起使用,但无法弄清楚为什么同样的东西不能用于boilerpipe。我正在尝试基本上从JRuby中执行与此Java等效的操作:URLurl=newURL("http://www.example.com/some-location/index.html");Stringtext=ArticleExtractor.INSTANCE.getText(url);在JRuby中试过这个:require'java'url=java.net.URL.new("http://www
我只想对我一直在思考的这个问题有其他意见,例如我有classuser_controller和classuserclassUserattr_accessor:name,:usernameendclassUserController//dosomethingaboutanythingaboutusersend问题是我的User类中是否应该有逻辑user=User.newuser.do_something(user1)oritshouldbeuser_controller=UserController.newuser_controller.do_something(user1,user2)我
什么是ruby的rack或python的Java的wsgi?还有一个路由库。 最佳答案 来自Python标准PEP333:Bycontrast,althoughJavahasjustasmanywebapplicationframeworksavailable,Java's"servlet"APImakesitpossibleforapplicationswrittenwithanyJavawebapplicationframeworktoruninanywebserverthatsupportstheservletAPI.ht
导读语言模型给我们的生产生活带来了极大便利,但同时不少人也利用他们从事作弊工作。如何规避这些难辨真伪的文字所产生的负面影响也成为一大难题。在3月9日智源Live第33期活动「DetectGPT:判断文本是否为机器生成的工具」中,主讲人Eric为我们讲解了DetectGPT工作背后的思路——一种基于概率曲率检测的用于检测模型生成文本的工具,它可以帮助我们更好地分辨文章的来源和可信度,对保护信息真实、防止欺诈等方面具有重要意义。本次报告主要围绕其功能,实现和效果等展开。(文末点击“阅读原文”,查看活动回放。)Ericmitchell斯坦福大学计算机系四年级博士生,由ChelseaFinn和Chri
电脑0x0000001A蓝屏错误怎么U盘重装系统教学分享。有用户电脑开机之后遇到了系统蓝屏的情况。系统蓝屏问题很多时候都是系统bug,只有通过重装系统来进行解决。那么蓝屏问题如何通过U盘重装新系统来解决呢?来看看以下的详细操作方法教学吧。 准备工作: 1、U盘一个(尽量使用8G以上的U盘)。 2、一台正常联网可使用的电脑。 3、ghost或ISO系统镜像文件(Win10系统下载_Win10专业版_windows10正式版下载-系统之家)。 4、在本页面下载U盘启动盘制作工具:系统之家U盘启动工具。 U盘启动盘制作步骤: 注意:制作期间,U盘会被格式化,因此U盘中的重要文件请注
我正在使用Ruby,我正在与一个网络端点通信,该端点在发送消息本身之前需要格式化“header”。header中的第一个字段必须是消息长度,它被定义为网络字节顺序中的2二进制字节消息长度。比如我的消息长度是1024。如何将1024表示为二进制双字节? 最佳答案 Ruby(以及Perl和Python等)中字节整理的标准工具是pack和unpack。ruby的packisinArray.您的长度应该是两个字节长,并且按网络字节顺序排列,这听起来像是n格式说明符的工作:n|Integer|16-bitunsigned,network(bi
在应用开发中,有时候我们需要获取系统的设备信息,用于数据上报和行为分析。那在鸿蒙系统中,我们应该怎么去获取设备的系统信息呢,比如说获取手机的系统版本号、手机的制造商、手机型号等数据。1、获取方式这里分为两种情况,一种是设备信息的获取,一种是系统信息的获取。1.1、获取设备信息获取设备信息,鸿蒙的SDK包为我们提供了DeviceInfo类,通过该类的一些静态方法,可以获取设备信息,DeviceInfo类的包路径为:ohos.system.DeviceInfo.具体的方法如下:ModifierandTypeMethodDescriptionstatic StringgetAbiList()Obt
这篇文章是继上一篇文章“Observability:从零开始创建Java微服务并监控它(一)”的续篇。在上一篇文章中,我们讲述了如何创建一个Javaweb应用,并使用Filebeat来收集应用所生成的日志。在今天的文章中,我来详述如何收集应用的指标,使用APM来监控应用并监督web服务的在线情况。源码可以在地址 https://github.com/liu-xiao-guo/java_observability 进行下载。摄入指标指标被视为可以随时更改的时间点值。当前请求的数量可以改变任何毫秒。你可能有1000个请求的峰值,然后一切都回到一个请求。这也意味着这些指标可能不准确,你还想提取最小/