隐藏DLL模块
1. 数据类型 LIST_ENTRY
说明:内核使用该结构将所有对象维护在一个双向链表中。一个对象分属多个链表是很常见的, Flink 成员是一个向前链接指向下一个 LIST_ENTRY 结构, Blink 成员则是一个向后链接,指向前一个 LIST_ENTRY 结构。通常情况下,这些链表都成环形,也就是说,最后一个 Flink 指向链表中的第一个 LIST_ENTRY 结构,而第一个 Blink 指向最后一个。这样就很容易双向遍历该链表。如果一个程序要遍历整个链表,它需要保存第一个 LIST_ENTRY 结构的地址,以判断是否已遍历了整个链表。如果链表仅包含一个 LIST_ENTRY 结构,那么该 LIST_ENTRY 结构必须引用其自身,也就是说,Flink 和 Blink 都指向其自己。
typedef struct _LIST_ENTRY
{
struct _LIST_ENTRY *Flink;
struct _LIST_ENTRY *Blink;
}LIST_ENTRY, *PLIST_ENTRY;
0:000> dt _PEB
ntdll!_PEB
+0x000 InheritedAddressSpace : UChar
+0x001 ReadImageFileExecOptions : UChar
+0x002 BeingDebugged : UChar
+0x003 SpareBool : UChar
+0x004 Mutant : Ptr32 Void
+0x008 ImageBaseAddress : Ptr32 Void
+0x00c Ldr : Ptr32 _PEB_LDR_DATA
…
0:000> dt _PEB_LDR_DATA
ntdll!_PEB_LDR_DATA
+0x000 Length : Uint4B
+0x004 Initialized : UChar
+0x008 SsHandle : Ptr32 Void
+0x00c InLoadOrderModuleList : _LIST_ENTRY
+0x014 InMemoryOrderModuleList : _LIST_ENTRY
+0x01c InInitializationOrderModuleList : _LIST_ENTRY
+0x024 EntryInProgress : Ptr32 Void
typedef struct _PEB_LDR_DATA {
ULONG Length;
BOOLEAN Initialized;
PVOID SsHandle;
LIST_ENTRY InLoadOrderModuleList; //按加载顺序
LIST_ENTRY InMemoryOrderModuleList; //按内存顺序
LIST_ENTRY InInitializationOrderModuleList;//按初始化顺序
PVOID EntryInProgress;
} PEB_LDR_DATA, *PPEB_LDR_DATA;
该结构的黑体三个成员是指向LDR_MODULE链表结构中相应三条双向链表头的指针,分别是按照加载顺序、在内存中地址顺序和初始化顺序排列的模块信息结构的指针.
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;
typedef struct _UNICODE_STRING {
USHORT Length;
USHORT MaximumLength;
PWSTR Buffer;
} UNICODE_STRING, *PUNICODE_STRING
汇编:
MOV EAX, DWORD PTR FS:[30] ;获取PEB基址
MOV EAX, DWORD PTR DS:[EAX+C] ;获取PEB_LDR_DATA结构指针
MOV ESI, DWORD PTR DS:[EAX+1C] ;获取InInitializationOrderModuleList成员指针
LODS DWORD PTR DS:[ESI] ;获取双向链表当前节点的后继指针
MOV EBX, DWORD PTR DS:[EAX+8] ;取其基地址,该结构当前包含的是kernel32.dll
2 遍历双向链表
PEB_LDR_DATA *pPEBLDR;
LDR_MODULE *pLdrMod;
LIST_ENTRY *pListEntry, *pStart;
void *p, *BaseAddress, *FullDllName;
_asm
{
mov eax, fs:[0x30]
mov eax, [eax + 0x0C]
mov pPEBLDR, eax
}
printf("PEB_LDR_DATA:0x%08x\n",pPEBLDR);
printf("LDR->Length:0x%08x\n",pPEBLDR->Length);
printf("LDR->InLoadOrderModuleList:\t0x%08x\n",pPEBLDR->InLoadOrderModuleList);
printf("LDR->InMemoryOrderModuleList:\t0x%08x\n",pPEBLDR->InMemoryOrderModuleList);
printf("LDR->InInitializationOrderModuleList:\t0x%08x\n",pPEBLDR->InInitializationOrderModuleList);
//遍历双向链表
pListEntry = (LIST_ENTRY*)(PUCHAR)&(pPEBLDR -> InLoadOrderModuleList);
pStart = pListEntry;
printf("BaseAddress\t\tFullDllName\n====================================\n");
do
{
pListEntry = pListEntry -> Flink;
pLdrMod = (LDR_MODULE*)pListEntry;
printf("0x%08x\t\t",pLdrMod->BaseAddress);
wprintf(L"%s\n",pLdrMod->FullDllName.Buffer);
}while(pListEntry != pStart);
3高级应用(隐藏模块)
#include<windows.h>
typedef struct _UNICODE_STRING {
USHORT Length;
USHORT MaximumLength;
PWSTR Buffer;
} UNICODE_STRING, *PUNICODE_STRING;
typedef struct _PEB_LDR_DATA {
ULONG Length;
BOOLEAN Initialized;
PVOID SsHandle;
LIST_ENTRY InLoadOrderModuleList; //按加载顺序
LIST_ENTRY InMemoryOrderModuleList; //按内存顺序
LIST_ENTRY InInitializationOrderModuleList;//按初始化顺序
PVOID EntryInProgress;
} PEB_LDR_DATA, *PPEB_LDR_DATA;
//每个模块信息的LDR_MODULE部分
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;
//隐藏模块
VOID HideModule(HMODULE hLibrary);
BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
HMODULE hMod = NULL;
switch( ul_reason_for_call )
{
case DLL_PROCESS_ATTACH:
HideModule((HMODULE)hModule);
break;
}
return TRUE;
}
VOID HideModule(HMODULE hLibrary)
{
PPEB_LDR_DATA pLdr = NULL;
PLDR_MODULE FirstModule = NULL, GurrentModule = NULL;
__try
{
__asm
{
mov esi, fs:[0x30]
mov esi, [esi + 0x0C]
mov pLdr,esi
}
FirstModule = (pLDR_MODULE)(pLdr -> InLoadOrderModuleList.Flink);
CurrentModule = FirstModule;
while(!(CurrentModule ->BaseAddress == hLibrary))
{
CurrentModule = (PLDR_MODULE)(CurrentModule
-> InLoadOrderModuleList.Blink );
if(CurrentModule == FirstModule)
break;
}
if(CurrentModule -> BaseAddress != hLibrary) return;
//Dll解除链接
((PLDR_MODULE)(CurrentModule -> InLoadOrderModuleList.Flink))
-> InLoadOrderModuleList.Blink = CurrentModule -> InLoadOrderModuleList.Blink;
((PLDR_MODULE)(CurrentModule->InLoadOrderModuleList.Blink))
->InLoadOrderModuleList.Flink = CurrentModule->InLoadOrderModuleList.Flink;
memset(CurrentModule->FullDllName.Buffer, 0, CurrentModule->FullDllName.Length);
memset(CurrentModule, 0, sizeof(PLDR_MODULE));
__except(EXCEPTION_EXECUTE_HANDLER)
{
return;
}
}
3 检测隐藏进程
说明: 对于Windows下的每一个进程,系统都会给它分配一个excutive process (EPROCESS) block。该结构包含和指向一系列其他相关的数据结构,比如每个进程的所有线程信息。EPROCESS及其所有相关数据结构都位于系统空间,只有Process environment block(peb)是位于进程地址空间,因为它包含由用户模式代码所决定的信息.
ntdll!_EPROCESS
+0x000 Pcb : _KPROCESS
+0x06c ProcessLock : _EX_PUSH_LOCK
+0x070 CreateTime : _LARGE_INTEGER
+0x078 ExitTime : _LARGE_INTEGER
+0x080 RundownProtect : _EX_RUNDOWN_REF
+0x084 UniqueProcessId : Ptr32 Void
+0x088 ActiveProcessLinks : _LIST_ENTRY
+0x090 QuotaUsage : [3] Uint4B
+0x09c QuotaPeak : [3] Uint4B
+0x0a8 CommitCharge : Uint4B
+0x0ac PeakVirtualSize : Uint4B
+0x0b0 VirtualSize : Uint4B
+0x0b4 SessionProcessLinks : _LIST_ENTRY
+0x0bc DebugPort : Ptr32 Void
+0x0c0 ExceptionPort : Ptr32 Void
+0x0c4 ObjectTable : Ptr32 _HANDLE_TABLE
+0x0c8 Token : _EX_FAST_REF
+0x0cc WorkingSetLock : _FAST_MUTEX
+0x0ec WorkingSetPage : Uint4B
+0x0f0 AddressCreationLock : _FAST_MUTEX
…
0:000> dt _LIST_ENTRY
ntdll!_LIST_ENTRY
+0x000 Flink : Ptr32 _LIST_ENTRY
+0x004 Blink : Ptr32 _LIST_ENTRY
0:000> dt _HANDLE_TABLE
ntdll!_HANDLE_TABLE
+0x000 TableCode : Uint4B
+0x004 QuotaProcess : Ptr32 _EPROCESS
+0x008 UniqueProcessId : Ptr32 Void
+0x00c HandleTableLock : [4] _EX_PUSH_LOCK
+0x01c HandleTableList : _LIST_ENTRY
+0x024 HandleContentionEvent : _EX_PUSH_LOCK
+0x028 DebugInfo : Ptr32 _HANDLE_TRACE_DEBUG_INFO
+0x02c ExtraInfoPages : Int4B
+0x030 FirstFree : Uint4B
+0x034 LastFree : Uint4B
+0x038 NextHandleNeedingPool : Uint4B
+0x03c HandleCount : Int4B
+0x040 Flags : Uint4B
+0x040 StrictFIFO : Pos 0, 1 Bit
注意:ObjectTable结构体内偏移0x008处的UniqueProcessId就是当前EPROCESS对应进程的ID号。同时,ObjectTable结构体内偏移0x004处有一个指针QuotaProcess,指向了当前进程的EPROCESS结构(Sytem进程比较特殊,对应的指针为空)。因而,我们不仅能够得到进程的ID号,而且可以获得进程的其他信息.
对于偏移0x088处的ActiveProcessLinks,我们先通过函数PsGetCurrentProcess获得任意进程的EPROCESS的地址,该结构偏移0x084处存放进程ID的指针,读取该指针处的ID作为循环的终止判断条件。到EPROCESS的偏移0x088处ActiveProcessLinks中读取当前进程之后的进程的ActiveProcessLinks指针,通过该指针计算其EPROCESS地址。如此循环,即可以获得所有活动进程的信息.
1. 数据类型 LIST_ENTRY
说明:内核使用该结构将所有对象维护在一个双向链表中。一个对象分属多个链表是很常见的, Flink 成员是一个向前链接指向下一个 LIST_ENTRY 结构, Blink 成员则是一个向后链接,指向前一个 LIST_ENTRY 结构。通常情况下,这些链表都成环形,也就是说,最后一个 Flink 指向链表中的第一个 LIST_ENTRY 结构,而第一个 Blink 指向最后一个。这样就很容易双向遍历该链表。如果一个程序要遍历整个链表,它需要保存第一个 LIST_ENTRY 结构的地址,以判断是否已遍历了整个链表。如果链表仅包含一个 LIST_ENTRY 结构,那么该 LIST_ENTRY 结构必须引用其自身,也就是说,Flink 和 Blink 都指向其自己。
typedef struct _LIST_ENTRY
{
struct _LIST_ENTRY *Flink;
struct _LIST_ENTRY *Blink;
}LIST_ENTRY, *PLIST_ENTRY;
0:000> dt _PEB
ntdll!_PEB
+0x000 InheritedAddressSpace : UChar
+0x001 ReadImageFileExecOptions : UChar
+0x002 BeingDebugged : UChar
+0x003 SpareBool : UChar
+0x004 Mutant : Ptr32 Void
+0x008 ImageBaseAddress : Ptr32 Void
+0x00c Ldr : Ptr32 _PEB_LDR_DATA
…
0:000> dt _PEB_LDR_DATA
ntdll!_PEB_LDR_DATA
+0x000 Length : Uint4B
+0x004 Initialized : UChar
+0x008 SsHandle : Ptr32 Void
+0x00c InLoadOrderModuleList : _LIST_ENTRY
+0x014 InMemoryOrderModuleList : _LIST_ENTRY
+0x01c InInitializationOrderModuleList : _LIST_ENTRY
+0x024 EntryInProgress : Ptr32 Void
typedef struct _PEB_LDR_DATA {
ULONG Length;
BOOLEAN Initialized;
PVOID SsHandle;
LIST_ENTRY InLoadOrderModuleList; //按加载顺序
LIST_ENTRY InMemoryOrderModuleList; //按内存顺序
LIST_ENTRY InInitializationOrderModuleList;//按初始化顺序
PVOID EntryInProgress;
} PEB_LDR_DATA, *PPEB_LDR_DATA;
该结构的黑体三个成员是指向LDR_MODULE链表结构中相应三条双向链表头的指针,分别是按照加载顺序、在内存中地址顺序和初始化顺序排列的模块信息结构的指针.
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;
typedef struct _UNICODE_STRING {
USHORT Length;
USHORT MaximumLength;
PWSTR Buffer;
} UNICODE_STRING, *PUNICODE_STRING
汇编:
MOV EAX, DWORD PTR FS:[30] ;获取PEB基址
MOV EAX, DWORD PTR DS:[EAX+C] ;获取PEB_LDR_DATA结构指针
MOV ESI, DWORD PTR DS:[EAX+1C] ;获取InInitializationOrderModuleList成员指针
LODS DWORD PTR DS:[ESI] ;获取双向链表当前节点的后继指针
MOV EBX, DWORD PTR DS:[EAX+8] ;取其基地址,该结构当前包含的是kernel32.dll
2 遍历双向链表
PEB_LDR_DATA *pPEBLDR;
LDR_MODULE *pLdrMod;
LIST_ENTRY *pListEntry, *pStart;
void *p, *BaseAddress, *FullDllName;
_asm
{
mov eax, fs:[0x30]
mov eax, [eax + 0x0C]
mov pPEBLDR, eax
}
printf("PEB_LDR_DATA:0x%08x\n",pPEBLDR);
printf("LDR->Length:0x%08x\n",pPEBLDR->Length);
printf("LDR->InLoadOrderModuleList:\t0x%08x\n",pPEBLDR->InLoadOrderModuleList);
printf("LDR->InMemoryOrderModuleList:\t0x%08x\n",pPEBLDR->InMemoryOrderModuleList);
printf("LDR->InInitializationOrderModuleList:\t0x%08x\n",pPEBLDR->InInitializationOrderModuleList);
//遍历双向链表
pListEntry = (LIST_ENTRY*)(PUCHAR)&(pPEBLDR -> InLoadOrderModuleList);
pStart = pListEntry;
printf("BaseAddress\t\tFullDllName\n====================================\n");
do
{
pListEntry = pListEntry -> Flink;
pLdrMod = (LDR_MODULE*)pListEntry;
printf("0x%08x\t\t",pLdrMod->BaseAddress);
wprintf(L"%s\n",pLdrMod->FullDllName.Buffer);
}while(pListEntry != pStart);
3高级应用(隐藏模块)
#include<windows.h>
typedef struct _UNICODE_STRING {
USHORT Length;
USHORT MaximumLength;
PWSTR Buffer;
} UNICODE_STRING, *PUNICODE_STRING;
typedef struct _PEB_LDR_DATA {
ULONG Length;
BOOLEAN Initialized;
PVOID SsHandle;
LIST_ENTRY InLoadOrderModuleList; //按加载顺序
LIST_ENTRY InMemoryOrderModuleList; //按内存顺序
LIST_ENTRY InInitializationOrderModuleList;//按初始化顺序
PVOID EntryInProgress;
} PEB_LDR_DATA, *PPEB_LDR_DATA;
//每个模块信息的LDR_MODULE部分
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;
//隐藏模块
VOID HideModule(HMODULE hLibrary);
BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
HMODULE hMod = NULL;
switch( ul_reason_for_call )
{
case DLL_PROCESS_ATTACH:
HideModule((HMODULE)hModule);
break;
}
return TRUE;
}
VOID HideModule(HMODULE hLibrary)
{
PPEB_LDR_DATA pLdr = NULL;
PLDR_MODULE FirstModule = NULL, GurrentModule = NULL;
__try
{
__asm
{
mov esi, fs:[0x30]
mov esi, [esi + 0x0C]
mov pLdr,esi
}
FirstModule = (pLDR_MODULE)(pLdr -> InLoadOrderModuleList.Flink);
CurrentModule = FirstModule;
while(!(CurrentModule ->BaseAddress == hLibrary))
{
CurrentModule = (PLDR_MODULE)(CurrentModule
-> InLoadOrderModuleList.Blink );
if(CurrentModule == FirstModule)
break;
}
if(CurrentModule -> BaseAddress != hLibrary) return;
//Dll解除链接
((PLDR_MODULE)(CurrentModule -> InLoadOrderModuleList.Flink))
-> InLoadOrderModuleList.Blink = CurrentModule -> InLoadOrderModuleList.Blink;
((PLDR_MODULE)(CurrentModule->InLoadOrderModuleList.Blink))
->InLoadOrderModuleList.Flink = CurrentModule->InLoadOrderModuleList.Flink;
memset(CurrentModule->FullDllName.Buffer, 0, CurrentModule->FullDllName.Length);
memset(CurrentModule, 0, sizeof(PLDR_MODULE));
__except(EXCEPTION_EXECUTE_HANDLER)
{
return;
}
}
3 检测隐藏进程
说明: 对于Windows下的每一个进程,系统都会给它分配一个excutive process (EPROCESS) block。该结构包含和指向一系列其他相关的数据结构,比如每个进程的所有线程信息。EPROCESS及其所有相关数据结构都位于系统空间,只有Process environment block(peb)是位于进程地址空间,因为它包含由用户模式代码所决定的信息.
ntdll!_EPROCESS
+0x000 Pcb : _KPROCESS
+0x06c ProcessLock : _EX_PUSH_LOCK
+0x070 CreateTime : _LARGE_INTEGER
+0x078 ExitTime : _LARGE_INTEGER
+0x080 RundownProtect : _EX_RUNDOWN_REF
+0x084 UniqueProcessId : Ptr32 Void
+0x088 ActiveProcessLinks : _LIST_ENTRY
+0x090 QuotaUsage : [3] Uint4B
+0x09c QuotaPeak : [3] Uint4B
+0x0a8 CommitCharge : Uint4B
+0x0ac PeakVirtualSize : Uint4B
+0x0b0 VirtualSize : Uint4B
+0x0b4 SessionProcessLinks : _LIST_ENTRY
+0x0bc DebugPort : Ptr32 Void
+0x0c0 ExceptionPort : Ptr32 Void
+0x0c4 ObjectTable : Ptr32 _HANDLE_TABLE
+0x0c8 Token : _EX_FAST_REF
+0x0cc WorkingSetLock : _FAST_MUTEX
+0x0ec WorkingSetPage : Uint4B
+0x0f0 AddressCreationLock : _FAST_MUTEX
…
0:000> dt _LIST_ENTRY
ntdll!_LIST_ENTRY
+0x000 Flink : Ptr32 _LIST_ENTRY
+0x004 Blink : Ptr32 _LIST_ENTRY
0:000> dt _HANDLE_TABLE
ntdll!_HANDLE_TABLE
+0x000 TableCode : Uint4B
+0x004 QuotaProcess : Ptr32 _EPROCESS
+0x008 UniqueProcessId : Ptr32 Void
+0x00c HandleTableLock : [4] _EX_PUSH_LOCK
+0x01c HandleTableList : _LIST_ENTRY
+0x024 HandleContentionEvent : _EX_PUSH_LOCK
+0x028 DebugInfo : Ptr32 _HANDLE_TRACE_DEBUG_INFO
+0x02c ExtraInfoPages : Int4B
+0x030 FirstFree : Uint4B
+0x034 LastFree : Uint4B
+0x038 NextHandleNeedingPool : Uint4B
+0x03c HandleCount : Int4B
+0x040 Flags : Uint4B
+0x040 StrictFIFO : Pos 0, 1 Bit
注意:ObjectTable结构体内偏移0x008处的UniqueProcessId就是当前EPROCESS对应进程的ID号。同时,ObjectTable结构体内偏移0x004处有一个指针QuotaProcess,指向了当前进程的EPROCESS结构(Sytem进程比较特殊,对应的指针为空)。因而,我们不仅能够得到进程的ID号,而且可以获得进程的其他信息.
对于偏移0x088处的ActiveProcessLinks,我们先通过函数PsGetCurrentProcess获得任意进程的EPROCESS的地址,该结构偏移0x084处存放进程ID的指针,读取该指针处的ID作为循环的终止判断条件。到EPROCESS的偏移0x088处ActiveProcessLinks中读取当前进程之后的进程的ActiveProcessLinks指针,通过该指针计算其EPROCESS地址。如此循环,即可以获得所有活动进程的信息.