Staredit Network

Staredit Network -> Computers and Technical -> Bot Programming Tutorial
Report, edit, etc...Posted by MindArchon on 2005-10-10 at 23:56:54
About this tutorial
I've thrown this together because when I started out programming a bot, I had almost no idea where to start. Sure, there are places such as BNET Docs that tell you what packets and stuff to send, but getting past the initial phase of "WTF is that and why the hell are we doing it" was hard.

This tutorial was written for mostly anybody, but code examples are in VB, and data that involves Visual Basic is in orange text.

Introduction to bot programming
A bot such as StealthBot is actually imitating the starcraft client when it connects to battle.net. It sends data in exactly (more or less) the same way that the actual Starcraft client does.

Once connected to battle.net, the client sends and recieves packets.

A packet is simply information send to and from a server (in this case battle.net). Since battle.net uses TCP protocol, there is actually a server to connect to.

All packets are made up of bytes. Lets take a look at a packet we might send to battle.net.

CODE
FF 0C 19 00 02 00 00 00 4F 70 20 4D 69 6E 64 41  ΓΏ ....Op MindA
72 63 68 6F 6E 28 55 29 00                       rchon(U)........

(this is packet 0x0C, used to join channels)


On the left is the data we are sending in hexadecimal, while the data on the right is what the packet would look like in string format.

For instance, in that apcket FF would be a byte, 0C would be a byte, 19 would be a byte.. etc. The data is in hex format (FF) but converted to decimal it is 255 (you can use windows calculator for this).

A byte is made up of 8 bits (bit = 0 or 1 [binary]). 2 bytes make up a word (integer). So FF 0C would make up a word (3327 in decimal). A dword is 4 bytes. So FF 0C 19 00 would be a dword (1641727 in decimal).

All BNCS (Battle.Net Chat Server) servers have a specific kind of packet format. This format is

QUOTE(bnetdocs.valhallalegends.com)
(BYTE)      Always 0xFF
(BYTE)      Message ID
(WORD)      Message length, including this header
...      Message Data


In Visual Basic 0x means the same as &H which indentifies the value as hexadecimal

So lets analyze the packet above.
FF = every packet starts with this, so thats one byte.
0C = The packet ID (every packet has its own individual ID, so battle.net can figure out what your trying to do)
19 00 = A word (2 bytes). Converts to 25. The entire packet is 25 bytes long

Next is the data inside the packet.
In this particular packet, the data is:

DWORD = [channel flags]
String = channel to join.

