kennylovecode Posted May 28 Posted May 28 Hello everyone, it's been a long time since I last looked at anything related to conquest. Recently, a friend asked me if there are any cheats for the 1.6 version, specifically the English version 5017, which doesn't have any added magic items. I searched online for about a night, but didn't find anything suitable. So I decided to make one myself. For CE, a proxy might be a good choice for me I found the basic source code for a proxy on elitepvpers, which is based on version 5180 After some modifications, I am now able to proxy version 5017, but only for packet forwarding. I am currently deeply entangled in a problem that I have been debugging for many days. It may be due to my lack of understanding in the field of encryption and decryption Problem description: When I receive a client packet with type=1052, I decrypt it and it can be decrypted normally. When I was about to re-encrypt this packet and send it to the server, I noticed that the encrypted ciphertext was different from the original ciphertext. Naturally, if I sent it to the server, I wouldn't receive a response. I made many attempts, but they all ended in failure, Then I tried adding some test code at the entry of the basic program and found that the encryption algorithm could decrypt normally, but no matter what content was encrypted, the decryption was incorrect and did not match the encrypted plaintext sent by the client. The following is my encryption class: using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using PojervProxy.OOP; namespace PojervProxy.Cryptography { public class GameCryptographer //Komt Off de code of de ander private server { ushort m_InCounter = 0; ushort m_OutCounter = 0; bool m_UseAlt = false; private byte[] m_Key1 = { 0x9D, 0x90, 0x83, 0x8A, 0xD1, 0x8C, 0xE7, 0xF6, 0x25, 0x28, 0xEB, 0x82, 0x99, 0x64, 0x8F, 0x2E, 0x2D, 0x40, 0xD3, 0xFA, 0xE1, 0xBC, 0xB7, 0xE6, 0xB5, 0xD8, 0x3B, 0xF2, 0xA9, 0x94, 0x5F, 0x1E, 0xBD, 0xF0, 0x23, 0x6A, 0xF1, 0xEC, 0x87, 0xD6, 0x45, 0x88, 0x8B, 0x62, 0xB9, 0xC4, 0x2F, 0x0E, 0x4D, 0xA0, 0x73, 0xDA, 0x01, 0x1C, 0x57, 0xC6, 0xD5, 0x38, 0xDB, 0xD2, 0xC9, 0xF4, 0xFF, 0xFE, 0xDD, 0x50, 0xC3, 0x4A, 0x11, 0x4C, 0x27, 0xB6, 0x65, 0xE8, 0x2B, 0x42, 0xD9, 0x24, 0xCF, 0xEE, 0x6D, 0x00, 0x13, 0xBA, 0x21, 0x7C, 0xF7, 0xA6, 0xF5, 0x98, 0x7B, 0xB2, 0xE9, 0x54, 0x9F, 0xDE, 0xFD, 0xB0, 0x63, 0x2A, 0x31, 0xAC, 0xC7, 0x96, 0x85, 0x48, 0xCB, 0x22, 0xF9, 0x84, 0x6F, 0xCE, 0x8D, 0x60, 0xB3, 0x9A, 0x41, 0xDC, 0x97, 0x86, 0x15, 0xF8, 0x1B, 0x92, 0x09, 0xB4, 0x3F, 0xBE, 0x1D, 0x10, 0x03, 0x0A, 0x51, 0x0C, 0x67, 0x76, 0xA5, 0xA8, 0x6B, 0x02, 0x19, 0xE4, 0x0F, 0xAE, 0xAD, 0xC0, 0x53, 0x7A, 0x61, 0x3C, 0x37, 0x66, 0x35, 0x58, 0xBB, 0x72, 0x29, 0x14, 0xDF, 0x9E, 0x3D, 0x70, 0xA3, 0xEA, 0x71, 0x6C, 0x07, 0x56, 0xC5, 0x08, 0x0B, 0xE2, 0x39, 0x44, 0xAF, 0x8E, 0xCD, 0x20, 0xF3, 0x5A, 0x81, 0x9C, 0xD7, 0x46, 0x55, 0xB8, 0x5B, 0x52, 0x49, 0x74, 0x7F, 0x7E, 0x5D, 0xD0, 0x43, 0xCA, 0x91, 0xCC, 0xA7, 0x36, 0xE5, 0x68, 0xAB, 0xC2, 0x59, 0xA4, 0x4F, 0x6E, 0xED, 0x80, 0x93, 0x3A, 0xA1, 0xFC, 0x77, 0x26, 0x75, 0x18, 0xFB, 0x32, 0x69, 0xD4, 0x1F, 0x5E, 0x7D, 0x30, 0xE3, 0xAA, 0xB1, 0x2C, 0x47, 0x16, 0x05, 0xC8, 0x4B, 0xA2, 0x79, 0x04, 0xEF, 0x4E, 0x0D, 0xE0, 0x33, 0x1A, 0xC1, 0x5C, 0x17, 0x06, 0x95, 0x78, 0x9B, 0x12, 0x89, 0x34, 0xBF, 0x3E}; private byte[] m_Key2 = { 0x62, 0x4F, 0xE8, 0x15, 0xDE, 0xEB, 0x04, 0x91, 0x1A, 0xC7, 0xE0, 0x4D, 0x16, 0xE3, 0x7C, 0x49, 0xD2, 0x3F, 0xD8, 0x85, 0x4E, 0xDB, 0xF4, 0x01, 0x8A, 0xB7, 0xD0, 0xBD, 0x86, 0xD3, 0x6C, 0xB9, 0x42, 0x2F, 0xC8, 0xF5, 0xBE, 0xCB, 0xE4, 0x71, 0xFA, 0xA7, 0xC0, 0x2D, 0xF6, 0xC3, 0x5C, 0x29, 0xB2, 0x1F, 0xB8, 0x65, 0x2E, 0xBB, 0xD4, 0xE1, 0x6A, 0x97, 0xB0, 0x9D, 0x66, 0xB3, 0x4C, 0x99, 0x22, 0x0F, 0xA8, 0xD5, 0x9E, 0xAB, 0xC4, 0x51, 0xDA, 0x87, 0xA0, 0x0D, 0xD6, 0xA3, 0x3C, 0x09, 0x92, 0xFF, 0x98, 0x45, 0x0E, 0x9B, 0xB4, 0xC1, 0x4A, 0x77, 0x90, 0x7D, 0x46, 0x93, 0x2C, 0x79, 0x02, 0xEF, 0x88, 0xB5, 0x7E, 0x8B, 0xA4, 0x31, 0xBA, 0x67, 0x80, 0xED, 0xB6, 0x83, 0x1C, 0xE9, 0x72, 0xDF, 0x78, 0x25, 0xEE, 0x7B, 0x94, 0xA1, 0x2A, 0x57, 0x70, 0x5D, 0x26, 0x73, 0x0C, 0x59, 0xE2, 0xCF, 0x68, 0x95, 0x5E, 0x6B, 0x84, 0x11, 0x9A, 0x47, 0x60, 0xCD, 0x96, 0x63, 0xFC, 0xC9, 0x52, 0xBF, 0x58, 0x05, 0xCE, 0x5B, 0x74, 0x81, 0x0A, 0x37, 0x50, 0x3D, 0x06, 0x53, 0xEC, 0x39, 0xC2, 0xAF, 0x48, 0x75, 0x3E, 0x4B, 0x64, 0xF1, 0x7A, 0x27, 0x40, 0xAD, 0x76, 0x43, 0xDC, 0xA9, 0x32, 0x9F, 0x38, 0xE5, 0xAE, 0x3B, 0x54, 0x61, 0xEA, 0x17, 0x30, 0x1D, 0xE6, 0x33, 0xCC, 0x19, 0xA2, 0x8F, 0x28, 0x55, 0x1E, 0x2B, 0x44, 0xD1, 0x5A, 0x07, 0x20, 0x8D, 0x56, 0x23, 0xBC, 0x89, 0x12, 0x7F, 0x18, 0xC5, 0x8E, 0x1B, 0x34, 0x41, 0xCA, 0xF7, 0x10, 0xFD, 0xC6, 0x13, 0xAC, 0xF9, 0x82, 0x6F, 0x08, 0x35, 0xFE, 0x0B, 0x24, 0xB1, 0x3A, 0xE7, 0x00, 0x6D, 0x36, 0x03, 0x9C, 0x69, 0xF2, 0x5F, 0xF8, 0xA5, 0x6E, 0xFB, 0x14, 0x21, 0xAA, 0xD7, 0xF0, 0xDD, 0xA6, 0xF3, 0x8C, 0xD9}; private byte[] m_Key3; private byte[] m_Key4; public GameCryptographer() { } public ushort InCounter { get { return m_InCounter; } } public ushort OutCounter { get { return m_OutCounter; } } public void SetKeys(byte[] InKey1, byte[] InKey2) { byte[] addKey1 = new byte[4]; byte[] addKey2 = new byte[4]; byte[] addResult = new byte[4]; //addKey1.i = 0; //addKey2.i = 0; byte[] tempKey = new byte[4]; long LMULer; // InKey1[0] = 0x20; // InKey1[1] = 0x5c; // InKey1[2] = 0x48; // InKey1[3] = 0xf4; // InKey2[0] = 0x00; // InKey2[1] = 0x44; // InKey2[2] = 0xa6; // InKey2[3] = 0x2e; //if (Key3) delete [] Key3; //if (Key4) delete [] Key4; Monitor.Enter(this); m_Key3 = new Byte[256]; m_Key4 = new Byte[256]; for (int x = 0; x < 4; x++) { addKey1[x] = InKey1[3 - x]; addKey2[x] = InKey2[3 - x]; } //cout << "Key1: " << addKey1.i << endl; //cout << "Key2: " << addKey2.i << endl; uint Adder1; uint Adder2; uint Adder3; Adder1 = (uint)((addKey1[3] << 24) | (addKey1[2] << 16) | (addKey1[1] << 8) | (addKey1[0])); Adder2 = (uint)((addKey2[3] << 24) | (addKey2[2] << 16) | (addKey2[1] << 8) | (addKey2[0])); Adder3 = Adder1 + Adder2; addResult[0] = (byte)(Adder3 & 0xff); addResult[1] = (byte)((Adder3 >> 8) & 0xff); addResult[2] = (byte)((Adder3 >> 16) & 0xff); addResult[3] = (byte)((Adder3 >> 24) & 0xff); for (int b = 3; b >= 0; b--) { // printf("%.2x ", addResult.c[b]); tempKey[3 - b] = addResult[b]; } tempKey[2] = (byte)(tempKey[2] ^ (byte)0x43); tempKey[3] = (byte)(tempKey[3] ^ (byte)0x21); for (int b = 0; b < 4; b++) { tempKey[b] = (byte)(tempKey[b] ^ InKey1[b]); } //Build the 3rd Key for (int b = 0; b < 256; b++) { m_Key3[b] = (byte)(tempKey[3 - (b % 4)] ^ m_Key1[b]); } for (int x = 0; x < 4; x++) { addResult[x] = tempKey[3 - x]; } Adder3 = (uint)((addResult[3] << 24) | (addResult[2] << 16) | (addResult[1] << 8) | (addResult[0])); LMULer = Adder3 * Adder3; LMULer = LMULer << 32; LMULer = LMULer >> 32; Adder3 = Convert.ToUInt32(LMULer & 0xffffffff); addResult[0] = (byte)(Adder3 & 0xff); addResult[1] = (byte)((Adder3 >> 8) & 0xff); addResult[2] = (byte)((Adder3 >> 16) & 0xff); addResult[3] = (byte)((Adder3 >> 24) & 0xff); for (int b = 3; b >= 0; b--) { tempKey[3 - b] = addResult[b]; } //Build the 4th Key for (int b = 0; b < 256; b++) { m_Key4[b] = Convert.ToByte(tempKey[3 - (b % 4)] ^ m_Key2[b]); } Monitor.Exit(this); //cout << "Int representation: " << charadd.i << endl; } public void Reset() { m_UseAlt = true; m_OutCounter = 0; } public void Encrypt(ref byte[] Data) { try { //Monitor.Enter(this); for (int b = 0; b < Data.Length; b++) { Data[b] = (byte)(Data[b] ^ 0xab); Data[b] = (byte)(Data[b] << 4 | Data[b] >> 4); if (m_UseAlt) { Data[b] = (byte)(m_Key4[m_OutCounter >> 8] ^ Data[b]); Data[b] = (byte)(m_Key3[m_OutCounter & 0x00ff] ^ Data[b]); } else { Data[b] = (byte)(m_Key2[m_OutCounter >> 8] ^ Data[b]); Data[b] = (byte)(m_Key1[m_OutCounter & 0x00ff] ^ Data[b]); } m_OutCounter++; } //Console.WriteLine("OutCounter = " + m_OutCounter); //Monitor.Exit(this); } catch (Exception e) { Console.WriteLine(e.ToString()); } } public void Decrypt(ref byte[] Data) { try { byte[] Key1; byte[] Key2; if (m_UseAlt) { Key1 = m_Key3; Key2 = m_Key4; } else { Key1 = m_Key1; Key2 = m_Key2; } //Monitor.Enter(this); for (int b = 0; b < Data.Length; b++) { Data[b] = (byte)(Data[b] ^ 0xab); Data[b] = (byte)(Data[b] << 4 | Data[b] >> 4); Data[b] = (byte)(Key2[m_InCounter >> 8] ^ Data[b]); Data[b] = (byte)(Key1[(m_InCounter & 0x00ff)] ^ Data[b]); m_InCounter++; } //Monitor.Exit(this); } catch (Exception e) { Console.WriteLine(e.ToString()); } } } } Here is my program entry debugging code: byte[] original = new byte[28] { 28, 0, 28, 4, 59, 114, 15, 0, 184, 34, 0, 0, 119, 0, 214, 208, 32, 123, 213, 26, 118, 52, 0, 0, 29, 39, 0, 0 }; byte[] encrypted = new byte[28] { 149, 132, 116, 101, 35, 98, 3, 226, 84, 45, 51, 165, 99, 203, 24, 98, 93, 62, 237, 131, 244, 5, 246, 227, 7, 114, 62, 162 }; /** 创建客户端加密实例 */ GameCryptographer clientCrypt = new GameCryptographer(); /** 创建服务端加密实例 */ GameCryptographer serverCrypt = new GameCryptographer(); serverCrypt.Decrypt(ref encrypted); byte[] test = new byte[] { 1, 2, 3}; /** 解密客户端发来的封包(这是关于一个链接唤起的封包) */ serverCrypt.Encrypt(ref test); serverCrypt.Decrypt(ref test); I divided this debugging into two steps. The first step is to test whether the encrypted packets sent by the client can be decrypted normally. This is normal. But I tested the encrypted packets 1, 2, and 3 and decrypted them, and the decrypted data did not match the original text. I don't even know where the mistake lies. Can anyone guide me Quote
kennylovecode Posted May 28 Author Posted May 28 Closed. Solved by my self... Just some logical issues Quote
Spirited Posted May 29 Posted May 29 I'm glad you figured this out! For those looking on this, what you're looking to do is create a man-in-the-middle attack, and you can't use the same cipher instance for both the client and server. Each needs their own cipher instantiation for impersonating the client and server. For 5017, that still uses the old TQ cipher (which has a separate server and client implementation). https://gitlab.com/conquer-online/wiki/-/snippets/1840784 https://gitlab.com/conquer-online/wiki/-/snippets/1840785 Quote
kennylovecode Posted June 3 Author Posted June 3 On 5/29/2025 at 2:31 PM, Spirited said: I'm glad you figured this out! For those looking on this, what you're looking to do is create a man-in-the-middle attack, and you can't use the same cipher instance for both the client and server. Each needs their own cipher instantiation for impersonating the client and server. For 5017, that still uses the old TQ cipher (which has a separate server and client implementation). 我很高兴你解决了这个问题!对于正在观看的人,你想要做的是创建一个中间人攻击,你不能在客户端和服务器之间使用相同的加密实例。每个都需要自己的加密实例来冒充客户端和服务器。对于 5017,它仍然使用旧的 TQ 加密(它有单独的服务器和客户端实现)。 https://gitlab.com/conquer-online/wiki/-/snippets/1840784 https://gitlab.com/conquer-online/wiki/-/snippets/1840785 There used to be an existing library that could be used, but I actually referenced this library from CptSky's project, but I only used the COSAC class and learned it. At present, I have successfully set up my own proxy, but when I perform the automatic jumping operation, its packet is sent from the proxy and then to the server, and the server responds with a packet. This is not a problem, but the client will not perform the jump action, which will cause the client's coordinates to be inconsistent with the server's. Because the client may have performed a jumping screen action and then sent a packet, but if the proxy sends the packet directly, it cannot trigger this action. Should we find the jumping function from the client, write it to memory, and call this function to execute the action? Quote
kennylovecode Posted June 3 Author Posted June 3 Currently, when developing a proxy server, I am using version 5065 to test a running private server. This game server has done some work to prevent cheating. I am currently facing a very interesting challenge. When I use its website download client to enter the game normally, everything else is also normal. I use CMD to view the game's IP and port, and can see ports 9168 and 5816. When selecting a server, it displays 3 servers to choose from. But its client server-side. dat actually only has one, and the exposed port is 9866. This made me very interested, so I upgraded my proxy to support the 5065 client. And installed an original 5065 client, modified the server. dat, entered its IP server name and port 9866 There are currently several situations: When I use port 9866, I can log in to zone server 1 normally. When I use port 9866 and log in to the other two service ports, it will prompt me that the server is not started When I log in using port 9168, all servers will crash. So I used my proxy debug to check the following issues When I log in using port 9168, the server immediately sends a packet of length 399. The client was unable to process this packet, so it crashed and automatically exited. I try to discard this packet after receiving it and not return it to the client, which will get stuck waiting for a progress bar. This is a very magical thing, and I am working hard to explore it. My guess is that the server developer injected a DLL into the client This DLL listens on port 9168, and whenever a request is initiated, it sends a handshake operation to the client, which is also completed by this DLL. Only after a successful handshake can the login operation continue. This is one of its anti cheating procedures. Anyway, this is my little guess and I find it very interesting, so I will continue to test and have an offensive and defensive battle with server developers, hahaha Quote
kennylovecode Posted June 3 Author Posted June 3 004A5671|004A5688|004A56A8|00481897|0048189C|004818A1|004818A7|00481898|0048189D|0042E7B6|0042E8E4|00462EDC|00464D61|00464D70|00462EE2|0042E7BC|0042E8EA|0048189D|00481898|00462EDD|0042E7B7|0042E8E5|0047B5C3|0047B5CC|6873020000|6800000000|8D8E98070000|90909090|680401000068000100008BCE9090|680401000068000100008BCE9090|689401000068750200008BCF909090909090|004C0848|004C08A8|004C08E2|0047C767|风云天下 When I captured the packet sent from the server and converted it into a string, I could see information containing some memory addresses and server names Quote
Spirited Posted June 4 Posted June 4 If you upgrade your proxy to that version, then you also need to upgrade your ciphers to Blowfish and introduce the DH Key Exchange. It's more complicated for sure than a 5017 proxy. Something else that you could try doing that avoids ciphers all together is making a memory based bot. That way, you don't need to proxy anything. Quote
kennylovecode Posted June 4 Author Posted June 4 1 hour ago, Spirited said: If you upgrade your proxy to that version, then you also need to upgrade your ciphers to Blowfish and introduce the DH Key Exchange. It's more complicated for sure than a 5017 proxy. Something else that you could try doing that avoids ciphers all together is making a memory based bot. That way, you don't need to proxy anything. 如果你将代理升级到那个版本,那么你还需要将你的加密算法升级到 Blowfish,并引入 DH 密钥交换。这当然比 5017 代理要复杂得多。还有另一种你可以尝试的方法,那就是完全避免使用加密算法,即制作一个基于内存的机器人。这样,你就不需要代理任何东西了。 The encryption algorithm is actually not a big problem for me now, because most sources can be found as references. I mentioned at the beginning about memory based robots, but I am not familiar with assembly language and cannot find the memory base address controlled by the program for CE tools, which stopped me. It may take longer to study and research. But if I use a proxy, I am currently encountering this problem. The automatically sent packets cannot be synchronized to the client. The only way I can think of is to call the client's skip method through memory, which requires finding the skip function address and then calling func This really confuses me, it seems like I can't get around this step Quote
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.