FastMutex的陷阱

相对ERESOURCE来说,FastMutex的使用比较方便,不用考虑禁APC问题,也不用考虑FastMutex结构体的释放,用完即可以三不管。对于竞争不激烈且读写不明显的情况下,用FastMutex还是不错的。

不过使用过程中要注意其隐藏的陷阱:

1,ExAcquireFastMutex 会提升IRQL至APC_LEVEL,且保存原来的IRQL用以在ExReleaseFastMutex时恢复。所以在嵌套获取FastMutex时要特别注意放锁一定要依加锁顺序反向操作,不然会导致IRQL紊乱。为此系统特别提供了Unsafe操作函数(ExAcquireFastMutexUnsafe及ExReleaseFastMutexUnsafe),这两个函数不会操作IRQL,但使用上却有前提条件。

2,FastMutex的实现是通过原子锁与Gate对象来实现的,在没有竞争的情况下,通过原子锁可以速实现加锁及解锁,此外Gate对象是不可重入的,不像Event等(Dispatch Object)及ERESOURCE等可重入的锁(同一个线程可以同时加锁多次不会死锁)。

至于FastMutex为什么要提升IRQL至APC_LEVEL的原因,和文件系统调用FsRtlEnterFileSystem禁APC的原因是一样的,直接目的是防止当前线程被挂起(Suspend)。设想在没有禁止APC的情况下调用ExAcquireFastMutexUnsafe加锁之后线程随时可能被挂起,一旦被挂起的话系统中试图获取该锁的其它线程则只能等待,不管此线程是否高优先级或关键的系统任务。同理可以理解Event,ERESOURCE等为什么也需要禁止APC的原因。

除了FastMutex之外,Windows内核还实现了另外两种Mutex: Regular Mutex及Guarded Mutex。不过从Windows 8开始,GuardedMutext和FastMutex的实现是一样的,二者并无差别。Regular Mutex通过KeInitializeMutex初始化,加锁及放锁是通过KeWaitForXXXObject及KeReleaseMutex来操作的。我们知道 Event也是通过KeWaitForXXXObject来加锁的,但是Regular Mutex的加锁操作却会禁止当前线程的Kernel APC,Event却不会,所以在操作Event之前要显示地进入临界区或禁止APC操作以防止可能的死锁发生。

需要注意的是禁止Kernel APC并不会影响i/o操作,但是FastMutex及GauardedMutex不止是禁止Kernel APC,还会禁止Special APC,这样的话i/o操作是不允许的,因为i/o完成例程是在Special APC中做的。这些细微差别在使用中必须考虑到。

LDM & ldmtool

在Windows系统中创建了一个带区动态卷,其实就是用两个磁盘以RAID0方式整合也一个大磁盘。但启动进入Ubuntu (14.04) 系统后并不能正确识别。下图是我在测试系统中创建了一个带区卷E:(由2个盘构成)和一个RAID5卷F:(由4个盘构成):

LDM-RAID-Volumes

这两个由Windows系统创建的动态卷,是LDM格式(Logical Disk Manager),Linux系统中不能正确识别。最初我尝试用mdadm强制加载带区卷,分别试了64K及128K chunk,但全部无效,尝试办法如下:
root@ubuntu:~# mdadm --build /dev/md0 --level=0 --chunk=128 --raid-devices=2 /dev/sd[cd]3
mdadm: array /dev/md0 built and started.
root@ubuntu:~# mdadm --detail /dev/md0
/dev/md0:
        Version :
  Creation Time : Thu Jun 25 16:08:11 2015
     Raid Level : raid0
     Array Size : 41680640 (39.75 GiB 42.68 GB)
   Raid Devices : 2
  Total Devices : 2

          State : clean
Active Devices : 2
Working Devices : 2
Failed Devices : 0
  Spare Devices : 0

     Chunk Size : 128K

    Number   Major   Minor   RaidDevice State
       0       8       35        0      active sync   /dev/sdc3
       1       8       51        1      active sync   /dev/sdd3
root@ubuntu:~# tune2fs -l /dev/md0
tune2fs 1.42.9 (4-Feb-2014)
tune2fs: Bad magic number in super-block while trying to open /dev/md0
Couldn't find valid filesystem superblock.
root@ubuntu:~# mdadm -S /dev/md0
mdadm: stopped /dev/md0
root@ubuntu:~# mdadm --build /dev/md0 --level=0 --chunk=128 --raid-devices=2 /dev/sd[dc]3
mdadm: array /dev/md0 built and started.
root@ubuntu:~# mdadm --detail /dev/md0
/dev/md0:
        Version :
  Creation Time : Thu Jun 25 16:08:57 2015
     Raid Level : raid0
     Array Size : 41680640 (39.75 GiB 42.68 GB)
   Raid Devices : 2
  Total Devices : 2

          State : clean
