NtUserBuildHwndList fails

here is my code. I am basically calling NtUserBuildHwndList but for some reason it fails with STATUS_INVALID_HANDLE.

    typedef NTSTATUS(NTAPI *_NtUserBuildHwndList)(
HDESK hDesktop,
HWND hwndParent,
BOOLEAN bChildren,
ULONG dwThreadId,
ULONG lParam,
HWND *pWnd,
ULONG *pBufSize
);
    _NtUserBuildHwndList NtUserBuildHwndList;
    HMODULE win32 = LoadLibraryW(L"win32u.dll");
NtUserBuildHwndList = (_NtUserBuildHwndList)GetProcAddress(win32, "NtUserBuildHwndList");
if (!NtUserGetThreadDesktop)
	puts("major err");

printf("gle: %d\n", GetLastError());
DWORD dwBuffer = 0;
HWND* pBuf = NULL;
DWORD dwThread = GetCurrentThreadId();
HDESK hDesktopWindow = OpenInputDesktop(0, 0, GENERIC_ALL);

NTSTATUS Status = NtUserBuildHwndList(hDesktopWindow, NULL, FALSE, 0, 0, NULL, &dwBuffer);
if (!NT_SUCCESS(Status)) {
	printf("status 123: %x\n", Status);
}

The exported function is resolved correctly but calling NtUserBuildHwndList fails with STATUS_INVALID_HANDLE. What is the reason for this? I have not done anything incorrectly at all that I can tell of.

I have tried it again it still does not work? what am i doing wrong

I dont think this is the right place to ask about usermode code and undocumented at that but you should atleast be able to verify some of the undocumentedness with a debugger in hand

Your Prototype appears to be Wrong the function appears to be take 8 parameters instead of 7 as in your prototype

you should be aware win32u.dll is just a stub that does a syscall
the actual implementation is in win32kfull.sys

dumping the UNWIND_INFO for that particular function and looking for further argumets apart from 4 passed in registers

i can notice it takes 4 more parameters (total 8 in all)

here is how to do it

: kd> .fnent win32kfull!NtUserBuildHwndList
Debugger function entry 000001b6`b2179ae8 for:
(fffff65d`7aabc6c0)   win32kfull!NtUserBuildHwndList   |  (fffff65d`7aabc9ec)   win32kfull!xxxImeWindowPosChanged
Exact matches:
    win32kfull!NtUserBuildHwndList (void)

BeginAddress      = 00000000`0005c6c0
EndAddress        = 00000000`0005c9e5
UnwindInfoAddress = 00000000`002e5060

Unwind info at fffff65d`7ad45060, 1c bytes
  version 2, flags 1, prolog 16, codes 8
  handler routine: win32kfull!_C_specific_handler (fffff65d`7abaacd2), data 1
  00: offs 9, unwind op 6, op info 0	UWOP_EPILOG Length: 9. Flags: 0
  01: offs 5, unwind op 6, op info 1	UWOP_EPILOG Offset from end: 105 (FFFFF65D7AABC8E0)
  02: offs 16, unwind op 2, op info d	UWOP_ALLOC_SMALL.
  03: offs 12, unwind op 0, op info f	UWOP_PUSH_NONVOL reg: r15.
  04: offs 10, unwind op 0, op info d	UWOP_PUSH_NONVOL reg: r13.
  05: offs e, unwind op 0, op info c	UWOP_PUSH_NONVOL reg: r12.
  06: offs c, unwind op 0, op info 7	UWOP_PUSH_NONVOL reg: rdi.
  07: offs b, unwind op 0, op info 6	UWOP_PUSH_NONVOL reg: rsi.
