Jump to content

Apparent bug in 2k netman.dll


Recommended Posts

I'm *still* at it trying to get my Win2k kernel32 rewrite finished, and chasing the last few bugs is driving me batty. For anyone not up-to-date on my progress, my VM boots, but I'm getting a few errors in Event Viewer and this causes some application problems. I've been trying to hunt down the bugs via remote debugging and LOTS of debugging output in my rewritten DLL.

This would be a lot easier if I wasn't chasing red herrings. Case in point:

Error: exception 0xC0000005 (access violation). When a thread awakens from a call to Sleep(), it finds that EIP is invalid and throws an exception.

What's really happening: netman is making an API call that causes a thread to get spawned, but unloads the DLL before it can complete.

Here's an assembly code snippet from netman.dll:

.text:7627C5BB                 push    offset aWzcsvc  ; "wzcsvc"
.text:7627C5C0 call ds:LoadLibraryW
.text:7627C5C6 mov esi, eax
.text:7627C5C8 test esi, esi
.text:7627C5CA jz short loc_7627C5F0
.text:7627C5CC push offset aWzctrayiconrea ; "WZCTrayIconReady"
.text:7627C5D1 push esi ; hModule
.text:7627C5D2 call ds:GetProcAddress
.text:7627C5D8 test eax, eax
.text:7627C5DA jz short loc_7627C5E5
.text:7627C5DC lea ecx, [ebp+NameBuffer]
.text:7627C5E2 push ecx
.text:7627C5E3 call eax
.text:7627C5E5
.text:7627C5E5 loc_7627C5E5: ; CODE XREF: CConnectionManager::Advise(IUnknown *,ulong *)+18Fj
.text:7627C5E5 test esi, esi
.text:7627C5E7 jz short loc_7627C5F0
.text:7627C5E9 push esi ; hLibModule
.text:7627C5EA call ds:FreeLibrary
.text:7627C5F0
.text:7627C5F0 loc_7627C5F0:

And this is what it means in C:

hModule = LoadLibraryW(L"wzcsvc");
hModuleA = hModule;
if ( hModule )
{
WZCTrayIconReady = GetProcAddress(hModule, "WZCTrayIconReady");
if ( WZCTrayIconReady )
{
((void (__stdcall *)(WCHAR *))WZCTrayIconReady)(&NameBuffer);
}
if ( hModuleA )
{
FreeLibrary(hModuleA);
}
}

So basically it loads wzcsvc.dll, calls WZCTrayIconReady, and then unloads it. The problem is that WZCTrayIconReady spawns a worker thread via QueueUserWorkItem, and the worker routine does a Sleep() for 1 second. So when the call immediately returns to netman, it immediately unloads wzcsvc--and when the worker thread wakes up, BOOM! Even the MSDN documentation for QueueUserWorkItem() points out to make sure to not unload a DLL before the work item has completed.

XP fixes this by statically linking wzcsvc.dll into netman.dll. Now, it's possible that my slowing things down with all the debugging output is causing the error to manifest itself. I might have to fix netman before I can continue on my kernel32 rewrite mission.

(sigh)

Link to comment
Share on other sites


At least it was easy to fix...

Today's red herring: secur32.dll

.text:7C342FF0 ; void __stdcall SecpUnloadVMList(void)
.text:7C342FF0 ?SecpUnloadVMList@@YGXXZ proc near ; CODE XREF: ProcDetach(void *,void *)+30p
.text:7C342FF0 push esi
.text:7C342FF1 push edi
.text:7C342FF2 mov edi, offset ?SecVMListLock@@3U_RTL_CRITICAL_SECTION@@A ; _RTL_CRITICAL_SECTION SecVMListLock
.text:7C342FF7 push edi
.text:7C342FF8 call ds:RtlEnterCriticalSection
.text:7C342FFE mov esi, ?SecVMList@@3PAU_VMLIST@@A ; _VMLIST * SecVMList
.text:7C343004
.text:7C343004 loc_7C343004: ; CODE XREF: SecpUnloadVMList(void)+44j
.text:7C343004 test esi, esi
.text:7C343006 jz short loc_7C343036
.text:7C343008 push 8000h
.text:7C34300D lea eax, [esi+4]
.text:7C343010 push 0 <---------------- BUG
.text:7C343012 push eax
.text:7C343013 push 0FFFFFFFFh
.text:7C343015 call ds:NtFreeVirtualMemory
.text:7C34301B mov ecx, esi
.text:7C34301D mov esi, [esi]
.text:7C34301F mov eax, large fs:18h
.text:7C343025 mov eax, [eax+30h]
.text:7C343028 push ecx
.text:7C343029 push 0
.text:7C34302B push dword ptr [eax+18h]
.text:7C34302E call ds:RtlFreeHeap
.text:7C343034 jmp short loc_7C343004
.text:7C343036 ; ---------------------------------------------------------------------------
.text:7C343036
.text:7C343036 loc_7C343036: ; CODE XREF: SecpUnloadVMList(void)+16j
.text:7C343036 push edi
.text:7C343037 call ds:RtlLeaveCriticalSection
.text:7C34303D push edi
.text:7C34303E call ds:RtlDeleteCriticalSection
.text:7C343044 pop edi
.text:7C343045 pop esi
.text:7C343046 retn
.text:7C343046 ?SecpUnloadVMList@@YGXXZ endp

This routine is called by scur32!ProcDetach, which is called when secur32.dll is unloaded. The problem is highlighted above: the call to NtFreeVirtualMemory gets passed along to ntoskrnl ZwVirtualMemory:

