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:
for Assemby:
mov eax, [fs:0x30] ;FASM syntax or movl %fs:0x30, %eax ;AT&T syntax (if you use mingw32, for instance)
for C:
/* Microsoft C */ __asm { mov eax, fs:[0x30] mov [addr], eax } /* mingw32 */ __asm__("movl %%fs:0x30, %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)) should be defiend as dd (BOOL
is 4 bytes whileBOOLEAN
is 1 - thanks Krystalgamer for pointing this out);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).
typedef struct _PEB_LDR_DATA { ULONG Length; BOOL Initialized; PVOID SsHandle; LIST_ENTRY InLoadOrderModuleList; LIST_ENTRY InMemoryOrderModuleList; LIST_ENTRY InInitializationOrderModuleList; }PEB_LDR_DATA, *PPEB_LDR_DATA;
The LIST_ENTRY
structure is declared as follows:
typedef struct _LIST_ENTRY { struct _LIST_ENTRY *Flink; struct _LIST_ENTRY *Blink; }LIST_ENTRY, *PLIST_ENTRY;
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.
LDR_MODULE
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 { LIST_ENTRY InLoadOrderModuleList; LIST_ENTRY InMemoryOrderModuleList; LIST_ENTRY InInitializationOrderModuleList; PVOID BaseAddress; PVOID EntryPoint; ULONG SizeOfImage; UNICODE_STRING FullDllName; UNICODE_STRING BaseDllName; ULONG Flags; SHORT LoadCount; SHORT TlsIndex; LIST_ENTRY HashTableEntry; ULONG TimeDateStamp; }LDR_MODULE, *PLDR_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 aremasochistic 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 { USHORT Length; USHORT MaximumLength; PWSTR Buffer; } 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.
Last Step
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.
For those who doubt their knowledge this or may be even this link would give the idea.
P.S.
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.
P.P.S
Almost forgot! Comments/suggestions are welcome and appreciated.