Tutorial reHonored - Restoring the developer console in Dishonored 2

Hexui Undetected CSGO Cheats Sinkicheat PUBG Cheat

timb3r

Semi-Retired
Dank Tier VIP
Jul 15, 2018
767
22,668
47


Game: Dishonored 2
Developer: Arkane Studios
Engine: VoidEngine (based on idTech 5)
Version: v1.77.5.0
DRM: Denuvo
Where to buy: Steam

Introduction:

Still not quite finished with messing around in the internals of the idTech engine I decided to take a look at Dishonored 2 which switched from Unreal Engine 3 over to idTech for the sequel. The first thing I noticed was no console. No matter what command line options I used or config files I changed I couldn't get the console to come down. Puzzled I started searching the web looking for any references I could when I stumbled upon this post by SunBeam on Fearless.

Interesting. If you're too lazy to read (which I highly recommend you do) he basically comes to the conclusion that code responsible for powering the console has been stripped from the executable. My response was similar to the following:



So I set off to restore the console to it's former glory.

How the console works:

Within the idTech engine there's a few key functions responsible for retrieving what the user types, parsing it and displaying it. However with DH2 the code that draws to the screen is gone as is the memory buffer which the console allocates to store all the text (or appeared that way anyhow). In order to fix this we're going to have to place a few hooks and write some replacement code for the missing parts. This is not as as difficult as writing a w2s for AOE so don't panic (@BDKPlayer).

How do we find all these functions to hook? x64dbg, a coffee and some decent tunes. Fair warning to those new to reversing it takes time and it is an extremely long process until you're very familiar with the internals of the game and assembly code in general. So if you lack the disciprine or motivation to spend hours trawling over red hot assembler maybe just stick to changing your ammo to 1337 on AssaultCube with Cheat Engine.

In the post SunBeam mentions the parse / execute function of the console. At least in my version of the game the execute function was not located in the same place in memory. So how to find? Well if we examine the assembler code there's a few instructions that look fairly unique:

Code:
; Like this
MOV EAX, 1440h

; And this
MOV QWORD PTR SS:[RBP+1460], RSI
However this can change from version to version.

Another way to find it is if you know idTech (which I do) there's a number of hard-coded strings in most games when you type an invalid console command:
invalid.PNG



And if we scroll up slightly:
func.PNG

"Look familiar?"

This is typically where experience and intuition pays off for reversing. Although the functions are slightly different from one another they are functionally identical and a quick read through the code reveals that.

All about that exec:

So since we have no console to type anything, nor do we know the names of any functions to test we'll have to leave the executing of commands for later. What would be really helpful is if we could somehow capture the output from the game to learn more about the internals. One of the good things about idTech is it has so many fucking strings it makes it really easy to locate code in a debugger.

If we go back to our string references we can attempt to locate a string that would (probably) be out-putted to the console. Some useful things to try would be the name of developers, the name of the engine, map, initialization, "%s\n" etc.

For example:
console-output.PNG

"This looks quite initalisy"

If we drop into the function where the string is referenced:
inside-function.PNG

"Let's play a game"

Take a few seconds to read over the code and tell me what you see. There's a pattern here try to figure it out before continuing the article (I'll wait).

If you said MOV EDX, LEA RCX, CALL: you win the prize! It's a pretty safe bet that dishonored2.14401F620 is some type of logging call. However just from looking at the disassembly above we can see it most likely takes variable parameters. Meaning our function is going to need to be able to handle that without corrupting the stack.

trippy.gif

Stack corruption: Trippy

If you need an adult:

If this is starting to become too intense for you, stop the tutorial go through the beginner's guide (specifically the calling internal functions one) and come back to this later.

Point Break:

1569548826336.jpeg

"No wait the other one: Break POINTS"

If we place a break point on the dishonored2.14401F620 function we can observe the stack and see what the function does with the passed in parameters.

Here's the disassembly of the function (it's weird):

Code:
mov qword ptr ss:[rsp+10],rdx
mov qword ptr ss:[rsp+18],r8
mov qword ptr ss:[rsp+20],r9
ret
That looks like an incomplete call to a class function, there's a similar function in Doom 2016 and it has bit more going on. I guess this sort of proves SunBeam was correct for assuming the code had been stripped in the release binary.

This is where Denuvo is a goddamn pain in the ass. You can't launch the game with a debugger attached or it will refuse to load. If you're quick enough you can run the game and immediately attach x64dbg as the window opens. This is the first annoyance with Denuvo there's several more coming. You really want to attach as quickly as possible so you can view all the data flowing into the function.

Here's a dump of the registers:
pump.PNG

"Pump appears to be the game's message processing function."

