Fingerprinting Models when hooking DirectX (Vermintide 2)

Fingerprinting Models?

Reading “Game Hacking” from Nick Cano, where he explains how to fingerprint the models we want to reveal for certain cheats, motivated me to try it on DirectX11, and I explain how to do it in this post ūüôā

When we want to do a Wallhack, for example, the most difficult part it is perhaps finding the correct model you want to reveal and make it visible through the walls. You can find on internet¬†different pieces of code that show examples of how this could be done, but none of them (or at least I couldn’t find) explain properly and in a clear way the logic and meaning of the code. That’s why I wanted to write this blog post to show you not only the code but also the logic behind it to reach the goal: Fingerprint the Model you want to reveal on a wallhack.

But wait, you said something about a Model…what is it a Model? Well, Models and Textures are two main topics when we talk about DirectX. We could say that Models are each “object” or portion of an “object” that is rendered inside the game by DirectX. And textures are what they are made of when rendered, but basically, each of them is, at a basic level, just tables of data that are used to provide visual detail in graphics apps.

How do we identify a Model?

As explained in the last post, there are multiple differences¬†between each version of DirectX, thus I’m going to explain again the differences between version 9 and 11 when you want to fingerprint a Model.

For DirectX 9 the main method you want to hook is IDirect3DDevice9::DrawIndexedPrimitive.¬†The parameters of this function are the most valuable thing here, especially:¬†NumVertices and primCount. These two values together identify in a “unique” way each Model the game is rendering, they give us information about the model they are currently rendering. We want these values of course.

For DirectX 11, the main method you want to hook is ID3D11DeviceContext::DrawIndexed. The first thing you may have noticed is that apart from the name, the method we are hooking is not anymore on the Device but on the DeviceContext. Small changes that have a different impact on what we need to do in order to identify our so desired models.

But what values do we want from DrawIndexed? Actually, for this version the values are four:

  • Stride
  • Vertex Byte Width
  • Index Byte Width
  • Constant Byte Width

If we were able to store every property of each model the game is rendering and then somehow use this list to look for each model on the game, we would be able to find the Model that we are looking for.

For this we will need a few things:

  • Hook DrawIndexed
  • Store all the properties for each Model
  • Browse between these list of properties of a model
  • Identify each item of the list in the game (this is the best part since it will give us the ability to look for the model on the screen while each item¬†is rendered)
  • And recover the values we need.

Let’s see how to achieve each point!

Hook DrawIndexed

Same as before we are going to use¬†Detours to create the trampoline redirect the original function to ours. But before doing that we will need to know the address DrawIndexed. Guess what, the trick we saw in the last post of using the offset of .dll won’t work. Why? Because DirectX will create a VTable for each object and the method we want will be allocated dynamically on run-time. What can we do?

Easy, we will use the reference we already got to our ID3D11DeviceContext, saved as pContext, and we will extract the real address of ID3D11DeviceContext::DrawIndexed by using its VTable. The DeviceContext will have a VTable with multiple pointers to each of their methods and we need to grab the correct one. The offset of DrawIndexed is 12, you can know this by searching on Google but I will probably make a quick write up of how you can find and confirm this value soon.

So, we have a pointer to pContext and we have the offset of the method we need. Let’s define some variables and how we get the address we need:

typedef void(__stdcall *ID3D11DrawIndexed)(ID3D11DeviceContext* pContext, UINT IndexCount, UINT StartIndexLocation, INT BaseVertexLocation);

DWORD_PTR* pDeviceContextVTable = NULL;
ID3D11DrawIndexed fnID3D11DrawIndexed;

pDeviceContextVTable = (DWORD_PTR*)pContext;
pDeviceContextVTable = (DWORD_PTR*)pDeviceContextVTable[0];
fnID3D11DrawIndexed = (ID3D11DrawIndexed)pDeviceContextVTable[12];
std::cout << "[+] pDeviceContextVTable Addr: " << std::hex << pDeviceContextVTable << std::endl;
std::cout << "[+] fnID3D11DrawIndexed Addr: " << std::hex << fnID3D11DrawIndexed << std::endl;

We are just dereferencing the VTable into pDeviceContextVTable and extracting the 12th element. Now we can call Detours and hook DrawIndexed properly.

void detourDirectXDrawIndexed()
	std::cout << "[+] Calling fnID3D11DrawIndexed Detour" << std::endl;
	// Detours the original fnIDXGISwapChainPresent with our Present fnID3D11DrawIndexed, (PBYTE)hookD3D11DrawIndexed
	DetourAttach(&(LPVOID&)fnID3D11DrawIndexed, (PBYTE)hookD3D11DrawIndexed);

Store all the properties for each Model

To obtain the values we will be using different methods from pContext to retrieve them. Stride is pretty straight forward since its value is assigned to the pointer passed as a parameter to ID3D11DeviceContext::IAGetVertexBuffers. From the same method, we can retrieve the Vertex ByteWidth after using similar functions from DirectX to access each of the properties, as you can see in the code below.