Active Devices : 2
Working Devices : 2
Failed Devices : 0
  Spare Devices : 0

     Chunk Size : 128K

    Number   Major   Minor   RaidDevice State
       0       8       51        0      active sync   /dev/sdd3
       1       8       35        1      active sync   /dev/sdc3
root@ubuntu:~# tune2fs -l /dev/md0
tune2fs 1.42.9 (4-Feb-2014)
tune2fs: Bad magic number in super-block while trying to open /dev/md0
Couldn't find valid filesystem superblock.
root@ubuntu:~# mdadm -S /dev/md0
mdadm: stopped /dev/md0
root@ubuntu:~#

后来几经搜索,发现解决方案原来很简单,只需要安装ldmtool。

root@ubuntu:~# parted -l
Model: VMware, VMware Virtual S (scsi)
Disk /dev/sda: 85.9GB
Sector size (logical/physical): 512B/512B
Partition Table: msdos

Number  Start   End     Size    Type     File system     Flags
1      1049kB  1000MB  999MB   primary  ext2            boot
2      1000MB  81.0GB  80.0GB  primary  xfs
3      81.0GB  85.9GB  4898MB  primary  linux-swap(v1)


Model: VMware, VMware Virtual S (scsi)
Disk /dev/sdb: 42.9GB
Sector size (logical/physical): 512B/512B
Partition Table: msdos

Number  Start   End     Size    Type     File system  Flags
1      1049kB  42.9GB  42.9GB  primary  ext4


Model: VMware, VMware Virtual S (scsi)
Disk /dev/sdc: 21.5GB
Sector size (logical/physical): 512B/512B
Partition Table: gpt

Number  Start   End     Size    File system  Name                          Flags
1      17.4kB  1066kB  1049kB               LDM metadata partition
2      1066kB  134MB   133MB                Microsoft reserved partition  msftres
3      134MB   21.5GB  21.3GB               LDM data partition


Model: VMware, VMware Virtual S (scsi)
Disk /dev/sdd: 21.5GB
Sector size (logical/physical): 512B/512B
Partition Table: gpt

Number  Start   End     Size    File system  Name                          Flags
1      17.4kB  1066kB  1049kB               LDM metadata partition
2      1066kB  134MB   133MB                Microsoft reserved partition  msftres
3      134MB   21.5GB  21.3GB               LDM data partition


Model: VMware, VMware Virtual S (scsi)
Disk /dev/sde: 64.4GB
Sector size (logical/physical): 512B/512B
Partition Table: gpt

Number  Start   End     Size    File system  Name                          Flags
1      17.4kB  1066kB  1049kB               LDM metadata partition
2      1066kB  134MB   133MB                Microsoft reserved partition  msftres
3      134MB   64.4GB  64.3GB               LDM data partition


Model: VMware, VMware Virtual S (scsi)
Disk /dev/sdf: 64.4GB
Sector size (logical/physical): 512B/512B
Partition Table: gpt

Number  Start   End     Size    File system  Name                          Flags
1      17.4kB  1066kB  1049kB               LDM metadata partition
2      1066kB  134MB   133MB                Microsoft reserved partition  msftres
3      134MB   64.4GB  64.3GB               LDM data partition


Model: VMware, VMware Virtual S (scsi)
Disk /dev/sdg: 64.4GB
Sector size (logical/physical): 512B/512B
Partition Table: gpt

Number  Start   End     Size    File system  Name                          Flags
1      17.4kB  1066kB  1049kB               LDM metadata partition
2      1066kB  134MB   133MB                Microsoft reserved partition  msftres
3      134MB   64.4GB  64.3GB               LDM data partition


Model: VMware, VMware Virtual S (scsi)
Disk /dev/sdh: 64.4GB
Sector size (logical/physical): 512B/512B
Partition Table: gpt

Number  Start   End     Size    File system  Name                          Flags
1      17.4kB  1066kB  1049kB               LDM metadata partition
2      1066kB  134MB   133MB                Microsoft reserved partition  msftres
3      134MB   64.4GB  64.3GB               LDM data partition

