Skip to content

Instantly share code, notes, and snippets.

@michel-pi
Created June 20, 2021 21:48
Show Gist options
  • Save michel-pi/6faf624ae843c6d3f381d4f913f79fd4 to your computer and use it in GitHub Desktop.
Save michel-pi/6faf624ae843c6d3f381d4f913f79fd4 to your computer and use it in GitHub Desktop.
Using NtRaiseHardError to display a MessageBox
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace Undocumented.Windows
{
public static unsafe class MessageBox
{
// http://undocumented.ntinternals.net/index.html?page=UserMode%2FUndocumented%20Functions%2FError%2FNtRaiseHardError.html
private static readonly delegate* unmanaged[Stdcall]<uint, uint, uint, uint*, uint, uint*, uint> _ntRaiseHardError;
static MessageBox()
{
var ntdll = NativeLibrary.Load("ntdll.dll");
_ntRaiseHardError =
(delegate* unmanaged[Stdcall]<uint, uint, uint, uint*, uint, uint*, uint>)
NativeLibrary.GetExport(ntdll, "NtRaiseHardError");
}
public static int Show(string text, string caption, uint type)
{
// used when: MessageBoxW(null, text, caption, MB_DEFAULT_DESKTOP_ONLY | MB_SERVICE_NOTIFICATION)
// more advanced message boxes can be created with NtUserCreateWindow
if (caption == null)
{
// https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-messagebox
// The dialog box title. If this parameter is NULL, the default title is Error.
caption = "Error";
}
if (text == null)
{
// no text
// handle gracefully
text = string.Empty;
}
fixed (char* textPointer = text)
{
fixed (char* captionPointer = caption)
{
var textBuffer = new UnicodeString
{
Buffer = textPointer,
Length = (ushort)(text.Length * sizeof(char)),
MaximumLength = (ushort)(text.Length * sizeof(char))
};
var captionBuffer = new UnicodeString
{
Buffer = captionPointer,
Length = (ushort)(caption.Length * sizeof(char)),
MaximumLength = (ushort)(caption.Length * sizeof(char))
};
// the 4th (index 3) parameter can optionally be message box timeout value
var parameters = stackalloc nint[]
{
(nint)(&textBuffer),
(nint)(&captionBuffer),
(nint)type
};
Unsafe.SkipInit(out uint response);
var status = _ntRaiseHardError(
0x50000018u, // NtStatus -> STATUS_SERVICE_NOTIFICATION (0x40000018u) | HARDERROR_OVERRIDE_ERRORMODE (0x10000000u)
3u, // length of parameters array
3u, // always 3
(uint*)parameters,
default, // turns out to be OptionOk for default which is 0 i believe.
&response);
if (status != 0)
{
// error
// https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-messagebox
// msdn: If the function fails, the return value is zero. To get extended error information, call GetLastError.
// SetLastError with RtlNtStatusToDosError
return 0;
}
return (int)response;
}
}
}
internal struct UnicodeString
{
public ushort Length;
public ushort MaximumLength;
public char* Buffer;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment