反编译 .NET 封送代码

逆向工程 拆卸 恶意软件 。网
2021-07-09 10:49:11

我正在尝试分析一个 .NET 恶意软件样本,它或多或少是这样的:

internal static class Class1
{
    public static byte[] Code = new byte[]
    {
        9,
        249,
        131,
        225,
        ...,
    }

    private static void Main()
    {  
        // AFAIU this copies the marshaled code in Code to freshly allocated memory
        IntPtr ptr = Class1.Alloc((uint)Class1.Code.Length);
        for (int i = 0; i < Class1.Code.Length; i++)
        {
            int num = (int)Marshal.ReadByte(Class1.Code, i);
            Marshal.WriteByte(ptr, i, (byte)num);
        }

        Class1.newObject newObject = (Class1.newObject)Marshal.GetDelegateForFunctionPointer(ptr, typeof(Class1.newObject));

        object.Equals(null, null);
        [...snip...]
        object.Equals(null, null); // probably some timing mechanism

        Marshal.GetFunctionPointerForDelegate(newObject);
        newObject();
    }
}

我试图找到 newObject() 背后的代码,它显然是从封送代码中实例化的。我不是 .Net 专家,但从我读到的编组代码是某种序列化,可以应用于对象以通过二进制通道(例如 TCP 连接)传输功能代码。据我所知,我应该能够将其反转为可理解的,或者至少类似于字节码的格式。

我尝试过像 ILSpy 和 dotPeek 这样的软件,但它们似乎没有将代码识别Code为托管代码,并且没有提供可用的输出。

2个回答

我试图找到 newObject() 背后的代码,它显然是从封送代码中实例化的。我不是 .Net 专家,但从我读到的编组代码是某种序列化,可以应用于对象以通过二进制通道(例如 TCP 连接)传输功能代码。

我不确定“编组代码”是什么意思,但 .Net 中的编组通常是指与本机代码的互操作性。

这并不是真正的序列化,我相信它并不常用于此目的。(它可用于将单个对象转换为字节数组,但这在这里并不真正相关,因为您的代码不这样做。)


现在,你的代码:Marshal包含的互操作性有用的方法,包括本地和托管类型之间的转换。

这正是GetDelegateForFunctionPointer()它所做的:它将非托管函数指针(指向某些非托管代码)转换为可以从 .Net 调用的委托。

为了验证这确实是您的代码所做的,我创建了一个稍微简化的版本,并将一个非常简单的函数的 x86 机器代码放入字节数组中:

class Program
{
    private static readonly byte[] code =
    {
        0xB8, 0x2A, 0x00, 0x00, 0x00, // mov eax, 0x2a
        0xC3 // ret
    };

    private delegate int FuncInt();

    private static void Main()
    {
        IntPtr ptr = AllocExecutableMemory(code.Length);
        for (int i = 0; i < code.Length; i++)
        {
            Marshal.WriteByte(ptr, i, code[i]);
        }
        var del = (FuncInt)Marshal.GetDelegateForFunctionPointer(ptr, typeof(FuncInt));
        Console.WriteLine(del());
    }

    private static IntPtr AllocExecutableMemory(int length)
    {
        // see http://www.pinvoke.net/default.aspx/kernel32.virtualalloc,
        // but change the return type from UIntPtr to IntPtr
        return NativeMethods.VirtualAlloc(
            UIntPtr.Zero, (UIntPtr)length,
            AllocationType.COMMIT, MemoryProtection.EXECUTE_READWRITE);
    }
}

正如预期的那样,此代码打印 42 (=0x2A)。

这意味着要分析Code,您必须将其视为非托管代码(很可能是 x86)。

您可能会在 IDA 中打开文件并强制 IDA 将 IL 机器代码字节解释为代码,从而为您提供 IL 机器代码字节的反汇编。

但是既然您询问了反编译它,您可以尝试将 IL 机器码字节“强制提供”给ILSpy的反编译器函数,以查看它是否可以为您反编译。您需要查看ILSpy的源代码以找出要调用的函数。

我意识到这不是一个理想的解决方案,所以希望有人可以发布更好的答案:)