Motivation

A few weeks ago one idea came to my mind, it was time to go for something a little harder, try to actually hack something that uses a well known Anti-cheat, analyze it statically and dynamically, and learn about how the Anti-cheat do to keep cheaters away from the games.

As you may know, tools like CheatEngine can’t even be running while you play those games. Great! Another challenge, we could try to analyze how an Anti-cheat does to detect it, and compile our own version of CheatEngine that avoid all those controls. You can find different posts about this on the internet, many of them have been written many years ago, but you never learn until you try to do it by yourself, that’s why I’m going to make this write up about the things I learned on the way.

So here we are, at the beginning of a long journey. We are going to analyze Black Desert Online, some portions of its anticheat (XINGCODE 3) and I will be going to write a series of blog post about all the things that result interesting from this process.

Introduction

While I was doing this and writing this blog post, @vmcall wrote two posts about this game that really caught my attention and I truly recommend you to read them if you want to enjoy two good write-ups about this game. My intention would be to complement those posts and share my experience. I also want to thanks @NoxOner for the pro tips he gave me 😉

On the first post @vmcall explains how to dump the process of Black Desert (BlackDesert64.exe) by using Scylla and x64dbg. We are going to see on this post a few things a did when I dumped the process in order to make the statically reversing easier.

Black Desert binary has been packed with a well-known packer called Themida. Usually, when you dump a process from memory, specially if this process is packed, there are multiple things that get corrupted or to say it correctly, don’t get dump as expected.

Output of RDB Packer Detector

One of those things is the IAT (we are going to see what a IAT is and how to identify them). If we dump a process and for some reason we don’t have this table, reversing will be extremely harder since we are blind and we are going to see calls to functions like qword_1419FB008 instead of GetProcAddress. If we are able to see the real name, things become much easier, right?

Another interesting thing to do is apply FLIRT signatures from IDA. These signature come with IDA and you can find a lot of public collections on the internet that when loaded into a binary will try to identify well-known functions from multiple libraries.

Import Address Table (IAT)

Usually, function addresses from a dll are not static, therefore the Import Address Table is used to store all the function pointers from the loaded dlls used by the process. Basically, it is a portion of memory where you will find a list of pointers to each of the functions that the program is used. Whenever the process need to access to i.e WriteFile from kernel32.dll, the program will point to the IAT table in order to obtain the real address of the function inside the loaded dll.

In the image we can see a dump of an IAT from a running process. Two main columns can be appreciate it: the first one belong to the address in memory for each entry of the IAT called Address; the second is a pointer to the real address of each function from the loaded dlls. By doing this, whenever the program wants to access to any function from a dlls it will point to this table in order to retrieve the dynamic address of the function.

The third column is a comment added by x64dbg, which resolves the name of the function from each pointer based on the symbols loaded in memory by the debugger. When we dump a process from memory, we won’t be able to see this last information since all the pointer won’t longer exist on the static dump, and whatever tool we are using won’t be able to resolve those pointers as x64dbg did here.

Is there a way to restore this info for an static analysis?

Yes, of course. If we dump a process by using Scylla, this tool will automatically try to identifythe IAT inside of the memory , but packers are nasty and usually they remove the original IAT and allocate a one new table in memory after unpacking the real binary. This tools may have problem identifying the real IAT in memory, actually if we try to do this Scylla will tell us the that the size of the IAT table is 8 bytes (one entry), this can’t be possible.

IAT is typically an array of pointers grouped together and with a null-pointer that separates each DLL’s from another. Identify them in memory is quite trivial for both cases (statically with IDA and dinamically with x64dbg). For x64dbg, you can just simple look for any piece of assembly that makes a call to a function from a dll and follow the pointer to the IAT:

Call to GetModuleHandleW

In order to identify the IAT inside the dump, we need to look for an array of pointers as we explained before. One possibility could be to look for call to functions with this format qword_1419FB008, which would be something like call cs: qword_1419FB008 . If we follow this variable in IDA we will find the IAT that that looks pretty much to something like this:

IAT with IDA

In the last image we can see multiple entries of the IAT and also at the right we can see all the xref that IDA has identified. Black Desert Online IAT table contains around 1000 entries, you can imagine how this will simplify the static reversing 😉

Anti-cheat initialization without symbols

Restoring the IAT

