Skip to content

Commit

Permalink
Add README
Browse files Browse the repository at this point in the history
  • Loading branch information
dechamps committed Feb 6, 2022
1 parent 42e7e3c commit 77034d2
Show file tree
Hide file tree
Showing 2 changed files with 153 additions and 0 deletions.
153 changes: 153 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
# WindowInvestigator: a window manager investigation toolbox
*Brought to you by [Etienne Dechamps][] - [GitHub][]*

WindowInvestigator is a small set of highly specialized tools that can be used
to investigate Windows window manager issues. It was developed with the goal of
investigating the behaviour of a piece of internal Windows code called the Rude
Window Manager; see [RudeWindowFixer][] for more background. However it might
also be useful in other scenarios.

For example, WindowInvestigator is what made it possible to come up with
analyses like the following:

![Firefox timeline](firefox-timeline.png)

## WindowManagementLogging tracing

Some of the internal Windows shell window management code, including Rude Window
Manager code, uses an internal class called `WindowManagementLogging` to log
various events and the results of computations. I found these logs to be
extremely relevant to my investigation.

`WindowManagementLogging` acts as an [Event Tracing for Windows (ETW)][]
provider. Through binary reverse engineering, it has been determined that the
provider GUID is `F84AA759-31D3-59BF-2C89-3748CF17FD7E`.

You can use this GUID in event consumers to receive the log. For example you
can enter the GUID directly in [TraceView][] for real time logging, or you can
load the included [`WindowManagementLogging.wprp`][] [recording profile][] into
the [Windows Performance Recorder (WPR)][] for a more thorough analysis.

## WindowMonitor

WindowMonitor is a command line tool that is aimed at investigating window
states and how they change over time. If called without any command line
arguments, it will:

- Dump an initial list of all [visible windows][] to the standard output, along
with the values of various window properties.
- This is to provide the initial reference starting point.
- Create a [message-only window][] that:
- Registers and listens to [shell hook messages][].
- The message identifier for shell hook messages is typically `0xC029`, but
that is not necessarily always the case. Look for the `Started` trace
event to determine the actual message identifier.
- Registers and listens to [appbar][] messages.
- WindowMonitor uses `WM_USER` (`0x400`) as the identifier for appbar
messages.
- An appbar message to watch out for is [`ABN_FULLSCREENAPP`][] which
is sent as a consequence of the monitor rudeness state changing.
- Sends a [`WM_TIMER`][] (`0x113`) message to itself every 16 milliseconds.
- Every time any message is received on the aforementioned window, WindowMonitor
logs it through an [Event Tracing for Windows (ETW)][] provider.
- The provider GUID is `500D9509-6850-440C-AD11-6EA625EC91BC`. You can enter
that GUID directly in [TraceView][] for real time logging, or you can load
the included [`WindowInvestigator.wprp`][] [recording profile][] into the
[Windows Performance Recorder (WPR)][] for a more thorough analysis.
- In addition to logging the message itself, every time a message is received,
WindowMonitor will go through every visible window and log any changes that
may have occurred to any of the watched window properties (e.g. styles,
position) since the last message.
- This also includes changes to the Z-order, which are determined by watching
for changes in the order in which windows are returned from
[`EnumWindows()`][].

WindowMonitor can also be called with a specific window handle as a command line
argument (e.g. `WindowMonitor.exe 0x4242`). In that case, WindowMonitor will not
listen for messages; instead, it will check the state of that window every 2
milliseconds and will log any changes. Note that Z-order changes are not
reported in this mode. This single-window mode is useful when you need more
precise timing information.

Note: it is recommended to run WindowMonitor as Administrator; this will allow
it to set the Real-Time [process priority class][] to achieve the most precise
timing.

Note: WindowMonitor might not run on some Windows versions because it retrieves
a couple of undocumented window properties through reverse-engineered
`user32.dll` Windows API entry points that might not exist in all versions. You
might need to adjust the code to remove calls to these APIs.

## DelayedPosWindow

This extremely simple tool simply displays a standard window that has the
particularity of injecting a customizable delay in the processing of
[`WM_WINDOWPOSCHANGING`][] messages. This is useful to simulate applications
that are slow to process these messages (e.g. Firefox), and to force latent race
conditions to the surface.

