RCE Endeavors 😅

November 19, 2021

Reverse Engineering REST APIs: Hooking Winsock (5/12)

Filed under: Game Hacking,Programming,Reverse Engineering — admin @ 12:28 PM

Table of Contents:

Having identified some promising leads in the previous post, it is time to start the investigation. To begin with, the Xbox Live API functions HCHttpCallRequestSetUrl, HCHttpCallRequestSetHeader, HCHttpCallRequestSetRequestBodyBytes, and similar all look very promising. If they are indeed used for the client-server communication that we are interested in, then it should be a simple matter of hooking into them and getting a hold of the HTTP content.

We can begin by attaching x64dbg to the Age of Empires IV process (RelicCardinal.exe), and finding what functions reference these strings. Lets start with HCHttpCallRequestSetUrl. In this case, there is only one location:

00007FF64CC88673 | 48:C7C3 FFFFFFFF               | mov rbx,FFFFFFFFFFFFFFFF                          |
00007FF64CC8867A | 4C:8BC3                        | mov r8,rbx                                        |
00007FF64CC8867D | 0F1F00                         | nop dword ptr ds:[rax],eax                        |
00007FF64CC88680 | 49:FFC0                        | inc r8                                            |
00007FF64CC88683 | 42:803C07 00                   | cmp byte ptr ds:[rdi+r8],0                        |
00007FF64CC88688 | 75 F6                          | jne reliccardinal.7FF64CC88680                    |
00007FF64CC8868A | 49:8D4E 08                     | lea rcx,qword ptr ds:[r14+8]                      |
00007FF64CC8868E | 48:8BD7                        | mov rdx,rdi                                       |
00007FF64CC88691 | E8 9AEBFFFF                    | call reliccardinal.7FF64CC87230                   |
00007FF64CC88696 | 90                             | nop                                               |
00007FF64CC88697 | 4C:8BC3                        | mov r8,rbx                                        |
00007FF64CC8869A | 66:0F1F4400 00                 | nop word ptr ds:[rax+rax],ax                      |
00007FF64CC886A0 | 49:FFC0                        | inc r8                                            |
00007FF64CC886A3 | 42:803C06 00                   | cmp byte ptr ds:[rsi+r8],0                        |
00007FF64CC886A8 | 75 F6                          | jne reliccardinal.7FF64CC886A0                    |
00007FF64CC886AA | 49:8D4E 28                     | lea rcx,qword ptr ds:[r14+28]                     |
00007FF64CC886AE | 48:8BD6                        | mov rdx,rsi                                       |
00007FF64CC886B1 | E8 7AEBFFFF                    | call reliccardinal.7FF64CC87230                   |
00007FF64CC886B6 | 90                             | nop                                               |
00007FF64CC886B7 | 41:80BE 20010000 00            | cmp byte ptr ds:[r14+120],0                       |
00007FF64CC886BF | 74 2A                          | je reliccardinal.7FF64CC886EB                     |
00007FF64CC886C1 | 48:897424 28                   | mov qword ptr ss:[rsp+28],rsi                     | [rsp+28]:"­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­
00007FF64CC886C6 | 48:897C24 20                   | mov qword ptr ss:[rsp+20],rdi                     |
00007FF64CC886CB | 4D:8B8E 18010000               | mov r9,qword ptr ds:[r14+118]                     |
00007FF64CC886D2 | 4C:8D05 0FAD3103               | lea r8,qword ptr ds:[7FF64FFA33E8]                | 00007FF64FFA33E8:"HCHttpCallRequestSetUrl [ID %llu]: method=%s url=%s"
00007FF64CC886D9 | BA 04000000                    | mov edx,4                                         |
00007FF64CC886DE | 48:8D0D DB51EF04               | lea rcx,qword ptr ds:[7FF651B7D8C0]               | 00007FF651B7D8C0:&"HTTPCLIENT"
00007FF64CC886E5 | E8 46ADFFFF                    | call reliccardinal.7FF64CC83430                   |

