【问题标题】:Marshal.GetDelegateForFunctionPointer returns memory access violationMarshal.GetDelegateForFunctionPointer 返回内存访问冲突
【发布时间】:2021-07-20 11:31:51
【问题描述】:

我正在尝试使用Marshal.GetDelegateForFunctionPointer 执行我使用我的代码从磁盘获取的LdrLoadDll 的系统调用存根,该代码使用Marshal.AllocHGlobal,后来将内存保护更改为 RWX(这是来自磁盘的系统调用存根存在)。我的代码中定义的系统调用存根大小为 23 个字节。这是我想使用指向包含系统调用存根的已分配内存的指针时使用的代码

// pLdrLoadDll contains the pointer to the allocated memory containing the syscall stub
// LdrLoadDllDelegate is my delegate for LdrLoadDll
Delegate funcDelegate = Marshal.GetDelegateForFunctionPointer(pLdrLoadDll, typeof(LdrLoadDllDelegate));
NativeDeclarations.NTSTATUS retValue =  (NativeDeclarations.NTSTATUS)funcDelegate.DynamicInvoke(funcargs);

执行此行时代码返回内存访问冲突

Delegate funcDelegate = Marshal.GetDelegateForFunctionPointer(pLdrLoadDll, typeof(LdrLoadDllDelegate));

我尝试使用其他方法,比如这个,它返回相同的错误。

LdrLoadDllDelegate fSyscallLdrLoadDll = (LdrLoadDllDelegate)Marshal.GetDelegateForFunctionPointer(pLdrLoadDll, typeof(LdrLoadDllDelegate));

我使用相同的函数来获取其他系统调用存根,并且使用这两种方法都可以正常工作。我也尝试使用Marshal.GetDelegateForFunctionPointer 来执行LdrLoadDll,但是从内存中的ntdll 可以正常工作。这是我测试的唯一一个不适用于我的系统调用存根提取器函数的函数。我真的不知道为什么它会抛出那个错误。

这是我用来从磁盘中提取系统调用存根的代码

public const int SYSCALL_STUB_SIZE = 23;
public static IntPtr GetSyscallStub(string FuncName) {
    IntPtr output = IntPtr.Zero;
    // get NTDLL full path
    string NTDLLFullPath;
    try{ NTDLLFullPath = (Process.GetCurrentProcess().Modules.Cast<ProcessModule>().Where(x => "ntdll.dll".Equals(Path.GetFileName(x.FileName), StringComparison.OrdinalIgnoreCase)).FirstOrDefault().FileName); }catch{ NTDLLFullPath = null; }
    if (NTDLLFullPath != null) {
        // allocate and copy original DLL to unmanaged memory
        byte[] NTDLLBytes = System.IO.File.ReadAllBytes(NTDLLFullPath);
        IntPtr pNTDLLBytes = Marshal.AllocHGlobal(NTDLLBytes.Length);
        Marshal.Copy(NTDLLBytes, 0, pNTDLLBytes, NTDLLBytes.Length);
        PEReader NTDLL = new PEReader(NTDLLBytes);
        int RegionSize = NTDLL.Is32BitHeader ? (int)NTDLL.OptionalHeader32.SizeOfImage : (int)NTDLL.OptionalHeader64.SizeOfImage;
        int SizeOfHeaders = NTDLL.Is32BitHeader ? (int)NTDLL.OptionalHeader32.SizeOfHeaders : (int)NTDLL.OptionalHeader64.SizeOfHeaders;
        IntPtr pNTDLLImage = Marshal.AllocHGlobal(RegionSize);
        // copying image header
        Marshal.Copy(NTDLLBytes, 0, pNTDLLImage, SizeOfHeaders);
        // copying sections
        for (int i = 0; i < NTDLL.FileHeader.NumberOfSections; i++) {
            IntPtr pVASectionBase = (IntPtr)((UInt64)pNTDLLImage + NTDLL.ImageSectionHeaders[i].VirtualAddress);
            Marshal.Copy(NTDLLBytes, NTDLL.ImageSectionHeaders[i].PointerToRawData, pVASectionBase, (int)NTDLL.ImageSectionHeaders[i].SizeOfRawData);
        }
        // allocate unmanaged memory for the syscall stub
        IntPtr pSyscallStub = Marshal.AllocHGlobal(SYSCALL_STUB_SIZE); // dont forget to change it to RX later
        // get pointer to function
        IntPtr pFunc = GetExportAddress(pNTDLLImage, FuncName);
        // copy from the function pointer to the allocated memory for syscall stub
        byte[] bSyscallStub = new byte[SYSCALL_STUB_SIZE];
        Marshal.Copy(pFunc, bSyscallStub, 0, SYSCALL_STUB_SIZE);
        Marshal.Copy(bSyscallStub, 0, pSyscallStub, SYSCALL_STUB_SIZE);
        // change syscall stub memory to RWX,using RX crashes the process,dont ask me why
        uint oldProtect;
        VirtualProtect(pSyscallStub, (UIntPtr)SYSCALL_STUB_SIZE, 0x40, out oldProtect);
        // free temporary allocations
        Marshal.FreeHGlobal(pNTDLLBytes);
        Marshal.FreeHGlobal(pNTDLLImage);
        output = pSyscallStub;
        return output;
    }else {
        Console.WriteLine("Failed to get NTDLL path.");
        return output;
    }
}

*GetExportAddress = GetProcAddress

我使用内置 CSC 作为编译器在 Windows 10 v20H2 上测试代码。为了清除它,我正在学习 AV/EDR 防御规避,因为我使用系统调用存根而不是 P/Invoke。

【问题讨论】:

    标签: c# windows system-calls


    【解决方案1】:

    在对 ntdll.dll 进行一些调试后,发现 LdrLoadDll 没有使用普通的系统调用存根,因为它是多个系统调用的组合。访问冲突错误是由于系统调用存根不完整引起的,因为它的大小不像正常的系统调用存根。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-05-22
      • 2017-01-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-07-31
      • 2018-09-29
      • 1970-01-01
      相关资源
      最近更新 更多