So we can see that the string is passed in via RCX, followed by three parameters in RDX, R8 and R9. If I was to guess I'd say the function looks a bit like this:

C++:
void Log(const char* rcx, void* rdx, void* r8, void* r9)
{
    printf(rcx, rdx, r8, r9);
}
In fact we can test this by quickly throwing a DLL together and hooking that function:

C++:
// You know what this does: comon
moduleBaseAddr = (uintptr_t)GetModuleHandle(NULL);
      
// Hook for retriving console output
uintptr_t consoleOutput = (moduleBaseAddr + 0x401F620);
      
printf("[?] Console hook at: %llx\n", consoleOutput);

DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());

DetourAttach(&(PVOID&)consoleOutput, Log);

LONG lError = DetourTransactionCommit();
if (lError != NO_ERROR) {
    printf("[-] Failed to detour");
    return FALSE;
}
And we should get some nice output like so:

beta-console-small.png

"Excellent, just excellent"

Kicking it another notch:



This is all good and well but it's a bit basic and crappy looking. How about making it look a bit more professional and actually having the console inside the game and usable like it's supposed to be? It's fairly simple to integrate ImGui and borrow the ConsoleApp code from the examples.

Now check this out:
idtech-console.PNG

"Now we're talking look at that BEAST"

Where's my damn mouse?

It's pretty hard to click on a thing if you have to guess where your mouse cursor is. Luckily we can just ask ImGui to render the mouse for us when the console is active:

C++:
// Win32 message handler
extern LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    if (bShowConsole) {
        if (ImGui_ImplWin32_WndProcHandler(hWnd, msg, wParam, lParam))
            return 0;
    }

    switch (msg)
    {
        case WM_KEYDOWN:
        if (wParam == 192) { // ~
            bShowConsole = !bShowConsole;

            ImGuiIO& io = ImGui::GetIO(); (void)io;
            io.MouseDrawCursor = bShowConsole;
        }
        return 0;

        case WM_DESTROY:
        ::PostQuitMessage(0);
        return 0;
    }

    return ::DefWindowProc(hWnd, msg, wParam, lParam);
}
So good so far we can toggle the console with '~' and draw a mouse pointer to interact with it. But we're only HALF done we need to go back and finish our console execution code.

The people have a craving...

And the only solution is executing internal game functions. Luckily SunBeam put most of the pieces together on this one (HINT: Go read his post). All we need to do quickly put some code together in C++.

I'll re-explain how it works just in-case the post goes offline.

The function takes a pointer to a structure but it only cares about the string pointer and length at offset 0x28 and 0x20 repectively. This structure already exists within the game's memory so we can simply overwrite it with our values:

C++:
// Setup the structure
uintptr_t baseObject = *((uintptr_t*)(moduleBaseAddr + 0x218BDA0));
char* cmdString = (char*)(baseObject + 0x28);
int* cmdLength = (int*)(baseObject + 0x20);

// Overwrite the data
memcpy(cmdString, cmd, strlen(cmd) + 1);
*cmdLength = strlen(cmd) + 1;

// Create a function pointer to the exec command
typedef void(__fastcall* executeCommand_t)(uintptr_t);
executeCommand_t fnExecuteCommand = (executeCommand_t)(moduleBaseAddr + 0x3FA6B10);

// EXECUTE
fnExecuteCommand(baseObject);
Now we should be able to run the game, inject our DLL and BEHOLD:

listcmds.png

"The output of running listcmds a standard idTech command"

When keeping it real goes horribly wrong:

It'll become immediately apparent that our console is fairly limited in what we can do. Arkane Studios decided that they didn't want any filthy, disgusting, leprous cheaters dicking about in their super secret dev console. There's a number of protections in place but the biggest one is a lot of commands either point to partial or null code. So bypassing the protection is useless if calling the command will crash the game.

However some commands still do work. I couldn't be bothered to try them all but one command I found particularly useful and built it into my code.
 
Last edited:

timb3r

Semi-Retired
Dank Tier VIP
Jul 15, 2018
767
22,668
47
Here's the protection routine that catches illegal commands and prints the error message:

bypass.PNG


Nice static flag there Arkane.

Update the exec function like so:

C++:
uintptr_t tripFlag = 0x00000001431C5B78;

// Remove prots for archived cVars
*((bool*)(tripFlag)) = true;
// EXECUTE
fnExecuteCommand(baseObject);
// Restore after we execute our cmd
*((bool*)(tripFlag)) = false;
Now our code will unset the flag, execute the command we issued and restore the flag just in case there's protection routine monitoring the flag. Now we can freely execute any commands we like but as I said before most commands have been neutered or will cause a crash (so be careful). But the main reason I needed this was I wanted a way to pause the game without going to the game menu.

