Valhalla Legends Forums Archive | General Programming | [Python] Battle.net Login

AuthorMessageTime
Topaz
I haven't been able to get the below to properly login. I am ipbanned immediately - I have compared packetlogs, and it should have worked. If you know or see what I did wrong, please post.

main.py
[code]iimport socket
import struct
import bncs

#connect
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(('europe.battle.net', 6112))
sock.send(chr(1))

#initialize parser and packetbuilder
pbuilder = bncs.builder(sock)
pbuilder.Send0x50()
parser = bncs.parser(sock)

#loop that checks for data
strBuffer = ''
buflen = 0

while 1:
    strTemp = sock.recv(1024)
    recvlen = len(strTemp)

    strBuffer += strTemp
    buflen += recvlen

    while  (buflen >= 4):
        lngLen, = struct.unpack('<b', strBuffer[2:3])
        if (buflen < lngLen): break
        parser.parsePacket(strBuffer[:lngLen])
        strBuffer = strBuffer[lngLen:]
        buflen -= lngLen
[/code]

bncs.py
[code]import struct
import socket

class packetbuffer:
   
    def __init__(self, sock):
        self.buffer = []
        self.sock = sock
       
    def insertData(self, data):
        self.buffer.append(data)

    def insertString(self, data):
        self.buffer.append(data)

    def insertNTString(self, data):
        self.buffer.append(data + chr(0))

    def insertDWORD(self, data):
        data = self.makeDWORD(data)
        self.buffer.append(data)

    def makeDWORD(self, data):
        return struct.pack('I', data)

    def makeWORD(self, data):
        return struct.pack('H', data)

    def getDWORD(self, data):
        return struct.unpack('<I', data)

    def getWORD(self, data):
        return struct.unpack('<H', data)
       
    def sendPacket(self, packetID):

        tmp = ''
        for i in self.buffer: tmp += i

        packetlen = self.makeWORD(len(tmp) + 4)

        header = chr(0xff) + chr(packetID) + packetlen

        self.sock.send(header + tmp)
       
        self.clear()
       
    def clear(self):
        self.buffer = list()

class builder:

    def __init__(self, sock):
        self.sock = sock
       
    def Send0x50(self):
        pbuffer = packetbuffer(self.sock)
       
        pbuffer.insertDWORD(0x00)
        pbuffer.insertString('68XIRATS')
        pbuffer.insertDWORD(0xCB)
        pbuffer.insertDWORD(0x00)
        pbuffer.insertDWORD(0x00)
        pbuffer.insertDWORD(0x00)
        pbuffer.insertDWORD(0x00)
        pbuffer.insertDWORD(0x00)
        pbuffer.insertNTString('USA')
        pbuffer.insertNTString('United States')
        pbuffer.sendPacket(0x50)

class parser:
    def __init__(self, sock):
        self.sock = sock

    def parsePacket(self, data):     
        packetID = ord(data[1:2])

        print 'data: ' + data
       
        print 'Packet received: %s' %hex(packetID)[/code]
July 7, 2006, 1:01 AM
UserLoser
[quote]
self.buffer.append(data + '.')
[/quote]