0: kd> $$ UWOP_ALLOC_SMALL  opinfo is 0xd  so size allocated is 0xd * 0x8 + 0x8
0: kd> ? 0xd * 0x8 + 0x8
Evaluate expression: 112 = 00000000`00000070
0: kd> $$ homeparamspace = 0x20 + ret addr = 0x8 + 5 nv regs are saved = 0x28 
0: kd> so the fifth arg will be at 0xc0 and so on
Debug Options: <none>
0: kd> # mov*,*rsp+*c0  win32kfull+5c6c0 win32kfull + 5c9e5
win32kfull!NtUserBuildHwndList+0xb8:
fffff65d`7aabc778 8b8c24c0000000  mov     ecx,dword ptr [rsp+0C0h]
win32kfull!xxxImeWindowPosChanged+0x28d:
fffff65d`7aabcc79 4c8bbc24c0000000 mov     r15,qword ptr [rsp+0C0h]
win32kfull!FindWindowEx+0x1fe:
fffff65d`7aabd226 488b8c24c0000000 mov     rcx,qword ptr [rsp+0C0h]
0: kd> # mov*,*rsp+*c8  win32kfull+5c6c0 win32kfull + 5c9e5
win32kfull!NtUserBuildHwndList+0x12e:
fffff65d`7aabc7ee 8b9424c8000000  mov     edx,dword ptr [rsp+0C8h]
win32kfull!xxxImeWindowPosChanged+0x295:
fffff65d`7aabcc81 4c8bac24c8000000 mov     r13,qword ptr [rsp+0C8h]
win32kfull!FindWindowEx+0x246:
fffff65d`7aabd26e 4c8b8424c8000000 mov     r8,qword ptr [rsp+0C8h]
0: kd> # mov*,*rsp+*d0  win32kfull+5c6c0 win32kfull + 5c9e5
win32kfull!NtUserBuildHwndList+0x13f:
fffff65d`7aabc7ff 488b8c24d0000000 mov     rcx,qword ptr [rsp+0D0h]
win32kfull!NtUserBuildHwndList+0x189:
fffff65d`7aabc849 488b8c24d0000000 mov     rcx,qword ptr [rsp+0D0h]
win32kfull!FindWindowEx+0x2b7:
fffff65d`7aabd2df 448bb424d0000000 mov     r14d,dword ptr [rsp+0D0h]
0: kd> # mov*,*rsp+*d8  win32kfull+5c6c0 win32kfull + 5c9e5
win32kfull!NtUserBuildHwndList+0x153:
fffff65d`7aabc813 4c8ba424d8000000 mov     r12,qword ptr [rsp+0D8h]
0: kd> # mov*,*rsp+*e0  win32kfull+5c6c0 win32kfull + 5c9e5
0: kd> $$ function takes 8 params 
0: kd> $$ where did you find the prototype

just prototyping with random type it seems to work properly with here

#include <stdio.h>
#include <windows.h>
typedef NTSTATUS ( NTAPI * NtUserBuildHwndList)(
	UINT64 a, UINT64 b, UINT64 c, UINT64 d, UINT64 e, UINT64 f, UINT64 *g, UINT64 *h );
int main( void )
{
	NtUserBuildHwndList nubhwlist;	
	HMODULE hMod = LoadLibraryA("win32u.dll");
	printf("%I64x\n", hMod);
	if (hMod) {
		nubhwlist = (NtUserBuildHwndList) GetProcAddress(hMod, "NtUserBuildHwndList");
		if (nubhwlist) {
			printf("%I64x\n", nubhwlist);
			UINT64 count = 0xbeadb0b5beadb0b5;
			UINT64 List[4096] = {  0xbeadb0b5 };
			NTSTATUS stat = nubhwlist(0, 0, 0, 0, 0, 512 , List, &count);
			printf("%x\t%I64x\n", stat, count);
		}
	}    
} 

resulting in 0x164 handles

:\>ntubhwlist.exe
7ff8710a0000
7ff8710a1410
0       beadb0b500000164

@raj_r said:
I dont think this is the right place to ask about usermode code and undocumented at that but you should atleast be able to verify some of the undocumentedness with a debugger in hand

Your Prototype appears to be Wrong the function appears to be take 8 parameters instead of 7 as in your prototype

you should be aware win32u.dll is just a stub that does a syscall
the actual implementation is in win32kfull.sys

dumping the UNWIND_INFO for that particular function and looking for further argumets apart from 4 passed in registers

i can notice it takes 4 more parameters (total 8 in all)

here is how to do it

: kd> .fnent win32kfull!NtUserBuildHwndList
Debugger function entry 000001b6`b2179ae8 for:
(fffff65d`7aabc6c0)   win32kfull!NtUserBuildHwndList   |  (fffff65d`7aabc9ec)   win32kfull!xxxImeWindowPosChanged
Exact matches:
    win32kfull!NtUserBuildHwndList (void)