root@ubuntu:~# ldmtool scan
[
  "3ffea520-1b24-11e5-8565-000c293dbd64"
]
root@ubuntu:~# ldmtool show diskgroup 3ffea520-1b24-11e5-8565-000c293dbd64
{
  "name" : "WIN-FB20IXK90MU-Dg0",
  "guid" : "3ffea520-1b24-11e5-8565-000c293dbd64",
  "volumes" : [
    "Volume2",
    "Volume1"
  ],
  "disks" : [
    "Disk1",
    "Disk2",
    "Disk3",
    "Disk4",
    "Disk6",
    "Disk5"
  ]
}
root@ubuntu:~# ldmtool show volume 3ffea520-1b24-11e5-8565-000c293dbd64 Volume1
{
  "name" : "Volume1",
  "type" : "RAID5",
  "size" : 376688640,
  "chunk-size" : 128,
  "hint" : "F:",
  "partitions" : [
    "Disk1-01",
    "Disk2-01",
    "Disk3-01",
    "Disk4-01"
  ]
}
root@ubuntu:~# ldmtool show volume 3ffea520-1b24-11e5-8565-000c293dbd64 Volume2
{
  "name" : "Volume2",
  "type" : "striped",
  "size" : 83353600,
  "chunk-size" : 128,
  "hint" : "E:",
  "partitions" : [
    "Disk5-01",
    "Disk6-01"
  ]
}
root@ubuntu:~# ldmtool show partition 3ffea520-1b24-11e5-8565-000c293dbd64 Disk1-01
{
  "name" : "Disk1-01",
  "start" : 2014,
  "size" : 125562880,
  "disk" : "Disk1"
}
root@ubuntu:~# ldmtool show partition 3ffea520-1b24-11e5-8565-000c293dbd64 Disk2-01
{
  "name" : "Disk2-01",
  "start" : 2014,
  "size" : 125562880,
  "disk" : "Disk2"
}
root@ubuntu:~# ldmtool show partition 3ffea520-1b24-11e5-8565-000c293dbd64 Disk3-01
{
  "name" : "Disk3-01",
  "start" : 2014,
  "size" : 125562880,
  "disk" : "Disk3"
}
root@ubuntu:~# ldmtool show partition 3ffea520-1b24-11e5-8565-000c293dbd64 Disk4-01
{
  "name" : "Disk4-01",
  "start" : 2014,
  "size" : 125562880,
  "disk" : "Disk4"
}
root@ubuntu:~# ldmtool show partition 3ffea520-1b24-11e5-8565-000c293dbd64 Disk5-01
{
  "name" : "Disk5-01",
  "start" : 2014,
  "size" : 41676800,
  "disk" : "Disk5"
}
root@ubuntu:~# ldmtool show partition 3ffea520-1b24-11e5-8565-000c293dbd64 Disk6-01
{
  "name" : "Disk6-01",
  "start" : 2014,
  "size" : 41676800,
  "disk" : "Disk6"
}

用ldmtool解析并创建动态卷只需要一个简单命令

root@ubuntu:~# ldmtool create all
[
  "ldm_vol_WIN-FB20IXK90MU-Dg0_Volume2",
  "ldm_vol_WIN-FB20IXK90MU-Dg0_Volume1"
]

root@ubuntu:~# ls -l /dev/mapper/
total 0
crw------- 1 root root 10, 236 Jun 25 16:29 control
lrwxrwxrwx 1 root root       7 Jun 25 16:29 ldm_part_WIN-FB20IXK90MU-Dg0_Disk1-01 -> ../dm-1
lrwxrwxrwx 1 root root       7 Jun 25 16:29 ldm_part_WIN-FB20IXK90MU-Dg0_Disk2-01 -> ../dm-2
lrwxrwxrwx 1 root root       7 Jun 25 16:29 ldm_part_WIN-FB20IXK90MU-Dg0_Disk3-01 -> ../dm-3
lrwxrwxrwx 1 root root       7 Jun 25 16:29 ldm_part_WIN-FB20IXK90MU-Dg0_Disk4-01 -> ../dm-4
lrwxrwxrwx 1 root root       7 Jun 25 16:29 ldm_vol_WIN-FB20IXK90MU-Dg0_Volume1 -> ../dm-5
lrwxrwxrwx 1 root root       7 Jun 25 16:29 ldm_vol_WIN-FB20IXK90MU-Dg0_Volume2 -> ../dm-0

