Valhalla Legends Forums Archive | Battle.net Bot Development | C# Packet Formatter

AuthorMessageTime
Myndfyr
I use this quite a bit, and so I thought I'd share. Right now, one of the WriteBytes() parameters uses a type called ConnectionType. This is simply an enumeration -- I define it:
[code]
public enum ConnectionType {
Chat,
Shareware,
StandardOpen,
WarcraftNLS,
Bnls,
BotNet
}
[/code]
but you can define it however you want, provided you use the correct namespaces.

The way to use this is just to call RawLog.Init(path), then RawLog.Write(data_packet, whether_or_not_it_is_incoming, connection_mechanism). Make sure to use RawLog.Close() to flush the stream to disk.

A note: looking through this, I realized that the code is from Alpha 3 of my bot, slightly modified. I lost my bot source in a crash, and this class is from a decompilation of the original source, modified to include static methods rather than instance methods (although you are free to use instance methods), so some methods might look a little weird. Also -- the static Close(bool) method is so named because I had an instance method prototyped Close(void); the boolean has no impact on whether or not the log closes.

Enjoy :)

[code]
using System;
using System.IO;
using System.Text;

namespace ArmaBot
{
   /// <summary>
   /// Logs binary packet data.
   /// </summary>
   public class RawLog
   {
      private RawLog(string path)
      {
         FileStream stream1;
         this.path = path;
         if (!File.Exists(path))
         {
            stream1 = File.Create(path);
            stream1.Close();

         }
         this.fs = new FileStream(path, FileMode.Append, FileAccess.Write);
         this.sw = new StreamWriter(this.fs, Encoding.ASCII);


      }
      private static RawLog _log;
      public static void Init(string name)
      {
         _log = new RawLog(name);
      }

      public static void Write(byte[] data, bool incoming, ConnectionType type)
      {
         _log.WriteBytes(data, incoming, type);
      }
      public static void Close(bool close)
      {
         _log.Close();
      }

      public void Close()
      {
         this.sw.WriteLine("LOG CLOSED");
         this.sw.Close();
      }

      private string FormatByte(byte val)
      {
         string text1;
         for (text1 = val.ToString("x"); (text1.Length < 2); text1 = string.Concat("0", text1))
         {

         }
         return text1;
      }
      private string FormatByteArr(byte[] data)
      {
         string text1;
         StringBuilder builder1;
         int num1;
         char ch1;
         if (data.Length > 16)
         {
            throw new ArgumentException("When formatting a byte array, the data length cannot exceed 16.", "data");

         }
         text1 = Encoding.ASCII.GetString(data);
         builder1 = new StringBuilder(string.Empty);
         for (num1 = 0; (num1 < text1.Length); num1 = (num1 + 1))
         {
            ch1 = text1[num1];
            if (((char.IsLetterOrDigit(ch1) || char.IsPunctuation(ch1)) || (char.IsSeparator(ch1) || char.IsWhiteSpace(ch1))) && !char.IsControl(ch1))
            {
               builder1.Append(text1[num1]);

            }
            else
            {
               builder1.Append('.');

            }

         }
         while ((builder1.Length < 16))
         {
            builder1.Append(" ");

         }
         return builder1.ToString();
      }
      private string FormatLine(int offset)
      {
         string text1;
         for (text1 = offset.ToString("x"); (text1.Length < 4); text1 = string.Concat("0", text1))
         {

         }
         return text1;
      }
      public void WriteBytes(byte[] data, bool incoming, ConnectionType type)
      {
         this.WriteBytes(data, incoming, type, 0, data.Length);
      }
      public void WriteBytes(byte[] data, bool incoming, ConnectionType type, int offset, int length)
      {
         byte[] array1;
         StringBuilder builder1;
         int num1;
         StringBuilder builder2;
         byte[] array2;
         int num2;
         DateTime time1;
         if (incoming)
         {
            this.sw.Write("Incoming Packet Received (via " + type.ToString() + "): ");

         }
         else
         {
            this.sw.Write("Outgoing Packet Sent (via " + type.ToString() + "): ");

         }
         time1 = DateTime.Now;
         this.sw.Write(time1.ToLongDateString());
         this.sw.Write(" ");
         time1 = DateTime.Now;
         this.sw.WriteLine(time1.ToLongTimeString());
         this.sw.WriteLine(string.Empty);
         array1 = new byte[((uint) length)];
         Array.Copy(data, offset, array1, 0, length);
         builder1 = new StringBuilder();
         for (num1 = 0; (num1 < length); num1 = (num1 + 16))
         {
            builder2 = new StringBuilder(76, 76);
            array2 = new byte[((uint) (((length - num1) > 16) ? 16 : (length - num1)))];
            Array.Copy(array1, num1, array2, 0, array2.Length);
            builder2.Append(this.FormatLine(num1));
            builder2.Append(" ");
            for (num2 = 0; (num2 < array2.Length); num2 = (num2 + 1))
            {
               builder2.Append(this.FormatByte(array2[num2]));
               builder2.Append(" ");
               if (((num2 + 1) % 8) == 0)
               {
                  builder2.Append(" ");

               }

            }
            while ((builder2.Length < 59))
            {
               builder2.Append(" ");

            }
            builder1.Append(builder2.ToString());
            builder1.Append(this.FormatByteArr(array2));
            builder1.Append(Environment.NewLine);

         }
#if DEBUG
         System.Diagnostics.Trace.Write(builder1.ToString(), "");
#endif
         this.sw.WriteLine(builder1.ToString());
      }
      private FileStream fs;
      private string path;
      private StreamWriter sw;

   }
}
[/code]
April 1, 2004, 5:04 PM
Myndfyr
Tuberload asked for some sample output. Here it is:

BNLS Connection BNLS_Authorize packet:
[code]
Outgoing Packet Sent (via BNLS): Sunday, March 21, 2004 2:18:12 AM

0000 0b 00 0e 41 72 6d 61 42 6f 74 00 ...ArmaBot.

Incoming Packet Received (via BNLS): Sunday, March 21, 2004 2:18:12 AM

0000 07 00 0e 8d 30 3e 6b ....0.k
[/code]

Part of going between BNLS and BNCS:
[code]
Incoming Packet Received (via BNLS): Thursday, April 01, 2004 1:58:08 PM

0000 17 00 03 df d1 13 8b 97 41 0c 67 96 8d 48 0e 5d ..._Q...A.g..H.]
0010 3c 56 7e 16 70 d5 0d .V..pU.

Outgoing Packet Sent (via WarcraftNLS): Thursday, April 01, 2004 1:58:08 PM

0000 ff 54 18 00 df d1 13 8b 97 41 0c 67 96 8d 48 0e .T.._Q...A.g..H.
0010 5d 3c 56 7e 16 70 d5 0d ].V..pU.

Incoming Packet Received (via WarcraftNLS): Thursday, April 01, 2004 1:58:08 PM

0000 ff 54 1c 00 00 00 00 00 73 ee c3 11 2e 8e ea e1 .T......snC...ja
0010 c7 d7 5d 45 a3 bb 6d 78 fb 1e 11 0b GW]E#;mx{...

Outgoing Packet Sent (via BNLS): Thursday, April 01, 2004 1:58:08 PM