Lets begin by taking a look at the structure of these instructions. We can see that there are two blocks [00007FF64CC88673 to 00007FF64CC88691] and [00007FF64CC88697 to 00007FF64CC886B1], which perform very similar functionality: they load some parameters, and call reliccardinal.7FF64CC87230. To analyze this assembly listing, we will start bottom-up and look at the block beginning at 00007FF64CC88697. Why do it this way? Because it is closer to the instructions that print out information related to HCHttpCallRequestSetUrl.

Here, at 00007FF64CC886B1, we call reliccardinal.7FF64CC87230, and then check if [R12+0x120] is equal to 0. If it is not, we skip calling next set of instructions; otherwise we continue executing and call reliccardinal.7FF64CC83430. The format string provides a lot of information about the arguments of this function. We can deduce that [R14+0x118] holds the request ID, [RSP+0x20] holds the method, and [RSP+0x28] holds the URL. Following the x64 calling convention, and taking hints from the disassembly, we can deduce that the function call looks similar to

reliccardinal_7FF64CC83430((void *)0x7FF651B7D8C0, 4, "HCHttpCallRequestSetUrl [ID %llu]: method=%s url=%s",
    id, method, url);

This is a good start. We can now work backwards to learn more. If we look at the call to reliccardinal.7FF64CC87230 that occurs at 00007FF64CC886B1, we see that it takes three arguments: [R14+0x28], and the RSI and R8 registers. The RSI register was loaded into [RSP+28], the last argument of reliccardinal.7FF64CC83430, which we have determined is the request URL. The R8 register appears to be calculated based off of RSI; it is incremented until it reaches a place where [RSI+R8] is 0. Since RSI is a string containing the URL, then R8 must be calculating where the null terminator is, effectively calculating the length of the URL. The value of [R14+0x28] is still undetermined, but if we look higher in the disassembly, towards the beginning of the function, we see these instructions:

00007FF64CC885D9 | 49:8BF0                        | mov rsi,r8                                        |
00007FF64CC885DC | 48:8BFA                        | mov rdi,rdx                                       |
00007FF64CC885DF | 4C:8BF1                        | mov r14,rcx                                       |                                    

The R14 register gets assigned here to RCX, the first argument of this function we are reversing, and doesn’t get re-assigned at any other point in the function. Different offsets into R14 get referenced and passed as arguments to other functions, meaning that it is some kind of structure. We can piece together what it is by looking at the HCHttpCallRequestSetUrl documentation and discover that it is the HCCallHandle opaque pointer. We also see that RDI and RSI get assigned here as well to the second and third arguments respectively. Since we have the function definition from the documentation we can conclude that RDI holds the method and RSI holds the URL. This is consistent with what we found from looking deeper into the function disassembly.

This gives us enough information to see what this block is doing: it is taking the input URL and copying it to an internal buffer inside the HCCallHandle structure. We can apply this logic for the other, nearly identical, block beginning at 00007FF64CC88673 and see that it is doing the same thing for the HTTP method parameter.

Putting all of this together allows us to begin reconstructing the original code of this function. Taking what we know, we can translate the above assembly listing into something that looks like this:

