我以为面试官在第二层,没想到他在第5层(dll注入:系统kernel32.dll为什么在每个进程中的基址相同)

在漫长的春招过程中,身为一个大三找实习的弱鸡,免不了被面试官捶打。
昨天被问到一个dll注入的问题,当时心想,正中我的下怀,唉,我就叽里呱啦把远程线程注入的实现过程讲了一下:

1.使用进程PID打开进程,获得句柄
2.使用进程句柄申请一块可读可写的内存地址
3.把dll路径写入内存
4.使用CreateRomteThread创建远程线程,调用LoadLibrary实现注入
5.释放收尾工作或者卸载dll

当然这是第一层,我知道面试肯定没有这么简单,我在第二层等着面试官。

果不其然,面试官问道:“唉,那么问题来了,你在不同的进程调用loadlibrary(),是怎么调用的?”

我:“我是从Kernel32.dll动态库里去获得Loadlibrary的地址,因为LoadLibrary在Kernel32.dll中,在Windows系统下,Kernel32.dll的加载基址在每个进程中都是一样的,所以LoadLibrary在每个进程的地址就是一样的。”

我以为到第二层就已经结束了。

没想到面试官非常兴奋地问道:“那么问题又来了,为什么dll在每个进程中默认加载机制一样?”

心里一惊 Σ(っ °Д °;)っ

硬着头皮回答道:“Windows默认系统dll在所有进程的加载基址不变,是因为所有进程公用一个内核空间,在内核空间对这里做了规定,所以加载基址一样。”
当然回答的时候,我知道这应该是不对的,所以一直用疑问的语气尝试让面试官给出结果。

面试官听完我的回答:“你知道地址随机化吗?”

“啊,我知道啊,地址随机化就是ALSR,他就是PE文件每次加载到内存的起始地址都会随机变化,一种保护内存的手段。”

面试官:“好,那你知道在系统开启地址随机化的时候,为什么每个进程加载基址的位置还是一样的吗?”

这时候,我才意识到,原来面试官是在第5层。

然后我陷入了思考…

首先下面这句话,无疑是正确的。

Windows默认的是同一个系统中dll的文件加载位置是固定的。

然后下面这段话,无疑也是正确的。

ASLR(Address space layout randomization)windows系统中运行的同一个程序其模块加载的地址空间是随机的

那么当它们俩撞在了一起,就让我想起了矛盾相争的故事。“啊,我的茅牛逼,没有扎不破的盾,我的盾也牛逼,没有茅能扎破它”。

”每次加载基址变化的话,那么就绪要重定位,但是重定位的位置在每个进程中,不一定一样啊。”

面试官:“嗯,对,那你再想想,他是在什么时候进行处理的呢?”

我当时想的是,每次系统开启之后就不变了,那么我就赌一手:“每次系统开启的过程中,会进行重定位的处理,之后就不变了。”

面试官:“嗯嗯好,那下一个问题”。

我:疑惑脸,不知道自己回答的对不对,还是面试结束后再去看一下吧。
没想到这个问题老千层饼了。

下面进入正题。

为什么在ASLR机制下,系统DLL加载基址在每个进程中相同

ASLR机制:

ASLR(Address Space Layout
Randomization,地址空间布局随机化)是一种针对缓冲区溢出的安全保护技术。借助ASLR,PE文件每次加载到内存的起始地址都会随机变化。
ASLR会随机化地址:1、堆地址 2、栈地址 3、PE文件加载基址 4、PEB与TEB地址

详细文章:《Windows安全机制——ASLR(地址随机化)及如何关闭ASLR》https://blog.csdn.net/weixin_43742894/article/details/105702886

这里有一篇是对为什么系统DLL加载基址不变的解释:
http://www.nynaeve.net/?p=198

简单概括:

因为某些原因,系统中的一些DLL需要加载在不同进程的同样的基地址上(在终端服务会话
中,其中一些DLL可能使用轮流基地址) ,尽管微软没有明确的文档化, 一些程序的运行依于
这些“固定基址的DLL”。

这并不意味着这些DLL 的基地址不可改变,但是在系统运行的时候,所有的进程都将会把这些
DLL 映射到同样的基地址上(如果它们确实映射这些DLL 的话)。

当前在系统范围内需要同样的基地址的DLL 有 NTDLL, kernel32, and user32,但是它们需要
固定基址的原因是不同的。

然后这篇文章里面有一句话:
ASLR 机制在每次引导的时候为一个给定的DLL选择一个同样的基地址。因此,在系统启动的时候,NTDLL可以拥有一个随机的起始地址,在重新启动之前,这个指定的随机地址被所有的进程所使用。

似乎好像和我回答的有点相像。但这只是规定,我们就去看一下到底是不是这么一回事。

下面是用windbg查找kernel32.dll的基址(这里只是以kernel32为例)
在这里插入图片描述
在_PEB中找到Ldr这根链表
在这里插入图片描述
接下来,解析LDR:
在这里插入图片描述
我们就选取InLoadOrderModuleList这个链;对它的Flink进行解析,

通过查阅MSDN,知道,这个Flink指向的具体的数据结构类型是:_LDR_DATA_TABLE_ENTRY
在这里插入图片描述
继续遍历InLoadOrderLinks的Flink字段:
在这里插入图片描述
还不是Kernel32.dll,继续走:
到此,通过遍历InLoadOrderLinks链,我们找到了KERNEL32.DLL,取出基址就比较容易了,在0x18偏移处;
在这里插入图片描述
重启系统
然后我们关闭系统,重启之后,重新定位kernel32.dll的位置。
在这里插入图片描述
然后我们查看其他进程的kernel32的加载基址:
在这里插入图片描述
测试结论:

我们发现不同进程中的kernel32.dll的基址相同,但是当系统进行重启后,加载基址就会变化,也就是说可以认为这里的地址随机化在系统启动的时候就进行重定位,此后在此系统中,系统dll在每个进程中的基址相同。

经过我们的测试结论是正确的,就是ASLR 机制在每次引导的时候为一个给定的DLL选择一个同样的基地址。因此,在系统启动的时候,NTDLL可以拥有一个随机的起始地址,在重新启动之前,这个指定的随机地址被所有的进程所使用。

但是至于这样子ASLR不改变进程中系统dll的基址的原因,我们还需要继续探究。

小弟才疏学浅,还得继续研究,留待搞清楚之后补齐。

在这里插入图片描述

  • 9
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值