I don’t want to dig deeper here because you can find usually pieces of code like these or similar in multiple forums or hacking communities, and you don’t really need to understand each method from the API of DirectX in order to achieve your goals.

For the rest of the values the same process will be used, but calling different methods from pContext such as ID3D11DeviceContext::IAGetIndexBuffer and ID3D11DeviceContext::PSGetConstantBuffers.

//get stride & vedesc.ByteWidth
pContext->IAGetVertexBuffers(0, 1, &veBuffer, &Stride, &veBufferOffset);
if (veBuffer)
if (veBuffer != NULL) { veBuffer->Release(); veBuffer = NULL; }

//get indesc.ByteWidth
pContext->IAGetIndexBuffer(&inBuffer, &inFormat, &inOffset);
if (inBuffer)
if (inBuffer != NULL) { inBuffer->Release(); inBuffer = NULL; }

//get pscdesc.ByteWidth
pContext->PSGetConstantBuffers(pscStartSlot, 1, &pscBuffer);
if (pscBuffer != NULL)
if (pscBuffer != NULL) { pscBuffer->Release(); pscBuffer = NULL; }

Before we continue we need to store all this set of properties so we can then browse them and find the correct one. Each time DrawIndexed is called, we will insert just once each set of properties:

propertiesModel paramsModel;
paramsModel.stride = Stride;
paramsModel.vedesc_ByteWidth = vedesc.ByteWidth;
paramsModel.indesc_ByteWidth = indesc.ByteWidth;
paramsModel.pscdesc_ByteWidth = pscdesc.ByteWidth;

seenParams is going to be an unordered_set of a custom struct called propertiesModel. We need to define operations such as == and hash in order to make sure that it works properly and each set of properties is inserted only once:

std::unordered_set<propertiesModel> seenParams;

// Model Structures
struct propertiesModel
	UINT stride;
	UINT vedesc_ByteWidth;
	UINT indesc_ByteWidth;
	UINT pscdesc_ByteWidth;

bool operator==(const propertiesModel& lhs, const propertiesModel& rhs)
	if (lhs.stride != rhs.stride
		|| lhs.vedesc_ByteWidth != rhs.vedesc_ByteWidth
		|| lhs.indesc_ByteWidth != rhs.indesc_ByteWidth
		|| lhs.pscdesc_ByteWidth != rhs.pscdesc_ByteWidth)
		return false;
		return true;

namespace std {
	template<> struct hash<propertiesModel>
		std::size_t operator()(const propertiesModel& obj) const noexcept
			std::size_t h1 = std::hash<int>{}(obj.stride);
			std::size_t h2 = std::hash<int>{}(obj.vedesc_ByteWidth);
			std::size_t h3 = std::hash<int>{}(obj.indesc_ByteWidth);
			std::size_t h4 = std::hash<int>{}(obj.pscdesc_ByteWidth);
			return (h1 + h3 + h4) ^ (h2 << 1);

Browser between these list of Model

This part is really simple, we will use GetAsyncKeyState to determinate whether a key or not is pressed and move forward or backward inside the set:

auto current = seenParams.find(currentParams);

if (GetAsyncKeyState(VK_PRIOR) & 1)
	//FIX cannot dereference element, because end and begin return an iterator instead of an element
	if (current == seenParams.end())
		std::cout << "Position " << std::dec << currentParamPosition << " of " << std::dec << seenParams.size() << std::endl;
		// TODO: I need a fix for this I get error "Cannot dereference end list iterator"
		current = seenParams.begin();
		currentParamPosition = 1;
	currentParams = *current;
if (GetAsyncKeyState(VK_NEXT) & 1)
	if (current == seenParams.begin())
		std::cout << "Position " << std::dec << currentParamPosition << " of " << std::dec << seenParams.size() << std::endl;
		// TODO: I need a fix for this I get error "Cannot dereference end list iterator"
		// current = seenParams.end();
		currentParamPosition = seenParams.size();
	currentParams = *current;

Identify each item of the list in the game

What would be the best way to do this? Having a big list of attributes doesn’t help us if we can’t identify each element on that list and see if it is what we are looking for. Therefore, we are going to use a good technique the book explains: Highlighting the models

How? We are going to create a new texture of color red and we are going to apply it to the model we have currently selected from our list. If we do this correctly we should see inside the game something made complete of a red texture.

Again I’m not going to dig too deep into this because it’s too specific of DirectX11 but I’m going to mention one thing that it’s¬†important to know. There are two methods that are implemented in this template: 2DTextures and Shader. How well works each of them will depend on the game and the engine it’s using. Its possible to switch between them by pressing¬†F9 while the dll is injected.

Recover the values we need

This last step is the most simple and more grateful. Once we’ve found the correct model (for example our enemies), we can press¬†L and it will dump the four properties to the Console so you can store them and use them later.

What can we do with these values to achieve a wallhack? that will come in one of the next posts soon ūüôā

The updated template is going to be uploaded to github today with all the new features.