0000 17 00 0a 73 ee c3 11 2e 8e ea e1 c7 d7 5d 45 a3 ...snC...jaGW]E#
0010 bb 6d 78 fb 1e 11 0b ;mx{...
[/code]
April 1, 2004, 9:05 PM
Tuberload
I thought I would convert this logger into Java for anyone that is interested.

[code]
/**
*      A type safe enum used to represent the different connection types on
*   Battle.net. To be used with RawLog objects.
*
*   @author Tuberload/Myndfyre
*   @version 1.0
*/

public final class ConnectionType
{
   // static definitions for all valid connection types
   public static final ConnectionType CHAT         = new ConnectionType ("Chat");
   public static final ConnectionType SHAREWARE   = new ConnectionType ("Shareware");
   public static final ConnectionType STANDARDOPEN   = new ConnectionType ("Standard Open");
   public static final ConnectionType WARCRAFTNLS   = new ConnectionType ("Warcraft NLS");
   public static final ConnectionType BNLS         = new ConnectionType ("BNLS");
   public static final ConnectionType BOTNET      = new ConnectionType ("Botnet");
   
   // provide support for iterating over enum values
   public static final ConnectionType[] CONNECTION_TYPE = { CHAT, SHAREWARE,
                                              STANDARDOPEN,
                                              WARCRAFTNLS,
                                              BNLS, BOTNET };
   
   // instance variables for enum values
   private final String id;
   
   /**
    *   Prevent outside instantiation of the object.
    */
   private ConnectionType (String id)
   {
      this.id = id;
   }
   
   /**
    *   Return the ID of the specified enum value.
    */
   public String toString()
   {
      return id;
   }
   
}
[/code]

[code]
/**
*      A Battle.net packet formater. Originaly created by Myndfyre and then
*   converted to Java by Tuberload. There has been some modifications made
*   during the conversion process.
*
*   @author Tuberload/Myndfyre
*   @version 1.0
*/
import java.io.*;
import java.util.*;
import java.text.DateFormat;

public class RawLog
{
   private static BufferedOutputStream bos;
   private static String path;
   
   private static RawLog log;
   
   /**
    *   Setup the buffered output stream, and prepare to start logging.
    */
   private RawLog (String path) throws Exception
   {
      this.path = path;
      File file = new File (path);
      
      if (!file.exists())
      {
         file.createNewFile();
      }
      
      bos = new BufferedOutputStream (new FileOutputStream (file, true));
   }
   
   /**
    *   Initialize the logger.
    */
   public static void init (String path) throws Exception
   {
      log = new RawLog (path);
   }
   
   /**
    *   Writes an array of bytes to the log
    *   @param data The data to be written to the log.
    *   @param incoming Is the data to be logged incoming or outgoing data.
    *   @param type What are you connected to battle.net as, i.e. DiabloII.
    */
   public static void write (int[] data, boolean incoming, ConnectionType type)
   throws Exception
   {
      writeBytes (data, incoming, type, 0, data.length);
   }
   
   /**
    *   Close the logger.
    */
   public static void close() throws Exception
   {
      bos.close();
   }

   /**
    *   Write the data to the log file.
    */
   private static void writeBytes (int[] data, boolean incoming,
      ConnectionType type, int offset, int length) throws Exception
   {
      int[] array1;
      int[] array2;
      int num1;
      int num2;
      StringBuffer buf1;
      StringBuffer buf2;
      Calendar cal = Calendar.getInstance();
      DateFormat d_format = DateFormat.getDateTimeInstance();
      String time = d_format.format (cal.getTime());
      
      if (incoming) bos.write (("Incoming Packet Recieved (via " +
         type.toString() + "): ").getBytes());
      else bos.write (("Outgoing Packet Sent (via " + type.toString() +
         "): ").getBytes());
         
      bos.write (time.getBytes());
      bos.write ("\r\n\r\n".getBytes());
      
      array1 = new int[length];
      System.arraycopy (data, offset, array1, 0, length);
      buf1 = new StringBuffer ();
      
      for (num1 = 0; num1 < length; num1 += 16)
      {
         buf2 = new StringBuffer (76);
         array2 = new int[(length-num1) > 16 ? 16: length-num1];
         System.arraycopy (array1, num1, array2, 0, array2.length);
         buf2.append (formatOffset (num1));
         buf2.append (" ");
         
         for (num2 = 0; num2 < array2.length; num2++)
         {
            buf2.append (formatByte (array2[num2]));
            buf2.append (" ");
            if (((num2+1) % 8) == 0) buf2.append (" ");
         }
         while (buf2.length() < 59) buf2.append (" ");
         
         bos.write (buf2.toString().getBytes());
         bos.write (formatByteArray (array2).getBytes());
         //System.out.println (formatByteArray (array2));
         bos.write ("\r\n".getBytes());
      }
      bos.write ("\r\n".getBytes());
   }
   
   /**
    *   Format a byte array.
    */
   private static String formatByteArray (int[] data)
   {
      StringBuffer buf1;
      int num1;
      char ch1;
      
      if (data.length > 16) throw new IllegalArgumentException
         ("When formatting a byte array, the data length cannot exceed 16.");
      
      //text1 = String.valueOf (data);
      buf1 = new StringBuffer();
      for (int i = 0; i < data.length; i++)
         buf1.append ((char)data[i]);
      
      for (num1 = 0; num1 < buf1.length(); num1++)
      {
         ch1 = buf1.charAt (num1);
         if (Character.isISOControl (ch1)) buf1.replace (num1, num1+1, ".");
      }   
      
      while (buf1.length() < 16)   buf1.append (' ');
      return buf1.toString();
   }
   
   /**
    *   Format the offset of the data being logged.
    */
   private static String formatOffset (int offset)
   {
      StringBuffer text1 = new StringBuffer (Integer.toHexString (offset));
      
      while (text1.length() < 4)   text1.insert (0, '0');
      
      return text1.toString();
   }
   
   /**
    *   Format a single byte.
    */
   private static String formatByte (int val)
   {
      StringBuffer text1 = new StringBuffer (Integer.toHexString (val));
      
      while (text1.length() < 2)   text1.insert (0, '0');
      
      return text1.toString();
   }
   
   
}
[/code]
Hope this helps someone.
April 8, 2004, 11:40 PM
Myndfyr
[quote author=Tuberload link=board=17;threadid=6118;start=0#msg54109 date=1081467618]
I thought I would convert this logger into Java for anyone that is interested.

[code]
/**
*      A type safe enum used to represent the different connection types on
*   Battle.net. To be used with RawLog objects.
*
*   @author Tuberload/Myndfyre
*   @version 1.0
*/

public final class ConnectionType
{
// code
   /**
    *   Prevent outside instantiation of the object.
    */
   public ConnectionType (String id)
   {
      this.id = id;
   }

[/code]
[/quote]

Hey mate,

The way you prevent an external class from creating new instances is by marking the constructor as private instead of public. I believe that was part of the code that I sent you, but no big deal. Just something quick to change. :)
April 9, 2004, 1:10 AM
Tuberload
Woops that was an accident caused by habit. Thanks for pointing that out.
April 9, 2004, 1:16 AM

Search