Errare humanum est... For some reasons, I have missed an important aspect of DLL injection in my previous article. Namely - hiding your injected DLL. It may be unnecessary when you inject DLL into your own process (e.g. for debugging purposes), but what if you are a tough malware researcher trying to trace the activity of some bad executable? In such case, the less you inform the malware you deal with about your presence - the better.
In this article I will cover the easiest way to hide your injected library from the "victim" process. Intentionally or not, but we will have to dive a bit into Windows internals starting with the TIB (Thread Information Block) and ending with good old UNICODE_STRING data structure.
TIB - Thread Information Block (aka TEB)
You may remember the [FS: 0x00000000] cell. There are thousands of resources mentioning this memory location (and this blog is not an exception) mainly as a location which contains the address of the latest EXCEPTION_REGISTRATION structure added. It is also the first cell of the Thread Information Block (TIB), which is a Win32 structure containing information about the running thread.
I am not going to provide the full declaration of this structure here, as we have almost no interest in it. Those interested may read this. What we do need, is the data located at offset 0x30 in the TIB - the address of the Process Environment Block (PEB).
Depending on the programming language you are using, you need to do the following in order to get the PEB address:
mov eax, [fs:0x30] ;FASM syntax
movl %fs:0, %eax ;AT&T syntax (if you use mingw32, for instance)
/* Microsoft C */
mov eax, fs:[0x30]
mov [addr], eax
/* mingw32 */
__asm__("movl %%fs:0, %0":"=r"(addr));
in both cases addr is the name of the variable where you want to store the address of the PEB.
PEB - Process Environment Block
Alright, now we have the PEB address. But how should we store it? All is clear in case of Assembly - just store it as double word, but what about C? There are several options. Given that we are not going to do much with it, you can store it as an unsigned int or void*. In either case, I believe it will be interesting to see what lies beneath the name. Therefore, let's declare the _PEB structure and PEB type (you may take the declaration from here). There are several things that you have to keep in mind if you are writing in Assembly:
- BOOL should be defined as db (sizeof(BOOL) equals to sizeof(char));
- BYTE speaks for itself and is equal to db;
- HANDLE, ULONG, PVOID, PPVOID and other pointers are all double words and should be defined as dd.
Field at offset 0x02 (BeingDebugged) may be used for debugger detection, however, this method may be unreliable and should be backed up by additional checks. But the field of interest for us is the LoaderData, located at offset 0x0C. This field contains the address of the PEB_LDR_DATA structure which will lead us to the list of loaded modules (the main executable that started the process and all the loaded DLLs).
There is not much to say about it. The structure itself is described below:
typedef struct _PEB_LDR_DATA
The LIST_ENTRY structure is declared as follows:
typedef struct _LIST_ENTRY
struct _LIST_ENTRY *Flink;
struct _LIST_ENTRY *Blink;
Each LIST_ENTRY structure in PEB_LDR_DATA represents a head of chain of structures, which in turn are parts of the LDR_MODULE structure (we will see that in a minute).
InLoadOrderModuleList represents the linked list of loaded modules ordered by the load order (first loaded module is the main executable).
InMemoryOrderModuleList represents the linked list of loaded modules ordered by their memory location (by their module handles).
InInitializationOrderModuleList represents the linked list of loaded modules in the order they were initialized. However, it is important to remember, that the Flink and Blink point to the LIST_ENTRY structures, not to the LDR_MODULE structures. Let me explain it in the next section.
We finally have reached the structure that we've been looking for. Each LDR_MODULE structure has all the basic information about one of the modules present in memory and the list of these structures is used by several Windows API functions (for example GetModuleHandle).
Let us inspect the declaration of the LDR_MODULE structure before we start looking for one related to our injected DLL:
typedef struct _LDR_MODULE
It may be a good suggestion to use the InLoadOrderModuleList for going through the linked list of LDR_MODULE as its Blink and Flink pointers also point to the beginning of the LDR_MODULE structure, whereas, if you decide to use InMemoryOrderModuleList, you would have to subtract the size of LIST_ENTRY structure from either Flink or Blink in order to get the address of the LDR_MODULE structure. If you are masochistic enough to use InInitializationOrderModuleList, then you would have to subtract sizeof(LIST_ENTRY) * 2. I said masochistic, but it is not really that painful if you keep in mind that you are dealing with pointers and cast your variables properly.
Lets see what we have here. The first is the LIST_ENTRY structure used to link modules in the order they were loaded, the second - in the order they are positioned in virtual memory and the third - in the order they (modules) have been initialized.
BaseAddress is the actual address where the module is loaded at. In ideal case, it is equal to the ImageBase specified in the Optional Header of a PE file, but it may differ.
EntryPoint is the address of the entry point of that specific module or NULL if the module has not entry point (which is allowable for DLLs).
SizeOfImage - the size of the image in memory.
FullDllName - full name of the module, including full path to the file. Important to mention that it is stored in the form of UNICODE_STRING which has the following format:
typedef struct _LSA_UNICODE_STRING
} LSA_UNICODE_STRING, *PLSA_UNICODE_STRNIG, UNICODE_STRING, *PUNICODE_STRING;
BaseDllName - the name of the file itself. Represented as a UNICODE_STRING as well. Most likely, this is the field that you would use in order to find the entry related to your injected DLL.
The last step to take in order to hide your injected DLL is to simply remove it from the linked list of the LDR_MODULE structures. My assumption is that if you dare to mess with Windows internals, you probably know how to mess with linked lists and, therefore, are able to perform that operation.
We're almost done, but let me give you a good suggestion - keep a copy of the LDR_MODULE structure which describes your DLL somewhere and spend some additional time writing a function capable of inserting it back correctly in case of need.
I hope this article is helpful. See you at the next.
Almost forgot! Comments/suggestions are welcome and appreciated.