参考:

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 != 0){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,然后调整每个重定位块中的每一个重定位项、即指针,具体就是在指针上加 DeltaDelta = (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 > 0){Count = (RelocationDir->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(USHORT);Page = ImageBase + RelocationDir->VirtualAddress;TypeOffset = (PUSHORT)(RelocationDir + 1);/* 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;
}

转载于:https://www.cnblogs.com/predator-wang/p/4819012.html

阅读 LdrInitializeThunk相关推荐

  1. 论文阅读工具ReadPaper

    对于搞科研的同学们来说,看论文是要经历的第一关,尤其是要读好多篇论文的时候,更是着实令人头大. 这不,最近无意中发现了个在线论文阅读网站:readpaper.com,号称「论文阅读笔记神器,硕博科研学 ...

  2. YOLOv4全文阅读(全文中文翻译)

    YOLOv4全文阅读(全文中文翻译) YOLOv4: Optimal Speed and Accuracy of Object Detection 论文链接: https://arxiv.org/pd ...

  3. 多目标跟踪:CVPR2019论文阅读

    多目标跟踪:CVPR2019论文阅读 Robust Multi-Modality Multi-Object Tracking 论文链接:https://arxiv.org/abs/1909.03850 ...

  4. 快速人体姿态估计:CVPR2019论文阅读

    快速人体姿态估计:CVPR2019论文阅读 Fast Human Pose Estimation 论文链接: http://openaccess.thecvf.com/content_CVPR_201 ...

  5. Action4D:人群和杂物中的在线动作识别:CVPR209论文阅读

    Action4D:人群和杂物中的在线动作识别:CVPR209论文阅读 Action4D: Online Action Recognition in the Crowd and Clutter 论文链接 ...

  6. 深度学习点云语义分割:CVPR2019论文阅读

    深度学习点云语义分割:CVPR2019论文阅读 Point Cloud Oversegmentation with Graph-Structured Deep Metric Learning 摘要 本 ...

  7. 3D目标检测论文阅读多角度解析

    3D目标检测论文阅读多角度解析 一.前言 CNN(convolutional neural network)在目标检测中大放异彩,R-CNN系列,YOLO,SSD各类优秀的方法层出不穷在2D图像的目标 ...

  8. 3D目标检测论文阅读摘要

    3D目标检测论文阅读摘要 2D Object Detection 的研究已经非常成熟了,代表作品有RPN系列的FasterRCNN,One Shot系列的YOLOv1-YOLOv3,这里推荐一个2D ...

  9. 2021年大数据Flink(四十八):扩展阅读  Streaming File Sink

    目录 扩展阅读  Streaming File Sink 介绍 场景描述 Bucket和SubTask.PartFile 案例演示 扩展阅读  配置详解 PartFile PartFile序列化编码 ...

最新文章

  1. 深度学习实现场景字符识别模型|代码干货
  2. appium ios 真机自动化环境搭建
  3. 如何确定最初克隆本地Git存储库的URL?
  4. 未来教育python视频百度云-2019年计算机二级Python语言程序设计考试大纲
  5. 【数字信号处理】序列傅里叶变换 ( 基本序列的傅里叶变换 | 求 cosωn 的傅里叶变换 | 复变函数欧拉公式 )
  6. mysql 授权是哪一个表_MySQL授权系统的五个表
  7. 费用流 ZOJ 3933 Team Formation
  8. 基于XML的AOP实现事务控制
  9. nginx对websocket的支持及uliweb chatroom的测试
  10. HDOJ-1257 最少拦截系统
  11. python 函数可以作为容器对象的元素_11.Python初窥门径(函数名,可迭代对象,迭代器)...
  12. Java重写equals方法时为什么要重写hashCode方法
  13. nodejs初步搭建HelloWord
  14. windows下创建vp9的VS版本
  15. Linux 视频教程 ( 猿课 )
  16. Java实现PDF文件转图片(支持单页和多页)
  17. 平面设计中立体表现技法
  18. 示例-Luat示例-HTTP
  19. 静态NAT 如何配置?
  20. MySQL自定义函数和存储过程

热门文章

  1. RIP协议及距离向量算法(详解)
  2. LeetCode 1601. 最多可达成的换楼请求数目(回溯+剪枝)
  3. LeetCode 218. 天际线问题(multiset优先队列)*
  4. LeetCode 1428. 至少有一个 1 的最左端列(二分查找)
  5. mysq命令行导出sql_mysql 命令行导入导出 sql
  6. 如何通过像素点找到世界坐标_如何通过阅读来找到自己理论研究的“视域”?...
  7. 噪声产生原因_空调噪声大?啄木鸟家庭维修,看看属于哪一个问题
  8. python rpc webservice_PythonXMLRPC服务器端和客户端实例
  9. 同一个项目相互调接口_408计算机网络D3-第二章:网络体系结构与参考模型(上)分层结构-协议-接口-服务...
  10. 目前流行的装修风格_现在最流行的八大装修风格