Date: | June 12, 2006 / year-entry #196 |
Tags: | code |
Orig Link: | https://blogs.msdn.microsoft.com/oldnewthing/20060612-00/?p=30903 |
Comments: | 20 |
Summary: | 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... |
Occasionally I catch people doing things like broadcasting
a 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
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 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
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.
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 The same logic applies to nearly all of the standard Windows messages. The ones that are actually designed to be broadcast are as follows:
If you try to broadcast a message in the
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
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)
Comments are closed. |
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".
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 :)
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.
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.
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.
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.
Most of the time, when people broadcast WM_USER or WM_APP messages, they should be calling RegisterWindowMessage and broadcasting that.
"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.
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.
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?
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.)
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.)
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…
> 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).
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….
"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.
"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.
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!