反编译.NET 4.6 DLL遇__fieldref等未知函数编译错误求解
我正在反编译一个基于.NET 4.6的DLL,用de4dot完成反编译与反混淆后得到大量类文件,但遇到两个未知函数__fieldref和__untypedstackalloc导致编译失败,相关代码及场景如下:
相关代码片段
__untypedstackalloc的调用与使用场景
void* voidPtr = (void*)__untypedstackalloc(12);
完整使用场景:
internal static unsafe string smethod_1(byte[] byte_0) { // ISSUE: untyped stack allocation void* voidPtr = (void*)__untypedstackalloc(12); *(int*)voidPtr = 0; *(int*)((IntPtr)voidPtr + 4) = 0; while (*(int*)((IntPtr)voidPtr + 4) < byte_0.Length && (byte_0[*(int*)((IntPtr)voidPtr + 4)] != (byte)0 || byte_0[*(int*)((IntPtr)voidPtr + 4) + 1] != (byte)0)) { *(int*)voidPtr = *(int*)voidPtr + 1; *(int*)((IntPtr)voidPtr + 4) = *(int*)((IntPtr)voidPtr + 4) + 2; } byte[] bytes = new byte[*(int*)voidPtr * 2]; *(int*)((IntPtr)voidPtr + 8) = 0; while (*(int*)((IntPtr)voidPtr + 8) < *(int*)voidPtr * 2) { bytes[*(int*)((IntPtr)voidPtr + 8)] = byte_0[*(int*)((IntPtr)voidPtr + 8)]; *(int*)((IntPtr)voidPtr + 8) = *(int*)((IntPtr)voidPtr + 8) + 1; } return Encoding.Unicode.GetString(bytes); }
__fieldref的调用与关联类定义
RuntimeHelpers.InitializeArray((Array)numArray, __fieldref(Class73.class76_0));
Class73的定义:
[CompilerGenerated] internal sealed class Class73 { internal static readonly Class73.Class78 class78_0; internal static readonly Class73.Class76 class76_0; internal static readonly Class73.Class74 class74_0; internal static readonly Class73.Class75 class75_0; internal static readonly Class73.Class77 class77_0; internal static readonly Class73.Class74 class74_1; internal static readonly Class73.Class77 class77_1; internal static readonly Class73.Class74 class74_2; [StructLayout(LayoutKind.Explicit, Size = 16, Pack = 1)] private struct Class74 { } [StructLayout(LayoutKind.Explicit, Size = 32, Pack = 1)] private struct Class75 { } [StructLayout(LayoutKind.Explicit, Size = 128, Pack = 1)] private struct Class76 { } [StructLayout(LayoutKind.Explicit, Size = 256, Pack = 1)] private struct Class77 { } [StructLayout(LayoutKind.Explicit, Size = 1024, Pack = 1)] private struct Class78 { } }
函数分析与解决方案
一、__untypedstackalloc
作用
本质是在栈上分配指定字节数的未类型化内存块,返回指向该内存的void*指针,用于临时存储少量数据(比如示例中用12字节存3个int变量,每个int占4字节)。栈内存会在方法返回时自动释放,适合短期临时存储。
为什么封装静态方法会出问题
C#中stackalloc分配的内存属于当前方法的栈帧,如果把它封装成独立静态方法返回指针,调用该方法后原栈帧销毁,返回的指针会指向已被释放的内存,后续访问会触发非法内存操作。
解决方法
直接在调用处用stackalloc替换原函数调用,无需封装:
// 替换原void* voidPtr = (void*)__untypedstackalloc(12); byte* tempPtr = stackalloc byte[12]; // 分配12字节栈内存 void* voidPtr = tempPtr;
或者用int*分配(3个int正好12字节),效果完全一致:
int* tempPtr = stackalloc int[3]; void* voidPtr = tempPtr;
二、__fieldref
作用
用于获取静态字段的RuntimeFieldHandle,供RuntimeHelpers.InitializeArray使用——该方法需要通过字段句柄读取元数据中存储的数组初始值,完成目标数组的初始化。
反编译异常原因
de4dot反混淆时未正确解析IL中的ldtoken指令(该指令用于加载字段的元数据令牌),错误生成了__fieldref(Class73.class76_0)的调用(实际应获取字段本身的元数据句柄,而非字段的实例对象)。
解决方法
有两种可行方案:
方案1:直接替换调用处代码(推荐,性能更高)
用反射直接获取目标字段的FieldHandle,替换原函数调用:
// 替换原RuntimeHelpers.InitializeArray((Array)numArray, __fieldref(Class73.class76_0)); RuntimeHelpers.InitializeArray( (Array)numArray, typeof(Class73).GetField("class76_0", BindingFlags.Static | BindingFlags.NonPublic).FieldHandle );
方案2:实现通用__fieldref方法(适合多处调用场景)
如果代码中有多处__fieldref调用,可以实现一个通用方法通过反射匹配字段:
using System; using System.Reflection; public static class DecompileHelpers { public static RuntimeFieldHandle __fieldref(object staticFieldInstance) { Type declaringType = staticFieldInstance.GetType().DeclaringType; // 遍历类型的所有静态非公开字段(包含internal) foreach (FieldInfo field in declaringType.GetFields(BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Internal)) { if (field.GetValue(null).Equals(staticFieldInstance)) { return field.FieldHandle; } } throw new ArgumentException("无法找到对应的静态字段"); } }
内容的提问来源于stack exchange,提问作者Fardin

