如何从 RAM 内存中存储和加载 class?

How to store and load a class from the RAM memory?

今天有朋友问我是否可以像用 IDA 反转可执行文件一样反转 jar 文件。

答案显然是"yes",但出于好奇,我需要尝试一下。

IDA free 不允许我反编译 JAR 文件,但我终于找到了 CFR 和 JD。

所以我创建了一个 crackMe.jar 询问我们密码,然后解密 2 个 DLL 并读取它们的输出。

第一个 DLL 的输出是一个 class 文件,用于检查密码是否正确,第二个 DLL 的输出也是一个 class 文件,用于擦除两个输出- 目的是让破解者无法读取它们。

不过这个crackMe还是太容易破解了...破解者只需要修改crackMe.jar的Main.class,保留DLL的输出,然后修改tmp.class 并删除 tmp$del.class.

我想制作另一个 DLL 来检查 Main.class 是否被修改...但我们可以轻松破解它。

这就是为什么我需要在第一时间知道是否可以将文件存储在 RAM 内存中。

为什么我们使用CFR 时源代码在java 中是可读的?我们有没有办法加密 class 并以十六进制执行它而不使用外部程序解密它?

是否可以在内存中提取一些内容并保持对它的访问,即使我们在 java 中没有指针? (在 C++ 中可能吗?)-我已经找到了这个主题:https://unix.stackexchange.com/questions/59300/how-to-place-store-a-file-in-memory-on-linux

我们可以管理内存的访问权限吗?因此,如果程序以管理员身份启动,CPU 是否可以将控制权保留在一部分内存上,只允许程序读取它而不允许用户读取? - https://en.wikipedia.org/wiki/Protection_ring

crackMe.jar的源代码:

public class Main
{
    public static void main(String[] args) throws Exception
    {
        if (new java.io.File(fct("yju!ibq+hifpx")).exists() || new java.io.File(fct("yju+hifpx")).exists())
        {
            new java.io.File(fct("i^y^3aqi")).delete();
            new java.io.File(fct("i^y^d/3aqi")).delete();
            javax.swing.JOptionPane.showMessageDialog(null, fct("Zkf_qb%qttsjo|onqjyju+hifpxto%qrm)aji3`q^xp"));
        }
        String str = javax.swing.JOptionPane.showInputDialog(fct("Jkybw~lzo%mfpxttoi7"));
        byte[] keyBytes = fct("o^{^").getBytes();
        javax.crypto.spec.SecretKeySpec secretKeySpec = new javax.crypto.spec.SecretKeySpec(keyBytes, fct("Gittkfxe"));
        javax.crypto.Cipher cipher = javax.crypto.Cipher.getInstance(fct("Gittkfxe"));
        cipher.init(javax.crypto.Cipher.DECRYPT_MODE, secretKeySpec);
        java.io.File fileTmp1 = new java.io.File(fct("yju!ibq+hifpx"));
        java.io.BufferedInputStream bufferedInputStream = new java.io.BufferedInputStream(new java.io.FileInputStream(fct("i^y^d/3aqi")));
        javax.crypto.CipherOutputStream cipherOutputStream = new javax.crypto.CipherOutputStream(new java.io.BufferedOutputStream(new java.io.FileOutputStream(fileTmp1)), cipher);
        int i;
        if (str != null && str != "")
        {
            do
            {
                i = bufferedInputStream.read();
                if (i != -1)
                    cipherOutputStream.write(i);
            }while (i != -1);
        }
        bufferedInputStream.close();
        cipherOutputStream.close();
        java.io.File fileTmp2 = new java.io.File(fct("yju+hifpx"));
        bufferedInputStream = new java.io.BufferedInputStream(new java.io.FileInputStream(fct("i^y^3aqi")));
        cipherOutputStream = new javax.crypto.CipherOutputStream(new java.io.BufferedOutputStream(new java.io.FileOutputStream(fileTmp2)), cipher);
        if (str != null && str != "")
        {
            do
            {
                i = bufferedInputStream.read();
                if (i != -1)
                    cipherOutputStream.write(i);
            }while (i != -1);
        }
        bufferedInputStream.close();
        cipherOutputStream.close();
        Runtime.getRuntime().exec(fct("o^{^%qrm%") + str);
        Thread.sleep(200);
        fileTmp1.delete();
        fileTmp2.delete();
    }

    private static String fct(String str)
    {
        char[] strC = str.toCharArray();
        for (int i = 0, j = str.length(); i < j; i++)
        {
            if (i % 2 == 0)
                strC[i] -= 5;
            else
                strC[i] += 3;
        }
        return String.valueOf(strC);
    }
}

tmp.class和tmp$del.class

的源代码

它们不在 crackMe.jar 中,而是在名称为 data.dll 和 data_2.dll.

的同一文件夹中

此源代码已加密。

class tmp
{
    private static String str1, str2 = "Iivfv1$Nvfr^$:Ho\}%", str3 = "EGq\"", str4 = "W\xltwkeix\h%";
    private static boolean bool;

    protected static void setBool(boolean b)
    {
        bool = b;
    }

    protected static boolean getBool()
    {
        return bool;
    }

    public static void main(String[] args)
    {
        new tmp(true, args[0]);
    }