Now that we have identified the IAT table in the running process as well in the static dump, we could know try to migrate all the symbols from the process to IDA. Since x64dbg is resolving those symbols for us we could try to copy the IAT from the running process and rename the functions on IDA so we can use them when reversing.

I assume there is a better way to do this, but I decided to do the first part manually and then write an small script with IDAPython. First, we are going to go to the beginning of the IAT with x64xdb and we do “Right click -> Follow in Dump” on the first entry of the table. This will set the Dump 1 as follows:

IAT in x64dbg

Now we are going to select all the values starting on the first address until the last entry of the table. Finally, we are going to do “Right Click -> Copy -> Selected Lines”. If we store this on a new txt file we will have something like this:

00000001419FB000 00007FFC8F3C38F0 advapi32.SetServiceStatusStub 00000001419FB008 00007FFC8F3C3910 advapi32.CryptGenRandomStub 00000001419FB010 00007FFC8F3C3390 advapi32.CryptReleaseContextStub 00000001419FB018 00007FFC8F3C2F60 advapi32.CryptAcquireContextWStub 00000001419FB020 00007FFC8F3C2F80 advapi32.RegQueryValueExAStub 00000001419FB028 00007FFC8F3C2EA0 advapi32.RegOpenKeyExAStub 00000001419FB030 00007FFC8F3C3320 advapi32.CryptAcquireContextAStub 00000001419FB038 00007FFC8F3C7140 advapi32.RegDeleteValueWStub 00000001419FB040 00007FFC8F3C2A10 advapi32.RegCreateKeyExWStub ... ... 00000001419FB308 00007FFC8FE40D30 kernel32.FindFirstFileA 00000001419FB310 00007FFC8FE40DA0 kernel32.FindNextFileA 00000001419FB318 00007FFC8FE3FB30 kernel32.MoveFileExW 00000001419FB320 00007FFC8FE41050 kernel32.RemoveDirectoryW 00000001419FB328 00007FFC8FE41080 kernel32.SetFileAttributesW 00000001419FB330 00007FFC8FE40E20 kernel32.GetDiskFreeSpaceExW

We have three columns but we are interested in the first and the last one. Why? Because the second one only contains the dynamic addresses of each function and we don’t care about that when reversing statically with IDA because those modules addresses are not present on the dump. If we can just rename each pointer name with the name of the method, IDA will automatically identify most of those functions from the windows API and dynamically update the arguments and types on the dump.

In order to do this automatically IDA provides us an API called IDAPython. With this, we can write a small script that basically obtains each line of the IAT, search each memory address inside the dump, and rename the function name with the real name of the method.

The result will be something like this:

import idaapi import idautils import idc if __name__ == '__main__': file = open( 'IAT.txt', 'r') for line in file: columns = line.split(' ') address = columns[0] fnName = columns[2].replace('\r', '').replace('

', '') if '.' in fnName: fnName = fnName.split('.')[1] print address ' ' fnName idc.MakeNameEx(int(address, 16), fnName, idc.SN_NOWARN) file.close()

The script is going to iterate through each line from the IAT and it will place the correct name for each memory address inside the dump.

Fixed IAT inside the dump.

IDA will automatically recognize some of the methods and it will properly add the arguments and function information to each of the entries.

Do you remember the image about the Anti-cheat initialization? This is how it looks now:

Much better right? 🙂 Picture your self that now we have imported almost 1000 methods to the dump IAT that will be available inside the whole binary.

FLIRT signatures

The second thing I like to do, especially for stripped binaries, is to use Fast Library Identification and Recognition Technology (FLIRT) signatures. This allows IDA to search for functions from multiple libraries inside a binary by analyzing each byte of the disassembled program in order to determinate if it corresponds to the beginning of a known library function.

This information required to identify each function is stored in files with all the patterns for each function. To use this from IDA you just need to open the Signature Subview and do “Right click -> Apply new signature”:

Menu to select the signature to be applied.

Selecting each signature will depend on our criteria to identify related Libraries. After apply them, you will see something like this:

Conclusion

Taking time to parse and improve the information available on a binary before proceeding with the reversing is really important and it gives us a good advantage against those programs that are hard to reverse such as an Anti-cheat. IDA has multiple plugins such as ClassInformer that makes reversing C++ programs much easier.