Win32之隱藏DLL隱藏模塊技術
這一講涉及到windows底層技術.跟匯編內容. 我們才可以實現模塊隱藏(也稱為DLL隱藏)
一丶API反匯編勾引興趣
我們都用過Windows的進程跟線程API 也就是 GetCurrentThreadId() 跟 GetCurrentProcessId():
一個是獲取進程ID,一個是線程ID
那么我們利用反匯編技術.看看其實現代碼是什么.
可以看到使用了FS寄存器.並且獲取18內容. 然后+20內容獲取出我們的進程Pid
那么FS寄存器是什么.
二丶FS寄存器簡介
FS寄存器如果學習過內核的同學們應該知道.段選擇子. 在這里就不說了.
FS是執向一個TEB結構的一個寄存器. TEB結構是什么. TEB結構是線程環境快. 保存了當前的線程的信息.
MSDN中.TEB是一個假的TEB.只告訴你有這個東西. 具體我們需要時Windbg查看結構體成員.
MSDN鏈接: https://docs.microsoft.com/zh-cn/windows/desktop/api/winternl/ns-winternl-_teb
使用WinDbg的dt命令即可查看.
如下圖:
我們通過反匯編.知道了TEB結構的地址 .也就是 mov eax,dword ptr fs:[0x18]
那么其實Windows底層也提供了一個獲取TEB結構的API . NtCurrentTeb() 返回TEB的地址.因為TEB結構我們並沒有.所以直接用DWORD接受即可.
當然可以通過dt TEB知道結構體的成員. 那么我們可以偽造. 不過這個不是我們的重點.
首先Win32下TEB 的結構跟64位下的TEB結構不一樣.
32位下 TEB獲取是 mov eax,fs:[0x18] 64位下是 mov rax,qword ptr gs:[0x30] 具體的可以自己逆向一個訪問TEB的API查看即可.
三丶以32位講解. 熟悉TEB結構體.
這里直接列出dt出來的32位下的TEB結構體
+0x000 NtTib : _NT_TIB //TIB結構.存儲棧信息 +0x01c EnvironmentPointer : Ptr32 Void +0x020 ClientId : _CLIENT_ID //保存進程ID跟線程ID +0x028 ActiveRpcHandle : Ptr32 Void +0x02c ThreadLocalStoragePointer : Ptr32 Void +0x030 ProcessEnvironmentBlock : Ptr32 _PEB //PEB進程環境快結構. +0x034 LastErrorValue : Uint4B +0x038 CountOfOwnedCriticalSections : Uint4B +0x03c CsrClientThread : Ptr32 Void +0x040 Win32ThreadInfo : Ptr32 Void +0x044 User32Reserved : [26] Uint4B +0x0ac UserReserved : [5] Uint4B +0x0c0 WOW32Reserved : Ptr32 Void +0x0c4 CurrentLocale : Uint4B +0x0c8 FpSoftwareStatusRegister : Uint4B +0x0cc SystemReserved1 : [54] Ptr32 Void +0x1a4 ExceptionCode : Int4B +0x1a8 ActivationContextStackPointer : Ptr32 _ACTIVATION_CONTEXT_STACK +0x1ac SpareBytes : [36] UChar +0x1d0 TxFsContext : Uint4B +0x1d4 GdiTebBatch : _GDI_TEB_BATCH +0x6b4 RealClientId : _CLIENT_ID +0x6bc GdiCachedProcessHandle : Ptr32 Void +0x6c0 GdiClientPID : Uint4B +0x6c4 GdiClientTID : Uint4B +0x6c8 GdiThreadLocalInfo : Ptr32 Void +0x6cc Win32ClientInfo : [62] Uint4B +0x7c4 glDispatchTable : [233] Ptr32 Void +0xb68 glReserved1 : [29] Uint4B +0xbdc glReserved2 : Ptr32 Void +0xbe0 glSectionInfo : Ptr32 Void +0xbe4 glSection : Ptr32 Void +0xbe8 glTable : Ptr32 Void +0xbec glCurrentRC : Ptr32 Void +0xbf0 glContext : Ptr32 Void +0xbf4 LastStatusValue : Uint4B +0xbf8 StaticUnicodeString : _UNICODE_STRING +0xc00 StaticUnicodeBuffer : [261] Wchar +0xe0c DeallocationStack : Ptr32 Void +0xe10 TlsSlots : [64] Ptr32 Void +0xf10 TlsLinks : _LIST_ENTRY +0xf18 Vdm : Ptr32 Void +0xf1c ReservedForNtRpc : Ptr32 Void +0xf20 DbgSsReserved : [2] Ptr32 Void +0xf28 HardErrorMode : Uint4B +0xf2c Instrumentation : [9] Ptr32 Void +0xf50 ActivityId : _GUID +0xf60 SubProcessTag : Ptr32 Void +0xf64 EtwLocalData : Ptr32 Void +0xf68 EtwTraceData : Ptr32 Void +0xf6c WinSockData : Ptr32 Void +0xf70 GdiBatchCount : Uint4B +0xf74 CurrentIdealProcessor : _PROCESSOR_NUMBER +0xf74 IdealProcessorValue : Uint4B +0xf74 ReservedPad0 : UChar +0xf75 ReservedPad1 : UChar +0xf76 ReservedPad2 : UChar +0xf77 IdealProcessor : UChar +0xf78 GuaranteedStackBytes : Uint4B +0xf7c ReservedForPerf : Ptr32 Void +0xf80 ReservedForOle : Ptr32 Void +0xf84 WaitingOnLoaderLock : Uint4B +0xf88 SavedPriorityState : Ptr32 Void +0xf8c SoftPatchPtr1 : Uint4B +0xf90 ThreadPoolData : Ptr32 Void +0xf94 TlsExpansionSlots : Ptr32 Ptr32 Void +0xf98 MuiGeneration : Uint4B +0xf9c IsImpersonating : Uint4B +0xfa0 NlsCache : Ptr32 Void +0xfa4 pShimData : Ptr32 Void +0xfa8 HeapVirtualAffinity : Uint4B +0xfac CurrentTransactionHandle : Ptr32 Void +0xfb0 ActiveFrame : Ptr32 _TEB_ACTIVE_FRAME +0xfb4 FlsData : Ptr32 Void +0xfb8 PreferredLanguages : Ptr32 Void +0xfbc UserPrefLanguages : Ptr32 Void +0xfc0 MergedPrefLanguages : Ptr32 Void +0xfc4 MuiImpersonation : Uint4B +0xfc8 CrossTebFlags : Uint2B +0xfc8 SpareCrossTebBits : Pos 0, 16 Bits +0xfca SameTebFlags : Uint2B +0xfca SafeThunkCall : Pos 0, 1 Bit +0xfca InDebugPrint : Pos 1, 1 Bit +0xfca HasFiberData : Pos 2, 1 Bit +0xfca SkipThreadAttach : Pos 3, 1 Bit +0xfca WerInShipAssertCode : Pos 4, 1 Bit +0xfca RanProcessInit : Pos 5, 1 Bit +0xfca ClonedThread : Pos 6, 1 Bit +0xfca SuppressDebugMsg : Pos 7, 1 Bit +0xfca DisableUserStackWalk : Pos 8, 1 Bit +0xfca RtlExceptionAttached : Pos 9, 1 Bit +0xfca InitialThread : Pos 10, 1 Bit +0xfca SpareSameTebBits : Pos 11, 5 Bits +0xfcc TxnScopeEnterCallback : Ptr32 Void +0xfd0 TxnScopeExitCallback : Ptr32 Void +0xfd4 TxnScopeContext : Ptr32 Void +0xfd8 LockCount : Uint4B +0xfdc SpareUlong0 : Uint4B +0xfe0 ResourceRetValue : Ptr32 Void
根據上圖我們首先看注釋的成員.因為成員很多.只講解重要的.其余成員可以通過Google獲取.
_NT_TIB結構.這個結構
保存了棧從哪里開始到哪里結束.版本信息.等等.
下面的是 保存進程ID跟線程ID的結構體. 我們看一下.
進程ID是 1db8 線程ID是 0x394
上面的命令windbg可以通過點擊. 我是點擊的所以命令很乏在. dx....... xxxx
我們直接可以使用 dt _CLIENT_ID 地址 來看.
TEB首地址我們知道是 fs:[0x18] 所以我們db一下看看地址是什么.下面會使用這個地址.
上面我們知道了 _CLIENT_ID結構體了.那么我們就可以自己不適用API 然后獲取進程PID了跟線程PID了.
四丶使用代碼實現自己的獲取進程PID跟線程PID函數.
DWORD GetMyCurrentProcessPid32() { //獲取自己進程的PID /* 1.使用API 獲取TEB首地址 NtCurrentTeb(); 2.使用匯編獲取 */ //API 獲取TEB的地址 //DWORD dwTebAddress = (DWORD)NtCurrentTeb(); // 64是 GS:[30] 模塊首地址 //dwTebAddress = dwTebAddress + 0x20; //DWORD dwPid = *(DWORD *)dwTebAddress; DWORD dwPid = 0; __asm { mov eax, fs:[0x18] add eax, 0x20 //加0x20是獲取進程pid + 0x24則是獲取線程PID mov eax,[eax] mov dwPid,eax } return dwPid; }
如果懂了上面的結構.那么上面的代碼就很好理解了. 上面有兩種方法實現.一個是指針實現.也就是通過NtCurrentTeb()獲取TEB結構.自己加偏移取內容得出進程PID
另一種就是匯編了.
匯編很簡單.
1.首先獲取TEB結構首地址
2.TEB結構首地址 + 0x20偏移 獲取到的是 _CLIENT_ID結構.
3.因為是結構本身.所以對 [eax] 也就是對eax取內容.得出進程PID
4.將進程PID賦值給變量.然后下方返回.
實驗代碼截圖:
完全可以獲取出來.
上面實現自己的API只是對TEB結構有一個認識.知道可以做什么.
下面貼一份64位程序獲取自己的進程PID
LONGLONG GetMyCurrentProcessPid64() { /* 1.使用API 獲取TEB首地址 NtCurrentTeb(); */ //API 獲取TEB的地址 __int64 dwTebAddress = (__int64)NtCurrentTeb(); // GS:[30] 模塊首地址 dwTebAddress = dwTebAddress + 0x40; //64位改成40偏移了.具體偏移自己使用Windbg查看64的環境下的TEB結構.不多解釋. __int64 dwPid = *(__int64 *)dwTebAddress; return dwPid; }
五丶PEB結構
其實最主要的是我們要將的PEB結構.
PEB就是進程環境塊.保存了進程的一些列細心.
如果是32位情況下則是+0x30
我們看下PEB結構中的信息
[+0x000] InheritedAddressSpace : 0x0 [Type: unsigned char] [+0x001] ReadImageFileExecOptions : 0x0 [Type: unsigned char] [+0x002] BeingDebugged : 0x1 [Type: unsigned char] //一個char類型.為1表示調試狀態.為0表示沒有調試.可以用於反調試. API也是從這里獲取的標志 [+0x003] BitField : 0x8 [Type: unsigned char] [+0x003 ( 0: 0)] ImageUsesLargePages : 0x0 [Type: unsigned char] [+0x003 ( 1: 1)] IsProtectedProcess : 0x0 [Type: unsigned char] [+0x003 ( 2: 2)] IsLegacyProcess : 0x0 [Type: unsigned char] [+0x003 ( 3: 3)] IsImageDynamicallyRelocated : 0x1 [Type: unsigned char] [+0x003 ( 4: 4)] SkipPatchingUser32Forwarders : 0x0 [Type: unsigned char] [+0x003 ( 7: 5)] SpareBits : 0x0 [Type: unsigned char] [+0x004] Mutant : 0xffffffff [Type: void *] [+0x008] ImageBaseAddress : 0x11d0000 [Type: void *] [+0x00c] Ldr : 0x77190200 [Type: _PEB_LDR_DATA *] //用於模塊隱藏的結構體 [+0x010] ProcessParameters : 0x7216d0 [Type: _RTL_USER_PROCESS_PARAMETERS *] [+0x014] SubSystemData : 0x0 [Type: void *] [+0x018] ProcessHeap : 0x720000 [Type: void *] [+0x01c] FastPebLock : 0x77192100 [Type: _RTL_CRITICAL_SECTION *] [+0x020] AtlThunkSListPtr : 0x0 [Type: void *] [+0x024] IFEOKey : 0x0 [Type: void *] [+0x028] CrossProcessFlags : 0x2 [Type: unsigned long] [+0x028 ( 0: 0)] ProcessInJob : 0x0 [Type: unsigned long] [+0x028 ( 1: 1)] ProcessInitializing : 0x1 [Type: unsigned long] [+0x028 ( 2: 2)] ProcessUsingVEH : 0x0 [Type: unsigned long] [+0x028 ( 3: 3)] ProcessUsingVCH : 0x0 [Type: unsigned long] [+0x028 ( 4: 4)] ProcessUsingFTH : 0x0 [Type: unsigned long] [+0x028 (31: 5)] ReservedBits0 : 0x0 [Type: unsigned long] [+0x02c] KernelCallbackTable : 0x0 [Type: void *] [+0x02c] UserSharedInfoPtr : 0x0 [Type: void *] [+0x030] SystemReserved [Type: unsigned long [1]] [+0x034] AtlThunkSListPtr32 : 0x0 [Type: unsigned long] [+0x038] ApiSetMap : 0x40000 [Type: void *] [+0x03c] TlsExpansionCounter : 0x0 [Type: unsigned long] [+0x040] TlsBitmap : 0x77194250 [Type: void *] [+0x044] TlsBitmapBits [Type: unsigned long [2]] [+0x04c] ReadOnlySharedMemoryBase : 0x7efe0000 [Type: void *] [+0x050] HotpatchInformation : 0x0 [Type: void *] [+0x054] ReadOnlyStaticServerData : 0x7efe0a90 [Type: void * *] [+0x058] AnsiCodePageData : 0x7efa0000 [Type: void *] [+0x05c] OemCodePageData : 0x7efa0000 [Type: void *] [+0x060] UnicodeCaseTableData : 0x7efd0028 [Type: void *] [+0x064] NumberOfProcessors : 0x8 [Type: unsigned long] [+0x068] NtGlobalFlag : 0x70 [Type: unsigned long] [+0x070] CriticalSectionTimeout : {-25920000000000} [Type: _LARGE_INTEGER] [+0x078] HeapSegmentReserve : 0x100000 [Type: unsigned long] [+0x07c] HeapSegmentCommit : 0x2000 [Type: unsigned long] [+0x080] HeapDeCommitTotalFreeThreshold : 0x10000 [Type: unsigned long] [+0x084] HeapDeCommitFreeBlockThreshold : 0x1000 [Type: unsigned long] [+0x088] NumberOfHeaps : 0x1 [Type: unsigned long] [+0x08c] MaximumNumberOfHeaps : 0x10 [Type: unsigned long] [+0x090] ProcessHeaps : 0x77194760 [Type: void * *] [+0x094] GdiSharedHandleTable : 0x0 [Type: void *] [+0x098] ProcessStarterHelper : 0x0 [Type: void *] [+0x09c] GdiDCAttributeList : 0x0 [Type: unsigned long] [+0x0a0] LoaderLock : 0x771920c0 [Type: _RTL_CRITICAL_SECTION *] [+0x0a4] OSMajorVersion : 0x6 [Type: unsigned long] [+0x0a8] OSMinorVersion : 0x1 [Type: unsigned long] [+0x0ac] OSBuildNumber : 0x1db1 [Type: unsigned short] [+0x0ae] OSCSDVersion : 0x100 [Type: unsigned short] [+0x0b0] OSPlatformId : 0x2 [Type: unsigned long] [+0x0b4] ImageSubsystem : 0x3 [Type: unsigned long] [+0x0b8] ImageSubsystemMajorVersion : 0x6 [Type: unsigned long] [+0x0bc] ImageSubsystemMinorVersion : 0x0 [Type: unsigned long] [+0x0c0] ActiveProcessAffinityMask : 0xff [Type: unsigned long] [+0x0c4] GdiHandleBuffer [Type: unsigned long [34]] [+0x14c] PostProcessInitRoutine : 0x0 [Type: void (*)()] [+0x150] TlsExpansionBitmap : 0x77194248 [Type: void *] [+0x154] TlsExpansionBitmapBits [Type: unsigned long [32]] [+0x1d4] SessionId : 0x1 [Type: unsigned long] [+0x1d8] AppCompatFlags : {0x0} [Type: _ULARGE_INTEGER] [+0x1e0] AppCompatFlagsUser : {0x0} [Type: _ULARGE_INTEGER] [+0x1e8] pShimData : 0x0 [Type: void *] [+0x1ec] AppCompatInfo : 0x0 [Type: void *] [+0x1f0] CSDVersion : "Service Pack 1" [Type: _UNICODE_STRING] [+0x1f8] ActivationContextData : 0x60000 [Type: _ACTIVATION_CONTEXT_DATA *] [+0x1fc] ProcessAssemblyStorageMap : 0x0 [Type: _ASSEMBLY_STORAGE_MAP *] [+0x200] SystemDefaultActivationContextData : 0x50000 [Type: _ACTIVATION_CONTEXT_DATA *] [+0x204] SystemAssemblyStorageMap : 0x0 [Type: _ASSEMBLY_STORAGE_MAP *] [+0x208] MinimumStackCommit : 0x0 [Type: unsigned long] [+0x20c] FlsCallback : 0x0 [Type: _FLS_CALLBACK_INFO *] [+0x210] FlsListHead [Type: _LIST_ENTRY] [+0x218] FlsBitmap : 0x77194240 [Type: void *] [+0x21c] FlsBitmapBits [Type: unsigned long [4]] [+0x22c] FlsHighIndex : 0x0 [Type: unsigned long] [+0x230] WerRegistrationData : 0x0 [Type: void *] [+0x234] WerShipAssertPtr : 0x0 [Type: void *] [+0x238] pContextData : 0x70000 [Type: void *] [+0x23c] pImageHeaderHash : 0x0 [Type: void *] [+0x240] TracingFlags : 0x0 [Type: unsigned long] [+0x240 ( 0: 0)] HeapTracingEnabled : 0x0 [Type: unsigned long] [+0x240 ( 1: 1)] CritSecTracingEnabled : 0x0 [Type: unsigned long] [+0x240 (31: 2)] SpareTracingBits : 0x0 [Type: unsigned long]
這個結構中我們簡單了解一下.
第一個注釋的地方.那個地方可以用於反調試. 如果我們程序是調試狀態.那么這個位置則為1.否則正常狀態啟動則為0
第二個是我們用於模塊隱藏的一個結構. 執向一個 _PEB_LDR_DATA的結構
我們看下這個結構
這個結構. +0c偏移 看名字.是一個保存模塊列表信息的一個結構. 其中執向的是一個_LIST_ENTRY這是一個雙向鏈表. 其實真正執向的結構不是這個.
而是: _LDR_DATA_TABLE_ENTRY 結構.而這個結構MSDN也說了.但是結構成員是不缺定的.所以我們繼續使用Windbg查看就可以得出這個結構真實的面目.
當我們看到這個結構就會發現一個新天地.這個結構中保存了模塊信息. 其中 + 0偏移 + 8 偏移就是上面的雙向鏈表.只不過windows把下面的成員給我們隱藏了.
六丶_LDR_DATA_TABLE_ENTRY 結構成員解析
DR_DATA_TABLE_ENTRY ntdll!_LDR_DATA_TABLE_ENTRY +0x000 InLoadOrderLinks : _LIST_ENTRY 雙向鏈表頭 +0x008 InMemoryOrderLinks : _LIST_ENTRY 雙向鏈表尾 +0x010 InInitializationOrderLinks : _LIST_ENTRY +0x018 DllBase : Ptr32 Void dll模塊基址. +0x01c EntryPoint : Ptr32 Void dll模塊入口點 +0x020 SizeOfImage : Uint4B 鏡像大小 +0x024 FullDllName : _UNICODE_STRING UNICODE_STRING結構的模塊路徑 +0x02c BaseDllName : _UNICODE_STRING +0x034 Flags : Uint4B +0x038 LoadCount : Uint2B +0x03a TlsIndex : Uint2B +0x03c HashLinks : _LIST_ENTRY +0x03c SectionPointer : Ptr32 Void +0x040 CheckSum : Uint4B +0x044 TimeDateStamp : Uint4B +0x044 LoadedImports : Ptr32 Void +0x048 EntryPointActivationContext : Ptr32 _ACTIVATION_CONTEXT +0x04c PatchInformation : Ptr32 Void +0x050 ForwarderLinks : _LIST_ENTRY +0x058 ServiceTagLinks : _LIST_ENTRY +0x060 StaticLinks : _LIST_ENTRY +0x068 ContextInformation : Ptr32 Void +0x06c OriginalBase : Uint4B +0x070 LoadTime : _LARGE_INTEGER
知道上面的結構.那么我們要實現模塊隱藏就很簡單了.
思路:
我們獲取到了這個結構.因為是鏈表.可以遍歷鏈表. 根據DllBase判斷 你的模塊基址跟這個模塊基址是否一樣.如果一樣那么我們就斷開鏈表
也就是把當前模塊的鏈表頭跟尾巴.執向下一個. 保證沒有鏈表執向即可.
實現一個模塊隱藏很簡單.難的就是上面的結構.我們必須要熟悉.偏移要知道.我們才可以做到模塊隱藏.
具體代碼我會放到下面. 然后講解代碼.
#include <stdio.h> #include <Windows.h> #include <stdlib.h> DWORD g_isHide = 0; 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; } PEB_LDR_DATA, *PPEB_LDR_DATA; typedef struct _LDR_MODULE { LIST_ENTRY InLoadOrderModuleList; //+0x00 LIST_ENTRY InMemoryOrderModuleList; //+0x08 LIST_ENTRY InInitializationOrderModuleList; //+0x10 void* BaseAddress; //+0x18 void* EntryPoint; //+0x1c ULONG SizeOfImage; UNICODE_STRING FullDllName; UNICODE_STRING BaseDllName; ULONG Flags; SHORT LoadCount; SHORT TlsIndex; HANDLE SectionHandle; ULONG CheckSum; ULONG TimeDateStamp; } LDR_MODULE, *PLDR_MODULE; void HideDll() //這個函數是主要的 { HMODULE hMod = ::GetModuleHandle("ntdll.dll"); PLIST_ENTRY Head, Cur; PPEB_LDR_DATA ldr; PLDR_MODULE ldm; __asm { mov eax, fs:[0x30] //獲取PEB結構 mov ecx, [eax + 0x0c] //Ldr //獲取_PEB_LDR_DATA結構 mov ldr, ecx } Head = &(ldr->InLoadOrderModuleList); //獲取模塊鏈表地址 Cur = Head->Flink; //獲取指向的結點. do { ldm = CONTAINING_RECORD(Cur, LDR_MODULE, InLoadOrderModuleList); //獲取 _LDR_DATA_TABLE_ENTRY結構體地址 //printf("EntryPoint [0x%X]\n",ldm->BaseAddress); if (hMod == ldm->BaseAddress) //判斷要隱藏的DLL基址跟結構中的基址是否一樣 { g_isHide = 1; //如果進入.則標志置為1,表示已經開始進行隱藏了. ldm->InLoadOrderModuleList.Blink->Flink = //雙向鏈表. 斷開鏈表 ldm->InLoadOrderModuleList.Flink; ldm->InLoadOrderModuleList.Flink->Blink = ldm->InLoadOrderModuleList.Blink; ldm->InInitializationOrderModuleList.Blink->Flink = ldm->InInitializationOrderModuleList.Flink; ldm->InInitializationOrderModuleList.Flink->Blink = ldm->InInitializationOrderModuleList.Blink; ldm->InMemoryOrderModuleList.Blink->Flink = ldm->InMemoryOrderModuleList.Flink; ldm->InMemoryOrderModuleList.Flink->Blink = ldm->InMemoryOrderModuleList.Blink; break; } Cur = Cur->Flink; } while (Head != Cur); } int main() { printf("按鍵開始隱藏\r\n"); getchar(); HideDll(); if (g_isHide == 0) { printf("沒有成功隱藏\r\n"); system("pause"); return 0; } printf("成功隱藏\r\n"); system("pause"); return 0; }
上面是我們隱藏ntdll.看一下沒有隱藏的時候
隱藏一下ntdll看下.