在删除文件的时候,系统会调用
MmFlushImageSection,如果返回True,则可以删除,该函数在WRK中的说明是
BOOLEAN MmFlushImageSection ( __in PSECTION_OBJECT_POINTERS SectionPointer, __in MMFLUSH_TYPE FlushType )
/*
Routine Description:
This function determines if any views of the specified image section are mapped, and if not, flushes valid pages (even modified ones) from the specified section and returns any used pages to the free list. This is accomplished by examining the prototype PTEs from the specified offset to the end of the section, and if any prototype PTEs are in the transition state, putting the prototype PTE back into its original state and putting the physical page on the free list.
Arguments:
SectionPointer - Supplies a pointer to a section object pointers within the FCB.
FlushType - Supplies the type of flush to check for. One of MmFlushForDelete or MmFlushForWrite.
Return Value:
Returns TRUE if either no section exists for the file object or the section is not mapped and the purge was done, FALSE otherwise.
--*/
可以看到是说只要没section或者section没被map都可以返回TRUE。
不用说,应用层的由于都是map在空间上使用,所以肯定返回FALS了。
而驱动的呢,却不是这样,虽然在MmLoadSystemImage里面有map驱动文件的操作,系统加载驱动模块却是直接内存管理分配一块虚拟地址空间V1,然后申请PFN物理内存。
最后把map的内存拷到V1中,接着就unmap掉,所以驱动文件最后还是没有map。自然驱动文件就可以删除了。
MmLoadSystemImage返回时,其实对应的section已经不存在了。只不过control_area和segment还在,control_area里面的各种ref数都是0,也许换个说明,共享内存是control_area/segment才对, 因为里面的原型pte什么的是在!ca或者segment后面
下面这个数据是某个驱动刚进入他的driverentry时,其ca的状态,确实是各个计数都为0了
kd> !ca 818e66c0
ControlArea @ 818e66c0 Segment e1927c58 Flink 82201114 Blink 81963e6c Section Ref 0 Pfn Ref 22 Mapped Views 0 User Ref 0 WaitForDel 0 Flush Count 0 File Object 82152680 ModWriteCount 0 System Views 0 WritableRefs 0 Flags (80000a0) Image File Accessed
但有个问题,section都不存在了,那control_area和segment谁来保存放在哪呢?貌似是在文件系统的fcb里面。就是那啥fileobject->sectionObjectPointers,可以断点这个位置的初始化
虽然我们知道WINDOWS下的模块内存共享是通过section来实现的,但也不可能在驱动加载后,再去调用 ZwMapViewOfSection会和驱动中使用的是同一块物理内存,原因我们上面说过了,内核是重新分配了一块内核地址,然后把
section对应的内存拷过去的。
问题:那如果先map一份,改掉几个字节,然后再加载驱动呢?会不会驱动加载的时候用的是我们修改之后的呢?
A:1。首先我直接mapfile,但是用data的形式,所以只有data section,但我修改MZ头的话,会影响驱动的加载,因为驱动加载的时候会判断MZ头,而加载时候读取文件时是从缓存data section取的,所以被影响了
2.使用sec_image map一份
kd> dd 8191fa8c 对应的section pointer 8191fa8c 81963e68 00000000 822bda38 821ad6e8 8191fa9c 40000000 0a060005 20646156 8191f900 8191faac 00000000 00000000 000003d0 000003d1 8191fabc 01080000 8191fad8 e1b759a0 e1b759a8 8191facc 41000000 0a0c0006 61436d4d e1b75960 8191fadc 00000000 00000000 00000000 00000000 8191faec 00000001 00000000 00000001 00002000 8191fafc 00000000 00000000 00000000 00000000 kd> !ca 822bda38 image section
ControlArea @ 822bda38 Segment e1927c58 Flink 00000000 Blink 00000000 Section Ref 1 Pfn Ref 1 Mapped Views 1 User Ref 2 WaitForDel 0 Flush Count 0 File Object 82152680 ModWriteCount 0 System Views 0 WritableRefs 0 Flags (a0) Image File
xxxxx.sys
Segment @ e1927c58 ControlArea 822bda38 BasedAddress 00010000 Total Ptes 22 WriteUserRef 0 SizeOfSegment 22000 Committed 0 PTE Template 822bda70000004e0 Based Addr 10000 Image Base 0 Image Commit 22 Image Info e1927da8 ProtoPtes e1927c98
Subsection 1 @ 822bda70 ControlArea 822bda38 Starting Sector 0 Number Of Sectors 10a Base Pte e1927c98 Ptes In Subsect 22 Unused Ptes 0 Flags 1e000070 Sector Offset 1e0 Protection 7 kd> !pte e1927c98 1 VA e1927c98 PDE at E1927C98 PTE at E1927C98 contains 0000000004DF4321 contains 0000000004DF4321 pfn 4df4 CG--A--KREV pfn 4df4 CG--A--KREV
kd> !pte 00180000 驱动手工 map出来的base addr VA 00180000 PDE at C0600000 PTE at C0000C00 contains 0000000017B01067 contains 0000000004DF4225 pfn 17b01 ---DA--UWEV pfn 4df4 C---A--UREV
可以看到。是和section代表的物理内存一样。同时都有copy on write属性
接着我修改了MZ头
kd> !pte 00180000 VA 00180000 PDE at C0600000 PTE at C0000C00 contains 0000000017B01067 contains 0000000004FB5067 pfn 17b01 ---DA--UWEV pfn 4fb5 ---DA--UWEV
发现对应的物理页不同了,同时 木有了 copy on write.
所以说,修改的内容还是影响不了后续加载的驱动模块的内容,除非把copy on write禁掉再修改,其中一个方法就是 改cr0,或者MDL
尼玛,今天星期六啊,跑来研究这玩意,饭还没吃。
kd> !ca 81b16290
ControlArea @ 81b16290 Segment e1bf6870 Flink 00000000 Blink 00000000 Section Ref 1 Pfn Ref 22 Mapped Views 1 User Ref 2 WaitForDel 0 Flush Count 0 File Object 818f2a78 ModWriteCount 0 System Views 0 WritableRefs 0 Flags (80000a0) Image File Accessed
BdApiUtil.sys
Segment @ e1bf6870
调用ZwUnmapViewOfSection如果变成:
kd> dt _control_area 81b16290 nt!_CONTROL_AREA 0x000 Segment : 0xe1bf6870 _SEGMENT 0x004 DereferenceList : _LIST_ENTRY [ 0x0 - 0x0 ] 0x00c NumberOfSectionReferences : 1 0x010 NumberOfPfnReferences : 0x22 0x014 NumberOfMappedViews : 0 0x018 NumberOfSystemCacheViews : 0 0x01c NumberOfUserReferences : 1 0x020 u : __unnamed 0x024 FilePointer : 0x818f2a78 _FILE_OBJECT 0x028 WaitingForDeletion : (null) 0x02c ModifiedWriteCount : 0 0x02e FlushInProgressCount : 0 0x030 WritableUserReferences : 0 0x034 QuadwordPad : 0
可以看到map view 操作影响user ref和 map views的个数
而zwcreatesection完成后,会对section的user ref和 section ref个数都 1
zwclose后,会对zwcreatesection增加的个数-1
================================
另外还有win32k这个特殊的模块
首先要提一个观点,一些资料里面说:内核空间对所有的进程都是共享的。
按一般读者的理解,那就是不管哪个进程访问0x80000000(32bit系统下)以上的内存,得到的内容都是一样的。
我想说的是,这句话严格来说是不对的。至少在windows下的session空间是不一样的,不同session的进程访问对应的session空间是不一样的。
其中WIN32K也是在session空间之内,那是不是也说明不同的session对应的win32k模块的物理内存不一样的,一般情况下是一样的,本人通过粗略读了下WRK,
它有点像dll,只要不同session的win32k的默认基地址(准确的说是和第一个win32k加载的基地址)一样的话,那它们对应的物理内存是一样的,并且它有copy on write机制(像dll)
if (FoundDataTableEntry == NULL) {
//没加载过的,如win32k第一次加载
Status = MiSessionWideReserveImageAddress (SectionPointer, &BaseAddress, &NewSectionPointer);//而且这里是返回新的NewSectionPointer,这个新的NewSectionPointer是没对应文件的,是一个页文件支撑的section, 并且已经把SectionPointer对应的文件内容已经拷贝过来了。
if (!NT_SUCCESS(Status)) { return Status; }
if (NewSectionPointer != NULL) { SectionPointer = NewSectionPointer; *InputSectionPointer = NewSectionPointer; } } else {
//已经加载过了。 BaseAddress = FoundDataTableEntry->DllBase; }
貌似到这里的时候,BaseAddress还没有map对应的物理内存,MiShareSessionImage就是把MappedBase这个虚拟地址映射到Section代表的物理内存上,而此时,像下面说的,NewSectionPointer已经不是对应文件了,
它是创建一个页文件backed 的NewSectionPointer,然后把以前section的内存拷过来,最后obderef 以前的section
Status = MiShareSessionImage (BaseAddress, SectionPointer);
NTSTATUS MiShareSessionImage ( IN PVOID MappedBase, IN PSECTION Section )
/*
Routine Description:
This routine maps the given image into the current session space. This allows the image to be executed backed by the image file in the filesystem and allow code and read-only data to be shared.
这个函数虽然说是backed by image file.但我上面说过,由于使用的是返回的NewSectionPointer,所以这时其实已经没对应真实文件了。
里面执行的关键函数MiAddMappedPtes就是把MappedBase和ControlArea代表的物理地址对应起来,ControlArea就是NewSectionPointer里面来的了,并不像非session驱动模块那样是把control代表的内存copy出来。
StartPte = MiGetPteAddress (MappedBase);
Status = MiAddMappedPtes (StartPte, NumberOfPtes, ControlArea);
曾经简单的像这样验证过,win2003下,首先session 0
kd> lm m win32k start end module name bf800000 bf9cf000 win32k (deferred) kd> db bf800000 bf800000 4d 5a 90 00 03 00 00 00-04 00 00 00 ff ff 00 00 MZ.............. bf800010 b8 00 00 00 00 00 00 00-40 00 00 00 00 00 00 00 ........@....... bf800020 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................ bf800030 00 00 00 00 00 00 00 00-00 00 00 00 f8 00 00 00 ................ bf800040 0e 1f ba 0e 00 b4 09 cd-21 b8 01 4c cd 21 54 68 ........!..L.!Th bf800050 69 73 20 70 72 6f 67 72-61 6d 20 63 61 6e 6e 6f is program canno bf800060 74 20 62 65 20 72 75 6e-20 69 6e 20 44 4f 53 20 t be run in DOS bf800070 6d 6f 64 65 2e 0d 0d 0a-24 00 00 00 00 00 00 00 mode....$....... kd> !pte bf800000 VA bf800000 PDE at C0602FE0 PTE at C05FC000 contains 0000000013F9F063 contains 0000000016556221 pfn 13f9f ---DA--KWEV pfn 16556 C---A--KREV
注意有copy on write
接着我远程桌面登陆win2003,目的是触发创建新的sesstion 1,此时会再加载win32k,
下断点MiShareSessionImage,MiShareSessionImage的第二个参数是section,可以从中获取到control_area,下面看其内容
kd> !ca 0x81e6aea8
ControlArea @ 81e6aea8 Segment e155b008 Flink 00000000 Blink 00000000 Section Ref 1 Pfn Ref 1c6 Mapped Views 0 User Ref 1 WaitForDel 0 Flush Count 0 File Object 81f16028 ModWriteCount 0 System Views 0 WritableRefs 0 Flags (a0400a0) Image File DeleteOnClose ImageMappedInSystemSpace Accessed
WINDOWSsystem32win32k.sys//上面的说法好像有点错,上面提到newsection已经没对应的文件,可这里明明有,而且flags那里也标明了是Image File
补充:WRK和真实的2003系统有点出入 ,发现并没创建新的section出来,还是用老的。另外既然内存中物理内存就是section中的,那是不是可以map后修改呢,答案是不行的,wrk里面的map函数中会判断ImageMappedInSystemSpace标志
Segment @ e155b008 ControlArea 81e6aea8 BasedAddress bf800000 Total Ptes 1cf WriteUserRef 0 SizeOfSegment 1cf000 Committed 0 PTE Template 81e6afe000000420 Based Addr bf800000 Image Base 0 Image Commit 1a Image Info e155bec0 ProtoPtes e155b048
kd> !pte e155b048 1 VA e155b048 PDE at E155B048 PTE at E155B048 contains 0000000016556221 contains 0000000016556221 pfn 16556 C---A--KREV pfn 16556 C---A--KREV
可以清楚看到session 0的MZ头保持和control_area中的一致,并不会像非session模块那样是重新copy的。接着我们看session 1的win32k模块的样子。
kd> !process -1 0 PROCESS 821d86f8 SessionId: 1 Cid: 0510 Peb: 7ffd6000 ParentCid: 017c DirBase: 14c0c3e0 ObjectTable: e2060958 HandleCount: 137. Image: winlogon.exe
kd> lml start end module name 80800000 80a47000 nt (pdb symbols) d:symbolsonlinentkrnlpa.pdb80A87123E83C40579E8319E5DB7B523C1ntkrnlpa.pdb bf800000 bf9cf000 win32k (pdb symbols) d:symbolsonlinewin32k.pdbD4FA682D54AD4C9AB1251A10DF1F1C522win32k.pdb f75b7000 f75c6000 intelppm (no symbols) kd> db bf800000 bf800000 4d 5a 90 00 03 00 00 00-04 00 00 00 ff ff 00 00 MZ.............. bf800010 b8 00 00 00 00 00 00 00-40 00 00 00 00 00 00 00 ........@....... bf800020 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................ bf800030 00 00 00 00 00 00 00 00-00 00 00 00 f8 00 00 00 ................ bf800040 0e 1f ba 0e 00 b4 09 cd-21 b8 01 4c cd 21 54 68 ........!..L.!Th bf800050 69 73 20 70 72 6f 67 72-61 6d 20 63 61 6e 6e 6f is program canno bf800060 74 20 62 65 20 72 75 6e-20 69 6e 20 44 4f 53 20 t be run in DOS bf800070 6d 6f 64 65 2e 0d 0d 0a-24 00 00 00 00 00 00 00 mode....$....... kd> !pte bf800000 VA bf800000 PDE at C0602FE0 PTE at C05FC000 contains 00000000053DF063 contains 0000000016556221 pfn 53df ---DA--KWEV pfn 16556 C---A--KREV
可以看到pfn是一样的,而且有copy on write标志。
有一点要说明的是,这个测试虚拟地址只是MZ头的,在代码内存区是下面这样的,可以看到,在代码区那里没copy on write标志
PROCESS 81e5bd88 SessionId: 0 Cid: 0d10 Peb: 7ffd8000 ParentCid: 0384 DirBase: 14c0c3c0 ObjectTable: e20813c0 HandleCount: 23. Image: cmd.exe
PROCESS 818ff378 SessionId: 2 Cid: 03d4 Peb: 7ffdc000 ParentCid: 017c DirBase: 14c0c300 ObjectTable: e1c43c38 HandleCount: 72. Image: csrss.exe
PROCESS 821d86f8 SessionId: 2 Cid: 0510 Peb: 7ffd6000 ParentCid: 017c DirBase: 14c0c3e0 ObjectTable: e2060958 HandleCount: 137. Image: winlogon.exe
kd> .process /p/i 821d86f8 You need to continue execution (press 'g' <enter>) for the context to be switched. When the debugger breaks in again, you will be in the new process context. kd> g Break instruction exception - code 80000003 (first chance) nt!RtlpBreakWithStatusInstruction: 8086c81c cc int 3 kd> u bf90b533 win32k!NtUserUpdateLayeredWindow: bf90b533 6a74 push 74h bf90b535 68d84299bf push offset win32k!`string' 0x544 (bf9942d8) bf90b53a e89c22f6ff call win32k!_SEH_prolog (bf86d7db) bf90b53f 33f6 xor esi,esi bf90b541 8975e0 mov dword ptr [ebp-20h],esi bf90b544 33db xor ebx,ebx bf90b546 8975dc mov dword ptr [ebp-24h],esi bf90b549 8975e4 mov dword ptr [ebp-1Ch],esi kd> !pte bf90b533 VA bf90b533 PDE at C0602FE0 PTE at C05FC858 contains 00000000053DF063 contains 0000000015EE6021 pfn 53df ---DA--KWEV pfn 15ee6 ----A--KREV
kd> .process /p/i 81e5bd88 You need to continue execution (press 'g' <enter>) for the context to be switched. When the debugger breaks in again, you will be in the new process context. kd> g Break instruction exception - code 80000003 (first chance) nt!RtlpBreakWithStatusInstruction: 8086c81c cc int 3 kd> u bf90b533 win32k!NtUserUpdateLayeredWindow: bf90b533 6a74 push 74h bf90b535 68d84299bf push offset win32k!`string' 0x544 (bf9942d8) bf90b53a e89c22f6ff call win32k!_SEH_prolog (bf86d7db) bf90b53f 33f6 xor esi,esi bf90b541 8975e0 mov dword ptr [ebp-20h],esi bf90b544 33db xor ebx,ebx bf90b546 8975dc mov dword ptr [ebp-24h],esi bf90b549 8975e4 mov dword ptr [ebp-1Ch],esi kd> !pte bf90b533 VA bf90b533 PDE at C0602FE0 PTE at C05FC858 contains 0000000013F9F063 contains 0000000015EE6021 pfn 13f9f ---DA--KWEV pfn 15ee6 ----A--KREV
kd> !process -1 0 PROCESS 81e5bd88 SessionId: 0 Cid: 0d10 Peb: 7ffd8000 ParentCid: 0384 DirBase: 14c0c3c0 ObjectTable: e20813c0 HandleCount: 23. Image: cmd.exe
下面再演示一种方法:个人认为比较强大,一般人看不懂
代码语言:javascript复制kd> u bf90b533
win32k!NtUserUpdateLayeredWindow:
bf90b533 6a74 push 74h
bf90b535 68d84299bf push offset win32k!`string' 0x544 (bf9942d8)
bf90b53a e89c22f6ff call win32k!_SEH_prolog (bf86d7db)
bf90b53f 33f6 xor esi,esi
bf90b541 8975e0 mov dword ptr [ebp-20h],esi
bf90b544 33db xor ebx,ebx
bf90b546 8975dc mov dword ptr [ebp-24h],esi
bf90b549 8975e4 mov dword ptr [ebp-1Ch],esi
kd> !pte bf90b533
VA bf90b533
PDE at C0602FE0 PTE at C05FC858
contains 0000000013F9F063 contains 0000000015EE6021
pfn 13f9f ---DA--KWEV pfn 15ee6 ----A--KREV
kd> ?bf90b533-bf800000
Evaluate expression: 1094963 = 0010b533
kd> ?0010b533>>c
Evaluate expression: 267 = 0000010b
kd> !ca 0x81e6aea8
ControlArea @ 81e6aea8
Segment e155b008 Flink 00000000 Blink 00000000
Section Ref 1 Pfn Ref 1c6 Mapped Views 0
User Ref 1 WaitForDel 0 Flush Count 0
File Object 81f16028 ModWriteCount 0 System Views 0
WritableRefs 0
Flags (a0400a0) Image File DeleteOnClose ImageMappedInSystemSpace Accessed
WINDOWSsystem32win32k.sys
Segment @ e155b008
ControlArea 81e6aea8 BasedAddress bf800000
Total Ptes 1cf
WriteUserRef 0 SizeOfSegment 1cf000
Committed 0 PTE Template 81e6afe000000420
Based Addr bf800000 Image Base 0
Image Commit 1a Image Info e155bec0
ProtoPtes e155b048
Subsection 1 @ 81e6aee0
ControlArea 81e6aea8 Starting Sector 0 Number Of Sectors 2
Base Pte e155b048 Ptes In Subsect 1 Unused Ptes 0
Flags 11 Sector Offset 0 Protection 1
Subsection 2 @ 81e6af00
ControlArea 81e6aea8 Starting Sector 2 Number Of Sectors c8f
Base Pte e155b050 Ptes In Subsect 192 Unused Ptes 0
Flags 31 Sector Offset 0 Protection 3
Subsection 3 @ 81e6af20
ControlArea 81e6aea8 Starting Sector c91 Number Of Sectors 71
Base Pte e155bce0 Ptes In Subsect f Unused Ptes 0
Flags 11 Sector Offset 0 Protection 1
Subsection 4 @ 81e6af40
ControlArea 81e6aea8 Starting Sector d02 Number Of Sectors 58
Base Pte e155bd58 Ptes In Subsect 13 Unused Ptes 0
Flags 51 Sector Offset 0 Protection 5
Subsection 5 @ 81e6af60
ControlArea 81e6aea8 Starting Sector d5a Number Of Sectors 4
Base Pte e155bdf0 Ptes In Subsect 1 Unused Ptes 0
Flags 51 Sector Offset 0 Protection 5
Subsection 6 @ 81e6af80
ControlArea 81e6aea8 Starting Sector d5e Number Of Sectors e
Base Pte e155bdf8 Ptes In Subsect 2 Unused Ptes 0
Flags 11 Sector Offset 0 Protection 1
Subsection 7 @ 81e6afa0
ControlArea 81e6aea8 Starting Sector d6c Number Of Sectors 2d
Base Pte e155be08 Ptes In Subsect 6 Unused Ptes 0
Flags 71 Sector Offset 0 Protection 7
Subsection 8 @ 81e6afc0
ControlArea 81e6aea8 Starting Sector d99 Number Of Sectors 11
Base Pte e155be38 Ptes In Subsect 3 Unused Ptes 0
Flags 11 Sector Offset 0 Protection 1
Subsection 9 @ 81e6afe0
ControlArea 81e6aea8 Starting Sector daa Number Of Sectors 69
Base Pte e155be50 Ptes In Subsect e Unused Ptes 0
Flags 11 Sector Offset 0 Protection 1
kd> ?10b-1
Evaluate expression: 266 = 0000010a //10a<192,落在subsection 2
kd> dd e155b050 8*10a //每项占8个字节大小 (_MMPTE结构)
e155b8a0 15ee6121 00000000 15ee7860 00000000
e155b8b0 15ee8121 00000000 15ee9121 00000000
e155b8c0 15eea860 00000000 15f2b860 00000000
e155b8d0 15f2c860 00000000 15eed121 00000000
e155b8e0 15eee121 00000000 15eef121 00000000
e155b8f0 15ef0121 00000000 15ef1121 00000000
e155b900 15ef2121 00000000 15ef3121 00000000
e155b910 15eb4121 00000000 15f35860 00000000
kd> !pte e155b8a0 1
VA e155b8a0
PDE at E155B8A0 PTE at E155B8A0
contains 0000000015EE6121 contains 0000000015EE6121
pfn 15ee6 -G--A--KREV pfn 15ee6 -G--A--KREV
kd> ?15ee6 <<c
Evaluate expression: 367943680 = 15ee6000
kd> !db 15ee6000 533
#15ee6533 6a 74 68 d8 42 99 bf e8-9c 22 f6 ff 33 f6 89 75 jth.B...."..3..u
#15ee6543 e0 33 db 89 75 dc 89 75-e4 33 ff e8 65 22 f6 ff .3..u..u.3..e"..
#15ee6553 8b 4d 08 e8 4b 95 f6 ff-3b c6 0f 84 ff 00 00 00 .M..K...;.......
#15ee6563 8b 0d a0 30 9b bf 8b 51-28 89 55 8c 8d 55 8c 89 ...0...Q(.U..U..
#15ee6573 51 28 89 45 90 ff 40 04-89 75 fc 8b 75 1c 8b 0d Q(.E..@..u..u...
#15ee6583 bc 41 9b bf 85 f6 74 1f-3b f1 0f 83 f7 fe ff ff .A....t.;.......
#15ee6593 8b 16 89 55 c0 8b 76 04-89 75 c4 89 55 a8 89 75 ...U..v..u..U..u
#15ee65a3 ac 8d 55 a8 89 55 e0 8b-75 14 85 f6 74 2f 3b f1 ..U..U..u...t/;.
kd> db bf90b533
bf90b533 6a 74 68 d8 42 99 bf e8-9c 22 f6 ff 33 f6 89 75 jth.B...."..3..u
bf90b543 e0 33 db 89 75 dc 89 75-e4 33 ff e8 65 22 f6 ff .3..u..u.3..e"..
bf90b553 8b 4d 08 e8 4b 95 f6 ff-3b c6 0f 84 ff 00 00 00 .M..K...;.......
bf90b563 8b 0d a0 30 9b bf 8b 51-28 89 55 8c 8d 55 8c 89 ...0...Q(.U..U..
bf90b573 51 28 89 45 90 ff 40 04-89 75 fc 8b 75 1c 8b 0d Q(.E..@..u..u...
bf90b583 bc 41 9b bf 85 f6 74 1f-3b f1 0f 83 f7 fe ff ff .A....t.;.......
bf90b593 8b 16 89 55 c0 8b 76 04-89 75 c4 89 55 a8 89 75 ...U..v..u..U..u
bf90b5a3 ac 8d 55 a8 89 55 e0 8b-75 14 85 f6 74 2f 3b f1 ..U..U..u...t/;.
对于非session的模块,如自己写的驱动,
一般代码段对应的control_area中的pte指向的物理内存状态如下:
kd> !pte e1927c98 8*1f 1 VA e1927d90 PDE at E1927D90 PTE at E1927D90 contains 000000001797B8E0 not valid Transition: 1797b Protect: 7 - ReadWriteCopyExecute
是Transition状态
复习下上面的的知识,我们知道正在运行的驱动模块(非session模块)对应的pfn和control_area指向的物理内存pfn是不一样的,在这里实例中前者是7fca,后者是1797b
kd> !pte f5d8d140 //f5d8d140是我测试驱动模块的driverentry地址 VA f5d8d140 PDE at C0603D70 PTE at C07AEC68 contains 0000000000A8F063 contains 0000000007FCA163 pfn a8f ---DA--KWEV pfn 7fca -G-DA--KWEV
为了激活control_area的pfn回到常规的状态,我另外写了一个驱动,sec_image方式map了下这个文件 然后读取了下,就变成如下状态了
kd> !pte e1927c98 8*1f 1 VA e1927d90 PDE at E1927D90 PTE at E1927D90 contains 000000001797B321 contains 000000001797B321 pfn 1797b CG--A--KREV pfn 1797b CG--A--KREV
可以清晰看到,里面带有copy on write标志。所以像这些常规驱动模块(非session模块),其control_area中的物理内存,不管MZ头还是代码段,都有copy on write属性
PS:MmFlushImageSection其中一部分代码会有下面的判断
代码语言:javascript复制 if (SectionCheckType != CheckUserDataSection) {
SectRef = ControlArea->NumberOfSectionReferences;
}
else {
SectRef = ControlArea->NumberOfUserReferences;
}
if ((SectRef != 0) ||
(ControlArea->NumberOfMappedViews != 0) ||
(ControlArea->u.Flags.BeingCreated)) {
可以看到,有4个位置可能的判断导致失败 返回FALSE
//
// The segment is currently in use or being created.
//
if (DelayClose) {
//
// The section should be deleted when the reference
// counts are zero, set the delete on close flag.
//
ControlArea->u.Flags.DeleteOnClose = 1;
}
UNLOCK_PFN (OldIrql);
MiFreeEventCounter (SegmentEvent);
return FALSE;
}
======================================================================================================
再补充一点上面内核模块加载时候copy 过程的验证
kd> !pte e1927c98 1 //e1927c98是section对象中代表共享内存的第一页 pte, VA e1927c98 PDE at E1927C98 PTE at E1927C98 contains 0000000004DF48E0 not valid Transition: 4df4 Protect: 7 - ReadWriteCopyExecute kd> !db 4df4<<c # 4df4000 4d 5a 90 00 03 00 00 00-04 00 00 00 ff ff 00 00 MZ.............. # 4df4010 b8 00 00 00 00 00 00 00-40 00 00 00 00 00 00 00 ........@....... # 4df4020 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................ # 4df4030 00 00 00 00 00 00 00 00-00 00 00 00 d8 00 00 00 ................ # 4df4040 0e 1f ba 0e 00 b4 09 cd-21 b8 01 4c cd 21 54 68 ........!..L.!Th # 4df4050 69 73 20 70 72 6f 67 72-61 6d 20 63 61 6e 6e 6f is program canno # 4df4060 74 20 62 65 20 72 75 6e-20 69 6e 20 44 4f 53 20 t be run in DOS # 4df4070 6d 6f 64 65 2e 0d 0d 0a-24 00 00 00 00 00 00 00 mode....$....... kd> !ed 4df4050 6a6a6a6a kd> !db 4df4<<c # 4df4000 4d 5a 90 00 03 00 00 00-04 00 00 00 ff ff 00 00 MZ.............. # 4df4010 b8 00 00 00 00 00 00 00-40 00 00 00 00 00 00 00 ........@....... # 4df4020 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................ # 4df4030 00 00 00 00 00 00 00 00-00 00 00 00 d8 00 00 00 ................ # 4df4040 0e 1f ba 0e 00 b4 09 cd-21 b8 01 4c cd 21 54 68 ........!..L.!Th # 4df4050 6a 6a 6a 6a 72 6f 67 72-61 6d 20 63 61 6e 6e 6f jjjjrogram canno # 4df4060 74 20 62 65 20 72 75 6e-20 69 6e 20 44 4f 53 20 t be run in DOS # 4df4070 6d 6f 64 65 2e 0d 0d 0a-24 00 00 00 00 00 00 00 mode....$.......
看到,我们修改了section对应的物理内存的内容
随后我加载该驱动,f5d56000是驱动的MZ头
kd> db f5d56000 f5d56000 4d 5a 90 00 03 00 00 00-04 00 00 00 ff ff 00 00 MZ.............. f5d56010 b8 00 00 00 00 00 00 00-40 00 00 00 00 00 00 00 ........@....... f5d56020 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................ f5d56030 00 00 00 00 00 00 00 00-00 00 00 00 d8 00 00 00 ................ f5d56040 0e 1f ba 0e 00 b4 09 cd-21 b8 01 4c cd 21 54 68 ........!..L.!Th f5d56050 6a 6a 6a 6a 72 6f 67 72-61 6d 20 63 61 6e 6e 6f jjjjrogram canno f5d56060 74 20 62 65 20 72 75 6e-20 69 6e 20 44 4f 53 20 t be run in DOS f5d56070 6d 6f 64 65 2e 0d 0d 0a-24 00 00 00 00 00 00 00 mode....$....... kd> !pte f5d56000 VA f5d56000 PDE at C0603D70 PTE at C07AEAB0 contains 0000000000A8F063 contains 0000000011FE0163 pfn a8f ---DA--KWEV pfn 11fe0 -G-DA--KWEV
可以看到,MZ头和section中对应的pfn不一样,但它的内容却是从section中来的,所以验证了驱动模块加载过程是完全独立一个虚拟地址和物理内存的,只是它对应的内容来自section中应对的内容 。
data的ca里面,只有user ref才能阻止删除,section ref不能,单纯的改data ca中的mapview个数为1也不行
继续补充:
NTSTATUS MiMapViewOfImageSection ( )
{
// // Check to see if a purge operation is in progress and if so, wait // for the purge to complete. In addition, up the count of mapped // views for this control area. //
Status = MiCheckPurgeAndUpMapCount (ControlArea, TRUE);//这个函数里面会有一个判断,当previous=usermode并这个ca已经map在内核时,会返回失败 if (!NT_SUCCESS (Status)) { return Status; }
}
记:此文章是花了大概2天半时间断断续续而成,算是对驱动模块原理有了比较清晰的理解了
成文于2013年,未对win10研究