- Joined
- Oct 25, 2008
- Messages
- 224
- Reaction score
- 315
Like I've promised I've created a little tutorial on how to get packets/opcodes out of an unpacked client using IDA.
Requirements:
If you are going to analyze v90, you can download a pre-made IDA database
To start IDA, best use idaq.exe - That's the new QT version and it just looks much better than the old version.
If the quick-start comes up, select Go and then open the KMST IDB. Run IDA again and do the same for the v90 IDB.
On the left, you'll see a list of all methods (usually ordered by address), in the middle there is an assembly view of the current function (the tab is called IDA View). Of course there are more tabs like Hex View, Imports, Exports and later Pseudo Code.
Some important shortcuts:
F5: Decompile the current method into pseudo C code.
X: Find all references to the method you have marked with your cursor in asm/pseudocode.
ALT + ?: Start searching.
CTRL + ?: Continue searching.
Where ? can be:
T: Search a text string. This works in almost all sub-windows of IDA (method list, assembly, pseudocode...)
B: Search for a sequence of bytes (you may know it as AoB, but unlike CE this doesn't support variable bytes, sth. like E8 ? ? ? FF will not work )
I: Search for an integer value in assembly. This is much faster than text-search.
What we'll be doing is basically:
1) Locating the packet you need in the KMST client. Getting used to the names Nexon uses might take some time.
2) Looking for that method in the client you're analyzing.
Finding send-packets (which the client will receive) is not that hard. Nexon handles all of them in mainly 2 methods:
CWvsContext::OnPacket for most opcodes < 140 (decimal).
CField::OnPacket for everything above.
These two methods are already named in my v90 IDB. Just go in there and check where it goes for a specific opcode you need a packet structure for.
An example: I want the exact strucutre of the SpawnMonster packet and know that the opcode is 276 in v90.
In CField::OnPacket I see:
As my opcode is 276, it will go into the else-clause of that second if-statement. I set my cursor before that second if and press down until I see else directly before the cursor. That has to be the packet handler for monster packets. Doing the same in KMST reveals: It is indeed CMobPool::OnPacket.
The next thing I'd do is name that method I've found. In most cases I copy the name from KMST - there are 2 methods of doing that: Either, you copy only the visible part (CMobPool::OnPacket), or you mark that name and press N. It will then show you ?OnPacket@CMobPool@@QAEXJAAVCInPacket@@@Z. This string contains advanced information about the method like parameters and if you use this name, you'll also see the parameters in the function list on the left later on. A disadvantage is that it looks quite ugly in the assembly view.
So when I double click that OnPacket method, I'll get to a switch statement which happens to have the opcode I looked for. It passes the packet on to CMobPool::OnMobEnterField. Inside, it will actually start reading data from the packet.
Decode1: Read byte (mplew.write())
Decode2: Read short
Decode4: Read int
DecodeStr: Read string
DecodeBuffer: Read everything that is longer than 4 bytes and not a string. This is often used for longs (8), names with static length, or buff-stats.
In this method, it'll only read the first few bytes of the packet though. Mark the packet parameter (the on that's passed on to the Decode calls) and you'll directly see where else it is used. In this case, it is passed on to two other methods. One reads the buff stats which the monster currently has (Nexon calls buffs TemporaryStat or SecondaryStat) and the other reads the rest of the packet (position, foothold and that stuff).
Now you might ask: I see THAT it reads ints and what not, but how do I know WHAT it is?
That is indeed a problem. Sometimes it's obvious what it is, e.g. ints at the beginning of a packet are often object IDs, or for receive packets (which the client sends) timestamps. Sometimes it's also becomes obvious when the client passes the value it read on to another method, e.g. is_skill_need_master_level.
In most cases, you don't know what is though. Then you can only sniff in GMS and perhaps it'll become clear to you, or you'll hardcode the value GMS uses without knowing what it does, OR you just set it to 0. Note that this last possibility sometimes has unwanted side effects which you can't explain until you find out it was caused by that 0, that has already cost me hours of time.
Finding packets which the client sends:
This isn't need that often - the client sends all data you need to you and you just need to interpret them - you can't really break anything.
Sometimes you just can't find out what a certain value in a packet means though, and then the client might give you a hint.
Unfortunately, the client doesn't send it packets in once place, but they are scattered throughout the executable.
Useful methods for finding a packet-send method:
1) The method references a method that you have already named in your unpacked client and which is NOT used by many other methods. StringPool::GetInstance e.g. would be a bad choice, as it used 9000 times :
2) There is some unique value, e.g. a skill ID in the packet - simply used it in ALT+I. This can also be done for the opcode, but make sure to tick "Find all occurences" - if it is low, that value will be used very often by all kinds of asm-opcodes. You need to look out for a push, followed by COutPacket::COutPacket_0.
That's it for now, I think.
Requirements:
- IDA Pro 6.1:
You must be registered to see links
- An unpacked binary you want to analyze
- KMST leak (IDA database included):
You must be registered to see links
- The ability to learn yourself. Trial and Error is a very important concept for this.
If you are going to analyze v90, you can download a pre-made IDA database
You must be registered to see links
It has some common functions like CInPacket/COutPacket methods already named.To start IDA, best use idaq.exe - That's the new QT version and it just looks much better than the old version.
If the quick-start comes up, select Go and then open the KMST IDB. Run IDA again and do the same for the v90 IDB.
On the left, you'll see a list of all methods (usually ordered by address), in the middle there is an assembly view of the current function (the tab is called IDA View). Of course there are more tabs like Hex View, Imports, Exports and later Pseudo Code.
Some important shortcuts:
F5: Decompile the current method into pseudo C code.
X: Find all references to the method you have marked with your cursor in asm/pseudocode.
ALT + ?: Start searching.
CTRL + ?: Continue searching.
Where ? can be:
T: Search a text string. This works in almost all sub-windows of IDA (method list, assembly, pseudocode...)
B: Search for a sequence of bytes (you may know it as AoB, but unlike CE this doesn't support variable bytes, sth. like E8 ? ? ? FF will not work )
I: Search for an integer value in assembly. This is much faster than text-search.
What we'll be doing is basically:
1) Locating the packet you need in the KMST client. Getting used to the names Nexon uses might take some time.
2) Looking for that method in the client you're analyzing.
Finding send-packets (which the client will receive) is not that hard. Nexon handles all of them in mainly 2 methods:
CWvsContext::OnPacket for most opcodes < 140 (decimal).
CField::OnPacket for everything above.
These two methods are already named in my v90 IDB. Just go in there and check where it goes for a specific opcode you need a packet structure for.
An example: I want the exact strucutre of the SpawnMonster packet and know that the opcode is 276 in v90.
In CField::OnPacket I see:
Code:
if ( a2 < 177 || a2 > 275 )
{
if ( a2 < 276 || a2 > 302 )
{
if ( a2 < 303 || a2 > 310 )
{
The next thing I'd do is name that method I've found. In most cases I copy the name from KMST - there are 2 methods of doing that: Either, you copy only the visible part (CMobPool::OnPacket), or you mark that name and press N. It will then show you ?OnPacket@CMobPool@@QAEXJAAVCInPacket@@@Z. This string contains advanced information about the method like parameters and if you use this name, you'll also see the parameters in the function list on the left later on. A disadvantage is that it looks quite ugly in the assembly view.
So when I double click that OnPacket method, I'll get to a switch statement which happens to have the opcode I looked for. It passes the packet on to CMobPool::OnMobEnterField. Inside, it will actually start reading data from the packet.
Decode1: Read byte (mplew.write())
Decode2: Read short
Decode4: Read int
DecodeStr: Read string
DecodeBuffer: Read everything that is longer than 4 bytes and not a string. This is often used for longs (8), names with static length, or buff-stats.
In this method, it'll only read the first few bytes of the packet though. Mark the packet parameter (the on that's passed on to the Decode calls) and you'll directly see where else it is used. In this case, it is passed on to two other methods. One reads the buff stats which the monster currently has (Nexon calls buffs TemporaryStat or SecondaryStat) and the other reads the rest of the packet (position, foothold and that stuff).
Now you might ask: I see THAT it reads ints and what not, but how do I know WHAT it is?
That is indeed a problem. Sometimes it's obvious what it is, e.g. ints at the beginning of a packet are often object IDs, or for receive packets (which the client sends) timestamps. Sometimes it's also becomes obvious when the client passes the value it read on to another method, e.g. is_skill_need_master_level.
In most cases, you don't know what is though. Then you can only sniff in GMS and perhaps it'll become clear to you, or you'll hardcode the value GMS uses without knowing what it does, OR you just set it to 0. Note that this last possibility sometimes has unwanted side effects which you can't explain until you find out it was caused by that 0, that has already cost me hours of time.
Finding packets which the client sends:
This isn't need that often - the client sends all data you need to you and you just need to interpret them - you can't really break anything.
Sometimes you just can't find out what a certain value in a packet means though, and then the client might give you a hint.
Unfortunately, the client doesn't send it packets in once place, but they are scattered throughout the executable.
Useful methods for finding a packet-send method:
1) The method references a method that you have already named in your unpacked client and which is NOT used by many other methods. StringPool::GetInstance e.g. would be a bad choice, as it used 9000 times :
2) There is some unique value, e.g. a skill ID in the packet - simply used it in ALT+I. This can also be done for the opcode, but make sure to tick "Find all occurences" - if it is low, that value will be used very often by all kinds of asm-opcodes. You need to look out for a push, followed by COutPacket::COutPacket_0.
That's it for now, I think.