I'd love to see Chrome and Firefox and IE working together to detect this kind of thing and put up a malware warning explaining how to uninstall it, and see how quickly it can be eliminated.
Seriously, there is zero valid reason to ever inject code into another program, other than as a debugging tool on a system being debugged.
The modding communities for Bethesda games rely heavily on code injection, especially the script extender plugins that are central to the entire enterprise.
Yeah, but there's ZERO reasons why you should be allowed to modify software to suit you. ZERO. Security über alles, DRM control over all, how dare you modify the software God Developer has given you, it will make you Insecure.
On Windows, you can call "EnumProcessModules" to get all the DLLs loaded in the current process – and check those are what you expect.
It is potentially a bit fragile though - DLLs can load other DLLs, and it has happened before that in a new Windows version, Microsoft suddenly adds new implementation DLLs which get pulled in by the main system DLLs.
One approach might be to look at code-signing on all the DLLs, and ignore any unknown DLLs signed by Microsoft.
Would probably make sense to open a scary red warning at startup, telling users that their browser contains untrusted third party code, and stability/security cannot be guaranteed. Probably need to make sure there is no easy way to disable it, or else some IT will just disable it as part of installing DLL-injecting "security" software.
There are ways of doing hidden code injection, without the DLL coming up in the loaded DLLs list – https://reverseengineering.stackexchange.com/questions/2262/... – one option is for the loaded DLL to duplicate itself in memory, jump to the duplicate, then unload the original. Or, you can use VirtualAlloc2/WriteProcessMemory to load code into another process, and CreateRemoteThread to launch it.
I thought these "security" software vendors wouldn't be doing anything so fancy: but from the Chrome bug [0] it looks like some are:
> And I was able to confirm (in some of the dumps, we don't collect the right heap information in all dumps) that Trend Micro code (one region is a DLL that seems to be called ApiHookStub.x64.dll, another is not a direct DLL copy) which has been allocated on our process heap without going through the loader, presumably via something like ::VirtualProtectEx and ::WriteProcessMemory. This is a pattern I see used broadly in Edge crashes we root cause to third-party software.
However, that still can be detected – use VirtualQueryEx to iterate through process address space and find all executable memory regions – any not owned by a loaded DLL (or generated by JavaScript JIT/etc) are evidence of code injection, even if you don't know who the injector is.
IIRC we do block DLLs that aren't signed by either Google or Microsoft in some of our processes. In other processes we can't because third-party DLLs are needed for shell extensions (utility processes) or accessibility (browser process).
And, as you say, code injection is possible without loading a DLL, and does seem to happen.
In this case I don't understand how the state leaked from the file-system filter driver to our process, as it seems to have done. It's a mystery.
I have plans to do the address space iteration you speak of in our crash reporter.
> In this case I don't understand how the state leaked from the file-system filter driver to our process, as it seems to have done.
I assume you made some Windows API call somewhere, which ended up in the filesystem filter driver, which then clobbered the register. And I'm guessing the NT kernel code and Windows DLLs never save/restore the register, because it isn't supposed to be clobbered.
Couldn't one approach be to wrap all Windows API calls with some extra code which saves and restores all the callee-save registers, so even if a buggy kernel driver clobbers them, you don't get hurt by that? I don't know, maybe that's too expensive.
Instead of restoring, one could check for the clobbering, and crash the process immediately. Or maybe Microsoft should add such a wrapping to all calls to third party kernel drivers, and blue screen?
Those tactics could work. They would be a bit expensive however, and it would be a shame to have all users paying the performance penalty because of a few bad pieces of software. And, this would not have caught the two errors in the assembly language code within Chrome - that would require testing at every function call, not just Windows API calls.
It would be more practical (I think) to do this checking on a special build of Chrome that is shipped to a small percentage of users, so that not everybody pays the price.
But, this is an ecosystem problem and I'm not sure Chrome wants to shoulder the entire burden of finding bad software :-)
It would be interesting to see measurements of how big the expense is.
Also, I don't think one necessarily has to do it for every Windows API call – some Windows API calls are more likely to invoke third-party code than others; some API calls are far more performance-critical than others. Maybe one could find a subset of calls to focus on which maximise the likelihood of invoking third-party code but also minimise the performance impact.
> And, this would not have caught the two errors in the assembly language code within Chrome - that would require testing at every function call, not just Windows API calls
For code you control, I think some kind of static analysis would be a better approach – parse inline assembly code and check that every register it touches is marked as clobbered to the compiler. I saw some other comments you were replying to already on that topic. I think this kind of "dynamic" approach should be reserved for third-party code with low trustworthiness.
> It would be more practical (I think) to do this checking on a special build of Chrome that is shipped to a small percentage of users, so that not everybody pays the price.
I was thinking, you could also do it using API hooking. Have some hidden setting to control it, by default off. If it is off, no impact, same as now. If the flag is on, hook (some subset of) Windows APIs with the "unexpected-register-clobber-detector". That way you don't have to produce two completely different builds.
And maybe even, automatically turn that flag on if an install starts to experience crashes–especially if the presence of certain kinds of third-party software is detected.
> But, this is an ecosystem problem and I'm not sure Chrome wants to shoulder the entire burden of finding bad software :-)
Agree. Ideally, Microsoft would take the lead there, since it is their platform. But a world in which the Chrome team does it would be better than a world in which nobody does.
> Seriously, there is zero valid reason to ever inject code into another program
Sometimes, I use software which does not work the way I would like it to work. When this software is closed source, and the problem sufficiently annoying, I inject code to make it do what I want.
(And no, once I do this I don't open bug reports, unless I can reproduce the problem without code injection.)
Because that's the great thing about owning a computer, and knowing how to really use it. It's a tool for you to command.
Now, will most people ever do this? No. I wish everyone could, but unfortunately, injecting code in a useful way requires a reasonable amount of coding knowledge.
But, this is Hacker News. I would hope most users here can come up with lots of valid reasons to inject code into other programs.
There's a huge difference between "I, the user of this system, am intentionally injecting code into a process to debug/extend it in a way I want, and if it breaks I'll have a pretty good idea that it might be my injected code's fault and know where to start looking" and "software that I may not even have intentionally installed on this system or understand the function of has broken other software on this system".
Injecting keyboard events does not require running inside the current process. Either emulate a keyboard device or inject events via the APIs for that.
DLL injection is also used for in-game overlays by Steam/Discord/etc, and I'm not aware of a better method they could use instead given that they're expected to work for games that were never made with those services in mind.
They use wayland compositor (gamescope) under linux (even under X11), in their new SteamOS UI and it's already working a lot better. Plus they can do other awesome stuff with it like completely having control of where and how the game outputs its buffer. So it can fix games with bad multi-monitor behaviour, force game to render in their native aspect ratio / resolution etc... it's just great and makes gaming experience feel more like a console where things (usually) just work.
Gamescope + mangohud + steam overlay working together is very big part of SteamOS and Steam Deck's success, and it does zero runtime modification or hooking into the child process. I can imagine all the trouble you run with the hooking eventually and all the hacks it must have piled up.
They use gamescope under SteamOS, not Linux in general where you already have another compositor. Running all games under a nested compositor (gamescope) would have some perf impact (even if a small one) as well as an unexpected cursor acceleration profile.
True. I personally have setup my distro to launch directly into gamescope optionally for SteamOS like experience. On desktop client they still either hook or use vulkan layer (latter often in proton, thanks to dxvk). However I would not be surprised if they started using nested compositor even on desktop.
GL/Vulkan extensions for drawing overlays over the current frame. Compositor/Windows extensions for drawing overlays, the way Android has. You don't need to be running inside the current process to draw on the screen.
Seriously, there is zero valid reason to ever inject code into another program, other than as a debugging tool on a system being debugged.