PEB及LDR链
PEB地址的取得
在NT內核系統中fs寄存器指向TEB結構,TEB+0x30處指向PEB結構,PEB+0x0c處指向PEB_LDR_DATA結構,PEB_LDR_DATA+0x1c處存放一些指向動態鏈接庫信息的鏈表地址,win7下第一個指向ntdl.dll,第三個就是kernel32.dll的。
typedef struct _TEB
{
NT_TIB Tib; /* 00h */
PVOID EnvironmentPointer; /* 1Ch */
CLIENT_ID Cid; /* 20h */
PVOID ActiveRpcHandle; /* 28h */
PVOID ThreadLocalStoragePointer; /* 2Ch */
struct _PEB *ProcessEnvironmentBlock; /* 30h */
ULONG LastErrorValue; /* 34h */
ULONG CountOfOwnedCriticalSections; /* 38h */
PVOID CsrClientThread; /* 3Ch */
struct _W32THREAD* Win32ThreadInfo; /* 40h */
ULONG User32Reserved[0x1A]; /* 44h */
ULONG UserReserved[5]; /* ACh */
PVOID WOW32Reserved; /* C0h */
LCID CurrentLocale; /* C4h */
ULONG FpSoftwareStatusRegister; /* C8h */
PVOID SystemReserved1[0x36]; /* CCh */
LONG ExceptionCode; /* 1A4h */
struct _ACTIVATION_CONTEXT_STACK *ActivationContextStackPointer; /* 1A8h */
UCHAR SpareBytes1[0x28]; /* 1ACh */
GDI_TEB_BATCH GdiTebBatch; /* 1D4h */
CLIENT_ID RealClientId; /* 6B4h */
PVOID GdiCachedProcessHandle; /* 6BCh */
ULONG GdiClientPID; /* 6C0h */
ULONG GdiClientTID; /* 6C4h */
PVOID GdiThreadLocalInfo; /* 6C8h */
ULONG Win32ClientInfo[62]; /* 6CCh */
PVOID glDispatchTable[0xE9]; /* 7C4h */
ULONG glReserved1[0x1D]; /* B68h */
PVOID glReserved2; /* BDCh */
PVOID glSectionInfo; /* BE0h */
PVOID glSection; /* BE4h */
PVOID glTable; /* BE8h */
PVOID glCurrentRC; /* BECh */
PVOID glContext; /* BF0h */
NTSTATUS LastStatusValue; /* BF4h */
UNICODE_STRING StaticUnicodeString; /* BF8h */
WCHAR StaticUnicodeBuffer[0x105]; /* C00h */
PVOID DeallocationStack; /* E0Ch */
PVOID TlsSlots[0x40]; /* E10h */
LIST_ENTRY TlsLinks; /* F10h */
PVOID Vdm; /* F18h */
PVOID ReservedForNtRpc; /* F1Ch */
PVOID DbgSsReserved[0x2]; /* F20h */
ULONG HardErrorDisabled; /* F28h */
PVOID Instrumentation[14]; /* F2Ch */
PVOID SubProcessTag; /* F64h */
PVOID EtwTraceData; /* F68h */
PVOID WinSockData; /* F6Ch */
ULONG GdiBatchCount; /* F70h */
BOOLEAN InDbgPrint; /* F74h */
BOOLEAN FreeStackOnTermination; /* F75h */
BOOLEAN HasFiberData; /* F76h */
UCHAR IdealProcessor; /* F77h */
ULONG GuaranteedStackBytes; /* F78h */
PVOID ReservedForPerf; /* F7Ch */
PVOID ReservedForOle; /* F80h */
ULONG WaitingOnLoaderLock; /* F84h */
ULONG SparePointer1; /* F88h */
ULONG SoftPatchPtr1; /* F8Ch */
ULONG SoftPatchPtr2; /* F90h */
PVOID *TlsExpansionSlots; /* F94h */
ULONG ImpersionationLocale; /* F98h */
ULONG IsImpersonating; /* F9Ch */
PVOID NlsCache; /* FA0h */
PVOID pShimData; /* FA4h */
ULONG HeapVirualAffinity; /* FA8h */
PVOID CurrentTransactionHandle; /* FACh */
PTEB_ACTIVE_FRAME ActiveFrame; /* FB0h */
PVOID FlsData; /* FB4h */
UCHAR SafeThunkCall; /* FB8h */
UCHAR BooleanSpare[3]; /* FB9h */
} TEB, *PTEB;
?
typedef struct _PEB
{
UCHAR InheritedAddressSpace; // 00h
UCHAR ReadImageFileExecOptions; // 01h
UCHAR BeingDebugged; // 02h
UCHAR Spare; // 03h
PVOID Mutant; // 04h
PVOID ImageBaseAddress; // 08h
PPEB_LDR_DATA Ldr; // 0Ch
PRTL_USER_PROCESS_PARAMETERS ProcessParameters; // 10h
PVOID SubSystemData; // 14h
PVOID ProcessHeap; // 18h
PVOID FastPebLock; // 1Ch
PPEBLOCKROUTINE FastPebLockRoutine; // 20h
PPEBLOCKROUTINE FastPebUnlockRoutine; // 24h
ULONG EnvironmentUpdateCount; // 28h
PVOID* KernelCallbackTable; // 2Ch
PVOID EventLogSection; // 30h
PVOID EventLog; // 34h
PPEB_FREE_BLOCK FreeList; // 38h
ULONG TlsExpansionCounter; // 3Ch
PVOID TlsBitmap; // 40h
ULONG TlsBitmapBits[0x2]; // 44h
PVOID ReadOnlySharedMemoryBase; // 4Ch
PVOID ReadOnlySharedMemoryHeap; // 50h
PVOID* ReadOnlyStaticServerData; // 54h
PVOID AnsiCodePageData; // 58h
PVOID OemCodePageData; // 5Ch
PVOID UnicodeCaseTableData; // 60h
ULONG NumberOfProcessors; // 64h
ULONG NtGlobalFlag; // 68h
UCHAR Spare2[0x4]; // 6Ch
LARGE_INTEGER CriticalSectionTimeout; // 70h
ULONG HeapSegmentReserve; // 78h
ULONG HeapSegmentCommit; // 7Ch
ULONG HeapDeCommitTotalFreeThreshold; // 80h
ULONG HeapDeCommitFreeBlockThreshold; // 84h
ULONG NumberOfHeaps; // 88h
ULONG MaximumNumberOfHeaps; // 8Ch
PVOID** ProcessHeaps; // 90h
PVOID GdiSharedHandleTable; // 94h
PVOID ProcessStarterHelper; // 98h
PVOID GdiDCAttributeList; // 9Ch
PVOID LoaderLock; // A0h
ULONG OSMajorVersion; // A4h
ULONG OSMinorVersion; // A8h
ULONG OSBuildNumber; // ACh
ULONG OSPlatformId; // B0h
ULONG ImageSubSystem; // B4h
ULONG ImageSubSystemMajorVersion; // B8h
ULONG ImageSubSystemMinorVersion; // C0h
ULONG GdiHandleBuffer[0x22]; // C4h
PVOID ProcessWindowStation; // ???
} PEB, *PPEB;
?
1.認識LDR鏈??FS段寄存器作為選擇子指向當前的活動線程的TEB結構(Thread?Environment?Block)注釋。在TEB偏移的0x30處,就是指向PEB(Process?Environment?Block)注釋②?的指針。而在PEB偏移的0x0c處是指向PEB_LDR_DATA結構的指針。PEB_LDR_DATA的結構如下:
??
typedef?struct?_PEB_LDR_DATA
{
ULONG?Length;?//?+0x00
BOOLEAN?Initialized;?//?+0x04
PVOID?SsHandle;?//?+0x08
LIST_ENTRY?InLoadOrderModuleList;?//?+0x0c
LIST_ENTRY?InMemoryOrderModuleList;?//?+0x14
LIST_ENTRY?InInitializationOrderModuleList;//?+0x1c
}?PEB_LDR_DATA,*PPEB_LDR_DATA;?//?+0x24
我們看到,在PEB_LDR_DATA結構中,又包含三個LIST_ENTRY結構體分別命名為:
InLoadOrderModuleList;????????????????模塊加載順序
InMemoryOrderModuleList;??????????????模塊在內存中的順序
InInitializationOrderModuleList;?????模塊初始化裝載順序
LIST_ENTRY其結構定義如下:
typedef?struct?_LIST_ENTRY?{
???struct?_LIST_ENTRY?*Flink;
???struct?_LIST_ENTRY?*Blink;
}?LIST_ENTRY,?*PLIST_ENTRY,?*RESTRICTED_POINTER?PRLIST_ENTRY;
微軟是怎么解釋LIST_ENTRY結構中成員作用的呢?來看看MSDN
The?head?of?a?doubly-linked?list?that?contains?the?loaded?modules?for?the?process.?Each?item?in?the?list?is?a?pointer?to?an?LDR_DATA_TABLE_ENTRY?structure?
這個雙鏈表指向進程裝載的模塊,結構中的每個指針,指向了一個LDR_DATA_TABLE_ENTRY?的結構:
我們來看看這個結構的定義:
typedef?struct?_LDR_DATA_TABLE_ENTRY
{
?????LIST_ENTRY?InLoadOrderLinks;
?????LIST_ENTRY?InMemoryOrderLinks;
?????LIST_ENTRY?InInitializationOrderLinks;
?????PVOID?DllBase;
?????PVOID?EntryPoint;
?????ULONG?SizeOfImage;
?????UNICODE_STRING?FullDllName;
?????UNICODE_STRING?BaseDllName;
?????ULONG?Flags;
?????WORD?LoadCount;
?????WORD?TlsIndex;
?????union
?????{
??????????LIST_ENTRY?HashLinks;
??????????struct
??????????{
???????????????PVOID?SectionPointer;
???????????????ULONG?CheckSum;
??????????};
?????};
?????union
?????{
??????????ULONG?TimeDateStamp;
??????????PVOID?LoadedImports;
?????};
?????_ACTIVATION_CONTEXT?*?EntryPointActivationContext;
?????PVOID?PatchInformation;
?????LIST_ENTRY?ForwarderLinks;
?????LIST_ENTRY?ServiceTagLinks;
?????LIST_ENTRY?StaticLinks;
}?LDR_DATA_TABLE_ENTRY,?*PLDR_DATA_TABLE_ENTRY;
(在0x028處獲取 DllName的名稱字符串) @Windows XP Professional Service Pack 3 (x86) (5.1, Build 2600)
nt!_LDR_DATA_TABLE_ENTRY ……
……+0x010 DllBase : Ptr32 +0x014 EntryPoint : Ptr32 +0x018 SizeOfImage : Uint4BFullDllName : _UNICODE_STRING+0x01c Length : Uint2B+0x01e MaximumLength : Uint2B+0x020 字符串地址Buffer : Ptr32 BaseDllName : _UNICODE_STRING+0x024 Length : Uint2B+0x026 MaximumLength : Uint2B+0x028 字符串地址Buffer : Ptr32 ……
…… ?
?? 可以根據上述的定義,畫出一個直觀尋址的示意圖:
在定位到PEB_LDR_DATA?后,我們就可以根據之前介紹LDR_DATA_TABLE_ENTRY的結構,來定位每個模塊的基址了。PEB_LDR_DATA地址知道了,那么,怎么根據它去找到LDR_DATA_TABLE_ENTRY呢?
前面提到過,PEB_LDR_DATA?中的三個LIST_ENTRY全部是雙向鏈表結構,它的兩個成員Flink,Blink都指向LDR_DATA_TABLE_ENTRY,那么,在真實的系統環境中,PEB_LDR_DATA?中的每一個LIST_ENTRY填充的是什么呢?與LDR_DATA_TABLE_ENTRY的關系又是怎么連接起來的呢?沐浴更衣,亮出神器windbg,一探究竟:
追本溯源,首先查看PEB結構:
0:000>?!peb
PEB?at?7ffd4000
????Ldr.InInitializationOrderModuleList:??????00202988?.?00202cb0
????Ldr.InLoadOrderModuleList:????????????002028e8?.?00202db8
??Ldr.InMemoryOrderModuleList:?????????002028f0?.?00202dc0
于此同時也獲得各個加載模塊的基地址:
????????400000?3db8972c?Oct?25?08:58:20?2002?C:\Users\JoRrYChEn\Desktop\test.exe
???????77130000?4ec49caf?Nov?17?13:33:35?2011?C:\Windows\SYSTEM32\ntdll.dll
???????76e00000?4e21132a?J1ul?16?12:27:22?2011?C:\Windows\system32\kernel32.dll
???????751b0000?4e21132b?Jul?16?12:27:23?2011?C:\Windows\system32\KERNELBASE.dll???
我們看到,Ldr中有的三個List,根據MSDN規定LIST_ENTRY的結構,其中有兩個成員:Flink和Blink,他們分別指向著一個LDR_DATA_TABLE_ENTRY?結構。各個LIST_ENTRY的第一個填充就是鏈表的Flink,指向下一個鏈表節點,第二個填充的是鏈表的Blink,指向前一個鏈表節點。那么根據windbg提供的PEB_LDR_DATA?中的三個LIST_ENTRY的Flink,來看看其填充的各項值:
InLoadOrderModuleList:?Flink?:00202988?.?Blink:00202cb0
0:000>?dd?002028e8
002028e8??00202978?7720788c?00202980?77207894
002028f8??00000000?00000000?00400000?0040720b
0:000>?dd?00202978
00202978??00202ca0?002028e8?00202ca8?002028f0
00202988??00202dc8?7720789c?77130000?00000000
0:000>?dd?00202ca0
00202ca0??00202db8?00202978?00202dc0?00202980
00202cb0??7720789c?00202dc8?76e00000?76e51065
0:000>?dd?00202db8
00202db8??7720788c?00202ca0?77207894?00202ca8
00202dc8??00202cb0?00202988?751b0000?751b7afd
0:000>?dd?7720788c
7720788c??002028e8?00202db8?002028f0?00202dc0
7720789c??00202988?00202cb0?00000000?00000000
?????我們看到002028e8所在的模塊地址是00400000,是原始模塊加載基地址,它的Flink是00202978?,所在的模塊地址77130000?,是ntdll.dll的加載基地址,它的Flink是00202ca0?,所在的模塊地址是76e00000?,是kernel32.dll的加載基地址,它的Flink是00202db8?,所在模塊地址是751b0000,是KERNELBASE.dll的加載基地址。之后,便是一個循環,Flink又指向了002028e8。不難發現,這是個循環鏈表!
????同理,我們依此遍歷InMemoryOrderModuleList?和?InInitializationOrderModuleList兩條鏈:
Ldr.InMemoryOrderModuleList:
0:000>?dd?002028f0
002028f0??00202980?77207894?00000000?00000000
00202900??00400000?0040720b?00008010?00460044
0:000>?dd?00202980
00202980??00202ca8?002028f0?00202dc8?7720789c
00202990??77130000?00000000?0013c000?003c003a
0:000>?dd?00202ca8
00202ca8??00202dc0?00202980?7720789c?00202dc8
00202cb8??76e00000?76e51065?000d4000?00420040
0:000>?dd??00202dc0
00202dc0??77207894?00202ca8?00202cb0?00202988
00202dd0??751b0000?751b7afd?0004a000?00460044
0:000>?dd?77207894
77207894??002028f0?00202dc0?00202988?00202cb0
772078a4??00000000?00000000?00000000?00000000
InInitializationOrderModuleList:
0:000>dd?00202988
00202988??00202dc8?7720789c?77130000?00000000
0:000>?dd?00202dc8
00202dc8??00202cb0?00202988?751b0000?751b7afd
0:000>?dd?00202cb0
00202cb0??7720789c?00202dc8?76e00000?76e51065
0:000>?dd?7720789c
7720789c??00202988?00202cb0?00000000?00000000
????同InLoadOrderModuleList?一樣,最終,這也是雙向的循環鏈表結構。根據我們遍歷出來的結構,我們可以畫出如下的示意圖(test文件為例):
??不難發現,PEB_LDR_DATA給出的是三個List(InLoadOrderModuleList,InMemoryOrderModuleLis以及InInitializationOrderModuleList)模塊加載首個基址,也可以看成是整個List雙向鏈表的表頭,然后通過這個雙向循環鏈表的不斷的遍歷,來依此獲取不同List加載的順序。同時系統為每一個DLL維護的一個LDR_DATA_TABLE_ENTRY,該結構中,每一個DLL在不同List中,不但包含著著前繼加載模塊或者后繼加載模塊,還有著非常詳細的各個加載模塊的信息,包括加載基址和DLL名稱等等。
???這樣,我們可以關于之前根據PEB_LDR_DATA?后找到LDR_DATA_TABLE_ENTRY就非常的顯而易見了:
??通過不斷地遍歷,讀取其中的各項結構,至此,我們可以得出每一個List的在測試系統(win7,目標EXE文件?test)下加載的依此順序:
??
??遍歷?InInitializationOrderModuleList?得到次序:
??
????????NTDLL.DLL->?KERNELBASE.DLL->KERNEK32.DLL->NULL
????????遍歷?InLoadOrderModuleList?得到次序
????????TEST.EXE->NTDLL.DLL->?KERNEK32.DLL->KERNELBASE.DLL->NULL
??遍歷?InMemoryOrderModuleList?得到次序
??
????????TEST.EXE->NTDLL.DLL->?KERNEK32.DLL->KERNELBASE.DLL->NULL
??2.PEB_LDR_DATA鏈表的應用
??我們都知道,以前在xp下寫shellcode,最通用獲得kernel32基址代碼:
???
???mov?ebx,?fs:[?0x30?]???????//?獲得PEB
???mov?ebx,?[?ebx?+?0x0C?]????//?獲得PEB_LDR_DATA
???mov?ebx,?[?ebx?+?0x1C?]????//?InitializationOrderModuleList?第一項
???mov?ebx,?[?ebx?]???????????//?InitializationOrderModuleList?第二項
???mov?ebx,?[?ebx?+?0x8?]????//?獲得完整的路徑地址
???
??但是這段代碼放到win7就不通用,?為什么?前三句沒問題,首先是獲得PEB基地址,然后在偏移0X0C處獲得LDR,然后再取得LDR的偏移?0x1C處,獲得InInitializationOrderModuleList加載的下一個模塊基址,恰恰問題是出在第5句上,mov?ebx,[ebx],此時,ebx中的值按照前面所述,應該是第二個LDR_DATA_TABLE_ENTRY,可是根據之前的觀察,在win7的環境之下,InInitializationOrderModuleList第二個LDR_DATA_TABLE_ENTRY不是kernel32了,而是,KERNELBASE了(見圖2),kernel32排到了第三個了。所以,若要獲取win7下kernel32的基址,只需將之前的代碼略加修改:
??
???mov?ebx,?fs:[?0x30?]???????//?獲得PEB
???mov?ebx,?[?ebx?+?0x0C?]????//?獲得PEB_LDR_DATA
???mov?ebx,?[?ebx?+?0x1C?]????//?InitializationOrderModuleList第一項
???mov?ebx,?[?ebx?]???????????//?InitializationOrderModuleList第二項
???mov?ebx,?[?ebx?]???????????//?InitializationOrderModuleList第三項
???mov?ebx,?[?ebx?+?0x8?]????//?獲得完整的路徑地址
??
當然,若要想寫一個通用的shellcode,就必須先要判斷系統版本號再根據情況來確定是取鏈的第二項還是第三項。除開現有的字符串查找方法,有木有一個更為快捷的方法呢?我們試著來探討一下:
前輩們的經驗告訴我們,winXp下,InitializationOrderModuleList?加載的DLL模塊順序是固定的,通過遍歷其鏈,kernel32.dll必然是在第二位。但是在win7條件下,這個xp下固定的“第二位”就已經換成了KERNELBASE.DLL了。那么另外兩條InMemoryOrderModuleList和?InLoadOrderModuleList是不是模塊加載順序也變了呢?是不是也能像類似于查找InitializationOrderModuleList順序鏈的方式找到通用的加載順序呢?
帶著疑問,寫一個遍歷PEB.LDR.InLoadOrderModuleList程序驗證一下:
Win2000+Sp4下:
winxp+sp3
win7
??結果不難發現,InLoadOrderModuleList在所有上述系統中,按照順序:第一個是EXE模塊本身的ImageBase,第二個是NTDLL.DLL,第三個是KERNEL32.DLL。這樣,KERNEL32.DLL的順序是不是又固定了呢?
??
??mov?ebx,?fs:[?0x30?]???????//?獲得PEB
??mov?ebx,?[?ebx?+?0x0C?]????//?獲得PEB_LDR_DATA
??mov?ebx,?[?ebx?+?0x0C?]????//?InLoadOrderModuleList第一項
??mov?ebx,?[?ebx?]???????????//?InLoadOrderModuleList第二項
??mov?ebx,?[?ebx?]???????????//?InLoadOrderModuleList第三項
??mov?ebx,?[?ebx?+?0x18?]????//?獲得完整的路徑地址,此時偏移0x18
??
??同理,InMemoryOrderModuleList也可以發現類似的規律。
??
??寫完這篇手記之后,越覺得不對勁,這么淺顯的方法為什么沒有被拿來通用,google了一下,原來,早在09年,就有某牛總結出來了:
??http://blog.harmonysecurity.com/2009/06/retrieving-kernel32s-base-address.html
文章用到的是InMemoryOrderModuleList鏈,至于遍歷這些鏈為什么沒有成為通用的方法,很顯然,作者在后面加上了如是說明:
Their?appears?to?be?some?cases?on?Windows?2000?whereby?the?above?method?will?not?yield?the?correct?result.
提到了在2000平臺下,某些例子可能會得到錯誤的結果,最后提出來查找hash函數名的方法,這個和以前可以某些病毒在IAT里面找從kernel32.dll引入的函數,向上找“MZ”頭方法很類似,同樣也是依靠遍歷LDR鏈,只不過比較的就是DllName了。詳細的代碼請參考上文連接:D。
注釋:
http://undocumented.ntinternals.net/UserMode/Undocumented%20Functions/NT%20Objects/Thread/TEB.html?
注釋②
http://undocumented.ntinternals.net/UserMode/Undocumented%20Functions/NT%20Objects/Process/PEB.html?
轉載于:https://www.cnblogs.com/bokernb/p/6404795.html
總結
- 上一篇: 搜狗浏览器也可以直接安装Chrome插件
- 下一篇: 【bzoj4195】[Noi2015]程