【发布时间】: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