WriteToBuffer(callHandle->MethodBuffer, method, strlen(method));
WriteToBuffer(callHandle->UrlBuffer, url, strlen(url));
if (callHandle->ShowDebugOutput) {
    DebugWrite((void *)0x7FF651B7D8C0, 4, "HCHttpCallRequestSetUrl [ID %llu]: method=%s,
        url=%s", callHandle->Id, callHandle->MethodBuffer, callHandle->UrlBuffer);

With the debugger still attached, we can set breakpoints on where the two writes happen. If the breakpoints get hit, we should be able to get the HTTP method and URL for the outgoing request. So lets do that: set the breakpoints, tab back into the game, and begin performing some actions. If you do that, you will unfortunately find out that the breakpoints never get hit. The game does not appear to be using this function. It is the same story for the other HCHttpCallX functions; the request logic must be happening somewhere else.

Having spent some time on a fruitless search, we can take another approach. Instead of trying to investigate library internals, we can look one level higher at the Windows API. The functions that we are interested in are the send and recv functions provided by the Windows sockets API. Barring some very atypical implementation, all networking functionality will go through these two functions; send will be called to send data out to a socket, and recv will be used to read data from a socket. Lets verify this by setting a breakpoint on send.

Set the breakpoint and perform some activity that would require getting data from the server, i.e. refresh the available game lobbies. If the breakpoint has been successfully set, it should have been hit.

This is a great sign: we have some point at which to begin really reverse engineering and stepping backwards from. We know that the HTTP data must have been constructed at some point prior to calling send. We can take a quick detour and come up with a plan of action. To start with, we can hook the send and recv APIs and dump out the outgoing and incoming buffers. To do this, we will be using Microsoft’s Detours library. We will write this in a DLL that will be injected into the Age of Empires IV process. From within this DLL, we will hook send and recv and redirect them to our hook functions. From these hook functions we will dump out the buffers to a console. To start with, we can write the DllMain function to do this:

__declspec(dllexport) BOOL WINAPI DllMain(HINSTANCE hModule, DWORD dwReason, LPVOID reserved)
	static HookEngine hookEngine{};

	if (dwReason == DLL_PROCESS_ATTACH) {
		if (AllocConsole()) {
			(void)freopen("CONOUT$", "w", stdout);
			(void)freopen("CONOUT$", "w", stderr);
			std::cerr << "DLL Loaded" << std::endl;

		(void)hookEngine.Hook("Ws2_32.dll", "send", sendHook);
		(void)hookEngine.Hook("Ws2_32.dll", "recv", recvHook);

	if (dwReason == DLL_PROCESS_DETACH) {
		(void)hookEngine.Unhook("Ws2_32.dll", "send");
		(void)hookEngine.Unhook("Ws2_32.dll", "recv");

	return TRUE;

Upon attaching to the process, we will allocate a console that we will write the data buffers to. We then hook the send and recv functions in Ws2_32.dll. The HookEngine class is just a wrapper around the Detours API. The Hook function is implemented as follows:

bool HookEngine::Hook(std::string_view moduleName, std::string_view functionName, HookFncPtr hookAddress)
	if (IsHooked(moduleName, functionName)) {
		std::cerr << std::format("{}:{} is already hooked.", moduleName, functionName)
			<< std::endl;
		return false;

	auto functionAddress{ GetFunctionAddress(moduleName, functionName) };
	if (functionAddress == nullptr) {
		std::cerr << std::format("Hook installation failed. Address for {}:{} is nullptr.", moduleName, functionName)
			<< std::endl;
		return false;

	auto result{ Hook(functionAddress, hookAddress) };
	if (!result) {
		return false;

	m_hookedFunctions[std::string{ moduleName }].push_back(std::make_pair(std::string{ functionName }, functionAddress));

	std::cerr << std::format("Hook installed on {}:{} successfully.", moduleName, functionName)
		<< std::endl;

	return true;

bool HookEngine::Hook(FncPtr originalAddress, HookFncPtr hookAddress)
	DetourAttach(&(PVOID&)originalAddress, (PVOID)hookAddress);
	auto result{ DetourTransactionCommit() };
	if (result != NO_ERROR) {
		std::cerr << std::format("Hook transaction failed with code {}.", result) << std::endl;
		return false;

	m_hookToOriginal[hookAddress] = originalAddress;

	return true;

For more details on the implementation, you can view the source listing in the Github link provided in the table of contents at the top of this post.

We define the sendHook and recvHook functions, which will be called when the target process calls send and recv. These functions do nothing except print out the incoming and outgoing buffers in a nice format.

template <typename ReturnType, typename FunctionType>
ReturnType PassthroughHook(void *caller, SOCKET socket, char *buffer, int length, int flags)
	if (!IsIgnoredPacket(buffer, length)) {
		auto output{ MakePrintableAscii(buffer, length) };
		auto [ipAddress, port] { GetPeerInfo(socket) };
		for (const auto& line : output) {
			std::cerr << std::format("[{}:{}] - Data: {}", ipAddress, port, line)
				<< std::endl;

	auto original{ (FunctionType)HookEngine::GetOriginalAddressFromHook(caller) };
	ReturnType result{};
	if (original != nullptr) {
		result = original(socket, buffer, length, flags);

	return result;

int WSAAPI sendHook(SOCKET socket, const char* buffer, int length, int flags)
	return PassthroughHook<int, sendFnc>(sendHook, socket, const_cast<char*>(buffer), length, flags);

int WSAAPI recvHook(SOCKET socket, char* buffer, int length, int flags)
	return PassthroughHook<int, recvFnc>(recvHook, socket, buffer, length, flags);

We also define an IsIgnoredPacket function that prevents printing certain data buffers. This is to prevent our screen from being flooded with calls that happen constantly, i.e. heartbeats and other calls that we are not interested in. Building this DLL and injecting it into the process shows that the hooks are being called. We can see data coming in and going out from the process. However, when looking at it, we can’t really see anything useful.

There is some occasional information that is visible in plaintext, but it is hard to get the context of the data and what it matches up to.

This is a good start, but we have cast too wide of a net. By hooking send and recv, we are monitoring everything coming in and going out over the network. If the goal is to get at the REST APIs, we need to go further down in the code and isolate the logic responsible for them. That will be the topic of the next posts.

November 18, 2021

Reverse Engineering REST APIs: Debugging (4/12)

Filed under: Game Hacking,Programming,Reverse Engineering — admin @ 2:15 PM

Table of Contents:

The simplest way to begin reverse engineering an application is to look for hints from inside the application itself as to what it is doing. In a dynamic analysis setting, where we are going to examine a running executable, this is best done by attaching a debugger to it. From there we can begin looking at string references and see if there is anything interesting.

For example, our target goal is to find the request builder and response decryption functions in the target executable. After attaching a debugger, we can do a string dump and look for values such as “packet”, “encrypt”, “decrypt”, “message”, “response”, etc. and see if we can get any leads into where these functions might be located. We can also try to find out if they are implemented internally, or via a third-party library, and what encryption libraries are used, as well as other relevant features of the client-server communication flow.

To begin, lets attach x64dbg to the Age of Empires IV executable (RelicCardinal.exe). From the module view in the Symbols tab, we can find the main executable.

Once we are viewing the main module (via double clicking the row with reliccardinal.exe), we can take a look at the string references (right click, Search for -> Current Module -> String references).

From inspecting the network traffic previously, we know that TLS is being used to perform secure calls to the server. We can take a look for strings such as “encrypt”, “decrypt”, “tls”, and “ssl” to see if there is anything interesting. The first three searches didn’t yield anything too much, however, when looking for “ssl”, there is something notable. There is a string reference to SSLKEYLOGFILE.

SSLKEYLOGFILE is a special environment variable that can be set to log SSL key information. The contents of this file can then be used by Wireshark to decrypt the network traffic. If the game supports the use of this environment variable then it is another way to see the plaintext network traffic without having to deal with the hassle of setting up a HTTPS proxy and routing the application traffic through it. To test it is simple: create an environment variable called SSLKEYLOGFILE and set its value to a path on your filesystem. Then launch the game and see if anything is written out to the file. If you’ve done it successfully, there should be SSL key information in the file, confirming that the use of this environment variable is enabled.

Having this key information, we should be able to see decrypted data in Wireshark. To see this, we can launch Wireshark and begin monitoring. We then launch the game and perform some actions to ensure REST calls are being made. Finally, we import the SSL key information in Wireshark via Edit -> Preferences -> Protocols -> TLS and provide the SSL key file.

Once these changes are applied, we can filter out traffic to the server and verify that the decrypted application data is visible.

The decrypted requests appear to be present. We can view the full request-response flow by right clicking on a request and selecting Follow -> HTTP Stream

This quick find provided all of the insight needed to reverse engineer the REST APIs. But what if the SSLKEYLOGFILE environment variable was unsupported? We would have to dig a bit deeper into the strings dump, which we’ll do while we’re here anyway. Searching for “http” provides some interesting information:


These appear to be WebSocket functions. These are good to know, but probably not what we are interested in, as the client-server communication we are reverse engineering is not done via WebSockets. If these functions are used, it is probably from within a multiplayer game instance where the lower overhead full-duplex connection that WebSockets provide is a good choice.

"%s HTTP/%s\r\n%s%s%s%s%s%s%s%s%s%s%s"

This looks like a format string for an HTTP request.


These are constants used by the Xbox Live Networking library.


Different endpoints that may be called by the game. These can be useful as filters in Wireshark at some point, but not relevant to the endpoint that we are interested in.

"HCHttpCallRequestSetHeader [ID %llu]: %s=%s"
"HCHttpCallRequestSetRequestBodyBytes [ID %llu]: requestBodySize=%lu"
"HCHttpCallRequestSetRetryAllowed [ID %llu]: retryAllowed=%s"
"HCHttpCallRequestSetRetryCacheId [ID %llu]: retryAfterCacheId=%d"
"HCHttpCallRequestSetTimeout [ID %llu]: timeoutInSeconds=%u"
"HCHttpCallRequestTimeoutWindow: %u"
"HCHttpCallRequestSetUrl [ID %llu]: method=%s url=%s"
"HCHttpCallResponseAppendResponseBodyBytes [ID %llu]: bodySize=%llu (total=%llu)"
"HCHttpCallResponseGetResponseString [ID %llu]: responseString=%.2048s"
"HCHttpCallResponseSetResponseHeader [ID %llu]: Duplicated header %s=%s"
"HCHttpCallResponseSetResponseHeader [ID %llu]: %s=%s"
"HCHttpCallResponseSetErrorCode [ID %llu]: errorCode=%08X (%08X)"
"HCHttpCallResponseSetErrorMessage [ID %llu]: errorMessage=%s"
"HCHttpCallResponseSetResponseBodyBytes [ID %llu]: bodySize=%llu"
"HCHttpCallResponseSetStatusCode [ID %llu]: statusCode=%u"

This is a large set of functions related to constructing HTTP requests and retrieving HTTP responses. These come from the Xbox Live API and the functions that reference these strings seem like good candidates for investigation. There are additional references to libHttpClient, which is another library that works in conjunction with Xbox Live API. The API is pretty comprehensive and provides wrappers for WebSockets, REST APIs, and curl-like functionality with Microsoft’s xCurl implementation. As we begin to reverse engineer the executable, these sets of functions will be good to keep in mind.

Going through the string references has revealed a lot of good information about what is going on with respect to the networking functionality. We were lucky to find that the SSLKEYLOGFILE functionality is supported, and also picked up some good leads regarding functions to investigate. The next series of posts will take a look at these functions, and more, with the goal of finding the relevant code for building requests and processing responses.

Reverse Engineering REST APIs: Basics (3/12)

Filed under: Game Hacking,Programming,Reverse Engineering — admin @ 9:54 AM

Table of Contents:

This next series of posts will describe how to reverse engineer a client application in order to find out how it communicates with a server. Specifically, what we will find throughout the rest of the series, is how the Age of Empires IV game builds requests and decrypts responses. This will be done in multiple steps: first, we will do some basic analysis on the application with a debugger attached, then we will begin to look at the networking functions that the game uses in order to perform client-server communication. From there, we will look deeper into the game logic and reverse engineer the internal functions that build and decode messages. Lastly, we will wrap by up hooking these internal functions in order to see the decrypted content, which will grant us full control over what is being sent and received.

To do this is going to require a set of tools and libraries. Below is what will be used throughout the rest of this series:

  • Tool: x64dbg – A great debugger for x86/x64
  • Tool: Visual Studio 2022 – This will be used to develop and debug the API hook DLL
  • Library: Detours – We will use this library to perform the API hooking
  • Library [optional]: Catch2 – Used for writing unit tests

The tools were downloaded as is, and the libraries were downloaded and installed via vcpkg. The source is available for all of the code that will be written and discussed in this series.

November 17, 2021

Reverse Engineering REST APIs: The Easy Way (2/12)

Filed under: Game Hacking,Programming,Reverse Engineering — admin @ 4:33 PM

Table of Contents:

This post starts off the series by showing the easy way to reverse engineer REST APIs: by simply looking at the request and response flow. To start, we will launch the game and use Wireshark to look at the traffic going over the wire. In order to filter out the traffic, we need to find out what IP(s) the game is connected to. After launching the game and running netstat, we get the following output:

netstat -n -p TCP -b


The game is connecting on port 443, which means that communication is happening over TLS. Looking at Wireshark, there is no useful data to be seen since everything is encrypted. At best you can extract the hostname information from the server name indication extension of TLS, though useful information like query strings and content bodies will still remain encrypted.

This is a minor inconvenience, but still an inconvenience nonetheless. To get at the decrypted data requires a few more steps: first, we need to set up a proxy that intercepts traffic over HTTPS, then we need to run the game through this proxy.

Setting up a HTTPS proxy is pretty straightforward: mitmproxy can be used for this. After installing the tool, navigate to the Windows Proxy Settings and add in a proxy running on localhost:8080

Once this is done, you can launch mitmweb.exe from the mitmproxy installation directory. After launching mitmweb.exe, a new browser tab should pop up showing intercepted network flows. This will likely be initially empty.

In order to intercept traffic, mitmproxy needs to install a root certificate. Once mitmweb.exe is running, navigate to mitm.it. If everything is working as expected, there should be options to install the mitmproxy certificate for various operating systems and devices.

After the certificate is installed, the proxy should be fully set up. You can restart mitmweb.exe and observe some traffic being intercepted.

At this point, the proxy is verified as working. You can try to launch the game and begin inspecting traffic. However, if you do that, you will notice that no traffic is coming through. The game will appear to be working fine: it will log in to the network, display messages, show available lobbies, and function just as before. This means that the game’s traffic is not being routed through the proxy that we set up.

This might seem like a big roadblock, but fear not. There are multiple tools that configure applications to run through a proxy. For this I used Proxifier, which is paid software but provides a free 30-day trial. After installing and launching Proxifier, configure the HTTPS proxy by navigating to Profile -> Proxy Servers…

In this prompt, fill in the address of the HTTPS proxy that was set up with mitmproxy.

Once this is done, traffic of every application will be routed through the mitmproxy. To verify, you can launch the game again and look in the mitmproxy tab. You should now see the various APIs that get called from within the game in their unencrypted form.

From here on, you can begin interacting with the game and seeing what messages are sent to the server as a result of your actions. Doing so will allow you to discover more APIs, and you can begin to get a better picture of how the client-server interaction drives what you see in the game. Once you are done monitoring the traffic, you can shut down Proxifier, mitmproxy, and disable the manual proxy settings in Windows.

This concludes the easy way to reverse engineer REST APIs: through the combination of a couple of tools we are able to quickly see the requests and responses that are being sent, even if TLS is being used. The rest of this series will focus on reverse engineering the game itself to get the unencrypted request and response payloads.

Reverse Engineering REST APIs: Introduction (1/12)

Filed under: Game Hacking,Programming,Reverse Engineering — admin @ 4:32 PM

This series of posts will focus on reverse engineering REST APIs. The APIs that will be discussed are those used by the Age of Empires IV lobby and matchmaking system. These are an internal set of APIs that the game uses in order to retrieve, display, and update information about your account. This includes APIs for your profile, completed challenges, items, news, chat messages, available game lobbies, and many more.

These posts will be broken down into an easy way and a hard, but fun, way to get the same results. The easy way is covered in the next post and shows how to configure third-party tools to intercept and display the data. The rest of the series will focus on the more difficult way of reverse engineering the actual game in order to retrieve the request and response content.

A link to each post is provided below:

« Newer Posts

Powered by WordPress