- Joined
- Jan 18, 2010
- Messages
- 3,109
- Reaction score
- 1,139
With all of the resources I have provided, the only thing left to do is show you how to go about analyzing MapleStory 2 clients. I highly recommend relying on this for your packet structures, and then you can sniff the live servers to determine what the values of each byte actually is. This way, you will always have the correct packet data. A very common problem in MapleStory is the lack of using analyzed clients, and thus having to pad your packets with zeroes to prevent your game client from crashing. Let's not repeat past mistakes
Before we get started, I'm only going to say this once - the process of creating, analyzing, and working with IDA is a steep learning curve at first. However, once you've gotten the hang of the basics (which is really just trial and error, and asking questions), then it's really easy from there and will help you out a lot!
In order to analyze a MapleStory 2 client, you're going to need a handful of tools:
-
-
-
-
-
-
-
- MapleStory 2 Application (and its dependency DLLs)
1a. Installation
If you currently have IDA 7.0 installed, the IDC and Themida unpacking scripts downloaded, OllyDbg previously configured, and a working anti-debugger such as ScyllaHide, StrongOD, or PhantOm already setup, then you may skip to step 2.
First, download and run the installation for IDA Pro 7. When prompted, enter in the installation key to continue and finish. It should setup 32-bit shortcuts on your desktop and/or start where you'll be accessing it later. After that's setup, go ahead and download the MapleStory 2 IDC script, and put it on your desktop or wherever you plan to store your IDA stuff (IDBs, scripts, etc) - we'll be using this later.
Next, download and extract OllyDbg somewhere. Make sure the location is ignored by your antivirus because it'll be deleted, since they don't like us reverse engineering . Once you've downloaded and extracted Olly, go ahead and download OllyDumpEx and extract into OllyDbg's "Plugin" folder.
Then, create a new folder called "Scripts" in OllyDbg's "Plugin" folder. Download and move the Themida unpacking script to that "Scripts" folder you just made.
Finally, right-click on OllyDbg and run the program as administrator. Once it's loaded, navigate to Options->Appearance. Move over to the "Directories" tab and verify that these locations are correct. If you are for some reason using remote directories, then browse for their locations. After everything is correct, click OK, and exit out of OllyDbg.
1b. Installation Continued (x64)
If you are NOT running on an x64 based system, then you may skip this step (to step 2) because OllyDbg comes with StrongOD by default. Otherwise, go ahead and download and extract ScyllaHide somewhere.
First, copy over the "HookLibraryx86.dll" and "scylla_hide.ini" files from the ScyllaHide folder, into your OllyDbg's "Plugin" folder. You won't need to configure any of these settings, so don't worry about them.
Next, go into the "plugins" folder, and copy over the "ScyllaHideOlly1Plugin.dll" file into your OllyDbg's "Plugin" folder. These are all the necessary Scylla library plugins needed.
Then, go into the "NtApiTool" folder, and you'll see x64 and x86 folders - select the x86 folder. Now, run the "PDBReaderx86.exe" file, and give it some time. This might take a few moments to finish, so just let it run its course - it'll download PDBs off Microsoft's servers before it processes everything.
Finally, once the program has finished, a "NtApiCollection.ini" file will be generated. All you need to do now is just copy that file into OllyDbg's "Plugin" directory.
2. Unpacking the MapleStory 2 Client
Now that we have configured IDA, Olly, and our anti-debuggers, we're ready to unpack the client! By using an old Themida unpacking script, we can unpack the sections of the client, and then dump the process.
First, open up OllyDbg (run as administrator!) and navigate to File->Open to select the MapleStory2.exe file. It should load the packed application's binary code into Olly without a problem.
Next, right-click anywhere in the main thread window (the window with the assembly code instructions), and navigate to "Run Script". Select "Open", browse to your OllyDbg's Plugin->Scripts folder, and select the Themida unpacking script. Now, a popup will appear asking you to unpack the client, click Yes.
It will proceed to ask you if you want to run the SetEvent finder, click No.
The script will execute and eventually pause, opening a Log data window. Feel free to move them off to the right or something, these aren't important. Anywho, after the execution is paused, right-click once again and navigate to "Script Functions". In the sub-menu, select "Resume" to continue execution. If you're on an x64 OS, you're going to get a popup below, but don't worry - just click OK and ignore this.
After execution was resumed, you should get a new popup regarding your configuration. Click OK so we can configure what it's asking. Navigate to Plugins->StrongOD, and in the sub-menu select "Options". Toggle "KernelMode" so that it's enabled, and click save. Now, navigate to Plugins->PhantOm, and in the sub-menu select "Options". Toggle "Protect DRx" so that it's enabled, and click save. Once you're done, exit out of OllyDbg completely, and re-run it (again, as admin).
After you've successfully restarted OllyDbg, navigate back to Plugins->StrongOD->Options, and make sure "KernelMode" is enabled. If it is, click cancel. Now navigate back to Plugins->PhantOm->Options, and make sure "Protect DRx" is enabled. If it is, click cancel and we're good to go. Since our configuration has been updated, we must repeat our previous steps again. Run the script, and answer the prompts until you get back to where you were. When you've gotten to the previous point, your window should be correctly configured on the prompt:
This time, click OK, and afterwards you can continue script execution (right-click, Script Functions->Resume). Another prompt will appear regarding the magic jumps, click Yes.
Directly after, a nopper check prompt will come up, click No here.
Give it a few moments to breakpoint some things, and then you'll get a prompt regarding the VM OEP Turbo finding method. You'll click Yes to this one.
This will take a bit of time to process, maybe a minute or two. Watch the progress at the bottom left of OllyDbg carefully, and it'll eventually log "No API in eax register!!". We don't really care because all we're using this for is basically just a memory dump. Nonetheless, once you see this (and at the top-left of Olly it says "Paused"), navigate to Plugins->OllyDumpEx, and in the sub-menu select "Dump process".
After you've opened OllyDumpEx, a window should appear with an active base module, an image base at 0x400000, and the sections within the PE. It should look like this below.
Finally, select "Dump", and save the target file as "MapleStory2_dump" somewhere. Once it finishes dumping and it lets you know, close out of OllyDumpEx (click "Finish"), and close the process by navigating to Debug->Close. After it unloads the process, you can exit out of OllyDbg. Now you've got yourself a unpacked dump of the MapleStory 2 client! If done correctly, the size of the dump file should be between 34~40MB.
3. Analyzing the Client -> IDB
If you've gotten this far, then the complicated and annoying part is over with! All we've got to do now is analyze the application and let IDA construct an IDB file for us to use.
First, open up IDA Pro 7.0 and click OK on the about screen. Navigate to File->Open, and browse for the "MapleStory2_dump.exe" file that you just made. A window will pop up, just keep the default settings and make sure Portable Executable is selected. Click OK when you're done.
Next, you'll receive a popup regarding the image base not being loaded into the database. Simply click Yes to load the image base into the database, and continue parsing. If a warning regarding the imports section being destroyed pops up, just click OK to continue.
Then, we wait! IDA will begin to analyze the client and construct an IDB. Depending on your computer specs, this might take a LONG TIME. For me, I can analyze the client in less than an hour (averaging ~30min), so it'll vary for you. Just give it time and let it do its thing! Once it's done, it'll ding and display a graph view. Right-click anywhere in the graph view, and select "Text View" to return to disassembly view.
Finally, to get us started with our IDB, we can use my MapleStory 2 IDC script. This will be a very useful script for you to use because it'll work for almost every version, naming the important functions needed to update your packets automagically for you. To run a IDC script, navigate to File->Script File, and select the script you've downloaded. It will run and name the important functions for you. It should output that it's found functions, like so:
4. Setting up your IDB
So you've finally got your IDB ready to go, and now you're wondering how to get started. Before we can do anything, we first want to setup our IDB. The first thing I do after executing my IDC scripts and clicking "Function name" in the Functions window to sort my functions alphabetically is the hotkey CTRL + W. This will write your current data to your IDB file so that you won't lose stuff if IDA or your PC crashes. I recommend doing this every so often when naming a lot of functions in your IDB. Then, I load the Strings Window so that I can use them to my advantage for finding things. Right-click on the toolbar, and checkmark "Lists" if it isn't already checked. A new group of icons will appear on the toolbar, and you're going to want to select the one with the icon of 's'. This will generate all strings within the application.
Once you've got your windows setup, we need to add type information (structures) so that we can read the packet calls. Unlike in MapleStory where packet calls are actual functions (CInPacket:ecode4, for example), MapleStory 2 uses virtual calls everywhere. This means, in order to know what function is being called, we need to have a structure loaded that can be assigned to a variable. Use the hotkey SHIFT + F1 to open up a Local Types window. Within the Local Types window, we're going to need to add a few structures:
1. The CInPacket vftable
2. The CInPacket class
3. The COutPacket vftable
4. The COutPacket class
To insert a new structure into IDA's Local Types window, you can use the hotkey INS, or right-click and select Insert. Here are the structures you're going to need, in order. Do not place all of them in one struct and click OK. You must repeat the process for EACH structure!
Once you've added all of these into your Local Types window, you'll now be able to successfully assign variables to these structures to see their calls.
5. Assigning Variables to CInPacket/COutPacket References
So now that your IDB is all set up, you just need to know how to read the packet calls! When you're working with standard decodes (Decode1/2/4/etc), things aren't that bad. However, just wait until you hit a virtual object decode (CItem:ecode, for example). Finding those is a lot more painful.
Alright, so let's start off by naming Nexon's CClientPlatformNexonAmerica::OnSendLoginResult function. Just like you would with any other IDB, double-click on the function in IDA and use the hotkey F5 to decompile the function into C pseudo-code. If you scroll down a little, the function will look like this:
Notice the highlighted function? The "v4" variable that was assigned from a2 (iPacket) is your CInPacket reference. For anyone who isn't experienced with these MapleStory class names, the client's CInPacket is when the client is decoding a packet from the server. The client's COutPacket is when the client encodes a packet to send to the server. Furthermore, double-click on the highlighted function present to view its pseudo. At first, it'll look pretty scary because it's a CInPacket with no structure, so we have absolutely no clue what is being called.
Let's clean this up, shall we? If you're picky and prefer not to have typecasting enabled (which may help you in the future), you can use the hotkey Tab to disable all of the casting. Now, to convert this into readable function calls, right-click on the "v3" variable, and select "Convert to struct".
It will open a window of available structures. Select the CInPacket structure that we just added, and click OK to watch the magic happen. After assigning it into a structure, your pseudo should now look like this:
Perfect! Now, when we setup those calls in the packet on our server, it would look something like this:
I hope this gives you a good basic understanding on how exactly packets will look like throughout the client. Before I go over the OnPacket functions, let me give you a quick overview of what each type of Decode call means in MapleStory 2.
In addition to the given list, is CInPacket:ecodeHeader. This calls CInPacket::SetOffset(6) which is the end of the packet header, and then decodes a short which is the opcode. This function is used in several places and is named by my IDC script. By xrefing this, you'll always be able to find the major OnPacket functions all around the client.
6a. Understanding MapleStory 2 Functions
Diving right into MapleStory 2 clients isn't going to be too simple at first, but you'll get a better understanding over time. To help make it easier, I'll try my best to explain some of the important functions.
Alright, so I'm going to assume a majority of you have been around the MapleStory scene. In the MapleStory sections, a leaked v95 GMS PDB exists, and we have used this for years to reference what specific functions should look like. In MapleStory 2, I use the same principle, but without a fully-named PDB. You're probably wondering how this would be possible if we have nothing to go by since there's no debug info or names yet.
Your unpacked MapleStory 2 clients will actually include type names. Remember when I showed you how to enable the Lists in the toolbar? Two icons to the left of the Strings Window ('s'), or in the center of the section, is a Names Window. As I mentioned previously, Nexon uses virtual calls everywhere because they use a whole ton of abstraction. Take a look at my names window, and you'll notice that theres a base class called CClientPlatformNexonAmerica, just like I used to name the OnSendLoginResult function. This is Nexon's actual class names, and just like the process from MapleStory, I name my IDBs the same names Nexon uses.
Cool, huh? So now we know what the classes are, but we need to know the names of the functions. How can we go upon doing that? Well, up until MapleStory 2 v100 (latest client), Nexon has had a debug function in the client. This function would create two giant mappings of every Send opcode (ks_PACKET), and every Receive opcode (kc_PACKET). Using this function, we can get the name of the opcode that Nexon uses, and apply it to the class name to construct our OnPacket functions! This is exactly how CClientPlatformNexonAmerica::OnSendLoginResult was formed. We take the name of the class, and then from the opcode form the function name: "ks_send_login_result" -> SendLoginResult -> OnSendLoginResult.
As a reference, you can head over to my Library of MapleStory 2 IDBs thread and download the Version 70 IDB. Then, you can reference
duck u auto save, will rewrite the rest of this again later
6b. Finding MapleStory 2 Functions
I'll wrap everything up by going over how to find some important functions in the client from the ones already provided to you by my IDC script.
I will finish the rest of this later~
Before we get started, I'm only going to say this once - the process of creating, analyzing, and working with IDA is a steep learning curve at first. However, once you've gotten the hang of the basics (which is really just trial and error, and asking questions), then it's really easy from there and will help you out a lot!
In order to analyze a MapleStory 2 client, you're going to need a handful of tools:
-
You must be registered to see links
(archive password: tuts4you)-
You must be registered to see links
-
You must be registered to see links
(if running a x64 system)-
You must be registered to see links
-
You must be registered to see links
-
You must be registered to see links
(installation key: qY2jts9hEJGy)-
You must be registered to see links
- MapleStory 2 Application (and its dependency DLLs)
1a. Installation
If you currently have IDA 7.0 installed, the IDC and Themida unpacking scripts downloaded, OllyDbg previously configured, and a working anti-debugger such as ScyllaHide, StrongOD, or PhantOm already setup, then you may skip to step 2.
First, download and run the installation for IDA Pro 7. When prompted, enter in the installation key to continue and finish. It should setup 32-bit shortcuts on your desktop and/or start where you'll be accessing it later. After that's setup, go ahead and download the MapleStory 2 IDC script, and put it on your desktop or wherever you plan to store your IDA stuff (IDBs, scripts, etc) - we'll be using this later.
Next, download and extract OllyDbg somewhere. Make sure the location is ignored by your antivirus because it'll be deleted, since they don't like us reverse engineering . Once you've downloaded and extracted Olly, go ahead and download OllyDumpEx and extract into OllyDbg's "Plugin" folder.
Then, create a new folder called "Scripts" in OllyDbg's "Plugin" folder. Download and move the Themida unpacking script to that "Scripts" folder you just made.
Finally, right-click on OllyDbg and run the program as administrator. Once it's loaded, navigate to Options->Appearance. Move over to the "Directories" tab and verify that these locations are correct. If you are for some reason using remote directories, then browse for their locations. After everything is correct, click OK, and exit out of OllyDbg.
1b. Installation Continued (x64)
If you are NOT running on an x64 based system, then you may skip this step (to step 2) because OllyDbg comes with StrongOD by default. Otherwise, go ahead and download and extract ScyllaHide somewhere.
First, copy over the "HookLibraryx86.dll" and "scylla_hide.ini" files from the ScyllaHide folder, into your OllyDbg's "Plugin" folder. You won't need to configure any of these settings, so don't worry about them.
Next, go into the "plugins" folder, and copy over the "ScyllaHideOlly1Plugin.dll" file into your OllyDbg's "Plugin" folder. These are all the necessary Scylla library plugins needed.
Then, go into the "NtApiTool" folder, and you'll see x64 and x86 folders - select the x86 folder. Now, run the "PDBReaderx86.exe" file, and give it some time. This might take a few moments to finish, so just let it run its course - it'll download PDBs off Microsoft's servers before it processes everything.
Finally, once the program has finished, a "NtApiCollection.ini" file will be generated. All you need to do now is just copy that file into OllyDbg's "Plugin" directory.
2. Unpacking the MapleStory 2 Client
Now that we have configured IDA, Olly, and our anti-debuggers, we're ready to unpack the client! By using an old Themida unpacking script, we can unpack the sections of the client, and then dump the process.
First, open up OllyDbg (run as administrator!) and navigate to File->Open to select the MapleStory2.exe file. It should load the packed application's binary code into Olly without a problem.
Next, right-click anywhere in the main thread window (the window with the assembly code instructions), and navigate to "Run Script". Select "Open", browse to your OllyDbg's Plugin->Scripts folder, and select the Themida unpacking script. Now, a popup will appear asking you to unpack the client, click Yes.
It will proceed to ask you if you want to run the SetEvent finder, click No.
The script will execute and eventually pause, opening a Log data window. Feel free to move them off to the right or something, these aren't important. Anywho, after the execution is paused, right-click once again and navigate to "Script Functions". In the sub-menu, select "Resume" to continue execution. If you're on an x64 OS, you're going to get a popup below, but don't worry - just click OK and ignore this.
After execution was resumed, you should get a new popup regarding your configuration. Click OK so we can configure what it's asking. Navigate to Plugins->StrongOD, and in the sub-menu select "Options". Toggle "KernelMode" so that it's enabled, and click save. Now, navigate to Plugins->PhantOm, and in the sub-menu select "Options". Toggle "Protect DRx" so that it's enabled, and click save. Once you're done, exit out of OllyDbg completely, and re-run it (again, as admin).
After you've successfully restarted OllyDbg, navigate back to Plugins->StrongOD->Options, and make sure "KernelMode" is enabled. If it is, click cancel. Now navigate back to Plugins->PhantOm->Options, and make sure "Protect DRx" is enabled. If it is, click cancel and we're good to go. Since our configuration has been updated, we must repeat our previous steps again. Run the script, and answer the prompts until you get back to where you were. When you've gotten to the previous point, your window should be correctly configured on the prompt:
This time, click OK, and afterwards you can continue script execution (right-click, Script Functions->Resume). Another prompt will appear regarding the magic jumps, click Yes.
Directly after, a nopper check prompt will come up, click No here.
Give it a few moments to breakpoint some things, and then you'll get a prompt regarding the VM OEP Turbo finding method. You'll click Yes to this one.
This will take a bit of time to process, maybe a minute or two. Watch the progress at the bottom left of OllyDbg carefully, and it'll eventually log "No API in eax register!!". We don't really care because all we're using this for is basically just a memory dump. Nonetheless, once you see this (and at the top-left of Olly it says "Paused"), navigate to Plugins->OllyDumpEx, and in the sub-menu select "Dump process".
After you've opened OllyDumpEx, a window should appear with an active base module, an image base at 0x400000, and the sections within the PE. It should look like this below.
Finally, select "Dump", and save the target file as "MapleStory2_dump" somewhere. Once it finishes dumping and it lets you know, close out of OllyDumpEx (click "Finish"), and close the process by navigating to Debug->Close. After it unloads the process, you can exit out of OllyDbg. Now you've got yourself a unpacked dump of the MapleStory 2 client! If done correctly, the size of the dump file should be between 34~40MB.
3. Analyzing the Client -> IDB
If you've gotten this far, then the complicated and annoying part is over with! All we've got to do now is analyze the application and let IDA construct an IDB file for us to use.
First, open up IDA Pro 7.0 and click OK on the about screen. Navigate to File->Open, and browse for the "MapleStory2_dump.exe" file that you just made. A window will pop up, just keep the default settings and make sure Portable Executable is selected. Click OK when you're done.
Next, you'll receive a popup regarding the image base not being loaded into the database. Simply click Yes to load the image base into the database, and continue parsing. If a warning regarding the imports section being destroyed pops up, just click OK to continue.
Then, we wait! IDA will begin to analyze the client and construct an IDB. Depending on your computer specs, this might take a LONG TIME. For me, I can analyze the client in less than an hour (averaging ~30min), so it'll vary for you. Just give it time and let it do its thing! Once it's done, it'll ding and display a graph view. Right-click anywhere in the graph view, and select "Text View" to return to disassembly view.
Finally, to get us started with our IDB, we can use my MapleStory 2 IDC script. This will be a very useful script for you to use because it'll work for almost every version, naming the important functions needed to update your packets automagically for you. To run a IDC script, navigate to File->Script File, and select the script you've downloaded. It will run and name the important functions for you. It should output that it's found functions, like so:
4. Setting up your IDB
So you've finally got your IDB ready to go, and now you're wondering how to get started. Before we can do anything, we first want to setup our IDB. The first thing I do after executing my IDC scripts and clicking "Function name" in the Functions window to sort my functions alphabetically is the hotkey CTRL + W. This will write your current data to your IDB file so that you won't lose stuff if IDA or your PC crashes. I recommend doing this every so often when naming a lot of functions in your IDB. Then, I load the Strings Window so that I can use them to my advantage for finding things. Right-click on the toolbar, and checkmark "Lists" if it isn't already checked. A new group of icons will appear on the toolbar, and you're going to want to select the one with the icon of 's'. This will generate all strings within the application.
Once you've got your windows setup, we need to add type information (structures) so that we can read the packet calls. Unlike in MapleStory where packet calls are actual functions (CInPacket:ecode4, for example), MapleStory 2 uses virtual calls everywhere. This means, in order to know what function is being called, we need to have a structure loaded that can be assigned to a variable. Use the hotkey SHIFT + F1 to open up a Local Types window. Within the Local Types window, we're going to need to add a few structures:
1. The CInPacket vftable
2. The CInPacket class
3. The COutPacket vftable
4. The COutPacket class
To insert a new structure into IDA's Local Types window, you can use the hotkey INS, or right-click and select Insert. Here are the structures you're going to need, in order. Do not place all of them in one struct and click OK. You must repeat the process for EACH structure!
PHP:
struct CInPacket_reader_vtbl
{
int (__thiscall *Decodeb)(CInPacket_reader_vtbl *);
int (__thiscall *Decode1)(CInPacket_reader_vtbl *);
int (__thiscall *Decode2)(CInPacket_reader_vtbl *);
int (__thiscall *Decode4)(CInPacket_reader_vtbl *);
int (__thiscall *Decode8)(CInPacket_reader_vtbl *);
int (__thiscall *Decodef)(CInPacket_reader_vtbl *);
int (__thiscall *DecodeWstrA)(CInPacket_reader_vtbl *);
int (__thiscall *DecodeStrA)(CInPacket_reader_vtbl *);
int (__thiscall *DecodeStrW)(CInPacket_reader_vtbl *);
int (__thiscall *DecodeBuffer)(CInPacket_reader_vtbl *);
int (__thiscall *DecodeCoordsf)(CInPacket_reader_vtbl *);
int (__thiscall *DecodeCoords2)(CInPacket_reader_vtbl *);
int (__thiscall *Decode2ft10)(CInPacket_reader_vtbl *);
int (__thiscall *Decode2ft100)(CInPacket_reader_vtbl *);
int (__thiscall *Decode2ftx)(CInPacket_reader_vtbl *);
int (__thiscall *Decode2ftp1)(CInPacket_reader_vtbl *);
int (__thiscall *Decode2fdx)(CInPacket_reader_vtbl *);
};
PHP:
struct CInPacket
{
CInPacket_reader_vtbl *iPacket;
};
PHP:
struct COutPacket_writer_vtbl
{
int (__thiscall *Encodeb)(COutPacket_writer_vtbl *);
int (__thiscall *Encode1)(COutPacket_writer_vtbl *);
int (__thiscall *Encode2)(COutPacket_writer_vtbl *);
int (__thiscall *Encode4)(COutPacket_writer_vtbl *);
int (__thiscall *Encode8)(COutPacket_writer_vtbl *);
int (__thiscall *Encodef)(COutPacket_writer_vtbl *);
int (__thiscall *EncodeWstrA)(COutPacket_writer_vtbl *);
int (__thiscall *EncodeStrA)(COutPacket_writer_vtbl *);
int (__thiscall *EncodeStrW)(COutPacket_writer_vtbl *);
int (__thiscall *EncodeBuffer)(COutPacket_writer_vtbl *);
int (__thiscall *EncodeCoordsf)(COutPacket_writer_vtbl *);
int (__thiscall *EncodeCoords2)(COutPacket_writer_vtbl *);
int (__thiscall *Encode2ft10)(COutPacket_writer_vtbl *);
int (__thiscall *Encode2ft100)(COutPacket_writer_vtbl *);
int (__thiscall *Encode2ftx)(COutPacket_writer_vtbl *);
int (__thiscall *Encode2ftp1)(COutPacket_writer_vtbl *);
int (__thiscall *Encode2fdx)(COutPacket_writer_vtbl *);
};
PHP:
struct COutPacket
{
COutPacket_writer_vtbl *oPacket;
};
Once you've added all of these into your Local Types window, you'll now be able to successfully assign variables to these structures to see their calls.
5. Assigning Variables to CInPacket/COutPacket References
So now that your IDB is all set up, you just need to know how to read the packet calls! When you're working with standard decodes (Decode1/2/4/etc), things aren't that bad. However, just wait until you hit a virtual object decode (CItem:ecode, for example). Finding those is a lot more painful.
Alright, so let's start off by naming Nexon's CClientPlatformNexonAmerica::OnSendLoginResult function. Just like you would with any other IDB, double-click on the function in IDA and use the hotkey F5 to decompile the function into C pseudo-code. If you scroll down a little, the function will look like this:
Notice the highlighted function? The "v4" variable that was assigned from a2 (iPacket) is your CInPacket reference. For anyone who isn't experienced with these MapleStory class names, the client's CInPacket is when the client is decoding a packet from the server. The client's COutPacket is when the client encodes a packet to send to the server. Furthermore, double-click on the highlighted function present to view its pseudo. At first, it'll look pretty scary because it's a CInPacket with no structure, so we have absolutely no clue what is being called.
Let's clean this up, shall we? If you're picky and prefer not to have typecasting enabled (which may help you in the future), you can use the hotkey Tab to disable all of the casting. Now, to convert this into readable function calls, right-click on the "v3" variable, and select "Convert to struct".
It will open a window of available structures. Select the CInPacket structure that we just added, and click OK to watch the magic happen. After assigning it into a structure, your pseudo should now look like this:
Perfect! Now, when we setup those calls in the packet on our server, it would look something like this:
PHP:
public static OutPacket onSendLoginResult(ClientSocket socket, int loginState, int blockType) {
OutPacket packet = new OutPacket(LoopbackPacket.SendLoginResult);
packet.encodeByte(loginState);
packet.encodeInt(50);//Unknown
packet.encodeStringW(socket.getPassport());
packet.encodeLong(socket.getAccountSN());
packet.encodeLong(socket.getSyncTime());
packet.encodeInt(socket.getSyncTicks());//Unknown
packet.encodeByte(socket.getSyncTimeZone());
packet.encodeByte(blockType);
packet.encodeInt(10);//Unknown (this is a static constant that xrefs to UGC-related poop)
packet.encodeLong(123456789);//Unknown (this is a static constant that xrefs to UGC-related poop)
if (blockType == 1) {
packet.encodeLong(socket.getBlockDate());//Unknown
} else if (blockType == 2) {
packet.encodeLong(socket.getBlockDate());//Unknown
packet.encodeLong((System.currentTimeMillis() / 1000) + 1000);//Unknown
}
packet.encodeInt(3);//Unknown (client checks if this is 3 in multiple locations)
return packet;
}
I hope this gives you a good basic understanding on how exactly packets will look like throughout the client. Before I go over the OnPacket functions, let me give you a quick overview of what each type of Decode call means in MapleStory 2.
- Decodeb - This decodes a boolean (internally this is just Decode1() != 0)
- Decode1 - This decodes a single byte (e.g 1 -> 01)
- Decode2 - This decodes a 2-byte short (e.g 1 -> 01 00)
- Decode4 - This decodes a 4-byte integer (e.g 1 -> 01 00 00 00)
- Decode8 - This decodes a 8-byte long (e.g 1 -> 01 00 00 00 00 00 00 00)
- Decodef - This decodes a 4-byte floating point value
- DecodeWStrA - This decodes a wide-character (unicode) string and converts it into ANSI
- DecodeStrA - This decodes an ANSI string (short size, byte[] characters) (e.g 'Eric' -> 04 00 45 72 69 63)
- DecodeStrW - This decodes a wide-character string (short size, ushort[] characters)
- DecodeBuffer - This decodes a given amount of bytes provided as a parameter (usually decodes structs)
- DecodeCoordsf - This decodes a 3D coordinate set (x, y, z) as floats.
- DecodeCoords2 - This decodes a 3D coordinate set (x, y, z) as shorts.
- Decode2ft10 - This decodes a short and multiplies it by 10.0 (mainly used for movement)
- Decode2ft100 - This decodes a short and multiplies it by 100.0 (mainly used for movement)
- Decode2ftx - This decodes a short and multiplies it by a given floating-point value.
- Decode2ftp1 - This decodes a short and multiplies it by 0.1 (mainly used for movement)
- Decode2fdx - This decodes a short and divides it by a given floating-point value.
In addition to the given list, is CInPacket:ecodeHeader. This calls CInPacket::SetOffset(6) which is the end of the packet header, and then decodes a short which is the opcode. This function is used in several places and is named by my IDC script. By xrefing this, you'll always be able to find the major OnPacket functions all around the client.
6a. Understanding MapleStory 2 Functions
Diving right into MapleStory 2 clients isn't going to be too simple at first, but you'll get a better understanding over time. To help make it easier, I'll try my best to explain some of the important functions.
Alright, so I'm going to assume a majority of you have been around the MapleStory scene. In the MapleStory sections, a leaked v95 GMS PDB exists, and we have used this for years to reference what specific functions should look like. In MapleStory 2, I use the same principle, but without a fully-named PDB. You're probably wondering how this would be possible if we have nothing to go by since there's no debug info or names yet.
Your unpacked MapleStory 2 clients will actually include type names. Remember when I showed you how to enable the Lists in the toolbar? Two icons to the left of the Strings Window ('s'), or in the center of the section, is a Names Window. As I mentioned previously, Nexon uses virtual calls everywhere because they use a whole ton of abstraction. Take a look at my names window, and you'll notice that theres a base class called CClientPlatformNexonAmerica, just like I used to name the OnSendLoginResult function. This is Nexon's actual class names, and just like the process from MapleStory, I name my IDBs the same names Nexon uses.
Cool, huh? So now we know what the classes are, but we need to know the names of the functions. How can we go upon doing that? Well, up until MapleStory 2 v100 (latest client), Nexon has had a debug function in the client. This function would create two giant mappings of every Send opcode (ks_PACKET), and every Receive opcode (kc_PACKET). Using this function, we can get the name of the opcode that Nexon uses, and apply it to the class name to construct our OnPacket functions! This is exactly how CClientPlatformNexonAmerica::OnSendLoginResult was formed. We take the name of the class, and then from the opcode form the function name: "ks_send_login_result" -> SendLoginResult -> OnSendLoginResult.
As a reference, you can head over to my Library of MapleStory 2 IDBs thread and download the Version 70 IDB. Then, you can reference
You must be registered to see links
to name functions not already named in that IDB. From there, if you're having trouble confirming an opcode in v100, you can take a look at the function from v70 and see if they look the same. If they are the same function, then you've found the function for your opcode in your new client.duck u auto save, will rewrite the rest of this again later
6b. Finding MapleStory 2 Functions
I'll wrap everything up by going over how to find some important functions in the client from the ones already provided to you by my IDC script.
I will finish the rest of this later~
Last edited: