Themida Protected R6 Cheat Reverse Engineered

Preamble

Although this application is packed/virtualized, it will be de-virtualized/unpacked at runtime in sections. Virtualized/packing executables will not stop reverse engineering, this only really stops static analysis. Debugging with the help of dumping will be enough to see what this program is doing. Themida tries its best to prevent debugging but preventing debugging is extremely difficult and is more of a cat and mouse game. Another note on virtualized/packed code is the more virtualization/packed the more your CPU will have to work to virtualize/pack and devirtualize/unpack. This can lead to 100% CPU usage at times and make the game run very bad. This is why sections that contain data that don't change (rdata) are sometimes unpacked only once and left unpacked.

Main Loader Overview


This sample proved itself to be difficult. The loader would close if any of the following applications were running: Cheat Engine, x64dbg, Api Monitor (Only if attached to the process), Process Hacker, Procmon (Themida doesn't like procmons driver being loaded), and Wireshark. Most of these detections don't seem to be from Themida itself (besides procmon as stated before). At this point I assumed that all detections besides procmon where done by the creator of this malware. This means functions like FindWindowA, FindWindowExA, and lower level (undocumented functions) NtUserFindWindowEx, and NtUserBuildHwndList are all functions that are likely used to detect these windows.

In the photos above you can see that we hit NtUserFindWindowEx a few times. I easily patched out the detections for these tools and was able to use a good amount of tools that were not accessible before.

Lets also put a breakpoint on NtWriteFile, NtReadFile, NtOpenFile, connect, send, and recv. Themida shouldnt call any of these functions normally so if our breakpoint gets hit this means that potentially de-virtualized is calling these functions. This is a perfect place to dump the process and analyse it in IDA.

We hit a few breakpoints, but one sticks out to me. Its a call to NtWriteFile, this call writes data to a file name "core32.mni" that is created in the same directly as the loader is in. Lets dump the process and see if we can see anything.

Wow... all of this is unpacked code that IDA is able to parse. This is probably due to poor Themida implementation. Now that we have the 75% of the executable unpacked we can really start to see what this application does. Just look at all of these detection vectors.

There was indeed a few hits for "core" in the strings of this dump. Close to the core string we can see a image/jpeg string. After renaming the core32.mni file to a jpeg file we can see that its a screenshot of my desktop. This is further backed by the networking data that is sent to the server. The packet consists of the packet type (S) for screen shot followed by the username (thiswillbeez) of the user, then the time (time is caculated client side, I find this funny), and finally the reason for detection.

Taking screenshots and sending them to the server without concent is not the nicest thing to do. Since it writes the image to a file and then sends it the easist way to fix this is delete the core32.mni file, download an image of your choice, renaming it to core32.mni, changing the file permissions to READ ONLY (even for administrators). This will make it so everytime the application decides its time to take a screenshot and send it to the server we will be sending our image instead. This is what I did. (Blurred for viewers)

Downloaded Files


After logging in and selecting which cheat you would like (Rainbow Six) the loader will download two files and save them to disk inside of the working directory. These files are named "notepad.exe" and "framework.exe", both packed with Themida, both with the same detection vectors.

Sometimes looking at the file sizes can give you a few hints on whether or not the application is going to download anything more from the server.

Framework.exe (kdmapper)


Framework.exe is actually kdmapper. After running it in a debugger and finding that you need to pass a specific string as a flag it will get your HWID's and download the driver from the server. As seen below it looks like the driver is missing the entire DOS header (MZ and DOS string). You can also see the server sends back the size of the driver.

It turns out that the only missing bytes from the header are MZ, opening it in IDA once I change the first two bytes to MZ shows all imports/exports. Now that I have the driver I need to figure out how the usermode application and driver talk to each other. Kdmapper usually takes advantage of syscall hooking. This turns out to be the case. In the dump of framework.exe I searched for strings with "Nt" in them and then sorted by size. This gave me the following strings which lead me to believe that "NtTokenManagerConfirmOutstandingAnalogToken" was the syscall that is being hooked.

So now that we know which syscall is being hooked, its time to modify kdmapper to work with this syscall. Doing so isnt that hard, all I did was found dxgkrnl.sys inside of C:\Windows\System32\drivers\dxgkrnl.sys. I then opened it in IDA and found "NtTokenManagerConfirmOutstandingAnalogToken" in the exports, copied the bytes needed when uninstalling the hook (you can find this exact line of code here).

Now that we have a working Kdmapper lets move onto getting the usermode application to work.

Since we were able to simply map the driver ourselves it removed the need to spend anymore time on the mapper. It also saved me a bunch of time to just map it myself. If you ever run into a situation like this I highly suggest you take the easy route like me.

Notepad.exe (Usermode Application)


Notepad.exe is actually the usermode application that communicates with the driver. This application will open a file inside of your users root directory that contains your username/password.

The cheat then creates a hash with your RAM's HWID. It gets this HWID by using WMI to query your system for a SerialNumber that is not null. Keep in mind that if you are using a spoofer or you are inside of a VM these WMI values may not be present. If these queries fail (return NULL) then the application closes. Here are the WMI queries:

Given your username, password, hash of your HWID, and a Randomly generated int a "Q" packet is sent. (This is synonymous a login packet). An example of this login packet looks like this:

The server then responds with a hash that both the client and server compute. If these hashes are the same then application continues execution otherwise the application closes. After the application successfully logs in it then asks the server for a few base offsets (r6s_gamemanager, r6s_interfacemanager A.K.A profile manager, r6s_networkmanager, r6s_rendermanager).

Finally the past packet that is sent is an ONL packet. This packet is more of a last check type of thing. This packet is required to make the cheat work. Without it the cheat will close and say "Authentication Error".

Putting It All Together


All of the stuff above this section was only half of the work. The hardest part is about to begin.

Since this application is protected by Themida injecting a DLL is not going to be possible due to anti cracking related stuff. This leaves us being external so to speak. Lets make a list of the things we need to overcome.

  • 0.) We need to map the driver, this wont be hard ¯\_(ツ)_/¯
  • 1.) We need to make usermode.exe ALWAYS get the same HWID (Even on other systems). (difficult)
  • 2.) We need to be able to make the SAME hash of our HWID every single time. (hard)
  • 3.) We need to be able to have the same username/password. (not hard)
  • 4.) We need to make the randomly generated INT the same every single time. (difficult)
  • 5.) We need to emulate the server without being to invasive (not too hard)
  • 6.) We need to send offsets back to the application. (not hard)
  • 7.) We need to send ONL responce. (easy)

    Getting The Same HWID And Hash On All Systems. (0,1,2)


    Skipping step zero since we are already able to map the driver, onto step two.... notepad.exe (usermode.exe) gets HWID's by making WQL-WMI queries. This was the page I read to understand how it works.

    Since the query string is in rdata section we can get the offsets to these strings with IDA. Then at runtime we can write whatever query string we want over the actual query string. This wont help though if we cant plant my RAM's serial number somewhere in the database where we can later make the usermode application go and get it.

    After much planning I found a way to create a WMI object using powershell. This object will have my RAM's HWID under the Name variable, and my motherboards serial number under VariableValue. The powershell command is this:

  • Set-WmiInstance -Class Win32_Environment -Argument @{Name = '0x484D54333531533642465238432D50422020'; VariableValue='C023205001HFFW8A0'; UserName=''}

    Now that we have the HWID's from a valid session inside of a WMI object now we just need to make a query string that does not exceed the original in length. If we make one that is longer then we will be overwriting random things in rdata. The query string that I decided on looks like this:

  • Select Name FROM Win32_Environment WHERE Name LIKE '0x%'

    The query string has WHERE Name LIKE '0x%' is because there are MANY other important Win32_Environment variables, but none of them have Name variables that start with '0x' except the one that contains the HWID. This also keeps us from making a huge query string and overwriting over rdata.

    Now that we have the query strings and HWID's memory all that is left to do is to write in the query string at runtime. To do this all I had to do was write my null terminating query string to usermode.exe + 0x96700 . Since this in rdata it will always have 0x96700 as a static RVA. Nice! This means that the hash will always be the same since the query results are always the same. Thats it for 0,1, and 2.

    Username/Password


    Username/Password are stored inside of a file which is located inside of C:\Users\%USERNAME%\. The name of this file is: "OK2sRbtxYM". Its a plain text file that contains the following syntax: [username]|[password].

    This makes it really easy to have the same username/password on multiple machines.

    Random Int Not So Random.


    As seen before the randomly generated int is used as some sort of salt. This randomly generated int is part of the hash that both the client and server calculate. Our end goal is to take a prior session reuse it every time. This can only be done if all of the data is the same.

    I was able to run cheat engine with usermode.exe without it closing, but the mapper and the loader would both close. If I had to guess the creator of this cheat was testing/working on the usermode application along side cheat engine. Mostly luck on my side though. Cheat Engine opens an entire section of stuff that was not accessible before. Finding this randomly generated int is now just a matter of pointer scanning. I created a pointer chain for the random int value and tested the offsets on multiple VM's and computers to make sure it works. Again, I was very lucky I could use this tool. With that said these are the offsets that I found to work:

    Now its just a matter of changing these values at the right time. In the next section I will go over when and why I change the random INTS at the time I do.

    Emulating The Server.


    Emulating a server can be done gracefully or obnoxiously depending on implementation. There are many factors at play when it comes to emulating a server. Does the protocol use any form of TLS/SSL? Does the application make any DNS requests? (this makes it easy to send back a different IP). Lucky for me this application connected directly to the server (no DNS requests). There is also NO SSL/TLS, nor any form of encryption.

    When direct IP access is at play the first thing that may come to mind is IP-Tables for us linux users. These is a little too invasive for me and since we are able to see all rdata values in IDA why not just change the IP address at runtime to localhost (127.0.0.1)? This is what I did, I changed the IP to 127.0.0.1 (localhost) and listened on the same port. This worked very well, but just in case this is not an option for you and you also dont want to mess with IP-Tables you can use AddIpAddress, this is a less invasive option but still more than I felt was needed. So all we need to do is write "127.0.0.1" to usermode.exe + 0x96050 , and usermode.exe + 0xA5090 .

    Now in the last section I said I would wait until a specific time to change the random int. I change the random int when I receive the login packet. The reason I do this is because 1.) the random int is generated right before the packet is sent and is hashed when it receives a responce from the server. This gives us the chance to do something like
    if(packetType == LOGIN)
    {
         PatchRandomInt();
         send(&ClientSocket, &Memory::Patch::ValidHash, sizeof(Memory::Patch::ValidHash), NULL);
    }

    Sending Offsets and ONL Packet.


    This is really the easist part of the entire venture. Simply send back the up-to-date offsets as a null terminating string (not hex string but int). As for the ONL packet, all you need to do is send back fuckyouxeroxz\r\n . This will prevent the application from closing due to invalid authentication.

    Final Thoughts and Usage.


    This cheat had the potential to be ALOT more difficult. Although this was a Themida protected application it didnt really feel any more difficult due to the fact that Themida unpacked all of those sections which allowed me to see a good chunk of the application in IDA.

    Here are some images and videos of the cheat in action. (As you can see this cheat is stream proof.)