Live data from Hacker News

Please restore our registers when you’re done with them

randomascii.wordpress.com

81–90 of 142 posts

Re: Please restore our registers when you’re done with them

#81

Earlier quoted context omitted.

Yes. That's pretty common, unfortunately.

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.

There might be a few more. Invasive Anti cheat software seems necessary for competitive multiplayer games on PC.

Re: Please restore our registers when you’re done with them

#82
post #62
post #43

Earlier quoted context omitted.

It would just be slow for the caller to have to push and pop (say 30) registers in general that the specific callee (and transitive callees) may not even use. Most ABI specify some registers caller-saved, some registers callee-saved (retained unchanged from the perspective of the caller) and some registers scratch (not-saved). In the end it depends on the architecture and on typical workload which are the fastest--an…

You usually wouldn't need to push&pop all registers, just ones that you want to preserve across the call. Regardless, yeah, non-volatile aka callee-saved registers are extremely important for good performance of code that calls functions (esp. loops - without callee-saved registers, you'd have to store the loop counter & length on the stack!)

Programming defensively in this way is possible but has a huge cost (extra saves and restores). The caller is supposed to be able to trust that certain registers persist across the call. If it can't, it has to save everything to memory before the call and restore it. I suppose a compiler could add a special annotation for "this is an assembler routine and I don't trust that they know the rules", which would generate extra saves and restores, but presumably the routine was coded in assembly for extra speed, so 6 to 8 extra saves and restores would cancel that out.

If someone is trying to use the same assembler routine on Windows as on Linux, it's likely to be wrong for one of them.

Re: Please restore our registers when you’re done with them

#83
post #46
post #13

Earlier quoted context omitted.

Not worth checking for. The few people who write assembly code these days know what they are doing, and, bugs aside, the compiler knows what to do.

The old Russian proverb is "Trust but verify." I, for one, hate debugging asm. I do it a lot, and would prefer bugs be caught automatically, preferably soon after they are introduced.

You're part of a small population, and so you might not even trust the tool.

It's not clear to me how to write such a tool as assembly code is the opposite of structured.

Re: Please restore our registers when you’re done with them

#84
post #75

Earlier quoted context omitted.

My reading of it was that the bug was caused by XMM7 not being restored to its previous value. E.g. on Linux, functions should restore the values of ebx, esi, edi, ... once they're done with them. The article says (on Windows) that XMM7 needs to be restored too. If you can't trust one register being preserved (per the ABI), then you really can't trust the values of any registers.

Fair point, which is why I used the phrase "this particular example." In fact the compiler's lazy assumption about XMM7 was the key to determining that the ABI was being violated. It would have taken longer to figure this out if the compiler had been doing things my way, because then a failure of the callee to preserve XMM7 wouldn't have mattered.

You could avoid the crash in this specific case. But this crash was triggered by an assertion failure. The same code could be corrupting XMM7 in other situations, which could result in much more subtle bugs.

Re: Please restore our registers when you’re done with them

#85

Earlier quoted context omitted.

Yes. That's pretty common, unfortunately.

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.

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.

[0] https://bugs.chromium.org/p/chromium/issues/detail?id=121838...

Re: Please restore our registers when you’re done with them

#86

I think the real fix is to set all registers to a canary value at the start of int main(), and then when exiting check the canary value is still there. If anything has messed with any registers without permission, you crash and collect as much data as possible about any injected dll's. Then you correlate these to find the culprits, and for each you contact the authors of the DLL and figure out a way to block the inje…

Code can be injected anywhere. You'd have to add checks to every single function.

Re: Please restore our registers when you’re done with them

#87
post #42

Naïve question about ABIs: shouldn’t the caller be responsible for that? If I want a function to restore certain registers, wouldn’t it be simpler if I was the one that save them on my memory, call the function, and then override the registers with whatever values the function set? Otherwise it seems we’re just… asking for trouble, so to speak.

ABIs are a convention that everybody follows regarding how such preservation should or should not occur, for which registers.

It's common to say "caller cleans up registers x, y, z, callee cleans up a, b, c".

So yes, the caller could do it or not do it, the choices are all possible to do both ways, but that wouldn't be the agreed upon ABI, it'd be something different.

Re: Please restore our registers when you’re done with them

#88

Earlier quoted context omitted.

Yes. That's pretty common, unfortunately.

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.

Re: Please restore our registers when you’re done with them

#89

Earlier quoted context omitted.

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.

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…

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.

Post reply on HN