NTSTATUS ZwFreeVirtualMemory(

_In_ HANDLE ProcessHandle,

_Inout_ PVOID *BaseAddress,

_Inout_ PSIZE_T RegionSize,

_In_ ULONG FreeType

);

The third parameter is a pointer to a SIZE_T, and it isn't optional. Consequently, ntoskrnl throws an access violation exception when it tries to dereference the null pointer.

This was also easy to fix, though neither of these two bugs fixed my existing kernel32 bugs (though they at least aren't getting in the way now). Sometime soon I'll issue another update to KB2393802 with updated netman.dll and secur32.dll files. At this rate the patch is turning into its own mini service pack -- I'm beginning to wonder if we need some sort of naming or numbering convention for issuing our own patches separate from ones that MS issues.

Edited by WildBill
Link to comment
Share on other sites

I'm beginning to wonder if we need some sort of naming or numbering convention for issuing our own patches separate from ones that MS issues.

That's why I started to add the "UU" prefix like when I was still preparing single unofficial updates, ex. Windows2000-UU-KB927489-v3-x86-Global.exe. This way you know that it's an unofficial package as soon as you look at its name. It's also very easy to filter when searching.

Link to comment
Share on other sites

I'm *still* at it trying to get my Win2k kernel32 rewrite finished, and chasing the last few bugs is driving me batty. For anyone not up-to-date on my progress, my VM boots, but I'm getting a few errors in Event Viewer and this causes some application problems. I've been trying to hunt down the bugs via remote debugging and LOTS of debugging output in my rewritten DLL.

...

I don't get it, how can you rewrite DLLs without the source code?

Link to comment
Share on other sites

Really ?

If the MEM_RELEASE flag is set in the FreeType parameter, the variable pointed to by RegionSize must be zero. ZwFreeVirtualMemory frees the entire region that was reserved in the initial allocation call to ZwAllocateVirtualMemory.

I think that it is correct.

And Windows XP also same code.

L77FA7122:

push 00008000h

push 00000000h < -

lea eax,[esi+04h]

push eax

push FFFFFFFFh

call [ntdll.dll!NtFreeVirtualMemory]

mov ecx,esi

mov esi,[esi]

mov eax,fs:[00000018h]

mov eax,[eax+30h]

push ecx

push 00000000h

push [eax+18h]

call [ntdll.dll!RtlFreeHeap]

jmp L77FA2350

At least it was easy to fix...

Today's red herring: secur32.dll

.text:7C342FF0 ; void __stdcall SecpUnloadVMList(void)
.text:7C342FF0 ?SecpUnloadVMList@@YGXXZ proc near ; CODE XREF: ProcDetach(void *,void *)+30p
.text:7C342FF0 push esi
.text:7C342FF1 push edi
.text:7C342FF2 mov edi, offset ?SecVMListLock@@3U_RTL_CRITICAL_SECTION@@A ; _RTL_CRITICAL_SECTION SecVMListLock
.text:7C342FF7 push edi
.text:7C342FF8 call ds:RtlEnterCriticalSection
.text:7C342FFE mov esi, ?SecVMList@@3PAU_VMLIST@@A ; _VMLIST * SecVMList
.text:7C343004
.text:7C343004 loc_7C343004: ; CODE XREF: SecpUnloadVMList(void)+44j
.text:7C343004 test esi, esi
.text:7C343006 jz short loc_7C343036
.text:7C343008 push 8000h
.text:7C34300D lea eax, [esi+4]
.text:7C343010 push 0 <---------------- BUG
.text:7C343012 push eax
.text:7C343013 push 0FFFFFFFFh
.text:7C343015 call ds:NtFreeVirtualMemory
.text:7C34301B mov ecx, esi
.text:7C34301D mov esi, [esi]
.text:7C34301F mov eax, large fs:18h
.text:7C343025 mov eax, [eax+30h]
.text:7C343028 push ecx
.text:7C343029 push 0
.text:7C34302B push dword ptr [eax+18h]
.text:7C34302E call ds:RtlFreeHeap
.text:7C343034 jmp short loc_7C343004
.text:7C343036 ; ---------------------------------------------------------------------------
.text:7C343036
.text:7C343036 loc_7C343036: ; CODE XREF: SecpUnloadVMList(void)+16j
.text:7C343036 push edi
.text:7C343037 call ds:RtlLeaveCriticalSection
.text:7C34303D push edi
.text:7C34303E call ds:RtlDeleteCriticalSection
.text:7C343044 pop edi
.text:7C343045 pop esi
.text:7C343046 retn
.text:7C343046 ?SecpUnloadVMList@@YGXXZ endp

This routine is called by scur32!ProcDetach, which is called when secur32.dll is unloaded. The problem is highlighted above: the call to NtFreeVirtualMemory gets passed along to ntoskrnl ZwVirtualMemory:

NTSTATUS ZwFreeVirtualMemory(

_In_ HANDLE ProcessHandle,

_Inout_ PVOID *BaseAddress,

_Inout_ PSIZE_T RegionSize,

_In_ ULONG FreeType

);

The third parameter is a pointer to a SIZE_T, and it isn't optional. Consequently, ntoskrnl throws an access violation exception when it tries to dereference the null pointer.

This was also easy to fix, though neither of these two bugs fixed my existing kernel32 bugs (though they at least aren't getting in the way now). Sometime soon I'll issue another update to KB2393802 with updated netman.dll and secur32.dll files. At this rate the patch is turning into its own mini service pack -- I'm beginning to wonder if we need some sort of naming or numbering convention for issuing our own patches separate from ones that MS issues.

Edited by blackwingcat
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...