映像加载器
当系统中的进程被启动的时候,内核创建一个内核对象表示该进程,并执行各种和内核相关的初始化任务。绝大部分的初始化任务是在内核之外完成的,这些工作是由映像加载器完成的。映像加载器驻留在用户模式下的Ntdll.dll中,映像加载器完成以下的初始化工作:
- 1.初始化其他用户模式,包括堆栈的初始化,TLS和FLS的初始化
- 2.解析执行文件的IAT,DLL的导出表
- 3.加载卸载DLL,维护模块数据库
- 4.启用API集和API重定向
进程创建以后,映像加载器调用特殊的原生API,在一个基于栈中的环境帧中执行。由于加载器并不使用标准的调用进入正在运行的应用程序中,所以,在一个线程的栈痕迹中,永远不会看到加载器的初始化函数出现在调用中。
通过进行“观察映像加载器”这个实验,我们可以知道,加载器不一定在程序开始的时候运行,也可以在后续运行过程中,会对一些涉及到延迟加载其他模块的线程请求进行响应。
进程初始化早期工作
加载器完成早期初始化工作后,开始解析IAT,以及加载Dll,并且根据IAT和导出表,解析导入导出函数,构建模块数据库。
DLL名称解析
原始搜索dll目录的顺序
- 应用程序被激发的目录
- 当前目录(可以通过SetCurrentDirection修改)
- windows系统目录
- windows子系统目录(16位)
- windows目录
- %PATH%指定的目录(通过SetEnvironmentVariabe修改)
 ;更加安全的dll搜索路径
- knowndll注册表指定的dll路径
- 应用程序被激发的目录
- windows系统目录
- windows子系统目录(16位)
- windows目录
- 当前目录(可以通过SetCurrentDirection修改)
- %PATH%指定的目录(通过SetEnvironmentVariabe修改)
已加载的模块数据库
 ;加载器维护了一个关于进程加载模块的数据库,存放在PEB中,该数据库被存放在PEB_LDR-DATA结构中,在此结构中,记载器维护了3个双向链表。
 ;以下是关于PEB_LDR_DATA的结构体。
 ;其中包含了LIST_ENTRY类型三个数据结构,分别是模块加载顺序,内存加载顺序,初始化模块顺序。
 ;LIST_ENTRY是一个双向链表,这个双链表指向进程装载的模块,结构中的每个指针,指向了一个LDR_DATA_TABLE_ENTRY 的结构
利用windbg,进行以下操作
- !peb:显示peb信息
- 根据上述知识,我们知道LIST_ENTRY的指针指向的是LDR_DATA_TABLE_ENTRY结构体,由上图,dd 00251ee0,内存数据251f48,指向的是下一个LDR_DATA_TABLE_ENTRY结构体,251eac指向的上一个结构体。
- 不断的dd 内存,发现,最后一个结构体的下一个结构体,是第一个结构体,这样形成了一个双向循环链表。
导入信息解析
根据IAT和模块数据库装载Dll,然后进行重定位检测,如果进行了重定位,则解析dll中的重定位信息,当每个dll装载完成后,解析IAT,查找每个导入函数。
导入过程初始化的后期处理
dll等依赖文件被加载入进程后,则应该执行以下操作:
- 检查是否为NET应用
- 检查程序自身是否要求重定位
- 是否使用TLS
- 兼容性检测。