From the outside looking in, the more complex you
make the runtime checks
1 the harder servicing becomes, even if you service the whole stack at once
2 I pity the poor dev that inherits the inter tangled and strong inter dependencies you’re creating and their subsequent inability to innovate
FWIW, Grok 3 helped me find "NtQuerySystemInformation(SystemModuleInformationEx)" which doesn't give me certificate information, but at least seems to give me the PE checksum, which is better than nothing.
Guys, I really appreciate your intent to help me by advising me to go light on copy protection, but you really don't know the unique circumstances of my company and my product and the niche market I'm in. Please trust that I know what I'm doing. Or if you can't do that, just assume that I'm too stubborn to listen.
In any case, I am still very much interested if anyone can think of a technical solution to my original question that is better than "SystemModuleInformationEx".
If you want to check the attributes of the file on disk, it is simple enough. But it is kind of pointless to verify these using stock APIs because the OS already did that before loading your driver. And if the hackers have modified the stock signature checks to let them load a modified version of your driver, then you calling the same function again won't help
The PE format is tedious to parse, but well documented. And the signature(s) included are standard ASN.1. Again, with the on disk data, you can write your own check, but you still have the problem that your code has no way to verify that signature without access to PKI (which can be directly poisoned by the attacker) or embedding the key (and how do you protect that).
But if you want to check the actually loaded binary, it gets much harder. The loader uses a complex process to load the code and data sections into memory, and then modify the memory to work within the target address space (DLL imports, initializers etc.) and so by the time that your code is ready to run, any cryptographic signature is long ago invalid - even if you had a reliable way to validate it. Some people have tried to 'encrypt' or 'sign' smaller portions of the program. Like individual functions. But the same issues apply, and it can actually make it easer for an attacker because the function boundaries become easier to identify
So your best defense is to use code obfuscation. The process of inserting many extraneous constructs in the instruction stream - especially jumps and stack frames - that will confuse decompillers and humans. It will absolutely kill performance, and if you ever have a crash dump to analyze you will go nuts, but it will make it harder. And harder in a way that doesn't depend on any supporting infrastructure from the OS, PKI etc. that the attacker presumably already has control over.
Yes, I agree with you. In an ideal world, the OS would store the certificate ID of the loaded driver in its internal loader structures in RAM, so I could call e.g. NtQuerySystemInformation to retrieve that information. But it doesn't seem like the OS does that, sadly.
The NtQuerySystemInformation(SystemModuleInformationEx) solution I found is similar to my dream solution, except that it doesn't give me the certificate ID, it gives me the PE header's checksum. So I can now query that and compare it to the checksum that I know the driver is supposed to have. It's not as good as the certificate ID, but it's better than nothing.
I know that the hacker can workaround this new protection, but that's ok. I'm not looking for perfect protection. I'm just trying to build up several layers of protection to make things as annoying and time consuming for the hacker to break through as possible, hoping he might give up, because it costs too much time.
(And yes, I'm already using code obfuscation. I'm only using it for part of the code, and tweaked it so that the performance impact is not very big.)
In this ideal world, what would you really expect to get out of such an API? The only certificates that can be used to sign modules that will load into a non-test-mode Windows kernel (since Windows 10) are certificates owned by Microsoft. Therefore, if the system is not in test mode, such an API would always just tell you that the driver is signed by Microsoft.
Now of course this is omitting the detail that you can put additional signatures of your own on the driver binary after it has been signed by Microsoft, which are then promptly ignored when loading the driver. Further, in this world why would the kernel even check them or report them in such APIs?
If the system is in test mode however, all bets are off. You'd be better off checking that as a signal.
Yes, I will definitely check for test mode.
My understanding is that drivers signed with e.g. an EV certificate have 2 signatures in them: One that matches the EV certificate, and another one that is from Microsoft. Am I wrong? Why would I even need an EV certificate if it doesn't end up in the driver?
The EV certificate is required to open a hardware dashboard account with Microsoft, and to sign the CAB files that you submit via the portal. The charitable interpretation is that this vouches for Microsoft that you are the company you say you are. If you search these forums however you'll find others saying this part is a bit of a racket and that the CAs don't actually "extended validate" anything.
Just checking a couple drivers on my work machine, we put our own signature on the .sys file before submitting to Microsoft, while a certain infamous cybersecurity company does so after the file has already been MS-signed. Neither is actually a requirement to get the driver to load into Windows - if it has the one WHCP or WHQL signature, it's good enough.
Looking e.g. at Nvidia drivers, they have both an Nvidia and Microsoft signature in them. I haven't tried yet, but I'm assuming once I EV sign my own driver, it should also have my own signature in it, plus the Microsoft signature. So if there was a way to get signature information about loaded drivers from the OS, it should do what I want, just as long as I can manage to get my own signature in there, in addition to the Microsoft signature, which seems to be possible.
Anyway, doesn't really matter all that much, as long as there's no OS functionality to provide this information.
Here are some ideas:
You can verify certificates from usermode using routines such as WinVerifyTrust. Of course there are vulnerabilities and they could possibly be instrumented. In kernelmode you can check certificates using code integrity routines (ci.dll). Although not documented, you can find examples on GitHub.
You could store CRCs in your usermode binaries and verify them by your driver by reading them.
Other than verifying binaries you should be concerned about your usermode process getting instrumented, for instance by DLL injection. To get a protected process lite, you will need an ELAM certificate which is not easily obtained. But there are other ways to protect yourself. A call to SetProcessMitigationPolicy can protect your code from getting injected or modified. Also compile with control flow guards (CFG) enabled.
In kernel you can use ObRegisterCallbacks to prevent handles from getting created with PROCESS_VM_WRITE desired flags set. You can also use load image notify routines to block unwanted DLLs.
From my experience, what works well to annoy crackers is to perform one of a long list of checks once in a while. Some checks only on a blue moon, other checks only on a full moon, other checks only if system has 8 cores, other checks only if the user wears a brown shirt. As soon as you detect foul play, do not act immediately so that you cannot be traced back. Instead use a complicated state machine that propagates the corrupted state over time, if it's worth all the efforts.
Fantastic ideas, thank you Daniel !!
Some of these I'm already using, e.g. storing CRC in the usermode binaries, using ObRegisterCallbacks, and also I'm doing some checks only like 30 minutes after the software has started etc, and then introducing random looking bugs instead of just stopping or crashing the software. And yes, I'm aware of DLL injection and the need to stop that somehow.
Some things you suggested are actually new to me, e.g. SetProcessMitigationPolicy, or the image notify routines, or CFG. I will certainly look into those! ![]()
I had looked into ELAM certificates, but couldn't find a way to get them, so I gave up on that idea.
100% correct.
That EV cert does not necessarily end-up in the driver's .sys file. It's up to the submitter.
Thanks Peter for confirming. In my case I would desire to have the EV cert end-up in the driver, so I can verify if the driver is "mine" and not cracked.
For the archives, it should be noted that control flow guard has no impact on the efforts of an attacker with full access to the machine that the code will run on and who intends to modify or bypass your driver binary. Neither does SetProcessMitigationPolicy. And including alternate signatures in your binary (standard signatures) is just as unhelpful. Certainly verifying the PE checksum is a total waste of time. Using callbacks to deny HANDLE creation will upset legitimate admins, but may add some extra steps for an attacker
even if you manage to get an ELAM certificate, it is of no practical use to defend against this type of attack
obfuscating the code that does the protection check by including a temporal component, and varying the failure mode, in addition to making the actual instructions difficult to understand is useful and awkward for the reasons already mentioned
this will be my last post on this topic
Obviously this part is about preventing loaded images in memory from getting modified, not about protecting binaries on disk.
Instead of blocking, it's also possible to just strip off the access right that allow writing into your process memory. In either case, it's not the affair of an administrator.
Pardon for running late for the party ))
@TheOldOne if your concern is that "hackers" can load their driver (signed by whatever cert) but your original binary stays intact - can you unload and reload the driver every so often? Then your binary will replace theirs. Your usermode can detect inconsistent effects (like, a probe passed once but failed after reloading the driver).
And re. ELAM: a service protected by ELAM cannot be stopped from outside, so it's hard to delete and replace the exe file. Loading and unloading kernel drivers can be detected by the minifilter.
Thanks Pavel for the idea, I will consider it carefully.
One potential problem is that the minifilter driver modifies (some of) my EXE files. So if I restart the driver while my EXE files are running, that might impact the stability of the EXE files. So I'd probably have to restart my EXE files, as well, which may not be feasible. But I'll think about it, maybe I can make it work in specific situations.
FWIW, I've implemented the "NtQuerySystemInformation(SystemModuleInformationEx)" solution in the meanwhile and can confirm that it works as I hoped: Meaning it does give me the PE checksum and image size of the loaded driver, and these infos don't change even if the driver file is replaced in the background while the driver keeps running.
Now of course that's not a 100% reliable check, since it's easy enough for a cracker to modify the driver in such a way that the file size and PE checksum stays the same, since the PE checksum algo is too simplistic to provide good protection. But it's better than nothing, and the cracker would need to figure out that I'm testing the PE checksum in the first place. So it adds one more layer of protection the cracker has to break through.
As to ELAM: Do you know a way I can obtain an ELAM certificate?
As to ELAM: Do you know a way I can obtain an ELAM certificate?
ELAM drivers, like all drivers, are signed by Microsoft. To get the ability to sign ELAM drivers through them you have to participate in the MVI (Microsoft Virus Initiative) programme - see [1]. Unless you are a legitimate antimalware vendor I doubt you would get membership. Requirements for participation are getting more stringent recently and basically you have to have an antimalware product that passes thirdparty benchmarks on virus detection and performance assessments by MS, etc.
They're not going to be handing ELAM capability out for copyright protection, I don't think.
Thanks. Yes, that was the result of my research, as well.
This topic was automatically closed 60 days after the last reply. New replies are no longer allowed.