0x00 漏洞描述
3 月 10 日:微软发布安全通告 ADV200005,称 SMBv3 协议在处理某些请求的方式中存在代码执行漏洞,并提供了缓解措施。
3 月 12 日:微软正式发布 CVE-2020-0796 安全通告和漏洞修复补丁。
CVE-2020-0796是Windows 10 1903/1909的新SMB3压缩功能中的错误。
SMB协议版本3.1.1引入了一种功能,即客户端或服务器可以发布压缩功能,并有选择地压缩SMB3消息。
使用此功能协商会话后,客户端或服务器可以选择压缩某些SMB消息。为此,整个SMB包会被压缩,并且生成的文件头会被前置。此标头是一个小的(16字节)结构:魔术值,未压缩的数据大小,使用的压缩算法和偏移值。
CVE-2020-0796是由该偏移量大小中缺少边界检查导致的,该偏移量大小会被直接传递给多个子例程。传入较大的值将导致缓冲区溢出,并使内核崩溃。通过进一步的工作,可以将其开发为远程代码执行类型等的漏洞利用程序。
漏洞影响版本:
Windows 10 Version 1903 for 32-bit Systems
Windows 10 Version 1903 for x64-based Systems
Windows 10 Version 1903 for ARM64-based Systems
Windows Server, Version 1903 (Server Core installation)
Windows 10 Version 1909 for 32-bit Systems
Windows 10 Version 1909 for x64-based Systems
Windows 10 Version 1909 for ARM64-based Systems
Windows Server, Version 1909 (Server Core installation)
0x01 漏洞分析
1.分析环境搭建及配置
配置目标机(虚拟机): Windows版本:1909,未安装安全更新补丁(KB4551762)
查看其ip:
配置内核调试,管理员权限启动cmd,输入以下命令
配置内核调试主机(宿主机):
配置符号表:
配置完成后,重启虚拟机即可开始调试内核
2.打开Wireshark,进行抓包
3.触发蓝屏,进行调试。
(将目标机关闭防火墙)
采用蓝屏poc:
https://github.com/maxpl0it/Unauthenticated-CVE-2020-0796-PoC/blob/master/crash.py
触发蓝屏:虚拟机成功挂掉
Wireshark抓包如下:
使用!analyze -v
KEY_VALUES_STRING: 1
Key : Analysis.CPU.Sec
Value: 5
Key : Analysis.DebugAnalysisProvider.CPP
Value: Create: 8007007e on DESKTOP-JT38GVK
Key : Analysis.DebugData
Value: CreateObject
Key : Analysis.DebugModel
Value: CreateObject
Key : Analysis.Elapsed.Sec
Value: 61
Key : Analysis.Memory.CommitPeak.Mb
Value: 61
Key : Analysis.System
Value: CreateObject
ADDITIONAL_XML: 1
BUGCHECK_CODE: 50
BUGCHECK_P1: ffffc80d40cf0c1f
BUGCHECK_P2: 0
BUGCHECK_P3: fffff80414c3cee7
BUGCHECK_P4: 2
READ_ADDRESS: ffffc80d40cf0c1f Nonpaged pool
MM_INTERNAL_CODE: 2
PROCESS_NAME: System
TRAP_FRAME: ffffc68f9c9b2b90 -- (.trap 0xffffc68f9c9b2b90)
NOTE: The trap frame does not contain all registers.
Some register values may be zeroed or incorrect.
rax=ffffc68f9c9b2d58 rbx=0000000000000000 rcx=ffffc68f9c9b2d50
rdx=0000000000000001 rsi=0000000000000000 rdi=0000000000000000
rip=fffff80414c3cee7 rsp=ffffc68f9c9b2d20 rbp=ffffc68f9c9b2d70
r8=0000000000000000 r9=0000000000000033 r10=fffff80414c3ce90
r11=0000000000000000 r12=0000000000000000 r13=0000000000000000
r14=0000000000000000 r15=0000000000000000
iopl=0 nv up ei pl zr na po nc
nt!RtlDecompressBufferLZNT1+0x57:
fffff804`14c3cee7 0fb71e movzx ebx,word ptr [rsi] ds:00000000`00000000=????
Resetting default scope
STACK_TEXT:
ffffc68f`9c9b2148 fffff804`148ad492 : ffffc80d`40cf0c1f 00000000`00000003 ffffc68f`9c9b22b0 fffff804`1472bf20 : nt!DbgBreakPointWithStatus
ffffc68f`9c9b2150 fffff804`148acb82 : fffff804`00000003 ffffc68f`9c9b22b0 fffff804`147d7ce0 00000000`00000050 : nt!KiBugCheckDebugBreak+0x12
ffffc68f`9c9b21b0 fffff804`147c3917 : fffff804`14a681b8 fffff804`148d6fe5 ffffc80d`40cf0c1f ffffc80d`40cf0c1f : nt!KeBugCheck2+0x952
ffffc68f`9c9b28b0 fffff804`14807b0a : 00000000`00000050 ffffc80d`40cf0c1f 00000000`00000000 ffffc68f`9c9b2b90 : nt!KeBugCheckEx+0x107
ffffc68f`9c9b28f0 fffff804`146d01df : ffffc80c`43764000 00000000`00000000 00000000`00000000 ffffc80d`40cf0c1f : nt!MiSystemFault+0x18fafa
ffffc68f`9c9b29f0 fffff804`147d169a : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000054 : nt!MmAccessFault+0x34f
ffffc68f`9c9b2b90 fffff804`14c3cee7 : 00000000`00000000 00000000`00001100 ffffc80c`3feff8c0 00000000`00001000 : nt!KiPageFault+0x35a
ffffc68f`9c9b2d20 fffff804`1466e666 : ffffc80d`417bc04f ffffc80c`3feff8c0 ffffc80d`417bc04f fffff804`1466e5da : nt!RtlDecompressBufferLZNT1+0x57
ffffc68f`9c9b2db0 fffff804`1944e0bd : 00000000`00000002 00000000`00000033 00000000`ffffffff fffff804`00000000 : nt!RtlDecompressBufferEx2+0x66
ffffc68f`9c9b2e00 fffff804`194f7f41 : ffffc80c`00010020 ffffc80c`417bd150 00000000`00000001 00000000`ffffffff : srvnet!SmbCompressionDecompress+0xdd
ffffc68f`9c9b2e70 fffff804`194f699e : 00000000`00000000 ffffc80c`40cf06a0 00000000`00000002 ffffffff`ffffffff : srv2!Srv2DecompressData+0xe1
ffffc68f`9c9b2ed0 fffff804`19539a7f : ffffc80c`40cf06b0 ffffc80c`3fe31001 ffffc80c`3fe31000 fffff804`14739e00 : srv2!Srv2DecompressMessageAsync+0x1e
ffffc68f`9c9b2f00 fffff804`147c704e : ffffc68f`9c9b0028 ffffc68f`9ded7901 ffffffff`ee1e5d00 ffffc68f`9c9b2fd1 : srv2!RfspThreadPoolNodeWorkerProcessWorkItems+0x13f
ffffc68f`9c9b2f80 fffff804`147c700c : ffffc68f`9c9b2fd1 ffffc80c`3fe31040 ffffc68f`9c9b3000 fffff804`146c745e : nt!KxSwitchKernelStackCallout+0x2e
ffffc68f`9ded78f0 fffff804`146c745e : ffffc68f`9c9b2fd1 ffffc68f`9c9b3000 00000000`00000001 00000000`00000000 : nt!KiSwitchKernelStackContinue
ffffc68f`9ded7910 fffff804`146c725c : fffff804`19539940 ffffc80c`41fdd220 00000000`00000002 00000000`00000000 : nt!KiExpandKernelStackAndCalloutOnStackSegment+0x18e
ffffc68f`9ded79b0 fffff804`146c70d3 : 00000000`00000080 00000000`00000088 ffffc80c`3fe31040 ffffc68f`9ded7b00 : nt!KiExpandKernelStackAndCalloutSwitchStack+0xdc
ffffc68f`9ded7a20 fffff804`146c708d : fffff804`19539940 ffffc80c`41fdd220 ffffc80c`41fdd220 00000000`00000088 : nt!KeExpandKernelStackAndCalloutInternal+0x33
ffffc68f`9ded7a90 fffff804`195397d7 : ffffc80c`00000000 00000000`00000000 ffffb483`555434b0 00000000`00000000 : nt!KeExpandKernelStackAndCalloutEx+0x1d
ffffc68f`9ded7ad0 fffff804`14d174a7 : ffffc036`55f09c90 ffffc80c`3fe31040 00000203`64d9e500 00000000`00000000 : srv2!RfspThreadPoolNodeWorkerRun+0x117
ffffc68f`9ded7b30 fffff804`14737925 : ffffc80c`3fe31040 fffff804`14d17470 ffffb483`555434b0 00000000`00000000 : nt!IopThreadStart+0x37
ffffc68f`9ded7b90 fffff804`147cad5a : fffff804`139bd180 ffffc80c`3fe31040 fffff804`147378d0 00000000`00000001 : nt!PspSystemThreadStartup+0x55
ffffc68f`9ded7be0 00000000`00000000 : ffffc68f`9ded8000 ffffc68f`9ded1000 00000000`00000000 00000000`00000000 : nt!KiStartSystemThread+0x2a
SYMBOL_NAME: srvnet!SmbCompressionDecompress+dd
MODULE_NAME: srvnet
IMAGE_NAME: srvnet.sys
STACK_COMMAND: .thread ; .cxr ; kb
BUCKET_ID_FUNC_OFFSET: dd
FAILURE_BUCKET_ID: AV_R_INVALID_srvnet!SmbCompressionDecompress
OS_VERSION: 10.0.18362.1
BUILDLAB_STR: 19h1_release
OSPLATFORM_TYPE: x64
OSNAME: Windows 10
FAILURE_ID_HASH: {4320f3dd-f397-f147-9d97-e2ca1e080673}
Followup: MachineOwner
---------
查看栈回溯如下:
4.上ida
加载srv2.sys
查看Srv2DecompressMessageAsync函数,在该函数中调用了Srv2DecompressData函数
同时查看srv2DecompressMessageAsync函数的交叉引用
可以看到在Srv2ReceiveHandler中调用了它
而Srv2ReceiveHandler函数,是smb调用来进行数据包接收的,查看其反汇编,可以看到,smb协议根据protocolID来设置不同的处理函数,而1112364028即0x424D53FD,根据微软MS-SMB2协议文档,SMBCompression Transform Header的结构
查看Srv2ReceiveHandler函数,
1.SMB首先调用srv2!Srv2ReceiveHandler函数接收数据包,并根据ProtocolId设置对应的处理函数:
如果判断数据包中为压缩的数据(ProtocolID = 0xfc4d5342 即 0xFC,‘S’,‘M’,‘B’),则调用处置函数:Srv2DecompressMessageAsync函数。该函数继续调用Srv2DecompressData函数
继续跟进Srv2DecompressData函数,该函数进行buffer申请,然后调用SmbCompressDecompress进行解压缩。
buffer申请函数:SrvNetAllocateBuffer函数
而攻击者可以控制OriginalCompressedSegmentSize和offsetOrLength这两个参数。
在Srv2DecompressData函数中可以看到数据处理的部分:在进行buffer分配时,会调用SrvNetAllocateBuffer进行分配。但是在调用时,并未对OriginalCompressedSegmentSize和Offset/Length的长度进行任何检查,对二者相加的和也未进行安全检查,此处就存在一个整数溢出。
srv2!Srv2DecompressData函数调用位于srvnet模块中的SmbCompressionDecompress函数。而该函数又调用了ntoskrnl.exe函数中的nt!RtlDecompressBufferXpressLz函数,最后实际调用的是qmemcpy进行内存拷贝,于是就造成了溢出。
至此,漏洞原理已分析清楚,只要构造OriginalCompressedSegmentSize与Offset之和能产生整数溢出就有可能触发漏洞。
附录:
动态调试流程:
1.设置initial break重新启动
2.设置加载断点
3.停下后,在srv2DecompressData处下断点:
(经过多次试验!终于停下来了!)
4.单步跟踪: 单步跟踪来到srv2!Srv2DecompressData + 0x68地址处,往下继续执行,当执行到
mov rax, qword ptr [rsp+30h]处观察rsp+30处的内存可以看到正好是发送的构造数据包的头部
往下有调用SrvNetAllocateBuffer函数,根据函数调用原则,先将函数参数入栈再进行调用。
由前面反汇编可得,该SrvNetAllocateBuffer函数主要进行内存分配。其中rcx中存放的值恰好是OriginalCompressedSegmentSize与offset之和。(故上述根据反编译进行变量法分析的思路是正确的)
故缓冲区分配的大小为0x31
5.跟进分析SmbCompressionDecompress函数
该函数中,rcx为第一个参数,指定了使用的压缩算法的类型,rdx为第二个参数,指定了压缩数据存放的位置。
6.在nt!RtlDecompressBufferLZNT1处(即奔溃点)设置断点
(此处忘记截图了,借用一开始分析的图片:)
总结:大数据到小缓冲区于是造成了溢出。
0x02 漏洞利用
1.提权原理分析
原理:
漏洞存在于在srv2.sys驱动中,由于SMB没有正确处理压缩的数据包,在解压数据包的时候调用函数Srv2DecompressData处理压缩数据时候,对压缩数据头部压缩数据大小OriginalCompressedSegmentSize和其偏移Offset的没有检查其是否合法,导致其相加可分配较小的内存,后面调用SmbCompressionDecompress进行数据处理时候使用这片较小的内存可导致拷贝溢出或越界访问,而在执行本地程序的时候,可通过获取当前本地程序的token+0x40的偏移地址,通过发送压缩数据给SMB服务器,之后此偏移地址在解压缩数据时候拷贝的内核内存中,通过精心构造的内存布局在内核中修改token将权限提升。
(1)验证程序首先创建到SMS server的会话连接(记为session)。
(2)验证程序获取自身token数据结构中privilege成员在内核中的地址(记tokenAddr)。
(3)验证程序通过session发送畸形压缩数据(记为evilData)给SMB server触发漏洞。其中,evilData包含tokenAddr、权限数据、溢出占位数据。
(4) SMS server收到evilData后触发漏洞,并修改tokenAddr地址处的权限数据,从而提升验证程序的权限。
(5)验证程序获取权限后对winlogon进行控制,来创建system用户shell。
源码分析:
1.构造socket用于发送smb数据包,目的地址设为本机,建立session
2.获取token
构造数据:
3.poc接着调用RtCompressBuffer来压缩数据, 压缩数据包的主要内容是0x1108*A+(ktoken+0x40)。
经压缩后的数据长度0x13,之后这段压缩数据除去压缩数据段头部外,发送出去的压缩数据前面将会连接两个相同的值0x1FF2FFFFBC,而这两个值将会是提权的关键。此外,构造的smb header中,OriginalCompressedSegmentSize为0xffffffff,offset为0x10。
根据漏洞原理分析,漏洞原因是因为 Srv2DecompressData函数对报文字段缺乏合法性判断造成内存分配不当。
从源码中可得,OriginalSize+ Offset = 0xffffffff + 0×10 = 0xf,其将会被传递给SrvNetAllocateBuffer进行调用来进行buffer申请:
查看SrvNetAllocateBuffer反编译代码
SrvNetBufferLookasides表通过函数SrvNetCreateBuffer初始化,实际SrvNetCreateBuffer循环调用了SrvNetBufferLookasideAllocate分配内存, 调用SrvNetBufferLookasideAllocate的参数分别为[‘0x1100’, ‘0x2100’, ‘0x4100’, ‘0x8100’, ‘0x10100’, ‘0x20100’, ‘0x40100’, ‘0x80100’, ‘0x100100’]。在这里,内存分配参数为0xf,对应的lookaside表为0×1100大小的表项。
而 SrvNetBufferLookasideAllocate函数调用SrvNetAllocateBufferFromPool来进行内存分配,而其内部又是通过ExAllocatePoolWithTag函数进行分配,返回值指向内存分配地址。
内存分配完毕后,SrvNetAllocateBufferFromPool函数还对分配的内存进行一系列初始化操作,最后返回一个内存信息结构体指针。
总结内存布局如下图:
当smb服务端收到压缩数据后,调用SmbCompressionDecompress函数进行解压,而该函数实质调用的是RtlDecompressBufferEx2函数。结合RtlDecompressBufferEx2函数可以分析得到SmbCompressionDecompress函数的各个参数含义是:
SmbCompressionDecompress(CompressAlgo,//压缩算法
Compressed_buf,//指向数据包中的压缩数据
Compressed_size,//数据包中压缩数据大小,计算得到
UnCompressedBuf,//解压后的数据存储地址,*(alloc_buffer+0×18)+0×10
UnCompressedSize,//压缩数据原始大小,源于数据包OriginalCompressedSegmentSize
FinalUnCompressedSize)//最终解压后数据大小
从反编译代码可以看出,函数SmbCompressionDecompress中保存解压后数据的地址为*(alloc_buffer+0×18)+0×10的位置,根据内存分配过程分析,alloc_buffer + 0×18指向了实际内存分配起始位置偏移0×50处,所以拷贝目的地址为实际内存分配起始地址偏移0×60位置处。
在解压过程中,压缩数据解压后将存储到这个地址指向的内存中。根据evilData数据的构造过程,解压后的数据为占坑数据和tokenAddr。拷贝到该处地址后,tokenAddr将覆盖原内存数据结构中alloc_buffer+0×18处的数据。也就是解压缩函数SmbCompressionDecompress返回后,alloc_buffer+0×18将指向验证程序的tokenAddr内核地址。 而SMB_payload此时指向evilData中的权限数据,offset则为0×10。因此,这个内存移动完成后,权限数据将写入tokenAddr处。这意味着,SMS Server成功修改了验证程序的权限,从而实现了验证程序的提权!
动态调试:
token修改方法:
1.手动修改token的方法:
WinDBG中的!process扩展命令会显示指定进程或者全部进程的EPROCESS块信息。
其中的PROCESS
ffffe08fff46d380即表示System进程的EPROCESS的内存地址是0x
ffffe08fff46d380。
可使用dt _EPROCESS指令查看0x
ffffe08fff46d380
地址处的EPROCESS块信息:
。。。(
EPROCESS是较大的结构体,我们省略了部分,重点关注0x360偏移处的这个Token结构。
)
该结构是一个_EX_FAST_REF类型。
一般会通过位与法获得最终的值:
可使用!token扩展指令查看令牌的具体结构
1: kd> !token ffffcb89`8bc07420
_TOKEN 0xffffcb898bc07420
TS Session ID: 0
User: S-1-5-18
User Groups:
00 S-1-5-32-544
Attributes - Default Enabled Owner
01 S-1-1-0
Attributes - Mandatory Default Enabled
02 S-1-5-11
Attributes - Mandatory Default Enabled
03 S-1-16-16384
Attributes - GroupIntegrity GroupIntegrityEnabled
Primary Group: S-1-5-18
Privs:
02 0x000000002 SeCreateTokenPrivilege Attributes -
03 0x000000003 SeAssignPrimaryTokenPrivilege Attributes -
04 0x000000004 SeLockMemoryPrivilege Attributes - Enabled Default
05 0x000000005 SeIncreaseQuotaPrivilege Attributes -
07 0x000000007 SeTcbPrivilege Attributes - Enabled Default
08 0x000000008 SeSecurityPrivilege Attributes -
09 0x000000009 SeTakeOwnershipPrivilege Attributes -
10 0x00000000a SeLoadDriverPrivilege Attributes -
11 0x00000000b SeSystemProfilePrivilege Attributes - Enabled Default
12 0x00000000c SeSystemtimePrivilege Attributes -
13 0x00000000d SeProfileSingleProcessPrivilege Attributes - Enabled Default
14 0x00000000e SeIncreaseBasePriorityPrivilege Attributes - Enabled Default
15 0x00000000f SeCreatePagefilePrivilege Attributes - Enabled Default
16 0x000000010 SeCreatePermanentPrivilege Attributes - Enabled Default
17 0x000000011 SeBackupPrivilege Attributes -
18 0x000000012 SeRestorePrivilege Attributes -
19 0x000000013 SeShutdownPrivilege Attributes -
20 0x000000014 SeDebugPrivilege Attributes - Enabled Default
21 0x000000015 SeAuditPrivilege Attributes - Enabled Default
22 0x000000016 SeSystemEnvironmentPrivilege Attributes -
23 0x000000017 SeChangeNotifyPrivilege Attributes - Enabled Default
25 0x000000019 SeUndockPrivilege Attributes -
28 0x00000001c SeManageVolumePrivilege Attributes -
29 0x00000001d SeImpersonatePrivilege Attributes - Enabled Default
30 0x00000001e SeCreateGlobalPrivilege Attributes - Enabled Default
31 0x00000001f SeTrustedCredManAccessPrivilege Attributes -
32 0x000000020 SeRelabelPrivilege Attributes -
33 0x000000021 SeIncreaseWorkingSetPrivilege Attributes - Enabled Default
34 0x000000022 SeTimeZonePrivilege Attributes - Enabled Default
35 0x000000023 SeCreateSymbolicLinkPrivilege Attributes - Enabled Default
36 0x000000024 SeDelegateSessionUserImpersonatePrivilege Attributes - Enabled Default
Authentication ID: (0,3e7)
Impersonation Level: Anonymous
TokenType: Primary
Source: *SYSTEM* TokenFlags: 0x2000 ( Token in use )
Token ID: 3eb ParentToken ID: 0
Modified ID: (0, 3ec)
RestrictedSidCount: 0 RestrictedSids: 0x0000000000000000
OriginatingLogonSession: 0
PackageSid: (null)
CapabilityCount: 0 Capabilities: 0x0000000000000000
LowboxNumberEntry: 0x0000000000000000
Security Attributes:
Invalid AUTHZBASEP_SECURITY_ATTRIBUTES_INFORMATION with no claims
Process Token TrustLevelSid: S-1-19-1024-8192
也可使用dt _TOKEN查看字段定义。
在尝试把替换cmd.exe进程的TOKEN指针之前,我们先执行一下whoami看看。
图 4
手动查看cmd.exe进程的TOKEN指针。
kd> !process 0 0 cmd.exe
PROCESS ffff980ddcba8080
SessionId: 1 Cid: 0ae8 Peb: 1265dcd000 ParentCid: 0d38
DirBase: 530d6002 ObjectTable: ffffd58733943400 HandleCount: 75.
Image: cmd.exe
然后把cmd.exe进程的TOKEN指针替换为system的TOKEN指针。
kd> eq ffff980ddcba8080+0x360
再执行一下whoami显示,当前账户已经由admin变化为nt authority\system。
回到提权部分的动态调试
查看memmove()函数的目的地址是否就是cve-2020-0796-local.exe进程的TOKEN指针:
手动查看cve-2020-0796-LPE_x64.exe进程的TOKEN指针。
可以看出memmove()函数的目的地址
,cve-2020-0796-LPE_x64.exe的TOKEN指针存放位置
,该地址指向
,正好是ffffcb89`92e026b0+0x40
从_TOKEN结构体的定义中,我们了解在0x40偏移处是一个_SEP_TOKEN_PRIVILEGES的结构。
执行拷贝后,进行查看:
查看 提权进程的TOKEN中的Privileges :
这16字节数据正是之前我们曾观察到的System进程的TOKEN中的Privileges的Present值。Present字段表示启用的特权,Enabled字段表示拥有的特权。这16字节数据覆盖写进程的Privileges结构之后,系统即拥有并启用了的system进程所有的特权。
在修改本进程的_SEP_TOKEN_PRIVILEGES结构之后,就已经获得system进程的特权,后续调用了inject()函数。inject()函数中通过CreateToolhelp32Snapshot()创建进程快照,使用比较进程名的方法找到"winlogon.exe"进程。然后使用VirtualAllocEx申请了一块带有可执行属性的内存(PAGE_EXECUTE_READWRITE),WriteProcessMemory把shellcode写入目标进程,CreateRemoteThread()最终完成任务。
2.远控原理分析
0x03 漏洞危害
cve-2020-0796漏洞可能造成系统崩溃、拒绝服务,或者可能被用于远程代码执行。
(1) 系统崩溃:在工业控制系统场景中多数操作站、上位机、SCADA系统运行在windows系统上,操作站崩溃会导致操作员无法与操控设备;上位机、SCADA崩溃可导致工控设备失去响应,直到被人工重启。
(2) 远程代码执行:攻击者利用该缓冲区溢出漏洞可能导致工业控制系统数据泄露,如软件版本,系统信息、工艺参数等关键数据,也可直接获取操作权限。
0xFF 写在后面
遇到的问题:
1. https://stackoverflow.com/questions/43745499/error-symbol-file-could-not-be-found-defaulted-to-export-symbols-for-ntkrn
输入kn命令查看栈回溯时,看到一条关键的:
2.每次都要重启调试,其实只要设置一次,即有一次key即可后续了。
参考:
https://bbs.pediy.com/thread-258551.htm
http://www.tdhxkj.com/index.php/qiyexinwenfee836fb53c1adb9ffca0f12/2020/05-12/315.html
待研究:
1.利用如何绕过安全机制?
2.微软的win10的堆的规则?