WinPCap - Packet data containing strings?

This is a discussion on WinPCap - Packet data containing strings? within the C Programming forums, part of the General Programming Boards category; I am writing a program that uses winpcap to sniff packets. More specifically, packets from the game "Warcraft III." I ...

  1. #1
    Registered User
    Join Date
    Jan 2004
    Posts
    11

    Unhappy WinPCap - Packet data containing strings?

    I am writing a program that uses winpcap to sniff packets. More specifically, packets from the game "Warcraft III." I have figured out the formatting of the game's chat messages as a test. It looks something like this:

    Code:
    typedef struct wc3_chat
    {
    //DWORD id; <-- these are handled in wc3_header.
    DWORD event_id;
    DWORD flags;
    DWORD ping;
    DWORD ip;  // defunct
    DWORD account_number; // defunct
    DWORD reg_auth; //defunct
    char* username;
    char* text;
    }wc3_chat;
    Testing in-game, I found that this structure works fine for everything except for the strings which give odd behavior.

    Heres some bits from where I'm trying to use the structure:
    Code:
    const u_char *data = (sniffer.pkt_data + 14 + ip_len + tcp_len + 4);  //4 bytes for wc3_header struct
    wc3_chat *chat = (wc3_chat*)data;
    
    if (chat->event_id == 0x05)
         printf("%s: %s\n", &chat->username, &chat->text);
    *Edit* The problem is that chat->text doesn't display correctly. Instead of displaying the message, it displays the username minus the first 4 letters. It would appear that chat->text is pointing 4 bytes after wherever chat->username is pointing to instead of pointing after the NULL. See below for more information and a sample packet capture from Wireshark.

    Can anyone help me figure out what I'm doing wrong here?

    Thanks,
    Glorfindel
    Last edited by Glorfindel; 02-11-2009 at 08:36 AM. Reason: typo while fixing typo > <

  2. #2
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300
    It looks like you are trying to skip parts of the packet that are irrelevant to you, which makes sense. However, if you are not getting the results you want, the first thing to try would be printing the entire packet, then print the entire packet "organized" in some way (including "irrelevant" fields) and then decide where the problem occurs. That might mean converting some ints, etc, so that you can make sense of the entire packet. If you can do that, then you won't be having this difficulty.
    C programming resources:
    GNU C Function and Macro Index -- glibc reference manual
    The C Book -- nice online learner guide
    Current ISO draft standard
    CCAN -- new CPAN like open source library repository
    3 (different) GNU debugger tutorials: #1 -- #2 -- #3
    cpwiki -- our wiki on sourceforge

  3. #3
    Registered User
    Join Date
    Jan 2004
    Posts
    11
    I've used Wireshark to look at the packet I'm catching, and have checked all values preceding the two strings to be accurate. The "defunct" parts aren't used by the game anymore and hold the data: 0xbaadf00d (a game developer's joke?). Right up to that first string, all the values match up with what I see in Wireshark.

    Heres an example packet data from Wireshark:
    Code:
                                         05 00 00 00 00 00   ..ls.... ........
    0040  00 00 1f 00 00 00 00 00  00 00 0d f0 ad ba 0d f0   ........ ........
    0050  ad ba 54 73 29 72 65 63  72 75 69 74 40 55 53 45   ..Ts)rec ruit@USE
    0060  61 73 74 00 43 6c 61 6e  20 54 72 61 6e 73 63 65   ast.Clan  Transce
    0070  6e 64 65 6e 63 65 2e 20  41 20 53 74 61 72 63 72   ndence.  A Starcr
    0080  61 66 74 2f 42 72 6f 6f  64 77 61 72 20 6d 61 70   aft/Broo dwar map
    0090  70 69 6e 67 20 63 6c 61  6e 20 66 6f 72 20 70 65   ping cla n for pe
    00a0  6f 70 6c 65 20 6f 66 20  61 6c 6c 20 6c 65 76 65   ople of  all leve
    00b0  6c 73 2e 20 57 65 27 72  65 20 6c 6f 6f 6b 69 6e   ls. We'r e lookin
    00c0  67 20 66 6f 72 20 6d 65  6d 62 65 72 73 21 20 63   g for me mbers! c
    00d0  68 65 63 6b 20 75 73 20  6f 75 74 21 20 43 6c 61   heck us  out! Cla
    00e0  6e 20 54 73 00                                     n Ts.
    The printf message in this case displayed:
    Ts)recuit@USEast: ecruit@USEast

    -----------------

    The behavior I mentioned above is actually wrong.
    What is happening is actually that chat->text is always pointing 4 bytes after wherever chat->username is pointing, which causes text to always display the username minus the first 4 letters. The screenshot I posted was actually the result of me trying to add some placeholder variables between the username and text in the structure. I forgot to remove it before I ran the program for the screenshot. The times that the thing displayed correctly or almost correctly were simply whenever the user had a short name.

    I have absolutely no idea why that would happen... shouldn't it be pointing to the end of the string preceding it?

    I'll go back to edit my first post to reflect that... sorry!

  4. #4
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    "baadf00d" is one of many "funny" things you can make with hex-numbers, and quite often used to indicate "don't use this" [particularly, if you set a pointer to that value, you know it's going to "explode" if you ever try to use that pointer, and it's not a NULL pointer].

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  5. #5
    Registered User
    Join Date
    Jan 2004
    Posts
    11
    Thanks to someone from #winprog on EFNet, I've remembered that char* doesn't reserve any actual memory space for the string, but only enough to store the memory address to which it points. (seemingly 4-bytes). That being said, it appears that this method will not work to parse the packet data.

    I suppose I should use a class with member variables instead. I can read in each member including the strings in the constructor, and that way I won't have to worry about losing data when I read in the next packet anyway.

  6. #6
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300
    Quote Originally Posted by Glorfindel View Post
    What is happening is actually that chat->text is always pointing 4 bytes after wherever chat->username is pointing, which causes text to always display the username minus the first 4 letters. The screenshot I posted was actually the result of me trying to add some placeholder variables between the username and text in the structure. I forgot to remove it before I ran the program for the screenshot. The times that the thing displayed correctly or almost correctly were simply whenever the user had a short name.

    I have absolutely no idea why that would happen... shouldn't it be pointing to the end of the string preceding it?
    Probably because of this:
    Code:
    char* username;
    char* text;
    }wc3_chat;
    username is a char pointer, so it takes up four bytes. "text" will have a variable offset.
    If the real username in the packet ends at the first period, you can count the characters up to there and use that as the offset for the text -- using char *text from the struct will be useless.
    C programming resources:
    GNU C Function and Macro Index -- glibc reference manual
    The C Book -- nice online learner guide
    Current ISO draft standard
    CCAN -- new CPAN like open source library repository
    3 (different) GNU debugger tutorials: #1 -- #2 -- #3
    cpwiki -- our wiki on sourceforge

  7. #7
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    I'd say either use a fixed size buffer for text and user-name, or allocate sufficient memory for them. You need to copy each byte out, so you could use strlen() to figure out the length, and then strcpy() to copy it. If you allocate memory, don't forget to add one for the zero at the end of the string.

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  8. #8
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300
    Quote Originally Posted by Glorfindel View Post
    Thanks to someone from #winprog on EFNet, I've remembered that char* doesn't reserve any actual memory space for the string, but only enough to store the memory address to which it points. (seemingly 4-bytes). That being said, it appears that this method will not work to parse the packet data.
    A char* is 4 bytes long (on most systems), and it is four bytes long in the struct. It would be wrong to say that a pointer has no size.

    Obviously you can use this method to find the beginning of username. Since username has a variable length the only way to proceed from there is to use a method which deals with this for each individual struct, which shouldn't be very hard.

    An even easier way would be to take all the data starting at "username" and break it at the 00 byte (which appears as a period in wireshark).
    Code:
    0040  00 00 1f 00 00 00 00 00  00 00 0d f0 ad ba 0d f0   ........ ........
    0050  ad ba 54 73 29 72 65 63  72 75 69 74 40 55 53 45   ..Ts)rec ruit@USE
    0060  61 73 74 00 43 6c 61 6e  20 54 72 61 6e 73 63 65   ast.Clan  Transce
    Remember '\0' is a line terminator. That's it.
    C programming resources:
    GNU C Function and Macro Index -- glibc reference manual
    The C Book -- nice online learner guide
    Current ISO draft standard
    CCAN -- new CPAN like open source library repository
    3 (different) GNU debugger tutorials: #1 -- #2 -- #3
    cpwiki -- our wiki on sourceforge

  9. #9
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300
    Quote Originally Posted by matsp View Post
    I'd say either use a fixed size buffer for text and user-name, or allocate sufficient memory for them. You need to copy each byte out, so you could use strlen() to figure out the length, and then strcpy() to copy it. If you allocate memory, don't forget to add one for the zero at the end of the string.

    --
    Mats
    strlen will indeed work well since it counts up to a null terminator. So that will give you the offset.
    C programming resources:
    GNU C Function and Macro Index -- glibc reference manual
    The C Book -- nice online learner guide
    Current ISO draft standard
    CCAN -- new CPAN like open source library repository
    3 (different) GNU debugger tutorials: #1 -- #2 -- #3
    cpwiki -- our wiki on sourceforge

  10. #10
    Registered User carrotcake1029's Avatar
    Join Date
    Apr 2008
    Posts
    398
    Instead of throwing your data straight into the struct, you are going to want to analyze the packet piece by piece. Since you know the first 6 pieces are DWORDs, you know that the size is static and it is safe to read into it. When you have null terminated strings, you need to have a function basically loop through the remaining chars until it hits a null value, allocate however many characters you stepped over (plus one for the null terminator), and then you copy it over. I guess you could have memcpy do this....

  11. #11
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    Quote Originally Posted by carrotcake1029 View Post
    Instead of throwing your data straight into the struct, you are going to want to analyze the packet piece by piece. Since you know the first 6 pieces are DWORDs, you know that the size is static and it is safe to read into it. When you have null terminated strings, you need to have a function basically loop through the remaining chars until it hits a null value, allocate however many characters you stepped over (plus one for the null terminator), and then you copy it over. I guess you could have memcpy do this....
    strlen() and strcpy() will do that perfectly fine. But memcpy() with known size may be a tiny bit faster [as strcpy() is often implemented as
    Code:
    char *strcpy(char *dest, char *src)
    {
         size_t len = strlen(src);
         return memcpy(dest, src, len);
    }
    If you have already done strlen(), you don't need to do it again to copy the content. But strcpy() is a more obvious choice if performance isn't super-critical.

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  12. #12
    Registered User
    Join Date
    Jan 2004
    Posts
    11
    Thanks very much for the help! The code is working quite nicely now that I've rewritten the structure as a class and parsed the data in constructor. For anyone else trying to do something similar, here is the class I wrote to replace the structure.

    Code:
    class Cwc3chat
    {
    public:
    	Cwc3chat(const u_char *data)
    	{
    		
    		event_id = (DWORD)*data;
    		flags = (DWORD)(*data + 4);
    		ping = (DWORD)(*data + 8);
    		ip = (DWORD)(*data + 12);
    		account_number = (DWORD)(*data + 16);
    		reg_auth = (DWORD)(*data + 20);
    		char *temp = (char*)(data + 24);
    		char *temp2 = (char*)(data + 25 + strlen(temp));
    
    		username = (char*)malloc(strlen(temp) + 1);
    		text = (char*)malloc(strlen(temp2) + 1);
    
    		if (username != NULL)
    			strcpy(username, temp);
    		if (text != NULL)
    			strcpy(text, temp2);
    	}
    	~Cwc3chat()
    	{
    		free(username);
    		free(text);
    	}
    	DWORD event_id;
    	DWORD flags;
    	DWORD ping;
    	DWORD ip;  // defunct
    	DWORD account_number; // Defunct
    	DWORD reg_auth; //defunct
    	char *username;
    	char *text;
    };
    Thanks again,
    Glorfindel

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. singly linked circular list
    By DarkDot in forum C++ Programming
    Replies: 0
    Last Post: 04-24-2007, 09:55 PM
  2. Reading a file with Courier New characters
    By Noam in forum C Programming
    Replies: 3
    Last Post: 07-07-2006, 10:29 AM
  3. Dynamic data members?
    By confusalot in forum C++ Programming
    Replies: 4
    Last Post: 02-27-2005, 11:15 AM
  4. C diamonds and perls :)
    By Carlos in forum A Brief History of Cprogramming.com
    Replies: 7
    Last Post: 05-16-2003, 11:19 PM
  5. gcc problem
    By bjdea1 in forum Linux Programming
    Replies: 13
    Last Post: 04-29-2002, 07:51 PM

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21