windows 为什么驱动模块加载后可以删除而应用模块不可以

2019-03-28 10:35:16 浏览数 (1)

在删除文件的时候,系统会调用

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研究

0 人点赞