root@ubuntu:~# ls -l /dev/dm*
brw-rw---- 1 root disk 252, 0 Jun 25 16:29 /dev/dm-0
brw-rw---- 1 root disk 252, 1 Jun 25 16:29 /dev/dm-1
brw-rw---- 1 root disk 252, 2 Jun 25 16:29 /dev/dm-2
brw-rw---- 1 root disk 252, 3 Jun 25 16:29 /dev/dm-3
brw-rw---- 1 root disk 252, 4 Jun 25 16:29 /dev/dm-4
brw-rw---- 1 root disk 252, 5 Jun 25 16:29 /dev/dm-5
root@ubuntu:~#
root@ubuntu:~# tune2fs -l /dev/dm-0
tune2fs 1.42.9 (4-Feb-2014)
Filesystem volume name:   <none>
Last mounted on:          <not available>
Filesystem UUID:          22a45231-ede2-4c60-9c93-ee841ae2f2ee
Filesystem magic number:  0xEF53
Filesystem revision #:    1 (dynamic)
Filesystem features:      has_journal ext_attr resize_inode dir_index filetype extent flex
_bg sparse_super large_file huge_file uninit_bg dir_nlink extra_isize
Filesystem flags:         signed_directory_hash
Default mount options:    user_xattr acl
Filesystem state:         clean
Errors behavior:          Continue
Filesystem OS type:       Linux
Inode count:              2605056
Block count:              10419200
Reserved block count:     520960
Free blocks:              10210674
Free inodes:              2605045
First block:              0
Block size:               4096
Fragment size:            4096
Reserved GDT blocks:      1021
Blocks per group:         32768
Fragments per group:      32768
Inodes per group:         8192
Inode blocks per group:   512
RAID stride:              16
RAID stripe width:        32
Flex block group size:    16
Filesystem created:       Thu Jun 25 15:58:29 2015
Last mount time:          n/a
Last write time:          Thu Jun 25 15:58:31 2015
Mount count:              0
Maximum mount count:      -1
Last checked:             Thu Jun 25 15:58:29 2015
Check interval:           0 (<none>)
Lifetime writes:          132 MB
Reserved blocks uid:      0 (user root)
Reserved blocks gid:      0 (group root)
First inode:              11
Inode size:               256
Required extra isize:     28
Desired extra isize:      28
Journal inode:            8
Default directory hash:   half_md4
Directory Hash Seed:      a472f2f6-a27b-4362-91e2-dce4ad48a700
Journal backup:           inode blocks
root@ubuntu:~# tune2fs -l /dev/dm-5
tune2fs 1.42.9 (4-Feb-2014)
Filesystem volume name:   <none>
Last mounted on:          <not available>
Filesystem UUID:          f0655359-b2e0-4732-8df0-b0ab24918ec1
Filesystem magic number:  0xEF53
Filesystem revision #:    1 (dynamic)
Filesystem features:      has_journal ext_attr resize_inode dir_index filetype extent flex
_bg sparse_super large_file huge_file uninit_bg dir_nlink extra_isize
Filesystem flags:         signed_directory_hash
Default mount options:    user_xattr acl
Filesystem state:         clean
Errors behavior:          Continue
Filesystem OS type:       Linux
Inode count:              11771904
Block count:              47086080
Reserved block count:     2354304
Free blocks:              46299313
Free inodes:              11771893
First block:              0
Block size:               4096
Fragment size:            4096
Reserved GDT blocks:      1012
Blocks per group:         32768
Fragments per group:      32768
Inodes per group:         8192
Inode blocks per group:   512
RAID stride:              16
RAID stripe width:        48
Flex block group size:    16
Filesystem created:       Thu Jun 25 15:13:55 2015
Last mount time:          n/a
Last write time:          Thu Jun 25 15:14:21 2015
Mount count:              6
Maximum mount count:      -1
Last checked:             Thu Jun 25 15:14:21 2015
Check interval:           0 (<none>)
Lifetime writes:          133 MB
Reserved blocks uid:      0 (user root)
Reserved blocks gid:      0 (group root)
First inode:              11
Inode size:               256
Required extra isize:     28
Desired extra isize:      28
Journal inode:            8
Default directory hash:   half_md4
Directory Hash Seed:      88ddd8a2-506a-4e9b-9109-ce9c171b2607
Journal backup:           inode blocks

至此就可以自由访问 /dev/dm-0和/dev/dm-5两个设备卷了。不过还要说的是,ldmtool是通过DM (Device Mapper) 产生的RAID卷(和LVM使用的是同样的机制),而不是最常用的MD方式(即Multiple Devices),特别对于RAID5/RAID6此类的数据卷,MD Raid做了较多的优化,除性能更好之外,稳定性应该也好于Device Mapper方式。

