進程中dll模塊的隱藏
<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />
NetRoc
爲了避免自己的某個dll模塊被別人檢測出來,有時候希望在自己加載一個dll之後,或者將dll注入到他人進程之後避免被檢查出來。這就需要想辦法抹掉這個dll的模塊信息,使得Toolhelp、psapi等枚舉模塊的API無法枚舉它。
我們可以先簡單看看Windows枚舉進程內模塊的辦法吧:
首先是BOOL EnumProcessModules( HANDLE hProcess, HMODULE* lphModule, DWORD cb, LPDWORD lpcbNeeded);
EnumProcessModules實際調用EnumProcessModulesInternal進行枚舉。下面是vista下psapi的代碼片斷:
.text:514024B8 push ebx
.text:514024B9 push 18h
.text:514024BB lea eax, [ebp+stProcessBasicInfo]
.text:514024BE push eax
.text:514024BF push ebx ;ebx=0
.text:514024C0 push [ebp+hProcess]
.text:514024C3 call ds:__imp__NtQueryInformationProcess@20 ; NtQueryInformationProcess(x,x,x,x,x)
.text:514024C9 cmp eax, ebx
.text:514024CB jge short loc_514024E0
調用NtQueryInformationProcess獲得ProcessBasicInformation,在PROCESS_BASIC_INFORMATION結構中取得PEB地址。然後讀取指定進程PEB中的數據
text:514024E0 loc_514024E0: ; CODE XREF: EnumProcessModulesInternal(x,x,x,x,x)+24j
.text:514024E0 mov eax, [ebp+stProcessBasicInfo.PebBaseAddress]
.text:514024E3 cmp eax, ebx
.text:514024E5 jnz short loc_514024EE
.text:514024E7 push 8000000Dh
.text:514024EC jmp short loc_514024CE
.text:514024EE ; ---------------------------------------------------------------------------
.text:514024EE
.text:514024EE loc_514024EE: ; CODE XREF: EnumProcessModulesInternal(x,x,x,x,x)+3Ej
.text:514024EE push ebx ; lpNumberOfBytesRead
.text:514024EF push 4 ; nSize
.text:514024F1 lea ecx, [ebp+Ldr]
.text:514024F4 push ecx ; lpBuffer
.text:514024F5 add eax, 0Ch
.text:514024F8 push eax ; lpBaseAddress
.text:514024F9 push [ebp+hProcess] ; hProcess
.text:514024FC mov edi, ds:__imp__ReadProcessMemory@20 ; ReadProcessMemory(x,x,x,x,x)
.text:51402502 call edi ; ReadProcessMemory(x,x,x,x,x) ; ReadProcessMemory(x,x,x,x,x)
這裏讀取的是PEB地址+0C處的四個字節。
通過WinDbg我們可以看看nt!_PEB的結構
0: kd> dt nt!_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
+0x010 ProcessParameters : Ptr32 _RTL_USER_PROCESS_PARAMETERS
……
+0C處是一個_PEB_LDR_DATA結構指針,裏面包含了和LDR相關的一些數據,進程的模塊鏈表就保存在Ldr中。下面是_PEB_LDR_DATA的結構:
0: kd> dt nt!_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
其中,InLoadOrderModuleList、InMemoryOrderModuleList、InInitializationOrderModuleList就是進程當前已加載模塊的鏈表,只是按照不同的方式排序。EnumProcessModules是通過InMemoryOrderModuleList鏈表枚舉的,而根據Win2k代碼,ToolHelp32函數是通過InLoadOrderModuleList枚舉。這三個_LIST_ENTRY都是在一個RTL_PROCESS_MODULE_INFORMATION結構中的成員。這個結構在2k代碼中有引用,不過沒有確切的定義,下面是ReactOS中的定義,不過看起來我的vista PSAPI中使用的結構已經有所變化了,這裏只作參考。
//
// Loader Data Table Entry
//
typedef struct _LDR_DATA_TABLE_ENTRY
{
LIST_ENTRY InLoadOrderLinks;
LIST_ENTRY InMemoryOrderModuleList;
LIST_ENTRY InInitializationOrderModuleList;
PVOID DllBase;
PVOID EntryPoint;
ULONG SizeOfImage;
UNICODE_STRING FullDllName;
UNICODE_STRING BaseDllName;
ULONG Flags;
USHORT LoadCount;
USHORT TlsIndex;
union
{
LIST_ENTRY HashLinks;
PVOID SectionPointer;
};
ULONG CheckSum;
union
{
ULONG TimeDateStamp;
PVOID LoadedImports;
};
PVOID EntryPointActivationContext;
PVOID PatchInformation;
} LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY;
到這裏,隱藏模塊的方法就已經明瞭了:通過PEB取得Ldr數據,拿到三個模塊鏈表,並將要隱藏的模塊斷鏈即可。下面是主要代碼實現:
BOOL HideMyself()
{
HMODULE hMod = GetModuleHandle( _T( "ntdll.dll"));
HMODULE hModMyself = GetModuleHandle( _T("dll.dll"));
pfnNtQueryInformationProcess p = (pfnNtQueryInformationProcess)::GetProcAddress( hMod, "NtQueryInformationProcess");
PROCESS_BASIC_INFORMATION stInfo = {0};
DWORD dwRetnLen = 0;
DWORD dw = p( GetCurrentProcess(), 0, &stInfo, sizeof(stInfo), &dwRetnLen);
PPEB pPeb = stInfo.PebBaseAddress;
PLIST_ENTRY ListHead, Current;
PLDR_DATA_TABLE_ENTRY pstEntry = NULL;
ListHead = &( stInfo.PebBaseAddress->Ldr->InLoadOrderModuleList);
Current = ListHead->Flink;
while ( Current != ListHead)
{
pstEntry = CONTAINING_RECORD( Current, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);
//DebugOutW( L"Module:%s, base:0x%X/r/n", pstEntry->FullDllName.Buffer, pstEntry->EntryPoint);
if ( pstEntry->DllBase == hModMyself)
{
pstEntry->InLoadOrderLinks.Flink->Blink = pstEntry->InLoadOrderLinks.Blink;
pstEntry->InLoadOrderLinks.Blink->Flink = pstEntry->InLoadOrderLinks.Flink;
DebugOut( _T( "Hide injected dll."));
break;
}
Current = pstEntry->InLoadOrderLinks.Flink;
}
ListHead = &( stInfo.PebBaseAddress->Ldr->InMemoryOrderModuleList);
Current = ListHead->Flink;
while ( Current != ListHead)
{
pstEntry = CONTAINING_RECORD( Current, LDR_DATA_TABLE_ENTRY, InMemoryOrderModuleList);
DebugOutW( L"Module:%s, base:0x%X/r/n", pstEntry->FullDllName.Buffer, pstEntry->EntryPoint);
if ( pstEntry->DllBase == hModMyself)
{
pstEntry->InMemoryOrderModuleList.Flink->Blink = pstEntry->InMemoryOrderModuleList.Blink;
pstEntry->InMemoryOrderModuleList.Blink->Flink = pstEntry->InMemoryOrderModuleList.Flink;
DebugOut( _T( "Hide injected dll."));
break;
}
Current = pstEntry->InMemoryOrderModuleList.Flink;
}
DebugOutW( L"/r/n");
ListHead = &( stInfo.PebBaseAddress->Ldr->InInitializationOrderModuleList);
Current = ListHead->Flink;
while ( Current != ListHead)
{
pstEntry = CONTAINING_RECORD( Current, LDR_DATA_TABLE_ENTRY, InInitializationOrderModuleList);
DebugOutW( L"Module:%s, base:0x%X/r/n", pstEntry->FullDllName.Buffer, pstEntry->EntryPoint);
if ( pstEntry->DllBase == hModMyself)
{
pstEntry->InInitializationOrderModuleList.Flink->Blink = pstEntry->InInitializationOrderModuleList.Blink;
pstEntry->InInitializationOrderModuleList.Blink->Flink = pstEntry->InInitializationOrderModuleList.Flink;
DebugOut( _T( "Hide injected dll."));
break;
}
Current = pstEntry->InInitializationOrderModuleList.Flink;
}
//DebugOut( _T("Out HideMyself/r/n"));
return TRUE;
}
這樣處理之後,通過常規的枚舉進程方式已經枚舉不到隱藏模塊,ProcessExplorer也無法枚舉。但是,通過枚舉進程內存空間等非常規方法,仍然是可以找到的。關於PSAPI和Toolhelp函數枚舉模塊的原理,可以逆向Windows代碼,或者查找網上的代碼看看就明白了。