How does one find a structure of a packet via IDA?

Results 1 to 7 of 7
  1. #1
    Member crisroxmysoc is offline
    MemberRank
    Aug 2016 Join Date
    91Posts

    How does one find a structure of a packet via IDA?

    Hi guys I am trying to upgrade from v83 to V95.

    I understand there's 500+ guides on IDA(s) however they really never get into or explain how they get the structure of the packet, unless I am too stupid ,which is probably the case.

    I am talking about how in handlers or MaplePacketCreater how you either extract or send packets from/to the client.

    Such as:

    if receiving a packet from the client.

    int x = slea.readInt()
    int y = slea.readByte()

    If sending a packet to the client.

    mplew.write(x)
    mplew.writeShort(y)

    I want to know how you guys know the order of which byte/short/int to write or receive it in.

    also how do you know if the packet is either being received or sent?
    I am using the v95 leak and understand how to obtain packet headers which is the easy part (yeah me!). Like how header of packets can change I am sure the packet structure can change? Due to adding more information, took away some things, etc.

    Here is where I am stuck. I'll explain my process all the way through to where I am stuck.

    I open up v95, the ida. For conversation sake lets say I want to update my Inventory_Operation.

    Currently at v83 at 0x1D

    Lets try looking for Inventory_Operation in the IDA.
    https://ibb.co/Sxd1wcF //Image to follow along.

    Okay, its found in CWvsContext__OnPacket and its invoked when 0x1C is passed in. Thus 0x1C is the packet header for Inventory Operation. Fantastic.

    Now we can change 0x1D to 0x1C so it went down. Guess a good thing to keep in mind.

    However thats not it, what if the packet structure change?

    If we look in the current v83 how packets are sent in the InventoryOperation:

    Code:
    public static byte[] modifyInventory(boolean updateTick, final List<ModifyInventory> mods) {   
         final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();   
         mplew.writeShort(SendOpcode.INVENTORY_OPERATION.getValue());       
     mplew.writeBool(updateTick);    
        mplew.write(mods.size());   
         //mplew.write(0); v104 :)   
         int addMovement = -1;       
     for (ModifyInventory mod : mods) {   
             mplew.write(mod.getMode());   
             mplew.write(mod.getInventoryType());   
             mplew.writeShort(mod.getMode() == 2 ? mod.getOldPosition() : mod.getPosition());    
            switch (mod.getMode()) {     
               case 0: {//add item       
                 addItemInfo(mplew, mod.getItem(), true);     
                   break;            
        }           
         case 1: {//update quantity           
             mplew.writeShort(mod.getQuantity());           
             break;        
            }          
          case 2: {//move        
                                  mplew.writeShort(mod.getPosition());     
                   if (mod.getPosition() < 0 || mod.getOldPosition() < 0) {       
                     addMovement = mod.getOldPosition() < 0 ? 1 : 2;              
          }             
           break;       
             }              
      case 3: {//remove       
                 if (mod.getPosition() < 0) {   
                         addMovement = 2;          
              }         
               break;          
          }         
       }            
    mod.clear();       
     }      
      if (addMovement > -1) {  
              mplew.write(addMovement);   
         }       
     return mplew.getPacket();
        }
    How do I know it will be like
    Code:
     mplew.writeShort(SendOpcode.INVENTORY_OPERATION.getValue());       
     mplew.writeBool(updateTick);  
     mplew.write(mods.size());  
     
    and not something like

    Code:
    mplew.writeShort(SendOpcode.INVENTORY_OPERATION.getValue());       
    mplew.writeBool(updateTick);    
    mplew.writeInt(mods.size());   //From a byte to an Int
    I somewhere read that the stucutre can be read in the method being invoked(CWvsContext::OnInventoryOperation) but when I open it there's unnamed variables but it has something to do with decode1-4, where decode 1 = byte decode 2 = short and etc but I still dont see it

    TL;DR: I need to know how to obtain the structure of the packet within the iDA
    Attached Thumbnails Attached Thumbnails cwsvscontext.png  
    Last edited by crisroxmysoc; 01-06-19 at 09:28 PM.


  2. #2
    Valued Member Umbreon is offline
    MemberRank
    Apr 2012 Join Date
    100Posts

    Re: How does one find a structure of a packet via IDA?

    You're already referencing v95, so it should be easy enough to deduce what the decoded variables do with context. Take what's known in v95 and apply it to your targeted version. You're going to follow each CInPacket call and study what happens next, that's all it takes. I recommend ignoring whatever is in your source already and trying to build the packet structure yourself.

    Looking at CWvsContext::OnInventoryOperation, you see it takes a CInPacket as a parameter. Looking at the first call to this CInPacket, we get...
    if ( CInPacket::Decode1(iPacket) )
    {
    v2->m_bExclRequestSent = 0;
    v2->m_tExclRequestSent = get_update_time();
    }
    So the first byte of the packet is treated as a bool, when if true, allows the client to send further requests.

    The next call is another Decode1 (Let's all this value i), however, this one is used a bit differently. It's a count of how many times to loop the next section of code (the 'mods.size()' in your example. For each i, the loop requires additional data (Decode1, Decode1, Decode2). Further down you'll see a switch statement with additional decodes depending on the value of the first Decode1 in the loop. Follow each switch case and you'll have your proper packet structure. Learn to count with context and you'll be a master.

  3. #3
    Member crisroxmysoc is offline
    MemberRank
    Aug 2016 Join Date
    91Posts

    Re: How does one find a structure of a packet via IDA?

    Hey, want to thank you for the response.

    I have a couple of questions if thats okay with you?

    1. So opening up the CWvsContext::OnInventoryOperation and doing what you said "Follow the CinPacket" essentially, we get our first one which is

    Code:
    if ( (unsigned __int8)CInPacket::Decode1(a2) )  { 
       *((_DWORD *)v2 + 2094) = 0;   
     *((_DWORD *)v2 + 2095) = get_update_time(); 
     }
    
    How do you know its a Boolean, the simple fact that the if condition is either going to be true or not?

    and the second CINPacket being referenced is in a variable

    Code:
    k = (unsigned __int8)CInPacket::Decode1(a2);
    then follows another if statement where k > 0 then into a while loop

    how do you know its counting anything?

    To be honest, I can see myself learning this fairly easily but I just need a nice good ol' example if you know what I mean. What are the techniques besides just following the CinPacket::Decode how can I deduce them to make a packet structure and how do I know whats going on in them.

    - - - Updated - - -

    For example what is this CINPacket call doing

    Code:
    if ( k <= 0 )          {    
    if ( v68 )            {   
    
    v37 = CInPacket::Decode1(a2);           
    CUserLocal::SetSecondaryStatChangedPoint(v37);        
    v38 = v67;   
    v2 = v58;    
    
    for ( j = 0; v38 && j < *(_DWORD *)(v38 - 4); ++j )  
    CWvsContext::CheckEquipOnAutoStartQuest(*(_DWORD *)(v38 + 4 * j), 1);   
    CWvsContext::UpdateAutoQuestAlertIcon(v2);        
    }       
    
    else    {       
    v2 = v58;     
           }
    is that a mplew.writeShort/Byte/Int? EDIT this would be a mplew.writeShort() right? but I dont know whats inside the write call now

    - - - Updated - - -

    Oh, I think I am getting it now

    However how do you know whats inside the mplew.write(Y) like variable wise. Simple terms how do you know its talking about

  4. #4
    Member Feras is offline
    MemberRank
    Jan 2019 Join Date
    50Posts

    Re: How does one find a structure of a packet via IDA?

    Decode4 -> int
    Decode2 -> short
    Decode1 -> could be either a byte or a bool, to the client they're all just a data type that occupies one byte (oversimplified). Whether it's a bool or a byte depends on how the client uses it (if it's only ever used as a condition in an if-statement it's probably a bool).

    To find out what the decoded value does you just gotta follow the code and see how the variable is used. Here's a basic example:

    Code:
    void __thiscall CUser::OnEmotion(CUser *this, CInPacket *iPacket)
    {
      CUser *v2; // esi
      int v3; // ebx
      int v4; // ebp
      v2 = this;
      v3 = CInPacket::Decode4(iPacket);
      v4 = CInPacket::Decode4(iPacket);
      v2->m_bEmotionByItemOption = (unsigned __int8)CInPacket::Decode1(iPacket);
      CAvatar::SetEmotion((CAvatar *)&v2->vfptr, v3, v4);
    }
    This packet is sent when a user uses an emote. It seems like v3 and v4 are both ints. They're getting passed as parameters to the function CAvatar::SetEmotion, so lets take a look at that:

    Code:
    void __thiscall CAvatar::SetEmotion(CAvatar *this, int nEmotion, int tDuration)
    v3 was passed as nEmotion and v4 as tDuration, so for this emotion packet the first thing you'd write is an int for the type of emote used, and an int for how long it remains for (milliseconds).

    That Decode1 seems self-explanatory - it's a bool that's true if the emotion was triggered by an item, false otherwise. It's less obvious why that value is needed but that's just the client doing it's thing so we don't really need to know.

    It isn't always obvious how certain variables are used, and in some cases you might have to dig really deep to find out how things work. You might even need to do some guesswork or trial-and-error because IDA isn't always that helpful, but mainly just read through the IDA code and try making sense of it.

  5. #5
    Member crisroxmysoc is offline
    MemberRank
    Aug 2016 Join Date
    91Posts

    Re: How does one find a structure of a packet via IDA?

    That Decode1 seems self-explanatory - it's a bool that's true if the emotion was triggered by an item, false otherwise. It's less obvious why that value is needed but that's just the client doing it's thing so we don't really need to know.
    But it isnt used as a condition in an if statement its just being declared, but I guess it really doesnt matter does it as its both byte. I can still probably do mplew.write(either 0 or 1)? When

    I start to search the function the and go into CAvator::SetEmotion a2 and a3 arent named nEmotion or tDuration, did you label that yourself and if you did how did you know? and How does one find the packet header for this ?

    How do I know if this is a Recv or a send opcode?

    - - - Updated - - -

    And my other question, how do you find the rest of the packets? I have a feeling the OnPacket functions dont hold everything how does one go about that? and your comment was extremely helpful thank you.
    Last edited by crisroxmysoc; 02-06-19 at 07:31 PM.

  6. #6
    Member Feras is offline
    MemberRank
    Jan 2019 Join Date
    50Posts

    Re: How does one find a structure of a packet via IDA?

    But it isnt used as a condition in an if statement its just being declared,
    I just made a judgement based on the variable it's being assigned to, 'bByItemOption' - either it is triggered by an item or it isn't.

    I start to search the function the and go into CAvator::SetEmotion a2 and a3 arent named nEmotion or tDuration, did you label that yourself and if you did how did you know? and How does one find the packet header for this ?

    How do I know if this is a Recv or a send opcode?
    And my other question, how do you find the rest of the packets? I have a feeling the OnPacket functions dont hold everything how does one go about that?
    I'm using the leaked v95 PDB so all these function names and parameters were already there, I didn't have to name them. For other versions you'd need to compare with v95 to figure out the names. Finding the header is just by looking through the code in functions like CWvsContext::OnPacket and CField::OnPacket.

    You actually can find every packet handler by looking through OnPacket methods. All client-received packets go through CClientSocket::ProcessPacket, and from there it looks at the header and goes to CField::OnPacket, CLogin::OnPacket, etc. which is why finding client-recv packet handlers is so easy if you have a list of the opcodes (even without that, most of the packet handlers' names are self-explanatory).

    You'd know that it's a packet being received by the client (sent by server) when you see that the OnPacket function takes a CInPacket* parameter. For packets being sent by the client (recv by server) you'd see a COutPacket* in the client.

    As for packets sent by the client (recv by server), they're all over the place, with the only common feature being the use of COutPacket methods (and CClientSocket::SendPacket). They could be found inside the functions that handle damage calculations, using hotkeys, mouse button handlers, etc. so they don't usually have functions specifically made to create and send packets to the server.[/COLOR]

  7. #7
    Member crisroxmysoc is offline
    MemberRank
    Aug 2016 Join Date
    91Posts

    Re: How does one find a structure of a packet via IDA?


    I'm using the leaked v95 PDB so all these function names and parameters were already there, I didn't have to name them. For other versions you'd need to compare with v95 to figure out the names.
    I am also using the v95 leak and it doesnt name my parameters?

    How do you translate nexon names to odin?

    - - - Updated - - -

    Heres what I am seeing

    https://imgur.com/a/El4uEOE



Advertisement