    private static void fct1(int index)
    {
        if (index == 0)
            javax.swing.JOptionPane.showMessageDialog(null, fct5(str2));
    }

    private static void fct2(int index)
    {
        if (index == 0)
            javax.swing.JOptionPane.showMessageDialog(null, fct5(str4));
    }

    private static void fct3(int index)
    {
        new del().start();
        if (index == 0 && getBool())
        {
            fct1(0);
        }
        else if (index == 1 && !getBool())
        {
            fct2(0);
        }
    }

    private static boolean fct4()
    {
        return !bool&&!(fct5(str3).equals(str1));
    }

    private static String fct5(String str)
    {
        char[] strC = str.toCharArray();
        for (int i = 0, j = str.length(); i < j; i++)
        {
            if (i % 2 == 0)
                strC[i] -= 4;
            else
                strC[i] += 9;
        }
        return String.valueOf(strC);
    }

    protected tmp(boolean bool, String arg)
    {
        str1 = arg;
        setBool(false);
        if (fct4())
        {
            setBool(true);
            fct3(0);
        }
        else
        {
            fct3(1);
        }
    }

    static class del extends Thread
    {
        public void run()
        {
            try
            {
                Thread.sleep(50);
                byte[] keyBytes = "erase".getBytes();
                javax.crypto.spec.SecretKeySpec secretKeySpec = new javax.crypto.spec.SecretKeySpec(keyBytes, "Blowfish");
                javax.crypto.Cipher cipher = javax.crypto.Cipher.getInstance("Blowfish");
                cipher.init(javax.crypto.Cipher.ENCRYPT_MODE, secretKeySpec);
                java.io.BufferedInputStream bufferedInputStream = new java.io.BufferedInputStream(new java.io.FileInputStream("tmp.class"));
                javax.crypto.CipherOutputStream cipherOutputStream = new javax.crypto.CipherOutputStream(new java.io.BufferedOutputStream(new java.io.FileOutputStream("tmp.class")), cipher);
                int i;
                do
                {
                    i = bufferedInputStream.read();
                    if (i != -1)
                        cipherOutputStream.write(i);
                }while (i != -1);
                bufferedInputStream.close();
                cipherOutputStream.close();
                bufferedInputStream = new java.io.BufferedInputStream(new java.io.FileInputStream("tmp$del.class"));
                cipherOutputStream = new javax.crypto.CipherOutputStream(new java.io.BufferedOutputStream(new java.io.FileOutputStream("tmp$del.class")), cipher);
                do
                {
                    i = bufferedInputStream.read();
                    if (i != -1)
                        cipherOutputStream.write(i);
                }while (i != -1);
                bufferedInputStream.close();
                cipherOutputStream.close();
            }
            catch (Exception e)
            {

            }
        }
    }
}

我用来创建 DLL 文件的加密器的源代码

这个程序永远不会离开我的电脑所以黑客无法读取它。

我们只是用它来加密 tmp.class 和 tmp$del.class 到 data.dll 和 data_2.dll.

public class encrypt
{
    public static void main(String[] args) throws Exception
    {
        byte[] keyBytes = "java".getBytes();
        javax.crypto.spec.SecretKeySpec secretKeySpec = new javax.crypto.spec.SecretKeySpec(keyBytes, "Blowfish");
        javax.crypto.Cipher cipher = javax.crypto.Cipher.getInstance("Blowfish");
        cipher.init(javax.crypto.Cipher.ENCRYPT_MODE, secretKeySpec);

        java.io.BufferedInputStream bufferedInputStream = new java.io.BufferedInputStream(new java.io.FileInputStream("tmp.class"));
        javax.crypto.CipherOutputStream cipherOutputStream = new javax.crypto.CipherOutputStream(new java.io.BufferedOutputStream(new java.io.FileOutputStream("data.dll")), cipher);
        int i;
        do
        {
            i = bufferedInputStream.read();
            if (i != -1)
                cipherOutputStream.write(i);
        }while (i != -1);
        bufferedInputStream.close();
        cipherOutputStream.close();

        bufferedInputStream = new java.io.BufferedInputStream(new java.io.FileInputStream("tmp$del.class"));
        cipherOutputStream = new javax.crypto.CipherOutputStream(new java.io.BufferedOutputStream(new java.io.FileOutputStream("data_2.dll")), cipher);
        do
        {
            i = bufferedInputStream.read();
            if (i != -1)
                cipherOutputStream.write(i);
        }while (i != -1);
        bufferedInputStream.close();
        cipherOutputStream.close();

        System.exit(0);
    }
}

是的,可以从内存资源中加载 Class。您需要编写自定义类加载器来执行此操作,但该类加载器可以将字节码加载到 byte[] 中并调用 (protected) defineClass 方法之一。

但是,我不确定这会为您带来什么:

  • 如果您试图在代码 运行 在您的计算机上时对其进行保护,则完全没有必要。只需阻止饼干进入您的机器即可。 (但如果你做不到,那你就完蛋了;见下文。)

  • 如果您试图在 运行 在破解者的机器上保护您的代码,那么这将无济于事。破解者需要做的就是启动调试器,在 byte[] 中的解密字节码准备好调用 defineClass 的位置设置断点。

无论你做什么,如果破解者 "owns" 执行平台,他们就可以获得在其上运行的应用程序的字节码。 (这同样适用于任何本机代码库。)