参考阅读:
1,http://manpages.ubuntu.com/manpages/trusty/man1/ldmtool.1.html
2,https://github.com/mdbooth/libldm
3,https://notesbytom.wordpress.com/2012/06/14/linux-support-for-windows-dynamic-disks/
4,http://stackoverflow.com/questions/8427372/windows-spanned-disks-ldm-restoration-with-linux/22108676#22108676 <ldmtool出现之前的办法 Smile>

脑残的PsSetCreateProcessNotifyRoutine

Vista之前的系统,XP及Server 2003只有8个坑,就是说前8个调用成功之后,后续的所有调用都会失败,看来通过注册callback的方式来监控进程非常不靠谱。

从内核角度来说,尽量少的调用不仅提高系统响应时间,也降低了不靠谱的驱动所可能带来问题,但限制为区区8个总不是解决办法吧,比如Symantec家的SEP动不动就占3个,腾讯家(Tencent)更牛B,安全管家要用几个,QQ也要用,就连浏览器也要搞个驱动并也要注册个进程创建的回调通知,这也太让人无语了吧。

好在从Vista开始,这个限制从8个变成了64个,翻了整整8倍,呵呵,这下够用了?!

下面是一个典型的XP系统实例,8个回调会被占满了,后起的驱动们只能想其它办法了:

0: kd> x nt!PspCreateProcessNotifyRoutine 80564a40 nt!PspCreateProcessNotifyRoutine = <no type information> 0: kd> dd 80564a40 80564a40  e101894f e15e4eb7 e2031ff6 e1e8b56f 80564a50  e20a8d9f e51c641f e52246b7 e5ad82cf 80564a60  00000008 00000000 86a85d58 8656fac0 80564a70  86b9b5a0 86be5208 86be45a0 00000000 80564a80  00000000 00000000 00000000 00000000 80564a90  00000000 00000000 00000000 00000000 80564aa0  00000000 6d6f7441 00000000 00000001 80564ab0  00000000 00000000 00000000 00000000 0: kd> .for (r $t0=0; $t0 < 8; r $t0=$t0+1) {r $t1 = poi($t0 * 4 + nt!PspCreateProcessNotifyRoutine); .if ($t1 == 0) {.continue;}; r $t1 = $t1 & 0xFFFFFFF8; r $t1 = poi($t1 + 4); r $t0; r $t1; u $t1; ln $t1;} $t0=00000000 $t1=f7310790 TsFltMgr+0x10790: f7310790 55              push    ebp f7310791 8bec            mov     ebp,esp f7310793 83e4f8          and     esp,0FFFFFFF8h f7310796 83ec14          sub     esp,14h f7310799 53              push    ebx f731079a 56              push    esi f731079b 8b35a07b31f7    mov     esi,dword ptr [TsFltMgr+0x17ba0 (f7317ba0)] f73107a1 57              push    edi $t0=00000001 $t1=f771a788 ghcore+0x3788: f771a788 8bff            mov     edi,edi f771a78a 55              push    ebp f771a78b 8bec            mov     ebp,esp f771a78d 33c0            xor     eax,eax f771a78f 390564b471f7    cmp     dword ptr [ghcore+0x4464 (f771b464)],eax f771a795 7508            jne     ghcore+0x379f (f771a79f) f771a797 39056cb471f7    cmp     dword ptr [ghcore+0x446c (f771b46c)],eax f771a79d 741a            je      ghcore+0x37b9 (f771a7b9) $t0=00000002 $t1=ed1a3b00 SYMEVENT+0xcb00: ed1a3b00 8bff            mov     edi,edi ed1a3b02 55              push    ebp ed1a3b03 8bec            mov     ebp,esp ed1a3b05 51              push    ecx ed1a3b06 53              push    ebx ed1a3b07 8b5d08          mov     ebx,dword ptr [ebp+8] ed1a3b0a 56              push    esi ed1a3b0b 33f6            xor     esi,esi $t0=00000003 $t1=ecfeb120 QQFrmMgr+0xe120: ecfeb120 8bff            mov     edi,edi ecfeb122 55              push    ebp ecfeb123 8bec            mov     ebp,esp ecfeb125 51              push    ecx ecfeb126 51              push    ecx ecfeb127 53              push    ebx ecfeb128 8b5d0c          mov     ebx,dword ptr [ebp+0Ch] ecfeb12b 56              push    esi $t0=00000004 $t1=ecd098a0 SysPlant+0x78a0: ecd098a0 55              push    ebp ecd098a1 8bec            mov     ebp,esp ecd098a3 83e4f8          and     esp,0FFFFFFF8h ecd098a6 51              push    ecx ecd098a7 53              push    ebx ecd098a8 56              push    esi ecd098a9 57              push    edi ecd098aa a12480d1ec      mov     eax,dword ptr [SysPlant+0x16024 (ecd18024)] $t0=00000005 $t1=866b41d0 866b41d0 90              nop 866b41d1 90              nop 866b41d2 e9e7c4f466      jmp     QMUdisk+0x26be (ed6006be) 866b41d7 90              nop 866b41d8 90              nop 866b41d9 90              nop 866b41da 90              nop 866b41db 90              nop $t0=00000006 $t1=ecaf8070 BHDrvx86+0x6c070: ecaf8070 8bff            mov     edi,edi ecaf8072 55              push    ebp ecaf8073 8bec            mov     ebp,esp ecaf8075 807d1000        cmp     byte ptr [ebp+10h],0 ecaf8079 750f            jne     BHDrvx86+0x6c08a (ecaf808a) ecaf807b 8b450c          mov     eax,dword ptr [ebp+0Ch] ecaf807e 8b0d48e0b8ec    mov     ecx,dword ptr [BHDrvx86+0x102048 (ecb8e048)] ecaf8084 50              push    eax $t0=00000007 $t1=ee461eba dekfs+0x4eba: ee461eba 8bff            mov     edi,edi ee461ebc 55              push    ebp ee461ebd 8bec            mov     ebp,esp ee461ebf 83ec10          sub     esp,10h ee461ec2 807d1000        cmp     byte ptr [ebp+10h],0 ee461ec6 53              push    ebx ee461ec7 56              push    esi ee461ec8 57              push    edi

