我正在尝试重新使用一个使用混淆的加载程序的Java应用程序,以从另一个类似归档的混淆文件中加载类。我正在尝试获取这些已加载的类以进行进一步的分析,但是到目前为止,我在弄清存档文件的格式方面还没有任何成功。由于这些类无论如何都已加载到内存中,并且我使用Java探查器来获取特定类和对象的简短列表,这些类和对象很可能包含我要查找的内容,因此有一种方法可以从内存中截取这些类,这是一个核心在装载时或通过任何其他方式转储并检查/保存它们?假设我拥有系统的完全控制权,并且可以以任何方式停止或修改程序。

#1 楼

我建议使用Java代理从正在运行的JVM实例中提取类。代理是一种为应用程序提供检测功能的工具。说到代理,有两种广泛的开发方法:


在纯Java中
在C / C ++中以本机代理的形式。

本机代理比纯Java代理具有更多功能,但就您的目的而言已经足够。在执行流程的某个时刻,类加载器必须加载和解密加密的类。

下面提供了示例代码。每次加载新类时,代理都会注册一个回调(transform方法),以通知该回调。在回调中,我们仅将类的内容转储到磁盘。

import java.io.*;
import java.lang.instrument.*;
import java.security.*;

public class dumper
{
  //A java agent must have a premain method which acts as the entry-point
  public static void premain(String agentArgs, Instrumentation inst)
  {
    System.out.println("agent loaded");

    // Register our transformer
    inst.addTransformer(new transformer());    
  }
}

class transformer implements ClassFileTransformer
{
   // The transform method is called for each non-system class as they are being loaded  
   public byte[] transform(ClassLoader loader, String className, 
                           Class<?> classBeingRedefined, ProtectionDomain protectionDomain, 
                           byte[] classfileBuffer) throws IllegalClassFormatException
   {
     if (className != null)
     {
       // Skip all system classes
       if (!className.startsWith("java") && 
           !className.startsWith("sun") && 
           !className.startsWith("javax") && 
           !className.startsWith ("com") && 
           !className.startsWith("jdk") && 
           !className.startsWith("org"))
       {
         System.out.println("Dumping: " + className);

         // Replace all separator charactors
         String newName = className.replaceAll("/", "#") + ".class";

         try
         {
           FileOutputStream fos = new FileOutputStream(newName);
           fos.write(classfileBuffer);
           fos.close();
         }
         catch (Exception ex)
         {
           System.out.println("Exception while writing: " + newName);
         } 
       }
     }
     // We are not modifying the bytecode in anyway, so return it as-is
     return classfileBuffer;
   }
 }


我在这里对此过程进行了介绍:反转混淆的Java恶意软件

评论


这正是我一直在寻找的东西,对于我想做的其他事情,代理商通常是极大的帮助。谢谢!

– Nickanallen
17-2-18在16:28



如果您无法停止JVM并挂接Java代理怎么办?

– Sajuuk
19年5月5日在9:36

#2 楼

您可以在运行时使用HotSpot工具转储字节码,并使用反编译器反转字节码。我做了一个概念证明,在这里可用

它需要3个依赖项:


JDK库(sa-jdi.jar,tools.jar)用于转储字节码

Fernflower将字节码反编译为Java代码

RSyntaxTextArea显示Java源代码

还可以看看HSDB实用程序

#3 楼

我认为最简单的方法是在加载类时转储类,假设您知道混淆的类加载器在哪里并且可以对其进行编辑。

混淆的类加载器最终将不得不调用ClassLoader.defineClass在某一点。如果有类文件,则可以对其进行反汇编,找到对defineClass的所有调用(或可能会调用defineClass的反射方法),并在调用之前插入一个日志记录函数以转储结果。您可以为此使用Krakatau的反汇编程序/汇编程序。

最大的风险是,如果混淆的代码正在使用内省功能,则可能使事情变得混乱。例如,代码可能会计算其自身的校验和,在这种情况下,对其进行修改将导致问题。

如果有这种情况,您有两个选择:1)找到所有自省检查并删除/ modify / mock出来,或2)修改JVM本身。后一种方法对于混淆后的代码将是完全不可见的,但很难做到。

尝试尝试的另一件事是查看Java Agent接口。我本人还没有使用过它,所以我就不多说了,但这可能使您可以在运行时访问所有已加载的类,而无需进行代码修改。