根据我们上次所讲,其大概流程是差不多理清了,但是还有具体的一些细节和一个Tls的大头还没阐述,先把LoadDll的具体细节讲完,这里推荐大家去看毛德操先生的著作《内核情景分析》,这本书对ReactOS(一种也是基于NT的内核,但是不同于Windows,它是完全开源的)的解析入木三分,我读完这个DLL的装载后大有所获,所以强烈推荐这本好书!!!
好了,接下来就是我们的正题了先复习下调用流程。
LdrInitializeThunk > LdrPEStartup() > LdrFixupImports()> LdrpProcessImportDirectory() > LdrpProcessImportDirectoryEntry()> LdrGetExportByOrdinal() > LdrFixupForward() 首先复习下LdrFixupForward的内容
LdrFixupForward(PCHAR ForwardName)
{
CHAR NameBuffer[128];
UNICODE_STRING DllName;
NTSTATUS Status;
PCHAR p;
PLDR_DATA_TABLE_ENTRY Module;
PVOID BaseAddress;
strcpy(NameBuffer, ForwardName);
p = strchr(NameBuffer, '.');//将转向DLL的dll模块名和转向函数分割
if (p != NULL)
{
*p = 0;
DPRINT("Dll: %s Function: %sn", NameBuffer, p+1);
RtlCreateUnicodeStringFromAsciiz (&DllName,
NameBuffer);
Status = LdrFindEntryForName (&DllName, &Module, FALSE);//先判断有无载入,假如加载了,就可以直接找了
/* FIXME:
* The caller (or the image) is responsible for loading of the dll, where the function is forwarded.
*/
if (!NT_SUCCESS(Status))//若未加载转向的DLL
{
Status = LdrLoadDll(NULL,//加载该DLL,及初始化其依赖的DLL
NULL,
&DllName,
&BaseAddress);
if (NT_SUCCESS(Status))
{
Status = LdrFindEntryForName (&DllName, &Module, FALSE);
}
}
RtlFreeUnicodeString (&DllName);
if (!NT_SUCCESS(Status))
{
DPRINT1("LdrFixupForward: failed to load %sn", NameBuffer);
return NULL;
}
DPRINT("BaseAddress: %pn", Module->DllBase);
return LdrGetExportByName(Module->DllBase, (PUCHAR)(p+1), -1);//递归调用依据函数名调用的过程
}
return NULL;
}
可以看到LdrFixupForward->LdrLoadDll 这才是我们今天的主角
/*
在LdrFixupForward里调用语句Status = LdrLoadDll(NULL,NULL,&DllName,&BaseAddress);
*/
NTSTATUS NTAPI
LdrLoadDll (IN PWSTR SearchPath OPTIONAL,
IN PULONG LoadFlags OPTIONAL,
IN PUNICODE_STRING Name,
OUT PVOID *BaseAddress /* also known as HMODULE*, and PHANDLE 'DllHandle' */)
{
NTSTATUS Status;
PLDR_DATA_TABLE_ENTRY Module;
PPEB Peb = NtCurrentPeb();//获得peb指针
TRACE_LDR("LdrLoadDll, loading %wZ%s%Sn",
Name,
SearchPath ? L" from " : L"",
SearchPath ? SearchPath : L"");
Status = LdrpLoadModule(SearchPath, LoadFlags ? *LoadFlags : 0, Name, &Module, BaseAddress);//下发给LdrpLoadModule
if (NT_SUCCESS(Status) &&//若成功加载,并且不是映射为单纯的数据文件,因为我们这里是DLL文件,所以肯定进这个控制流
(!LoadFlags || 0 == (*LoadFlags & LOAD_LIBRARY_AS_DATAFILE)))
{
if (!(Module->Flags & LDRP_PROCESS_ATTACH_CALLED))//模块并不是进程加载时被调用
{
RtlEnterCriticalSection(Peb->LoaderLock);
Status = LdrpAttachProcess();
RtlLeaveCriticalSection(Peb->LoaderLock);
}
}
if ((!Module) && (NT_SUCCESS(Status)))//若模块
return Status;
*BaseAddress = NT_SUCCESS(Status) ? Module->DllBase : NULL;//返填充
return Status;
}
/*
LdrpLoadModule //通过该函数装载模块和其依赖DLL
LdrpAttachProcess //调用该函数的初始化函数
*/
可以看到,LdrLoadDll只是起到一个类似于stub的功能,主要实现下发给了LdrpLoadModule用以加载该模块及其依赖的DLL。而对于LdrpAttachProcess而言,则是调用这些DLL的初始化函数(dwReason == DLL_PROCESS_ATTACH),这个之后再讲,先把加载捋清了。
LdrFixupForward->LdrLoadDll->LdrpLoadModule
static NTSTATUS
LdrpLoadModule(IN PWSTR SearchPath OPTIONAL,
IN ULONG LoadFlags,
IN PUNICODE_STRING Name, //模块名
PLDR_DATA_TABLE_ENTRY *Module,//模块信息
PVOID *BaseAddress OPTIONAL)
{
UNICODE_STRING AdjustedName;
UNICODE_STRING FullDosName;
NTSTATUS Status;
PLDR_DATA_TABLE_ENTRY tmpModule;
HANDLE SectionHandle;
SIZE_T ViewSize;
PVOID ImageBase;
PIMAGE_NT_HEADERS NtHeaders;
BOOLEAN MappedAsDataFile;
PVOID ArbitraryUserPointer;
if (Module == NULL)
{
Module = &tmpModule;
}
/* adjust the full dll name */
LdrAdjustDllName(&AdjustedName, Name, FALSE);//调节全路径至BaseDllName
DPRINT(