It was Andrew (thanks!) who brought LdrRegisterDllNotification to my attention by asking on twitter if anyone has seen any malware using it. I couldn’t find any malware, but the function itself triggered my interest.
I quickly implemented a proof of concept. It shows how the callback that the API is registering could be utilized as a lame anti-* trick by malware that drops DLLs (in a case I am demoing), and maybe to do a few more tricky things (as I discuss below).
Consider the first .exe – let’s call it ‘good.exe’.
When executed, it drops a DLL called ‘foo.dll’.
It then resolves its one and only export ‘Bar’ and executes the function. The function shows a simple message box with a ‘Good’ message:
The second .exe is called ‘better.exe’ (obviously 🙂 ).
It drops the very same ‘foo.dll’.
Functionally, it is almost identical with ‘good.exe’ as it is using the same source code. The only difference is that it registers the callback using a LdrRegisterDllNotification function. From now on, anytime a DLL is loaded the callback function takes control and is free to modify the content of the loaded DLL ‘on the fly’. As such, it can easily patch it and create a complete different memory image from the one we may expect by just looking at a static file ‘dropped’ by the .exe.
In my case the change is of a ‘visualame’ type. It’s for us to observe it.
The callback does only one thing – it checks if the string at a hardcoded offset inside the loaded DLL is equal to ‘Good’ and swaps it with a new string ‘Evil’; therefore, running the ‘better.exe’ will produce the following result:
The example is intentionally simple, but there are many things that could be done here – the callback allows one to f.ex.
- disable unwanted DLLs (by f.ex. patching their code)
- detecting sandbox DLLs at a different stage (also use as a different enumeration method for loaded DLLs)
- swapping data and code of modules on the fly
- resolving API pointers earlier and potentially hooking / patching APIs used later in the code
- introducing simple, atomic, business logic changes to the executed code (hard to spot) f.ex. modify JNZ to JZ, NOPing some instructions, etc.
- etc.
Both files can be downloaded here.
Nothing ground breaking, but good to know about.