找回密码
 加入我们

QQ登录

只需一步,快速开始

搜索
查看: 5109|回复: 2

驱动下写用户内存不触发COW的问题。

[复制链接]

1

主题

4

回帖

0

精华

贵宾会员

积分
10
发表于 2016-1-15 15:56:59 | 显示全部楼层 |阅读模式
本帖最后由 emissary 于 2016-1-15 16:00 编辑

我的场景是在驱动下,通过 PsSetLoadImageNotifyRoutine 得到EXE文件镜像基址,再修改导入表实现 DLL 注入,在修改内存映射时使用的CR0或MDL的方法来绕过写保护。两种方法都试过。

在实际测试时,我修改记事本程序,启动第一个修改成功,WinDBG 能验证到。但之后再启动一个记事本程序时,发现系统并没有再重新从磁盘文件读取原始内容映射到内存,而是直接使用了第一个记事本程序的内存映射,我知道这是操作系统的复用特性,这样,我第二次和以后启动记事本程序后,在驱动中都不用修改它,每一个记事本程序实例的内存也是修改过的状态。


我现在碰到的问题是:
第一,EXE的内部空闲空间总是不够的,我想要重构导入表,那么我希望有更多的空间存放我的代码,所以我在驱动中动态申请了一片当前进程的虚拟内存用来存放新的导入表。

第二,每次(每一个单独的EXE启动)虚拟内存申请的时候,申请的基址我认为是可能不一样的,特别是新操作系统使用了随机分配算法。所以我认为很有可能在A记事本进程中,我申请用来存放导入表的地址在0x08000000而B记事本进程在0x0A000000。

第三,如果每个实例的新导入表(新申请的导入表空间)的偏移不一样,那么修改EXE文件镜像时,PE头内要填充的数据自然要重计算,数据内容也不一样。

第四,问题来了,在内核下通过CR0或MDL的方法写虚拟内存没触发页表COW,导致全局写入,引起了我一开始描述的问题。如果两个进程一前一后写入后,接着两个的主线程才运行起来怎么办?寄托于操作系统在我修改了A进程后,让A进程全部加载完DLL,再运行起B进程这样理想的环境?

最终我想要实现的其实也简单,就是我在驱动内部修改注入每一个记事本程序时,我修改的内存都是像用户层一样,对一个可写内存写入时触发COW,这样我的数据就会在新的物理页上,不影响原镜像。


这个问题其实是我两年前碰到的一个问题,一直没有得到解决,原因之一也是自身知识层次不足,这几天整理东西,看到了,就再发出来,希望有人能帮助我,替我解开这个疑惑。我得提前感谢回复我的朋友,因为这个问题对不知者真是很蛋疼,得表示感谢。


857

主题

2632

回帖

2

精华

管理员

此生无悔入华夏,  长居日耳曼尼亚。  

积分
36130
发表于 2016-1-15 16:15:16 | 显示全部楼层
这个没办法,如果在这个时机写内存,那么(修改)对后续的加载的模块都有效。
此外在LoadImageNotify里限制多多,MSDN描述原文如下:
The operating system does not call load-image notify routines when sections created with the SEC_IMAGE_NO_EXECUTE attribute are mapped to virtual memory.
In Windows 7, Windows Server 2008 R2, and earlier versions of Windows, the operating system holds an internal system lock during calls to load-image notify routines for images loaded in user process address space (user space). To avoid deadlocks, load-image notify routines must not call system routines that map, allocate, query, free, or perform other operations on user-space virtual memory.
A driver must remove any callbacks it registers before it unloads. You can remove the callback by calling the PsRemoveLoadImageNotifyRoutine routine.
When the main executable image for a newly created process is loaded, the load-image notify routine runs in the context of the new process. The operating system calls the driver's load-image notify routine at PASSIVE_LEVEL inside a critical region with normal kernel APCs disabled.
When the load-image notify routine is called, the input FullImageName points to a buffered Unicode string that identifies the executable image file. (The FullImageName parameter can be NULL in cases in which the operating system is unable to obtain the full name of the image at process creation time.) The ProcessId handle identifies the process in which the image has been mapped, but this handle is zero if the newly loaded image is a driver.
https://msdn.microsoft.com/en-us/library/windows/hardware/ff559957(v=vs.85).aspx

1

主题

4

回帖

0

精华

贵宾会员

积分
10
 楼主| 发表于 2016-1-15 16:36:55 | 显示全部楼层
Tesla.Angela 发表于 2016-1-15 16:15
这个没办法,如果在这个时机写内存,那么(修改)对后续的加载的模块都有效。
此外在LoadImageNotify里限制 ...

MSDN上的限制确实很重要,特别是内存锁一节,记得当时代码写完第一次测试出现问题,就是因为在 LoadImageNotifyRoutine 去申请 VM,结果调试很久找不到原因才想到去查MSDN。

当时也有猜想是COW没有置位,于是去获取PTE,去改COW位,不过改成了也没有效果,最终就放弃了。

感谢版主的回复。我再动脑筋想想,有思路跟你沟通。
您需要登录后才可以回帖 登录 | 加入我们

本版积分规则

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