Remember what happens when you broadcast a message


Occasionally I catch people doing things like broadcasting a WM_COMMAND message to all top-level windows. This is one of those things that is so obviously wrong I don't see how people even thought to try it in the first place.

Suppose you broadcast the message

SendMessage(HWND_BROADCAST, WM_COMMAND, 100, 0);

What happens?

Every top-level window receives the message with the same parameters, and every top-level window starts interpreting those parameters in their own idiosyncratic way. As you know (since you've written them yourself), each window procedure defines its own menu items and child windows and there is no guarantee that command 100 will mean the same thing to each window. A dialog box with the template

#define IDC_USEDEFAULT 100
...
    AUTORADIOBUTTON "Use &default color",
                    IDC_USEDEFAULT, 14, 38, 68, 10, WS_TABSTOP

would interpret the message as

id  IDC_USEDEFAULT (100)
command  BN_CLICKED (0)
window  NULL (0) — illegal parameter

Depending on how the dialog procedure is written, it might try to send a message back to the button control (and fail since you passed NULL as the window handle), or it might update some dialog state like disabling the color customization controls (since it was told that the user clicked the "User default color" radio button).

Another dialog box might have the template

#define IDC_CHANGE 100
...
    PUSHBUTTON      "C&hange", IDC_CHANGE, 88, 95, 50, 14

This dialog procedure would interpret the message as

id  IDC_CHANGE (100)
command  BN_CLICKED (0)
window  NULL (0) — illegal parameter

The reaction would probably be to apply the changes that were pending in the dialog.

Meanwhile, another window might have a menu that goes like this:

#define IDC_REFRESH 100
...
        MENUITEM "&Refresh", IDC_REFRESH

It is going to interpret the message as the user having selected "Refresh" from the window menu.

id  IDC_REFRESH (100)
command  0 — illegal parameter, must be 1 for menu items
window  NULL (0)

Not only is the command code invalid for a menu item, the window might be in a state where the program had disabled the "Refresh" option. Yet you sent the window a message as if to say that the user selected it anyway, which is impossible. Congratulations, you just presented the program with an impossible situation and it very well may crash as a result. For example, the program may have disabled the "Refresh" option since there is no current object to refresh. When you send it the "Refresh" command, it will try to refresh the current object and crash with a null pointer error.

Obviously, then, you cannot broadcast the WM_COMMAND message since there is no universal meaning for any of the command IDs. A command ID that means "Refresh" to one window might mean "Change" to another.

The same logic applies to nearly all of the standard Windows messages. The ones that are actually designed to be broadcast are as follows:

WM_SYSCOLORCHANGE
WM_SETTINGCHANGE (= WM_WININICHANGE)
WM_DEVMODECHANGE
WM_FONTCHANGE
WM_TIMECHANGE
WM_DDE_INITIATE

If you try to broadcast a message in the WM_USER or WM_APP ranges, then you're even crazier than I thought. As we've already seen, the meaning of window messages in those ranges are defined by the window class or the application that created the window. Not only are the parameters to the message context-sensitive, the message itself is! This means that sending a random window a WM_USER+1 message (say) will result in extremely random behavior. (We saw this before in the context of broadcasts, but it applies to directed delivery, too.) If it's a dialog box, it will think you sent a DM_SETDEFID message, and you just changed that dialog's default ID. If it's a common dialog box, it will think you sent a WM_CHOOSEFONT_GETLOGFONT message, and if you're lucky, it will crash trying to return the LOGFONT through an invalid pointer. (If you're not lucky, the parameter you passed will happen to be a valid pointer and the program will merely corrupt its own memory in some strange way, only to behave erratically later on.) If it's a tooltip control, then you just sent it the TTM_ACTIVATE message and you just manipulated the tooltip's activation state.

The same caution applies, using the same logic, to sending messages without universal meaning to windows whose window class you do not have an interface contract with. For example, I'll see people sending the PSM_PRESSBUTTON message to a window on the blind-faith assumption that it is a property sheet.

Remember, then, that when you send a message to a window, you need to be sure that the window will interpret it in the manner you intend.

Comments (20)
  1. Sean W. says:

    Does this imply that SendMessage(HWND_BROADCAST) is a (minor) security hole?  If an application can use something like SendMessage(HWND_BROADCAST, WM_USER+1, 0, 0) to potentially send every other app into a tizzy in the worst-case scenario, wouldn’t this be an easy target for things like viruses, worms, and ActiveX malware?  Certainly it seems to violate the virtual-machine principle of "process X must be prohibited from doing anything that would damage the execution of a random process Y".

  2. davidacoder says:

    So, the natural question is: Why allow it? Are there situations where broadcasting these messages actually makes sense? Or is this for backward compatability? But that would be strange, to be backward compatible with regards to clearly wrong behaviour of apps :)

  3. Arlie Davis says:

    No, it isn’t a security hole.  Think about it.  By the time a piece of code has the ability to call SendMessage, it has access to the entire Win32 API.  There’s no need to convince some other process to do something for you — you can already do it directly.

    All processes run in a specific window station (see CreateWindowStation) and a specific desktop (see CreateDesktop).  SendMessage (and many other user32.dll APIs) can only work between processes within the same Desktop.  Apps that have different security contexts never run in the same desktop.  Therefore, you can’t use this to get around security.

  4. Coleman says:

    Good article about what not to do.  

    Joe Newcomer has a couple of great windows message articles on his MVP site.

    http://www.flounder.com/messaging.htm

    http://www.flounder.com/messages.htm

    To play devil’s advocate, Raymond, these things aren’t readily evident in the MSDN.  

    [I consider it a “So what did you expect?” type of issue. I mean, broadcasting WM_COMMAND? What did you expect would happen? -Raymond]

    I’ve come across Win32 code that still uses WM_USER for it’s
    messaging.  Is that a Bad Thing?  You bet, but it worked fine
    in Win95 when the app was first developed and there was “no reason to
    change it”.   Some “tips” tell developers to broadcast WM_USER
    messages to prevent multiple instances of the app running.  
     Again, I’m sure that worked at some point, but no longer.

  5. So why doesn’t SendMessage filter out the messages that aren’t intended for broadcast?

    Also, w.r.t. SendMessage being a security hole – SendMessage sends messages to the apps on the sam desktop as the caller of SendMessage, since the desktop is considered a security boundary, there’s no security hole because any app on the desktop can send ANY message to any other app on the desktop.

  6. AB says:

    Most of the time, when people broadcast WM_USER or WM_APP messages, they should be calling RegisterWindowMessage and broadcasting that.

  7. Name required says:

    "Apps that have different security contexts never run in the same desktop."  Actually they can.  Services can be granted the ‘interact with desktop’ priviledge.  Also the RunAs feature puts different contexts on the same desktop.  Exploits of this feature are called shatter attacks: http://www.microsoft.com/technet/archive/security/news/htshat.mspx

    A while back I experemented with locking down XP with LUA.  RunAs was just as important to me as sudo is on a unix system.  My solution to the shatter attack was to CreateDesktop a second virtual desktop (setting a custom ACL on it) to do my RunAs activity.  SwitchDesktop is extremely fast compared to the ‘switch user’ feature.

  8. Why not disable HWND_BROADCAST for all but the half-dozen messages designed for it? Because then nobody can design a new message for it except Microsoft.

    It’s unlikely that many applications have command IDs in the 0x3000-0x4FFF range, for example. If you defined a message somewhere in that range, you could potentially create an open standard for some message to do something neat with HWND_BROADCAST. Very few applications would conflict with it, so it would be a reasonably responsible choice. But if only the six identified messages can use HWND_BROADCAST, you can’t do that at all.

    My personal instinct on this is to have a flag on the window somewhere that says "accept non-standard broadcasts". If the application doesn’t set that, it gets the six standard broadcasts only. If it does, it gets anything anyone wants to broadcast. The question is whether this is a big enough problem to make such a change on a global level.

  9. Gabe says:

    Why bother restricting it? Sure, people can do stupid things, but what if it makes sense to broadcast a message in certain situations?

    For example, WM_SYSCOMMAND with wParam=SC_MINIMIZE should minimize all windows, but wParam=SC_NEXTWINDOW doesn’t make sense. Should broadcasts be disallowed for some WM_SYSCOMMAND commands but not others?

    What if I’m running an app on a version of Windows Embedded where I know that the only windows will be mine? Should I have to circumvent the message broadcast filter now?

  10. A says:

    For example, WM_SYSCOMMAND with wParam=SC_MINIMIZE should minimize all windows

    What if some of the windows are disabled or hidden? "Congratulations, you just presented the program with an impossible situation and it very well may crash as a result."

  11. Ken Hagan says:

    How often does this happen? (I presume it does, since Raymond is writing about it.) It would be easy to restrict broadcast to the few messages designed for it, plus anything in the RegisterWindowMessage range. Would that be sufficient? What if broadcast were permitted to windows whose associated executable lived in the same folder as the broadcaster? (OK, that creates problems with defining the broadcaster, since a bad EXE may use a decent DLL to actually dispatch the broadcast.)

  12. BryanK says:

    To everyone recommending restricting the messages that may be broadcast:

    Aren’t there issues with broadcasting even those "designed-to-be-broadcast" messages, too?  For one, if any window is hung, the rest of the system hangs too, waiting for the hung window to respond, right?  (Or is it just that the sending process hangs until all messages have been completely broadcast?  Either way, it’s not all that great.)

    Actually, that may only be for SendMessage; PostMessage (if it allows you to post to HWND_BROADCAST) may not be affected by another hung window.  (Though the entire system might be, I’m not sure; it probably just depends on the window manager.)

  13. Sven says:

    The Tortoise CVS context menu extension sends a WM_COMMAND with wParam=41504 to the host application after a source control operation is finished. Explorer uses 41504 to refresh its windows, in my application 41504 is in the command range for displaying Internet favorites. Imagine, 41504 would delete files without notice…

  14. BryanK says:

    > Apps that have different security contexts never run in the same desktop.

    I do not believe this is correct.

    If you’re running as a completely non-privileged user, but you have a service running as localsystem with "allow service to interact with the desktop" turned on, then this is a large problem.  Actions that you as a user cannot take can be taken by the service (because it’s localsystem).

    The desktop is a message boundary, yes, but different security contexts *do* run on the same desktop.  List of services logging on as localsystem with access to my desktop right now (2K Pro):

    apcupsd  (I should submit a patch to them to fix this; the only reason it’s on the desktop is to show the status of the UPS, which can be done from another process.)

    ASMAgent  (some dumb "track what software you have installed" program required by corporate)

    Distributed Transaction Coordinator

    Fax Service  (manual, off)

    Iap  (some dumb Dell thing that I should try getting rid of, to see what that would do)

    Internet Connection Sharing (manual, off)

    Machine Debug Manager

    Netmeeting Remote Desktop Sharing (manual, off)

    Network Connections

    Print Spooler

    Protected Storage (!!!  This holds cert private keys…)

    QoS RSVP  (manual, off)

    Remote Access Auto Connection Manager (manual, off)

    Remote Access Connection Manager (manual, on)

    Remote Administrator (our local remote management program; it needs local desktop access so remote control works)

    Removable Storage

    Routing and Remote Access (disabled)

    RunAs Service

    Still Image Service

    Task Scheduler

    Utility Manager  (manual, off)

    I’m not even sure what many of these services do, but I doubt that many of them need access to the desktop.  It’s not usually that hard to have a separate program running on the desktop that talks to the localsystem service (using e.g. sockets, not some kind of RPC that’s based on windows messages), and does whatever desktop stuff is required.

    Notably missing from this list is our virus scanner program; apparently McAfee is smart enough to do their stuff differently, or something.  All their services log on as localsystem, but run on their own desktop (as it should be).

  15. ::wendy:: says:

    discalimer – I’m not a programer

    ignore this question if its glaringly obvious.

    if people (developers) are sending broadcast messages then pressumably they had an intention,  perhaps a ‘need’ to do this action that raymond deftly explians just doesn’t make sense based on how windows are coded.

    what’s the need?

    Is there an implication that there could be value in having some form of broadcast message?  would that open-up developer creativity or cause potential chaos.

    just wondering….

  16. Dude says:

    "No, it isn’t a security hole.  … By the time a piece of code has the ability to call SendMessage, it has access to the entire Win32 API."

    True, any app can call any Win32 API if it can SendMessage. That does not mean all apps receive equal treatement for all API!

    Just one example, many firewalls are selective about which apps have network access – like the Windows firewall.

    Hypothetical example, IE can open outbound connections. Windows firewall will block my spyware and prompt user. My app uses SendMessage to convince IE to open a connection and send personal info to my server. Sure, I can create a thread in the IE process to do the same – but what if I don’t have debug privs?

    What if the app is on Windows’ DEP exception list? Those are precisely the apps where a stack-smashing or heap overflow attack will work!

    Traditional ACL security contexts are NOT THE ONLY things that needs to be secured.

    SendMessage is an example why Shatter attacks are so dangerous.

  17. Norman Diamond says:

    The ones that are actually designed to be

    > broadcast are as follows:

    Thank you for starting that list.

    What is your opinion of this one:

    http://msdn.microsoft.com/library/default.asp?url=/library/en-us/power/base/wm_powerbroadcast.asp

    Monday, June 12, 2006 11:10 AM by LarryOsterman

    > there’s no security hole because any app on

    > the desktop can send ANY message to any other

    > app on the desktop.

    Hmm, thank you for pointing this out.  Even with RunAs and the the additional verification steps being presented by Vista beta 2, shatter attacks DO remain.  For example you’ve properly edited and compiled a driver using your limited privilege account, but when you need to install the driver you need to do a RunAs.  Once that command prompt or other window pops up on the desktop, it will obey any commands that are sent to it by the limited privilege virus.

    Monday, June 12, 2006 3:50 PM by A

    >> For example, WM_SYSCOMMAND with

    >> wParam=SC_MINIMIZE should minimize all

    >> windows

    >

    > What if some of the windows are disabled or

    > hidden? "Congratulations, you just presented

    > the program with an impossible situation and

    > it very well may crash as a result."

    Yeah, good question.  What if you click on the icon that says "show desktop" ( == minimize all windows) but some windows are disabled or hidden?  Some of the windows might even be Control Panel dialogs which are never capable of being minimized, and/or are disabled and/or have property sheets open but hidden behind them.  When you click Show Desktop, does that present Windows with an impossible situation?

    Monday, June 12, 2006 5:04 PM by BryanK

    > Aren’t there issues with broadcasting even

    > those "designed-to-be-broadcast" messages,

    > too?  For one, if any window is hung, the

    > rest of the system hangs too, waiting for

    > the hung window to respond, right?

    I think so.  I think this has been a reason why attempts to shut down or hibernate result in hangs if Internet Explorer or occasionally other applications are open at the time.

  18. And nothing else shatter says:

    > Yet you sent the window a message as if to say that the user selected it anyway, which is impossible. Congratulations, you just presented the program with an impossible situation and it very well may crash as a result.

    We have two bugs here :

    first, that message should not be broadcasted because it doesn’t know which action will cause in other windows.

    On the other hand we have an application relying on user interface status (a menu disabled/enabled) to keep track of its internal state which is plain wrong.

  19. A says:

    "What if you click on the icon that says "show desktop" ( == minimize all windows) but some windows are disabled or hidden?"

    IIRC, "Show Desktop" is just like the old "Minimize All Windows" command, except it also temporarily brings Explorer’s "desktop" window to the top. This effectively hides all windows, without presenting them with any impossible situations.

  20. Ken Hagan says:

    To "And nothing else shatter"…

    > On the other hand we have an application relying on user interface status (a menu disabled/enabled) to keep track of its internal state which is plain wrong.

    I disagree. There’s a difference between defending against end-users and defending against applications. For example, there are valid reasons for using the debugging APIs against applications that you didn’t write, but obviously you have to be careful because you can’t expect the app’s author to defend against your mistakes in this area!

Comments are closed.

Skip to main content