BeginAddress      = 00000000`0005c6c0
EndAddress        = 00000000`0005c9e5
UnwindInfoAddress = 00000000`002e5060

Unwind info at fffff65d`7ad45060, 1c bytes
  version 2, flags 1, prolog 16, codes 8
  handler routine: win32kfull!_C_specific_handler (fffff65d`7abaacd2), data 1
  00: offs 9, unwind op 6, op info 0	UWOP_EPILOG Length: 9. Flags: 0
  01: offs 5, unwind op 6, op info 1	UWOP_EPILOG Offset from end: 105 (FFFFF65D7AABC8E0)
  02: offs 16, unwind op 2, op info d	UWOP_ALLOC_SMALL.
  03: offs 12, unwind op 0, op info f	UWOP_PUSH_NONVOL reg: r15.
  04: offs 10, unwind op 0, op info d	UWOP_PUSH_NONVOL reg: r13.
  05: offs e, unwind op 0, op info c	UWOP_PUSH_NONVOL reg: r12.
  06: offs c, unwind op 0, op info 7	UWOP_PUSH_NONVOL reg: rdi.
  07: offs b, unwind op 0, op info 6	UWOP_PUSH_NONVOL reg: rsi.
0: kd> $$ UWOP_ALLOC_SMALL  opinfo is 0xd  so size allocated is 0xd * 0x8 + 0x8
0: kd> ? 0xd * 0x8 + 0x8
Evaluate expression: 112 = 00000000`00000070
0: kd> $$ homeparamspace = 0x20 + ret addr = 0x8 + 5 nv regs are saved = 0x28 
0: kd> so the fifth arg will be at 0xc0 and so on
Debug Options: <none>
0: kd> # mov*,*rsp+*c0  win32kfull+5c6c0 win32kfull + 5c9e5
win32kfull!NtUserBuildHwndList+0xb8:
fffff65d`7aabc778 8b8c24c0000000  mov     ecx,dword ptr [rsp+0C0h]
win32kfull!xxxImeWindowPosChanged+0x28d:
fffff65d`7aabcc79 4c8bbc24c0000000 mov     r15,qword ptr [rsp+0C0h]
win32kfull!FindWindowEx+0x1fe:
fffff65d`7aabd226 488b8c24c0000000 mov     rcx,qword ptr [rsp+0C0h]
0: kd> # mov*,*rsp+*c8  win32kfull+5c6c0 win32kfull + 5c9e5
win32kfull!NtUserBuildHwndList+0x12e:
fffff65d`7aabc7ee 8b9424c8000000  mov     edx,dword ptr [rsp+0C8h]
win32kfull!xxxImeWindowPosChanged+0x295:
fffff65d`7aabcc81 4c8bac24c8000000 mov     r13,qword ptr [rsp+0C8h]
win32kfull!FindWindowEx+0x246:
fffff65d`7aabd26e 4c8b8424c8000000 mov     r8,qword ptr [rsp+0C8h]
0: kd> # mov*,*rsp+*d0  win32kfull+5c6c0 win32kfull + 5c9e5
win32kfull!NtUserBuildHwndList+0x13f:
fffff65d`7aabc7ff 488b8c24d0000000 mov     rcx,qword ptr [rsp+0D0h]
win32kfull!NtUserBuildHwndList+0x189:
fffff65d`7aabc849 488b8c24d0000000 mov     rcx,qword ptr [rsp+0D0h]
win32kfull!FindWindowEx+0x2b7:
fffff65d`7aabd2df 448bb424d0000000 mov     r14d,dword ptr [rsp+0D0h]
0: kd> # mov*,*rsp+*d8  win32kfull+5c6c0 win32kfull + 5c9e5
win32kfull!NtUserBuildHwndList+0x153:
fffff65d`7aabc813 4c8ba424d8000000 mov     r12,qword ptr [rsp+0D8h]
0: kd> # mov*,*rsp+*e0  win32kfull+5c6c0 win32kfull + 5c9e5
0: kd> $$ function takes 8 params 
0: kd> $$ where did you find the prototype

just prototyping with random type it seems to work properly with here

#include <stdio.h>
#include <windows.h>
typedef NTSTATUS ( NTAPI * NtUserBuildHwndList)(
	UINT64 a, UINT64 b, UINT64 c, UINT64 d, UINT64 e, UINT64 f, UINT64 *g, UINT64 *h );
int main( void )
{
	NtUserBuildHwndList nubhwlist;	
	HMODULE hMod = LoadLibraryA("win32u.dll");
	printf("%I64x\n", hMod);
	if (hMod) {
		nubhwlist = (NtUserBuildHwndList) GetProcAddress(hMod, "NtUserBuildHwndList");
		if (nubhwlist) {
			printf("%I64x\n", nubhwlist);
			UINT64 count = 0xbeadb0b5beadb0b5;
			UINT64 List[4096] = {  0xbeadb0b5 };
			NTSTATUS stat = nubhwlist(0, 0, 0, 0, 0, 512 , List, &count);
			printf("%x\t%I64x\n", stat, count);
		}
	}    
} 

resulting in 0x164 handles

:\>ntubhwlist.exe
7ff8710a0000
7ff8710a1410
0       beadb0b500000164

thank you!, Im guessing the function definition changed across different versions?))
That is what I am assuming :slight_smile: I will go and see across different win32u versions if anything is changed.

do not quote the entrie reply quote only parts or fragments generally refrain from quoting unless it is an absolutely necessary context