Server 2008 X64系统已经可以支持64个注册回调了,这个限制是硬编码在

函数PsSetCreateProcessNotifyRoutine中的:

0: kd> u nt!PspSetCreateProcessNotifyRoutine nt!PspSetCreateProcessNotifyRoutine: fffff800`01c3fab0 48895c2408      mov     qword ptr [rsp+8],rbx fffff800`01c3fab5 48896c2410      mov     qword ptr [rsp+10h],rbp fffff800`01c3faba 4889742418      mov     qword ptr [rsp+18h],rsi …… fffff800`01c3fad4 bb01000000      mov     ebx,1 …… fffff800`01c3fb3e e89d3cf1ff      call    nt!ExDereferenceCallBackBlock (fffff800`01b537e0) fffff800`01c3fb43 4403e3          add     r12d,ebx fffff800`01c3fb46 4183fc40        cmp     r12d,40h fffff800`01c3fb4a 72ae            jb      nt!PspSetCreateProcessNotifyRoutine+0x4a (fffff800`01c3fafa) fffff800`01c3fb4c 66019fb4010000  add     word ptr [rdi+1B4h],bx fffff800`01c3fb53 7518            jne     nt!PspSetCreateProcessNotifyRoutine+0xbd (fffff800`01c3fb6d) ……

此系统上并未安装腾讯管家等类似的防毒程序,但是64个注册回调已有6个被使用,差不多全是M$自家给占用的:

0: kd> x nt!PspCreateProcessNotifyRoutine fffff800`019f8ee0 nt!PspCreateProcessNotifyRoutine = <no type information> 0: kd> dq fffff800`019f8ee0 fffff800`019f8ee0  fffff880`00008c4f fffff880`006d7acf fffff800`019f8ef0  fffff880`006f694f fffff880`007ff5cf fffff800`019f8f00  fffff880`098168af fffff880`0a30216f fffff800`019f8f10  00000000`00000000 00000000`00000000 fffff800`019f8f20  00000000`00000000 00000000`00000000 fffff800`019f8f30  00000000`00000000 00000000`00000000 fffff800`019f8f40  00000000`00000000 00000000`00000000 fffff800`019f8f50  00000000`00000000 00000000`00000000 0: kd> .for (r $t0=0; $t0 < 0x40; r $t0=$t0+1) {r $t1=poi($t0 * 8 + nt!PspCreateProcessNotifyRoutine); .if ($t1 == 0) {.continue}; r $t0; r $t1 = $t1 & 0xFFFFFFFFFFFFFFF0; r $t1 = poi($t1 + 8); r $t1; u $t1; ln $t1;} $t0=0000000000000000 $t1=fffff8000188dbd0 nt!ViCreateProcessCallback: fffff800`0188dbd0 fff3            push    rbx fffff800`0188dbd2 4883ec40        sub     rsp,40h fffff800`0188dbd6 833dc3a2160000  cmp     dword ptr [nt!ViVerifierEnabled (fffff800`019f7ea0)],0 fffff800`0188dbdd 0f8583b6fcff    jne     nt! ?? ::FNODOBFM::`string'+0x21140 (fffff800`01859266) fffff800`0188dbe3 4883c440        add     rsp,40h fffff800`0188dbe7 5b              pop     rbx fffff800`0188dbe8 c3              ret fffff800`0188dbe9 90              nop (fffff800`0188dbd0)   nt!ViCreateProcessCallback   |  (fffff800`0188dbf0)   nt!IopAllocateFileObjectExtension Exact matches: nt!ViCreateProcessCallback = <no type information> $t0=0000000000000001 $t1=fffffa6000e52ffc ksecdd!CredMarshalTargetInfo+0x8cc: fffffa60`00e52ffc 4883ec28        sub     rsp,28h fffffa60`00e53000 488364244800    and     qword ptr [rsp+48h],0 fffffa60`00e53006 4584c0          test    r8b,r8b fffffa60`00e53009 488bc2          mov     rax,rdx fffffa60`00e5300c 7546            jne     ksecdd!CredMarshalTargetInfo+0x924 (fffffa60`00e53054) fffffa60`00e5300e 488d542448      lea     rdx,[rsp+48h] fffffa60`00e53013 488bc8          mov     rcx,rax fffffa60`00e53016 ff15dc00feff    call    qword ptr [ksecdd!BCryptDestroySecret+0x191c8 (fffffa60`00e330f8)] (fffffa60`00e52730)   ksecdd!CredMarshalTargetInfo+0x8cc   |  (fffffa60`00e535f4)   ksecdd!AcquireCredentialsHandleW $t0=0000000000000002 $t1=fffffa600106f830 tcpip+0x69830: fffffa60`0106f830 4d85c0          test    r8,r8 fffffa60`0106f833 7405            je      tcpip+0x6983a (fffffa60`0106f83a) fffffa60`0106f835 e9860b0000      jmp     tcpip+0x6a3c0 (fffffa60`010703c0) fffffa60`0106f83a e911130000      jmp     tcpip+0x6ab50 (fffffa60`01070b50) fffffa60`0106f83f 90              nop fffffa60`0106f840 90              nop fffffa60`0106f841 90              nop fffffa60`0106f842 90              nop $t0=0000000000000003 $t1=fffffa600074e06c CI!I_PEProcessNotify: fffffa60`0074e06c 4584c0          test    r8b,r8b fffffa60`0074e06f 7528            jne     CI!I_PEProcessNotify+0x2d (fffffa60`0074e099) fffffa60`0074e071 53              push    rbx fffffa60`0074e072 4883ec20        sub     rsp,20h fffffa60`0074e076 488bda          mov     rbx,rdx fffffa60`0074e079 ff15f1d1f5ff    call    qword ptr [CI!_imp_IoGetCurrentProcess (fffffa60`006ab270)] fffffa60`0074e07f 488bc8          mov     rcx,rax fffffa60`0074e082 ff1530d0f5ff    call    qword ptr [CI!_imp_PsIsProtectedProcess (fffffa60`006ab0b8)] (fffffa60`0074e06c)   CI!I_PEProcessNotify   |  (fffffa60`0074e0a0)   CI!RSA32Alloc Exact matches: CI!I_PEProcessNotify = <no type information> $t0=0000000000000004 $t1=fffffa6002fac964 dekfs+0x6964: fffffa60`02fac964 48895c2408      mov     qword ptr [rsp+8],rbx fffffa60`02fac969 48896c2410      mov     qword ptr [rsp+10h],rbp fffffa60`02fac96e 56              push    rsi fffffa60`02fac96f 57              push    rdi fffffa60`02fac970 4154            push    r12 fffffa60`02fac972 4155            push    r13 fffffa60`02fac974 4156            push    r14 fffffa60`02fac976 4883ec50        sub     rsp,50h $t0=0000000000000005 $t1=fffffa600c545b2d peauth!I_PEProcessNotify: fffffa60`0c545b2d 48895c2408      mov     qword ptr [rsp+8],rbx fffffa60`0c545b32 57              push    rdi fffffa60`0c545b33 4883ec20        sub     rsp,20h fffffa60`0c545b37 e8cc6bfdff      call    peauth!WARBIRD::Stub_VerifyVerifierCheckSum (fffffa60`0c51c708) fffffa60`0c545b3c 90              nop fffffa60`0c545b3d 360000          add     byte ptr ss:[rax],al fffffa60`0c545b40 00fd            add     ch,bh fffffa60`0c545b42 140a            adc     al,0Ah (fffffa60`0c545b2d)   peauth!I_PEProcessNotify   |  (fffffa60`0c545fe1)   peauth!PEReturnCertChain Exact matches: peauth!I_PEProcessNotify = <no type information>

Security-Enhanced CRT,边界与潜规则

Windows Security-Enhanced CRT 相关函数(即_s后辍函数)并不是对老函数的直接替代,其中不仅有更严格的参数检查,还会有buffer的重置操作等,使用的时候要格外小心,一定要仔细阅读MSDN描述或者VC runtime源码 (以Visual Studio 2008为例: C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\crt\src)。

下面不妨测试3个代码片段:

测试代码【C1】:

void main() {
    char dst1[8];
    memcpy(dst1, NULL, 0);
    memcpy(NULL, NULL, 0);
}

测试代码【C2】:

void main() {

    char dst1[8];

    memcpy_s(dst1, 8, NULL, 0);
    memcpy_s(NULL, 0, NULL, 0);
    char *dst2 = (char *)malloc(0);
    if (dst2) {
        memcpy_s(dst2, 0, NULL, 0);
    free(dst2);
    }
}

测试代码【C3】:

void main() {

    char dst1[8];

    memcpy_s(NULL, 0, dst1, 8);

    char *dst2 = (char *)malloc(0);
    if (dst2) {
        memcpy_s(dst2, 0, dst1, 8);
        free(dst2);
    }
}

这几段代码实际上什么也没有做,只是参数有变化而已,但测试中会发现【C3】会导致程序崩溃。具体原因就是因为memcpy_s进行了更严格的参数检测,可对参考memcpy_s的源码,引用自Visual Studio 2008 runtime source memcpy_s.c:

/***
*  memcpy_s.c - contains memcpy_s routine
*
*       Copyright (c) Microsoft Corporation. All rights reserved.
*
*  Purpose:
*       memcpy_s() copies a source memory buffer to a destination buffer.
*       Overlapping buffers are not treated specially, so propagation may occur.
*
*******************************************************************/

#include <cruntime.h>
#include <string.h>
#include <internal.h>

/***
*  memcpy_s - Copy source buffer to destination buffer
*
*  Purpose:
*       memcpy_s() copies a source memory buffer to a destination memory buffer.
*       This routine does NOT recognize overlapping buffers, and thus can lead
*       to propagation.
*
*       For cases where propagation must be avoided, memmove_s() must be used.
*
*  Entry:
*       void *dst = pointer to destination buffer
*       size_t sizeInBytes = size in bytes of the destination buffer
*       const void *src = pointer to source buffer
*       size_t count = number of bytes to copy
*
*  Exit:
*       Returns 0 if everything is ok, else return the error code.
*
*  Exceptions:
*       Input parameters are validated. Refer to the validation section of the function.
*       On error, the error code is returned and the destination buffer is zeroed.
*
*******************************************************************/

errno_t __cdecl memcpy_s(
    void * dst,
    size_t sizeInBytes,
    const void * src,
    size_t count
)
{
    if (count == 0)
    {
        /* nothing to do */
        return 0;
    }

    /* validation section */
    _VALIDATE_RETURN_ERRCODE(dst != NULL, EINVAL);
    if (src == NULL || sizeInBytes < count)
    {
        /* zeroes the destination buffer */
        memset(dst, 0, sizeInBytes);

        _VALIDATE_RETURN_ERRCODE(src != NULL, EINVAL);
        _VALIDATE_RETURN_ERRCODE(sizeInBytes >= count, ERANGE);
        /* useless, but prefast is confused */
        return EINVAL;
    }

    memcpy(dst, src, count);
    return 0;
}

参考阅读:

1, _snwprintf_s的潜规则https://blog.dynox.cn/?p=791
2,   Security Features in the CRT:
      https://msdn.microsoft.com/zh-cn/library/vstudio/8ef0s5kh(v=vs.110).aspx