SECRET CLUB

Hooking the graphics kernel subsystem


Today’s cheats are predominantly using internal directx hooks or window overlays to visualize hidden game information. These two methods are widely documented, but other, more inconspicous method include hooking graphics routines in the Windows kernel, as we will demonstrate in this article. There’s no public release using a similar method to this, which is a shame, as it is actually quite easy to use and leaves minimal trace, in contrast to normal directx hooks.

dxgkrnl

The Microsoft DirectX graphics kernel subsystem, implemented in dxgkrnl.sys, is a part of the DirectX Graphics Infrastructure(DXGI) device driver interface. This driver acts as an abstraction layer for the respective display drivers, exposing various interfaces and acting as the intermediary for the user-mode implementation and the graphics card. This is a very broad subsystem and has many functions of interest; we’ve decided to concentrate on D3DKMTSubmitCommand

gdi32!D3DKMTSubmitCommand is used to submit command buffers to graphics drivers that support virtual addressing. These commands are generated completely in user-mode and are merely passed to the graphics driver through the graphics kernel subsystem. Its predecessor, DxgkDdiRenderKm, is only used for “legacy” graphics drivers, but might be interesting to look at as well, as it can most likely yield the same result. The D3DKMTSubmitCommand function passes one argument: an instance of the D3DKMT_SUBMITCOMMAND structure. This structure contains GPU commands, submission flags and context data, and aren’t to any use for us, unless we want to modify the actual gpu commands.

When gdi32!D3DKMTSubmitCommand is called, it routes through the system call NtGdiDdDDISubmitCommand, which is implemented in either win32 driver (some windows version has it implemented in win32kbase, some in win32kfull) as:

Fig.1 - win32kbase!NtGdiDdDDISubmitCommand

This unusual function call of the data member is actually a part of a larger function table for the entire dxgkrnl abstraction layer, which is neither documented in symbols nor exported in the binary, which might explain the unusual negligence surrounding this type of graphical use in cheats. This specific data member in question points to dxgkrnl!DxgkSubmitCommand, and the rest of the table speaks for itself (see x-refs):

Fig.2 - win32kbase function pointer table

This means that we can control all flow of execution by simply changing this data member, which is RW(!), making automated integrity checks a tad harder. When we’ve overwritten the pointer, we’re ready to draw to the screen buffer.

Why?

By intercepting this specific gpu call, we are perfectly in sync with the actual screen updates, allowing us to use GDI functions to manipulate the intermediate screen buffer. The only trace of us drawing to the game buffer is the obscure pointer swap that no anti-cheat actually checks for. Be aware that this is cpu-based, which means there is a big performance overhead, but it is possible to draw through the same hook utilizing the gpu.

To actually draw, we can use any Gdi function directly in kernel without any issues! Here’s an example that draws a simple box using the bit pattern copy operation NtGdiPatBlt:

int64_t __fastcall dxgkrnl_hook::submit_command_hook(D3DKMT_SUBMITCOMMAND* data)
{
	const auto current_process = IoGetCurrentProcess();
	const auto process_name = PsGetProcessImageFileName(current_process);

	if (std::memeq(process_name, dxgkrnl_hook::target_name))
	{
		// GET CONTEXT
		const auto ctx = NtUserGetDc(0x00);

		// DRAW TO GAME WINDOW BUFFER
		NtGdiPatBlt(ctx, 15, 15, 5, 5, PATCOPY);
	}

	return dxgkrnl_hook::original_submit_command(data);
}

Demonstration

I have assembled a proof-of-concept Github repository, which is taken out of a larger project, which is why some of the referenced symbols are undefined - they’re very trivial it to find, so that’ll be an exercise for the reader.

If you do not want to try the method out yourself, this video utilizes the same exact method for the player boxes, which demonstrates the perfect synchronization of our previously mentioned kernel hook.