I don't know python, but that looks like it's putting a period there instead of a null character?
July 7, 2006, 6:14 AM
Topaz
[quote author=UserLoser link=topic=15351.msg155426#msg155426 date=1152252856]
[quote]
self.buffer.append(data + '.')
[/quote]

I don't know python, but that looks like it's putting a period there instead of a null character?
[/quote]

They amount to the same thing.

EDIT:

You're right! Thanks a lot, UserLoser.

Note to self: never listen to Yegg when he gives Python advice.
July 7, 2006, 6:14 AM
Yegg
[quote author=Topaz link=topic=15351.msg155427#msg155427 date=1152252887]
[quote author=UserLoser link=topic=15351.msg155426#msg155426 date=1152252856]
[quote]
self.buffer.append(data + '.')
[/quote]

I don't know python, but that looks like it's putting a period there instead of a null character?
[/quote]

They amount to the same thing.

EDIT:

You're right! Thanks a lot, UserLoser.

Note to self: never listen to Yegg when he gives Python advice.
[/quote]

;D

That's not Python specific, the issue with the period. I figured it would work just fine. I decided to test it in Scheme by creating a binary file and reading it with a hex editor. I wrote "hello world" + "." to one file, and "hello world" + a null character (Python would display as '\x00'). The first file, using the period, shows a 0x2E representing the hex of the period. The second file shows the hex as 0x00 which is what we want.

I guess period and null character do have their differences  :).

Btw, did you do a faulty job comparing packet logs? I would have observed each byte in each packet.
July 7, 2006, 11:45 AM
K
Most hex editors use periods to display non-printable characters or characters that would screw up the formatting, since printing a tab, newline, or null in the middle of a string you are trying to align nicely will cause problems.    you can use isprint() in C/C++ to determine whether or not a character is printable, though it might not catch all of them.
July 7, 2006, 4:49 PM
Topaz
Got another weird issue

After unpacking the packet length ('lngLen, = struct.unpack('H', strBuffer[2:4])') and comparing it to the length of the buffer, I get two wildly different numbers that make parsing data impossible:

lngLen = 25168
strBuffer.Length = 99

Does anyone know why it would do this?
July 8, 2006, 12:15 AM
Yegg
Well, what was the value of strBuffer[2:4]?
July 8, 2006, 1:51 AM
K
Shouldn't it be 2:3, not 2:4?

Edit: perhaps that what you meant, as it is written as such in your first post.
July 8, 2006, 2:02 AM
Topaz
[quote author=K link=topic=15351.msg155467#msg155467 date=1152324122]
Shouldn't it be 2:3, not 2:4?

Edit: perhaps that what you meant, as it is written as such in your first post.
[/quote]

Thanks, I was experimenting since I had a problem with ord().
July 8, 2006, 2:06 AM
St0rm.iD
Looks like you are using the default endian setting of the struct module, big. Try prefixing your unpack strings with '<'
July 8, 2006, 4:15 AM
Topaz
Packet received:
Packet received: 5Š‡ÿPc
Packet received: ÙrD% ÅIX86ver6.mpq A=699062515 B=955808502 C=857775189 4 A=A-S B=B

Not sure why the parser passed a blank string, can anyone see why?
July 9, 2006, 5:13 AM
Topaz
After mulling over it for a bit, I looked at how warz did it with his moderation client. The new way of handling socket data is more or less the same way he did it, but oh well. The alpha post has been updated with the revised code.
July 10, 2006, 8:11 AM
UserLoser
[quote author=Topaz link=topic=15351.msg155511#msg155511 date=1152422009]
Packet received:
Packet received: 5Š‡ÿPc
Packet received: ÙrD% ÅIX86ver6.mpq A=699062515 B=955808502 C=857775189 4 A=A-S B=B

Not sure why the parser passed a blank string, can anyone see why?
[/quote]

Perhaps you received SID_NULL?
July 10, 2006, 4:45 PM
Topaz
[quote author=Topaz link=topic=15351.msg155511#msg155511 date=1152422009]
Packet received:
Packet received: 5Š‡ÿPc
Packet received: ÙrD% ÅIX86ver6.mpq A=699062515 B=955808502 C=857775189 4 A=A-S B=B

Not sure why the parser passed a blank string, can anyone see why?
[/quote]

[quote]Perhaps you received SID_NULL?
[/quote]

It shouldn't be the first packet I receive, though.
July 10, 2006, 6:34 PM
warz
Who says?
July 10, 2006, 6:41 PM
Topaz
[quote author=warz link=topic=15351.msg155570#msg155570 date=1152556905]
Who says?
[/quote]

I should at least receive the header, anyway. I got a blank chunk of data.
July 10, 2006, 7:11 PM
Topaz
Alright, I've gotten as far as to send SID_AUTH_CHECK, but it fails - the error is 'wrong product'. The cdkey is valid for the product, so I _think_ it's something I did wrong in getting the public/private values:

[code]
main.py

import socket
import struct
import bncs

#connect
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(('useast.battle.net', 6112))
sock.send(chr(1))

#initialize parser and packetbuilder
pbuilder = bncs.builder(sock)
pbuilder.Send0x50()
handler = bncs.handler(sock)

#loop that checks for data
strBuffer = ''
buflen = 0

while 1:
    strTemp = sock.recv(1024)
    recvlen = len(strTemp)

    strBuffer += strTemp
    buflen += recvlen

    while (buflen >= 4):
        lngLen, = struct.unpack('<b', strBuffer[2:3])
        if (buflen < lngLen): break
        handler.parsePacket(strBuffer[:lngLen])
        print strBuffer[:lngLen]
        strBuffer = strBuffer[lngLen:]
        buflen -= lngLen[/code]

[code]
pybncsutil

from ctypes import *
from struct import *

platformDict = { 'NIX':0x01, 'WIN32':0x01, 'PMAC':0x02, 'XMAC':0x03 }
#checkrevision functions

bncsutil = windll.LoadLibrary('./bncsutil.dll')

def extractMPQNumber(mpqName):
    return bncsutil.extractMPQNumber(mpqName)

def checkRevision(formula, hashFiles, mpqNumber):
    #Make sure hashFiles is a list - if it isn't, fail.
    if len(hashFiles) <> 3: return 0

    checkSum = create_string_buffer('\000' * 10) #mutable memory block
       
    if (bncsutil.checkRevisionFlat(formula, hashFiles[0],
            hashFiles[1], hashFiles[2], mpqNumber, checkSum) == True):
        #unpack the checksum, return first value of tuple
        return unpack('L', checkSum.value)[0]
    else:
        #failed
        return 0
       
def getExeInfo(exe):
    size = 256
    infoString = create_string_buffer(256)
    version = create_string_buffer('\000' * 10)
       
    result = bncsutil.getExeInfo(exe, infoString,
                            size, version, platformDict['WIN32'])

    while result > size:
        if size > 1024:
            return 0
        else:
            size = size + 256
            infoString = create_string_buffer(size)
           
            result = bncsutil.getExeInfo(exe, infoString,
                            size, version, platformDict['WIN32'])

    version = unpack('L', version.value)[0] #unpack version
       
    return [version, infoString.value] #return version and info as a list

#ols functions

def doubleHashPassword( password, cToken, sToken):
    dHash = create_string_buffer(20)
    bncsutil.doubleHashPassword(password, cToken, sToken, dHash)

    return dHash.value

def hashPassword(password):
    pHash = create_string_buffer(20)
    bncsutil.hashPassword(password, pHash)

    return pHash.value

#sha1 function

def calcHashBuf(data):
        dataLen = len(data)
        dHash = create_string_buffer(20)

        bncsutil.calcHashBuf(data, dataLen, dHash)

        return dHash.value

#key decoding

def decodeCDKey(cdkey):
    cdkey = create_string_buffer(cdkey)
    #decoder = self.bncsutil.kd_create(byref(cdkey), 13)
       
    #return decoder
    return cdkey

def quickDecode(cdkey, cToken, sToken):
    pdtValue = create_string_buffer('\000' * 20)
    pbValue = create_string_buffer('\000' * 20)
    keyHash = create_string_buffer(20)

    if bncsutil.kd_quick(cdkey, cToken, sToken,
                          pbValue, pdtValue, keyHash, 20) == 0:
        return 0 #failed to decode cdkey
    else:
        #returns the product value, private value, and key hash
        return [pbValue.value, pdtValue.value, keyHash.value][/code]

[code]
bncs.py

import struct
import random
import pybncsutil

pidDict = { 0x00:'SID_NULL', 0x05:'SID_CLIENTID', 0x0A:'SID_ENTERCHAT',
            0x0B:'SID_GETCHANNEL', 0x0C:'SID_JOINCHANNEL', 0x0F:'SID_CHATCOMMAND',
            0x10:'SID_LEAVECHAT', 0x19:'SID_MESSAGEBOX', 0x1E:'SID_CLIENTID2',
            0x25:'SID_PING' }

class sendbuffer:
   
    def __init__(self, sock):
        self.buffer = []
        self.sock = sock
       
    def insertData(self, data):
        self.buffer.append(data)

    def insertString(self, data):
        self.buffer.append(data)

    def insertNTString(self, data):
        self.buffer.append(data + chr(0))

    def insertDWORD(self, data):
        data = self.makeDWORD(data)
        self.buffer.append(data)

    def makeDWORD(self, data):
        return struct.pack('I', data)

    def makeWORD(self, data):
        return struct.pack('H', data)
   
    def sendPacket(self, packetID):
        tmp = ''
        for i in self.buffer: tmp = tmp + i
       
        packetlen = self.makeWORD(len(tmp) + 4)
        header = chr(0xff) + chr(packetID) + packetlen

        self.sock.send(header + tmp)
        self.clear()
       
    def clear(self):
        del self.buffer[:]
        self.buffer[:] = []

class recvbuffer:
    def __init__(self, rawbuffer):
        self.rawbuffer = rawbuffer
        self.position = 0

    def resetPosition(self):
        self.position = 0
       
    def getDWORD(self, data):
        return struct.unpack('<I', data)

    def getWORD(self, data):
        return struct.unpack('<H', data)

    def increment(self):
        self.position = self.position + 1

    def jump(self, bytes):
        self.position += bytes

    def back(self, bytes):
        self.position -= bytes

    def setpos(self, position):
        self.position = position

    def nextBYTE(self):
        param = self.rawbuffer[self.position:self.position + 1]
        byte = struct.unpack('b', param)[0]

        self.increment()
        return byte

    def nextString(self):
        pos = self.rawbuffer.find(chr(0), self.position)
        string = self.rawbuffer[self.position:pos]
       
        self.setpos(pos + 1)
       
        return string
   
    def nextDWORD(self):
        param = self.rawbuffer[self.position:self.position + 4]
        dword = struct.unpack('I', param)[0]
       
        self.jump(4)

        return dword

    def nextWORD(self):
        param = self.rawbuffer[self.position:self.position + 2]
       
        word = struct.unpack('H', param)[0]
        self.jump(2)

        return word

class builder:

    def __init__(self, sock):
        self.sock = sock
        self.pbuffer = sendbuffer(self.sock)
       
    def Send0x50(self):
        self.pbuffer.insertDWORD(0x00)
        self.pbuffer.insertString('68XIPXES')
        self.pbuffer.insertDWORD(0xCD)
        self.pbuffer.insertDWORD(0x00)
        self.pbuffer.insertDWORD(0x00)
        self.pbuffer.insertDWORD(0x00)
        self.pbuffer.insertDWORD(0x00)
        self.pbuffer.insertDWORD(0x00)
        self.pbuffer.insertNTString('USA')
        self.pbuffer.insertNTString('United States')
        self.pbuffer.sendPacket(0x50)

    def Send0x51(self, sToken, mpqName, formula):
        #checkrevision stuff
        cToken = random.randint(10000000, 99999999)
        exeInfo = pybncsutil.getExeInfo('./STAR/starcraft.exe')
        mpqNumber = pybncsutil.extractMPQNumber(mpqName)
        hashFiles = ['./STAR/starcraft.exe', './STAR/storm.dll', './STAR/battle.snp']
        checksum = pybncsutil.checkRevision(formula, hashFiles, mpqNumber)
        #key stuff
        keyData = pybncsutil.quickDecode('############', cToken, sToken)

        print keyData
               
        self.pbuffer.insertDWORD(cToken)        # client token
        self.pbuffer.insertDWORD(exeInfo[0])    # exe version
        self.pbuffer.insertDWORD(checksum)      # exe hash

        self.pbuffer.insertDWORD(1)            # number of keys
        self.pbuffer.insertDWORD(0)            # 0/1 using spawn (false/true)

        self.pbuffer.insertDWORD(13)            # key length
        self.pbuffer.insertData(keyData[0])    # public value
        self.pbuffer.insertData(keyData[1])    # product value
        self.pbuffer.insertDWORD(0)            # unknown value
        self.pbuffer.insertString(keyData[2])  # key hash

        self.pbuffer.insertNTString(exeInfo[1]) # exe info
        self.pbuffer.insertNTString('piebot')  # cdkey owner name

        self.pbuffer.sendPacket(0x51)          # finally done!

class handler:
    def __init__(self, sock):
        self.sock = sock

    def parsePacket(self, data):
        self.rbuffer = recvbuffer(data)

        if self.rbuffer.nextBYTE() <> -1: return
       
        packetID = self.rbuffer.nextBYTE()

        print 'Packet received: %s' %hex(packetID)

        if packetID == 0x50:
            self.parse0x50(data)
        elif packetID == 0x25:
            self.parse0x25(data)
        elif packetID == 0x51:
            self.parse0x51(data)

    def parse0x25(self, data):
        pbuilder = builder(self.sock)
   
    def parse0x50(self, data):
        pbuilder = builder(self.sock)
       
        logonType = self.rbuffer.nextDWORD()
        sToken = self.rbuffer.nextDWORD()
        self.rbuffer.jump(14)
        mpqName = self.rbuffer.nextString()
        formula = self.rbuffer.nextString()

        pbuilder.Send0x51(sToken, mpqName, formula)

    def parse0x51(self, data):
        result = self.rbuffer.nextDWORD()

        print 'result: %s' %hex(result)[/code]
July 16, 2006, 6:45 AM

Search