利用链如下:
fs寄存器 -> TEB -> PEB -> PEB_LDR_DATA -> LIST_ENTRY -> LDR_DATA_TABLE_ENTRY -> BaseOfDll
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIyZuBnL4gzM0EjM1ADMzATNwkTMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
windbg 用r命令查看fs寄存器的值
根据fs段寄存器,我们找到它的GDTR,如下
找到了GDT的索引为6
对应的值gdt[6] = ffc093df`f0000001,索引到的基址为0xffdff000
也可以用dg命令
看看TEB结构
kd> dt _teb
ntdll!_TEB
+0x000 NtTib : _NT_TIB
+0x01c EnvironmentPointer : Ptr32 Void
+0x020 ClientId : _CLIENT_ID
+0x028 ActiveRpcHandle : Ptr32 Void
+0x02c ThreadLocalStoragePointer : Ptr32 Void
+0x030 ProcessEnvironmentBlock : Ptr32 _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 ActivationContextStack : _ACTIVATION_CONTEXT_STACK
+0x1bc SpareBytes1 : [24] UChar
+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] Uint2B
+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 HardErrorsAreDisabled : Uint4B
+0xf2c Instrumentation : [16] Ptr32 Void
+0xf6c WinSockData : Ptr32 Void
+0xf70 GdiBatchCount : Uint4B
+0xf74 InDbgPrint : UChar
+0xf75 FreeStackOnTermination : UChar
+0xf76 HasFiberData : UChar
+0xf77 IdealProcessor : UChar
+0xf78 Spare3 : Uint4B
+0xf7c ReservedForPerf : Ptr32 Void
+0xf80 ReservedForOle : Ptr32 Void
+0xf84 WaitingOnLoaderLock : Uint4B
+0xf88 Wx86Thread : _Wx86ThreadState
+0xf94 TlsExpansionSlots : Ptr32 Ptr32 Void
+0xf98 ImpersonationLocale : 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 SafeThunkCall : UChar
+0xfb5 BooleanSpare : [3] UChar
TEB offset为0的地方是一个TIB,结构如下
kd> dt _NT_TIB
ntdll!_NT_TIB
+0x000 ExceptionList : Ptr32 _EXCEPTION_REGISTRATION_RECORD
+0x004 StackBase : Ptr32 Void
+0x008 StackLimit : Ptr32 Void
+0x00c SubSystemTib : Ptr32 Void
+0x010 FiberData : Ptr32 Void
+0x010 Version : Uint4B
+0x014 ArbitraryUserPointer : Ptr32 Void
+0x018 Self : Ptr32 _NT_TIB
TIB offset为0的地方 ExceptionList 是一个异常注册链的表头
StackBase 是该线程的栈地址
StackLimit 是该线程栈大小
Self是指向TEB的指针所以 fs:[0x18]也就是拿到了TEB的地址
继续来看TEB,
EnvironmentPointer是一个环境表指针,main函数的第三个参数就是环境表的地址,
+0x020 ClientId 这个结构我们也看一下,UniqueProcess 进程的PID,UniqueThread是该线程的TID
kd> dt _CLIENT_ID
ntdll!_CLIENT_ID
+0x000 UniqueProcess : Ptr32 Void
+0x004 UniqueThread : Ptr32 Void
+0x030 ProcessEnvironmentBlock 所说的fs:[0x30]也就是取PEB的地址
+0x034 LastErrorValue 如果出现错误,最后的错误返回值是多少,GetLastError就是从这取得值,不同的错误返回不同的错误码,如果为0,说明程序运行正常
Windbg可以通过 !gle(get last error)显示最后的错误值,跟GetLastError等同的效果!!!
例如:
0:000> !gle
LastErrorValue: (Win32) 0 (0) - The operation completed successfully.
LastStatusValue: (NTSTATUS) 0 - STATUS_WAIT_0
接着我们到PEB里继续探索,PEB结构如下:
kd> 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
+0x010 ProcessParameters : Ptr32 _RTL_USER_PROCESS_PARAMETERS
+0x014 SubSystemData : Ptr32 Void
+0x018 ProcessHeap : Ptr32 Void
+0x01c FastPebLock : Ptr32 _RTL_CRITICAL_SECTION
+0x020 FastPebLockRoutine : Ptr32 Void
+0x024 FastPebUnlockRoutine : Ptr32 Void
+0x028 EnvironmentUpdateCount : Uint4B
+0x02c KernelCallbackTable : Ptr32 Void
+0x030 SystemReserved : [1] Uint4B
+0x034 AtlThunkSListPtr32 : Uint4B
+0x038 FreeList : Ptr32 _PEB_FREE_BLOCK
+0x03c TlsExpansionCounter : Uint4B
+0x040 TlsBitmap : Ptr32 Void
+0x044 TlsBitmapBits : [2] Uint4B
+0x04c ReadOnlySharedMemoryBase : Ptr32 Void
+0x050 ReadOnlySharedMemoryHeap : Ptr32 Void
+0x054 ReadOnlyStaticServerData : Ptr32 Ptr32 Void
+0x058 AnsiCodePageData : Ptr32 Void
+0x05c OemCodePageData : Ptr32 Void
+0x060 UnicodeCaseTableData : Ptr32 Void
+0x064 NumberOfProcessors : Uint4B
+0x068 NtGlobalFlag : Uint4B
+0x070 CriticalSectionTimeout : _LARGE_INTEGER
+0x078 HeapSegmentReserve : Uint4B
+0x07c HeapSegmentCommit : Uint4B
+0x080 HeapDeCommitTotalFreeThreshold : Uint4B
+0x084 HeapDeCommitFreeBlockThreshold : Uint4B
+0x088 NumberOfHeaps : Uint4B
+0x08c MaximumNumberOfHeaps : Uint4B
+0x090 ProcessHeaps : Ptr32 Ptr32 Void
+0x094 GdiSharedHandleTable : Ptr32 Void
+0x098 ProcessStarterHelper : Ptr32 Void
+0x09c GdiDCAttributeList : Uint4B
+0x0a0 LoaderLock : Ptr32 Void
+0x0a4 OSMajorVersion : Uint4B
+0x0a8 OSMinorVersion : Uint4B
+0x0ac OSBuildNumber : Uint2B
+0x0ae OSCSDVersion : Uint2B
+0x0b0 OSPlatformId : Uint4B
+0x0b4 ImageSubsystem : Uint4B
+0x0b8 ImageSubsystemMajorVersion : Uint4B
+0x0bc ImageSubsystemMinorVersion : Uint4B
+0x0c0 ImageProcessAffinityMask : Uint4B
+0x0c4 GdiHandleBuffer : [34] Uint4B
+0x14c PostProcessInitRoutine : Ptr32 void
+0x150 TlsExpansionBitmap : Ptr32 Void
+0x154 TlsExpansionBitmapBits : [32] Uint4B
+0x1d4 SessionId : Uint4B
+0x1d8 AppCompatFlags : _ULARGE_INTEGER
+0x1e0 AppCompatFlagsUser : _ULARGE_INTEGER
+0x1e8 pShimData : Ptr32 Void
+0x1ec AppCompatInfo : Ptr32 Void
+0x1f0 CSDVersion : _UNICODE_STRING
+0x1f8 ActivationContextData : Ptr32 Void
+0x1fc ProcessAssemblyStorageMap : Ptr32 Void
+0x200 SystemDefaultActivationContextData : Ptr32 Void
+0x204 SystemAssemblyStorageMap : Ptr32 Void
+0x208 MinimumStackCommit : Uint4B
有个BeingDebugged成员,检测本身是否处于调试状态下
ProcessHeap && HeapSegmentReserve && HeapSegmentCommit && NumberOfHeap && MaximumNumberOfHeaps等等这几个跟堆有很大的关系
了解下Windows下创建堆的过程:
Windows创建堆会调用RtlCreateHeap(),句柄保存在ProcessHeap里,HeapSegmentCommit就是堆提交的大小(PE的可选头里也有相应的答案),HeapSegmentReserve是堆保留的大小。
+0x00c Ldr这是我们讨论的重点。看下它的结构
微软给出的PEB_LDR_DATA含义
Contains information about the loaded modules for the process.
kd> 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
后几个成员是非常重要的,含义如下
InLoadOrderModuleList; //模块加载顺序
InMemoryOrderModuleList; //模块在内存中的顺序
InInitializationOrderModuleList; //模块初始化装载顺序
值得注意的是,他们本身是一个LIST_ENTRY的结构,这是一个链表结构
kd> dt _LIST_ENTRY
ntdll!_LIST_ENTRY
+0x000 Flink : Ptr32 _LIST_ENTRY
+0x004 Blink : Ptr32 _LIST_ENTRY
显然是一个双向链表结构,并且这些指针又指向了LDR_DATA_TABLE_ENTRY 这个结构体,注意这个结构体的第四个字段也就是DLL的加载基址
typedef struct _LDR_DATA_TABLE_ENTRY
{
LIST_ENTRY InLoadOrderLinks;//模块加载顺序
LIST_ENTRY InMemoryOrderLinks;//模块在内存中的顺序
LIST_ENTRY InInitializationOrderLinks;//模块初始化装载顺序
PVOID DllBase; //DLL在内存的地址
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;
下面实战分析下:
先得到程序的peb
输入!peb可以显示一个程序的peb结构
PEB at 7ffdf000
InheritedAddressSpace: No
ReadImageFileExecOptions: No
BeingDebugged: No
ImageBaseAddress: 00400000
NtGlobalFlag: 0
NtGlobalFlag2: 0
Ldr 00251e90
Ldr.Initialized: Yes
Ldr.InInitializationOrderModuleList: 00251f28 . 00252180
Ldr.InLoadOrderModuleList: 00251ec0 . 00252170
Ldr.InMemoryOrderModuleList: 00251ec8 . 00252178
Base TimeStamp Module
400000 5ccbca25 May 03 12:57:09 2019 C:\Documents and Settings\ljq\桌面\Test_PEB.exe
7c920000 4d00f280 Dec 09 23:15:12 2010 C:\WINDOWS\system32\ntdll.dll
7c800000 53203b8a Mar 12 18:48:42 2014 C:\WINDOWS\system32\kernel32.dll
10000000 524f7ce5 Oct 05 10:43:49 2013 C:\Documents and Settings\ljq\桌面\MSVCR120D.dll
76d70000 4802bd96 Apr 14 10:12:38 2008 C:\WINDOWS\system32\Apphelp.dll
我们关注InLoadOrderModuleList这个成员,这个成员指的是模块加载顺序,找到这个值00251ec0
跳到那里,看一下:
此时指向的是一个**_LDR_DATA_TABLE_ENTRY**结构。前三项是连续的三个LIST_ENTRY结构,每个结构是8个字节,我们关注第二项成员和第四项成员
该dll的加载基址是0x400000,即是该程序的模块基址
看下一项,是EntryPoint,用LordPE看一下,果真是0x411113,ImageBase也一样
LDR_DATA_TABLE_ENTRY的下一项是SizeOfImage,值是0x1c000,与在pe分析器的结果一致
下面两个是UNICODE_STRING类型的数据,FullDllName,BaseDllName。
typedef struct _LSA_UNICODE_STRING {
USHORT Length;
USHORT MaximumLength;
PWSTR Buffer;} LSA_UNICODE_STRING, *PLSA_UNICODE_STRING, UNICODE_STRING, *PUNICODE_STRING;
前两个length我们不管,重点关注最后一个,是指向字符串的指针
去看一下,显示出了全路径信息
然后看一下BaseDllName
这次只显示模块名称。不显示全路径的信息了。
其他就没好显示的了,进入第一项成员的Flink里,即是下一个模块,这里就是真正的DLL加载模块了
进入后,第一项的Flink是下一个的LDR_DATA_TABLE_ENTRY,可以看到该模块的加载基址是0x7c920000,DllName的全路径地址是0x7c9a0048,模块名是地址是0x7c94040c
这样一直找就可以遍历所有模块了,这里就不演示了,直接上代码:
#include <stdio.h>
#include <stdlib.h>
#include "infor.h"
void InLoadOrderModuleTraverse(LDR_MODULE *pLdrModule, PPEB_LDR_DATA pPebLdrData)//按模块加载顺序遍历 这样传入的就是第一个模块
{
LDR_MODULE *pFirstModule = (LDR_MODULE *) ((char *)pPebLdrData + 0xc);//记录第一个模块地址,用于遍历链表 这里的offset为0xc是InLoadOrderModuleList的地址
printf("--------------------\n");
printf("InLoadOrderModuleTraverse:\n");
do {//在这里输出一些信息
printf("NextLdrDataTable : %8X\n", pLdrModule->InLoadOrderModuleList.Flink);
printf("DllBase : %8X\n", pLdrModule->BaseAddress);
wprintf(L"FullDllName : %ls\n", pLdrModule->FullDllName.Buffer);
wprintf(L"BaseDllName : %ls\n\n", pLdrModule->BaseDllName.Buffer);
pLdrModule = (LDR_MODULE *)pLdrModule->InLoadOrderModuleList.Flink;
} while ((LDR_MODULE *)pLdrModule != pFirstModule);
}
int main()
{
PEB *pPeb = NULL;
TEB *pTeb = NULL;
PEB_LDR_DATA *pLdrData = NULL;
_asm {
mov eax, fs:[0x18];//获取指向self的指针
mov pTeb, eax;//赋值给pTeb
}
pPeb = pTeb->ProcessEnvironmentBlock;//获取环境块的地址
pLdrData = pPeb->Ldr;//获取LDR的地址
InLoadOrderModuleTraverse((LDR_MODULE *)pLdrData->InLoadOrderModuleList.Flink, pPeb->Ldr);//传入第一个模块的地址
system("pause");
return 0;
}