Wallhack

In the previous posts we have done most of the heavy work, but what comes now is really simple compared with the rest. We have created a basic template for hooking DirectX11 and inject our own interface, then we created a Model Logger to allows us to highlight and identify the most important Models we wanted to modify, but something is missing, right? What can we do once we have the correct model highlighted? One of those things is a Wallhack of course!

If you are here you know what a Wallhack is, this kind of game tweaking has been always known. In this post, we are going to see how to use our template to enable Wallhacking on the selected models, or what it is the same, disable Z-Buffering on DirectX.

How Z-Buffering works?

Z-Buffering or Depth-Stencil (as is known in version 11), is a property that stored depth information used by DirectX when it is rendering a scene. It determines how deep each pixel is in the scene. By doing this, DirectX knows exactly which pixels to render from each model based on the value of this attribute.

If we imagine this as two dimensions array, it will select for each pixel in X and Y the value based on Z-Buffering value. In other words, the object/model that its closer to the point of view.

The main idea here will be to disable Z-Buffering for the models we want to reveal behind the rest of the models (walls, floors, etc).

DirectX 9 vs DirectX 11

In both versions, this is actually quite easy, however, I want to remark the differences. For version 9, disabling is as simple as calling the method IDirect3DDevice9::SetRenderState, which receives two parameters: the device state variable that will be modified, and the new value.

pDevice->SetRenderState(D3DRS_ZENABLE, false);

In this case, the variable we need to modify is D3DRS_ZENABLE, and the new value is false. It’s just as simple as that. Something important that we have to remember for both versions is that we need to enable this again after calling the original DrawIndexedPrimitive, enabling Z-Buffering for all the rest of the models.

For version 11, two different methods are required: ID3D11Device::CreateDepthStencilState and ID3D11DeviceContext::OMSetDepthStencilState. The first one will be used to create an ID3D11DepthStencilState that will disable Z-Buffering and then be sent as a parameter to the second method when the model we want to reveal is being rendered, that means inside DrawIndexed. More info here.

Let’s jump to the code!

Oh wait, before that, I will like to resume the steps we need to implement our wallhack.

Create a new DepthStencilState disabling Z-Buffering

Check if we are rendering the target model.

Disable Z-Buffering

Render the Model

Enable Z-Buffering again

Create a new DepthStencilState disabling Z-Buffering

How to create a new DepthStencilState is detailed in Microsoft documentation: Configuring Depth-Stencil Functionality. But remember that we will need to disable the Z-Buffering, so our version will be like this:

ID3D11DepthStencilState *m_DepthStencilState; // Disabling Z-Buffering D3D11_DEPTH_STENCIL_DESC depthStencilDesc; depthStencilDesc.DepthEnable = TRUE; depthStencilDesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL; depthStencilDesc.DepthFunc = D3D11_COMPARISON_ALWAYS; depthStencilDesc.StencilEnable = FALSE; depthStencilDesc.StencilReadMask = 0xFF; depthStencilDesc.StencilWriteMask = 0xFF; // Stencil operations if pixel is front-facing depthStencilDesc.FrontFace.StencilFailOp = D3D11_STENCIL_OP_KEEP; depthStencilDesc.FrontFace.StencilDepthFailOp = D3D11_STENCIL_OP_INCR; depthStencilDesc.FrontFace.StencilPassOp = D3D11_STENCIL_OP_KEEP; depthStencilDesc.FrontFace.StencilFunc = D3D11_COMPARISON_ALWAYS; // Stencil operations if pixel is back-facing depthStencilDesc.BackFace.StencilFailOp = D3D11_STENCIL_OP_KEEP; depthStencilDesc.BackFace.StencilDepthFailOp = D3D11_STENCIL_OP_DECR; depthStencilDesc.BackFace.StencilPassOp = D3D11_STENCIL_OP_KEEP; depthStencilDesc.BackFace.StencilFunc = D3D11_COMPARISON_ALWAYS;

Now its time to call CreateDepthStencilState. We will pass as parameter the D3D11_DEPTH_STENCIL_DESC we just created and a pointer to our new ID3D11DepthStencilState.

pDevice->CreateDepthStencilState(&depthStencilDesc, &m_DepthStencilState);

Now we are ready to move to the next step.

Check if we are rendering the target model

In our previous version of the template, we have been doing something similar to what we need here, but it will be better if we do some improvements. Until now we were checking the model we were currently rendering inside our hooked DrawIndexed method, but this was when we had a list of Models and we wanted to identify our target. What could we do now? Well, if we did our homework, we should have by now the Model Properties of our target and it’s time to use them.

Let’s create a new std::unordered_set to store all the properties belonging to the Models we want to reveal and store there our collected values:

std::unordered_set<propertiesModel> wallhackParams; void setupWallhack() { propertiesModel wallhackParamsItem; wallhackParamsItem.stride = 8; wallhackParamsItem.vedesc_ByteWidth = 16552; wallhackParamsItem.indesc_ByteWidth = 10164; wallhackParamsItem.pscdesc_ByteWidth = 832; wallhackParams.insert(wallhackParamsItem); }

Those values, belong to the models of the enemies in mid/large range for Vermintide 2, and I found them by logging the Models with our template. In many games you will see that enemies may be split in multiple models, not only for the parts of the body/clothes but also this models could change depending on the distance between you and the object. By selecting the model used for rendering my enemy in a mid/large range, allows me to see them normally when they are close but highlighted when they aren’t next to me.

Now we will modify our validations to see if we are rendering not only our current item from the Model List, but also one of the targeted models:

if ((paramsModel == currentParams || wallhackParams.find(paramsModel) != wallhackParams.end() )&& bShader) { // SNIPPED if (bWallhack) { // DISABLE Z-Buffering } } else if ( (paramsModel == currentParams || wallhackParams.find(paramsModel) != wallhackParams.end()) && bTexture) { // SNIPPED if (bWallhack) { // DISABLE Z-Buffering } }

If wallhackParams.find does not contain paramsModel it will be equal to .end element as the documentation says: std::set::find

Disable Z-Buffering

It’s so simple as the following line:

pContext->OMSetDepthStencilState(m_DepthStencilState, 0);

We are calling OMSetDepthStencilState with the DepthStencilState pointer we created before.

Render the Model

Then we have to call to the original the original DrawIndexed:

fnID3D11DrawIndexed(pContext, IndexCount, StartIndexLocation, BaseVertexLocation);

Enable Z-Buffering again

What would happen if we do not enable Z-Buffering again?

We are reveling multiple objects of the game since non of them have Z-Buffering enabled 🙂 That’s why we will need to store the original DepthStencilState we had before disabling it:

UINT stencilValue = 0; pContext->OMGetDepthStencilState(&m_origDepthStencilState, &stencilValue);

And finally, after calling DrawIndexed, we set this value again:

pContext->OMSetDepthStencilState(m_origDepthStencilState, stencilValue);

Result

Once we have everything working we will see something like this:

Next Steps?

DirectX is extremely powerful and we have seen just a few examples of what we can do. In the following posts we will continue discovering all the different kind of things we can achieve and the best way to approach them.