Rezlind
Member-
Posts
82 -
Joined
-
Last visited
Content Type
Profiles
Forums
Downloads
Everything posted by Rezlind
-
Hi everyone I was wondering if anyone could sanity check me on the 5187 MsgPlayer packet and monster names. I used WorldConquer (Comet based 5187 as a reference for this): /// <summary> /// Look of the entity. /// </summary> public UInt32 Mesh { get { return __Mesh; } set { __Mesh = value; WriteUInt32(4, value); } } /// <summary> /// Unique Id of the entity. /// </summary> public Int32 Id { get { return __Id; } set { __Id = value; WriteInt32(8, value); } } public UInt32 SynId { get { return __SynId; } set { __SynId = value; WriteUInt32(12, value); } } public UInt32 SynRank { get { return __SynMemberRank; } set { __SynMemberRank = value; WriteUInt32(16,value); } } /// <summary> /// Status of the entity. /// </summary> public UInt64 Status { get { return __Status; } set { __Status = value; WriteUInt64(24, value); } } public Int32 Helmet { get { return __Helmet; } set { __Helmet = value; WriteInt32(30, value); } } public Int32 GarmentType { get { return __GarmentType; } set { __GarmentType = value; WriteInt32(34, value); } } public Int32 ArmorType { get { return __ArmorType; } set { __ArmorType = value; WriteInt32(38, value); } } public Int32 WeaponRType { get { return __WeaponRType; } set { __WeaponRType = value; WriteInt32(42, value); } } public Int32 WeaponLType { get { return __WeaponLType; } set { __WeaponLType = value; WriteInt32(46, value); } } public UInt32 Mount { get { return __Mount; } set { __Mount = value; WriteUInt32(50, value); } } public UInt16 Life { get { return __Life; } set { __Life = value; WriteUInt16(58, value); } } public UInt16 MonsterLevel { get { return __MonsterLevel; } set { __MonsterLevel = value; WriteUInt16(60, value); } } public UInt16 HairStyle { get { return __HairStyle; } set { __HairStyle = value; WriteUInt32(62, value); } } public UInt16 PosX { get { return __PosX; } set { __PosX = value; WriteUInt16(64, value); } } public UInt16 PosY { get { return __PosY; } set { __PosY = value; WriteUInt16(66, value); } } public Byte Direction { get { return __Dir; } set { __Dir = value; mBuf[68] = value; } } public Byte Pose { get { return __Pose; } set { __Pose = value; mBuf[69] = value; } } public Byte Reborn { get { return __Reborn; } set { __Reborn = value; mBuf[73] = value; } } public UInt16 Level { get { return __Level; } set { __Level = value; WriteUInt16(74, value); } } public Byte WindowSpawn { get { return __WindowSpawn; } set { __WindowSpawn = value; mBuf[76] = value; } } public Byte AFK { get { return __AFK; } set { __AFK = value; mBuf[77] = value; } } public UInt32 SharedBP { get { return __SharedBP; } set { __SharedBP = value; WriteUInt32(78, value); } } public UInt32 FlowerCharm { get { return __FlowerCharm; } set { __FlowerCharm = value; WriteUInt32(91, value); } } public Byte NobilityRank { get { return __NobilityRank; } set { __NobilityRank = value; mBuf[95] = value; } } public UInt16 ArmorColor { get { return __ArmorColor; } set { __ArmorColor = value; WriteUInt16(99, value); } } public UInt16 ShieldColor { get { return __ShieldColor; } set { __ShieldColor = value; WriteUInt16(101, value); } } public UInt16 HelmetColor { get { return __HelmetColor; } set { __HelmetColor = value; WriteUInt16(103, value); } } public UInt32 QuizPoints { get { return __QuizPoints; } set { __QuizPoints = value; WriteUInt32(105, value); } } public Byte MountAddition { get { return __MountAddition; } set { __MountAddition = value; mBuf[107] = value; } } public UInt32 MountColor { get { return __MountColor; } set { __MountColor = value; WriteUInt32(112, value); } } public UInt16 EnlightenPoints { get { return __EnlightenPoints; } set { __EnlightenPoints = value; WriteUInt16(117, value); } } public UInt32 ClanIdentity { get { return __ClanIdentity; } set { __ClanIdentity = value; WriteUInt32(129, value); } } public Int32 ClanRank { get { return __ClanRank; } set { __ClanRank = value; WriteInt32(133, value); } } public Int32 ClanBattlePower { get { return __ClanBattlePower; } set { __ClanBattlePower = value; WriteInt32(137, value); } } public UInt32 UserTitle { get { return __UserTitle; } set { __UserTitle = value; WriteUInt32(141, value); } } Below is the function called when sending a player a monster packet: https://pastebin.com/RDfPkf4V Below is what appears in the client: https://imgur.com/a/o7HaABj Based on the World Conquer 5187 source, the only packet that should be sent when an monster npc appears on a client's screen is the MsgPlayer packet with only the monster relevant information filled out. Is there another packet I could be missing? Also on a side note how can I find out the packet structure for a packet on my own? Does that involve using Ghidra or some similar tool and looking for the packet number that I am interested in? Are there any tips on what to look for specifically? Thanks :)
-
This is amazing man well done!
-
Wow that's really impressive, what came of the project? Nothing haha, spend a lot or time on trying to get it to work but failed. Idle animation was working though so that was nice haha. Something was missing but couldn’t figure out what. We essentially rage quit. Haha I know the feel, going through the same thing with DH exchange on 5187 at the moment. Have you guys considered releasing the work you have so far on the C3 viewer so others can expand on it?
-
Took a look again after a few days and found that I'm still stuck on this unfortunately. I did discover that the client produces debug logs: ERROR: CGameSocket::ReceiveMsg() OnShakeHand failed at ..\3DRole/Network/socket.h, 564 -- Sun May 23 16:35:41 2021 So at-least I know I'm failing the handshake somehow. I've followed Comet pretty closely, and below is what I have done so far: I've done quite a bit of debugging and as far as I can tell I'm sending the packet in the correct structure: 1. Header - 11 bytes 2. Length of total packet (minus 11 junk bytes) - 4 bytes 3. Junk/Padding length - 4 bytes 4. Junk/Padding bytes - random 12-24 5. EncryptionIv length - 4 bytes 6. EncryptionIV - 8 bytes 7. DecryptionIVLength - 4 bytes 8. DecrpytionIV - 8 bytes 9 Primelength - 4 bytes 10. Prime - 128 bytes 11. GeneratorLength - 4 bytes 12. Generator - 2 bytes. 13. KeyLength - 4 bytes 14. Key - 128 bytes. I am also appending the TQFooter (8 bytes but no length bytes before and not included in the total length part of the packet. ) at the end of the packet. Is there anything else you can think of that I may be fucking up in that initial DH handshake send? Here is function snippet of that class https://pastebin.com/nB5y2AdM
-
This autopatcher/launcher can target some higher clients and injects the flash fix by default. Written by the author of World Conquer iirc. https://github.com/FelipeVieiraVendramini/AutoUpdater
-
A few months back I started looking at the prospect of creating a garment viewer in browser and decided to explore if it would be possible to render Conquer's C3 files in browser somehow. All Information I found seemed to point to requiring of Blender and a C3 Plugin that was created in the conquer community for that specific version of Blender. I set out to see if there were any modern tools that could convert the C3 files to formats that were more universally accessible. I came across this http://3doc.i3dconverter.com Tool. The free version sufficed for experimentation reasons - it was able to import the base C3 file in an C3 folder and convert it to an .obj format, where I then used an Online converter to conver the .obj to gbl/gltf format. Note: The free version of the tool will remove every 5th polygon from the exported version, for demo purposes it didn't matter but keep that in mind. Once in the .glb/gltf format, I used this npx package to convert the gltf file straight into a Js/Jsx component that can be imported into the react app and rendered inside a react-three fiber canvas: import React, { useRef, useState, Suspense } from "react"; import { Canvas, useFrame, useThree, extend } from "react-three-fiber"; import * as THREE from "three"; import './App.css' import FemaleTro from './FemaleTro'; import Piggie from './Piggie'; import { OrbitControls } from "three/examples/jsm/controls/OrbitControls" // Extend will make OrbitControls available as a JSX element called orbitControls for us to use. extend({ OrbitControls }); const Box = (props) => { // This reference will give us direct access to the mesh const mesh = useRef(); // Set up state for the hovered and active state const [active, setActive] = useState(false); // Rotate mesh every frame, this is outside of React without overhead useFrame(() => { mesh.current.rotation.x = mesh.current.rotation.y += 0.01; }); return ( <mesh {...props} ref={mesh} scale={active ? [2, 2, 2] : [1.5, 1.5, 1.5]} onClick={(e) => setActive(!active)} > <boxBufferGeometry args={[1, 1, 1]} /> <meshBasicMaterial attach="material" transparent side={THREE.DoubleSide}> </meshBasicMaterial> </mesh> ); } const CameraControls = () => { // Get a reference to the Three.js Camera, and the canvas html element. // We need these to setup the OrbitControls class. // https://threejs.org/docs/#examples/en/controls/OrbitControls const { camera, gl: { domElement }, } = useThree(); // Ref to the controls, so that we can update them on every frame using useFrame const controls = useRef(); useFrame((state) => controls.current.update()); return ( <orbitControls ref={controls} args={[camera, domElement]} enableZoom={true} maxAzimuthAngle={Math.PI / 23} maxPolarAngle={Math.PI} minAzimuthAngle={-Math.PI / 4} minPolarAngle={0} /> ); }; const App = () => { return ( <Canvas> <CameraControls /> <Suspense fallback={null}> <ambientLight intensity={0.5} /> <spotLight position={[10, 10, 10]} angle={5.15} penumbra={1} /> <pointLight position={[0, 0, 0]} /> <FemaleTro /> </Suspense> </Canvas> ); } export default App; NPM package dependencies: "dependencies": { "@react-three/drei": "^4.0.1", "@testing-library/jest-dom": "^5.11.4", "@testing-library/react": "^11.1.0", "@testing-library/user-event": "^12.1.10", "react": "^17.0.2", "react-dom": "^17.0.2", "react-scripts": "4.0.3", "react-three-fiber": "^5.3.22", "three": "^0.127.0", }, The end result was something like this: https://imgur.com/g1Pef4S It's by no means perfect but it was something I managed to put together in short amount of time to see what was possible. Happy to hear of more efficient ways of doing the conversion, or if there are even better processes in place for rendering this stuff in browser. It was actually this project that set me down the path of looking at different sources and trying to learn more about the conquer private server eco system. I hope to pick it back up and create a proper garment/item/npc viewer in browser for more robust webpages/guides. Hope this proof of concept/demo helps if anyone had similar ideas in mind!
-
For testing purposes I hard coded some things just to see what the value would be like, I went over this several times and with 11 bytes of padding at the start, and 12 bytes of junk length before the encryptionIV, the total length of the packet should be 326. I stepped through and ensured that all the data is being written correctly but sadly it still disconnects me. I may be a lost cause at this point. Random rnd = new Random(); var paddingBytes = new byte[11]; rnd.NextBytes(paddingBytes); Buffer.BlockCopy(paddingBytes, 0, mBuf, 0, 11); // 0-11 (11 bytes) Length = 326 - 11; // 12-15 (4 bytes) JunkLength = 12; // 16-19 (4 bytes) var junkBytes = new byte[12]; rnd.NextBytes(junkBytes); Buffer.BlockCopy(junkBytes, 0, mBuf, 20, 12); // 20-31 ( 12 bytes) EncryptionIVLength = 8; //32-35 ( 4 bytes) Buffer.BlockCopy(encryptionIV, 0, mBuf, 36, 8); // 36-43 ( 8 bytes) DecryptionIVLength = 8;// 44-47 ( 4 bytes) Buffer.BlockCopy(decryptionIV, 0, mBuf, 48, 8); // 48-55 (8 bytes) PrimeRootLength = 128; // 55-59 (4 bytes) Buffer.BlockCopy(Encoding.ASCII.GetBytes(aPrimeRoot), 0, mBuf, 60, 128); GeneratorLength = 2; // 188-191 (4 bytes) Buffer.BlockCopy(Encoding.ASCII.GetBytes(aGenerator), 0, mBuf, 192, 2); PrimaryKeyLength = 128; //194-197 (4 bytes) Buffer.BlockCopy(Encoding.ASCII.GetBytes(key), 0, mBuf, 198, 128);
-
Perhaps I didn't articulate this well sorry - This is the start of the DH handshake. The client disconnects the moment it receives the first DH Exchange packet after initially connecting to the Game server. I copy pasted a sample from a revised 5165 COEmu source to ensure I wasn't doing something incorrectly. As far as I know nothing about the DH exchange changed between 5165 and 5187 -> even when performing the query with the packet structure below: var random = new System.Random(); int PAD_LEN = 11; int _junk_len = 12; string tqs = "TQServer"; MemoryStream ms = new MemoryStream(); byte[] pad = new byte[PAD_LEN]; random.NextBytes(pad); byte[] junk = new byte[_junk_len]; random.NextBytes(junk); int size = 47 + aPrimeRoot.Length + aGenerator.Length + key.Length + 12 + 8 + 8; BinaryWriter bw = new BinaryWriter(ms); bw.Write(pad); bw.Write(size - PAD_LEN); bw.Write((UInt32)_junk_len); bw.Write(junk); bw.Write((UInt32)encryptionIV.Length); bw.Write(encryptionIV); bw.Write((UInt32)decryptionIV.Length); bw.Write(decryptionIV); bw.Write((UInt32)aPrimeRoot.ToCharArray().Length); foreach (char fP in aPrimeRoot.ToCharArray()) { bw.BaseStream.WriteByte((byte)fP); } bw.Write((UInt32)aGenerator.ToCharArray().Length); foreach (char fG in aGenerator.ToCharArray()) { bw.BaseStream.WriteByte((byte)fG); } bw.Write((UInt32)key.ToCharArray().Length); foreach (char SPK in key.ToCharArray()) { bw.BaseStream.WriteByte((byte)SPK); } foreach (char tq in tqs.ToCharArray()) { bw.BaseStream.WriteByte((byte)tq); } byte[] Packet = new byte[ms.Length]; Packet = ms.ToArray(); Console.WriteLine(PacketDump.Hex(Packet)); Buffer.BlockCopy(Packet, 0, mBuf, 0, (int)ms.Length); // Copy it to this current Msg in preparation of sending it to the server. Console.WriteLine(PacketDump.Hex(mBuf)); The client instantly disconnected. I'm not really sure what could cause a conquer client to instantly disconnect from the game server other than a malformed packet? I'm thinking perhaps I need to step away from this for a few days and come back to it with a fresh pair of eyes.
-
Seems I became stuck on the DH exchange as well. The client instantly disconnects upon receiving this packet and I'm not really sure why. Building up the packet public MsgDH(string key, byte[] encryptionIV, byte[] decryptionIV, string aPrimeRoot, string aGenerator) : base((321 + 12) - 11) { //Padding = BitConverter.ToUInt32(new byte[11], 0); Buffer.BlockCopy(new byte[11], 0, mBuf, 0, 11); Length = (321+12)-11; JunkLength = 12; Buffer.BlockCopy(new byte[12], 0, mBuf, 20, 12); // Junk Packet //Junk = BitConverter.ToUInt32(new byte[12], 0); EncryptionIVLength = 8; EncryptionIV = BitConverter.ToUInt64(encryptionIV, 0); DecryptionIVLength = 8; DecryptionIV = BitConverter.ToUInt64(decryptionIV, 0); __StrPacker = new StringPacker(this, 56); __StrPacker.AddString(aPrimeRoot); __StrPacker.AddString(aGenerator); __StrPacker.AddString(key); } The offsets: //public UInt32 Padding //{ // get { return __Padding; } // set //{ // __Padding = value; WriteUInt32(0, value); // } // } public UInt32 Length { get { return __Length; } set { __Length = value; WriteUInt32(12, value); } } public UInt32 JunkLength { get { return __JunkLength; } set { __JunkLength = value; WriteUInt32(16, value); } } //public UInt32 Junk //{ // get { return __Junk; } // set // { // __Junk = value; WriteUInt32(20, value); // } //} public UInt32 EncryptionIVLength { get { return __EncryptionIVLength; } set { __EncryptionIVLength = value; WriteUInt32(32, value); } } public UInt64 EncryptionIV { get { return __EncryptionIV; } set { __EncryptionIV = value; WriteUInt64(36, value); } } public UInt32 DecryptionIVLength { get { return __DecryptionIVLength; } set { __DecryptionIVLength = value; WriteUInt32(44, value); } } public UInt64 DecryptionIV { get { return __DecryptionIV; } set { __DecryptionIV = value; WriteUInt64(48, value); } } I compared it to the Comet 5187 MsgHandshake example and I think I match it pretty much exactly with the exception of the length of the padding (Which I refer to as Junk in my snippet). In Spirited's it seems to be a variable length of anything between 24-48 bytes long. Perhaps I'm misunderstanding that though. writer.Write(this.Padding.AsSpan(0, 9)); writer.Write(messageLength - 11); writer.Write(this.Padding.Length - 11); writer.Write(this.Padding.AsSpan(9, this.Padding.Length - 11)); writer.Write(this.EncryptionIV.Length); writer.Write(this.EncryptionIV); writer.Write(this.DecryptionIV.Length); writer.Write(this.DecryptionIV); writer.Write(this.PrimeRoot.Length); writer.Write(this.PrimeRoot, this.PrimeRoot.Length); writer.Write(this.Generator.Length); writer.Write(this.Generator, this.Generator.Length); writer.Write(this.ServerKey.Length); writer.Write(this.ServerKey, this.ServerKey.Length); return writer.ToArray();
-
Yes you were right I intended to include one more. Also, you were right to suggest looking at MsgEncryptCode. I foolishly thought I was sending the value but I was assigning the Seed variable to a private __Seed variable rather than the public Get Setter. In the end the server was not getting a valid seed, the offset from 4-8 was zeroed out. Rookie mistake and I now know to checkover even the simple things. Yikes Thanks a ton for your patience though.
-
Hehe understood, I definitely wasn't critiquing any of the code, plus I'm sure at the time it was one of the top releases in the private server community. So you were the second person to suggest that I do an SSCCE, I went ahead and copied your code for a quick console app, here is the code and it uses all the classes you have on Comet 5187 related to RC5: static void Main(string[] args) { string accName = "bob"; // Username is bob, password is also bob. // Copied the password bytes as I grab the 16 bytes on the server. string[] result = new string[] { "D0","1D", "BA", "D0", "E0", "A1", "8B", "94", "1B", "88", "E7", "75", "B0", "EC", "10", "10" }; byte[] passwordArray = result .Select(value => Convert.ToByte(value, 16)) .ToArray(); RC5 cipher = new RC5(2411687969); // The constant seed I was using for testing purposes. ScanCodeCipher scanCipher = new ScanCodeCipher(accName); Console.WriteLine("Before Encrypt {0}", PacketDump.Hex(passwordArray)); cipher.Decrypt(passwordArray, passwordArray); Console.WriteLine("After Encrypt {0}", PacketDump.Hex(passwordArray)); scanCipher.Decrypt(passwordArray, passwordArray); Console.WriteLine("After ScanCode Decrypt {0}", PacketDump.Hex(passwordArray)); } The results of this were exactly the same as on the server so now I can rule out there being something wrong with the RC5 cipher and the scan code cipher as I set them up on my end. The packet results when doing it on the server: The results when doing it in the SSCCE: At this point I think I need consider other points of failure: 1. Sending the seed, surely I couldn't have messed this up - it's a straightforward packet. 2. Grabbing the password bit from the packet. In the case of 2 -> MsgAccount Packet in it's entirety: The underlined portion is what I grab from the packet starting from 132, up to the max character length of 16. I'm not sure what the rest of the data is there or how it factors into handling the packet.
-
I used your implementation from comet and from phoenix to double check myself. The ref in the code I pasted was from the RC5 decrypt method. The Scan Code decrypt method that I last tested was yours: public void Decrypt(byte[] src, byte[] dst) { for (int i = 0; i < src.Length; i++) { // Check for the end of the string if (src[i] == 0) { dst.Slice(i, dst.Length - i).Fill(0); return; } // Substitute the byte byte position = (byte)(this.Sub[src[i] * 2]); dst[i] = ScanCodeCipher.Key[position % ScanCodeCipher.Key.Length]; } } Just to test, I changed it to overwrite itself rather than a new empty byte array: __Account = Program.Encoding.GetString(mBuf, 4, MAX_NAME_SIZE).TrimEnd('\0'); RivestCipher5 cipher = new RivestCipher5(aClient.Seed); var scanCodes = new ScanCodeCipher(__Account); Byte[] pwd_data = new Byte[MAX_NAME_SIZE]; Buffer.BlockCopy(mBuf, 132, pwd_data, 0, MAX_NAME_SIZE); cipher.Decrypt(ref pwd_data, MAX_NAME_SIZE); scanCodes.Decrypt(pwd_data, pwd_data); __Password = Encoding.ASCII.GetString(pwd_data).Trim('\0'); __Server = Program.Encoding.GetString(mBuf, 260, MAX_NAME_SIZE).TrimEnd('\0'); }
-
Heh yeah sorry that wasn't meant as a critique, I was just happy to find another reference source and something to compare it to. The decrypt function is Cpt Sky's decrypt function which I figured should still work, it has the ref keyword. Cpt Sky's Decrypt public unsafe void Decrypt(ref Byte[] aBuf, Int32 aLength) { if (aLength % 8 != 0) throw new ArgumentException("Length of the buffer must be a multiple of 64 bits.", "aLength"); fixed (Byte* buf = aBuf) { UInt32* data = (UInt32*)buf; for (Int32 k = 0, len = aLength / 8; k < len; ++k) { UInt32 lv = data[2 * k]; UInt32 rv = data[2 * k + 1]; // <--- +1 here. for (Int32 i = RC5_12; i >= 1; --i) { rv = rotr((rv - mSub[2 * i + 1]), lv) ^ lv; lv = rotr((lv - mSub[2 * i]), rv) ^ rv; } data[2 * k] = lv - mSub[0]; data[2 * k + 1] = rv - mSub[1]; } } } 5187 Comet RC5 Decrypt: public void Decrypt(Span<byte> src, Span<byte> dst) { // Pad the buffer var length = src.Length / 8; if (src.Length % 8 > 0) length = length + 1; src.CopyTo(dst); // Decrypt the buffer for (int word = 0; word < length; word++) { uint a = BitConverter.ToUInt32(dst.Slice(8 * word)); uint b = BitConverter.ToUInt32(dst.Slice((8 * word) + 4)); // <--- +4 here for (int round = RC5.Rounds; round > 0; round--) { b = (b - this.Sub[2 * round + 1]).RotateRight((int)a) ^ a; a = (a - this.Sub[2 * round]).RotateRight((int)b) ^ b; } BitConverter.GetBytes(a - this.Sub[0]).CopyTo(dst.Slice(8 * word)); BitConverter.GetBytes(b - this.Sub[1]).CopyTo(dst.Slice(8 * word + 4)); } } There is a slight difference between the two but not sure if at the end of the day its just two different ways to get to the same result.
-
Spent quite a few hours on this but it seems somehow, somewhere I implemented this incorrectly. I've gone over Spirited's Comet source, Spirited's Phoenix source, the Exodus binary source, Cpt Sky's original implementation, and double checked World Conquer's git history on the RC5 files to ensure there was no modification necessary that may have been missed in the original implementation. I compared them line by line where applicable and the only differences I could find were: 1. Phoenix Source 5187 seed byte generator uses a 10 byte initialization vector where as Cpt Sky and Comet are both using a 16 byte initialization vector 2. Phoenix Source 5187 When generating the substitution box the substitutionBuffer[index -1] adds + 0x9E3779B9 instead of subtracting 0x61C88647. I disregarded the 2nd discrepency because I later found a post where Spirited pointed out that RC5_QW32 needs to be changed to 0x61C88647 - Then it'll work on the 5180 - 5527 clients. At the moment, a password that should be decrypted as "bob" is instead being decrypted as "-Z\0\0\0\0\0Jf\0\0\0\0\0\05". For testing purposes, and as I had seen in one of the sources that implemented this I simply get the hash code of the user's IP address as the seed. My MsgEncryptCode class writes a Uint32 seed to offset 4 of the packet as indicated here (despite the version mismatch I couldn't find evidence that this packet structure was different for 5187 from 5635). When I then get a MsgAccount packet from the client I generate a new RivestCipher5 with the Uint32 seed from earlier. The seed passes through the code below to transform it into 16 bytes for use in the generating the key for the algorithm. I apologize for the messy code below as I've tried various things and in the process it's gotten a bit out of hand. byte[] theseSeeds = new byte[16]; for (int cnt = 0; cnt < 16; cnt++) { playerSeed *= 0x343fd; playerSeed += 0x269ec3; theseSeeds[cnt] = (byte)((playerSeed >> 16) & 0x7fff); } Afterwards, I create a new scanCodeCipher and decrypt the password, then run it through the scan code cipher. __Account = Program.Encoding.GetString(mBuf, 4, MAX_NAME_SIZE).TrimEnd('\0'); RivestCipher5 cipher = new RivestCipher5(aClient.Seed); var scanCodes = new ScanCodeCipher(__Account); Byte[] pwd_data = new Byte[MAX_NAME_SIZE]; Buffer.BlockCopy(mBuf, 132, pwd_data, 0, MAX_NAME_SIZE); cipher.Decrypt(ref pwd_data, MAX_NAME_SIZE); var result = new Byte[MAX_NAME_SIZE]; scanCodes.Decrypt(pwd_data, result); __Password = Encoding.ASCII.GetString(result).Trim('\0'); Possible points of failure I think could exist: 1. converting Span types to byte[] as I don't think I have access to those in my current version. 2. Offset wrong on MsgEncryptCode's Seed? As always I appreciate everyone's guidance. Thanks!
-
Ah right, I'm familiar with the constant he made COServer.Server.RC5_SEED to contain these bytes. If this constant is intended to be used when instantiating the cipher my confusion then lies in - where does the Uint32 key for decrypting the password come into play? With Spirited's source it's clear to me from looking at the RC5 class DecryptPassword method how the seed from earlier is utilized. But looking at Cpt Sky's implementation it isn't immediately apparent to me how that Uint32 key we sent to the client earlier in MsgEncryptCode is utilized. At a glance it doesn't seem to be used at all? Thanks
-
Sorry, it's evident I don't understand something when I can't even articulate the question well: I've been experimenting with bringing the enhanced version up a few versions. It was fairly simple to upgrade from 4330->5017. But I've kind of hit a roadblock upgrading from 5017-5187 due to not fully understanding how this RC5 cipher is structured. It would seem Cpt Sky included the RC5 implementation in the enhanced CopsV6 repo but when I try to implement it the decrypted password is always off. I'm positive I'm screwing up somewhere and I thought perhaps asking questions about certain aspects of the RC5 implementation would help me overcome whatever is messing up the decryption. Currently as I have it a 5187 client connects, the account server sends a MsgEncryptCode containing random UInt32 seed, then, when the client responds with the encrypted password in a MsgAccount packet I attempt to do the following: var seedBytes = BitConverter.GetBytes(aClient.Seed); Array.Resize(ref seedBytes, 16); RivestCipher5 cipher = new RivestCipher5(seedBytes); This doesn't seem correct but I do this because the RC5 implementation on CopsV6 enhanced demands a 16 byte seed. public unsafe RivestCipher5(Byte[] aSeed) { if (aSeed.Length != RC5_16) throw new ArgumentException("Length of the seed must be exactly " + RC5_16 + " bytes.", "aSeed"); One of the over-all questions I had is related to the summary notes above the RivestCipher5 function that say: And I realized that despite reading through countless EPvP posts I didn't quite understand the purpose of those keys as I hadn't seen them in use anywhere in the RivestCipher5 class. So to directly answer your question: Both port it, and understand it better. Thanks for responding!
-
Hey all, I feel like I only come to this forum with questions, hopefully soon I can provide something of value back. Last few days I've been trying to wrap my head around the 5187 Login sequence. Cops v6 enhanced included the RC5 class to use and while I've used the 5187 Comet source Login sequence as a reference, the RC5 implementations are coded a bit different than what I am working with. I think I understand how the exchange works at the top level as the docs on Comet 5187 clearly lay it out. But one of the things I don't fully understand are: What role does the following set of bytes play in this exchange process: /// Generates a random key (Key) to use for the algorithm. /// CO2: { 0x3C, 0xDC, 0xFE, 0xE8, 0xC4, 0x54, 0xD6, 0x7E, 0x16, 0xA6, 0xF8, 0x1A, 0xE8, 0xD0, 0x38, 0xBE } I've tried converting the seed to a byte array and resizing it to fill the extra space required but that doesn't seem to satisfy the Cipher as the decrypted password is not anywhere close to what it should be. I'm not really sure if I'm in over my head here but I'm going to keep bashing my head against this until it works, any guidance - even if its getting me to ask the right questions would be appreciated.
-
Just wondering if there is any benefit to "winding" down these maps if they had no activity for a while? I.e. turning off generators for those specific maps and trying to get it to unload from memory? Is that instead too risky to do once it's already loaded and thus better to just hold the high memory usage? I recognize that maintenance sorts these issues out but figured I'd ask. Thanks!
-
Heh, that definitely sounds maddening to track down when you assume everything is working correctly. May I ask what server you use to own back in the day? I never really "owned" a server, I more prototyped server architecture and my close friends would hop on to test features / stability. Technically, I think I started with CoFuture in Dec 2009 and then tried building on top of Impulse's base source in Aug 2010, but I started writing my own servers after about a year after doing that in Jan 2012? Now I work in the game industry, so I don't really get a lot of time / motivation to work on my old projects again. Maybe I'll get back to Chimera though (my "current" project on the back burner). I've had the itch to for a while. Ah yeah I'm sure its difficult to find the energy/desire to code after working on code all day. Especially the same kind of code lol. Note to anyone else in the future reading this thread - the actual solution has been edited in at the end of the first/original post in this thread!
-
Heh, that definitely sounds maddening to track down when you assume everything is working correctly. May I ask what server you use to own back in the day?
-
I feel like an idiot. The reason this was happening was because early on, before I understood how the packets worked I ran into something that made me add a temporary 1051 switch case fall through like this: This was probably day one of learning conquer and it stayed there until now. As you can imagine this completely messed up how packet 1051, MsgAccount was getting handled. Since removing this blunder I've yet to experience the login hanging. It appears I spoke too soon. Not really sure whats causing it still but will continue to debug;