参考:

http://blog.csdn.net/hw_henry2008/article/details/6568255

Windows 的 DLL 装入(除 ntdll.dll 外)和连接是通过 ntdll.dll 中的一个函数LdrInitializeThunk()实现的.

在进入这个函数之前,目标 EXE 映像已经被映射到当前进程的用户空间,系统 DLL ntdll.dll 的映像也已经被映射, 但是并没有在 EXE 映像与 ntdll.dll 映像之间建立连接(实际上EXE 映像未必就直接调用 ntdll.dll 中的函数)。、

LdrInitializeThunk()是 ntdll.dll 中不经连接就可进入的函数,实质上就是 ntdll.dll 的入口。除 ntdll.dll 以外,别的 DLL 都还没有被装入(映射)。此外,当前进程(除内核中的“进程控制块”EPROCESS 等数据结构外)在用户空间已经有了一个“进程环境块”PEB,以及该进程的第一个“线程环境块”TEB。这就是进入 __true_LdrInitializeThunk()前的“当前形势”。

VOID STDCALL
__true_LdrInitializeThunk (ULONG Unknown1, ULONG Unknown2,
ULONG Unknown3, ULONG Unknown4)
{
. . . . . . DPRINT("LdrInitializeThunk()/n");
if (NtCurrentPeb()->Ldr == NULL || NtCurrentPeb()->Ldr->Initialized == FALSE)
{
Peb = (PPEB)(PEB_BASE); //进程环境块
DPRINT("Peb %x/n", Peb);
ImageBase = Peb->ImageBaseAddress; //EXE 映像在用户空间的起点
. . . .
/* Initialize NLS data * //语言本地化有关
RtlInitNlsTables (Peb->AnsiCodePageData, Peb->OemCodePageData,
Peb->UnicodeCaseTableData, &NlsTable);
RtlResetRtlTranslations (&NlsTable); NTHeaders = (PIMAGE_NT_HEADERS)(ImageBase + PEDosHeader->e_lfanew);
. . . . . .
 /* create process heap */
//创建一个堆、 及其第一个区块,其初始的大小来自映像头部的建议值,其中 SizeOfHeapReserve 是估
计的最大值,SizeOfHeapCommit是初始值,这是在编译/连接时确定的。
RtlInitializeHeapManager();
Peb->ProcessHeap = RtlCreateHeap(HEAP_GROWABLE, NULL,
NTHeaders->OptionalHeader.SizeOfHeapReserve,
NTHeaders->OptionalHeader.SizeOfHeapCommit,
NULL, NULL);
/* create loader information */
//PEB 中的 ProcessHeap 字段指向本进程用户空间可动态分配的内存区块“堆”
Peb->Ldr = (PPEB_LDR_DATA)RtlAllocateHeap (Peb->ProcessHeap,
0,
sizeof(PEB_LDR_DATA));
. . . . . .

 /*    PEB 中的另一个字段 Ldr是个 PEB_LDR_DATA 结构指针,所指向的数据结构用来为本进程维持三个“模块”
队列、即 InLoadOrderModuleList、InMemoryOrderModuleList、InInitializationOrderModuleList。
所谓“模块”就是 PE 格式的可执行映像,包括 EXE映像和 DLL 映像
两个模块队列的不同之处在于排列的次序,一个是按装入的先后,一个是按装入的位置(实际上目前ReactOS
的代码中并未使用这个队列)。
每当为本进程装入一个模块、即.exe 映像或 DLL 映像时,就要为其分配/创建一个LDR_MODULE 数据结构,
并将其挂入 InLoadOrderModuleList
。然后,完成对这个模块的动态连接以后,就把它挂入
InInitializationOrderModuleList 队列.LDR_MODULE 数据结构中有三个队列头,因而可以同时挂在三个队列
中。
Peb->Ldr->Length = sizeof(PEB_LDR_DATA);
Peb->Ldr->Initialized = FALSE;
Peb->Ldr->SsHandle = NULL;
InitializeListHead(&Peb->Ldr->InLoadOrderModuleList);
InitializeListHead(&Peb->Ldr->InMemoryOrderModuleList);
InitializeListHead(&Peb->Ldr->InInitializationOrderModuleList); . . . . . .
/* add entry for ntdll */
NtModule = (PLDR_MODULE)RtlAllocateHeap (Peb->ProcessHeap,
0,
sizeof(LDR_MODULE));
. . . . . .
InsertTailList(&Peb->Ldr->InLoadOrderModuleList,
&NtModule->InLoadOrderModuleList);
InsertTailList(&Peb->Ldr->InInitializationOrderModuleList,
&NtModule->InInitializationOrderModuleList);
. . . . . .
/* add entry for executable (becomes first list entry) */
ExeModule = (PLDR_MODULE)RtlAllocateHeap (Peb->ProcessHeap,
0,
sizeof(LDR_MODULE));
. . . . . .
InsertHeadList(&Peb->Ldr->InLoadOrderModuleList,
&ExeModule->InLoadOrderModuleList);
. . . . . .
 /*当 CPU从 LdrPEStartup()返回时,EXE 对象需要直接或间接引入的所有 DLL 均已映射到用户空间并已完成连
接,对 EXE 模块的“启动” 、即初始化也已完成

注意在调用 LdrPEStartup()时的参数 ImageBase 是目标 EXE 映像在用户空间的位置
EntryPoint = LdrPEStartup((PVOID)ImageBase, NULL, NULL, NULL);
. . . . . .
}

 /* attach the thread */
RtlEnterCriticalSection(NtCurrentPeb()->LoaderLock);
//目的是调用各个 DLL 的初始化过程,以及对 TLS、即“线程本地存储(Thread Local Storage)”的初始化
//TLS:有时候又确实需要让每个线程都对于同一个全局变量有一份自己的拷贝,TLS就是为此而设的
LdrpAttachThread();
RtlLeaveCriticalSection(NtCurrentPeb()->LoaderLock);
}



//注意在调用 LdrPEStartup()时的参数 ImageBase 是目标 EXE 映像在用户空间的位置
PEPFUNC LdrPEStartup (PVOID ImageBase, HANDLE SectionHandle,
PLDR_MODULE* Module, PWSTR FullDosName)
{
//PE 映像的 NtHeader 中有个指针,指向一个 OptionalHeader。说是“Optional”,实际上却是关键性的。在
//OptionalHeader中有个字段 ImageBase,是具体映像建议、或者说希望被装入的地址
. . . . . .
DosHeader = (PIMAGE_DOS_HEADER) ImageBase;
NTHeaders = (PIMAGE_NT_HEADERS) (ImageBase + DosHeader->e_lfanew); /*
* If the base address is different from the
* one the DLL is actually loaded, perform any
* relocation.
*/
if (ImageBase != (PVOID) NTHeaders->OptionalHeader.ImageBase)
{
DPRINT("LDR: Performing relocations/n");
//ImageBase 是目标 EXE 映像在用户空间的位置
Status = LdrPerformRelocations(NTHeaders, ImageBase);
. . . . . .
} if (Module != NULL)
{
*Module = LdrAddModuleEntry(ImageBase, NTHeaders, FullDosName);
(*Module)->SectionHandle = SectionHandle;
}
else
{
Module = &tmpModule;
Status = LdrFindEntryForAddress(ImageBase, Module);
. . . . . .
} . . . . . . /*
* If the DLL's imports symbols from other
* modules, fixup the imported calls entry points.
*/
//它所处理的就是当前模块所需DLL模块的装入(如果尚未装入的话)和连接。如前所述,这个函数递归地施行于所有的模块,直至最底层的“叶节点”ntdll.dll为止。
DPRINT("About to fixup imports/n");
Status = LdrFixupImports(NULL, *Module);
if (!NT_SUCCESS(Status))
{
DPRINT1("LdrFixupImports() failed for %wZ/n", &(*Module)->BaseDllName);
return NULL;
}
DPRINT("Fixup done/n"); . . . . . .
Status = LdrpInitializeTlsForProccess();
. . . . . . /*
* Compute the DLL's entry point's address.
*/
. . . . . .
if (NTHeaders->OptionalHeader.AddressOfEntryPoint != )
{
EntryPoint = (PEPFUNC) (ImageBase + NTHeaders->OptionalHeader.AddressOfEntryPoint);
}
DPRINT("LdrPEStartup() = %x/n",EntryPoint);
return EntryPoint;
}
//调用关系[__true_LdrInitializeThunk > LdrPEStartup() > LdrPerformRelocations()]

typedef struct _IMAGE_DATA_DIRECTORY
{
DWORD VirtualAddress;
DWORD Size;
} IMAGE_DATA_DIRECTORY,*PIMAGE_DATA_DIRECTORY; //每个 IMAGE_BASE_RELOCATION 数据结构代表着一个“重定位块” ,每个重定位块的(容器)大小是两个页面(8KB),而 SizeOfBlock 则说明具体重定位块的实际大小。这实际的大小中包括了这 IMAGE_BASE_RELOCATION数据结构本身。
typedef struct _IMAGE_BASE_RELOCATION
{
DWORD VirtualAddress;
DWORD SizeOfBlock;
} IMAGE_BASE_RELOCATION,*PIMAGE_BASE_RELOCATION; static NTSTATUS
LdrPerformRelocations(PIMAGE_NT_HEADERS NTHeaders, PVOID ImageBase)
{
. . . . . .
//PE 映像的 OptionalHeader 中有个大小为 16 的数组 DataDirectory[],其元素都是“数据目录” 、即IMAGE_DATA_DIRECTORY 数据结构:其中之一(下标为 5)就是“重定位目录” ,这是一个IMAGE_BASE_RELOCATION结构数组。 RelocationDDir =&NTHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
. . . . . . ProtectSize = PAGE_SIZE;
//所谓重定位,就是计算出实际装入地址与建议装入地址间的位移 Delta,然后调整每个重定位块中的每一个重定位项、即指针,具体就是在指针上加 Delta
Delta = (ULONG_PTR)ImageBase - NTHeaders->OptionalHeader.ImageBase;
//IMAGE_BASE_RELOCATION结构数组
RelocationDir = (PIMAGE_BASE_RELOCATION)((ULONG_PTR)ImageBase + RelocationDDir->VirtualAddress);
RelocationEnd = (PIMAGE_BASE_RELOCATION)((ULONG_PTR)ImageBase + RelocationDDir->VirtualAddress +
RelocationDDir->Size); while (RelocationDir < RelocationEnd && RelocationDir->SizeOfBlock > )
{
Count = (RelocationDir->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(USHORT);
Page = ImageBase + RelocationDir->VirtualAddress;
TypeOffset = (PUSHORT)(RelocationDir + ); /* Unprotect the page(s) we're about to relocate. */
ProtectPage = Page;
Status = NtProtectVirtualMemory(NtCurrentProcess(), &ProtectPage,
&ProtectSize, PAGE_READWRITE, &OldProtect);
. . . . . . if (RelocationDir->VirtualAddress + PAGE_SIZE < NTHeaders->OptionalHeader.SizeOfImage)
{
ProtectPage2 = ProtectPage + PAGE_SIZE;
Status = NtProtectVirtualMemory(NtCurrentProcess(), &ProtectPage2,
&ProtectSize, PAGE_READWRITE, &OldProtect2);
. . . . . .
}
else
{
ProtectPage2 = NULL;
}
//具体的指针调整是由 LdrProcessRelocationBlock() 完成的,此前和此后的NtProtectVirtualMemory()只是为了先去除这些指针所在页面的写保护,而事后加以恢复。
RelocationDir = LdrProcessRelocationBlock(Page, Count, TypeOffset, Delta);
. . . . . . /* Restore old page protection. */
NtProtectVirtualMemory(NtCurrentProcess(),&ProtectPage,
&ProtectSize, OldProtect, &OldProtect); if (ProtectPage2 != NULL)
{
NtProtectVirtualMemory(NtCurrentProcess(), &ProtectPage2,
&ProtectSize, OldProtect2, &OldProtect2);
}
} return STATUS_SUCCESS;
}

阅读 LdrInitializeThunk的更多相关文章

  1. 【原】FMDB源码阅读(三)

    [原]FMDB源码阅读(三) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 FMDB比较优秀的地方就在于对多线程的处理.所以这一篇主要是研究FMDB的多线程处理的实现.而 ...

  2. 【原】FMDB源码阅读(二)

    [原]FMDB源码阅读(二) 本文转载请注明出处 -- polobymulberry-博客园 1. 前言 上一篇只是简单地过了一下FMDB一个简单例子的基本流程,并没有涉及到FMDB的所有方方面面,比 ...

  3. 【原】FMDB源码阅读(一)

    [原]FMDB源码阅读(一) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 说实话,之前的SDWebImage和AFNetworking这两个组件我还是使用过的,但是对于 ...

  4. 【原】AFNetworking源码阅读(六)

    [原]AFNetworking源码阅读(六) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 这一篇的想讲的,一个就是分析一下AFSecurityPolicy文件,看看AF ...

  5. 【原】AFNetworking源码阅读(五)

    [原]AFNetworking源码阅读(五) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 上一篇中提及到了Multipart Request的构建方法- [AFHTTP ...

  6. 【原】AFNetworking源码阅读(四)

    [原]AFNetworking源码阅读(四) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 上一篇还遗留了很多问题,包括AFURLSessionManagerTaskDe ...

  7. 【原】AFNetworking源码阅读(三)

    [原]AFNetworking源码阅读(三) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 上一篇的话,主要是讲了如何通过构建一个request来生成一个data tas ...

  8. 【原】AFNetworking源码阅读(二)

    [原]AFNetworking源码阅读(二) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 上一篇中我们在iOS Example代码中提到了AFHTTPSessionMa ...

  9. 【原】AFNetworking源码阅读(一)

    [原]AFNetworking源码阅读(一) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 AFNetworking版本:3.0.4 由于我平常并没有经常使用AFNetw ...

随机推荐

  1. 怎样正确设置remote_addr和x_forwarded_for

    怎样正确设置remote_addr和x_forwarded_for 2013-04-20 做网站时经常会用到remote_addr和x_forwarded_for这两个头信息来获取客户端的IP,然而当 ...

  2. 网页 CSS样式表

    昨天,我主要是对CSS样式表进行了一下复习. CSS样式表主要有三类:内联样式表.内嵌样式表.外部样式表,我们平时一般使用第二种样式表. 选择器主要包括:标签选择器.class选择器.ID选择器.复合 ...

  3. 查看Android下生成的.db数据库

    1.在cmd中找到sdk中的platform-tools文件夹. 2.输入adb shell命令. 3.再输入sqlite3 /data/data/com.svs.db/databases/svs.d ...

  4. linux zeromq

    本人在centos下安装zeromq 1.下载最新版的zeromq http://download.zeromq.org/ 2 解压 tar -xvf zeromq-3.1.0-beta.tar.gz ...

  5. position:fixed 居中问题

    设置Css属性position:fixed后如何使这个盒子居中呢?其实也很简单: 就是需要设置给这个div盒子设置属性: left:0; right:0; margin:0 auto; ******* ...

  6. List循环与Map循环的总结

    做了一下list和map的总结,没有什么技术含量,就全当复习了一下api. 测试环境是在junit4下,如果没有自己写一个main方法也是一样的. 首先是List的三种循环: @Test public ...

  7. dialog弹出,点击back按键无法返回问题解决

    今天阅读队友代码,调试代码中,发现对话框弹出点击back按键无法返回问题解决. 代码如下: /** * 单个按钮没有标题的弹框 * * @param context * @param content内 ...

  8. .net core 系列

    1..net core 验证码 2..net core 导出excel 3..net core 上传文件 4..net core 时间戳转换 5..net core 读取配置文件 6..net cor ...

  9. linux和win7设置静态ip

    ubuntu 静态ip设置 检查网络ifconfig (不是ipconfig)必须有2个地址一个回送地址:127.0.0.1一个实际地址:192.168.3.58 sudo vim /etc/netw ...

  10. Boxes in a Line(移动盒子)

      You have n boxes in a line on the table numbered 1 . . . n from left to right. Your task is to sim ...