- EnumWindows thực chất gọi thẳng vào Win32u!NtUserBuildHwndList - một syscall hookable qua Shadow SSDT, không phải API thông thường.
- DwmGetWindowAttribute được tài liệu hóa là hỗ trợ Vista, nhưng bên trong nó forward sang GetWindowCompositionAttribute trong user32.dll - hàm chỉ hỗ trợ từ Windows 7.
- Mọi call chain cuối cùng đều kết thúc ở win32u.dll - tầng syscall stub của kernel Win32.
TL;DR
Một thread từ vxunderground khám phá chuỗi gọi hàm thật sự ẩn sau ba Windows GUI API phổ biến nhất: EnumWindows, IsWindowVisible, và DwmGetWindowAttribute. Kết quả? Tài liệu chính thức của Microsoft nói một đằng, bên trong làm một nẻo - và đây là điều mà cả malware author lẫn security researcher đều cần hiểu rõ.
Bối cảnh: Khi tài liệu nói dối (nhẹ thôi)
Windows API có nhiều lớp. Developer thường chỉ tương tác với lớp ngoài cùng - các hàm documented trong Win32 API. Nhưng bên dưới đó là một chuỗi forward đến các hàm undocumented, rồi đến syscall stub, rồi mới vào kernel.
vxunderground mô tả đây là "silly and overly complicated malware concept" - nhưng những gì họ phát hiện ra lại cực kỳ thực tế với bất kỳ ai viết code tương tác với Windows window management.
Ba chuỗi gọi hàm bị mổ xẻ
1. Enumerate windows
Mọi người dùng EnumWindows để liệt kê tất cả top-level windows. Nhưng đây là những gì thật sự xảy ra:
User code gọi
EnumWindows(user32.dll)user32 forward vào
Win32u!NtUserBuildHwndList(win32u.dll)NtUserBuildHwndList là một syscall - transition thẳng vào kernel mode
Điểm quan trọng: NtUserBuildHwndList nằm trong Shadow SSDT (System Service Descriptor Table dành riêng cho Win32/GUI calls). Đây là lý do các security product và rootkit thường hook tại đây để chặn/filter window enumeration ở tầng kernel.
2. Kiểm tra visibility
IsWindowVisible(hwnd) trông như một API complex, nhưng bên trong chỉ là:
hWnd->style & WS_VISIBLEMột phép bitwise AND. WS_VISIBLE = 0x10000000 - một bit trong window style flags. Không có syscall, không có kernel transition.
Caveat quan trọng: WS_VISIBLE = TRUE không có nghĩa window thật sự hiển thị trên màn hình. Window có thể bị che hoàn toàn bởi window khác, bị đẩy ra ngoài màn hình, hoặc trên Windows 10+ bị "cloaked" (virtual desktop khác). Để check cloaked state cần dùng DwmGetWindowAttribute(DWMWA_CLOAKED).
3. Lấy screen coordinates chính xác - chỗ thú vị
Tài liệu Microsoft khuyên dùng DwmGetWindowAttribute thay vì GetWindowRect để lấy tọa độ chính xác (DWM trả về visual bounds, GetWindowRect bao gồm cả shadow/invisible resize border).
Call chain thật sự:
DwmGetWindowAttribute(dwmapi.dll) - documented, Vista+, trả vềHRESULTForward sang
GetWindowCompositionAttribute(user32.dll) - undocumented, Windows 7+, trả vềBOOLSyscall vào
Win32u!NtUserGetWindowCompositionAttribute(win32u.dll)
Và đây là điều buồn cười: DwmGetWindowAttribute được documented là hỗ trợ Vista. Nhưng hàm nó forward sang - GetWindowCompositionAttribute - chỉ tồn tại từ Windows 7.
Về lý thuyết, DwmGetWindowAttribute có thể chạy trên Vista... cho đến khi nó cố gọi một hàm Windows 7-only. Microsoft biết điều này và có lẽ quyết định: "lol ok whatever man".
Kỹ thuật đằng sau
GetWindowCompositionAttribute là hàm undocumented:
Không có header file, không có import library
Phải load thủ công qua
LoadLibrary("user32.dll")+GetProcAddressSignature khác với DwmGetWindowAttribute - parameters được pack vào một struct
WINDOWCOMPOSITIONATTRIBDATAMột số attribute values khác với DwmGetWindowAttribute (ví dụ:
DWMWA_DISALLOW_PEEK=0x11thay vì giá trị standard)Also known as
NtUserGetWindowCompositionAttribute- hint rằng đây thực chất là native API layer
Tại sao win32u.dll? Microsoft đã tách các GUI/Win32 syscall stubs ra khỏi user32.dll và gdi32.dll vào một DLL riêng - win32u.dll - để phân tách rõ ràng giữa API wrapper và syscall interface. Mọi GUI operation cuối cùng đều đi qua đây.
Ai cần biết điều này?
Malware authors: Window enumeration là kỹ thuật phổ biến để detect debuggers/AV (scan window titles), implement keyloggers (track active window), và screen capture. Biết hàm nào thật sự syscall vào kernel giúp bypass user-mode hooks đặt ở lớp Win32 API.
Security researchers & AV developers: Nếu muốn monitor window enumeration, hook tại EnumWindows là không đủ - attacker có thể gọi thẳng NtUserBuildHwndList. Hook tại Shadow SSDT (kernel level) mới thật sự bắt được.
Game developers & overlay authors: Hiểu tại sao DwmGetWindowAttribute(DWMWA_EXTENDED_FRAME_BOUNDS) cho kết quả khác GetWindowRect - DWM bounds là visual bounds thật sự, phù hợp cho overlay và screenshot.
Timeline
Mốc | Sự kiện |
|---|---|
2007 - Windows Vista | DwmGetWindowAttribute ra đời cùng Desktop Window Manager |
2009 - Windows 7 | GetWindowCompositionAttribute xuất hiện trong user32.dll |
2012 - Windows 8+ | Cloaked windows concept (virtual desktops) |
2017 | Windows Vista end-of-support (Vista/Win7 gap mất ý nghĩa thực tế) |
2025 | vxunderground thread khám phá lại những quirk này |
Takeaway
Windows API là một mê cung của forwarding chains. Hàm documented ngoài cùng thường chỉ là một wrapper mỏng ẩn một hoặc nhiều lớp undocumented bên dưới. Đối với security research, điều quan trọng là hiểu nơi thật sự mà một operation xảy ra - không phải hàm bạn gọi, mà là syscall cuối cùng chạm vào kernel.
Như vxunderground kết luận sau khi trace toàn bộ chain: "lol ok whatever man" - đôi khi đó là cách tốt nhất để react với những quirk của Windows internals tích lũy qua 30+ năm backwards compatibility.
Via: vxunderground on X, Microsoft Docs - DwmGetWindowAttribute, Microsoft Docs - GetWindowCompositionAttribute.