There's a cvar called g_stopTime which basically pauses the game like you went to the menu.

Here's the code:
C++:
static void FreezeGame(bool enable)
{
    char szBuf[30] = { 0 };
    printf(szBuf, "g_stopTime %d", enable ? 1 : 0);

    // This is done to be 100% sure the game doesn't pause
    // Otherwise the internal game state gets messed up
    ExecuteConsoleCommand("win_pauseOnFocusLoss 0");

    // Execute the pause function
    ExecuteConsoleCommand(szBuf);
}
You'll also notice a bunch of other cvars like godmode, freecam and player variables but they don't do anything. If you want to do stuff like that in game you'll have to develop it the old fashioned way.

A quick note about hacking Dishonored 2:

I didn't realize until I was about to release this article but the game is protected by Denuvo. If you're going to use Cheat Engine or something to mess with internal game data be careful what you change. If you accidentally touch a Denuvo variable the game will immediately exit. This makes certain things quite tedious. For example I was trying to mess with the player coordinates but kept accidentally tripping Denuvo and exiting the game (loosing all my work in the process). You can probably bypass this with enough persistence but I was just trying to restore the console not go up against frigging copy protection.

Goofs:

A lot of stupid and hilarious things happened when I was playing with this game take this guy for example:

"Nice sword there buddy"

I nopped out what I thought was the code for reducing the player's health however it's also shared with NPCs.

Here's that same guy headless after me choking him out, not sure how I was able to do that considering he doesn't have a head or neck:
'unconcious'.png

Pictured: An "unconscious" man

Pausing the game at the wrong time? Gonna have a T Party:
t-party.png


Want to burn people the GH way? Use your game to tell people:

w2s.png

"Some say BDKPlayer is still searching for that AOE W2S ..... it consumes him"

Some people say I'm terrible at video games, I respectfully disagree:
i-am-terrible.PNG


Outro:

Well that's all for now I hope everyone got something out of this article.

If you have any questions or comments let me know below!

Full project source is attached.

Total time wasted debugging: 10:52:52
 

Attachments

Last edited:

BDKPlayer

No hack no life
Dank Tier VIP
Dank Tier Donator
Oct 31, 2013
342
10,363
31
Seems rock solid at a first glance. Will read it in detail when im home. There are some things id like to give my opinion.

"This is not as as difficult as writing a w2s for AOE so don't panic (@BDKPlayer)."

Now that was random.


*names his assasination target BDKPlayer*

Cool, cool. Thats fine. I guess..

"Some say BDKPlayer is still searching for that AOE W2S ..... it consumes him"

Motherfucker. Throw three curveballs at me in a single article?


That w2s is now working like a charm. Im going to have my revenge. Also I will do visuals beyond your imagination.
 
  • Haha
Reactions: metrix

timb3r

Semi-Retired
Dank Tier VIP
Jul 15, 2018
767
22,668
47
Seems rock solid at a first glance. Will read it in detail when im home. There are some things id like to give my opinion.

"This is not as as difficult as writing a w2s for AOE so don't panic (@BDKPlayer)."

Now that was random.


*names his assasination target BDKPlayer*

Cool, cool. Thats fine. I guess..

"Some say BDKPlayer is still searching for that AOE W2S ..... it consumes him"

Motherfucker. Throw three curveballs at me in a single article?


That w2s is now working like a charm. Im going to have my revenge. Also I will do visuals beyond your imagination.
Hahaha, feel free to shit talk me the rest of the internet does :trollface:.

Guess I'll wait for the spicy @BDKPlayer timb3r memes.
 
Last edited:

Rake

Cesspool Admin
Administrator
Jan 21, 2014
11,573
78,998
2,316
this is some next level stuff man, excellent work



I had to make this meme for you ^
 
Last edited:
  • Love
Reactions: timb3r

Rake

Cesspool Admin
Administrator
Jan 21, 2014
11,573
78,998
2,316
This tutorial has been released from the premium forum to the general public!

 
  • Like
Reactions: timb3r
Attention! Before you post:

Read the How to Ask Questions Guide
99% of questions are answered in the Beginner's Guide, do it before asking a question.

No Hack Requests. Post in the correct section.  Search the forum first. Read the rules.

How to make a good post:

  • Fill out the form correctly
  • Tell us the game name & coding language
  • Post everything we need to know to help you
  • Ask specific questions, be descriptive
  • Post errors, line numbers & screenshots
  • Post code snippets using code tags
  • If it's a large project, zip it up and attach it

If you do not comply, your post may be deleted.  We want to help, please make a good post and we will do our best to help you.

Community Mods League of Legends Accounts