From db7652bc21bdc4d2fe62cfcef3abcc1807548b23 Mon Sep 17 00:00:00 2001 From: David Hall Date: Thu, 11 Jul 2019 14:52:17 -0600 Subject: [PATCH] Made unit tests for and fixed all functions from rtlsupportapi.h --- PInvoke/Kernel32/RtlSupportApi.cs | 288 +++++++++++++++-------- PInvoke/Kernel32/WinNT.cs | 92 ++++++++ UnitTests/PInvoke/Kernel32/Kernel32.csproj | 1 + UnitTests/PInvoke/Kernel32/RtlSupportApiTests.cs | 105 +++++++++ 4 files changed, 393 insertions(+), 93 deletions(-) create mode 100644 UnitTests/PInvoke/Kernel32/RtlSupportApiTests.cs diff --git a/PInvoke/Kernel32/RtlSupportApi.cs b/PInvoke/Kernel32/RtlSupportApi.cs index b7b13216..04770ade 100644 --- a/PInvoke/Kernel32/RtlSupportApi.cs +++ b/PInvoke/Kernel32/RtlSupportApi.cs @@ -1,6 +1,5 @@ using System; using System.Runtime.InteropServices; -using System.Text; namespace Vanara.PInvoke { @@ -8,121 +7,196 @@ namespace Vanara.PInvoke { /// Adds a dynamic function table to the dynamic function table list. /// - /// A pointer to an array of function entries. For a definition of the PRUNTIME_FUNCTION type, see WinNT.h. For more information on runtime - /// function entries, see the calling convention documentation for the processor. + /// A pointer to an array of function entries. For a definition of the PRUNTIME_FUNCTION type, see rtlsupportapi.h. For more + /// information on runtime function entries, see the calling convention documentation for the processor. /// /// The number of entries in the FunctionTable array. - /// The base address to use when computing full virtual addresses from relative virtual addresses of function table entries. - /// - /// The target global pointer. This is part of the Intel IPF calling convention. It is a pointer to a data area in an image. - /// This parameter does not exist on x64. + /// + /// The base address to use when computing full virtual addresses from relative virtual addresses of function table entries. /// /// If the function succeeds, the return value is TRUE. Otherwise, the return value is FALSE. - // BOOLEAN WINAPI RtlAddFunctionTable( _In_ PRUNTIME_FUNCTION FunctionTable, _In_ DWORD EntryCount, _In_ DWORD64 BaseAddress, _In_ ULONGLONG TargetGp); - // https://msdn.microsoft.com/en-us/library/windows/desktop/ms680588(v=vs.85).aspx + /// + /// + /// Function tables are used on 64-bit Windows to determine how to unwind or walk the stack. These tables are usually generated by + /// the compiler and stored as part of the image. However, applications must provide the function table for dynamically generated + /// code. For more information about function tables, see the architecture guide for your system. + /// + /// + /// This function is useful for code that is generated from a template or generated only once during the life of the process. For + /// more dynamically generated code, use the RtlInstallFunctionTableCallback function. + /// + /// + // https://docs.microsoft.com/en-us/windows/win32/api/winnt/nf-winnt-rtladdfunctiontable NTSYSAPI BOOLEAN RtlAddFunctionTable( + // PRUNTIME_FUNCTION FunctionTable, DWORD EntryCount, DWORD64 BaseAddress ); [DllImport(Lib.Kernel32, SetLastError = false, ExactSpelling = true)] - [PInvokeData("WinNT.h", MSDNShortId = "ms680588")] + [PInvokeData("rtlsupportapi.h", MSDNShortId = "4717f29e-c5f8-4b02-a7c8-edd065f1c793")] [return: MarshalAs(UnmanagedType.U1)] - public static extern bool RtlAddFunctionTable(IntPtr FunctionTable, uint EntryCount, ulong BaseAddress, ulong TargetGp); + public static extern bool RtlAddFunctionTable([In] IMAGE_RUNTIME_FUNCTION_ENTRY[] FunctionTable, uint EntryCount, ulong BaseAddress); /// Retrieves a context record in the context of the caller. - /// A pointer to a CONTEXT structure. + /// A pointer to a CONTEXT structure. /// This function does not return a value. - // VOID WINAPI RtlCaptureContext( _Out_ PCONTEXT ContextRecord); https://msdn.microsoft.com/en-us/library/windows/desktop/ms680591(v=vs.85).aspx + // https://docs.microsoft.com/en-us/windows/win32/api/winnt/nf-winnt-rtlcapturecontext NTSYSAPI VOID RtlCaptureContext( PCONTEXT + // ContextRecord ); [DllImport(Lib.Kernel32, SetLastError = false, ExactSpelling = true)] - [PInvokeData("WinNT.h", MSDNShortId = "ms680591")] + [PInvokeData("rtlsupportapi.h", MSDNShortId = "e2ce0cde-43ab-4681-be66-bd7509fd6ca2")] public static extern void RtlCaptureContext(ref CONTEXT ContextRecord); - /// Removes a dynamic function table from the dynamic function table list.A pointer to an array of function entries that were previously passed to RtlAddFunctionTable or an identifier previously passed to RtlInstallFunctionTableCallback. For a definition of the PRUNTIME_FUNCTION type, see WinNT.h.If the function succeeds, the return value is TRUE. Otherwise, the return value is FALSE. - // BOOLEAN WINAPI RtlDeleteFunctionTable( _In_ PRUNTIME_FUNCTION FunctionTable); - // https://msdn.microsoft.com/en-us/library/windows/desktop/ms680593(v=vs.85).aspx + /// Retrieves a context record in the context of the caller. + /// + /// The context flags to specify what aspects of the context to capture. See for pre-configured values. + /// + /// The captured CONTEXT structure. + [PInvokeData("rtlsupportapi.h", MSDNShortId = "e2ce0cde-43ab-4681-be66-bd7509fd6ca2")] + public static CONTEXT RtlCaptureContext(uint contextFlags = uint.MaxValue) + { + if (contextFlags == uint.MaxValue) contextFlags = CONTEXT_FLAG.CONTEXT_ALL; + var ctx = new CONTEXT { ContextFlags = contextFlags }; + RtlCaptureContext(ref ctx); + return ctx; + } + + /// Removes a dynamic function table from the dynamic function table list. + /// + /// A pointer to an array of function entries that were previously passed to RtlAddFunctionTable or an identifier previously passed + /// to RtlInstallFunctionTableCallback. For a definition of the PRUNTIME_FUNCTION type, see rtlsupportapi.h. + /// + /// If the function succeeds, the return value is TRUE. Otherwise, the return value is FALSE. + /// + /// Function tables are used on 64-bit Windows to determine how to unwind or walk the stack. These tables are usually generated by + /// the compiler and stored as part of the image. However, applications must provide the function table for dynamically generated + /// code. For more information about function tables, see the architecture guide for your system. + /// + // https://docs.microsoft.com/en-us/windows/win32/api/winnt/nf-winnt-rtldeletefunctiontable NTSYSAPI BOOLEAN RtlDeleteFunctionTable( + // PRUNTIME_FUNCTION FunctionTable ); [DllImport(Lib.Kernel32, SetLastError = false, ExactSpelling = true)] - [PInvokeData("WinNT.h", MSDNShortId = "ms680593")] + [PInvokeData("rtlsupportapi.h", MSDNShortId = "42bc3d83-8053-40e9-b153-f68733d0cb2b")] [return: MarshalAs(UnmanagedType.U1)] - public static extern bool RtlDeleteFunctionTable(IntPtr FunctionTable); + public static extern bool RtlDeleteFunctionTable([In] IMAGE_RUNTIME_FUNCTION_ENTRY[] FunctionTable); - /// Adds a dynamic function table to the dynamic function table list.The identifier for the dynamic function table callback. The two low-order bits must be set. For example, BaseAddress|0x3.The base address of the region of memory managed by the callback function.The size of the region of memory managed by the callback function, in bytes.A pointer to the callback function that is called to retrieve the function table entries for the functions in the specified region of memory. For a definition of the PGET_RUNTIME_FUNCTION_CALLBACK type, see WinNT.h.A pointer to the user-defined data to be passed to the callback function.An optional pointer to a string that specifies the path of a DLL that provides function table entries that are outside the process.When a debugger unwinds to a function in the range of addresses managed by the callback function, it loads this DLL and calls the OUT_OF_PROCESS_FUNCTION_TABLE_CALLBACK_EXPORT_NAME function, whose type is POUT_OF_PROCESS_FUNCTION_TABLE_CALLBACK. For more information, see the definitions of these items in WinNT.h.If the function succeeds, the return value is TRUE. If the function fails, the return value is FALSE. - // BOOLEAN WINAPI RtlInstallFunctionTableCallback( _In_ DWORD64 TableIdentifier, _In_ DWORD64 BaseAddress, _In_ DWORD Length, _In_ PGET_RUNTIME_FUNCTION_CALLBACK Callback, _In_ PVOID Context, _In_ PCWSTR OutOfProcessCallbackDll); - // https://msdn.microsoft.com/en-us/library/windows/desktop/ms680595(v=vs.85).aspx - [DllImport(Lib.Kernel32, SetLastError = false, ExactSpelling = true, CharSet = CharSet.Unicode)] - [PInvokeData("WinNT.h", MSDNShortId = "ms680595")] + /// Removes a dynamic function table from the dynamic function table list. + /// + /// A pointer to an array of function entries that were previously passed to RtlAddFunctionTable or an identifier previously passed + /// to RtlInstallFunctionTableCallback. For a definition of the PRUNTIME_FUNCTION type, see rtlsupportapi.h. + /// + /// If the function succeeds, the return value is TRUE. Otherwise, the return value is FALSE. + /// + /// Function tables are used on 64-bit Windows to determine how to unwind or walk the stack. These tables are usually generated by + /// the compiler and stored as part of the image. However, applications must provide the function table for dynamically generated + /// code. For more information about function tables, see the architecture guide for your system. + /// + // https://docs.microsoft.com/en-us/windows/win32/api/winnt/nf-winnt-rtldeletefunctiontable NTSYSAPI BOOLEAN RtlDeleteFunctionTable( + // PRUNTIME_FUNCTION FunctionTable ); + [DllImport(Lib.Kernel32, SetLastError = false, ExactSpelling = true)] + [PInvokeData("rtlsupportapi.h", MSDNShortId = "42bc3d83-8053-40e9-b153-f68733d0cb2b")] [return: MarshalAs(UnmanagedType.U1)] - public static extern bool RtlInstallFunctionTableCallback(ulong TableIdentifier, ulong BaseAddress, uint Length, PGET_RUNTIME_FUNCTION_CALLBACK Callback, IntPtr Context, [Optional] string OutOfProcessCallbackDll); + public static extern bool RtlDeleteFunctionTable([In] ulong FunctionTable); - /// Retrieves the function table entries for the functions in the specified region of memory. - /// The control address. - /// A pointer to the user-defined data to be passed from the function call. - /// - [UnmanagedFunctionPointer(CallingConvention.Winapi)] - [PInvokeData("WinNT.h")] - public delegate IntPtr PGET_RUNTIME_FUNCTION_CALLBACK(uint ControlPc, IntPtr Context); + /// Adds a dynamic function table to the dynamic function table list. + /// + /// The identifier for the dynamic function table callback. The two low-order bits must be set. For example, BaseAddress|0x3. + /// + /// The base address of the region of memory managed by the callback function. + /// The size of the region of memory managed by the callback function, in bytes. + /// + /// A pointer to the callback function that is called to retrieve the function table entries for the functions in the specified + /// region of memory. For a definition of the PGET_RUNTIME_FUNCTION_CALLBACK type, see rtlsupportapi.h. + /// + /// A pointer to the user-defined data to be passed to the callback function. + /// + /// + /// An optional pointer to a string that specifies the path of a DLL that provides function table entries that are outside the process. + /// + /// + /// When a debugger unwinds to a function in the range of addresses managed by the callback function, it loads this DLL and calls the + /// OUT_OF_PROCESS_FUNCTION_TABLE_CALLBACK_EXPORT_NAME function, whose type is POUT_OF_PROCESS_FUNCTION_TABLE_CALLBACK. + /// For more information, see the definitions of these items in rtlsupportapi.h. + /// + /// + /// If the function succeeds, the return value is TRUE. If the function fails, the return value is FALSE. + /// + /// + /// Function tables are used on 64-bit Windows to determine how to unwind or walk the stack. These tables are usually generated by + /// the compiler and stored as part of the image. However, applications must provide the function table for dynamically generated + /// code. For more information about function tables, see the architecture guide for your system. + /// + /// + /// This function is useful for very dynamic code. The application specifies the memory range for the generated code, but does not + /// need to generate a table until it is needed by an unwind request. At that time, the system calls the callback function with the + /// Context and the control address. The callback function must return the runtime function entry for the specified address. Be sure + /// to avoid creating a deadlock between the callback function and the code generator. + /// + /// + /// For code that is generated from a template or generated only once during the life of the process, use the RtlAddFunctionTable function. + /// + /// + // https://docs.microsoft.com/en-us/windows/win32/api/winnt/nf-winnt-rtlinstallfunctiontablecallback NTSYSAPI BOOLEAN + // RtlInstallFunctionTableCallback( DWORD64 TableIdentifier, DWORD64 BaseAddress, DWORD Length, PGET_RUNTIME_FUNCTION_CALLBACK + // Callback, PVOID Context, PCWSTR OutOfProcessCallbackDll ); + [DllImport(Lib.Kernel32, SetLastError = false, ExactSpelling = true)] + [PInvokeData("rtlsupportapi.h", MSDNShortId = "63b35b17-0b0e-46ed-9dbf-98290ab08bd1")] + [return: MarshalAs(UnmanagedType.U1)] + public static extern bool RtlInstallFunctionTableCallback(ulong TableIdentifier, ulong BaseAddress, uint Length, PGET_RUNTIME_FUNCTION_CALLBACK Callback, IntPtr Context = default, [MarshalAs(UnmanagedType.LPWStr)] string OutOfProcessCallbackDll = null); /// Searches the active function tables for an entry that corresponds to the specified PC value. /// The virtual address of an instruction bundle within the function. /// The base address of module to which the function belongs. - /// + /// /// The global pointer value of the module. /// This parameter has a different declaration on x64 and ARM systems. For more information, see x64 Definition and ARM Definition. /// /// - /// If there is no entry in the function table for the specified PC, the function returns NULL. Otherwise, the function returns the address of the - /// function table entry that corresponds to the specified PC. + /// If there is no entry in the function table for the specified PC, the function returns NULL. Otherwise, the function + /// returns the address of the function table entry that corresponds to the specified PC. /// - // PVOID WINAPI RtlLookupFunctionEntry( _In_ ULONGLONG ControlPc, _Out_ PULONGLONG ImageBase, _Out_ PULONGLONG TargetGp); - // https://msdn.microsoft.com/en-us/library/windows/desktop/ms680597(v=vs.85).aspx + // https://docs.microsoft.com/en-us/windows/win32/api/winnt/nf-winnt-rtllookupfunctionentry NTSYSAPI PRUNTIME_FUNCTION + // RtlLookupFunctionEntry( DWORD64 ControlPc, PDWORD64 ImageBase, PUNWIND_HISTORY_TABLE HistoryTable ); [DllImport(Lib.Kernel32, SetLastError = false, ExactSpelling = true)] - [PInvokeData("WinNT.h", MSDNShortId = "ms680597")] - public static extern IntPtr RtlLookupFunctionEntry(ulong ControlPc, out ulong ImageBase, out ulong TargetGp); - - /// Moves a block of memory from one location to another. - /// A pointer to the starting address of the move destination. - /// A pointer to the starting address of the block of memory to be moved. - /// The size of the block of memory to move, in bytes. - /// This function has no return value. - // void MoveMemory( _In_ PVOID Destination, _In_ const VOID *Source, _In_ SIZE_T Length); - // https://msdn.microsoft.com/en-us/library/windows/desktop/aa366788(v=vs.85).aspx - [DllImport(Lib.Kernel32, SetLastError = false, EntryPoint = "RtlMoveMemory")] - [PInvokeData("WinBase.h", MSDNShortId = "aa366788")] - public static extern void MoveMemory(IntPtr Destination, IntPtr Source, SizeT Length); + [PInvokeData("rtlsupportapi.h", MSDNShortId = "624b97fb-0453-4f47-b6bd-92aa14705e78")] + public static extern IntPtr RtlLookupFunctionEntry(ulong ControlPc, out ulong ImageBase, out UNWIND_HISTORY_TABLE HistoryTable); /// Retrieves the base address of the image that contains the specified PC value. /// - /// The PC value. The function searches all modules mapped into the address space of the calling process for a module that contains this value. + /// The PC value. The function searches all modules mapped into the address space of the calling process for a module that contains + /// this value. /// /// - /// The base address of the image containing the PC value. This value must be added to any relative addresses in the headers to locate the image. + /// The base address of the image containing the PC value. This value must be added to any relative addresses in the headers to + /// locate the image. /// /// /// If the PC value is found, the function returns the base address of the image that contains the PC value. /// If no image contains the PC value, the function returns NULL. /// - // PVOID WINAPI RtlPcToFileHeader( _In_ PVOID PcValue, _Out_ PVOID *BaseOfImage); - // https://msdn.microsoft.com/en-us/library/windows/desktop/ms680603(v=vs.85).aspx + // https://docs.microsoft.com/en-us/windows/win32/api/winnt/nf-winnt-rtlpctofileheader NTSYSAPI PVOID RtlPcToFileHeader( PVOID + // PcValue, PVOID *BaseOfImage ); [DllImport(Lib.Kernel32, SetLastError = false, ExactSpelling = true)] - [PInvokeData("WinNT.h", MSDNShortId = "ms680603")] + [PInvokeData("rtlsupportapi.h", MSDNShortId = "690c9f20-d471-49c9-a40c-28926f03acac")] public static extern IntPtr RtlPcToFileHeader(IntPtr PcValue, out IntPtr BaseOfImage); /// Restores the context of the caller to the specified context record. - /// A pointer to a CONTEXT structure. + /// A pointer to a CONTEXT structure. /// - /// A pointer to an EXCEPTION_RECORD structure. This parameter is optional and should typically be NULL. + /// A pointer to an EXCEPTION_RECORD structure. This parameter is optional and should typically be NULL. /// - /// An exception record is used primarily with long jump and C++ catch-throw support. If the ExceptionCode member is STATUS_LONGJUMP, the - /// ExceptionInformation member contains a pointer to a jump buffer. RtlRestoreContext will copy the non-volatile state from the jump - /// buffer in to the context record before the context record is restored. + /// An exception record is used primarily with long jump and C++ catch-throw support. If the ExceptionCode member is + /// STATUS_LONGJUMP, the ExceptionInformation member contains a pointer to a jump buffer. RtlRestoreContext will copy + /// the non-volatile state from the jump buffer in to the context record before the context record is restored. /// /// - /// If the ExceptionCode member is STATUS_UNWIND_CONSOLIDATE, the ExceptionInformation member contains a pointer to a callback function, - /// such as a catch handler. RtlRestoreContext consolidates the call frames between its frame and the frame specified in the context record before - /// calling the callback function. This hides frames from any exception handling that might occur in the callback function. The difference between this - /// and a typical unwind is that the data on the stack is still present, so frame data such as a throw object is still available. The callback function - /// returns a new program counter to update in the context record, which is then used in a normal restore context. + /// If the ExceptionCode member is STATUS_UNWIND_CONSOLIDATE, the ExceptionInformation member contains a pointer to a + /// callback function, such as a catch handler. RtlRestoreContext consolidates the call frames between its frame and the frame + /// specified in the context record before calling the callback function. This hides frames from any exception handling that might + /// occur in the callback function. The difference between this and a typical unwind is that the data on the stack is still present, + /// so frame data such as a throw object is still available. The callback function returns a new program counter to update in the + /// context record, which is then used in a normal restore context. /// /// /// This function does not return a value. - // VOID WINAPI RtlRestoreContext( _In_ PCONTEXT ContextRecord, _In_ PEXCEPTION_RECORD ExceptionRecord); https://msdn.microsoft.com/en-us/library/windows/desktop/ms680605(v=vs.85).aspx + // https://docs.microsoft.com/en-us/windows/win32/api/winnt/nf-winnt-rtlrestorecontext NTSYSAPI VOID RtlRestoreContext( PCONTEXT + // ContextRecord, _EXCEPTION_RECORD *ExceptionRecord ); [DllImport(Lib.Kernel32, SetLastError = false, ExactSpelling = true)] - [PInvokeData("WinNT.h", MSDNShortId = "ms680605")] + [PInvokeData("rtlsupportapi.h", MSDNShortId = "f5304d17-bc67-4e0f-a535-efca4e65c74c")] public static extern void RtlRestoreContext(ref CONTEXT ContextRecord, ref EXCEPTION_RECORD ExceptionRecord); /// Initiates an unwind of procedure call frames. @@ -130,31 +204,66 @@ namespace Vanara.PInvoke /// A pointer to the call frame that is the target of the unwind. If this parameter is NULL, the function performs an exit unwind. /// /// The continuation address of the unwind. This parameter is ignored if TargetFrame is NULL. - /// A pointer to an EXCEPTION_RECORD structure. + /// A pointer to an EXCEPTION_RECORD structure. /// A value to be placed in the integer function return register before continuing execution. /// This function does not return a value. - // void WINAPI RtlUnwind( _In_opt_ PVOID TargetFrame, _In_opt_ PVOID TargetIp, _In_opt_ PEXCEPTION_RECORD ExceptionRecord, _In_ PVOID ReturnValue); https://msdn.microsoft.com/en-us/library/windows/desktop/ms680609(v=vs.85).aspx + // https://docs.microsoft.com/en-us/windows/win32/api/winnt/nf-winnt-rtlunwind NTSYSAPI VOID RtlUnwind( PVOID TargetFrame, PVOID + // TargetIp, PEXCEPTION_RECORD ExceptionRecord, PVOID ReturnValue ); [DllImport(Lib.Kernel32, SetLastError = false, ExactSpelling = true)] - [PInvokeData("WinNT.h", MSDNShortId = "ms680609")] - public static extern void RtlUnwind(IntPtr TargetFrame, IntPtr TargetIp, ref EXCEPTION_RECORD ExceptionRecord, IntPtr ReturnValue); + [PInvokeData("rtlsupportapi.h", MSDNShortId = "254b2547-9d3d-468f-a360-20a12e9dd82e")] + public static extern void RtlUnwind([In, Optional] IntPtr TargetFrame, [In, Optional] IntPtr TargetIp, in EXCEPTION_RECORD ExceptionRecord, IntPtr ReturnValue); /// Initiates an unwind of procedure call frames. /// /// A pointer to the call frame that is the target of the unwind. If this parameter is NULL, the function performs an exit unwind. /// /// The continuation address of the unwind. This parameter is ignored if TargetFrame is NULL. - /// A pointer to an EXCEPTION_RECORD structure. + /// A pointer to an EXCEPTION_RECORD structure. /// A value to be placed in the integer function return register before continuing execution. - /// A pointer to a CONTEXT structure that stores context during the unwind operation. + /// This function does not return a value. + // https://docs.microsoft.com/en-us/windows/win32/api/winnt/nf-winnt-rtlunwind NTSYSAPI VOID RtlUnwind( PVOID TargetFrame, PVOID + // TargetIp, PEXCEPTION_RECORD ExceptionRecord, PVOID ReturnValue ); + [DllImport(Lib.Kernel32, SetLastError = false, ExactSpelling = true)] + [PInvokeData("rtlsupportapi.h", MSDNShortId = "254b2547-9d3d-468f-a360-20a12e9dd82e")] + public static extern void RtlUnwind([In, Optional] IntPtr TargetFrame, [In, Optional] IntPtr TargetIp, [In, Optional] IntPtr ExceptionRecord, IntPtr ReturnValue); + + /// Initiates an unwind of procedure call frames. + /// + /// A pointer to the call frame that is the target of the unwind. If this parameter is NULL, the function performs an exit unwind. + /// + /// The continuation address of the unwind. This parameter is ignored if TargetFrame is NULL. + /// A pointer to an EXCEPTION_RECORD structure. + /// A value to be placed in the integer function return register before continuing execution. + /// A pointer to a CONTEXT structure that stores context during the unwind operation. /// /// A pointer to the unwind history table. This structure is processor specific. For definitions of this structure, see Winternl.h. /// /// This function does not return a value. - // void WINAPI RtlUnwindEx( _In_opt_ PVOID TargetFrame, _In_opt_ PVOID TargetIp, _In_opt_ PEXCEPTION_RECORD ExceptionRecord, _In_ PVOID ReturnValue, _In_ - // PCONTEXT OriginalContext, _In_opt_ PUNWIND_HISTORY_TABLE HistoryTable); https://msdn.microsoft.com/en-us/library/windows/desktop/ms680615(v=vs.85).aspx + /// The FRAME_POINTERS structure is defined as follows: + // https://docs.microsoft.com/en-us/windows/win32/api/winnt/nf-winnt-rtlunwindex NTSYSAPI VOID RtlUnwindEx( PVOID TargetFrame, PVOID + // TargetIp, PEXCEPTION_RECORD ExceptionRecord, PVOID ReturnValue, PCONTEXT ContextRecord, PUNWIND_HISTORY_TABLE HistoryTable ); [DllImport(Lib.Kernel32, SetLastError = false, ExactSpelling = true)] - [PInvokeData("WinNT.h", MSDNShortId = "ms680615")] - public static extern void RtlUnwindEx(IntPtr TargetFrame, IntPtr TargetIp, ref EXCEPTION_RECORD ExceptionRecord, IntPtr ReturnValue, ref CONTEXT OriginalContext, IntPtr HistoryTable); + [PInvokeData("rtlsupportapi.h", MSDNShortId = "3d2d8778-311e-4cc1-b280-4f83ab457755")] + public static extern void RtlUnwindEx([In, Optional] IntPtr TargetFrame, [In, Optional] IntPtr TargetIp, in EXCEPTION_RECORD ExceptionRecord, IntPtr ReturnValue, in CONTEXT OriginalContext, in UNWIND_HISTORY_TABLE HistoryTable); + + /// Initiates an unwind of procedure call frames. + /// + /// A pointer to the call frame that is the target of the unwind. If this parameter is NULL, the function performs an exit unwind. + /// + /// The continuation address of the unwind. This parameter is ignored if TargetFrame is NULL. + /// A pointer to an EXCEPTION_RECORD structure. + /// A value to be placed in the integer function return register before continuing execution. + /// A pointer to a CONTEXT structure that stores context during the unwind operation. + /// + /// A pointer to the unwind history table. This structure is processor specific. For definitions of this structure, see Winternl.h. + /// + /// This function does not return a value. + /// The FRAME_POINTERS structure is defined as follows: + // https://docs.microsoft.com/en-us/windows/win32/api/winnt/nf-winnt-rtlunwindex NTSYSAPI VOID RtlUnwindEx( PVOID TargetFrame, PVOID + // TargetIp, PEXCEPTION_RECORD ExceptionRecord, PVOID ReturnValue, PCONTEXT ContextRecord, PUNWIND_HISTORY_TABLE HistoryTable ); + [DllImport(Lib.Kernel32, SetLastError = false, ExactSpelling = true)] + [PInvokeData("rtlsupportapi.h", MSDNShortId = "3d2d8778-311e-4cc1-b280-4f83ab457755")] + public static extern void RtlUnwindEx([In, Optional] IntPtr TargetFrame, [In, Optional] IntPtr TargetIp, [In, Optional] IntPtr ExceptionRecord, IntPtr ReturnValue, in CONTEXT OriginalContext, [In, Optional] IntPtr HistoryTable); /* public static extern void RtlCaptureStackBackTrace(); @@ -194,29 +303,30 @@ namespace Vanara.PInvoke /// The base address of the module to which the function belongs. /// The virtual address where control left the specified function. /// - /// The address of the function table entry for the specified function. To obtain the function table entry, call the RtlLookupFunctionEntry function. + /// The address of the function table entry for the specified function. To obtain the function table entry, call the + /// RtlLookupFunctionEntry function. /// /// A pointer to a CONTEXT structure that represents the context of the previous frame. /// /// - /// The location of the PC. If this parameter is 0, the PC is in the prologue, epilogue, or a null frame region of the function. If this parameter is 1, - /// the PC is in the body of the function. + /// The location of the PC. If this parameter is 0, the PC is in the prologue, epilogue, or a null frame region of the function. If + /// this parameter is 1, the PC is in the body of the function. /// /// This parameter is not present on x64. /// /// /// - /// A pointer to a FRAME_POINTERS structure that receives the establisher frame pointer value. The real frame pointer is defined only if - /// InFunction is 1. + /// A pointer to a FRAME_POINTERS structure that receives the establisher frame pointer value. The real frame pointer is + /// defined only if InFunction is 1. /// /// This parameter is of type PULONG64 on x64. /// /// An optional pointer to a context pointers structure. /// This function returns a pointer to an EXCEPTION_ROUTINE callback function. - // PEXCEPTION_ROUTINE WINAPI RtlVirtualUnwind( _In_ HandlerType, _In_ ImageBase, _In_ ControlPC, _In_ FunctionEntry, _Inout_ ContextRecord, _Out_ InFunction, _Out_ EstablisherFrame, _Inout_opt_ ContextPointers); - // https://msdn.microsoft.com/en-us/library/windows/desktop/ms680617(v=vs.85).aspx + // PEXCEPTION_ROUTINE WINAPI RtlVirtualUnwind( _In_ HandlerType, _In_ ImageBase, _In_ ControlPC, _In_ FunctionEntry, _Inout_ + // ContextRecord, _Out_ InFunction, _Out_ EstablisherFrame, _Inout_opt_ ContextPointers); https://msdn.microsoft.com/en-us/library/windows/desktop/ms680617(v=vs.85).aspx [DllImport(Lib.Kernel32, SetLastError = false, EntryPoint = "RtlVirtualUnwind")] - [PInvokeData("WinNT.h", MSDNShortId = "ms680617")] + [PInvokeData("rtlsupportapi.h", MSDNShortId = "ms680617")] public static extern EXCEPTION_ROUTINE RtlVirtualUnwindX64(UNW_FLAGS HandlerType, UIntPtr ImageBase, UIntPtr ControlPC, IntPtr FunctionEntry, ref CONTEXT ContextRecord, out IntPtr HandlerData, out ulong EstablisherFrame, IntPtr ContextPointers); /// The handler type. @@ -237,13 +347,5 @@ namespace Vanara.PInvoke public delegate EXCEPTION_DISPOSITION EXCEPTION_ROUTINE(ref EXCEPTION_RECORD ExceptionRecord, IntPtr EstablisherFrame, ref CONTEXT ContextRecord, IntPtr DispatcherContext); */ - - /// The RtlZeroMemory routine fills a block of memory with zeros, given a pointer to the block and the length, in bytes, to be filled. - /// A pointer to the memory block to be filled with zeros. - /// The number of bytes to fill with zeros. - // https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/wdm/nf-wdm-rtlzeromemory - [DllImport(Lib.Kernel32, SetLastError = false, ExactSpelling = true)] - [PInvokeData("WinNT.h")] - public static extern void RtlZeroMemory(IntPtr Destination, SizeT Length); } } \ No newline at end of file diff --git a/PInvoke/Kernel32/WinNT.cs b/PInvoke/Kernel32/WinNT.cs index 125f5cf0..159a156b 100644 --- a/PInvoke/Kernel32/WinNT.cs +++ b/PInvoke/Kernel32/WinNT.cs @@ -6,6 +6,20 @@ namespace Vanara.PInvoke { public static partial class Kernel32 { + public const string OUT_OF_PROCESS_FUNCTION_TABLE_CALLBACK_EXPORT_NAME = "OutOfProcessFunctionTableCallback"; + + /// Retrieves the function table entries for the functions in the specified region of memory. + /// The control address. + /// A pointer to the user-defined data to be passed from the function call. + /// Pointer to a structure. + [UnmanagedFunctionPointer(CallingConvention.Winapi)] + [PInvokeData("WinNT.h")] + public delegate IntPtr PGET_RUNTIME_FUNCTION_CALLBACK(IntPtr ControlPc, IntPtr Context); + + [UnmanagedFunctionPointer(CallingConvention.Winapi)] + [PInvokeData("WinNT.h")] + public delegate uint POUT_OF_PROCESS_FUNCTION_TABLE_CALLBACK(HPROCESS Process, IntPtr TableAddress, out uint Entries, [Out] IMAGE_RUNTIME_FUNCTION_ENTRY[] Functions); + /// /// /// An application-defined function previously registered with the AddSecureMemoryCacheCallback function that is called when a @@ -322,6 +336,44 @@ namespace Vanara.PInvoke VER_PRODUCT_TYPE = 0x0000080, } + /// + /// Copies the contents of a source memory block to a destination memory block, and supports overlapping source and destination + /// memory blocks. + /// + /// A pointer to the destination memory block to copy the bytes to. + /// A pointer to the source memory block to copy the bytes from. + /// The number of bytes to copy from the source to the destination. + /// None + /// + /// + /// The source memory block, which is defined by Source and Length, can overlap the destination memory block, which is defined by + /// Destination and Length. + /// + /// + /// The RtlCopyMemory routine runs faster than RtlMoveMemory, but RtlCopyMemory requires that the source and + /// destination memory blocks do not overlap. + /// + /// + /// Callers of RtlMoveMemory can be running at any IRQL if the source and destination memory blocks are in nonpaged system + /// memory. Otherwise, the caller must be running at IRQL <= APC_LEVEL. + /// + /// + // https://docs.microsoft.com/en-us/windows/win32/devnotes/rtlmovememory VOID RtlMoveMemory( _Out_ VOID UNALIGNED *Destination, _In_ + // const VOID UNALIGNED *Source, _In_ SIZE_T Length ); + [DllImport(Lib.Kernel32, SetLastError = false, ExactSpelling = true)] + [PInvokeData("winnt.h", MSDNShortId = "D374F14D-24C7-4771-AD40-3AC37E7A2D2F")] + public static extern void RtlMoveMemory([In] IntPtr Destination, [In] IntPtr Source, [In] SizeT Length); + + /// + /// The RtlZeroMemory routine fills a block of memory with zeros, given a pointer to the block and the length, in bytes, to be filled. + /// + /// A pointer to the memory block to be filled with zeros. + /// The number of bytes to fill with zeros. + // https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/wdm/nf-wdm-rtlzeromemory + [DllImport(Lib.Kernel32, SetLastError = false, ExactSpelling = true)] + [PInvokeData("winnt.h")] + public static extern void RtlZeroMemory(IntPtr Destination, SizeT Length); + /// Contains the hardware counter value. // typedef struct _HARDWARE_COUNTER_DATA { HARDWARE_COUNTER_TYPE Type; DWORD Reserved; DWORD64 Value;} HARDWARE_COUNTER_DATA, // *PHARDWARE_COUNTER_DATA; https://msdn.microsoft.com/en-us/library/windows/desktop/dd796394(v=vs.85).aspx @@ -341,6 +393,23 @@ namespace Vanara.PInvoke public ulong Value; } + /// Represents an entry in the function table on 64-bit Windows. + // https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-runtime_function + // typedef struct _IMAGE_RUNTIME_FUNCTION_ENTRY { DWORD BeginAddress; DWORD EndAddress; union { DWORD UnwindInfoAddress; DWORD UnwindData; } DUMMYUNIONNAME; } RUNTIME_FUNCTION, *PRUNTIME_FUNCTION, _IMAGE_RUNTIME_FUNCTION_ENTRY, *_PIMAGE_RUNTIME_FUNCTION_ENTRY; + [PInvokeData("winnt.h", MSDNShortId = "9ed16f9a-3403-4ba9-9968-f51f6788a1f8")] + [StructLayout(LayoutKind.Sequential)] + public struct IMAGE_RUNTIME_FUNCTION_ENTRY + { + /// The address of the start of the function. + public uint BeginAddress; + + /// The address of the end of the function. + public uint EndAddress; + + /// The address of the unwind information for the function. + public uint UnwindInfoAddress; + } + /// Contains the thread profiling and hardware counter data that you requested. // typedef struct _PERFORMANCE_DATA { WORD Size; BYTE Version; BYTE HwCountersCount; DWORD ContextSwitchCount; DWORD64 // WaitReasonBitMap; DWORD64 CycleTime; DWORD RetryCount; DWORD Reserved; HARDWARE_COUNTER_DATA HwCounters[MAX_HW_COUNTERS];} @@ -437,6 +506,29 @@ namespace Vanara.PInvoke public IntPtr UmsCompletionList; } + [PInvokeData("winnt.h")] + [StructLayout(LayoutKind.Sequential)] + public struct UNWIND_HISTORY_TABLE_ENTRY + { + public ulong ImageBase; + public IMAGE_RUNTIME_FUNCTION_ENTRY FunctionEntry; + } + + [PInvokeData("winnt.h")] + [StructLayout(LayoutKind.Sequential)] + public struct UNWIND_HISTORY_TABLE + { + public uint Count; + public byte LocalHint; + public byte GlobalHint; + public byte Search; + public byte Once; + public ulong LowAddress; + public ulong HighAddress; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 12)] + public UNWIND_HISTORY_TABLE_ENTRY[] Entry; + } + /// /// Represents a context frame on WOW64. Refer to the header file WinNT.h for the definition of this structure. /// diff --git a/UnitTests/PInvoke/Kernel32/Kernel32.csproj b/UnitTests/PInvoke/Kernel32/Kernel32.csproj index 6a18021f..ff48ca9f 100644 --- a/UnitTests/PInvoke/Kernel32/Kernel32.csproj +++ b/UnitTests/PInvoke/Kernel32/Kernel32.csproj @@ -48,6 +48,7 @@ + diff --git a/UnitTests/PInvoke/Kernel32/RtlSupportApiTests.cs b/UnitTests/PInvoke/Kernel32/RtlSupportApiTests.cs new file mode 100644 index 00000000..9b53404f --- /dev/null +++ b/UnitTests/PInvoke/Kernel32/RtlSupportApiTests.cs @@ -0,0 +1,105 @@ +using NUnit.Framework; +using System; +using System.Runtime.InteropServices; +using System.Text; +using Vanara.Extensions; +using Vanara.InteropServices; +using static Vanara.PInvoke.AdvApi32; +using static Vanara.PInvoke.Kernel32; + +namespace Vanara.PInvoke.Tests +{ + [TestFixture] + public class RtlSupportApiTests + { + [Test] + public void RtlAddDeleteFunctionTableTest() + { + using (var mem = new SafeHGlobalHandle(4096)) + { + var funcs = new[] { new IMAGE_RUNTIME_FUNCTION_ENTRY { BeginAddress = 0, EndAddress = 4096, UnwindInfoAddress = 2048 } }; + var baseAddr = (ulong)((IntPtr)mem).ToInt64(); + Assert.That(RtlAddFunctionTable(funcs, 1, baseAddr), ResultIs.Successful); + + try + { + var retAddr = RtlLookupFunctionEntry(baseAddr, out var img, out var hist); + Assert.That(retAddr, Is.EqualTo(IntPtr.Zero)); + } + finally + { + Assert.That(RtlDeleteFunctionTable(funcs), ResultIs.Successful); + } + } + } + + [Test] + public void RtlCaptureContextTest() + { + var ctx = RtlCaptureContext(); + Assert.That(ctx.ContextFlags, Is.EqualTo(CONTEXT_FLAG.CONTEXT_ALL)); + } + + [Test] + public void RtlInstallFunctionTableCallbackTest() + { + IntPtr pEntry = default; + using (SafeHGlobalHandle mem = new SafeHGlobalHandle(4096), entry = SafeHGlobalHandle.CreateFromStructure(new IMAGE_RUNTIME_FUNCTION_ENTRY { BeginAddress = 0, EndAddress = 4096, UnwindInfoAddress = 2048 })) + { + var baseAddr = (ulong)((IntPtr)mem).ToInt64(); + pEntry = (IntPtr)entry; + var id = baseAddr | 0x3; + Assert.That(RtlInstallFunctionTableCallback(id, baseAddr, 4096, callbk), ResultIs.Successful); + Assert.That(RtlDeleteFunctionTable(id), ResultIs.Successful); + } + + IntPtr callbk(IntPtr ControlPc, IntPtr Context) => pEntry; + } + + [Test] + public void RtlPcToFileHeaderTest() + { + Assert.That(() => RtlPcToFileHeader(IntPtr.Zero, out var p), Throws.Nothing); + // TODO - Too undocumented to implement. + } + + [Test] + public void RtlRestoreContextTest() + { + var ctx = default(CONTEXT); + var exc = default(EXCEPTION_RECORD); + Assert.That(() => RtlRestoreContext(ref ctx, ref exc), Throws.Nothing); + // TODO - Too undocumented to implement. + } + + [Test] + public void RtlUnwindTest() + { + Assert.Fail("Too undocumented to implement."); + Assert.That(() => RtlUnwind(default, default, IntPtr.Zero, default), Throws.Nothing); + } + + [Test] + public void RtlUnwindExTest() + { + Assert.Fail("Too undocumented to implement."); + Assert.That(() => RtlUnwindEx(default, default, IntPtr.Zero, default, default, default), Throws.Nothing); + } + + [Test] + public void RtlMoveMemoryTest() + { + using (SafeHGlobalHandle src = new SafeHGlobalHandle(1024), dest = new SafeHGlobalHandle(1024)) + { + src.Fill(7, 512); + Assert.That(() => RtlMoveMemory((IntPtr)dest, (IntPtr)src, 768), Throws.Nothing); + Assert.That(Marshal.ReadByte(((IntPtr)dest).Offset(256)), Is.EqualTo(7)); + Assert.That(Marshal.ReadByte(((IntPtr)dest).Offset(896)), Is.EqualTo(0)); + + Assert.That(() => RtlZeroMemory((IntPtr)dest, 128), Throws.Nothing); + Assert.That(Marshal.ReadByte(((IntPtr)dest).Offset(64)), Is.EqualTo(0)); + Assert.That(Marshal.ReadByte(((IntPtr)dest).Offset(256)), Is.EqualTo(7)); + } + } + } +} \ No newline at end of file