Wednesday 8 December 2010

Integrity checking

In order to make sure anti-malware software itself isn't comprimised, we can take a few precautions.

The first mechanism is assembly signing. Using a certificate we can make sure that the CLI will refuse to load the binary if it is modified by a 3rd party. It not only provides this security, but also proves the identity of the individual or group that produced the assembly. This means that we get security and assurance of authenticity, which is good for real security and user confidence. Visual Studio provides the ability to sign executables when they are produced. Certain tools are available that strip signatures from an assembly, so it is a good idea to programatically check that the assembly is signed at startup. We can also maintain a read/write lock on critical files to help make sure they are not modified.

An issue that is much more difficult to tackle is runtime manipulation. Process memory has been safe from direct manipulation since Windows NT4 due to the introduction of isolated process memory, but the WriteProcessMemory API still allows us to modify the memory of a remote process. It is also possible to create a thread in another process, allowing execution of arbitrary code in that process's context.

There are a few tricks we can use to check if our binary has been patched in memory. One thing we can do is perform a read operation on our own memory (the memory allocated for .code) and compare it to a hash to check that no code was modified. This is an annoyance for malware writers, but it could easily be patched by writing over the check code or modifying the hash. Whilst the system could be considered irreparebly comprimised if malware could write memory to a process running as SYSTEM, it's not to say that the situation could not be quite easily recovered from manually.

Another trick is memory protection scanning. If we remove the write flag from the protection value on all memory in our process that doesn't need to be read (e.g. the code) the malware must modify that memory's protection in order to write to it. This can be done in usermode by calling VirtualProtectEx. One could write a kernel-mode driver (KMD) to hook such calls and block them, but this is complex and likely outside the scope of this project. In our case, we can simply iterate all pages in our process' memory and check the protection constants match up.

No comments:

Post a Comment

Note: only a member of this blog may post a comment.