吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 5884|回复: 27
收起左侧

[调试逆向] DLL加载过程初探

  [复制链接]
kn0sky 发表于 2021-10-13 16:58

前言

前两天刷手机的时候,无意间想到了一些问题:存储DLL信息的_LDR_DATA_TABLE_ENTRY结构体是在什么时候填充的?是在Dllmain运行之前还是之后呢?这个结构体的作用是什么?本文记录一下探究的过程,如有不对望师傅们指出!

实验环境:物理机Windows10 x64 21H2

实验工具:IDA

测试程序:32位/64位

32位分析

动态加载通常使用LoadLibrary进行,一般VS2019默认是W后缀,就从W来分析吧(下文64位同)

LoadLibraryW

LoadLibraryW函数位于kernelBase.dll里

image-20211013094913918.png

这个函数是LoadLibraryExW的再次封装,这里直接凑齐参数调用ExW函数了,这里有三个参数:

  • DLL路径,
  • hFile,保留参数,必须是NULL
  • dwFlag,可以是NULL,会完整加载DLL,通过设定Flag可以进行部分加载,具体值在MSDN上有

参考自:LoadLibraryExW function (libloaderapi.h) - Win32 apps | Microsoft Docs

LoadLibraryExW

LoadLibraryExW函数位于kernelBase.dll里

image-20211013095040737.png

刚开始先判断hFile参数和dwFlag参数,如果是不合法的值,就跳走返回

image-20211013095134931.png

接下来将Ring3的字符串转换成R0的UNICODE_STRING结构体,转换失败就跳转返回,转换成功则进行末尾去0操作,接下来一切正常的话,会跳转走

image-20211013095356138.png

接下来是Flag值的判断,如果指定位有值,就跳去执行相应的操作,最后会来到LdrLoadDll函数(下文会介绍),第四个参数传址接收加载模块的模块地址,执行完之后,会返回一个值到eax,然后跳转

image-20211013095621981.png

如果返回值不是负数,也就是执行成功了,就再次跳转

image-20211013095709277.png

这里从地址里获取得到的模块地址,然后返回

LdrLoadDll

LdrLoadDll函数位于ntdll.dll里

image-20211013095947451.png

这个函数刚开始用局部变量接收了传入的BaseAddress地址

image-20211013100237722.png

往下走有一个函数调用,传入了空的LdrEntry结构体(通过动态调试发现这是LdrEntry结构体的)来接收数据,函数(下文介绍)执行完成之后,这个结构体会被填充好

image-20211013124500676.png

再之后,从这个结构体的18h偏移处(偏移0x18是Dllbase,就是模块地址)取出值设置到BaseAddress的地址里返回

LdrpLoadDllInternal

LdrpLoadDllInternal函数位于ntdll.dll里

image-20211013125034633.png

进入函数以后,会先判断dll是不是已经加载了,已经加载就获取其模块信息结构并返回,如果不是,会走到这里这里的var_20是局部变量,也是个LdrEntry结构(通过动态调试发现的),把var_20的地址给了eax,然后调用了这个函数

image-20211013125313605.png

然后把var_20的地址给了参数的LdrEntry赋值,之后操作var_20就是实际操作参数了,程序运行到LdrpPrepareModuleForExecution函数之前,LdrEntry结构体已经是填充完毕的状态了,LdrpPrepareModuleForExecution函数会进一步的进行DLL相关初始化操作,并执行DllMain的DLL_PROCESS_ATTACH分支程序,然后函数差不多就结束了,本次的分析目标已经达成了,就不往下分析了

64位分析

LoadLibraryW

image-20211013131357920.png

64位程序简洁好多啊

LoadLibraryExW

image-20211013131500427.png

类比32位的看吧,流程是一样的,这里进入LdrLoadDll,传入了BaseAddress的指针,作为第四个参数(r9)

LdrLoadDll

开始先把BaseAddress指针从r9存到了r14,然后就是这里:

image-20211013155141941.png

把空的LdrEntry(准确来说,不是空的,里面前10h字节有值,可能DllBase也有,也可能没有)给r9传参,通过LdrpLoadDll填充了该结构,然后通过偏移取得其中的DllBase,并返回

LdrpLoadDll

image-20211013163744493.png

这里把结构体地址给到了局部变量上保存,通过rsi传参调用了LdrpLoadDllInternal函数

LdrpLoadDllInternal

image-20211013163849768.png

首先进来后,先把结构体存在了rsp+20的位置上,然后入栈5个参数,rsp抬高50h字节,这个时候结构体存在了rsp+78h的位置

image-20211013163958780.png

顺利的话,后面会进到这一块来,继续进行初始化操作,这个函数跟32位的一样,继续初始化DLL运行DllMain函数

到此,64位的分析也到此结束了

总结

LoadLibrary API动态加载DLL的时候,会先进行LdrEntry结构体的填充,然后再继续执行DLL初始化操作和执行DllMain函数,该API返回的模块句柄其实就是模块地址,是在LdrLoadDll这一层从LdrEntry中找到的值

据查阅资料,进程会维护一个“模块数据库”,用来存储模块信息,模块信息结构是LDR_DATA_TABLE_ENTRY,所有的模块信息通过双向链表连接在一起,进程寻找模块中的内容的时候,会基于该结构获取的模块基址进行寻址

参考资料:《Windows 核心编程》《深入解析Windows操作系统》

免费评分

参与人数 8吾爱币 +15 热心值 +7 收起 理由
lyl610abc + 3 + 1 我很赞同!
Hmily + 7 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
zjun777 + 1 用心讨论,共获提升!
Passerby + 2 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
冥界3大法王 + 1 用心讨论,共获提升!
yunji + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
sumile + 1 + 1 我很赞同!
13305353308 + 1 + 1 我很赞同!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

a952135763 发表于 2021-10-13 17:39
tls回调 的 线程局部存储是咋调用的
mm258258 发表于 2021-10-13 18:15
马猴煮酒 发表于 2021-10-13 20:06
dongjing520 发表于 2021-10-13 20:42
感谢分享
Passerby 发表于 2021-10-13 21:28
写的很好 收藏了
qwe102030 发表于 2021-10-13 23:44
写的很好 收藏了
m4wayne 发表于 2021-10-14 08:38
赞赞赞,分析到到位,注释更是很清晰
xiaohuihui3 发表于 2021-10-14 09:22
学习学习了, 感谢分享
zxczxc213 发表于 2021-10-14 09:33
请问大神是用什么工具调式的??
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则 警告:本版块禁止灌水或回复与主题无关内容,违者重罚!

快速回复 收藏帖子 返回列表 搜索

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-3-28 19:55

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表