Channel flags are either hexadecimal 00 (only will join if the channel isn't empty), hexadecimal 01 (will join a channel with your country code in it, such as Brood War USA-1), or hexadecimal 02 (will join a channel regardless of if its empty or not).

In this example my flag is 02. I dont care if Op MindArchon(U) is empty or not, I just want to join it.

Since the flag is 4 bytes (its a dword) i send 02 00 00 00. Next is the string name I want to join, which always ends in a null character. This tells the server where a string ends.

Congratulations, you just analyzed your first BNCS packet!

Of course you probably wondered how I knew what the packet contained and stuff. There is a free database online that tells you almost every battle.net packet you would ever need to know. Check it out at http://bnetdocs.valhallalegends.com

Connecting to battle.net

If you using VB, you can connect to battle.net with a winsock control

There are four battle.net servers supported to blizzard.

useast.battle.net
uswest.battle.net
asia.battle.net
europe.battle.net

Those are all on port 6112.

Once connected to battle.net, the first thing you must send is what you want to do. You send this first. In this case we will send one byte (0x01) because that indicates we want to connect as a client. For instance, sending 0x02 would indicate we want to connect to FTP and so on.

After sending 0x01, you must start sending your packets. A complete login sequence can be found at http://bnetdocs.valhallalegends.com/sequence.php

In just about any language, it is simple and a lot less confusing to use a packet buffer. Basically you build the packet as you go, then send it when it is complete. Here is the packet buffer I use in VB, originally done by DarkMinion, modifed by me. Make sure you figure out what this does before you use it. This is a class module

CODE
Private buffer As String
Public Function InsertDWORD(data As Long)
   buffer = buffer & MakeDWORD(data)
End Function

Public Function InsertWORD(data As Integer)
buffer = buffer & MakeWORD(data)
End Function
Public Function InsertBYTE(data As Integer)
buffer = buffer & Chr(data)
End Function
Public Function InsertNTString(data As String)
buffer = buffer & data & Chr(0)
End Function
Public Function InsertNonNTString(data As String)
buffer = buffer & data
End Function
Public Function Clear()
buffer = ""
End Function
Public Function SendPacket(PacketID As Byte)
If MainFRM.bnet.State <> sckConnected Then Exit Function
MainFRM.bnet.SendData Chr(&HFF)
MainFRM.bnet.SendData Chr(PacketID)
MainFRM.bnet.SendData MakeWORD(Len(buffer) + 4)
MainFRM.bnet.SendData buffer
Dim outputb As String
outputb = Chr(&HFF) & Chr(PacketID) & MakeWORD(Len(buffer) + 4) & buffer
'AddC_SCColors MainFRM.channeltext, "BNETSEND:", vbYellow, DebugOutput(outputb), vbRed, True
Clear
End Function
Public Function SendPacketBNLS(PacketID As Byte)
If MainFRM.bnls.State <> sckConnected Then Exit Function
MainFRM.bnls.SendData MakeWORD(Len(buffer) + 3)
MainFRM.bnls.SendData Chr(PacketID)
MainFRM.bnls.SendData buffer
Dim outputb As String
outputb = MakeWORD(Len(buffer) + 3) & Chr(PacketID) & buffer
'AddC_SCColors MainFRM.channeltext, "BNLSSEND:", vbGreen, DebugOutput(outputb), vbRed, True
Clear
End Function
Public Function SendPacketRealm(PacketID As Byte)
If MainFRM.realm.State <> sckConnected Then Exit Function
MainFRM.realm.SendData MakeWORD(Len(buffer) + 3)
MainFRM.realm.SendData Chr(PacketID)
MainFRM.realm.SendData buffer
Dim outputb As String
outputb = MakeWORD(Len(buffer) + 3) & Chr(PacketID) & buffer
'AddC_SCColors MainFRM.channeltext, "BNLSSEND:", vbYellow, DebugOutput(outputb), vbRed, True
Clear
End Function


Then put these functions inside a module somewhere.

CODE
Public Function GetWord(data As String) As Long
Dim lReturn As Long
   Call CopyMemory(lReturn, ByVal data, 2)
   GetWord = lReturn
End Function

Public Function MakeLong(X As String) As Long
   If Len(X) < 4 Then
       Exit Function
   End If
   CopyMemory MakeLong, ByVal X, 4
End Function

Public Function KillNull(ByVal Text As String) As String
   Dim i As Integer
   i = InStr(1, Text, Chr(0))
   If i = 0 Then
       KillNull = Text
       Exit Function
   End If
   KillNull = Left(Text, i - 1)
End Function

Public Function StrtoHex(ByVal data As String, Optional addspace As Byte = 0) As String
   Dim buffer As String
   Dim tmphex As String
   Dim i As Integer
   For i = 1 To Len(data)
       tmphex = Hex(Asc(Mid$(data, i, 1)))
       If Val("&H" & tmphex) < 16 Then tmphex = "0" & tmphex
       
       buffer = IIf(addspace = 0, buffer & tmphex & " ", buffer & tmphex)
   Next i
   
   StrtoHex = Left$(buffer, Len(buffer) - 1)
End Function

Public Function HexToStr(ByVal Hex1 As String) As String
   Dim strTemp As String, strReturn As String, i As Long
   Hex1 = Replace(Hex1, " ", "")
   If Len(Hex1) Mod 2 <> 0 Then Exit Function

   For i = 1 To Len(Hex1) Step 2
   strReturn = strReturn & Chr(Val("&H" & Mid(Hex1, i, 2)))
   Next i
   HexToStr = strReturn
End Function

Public Function MakeDWORD(Value As Long) As String
Dim Result As String * 4
CopyMemory ByVal Result, Value, 4
MakeDWORD = Result
End Function

Public Function MakeWORD(Value As Integer) As String
Dim Result As String * 2
CopyMemory ByVal Result, Value, 2
MakeWORD = Result
End Function

Public Function GetDWORD(data As String) As Long
Dim lReturn As Long
   Call CopyMemory(lReturn, ByVal data, 4)
   GetDWORD = lReturn
End Function

MAKE SURE YOU UNDERSTAND WHAT THIS CODE IS DOING!!!


Now, when you usually send packets to battle.net, they respond with the exact same packet ID. According to http://bnetdocs.valhallalegends.com/sequence.php, after sending 0x01, you send 0x50. 0x50 echos back with what I call hashing data. Hashing data is battle.nets attempt at trying to stop people from creating bots.

Basically, battle.net sends you a bunch of data. In the next packet (0x51), you must send the data back, and battle.net expects it to be passed through a complicated algorithm. There are two main ways to do this.

Use hashes. Hashes are the game files originally used by the actual clients to pass the data through the algorithm. Check out the free hashing library at http://bncsutil.ionws.com/

Or, use BNLS. BNLS is a 3rd party server located at bnls.valhallalegends.com on port 9367. You send the data to them, and they send the "mathimized" data back to you. A packet listing for BNLS is at http://bnetdocs.valhallalegends.com

Parsing recieved data in packets

When I first started bot programming this is the part I had the most trouble with.

The trick is to only get the parts you need. I dont know anything about doing this in other language than Visual Basic, so that's all I can tell you for non-VB'ers.

In Visual Basic, using the Mid function is the key. For instance lets say the BNCS packet contained one dword. You would use this

CODE
Dim GetData as Long
GetData = GetDword(Mid(PacketData, 5, 4))


The Mid function only takes data starting from the 5th byte 4 bytes over. Using the getdword function we can convert the raw string to a long (dword).


Hope this small tutorial gets you on your feet on the world of bot programming.

If you have any questions, dont hesitate to post in this thread!

----------------------------------
Good Links:

http://forum.valhallalegends.com [use good grammar here!]
http://bnetdocs.valhallalegends.com [contains login sequence, and all the packets you send]
Report, edit, etc...Posted by synd][cate on 2005-10-11 at 02:37:41
I haven't the time to read through this 100% but this is awesome.. This will help me write my code for the b.net game tracker that I was coding previously.

If I can get a bot coded that will record games created I can get 4 starcraft keys and connect and record data from all 4 b.net realms.
Report, edit, etc...Posted by LegacyWeapon on 2005-10-11 at 15:01:49
You can use 1 key to connect to all 4 realms.
Report, edit, etc...Posted by synd][cate on 2005-10-11 at 16:26:30
QUOTE(LegacyWeapon @ Oct 11 2005, 02:01 PM)
You can use 1 key to connect to all 4 realms.
[right][snapback]331487[/snapback][/right]


Are you sure? I thought it would say this cd-key is in use, although when I was testing it might have been the same server.
Report, edit, etc...Posted by yeow on 2005-10-11 at 16:35:20
He's correct. It would only be in use if its a key thats been circulated around in the public.
Report, edit, etc...Posted by CheeZe on 2005-10-22 at 17:50:31
CODE
try:
           self.sock.connect((self.SERVER, self.PORT))
           self.sock.send(struct.pack('l', socket.htonl(0x01)))

           self.sock.send(struct.pack('l', socket.htonl(0xFF)))
           self.sock.send(struct.pack('l', socket.htonl(0x50)))
           self.sock.send(struct.pack('l', socket.htons(0x003A)))
           
           self.DisplayMessage("Connected!", "green")
           
           #Send Login Packet
           try:
               self.sock.send(struct.pack('l', socket.htonl(0)))
               self.sock.send("68XI")
               self.sock.send("PXES")
               self.sock.send(struct.pack('l', socket.htonl(0xCD)))
               self.sock.send(struct.pack('l', socket.htonl(0)))
               self.sock.send(struct.pack('l', socket.htonl(0)))
               self.sock.send(struct.pack('l', socket.htonl(0)))
               self.sock.send(struct.pack('l', socket.htonl(0)))
               self.sock.send(struct.pack('l', socket.htonl(0)))                
               self.sock.send("USA" + "\0")
               self.sock.send("United States" + "\0")
               
           except:
               self.Disconnect()


I think I have everything they asked for. I checked the values being sent by SB and my Bot; I was off by a little. But I don't know what to change because I followed exactly what I should have done. (Or didn't and simply can't find the error)

Reference:
struct.pack('l', socket.htonl(0x01)) changes 0x01 into a DWord
Next Page (1)