0x637c777b

What ndphlpr.vxd does

I saw today a video from a guy, MattKC, partially porting .NET 2.0 to Windows 95 and at some point he stumbled upon an error due to a strange driver named ndphlpr.vxd about which nothing can be found online:
https://youtu.be/CTUMNtKQLl8?t=1908

So I got curious and downloaded the Windows 95 Driver Development Kit (https://archive.org/details/DDK-9x-ME), thinking that maybe it would reference ndphlpr in the docs. But after grep'ing through the binaries I had no luck.

There was a Windows 98 SE (the best Windows!) VM collecting dust on my hard drive for ages, the original CPU it was installed on was a Phenom II! Which made VMware Workstation complain about different, available virtualization features since I am now on an Intel CPU. With that fixed, I was forced to download and install .NET 2.0 to get the ndphlpr.vxd file. It would not install at first because Internet Explorer was at version 5.0 but required 5.01...

^ Nostalgic for BSODs from user mode 😊

Searching for references inside the binaries of the full .NET installation tells us that ndphlpr is used inside mscorwks.dll and for that Microsoft will probably share the symbols!

And they do:
A "ndphlpr" string is referenced twice in the InitThreadManager() function. The naming of the function RunningOnWin95() suggests that at some point .NET was supposed to support Windows 95, since the function was not renamed to RunningOnWin9X().

An alternative path for Windows NT calls the standard GetThreadContext() API:

The first call to ndphlpr with the control code 0x86427531, its not clear what it does yet:

The ndphlpr device is also used in the Get/Set9XThreadContext() functions. Control code 0x86421357 gets and 0x8642135A sets the thread context for Win9X systems:

Thankfully, ndphlpr.vxd is only 6 kB. We can immediately see the control codes:

0x86427531 seems to be used to get the process and thread handles:

The DDK comes in handy:

More information about the .vxd file format:
https://www-user.tu-chemnitz.de/~heha/vxd/vxd.htm

Now, considering MattKC fixes the error by intercepting the call to DeviceIoControl and forcing OutBuffer to 0x40 like this:

if (hDevice == (HANDLE) 0xCAFEBABE) {
  if (dwIoControlCode == 0x86427531) {
    *(DWORD*)lpOutBuffer = 0x40;

mscorwks.dll will populate it's own methods with functions that require ndphlpr.vxd to be loaded and working:

Any time mscorwks.dll calls:

Thread::HandledJITCase
Thread::RedirectThreadAtHandledJITCase
Thread::InitRegDisplay
Thread::ResumeUnderControl
Thread::HandleJITCaseForAbort

it will crash. Though I don't know when this happens and if any .NET 2.0 apps use some API that ultimately leads there. Its not guaranteed that suspending or resuming a thread triggers it but it looks like it does:

PS: MattKC also used a terrible debugger in his research, while OllyDbg works perfectly fine on Windows 95.