Removing packet encryption from the Shaiya client

Cups

Administrator
Staff member
Developer
Joined
Sep 28, 2023
Messages
19
I've received a request for a guide on how to remove the packet encryption from the Shaiya client. This is useful for the purpose of quickly interacting with the client for the purpose of emulator development if you don't want to have to write supporting code for the encryption process, or simply for analyzing the functions. This guide is specifically for the Shaiya Phoenix Ep6.3 client, but the same logic can be applied to most other clients. I also hope to explain the reasoning behind how I arrived to finding this code.

As you may or may not be aware, the Shaiya client supports logging packet payloads to a "Log.txt" file, when logging is enabled. This data logs packets which are not encrypted, and therefore we can use them as a reference point. Here we see an example of some logged data:
Code:
[20:47:49] [TRACE ] ****************GameInit Connect****************

[20:47:49] [NET   ] RECV>> [A101/00C5] 01 A1 00 03 80 01 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 DF 24 96 07 29 48 23 2F 67 84 A9 9E 74 AF C3 8F AC 9D BF 1F 80 AC EC 5C B3 D8 68 F9 DD FC 5A E5 35 B6 E1 FA 61 BD B6 5F FF 3C 7B D9 04 DE 32 45 60 E2 B7 84 F0 D8 1F 87 CD 3C 51 02 ED BC 66 77 15 F4 9A 89 55 EF EC 69 07 8A CB 11 A6 85 56 B2 DA EC 8A 55 76 F6 64 4F 82 F0 9C 0D 3E 6E 2D 5D 50 01 89 1C 29 77 E4 0B 0A 1D A6 F2 B9 17 1B AD 80 4F 61 BF 4C 60 7C 4F C1 B9 68 23 B2 EF C1 E5 

[20:47:49] [NET   ] SEND>> [A101/0083] 01 A1 80 4A 06 8E EE A2 A3 33 3A C3 AB 16 17 32 F0 39 62 E7 46 12 90 53 28 04 89 AD B6 EC 9E 34 C1 6F 35 2F 7F F8 03 F0 9A B1 EA 9C 9B 0F 5F 8D 34 69 41 1C 8F C1 FF 29 E9 BF A0 D9 50 13 AC 28 1E 42 11 FC BA E1 BA 1E 3F C4 A4 06 1E 2F BB 0F A5 2B 7D B9 3A CB 5E E3 ED 46 57 E9 27 94 ED 53 96 4E 8D D6 CA 4D 0B CF CB AA 3D C1 64 28 16 FF 59 F4 B3 D8 6E 85 6B 8F A0 49 01 AE BF AA 0D EB AA 57 72 

[20:47:49] [TRACE ] ****************GameInit Connect OK****************

It should be quite clear that the packet is decrypted before the "RECV" call, and encrypted after the "SEND" call. Let's start by opening our debugger, and looking for a text reference to the "RECV>>" string, and finding where it is referenced. The function labelled WS2_32_16 is a call to the Winsock recv function, and it attempts to read up to 0x2000 (8192) bytes from the server. If the recv function returns an error, it exits, otherwise it copies the data to a global variable. We then note it enters a loop which processes each packet.
1696065503093.png
1696065712066.png

Let's look at the call to "sub_5CD4F0". It has 3 parameters - v10, v12 and v13. We can clearly see that v13 is not defined in this scope and must be defined earlier somewhere, thus we can deduce it is not relevant for a specific packet. A few function calls reference these parameters:
Code:
sub_4010D0(v12, v8 - 2);
sub_5D7090(&v10, 2u);
sub_5CD4F0("RECV>>", v10, v12, v13);
sub_5D4900(v10, v12);

Let's read this in reverse. 54D900 is the last function call that interests us. If we view the code, we can see that it does a bunch of logic of it's two parameters. "a1" being the packet opcode, "a2" being a pointer to the packet data.
1696066292356.png

Ok great, so we know from the caller point, that "v10" is definitely the opcode and "v12" is definitely the data. Obviously this is decrypted - it appears after "5CD4F0", which we know is our logging function. "5D7090" then, must be some logic which copies the opcode. The opcode is encrypted as part of the process, so we know that our culprit must be "4010D0". This is already quite obvious to the informed reader, as it takes in the raw data, and the length - 2. This is because the length of a packet is never encrypted. Let's look at the code for "4010D0".
1696066526265.png

Okay, this is quite interesting. We see that this function only works if "byte_22FAB30" or "byte_22FAB32" is set to a non-zero value, and then it does some logic. I've got a bit of experience with the encryption format, so I can already tell you that the first block is what we refer to as the "XOR table" encryption which is used after a player has fully loaded into the game world, and the second block is the AES encryption. We can comfortably renaming these two variables to "g_isXorDecryptionEnabled" and "g_isPacketDecryptionEnabled" respectively, and rename "byte_22FAB28" to "g_isAesEnabled". Something worth noting is that xor encryption is only used for server->client packets. Thus, if our aim is to completely disable packet encryption, we can ignore this.

Let's move on to looking for references to the "SEND>>" string. Do you notice anything similar?
1696066941734.png

We do some logging, copy some data, and then... call a function very similar to our previously identified decryption call. This function does something very similar - it checks a variable, and then calls some other functions. Rename "byte_22FAB31" to "g_isPacketEncryptionEnabled". As you'll notice, this function calls exactly the same two functions as the previous "RECV" call, other than the XOR table:
1696067463572.png

Great, so now we've identified which functions are responsible for decrypting and encrypting packets. Now, how do you disable this, you may ask? Well, these are cdecl calls which means the caller must clean up the stack, so we can NOP the call the these functions, but it'd require you to also NOP the "add esp, 8" instruction to avoid stack corruption. Depending on the binary, this can be annoying. A simpler way would be simply to insert a "RETN" statement at the decryption and encryption functions. I'll leave that as a choice to you.

Hopefully you found this guide useful or informative, and encourage users to either learn about the encryption process, or to get a head start with server emulation.
 
Back
Top