The delay is specified in milliseconds as a command line argument. For example,
`DelayedPosWindow.exe 100` will make every [`WM_WINDOWPOSCHANGING`][] message
take ~100 ms to process.

Similar to WindowMonitor, DelayedWindowPos will trace every window message
received, as well as details of `WM_WINDOWPOSCHANGING` messages. The trace
provider details are the same as WindowMonitor.

## TransparentFullscreenWindow

This trivial tool simply displays a window that has the `WS_EX_LAYERED` and
`WS_EX_TRANSPARENT` [extended window styles][]. It also makes the window full
screen by setting its dimensions to be the same as the screen. This emulates
a "sneaky" full screen window such as the GeForce Experience overlay window.

Note that this tool has only been tested in a single-monitor setup. The
dimensions of the window might be incorrect on a multi-monitor setup.

## Other recommended tools

- [GuiPropView][] is a nice tool for looking at window properties in general.
- [Spy++][] is useful to monitor the messages that a given window is receiving.
- Make sure to use the 64-bit version when monitoring a 64-bit process,
otherwise no messages will be shown.
- For reverse engineering Windows binaries, I used [Ghidra][].
- I found it to be quite effective, although the relevant code is littered
with C++ virtual function calls which are sadly [very][ghidrav1]
[painful][ghidrav2] to analyse in Ghidra.
- Whatever software you use, do make sure to use the [public Microsoft
symbols][] as these will make your life much easier!

## Developer information

[![.github/workflows/continuous-integration.yml](https://github.com/dechamps/WindowInvestigator/actions/workflows/continuous-integration.yml/badge.svg)](https://github.com/dechamps/WindowInvestigator/actions/workflows/continuous-integration.yml)

WindowInvestigator is designed to be built using CMake within the Microsoft
Visual C++ 2019 toolchain native CMake support.

There are no dependencies besides the Windows SDK.

[`ABN_FULLSCREENAPP`]: https://docs.microsoft.com/en-us/windows/win32/shell/abn-fullscreenapp
[appbar]: https://docs.microsoft.com/en-us/windows/win32/shell/application-desktop-toolbars
[Etienne Dechamps]: mailto:etienne@edechamps.fr
[`EnumWindows()`]: https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-enumwindows
[Event Tracing for Windows (ETW)]: https://docs.microsoft.com/en-us/windows/win32/etw/about-event-tracing
[extended window styles]: https://docs.microsoft.com/en-us/windows/win32/winmsg/extended-window-styles
[GitHub]: https://github.com/dechamps/WindowInvestigator
[Ghidra]: https://ghidra-sre.org/
[ghidrav1]: https://github.com/NationalSecurityAgency/ghidra/issues/516
[ghidrav2]: https://github.com/NationalSecurityAgency/ghidra/issues/573
[GuiPropView]: https://www.nirsoft.net/utils/gui_prop_view.html
[message-only window]: https://docs.microsoft.com/en-us/windows/win32/winmsg/window-features#message-only-windows
[process priority class]: https://docs.microsoft.com/en-us/windows/win32/procthread/scheduling-priorities#priority-class
[public Microsoft symbols]: https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/microsoft-public-symbols
[recording profile]: https://docs.microsoft.com/en-us/windows-hardware/test/wpt/authoring-recording-profiles
[RudeWindowFixer]: https://github.com/dechamps/RudeWindowFixer
[shell hook messages]: https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-registershellhookwindow
[TraceView]: https://docs.microsoft.com/en-us/windows-hardware/drivers/devtest/traceview
[Spy++]: https://docs.microsoft.com/en-us/visualstudio/debugger/spy-increment-help
[visible windows]: https://docs.microsoft.com/en-us/windows/win32/winmsg/window-features#window-visibility
[`WindowManagementLogging.wprp`]: WindowManagementLogging.wprp
[`WindowInvestigator.wprp`]: WindowInvestigator.wprp
[Windows Performance Recorder (WPR)]: https://docs.microsoft.com/en-us/windows-hardware/test/wpt/windows-performance-recorder
[`WM_TIMER`]: https://docs.microsoft.com/en-us/windows/win32/winmsg/wm-timer
[`WM_WINDOWPOSCHANGING`]: https://docs.microsoft.com/en-us/windows/win32/winmsg/wm-windowposchanging
Binary file added firefox-timeline.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 77034d2

Please # to comment.