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
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
bncs.py
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)
Quote
self.buffer.append(data + '.')
I don't know python, but that looks like it's putting a period there instead of a null character?
Quote from: UserLoser on July 07, 2006, 01:14 AM
Quote
self.buffer.append(data + '.')
I don't know python, but that looks like it's putting a period there instead of a null character?
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 from: Topaz on July 07, 2006, 01:14 AM
Quote from: UserLoser on July 07, 2006, 01:14 AM
Quote
self.buffer.append(data + '.')
I don't know python, but that looks like it's putting a period there instead of a null character?
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.
;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.
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.
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?
Well, what was the value of strBuffer[2:4]?
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 from: K on July 07, 2006, 09:02 PM
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.
Thanks, I was experimenting since I had a problem with ord().
Looks like you are using the default endian setting of the struct module, big. Try prefixing your unpack strings with '<'
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?
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.
Quote from: Topaz on July 09, 2006, 12:13 AM
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?
Perhaps you received SID_NULL?
Quote from: Topaz on July 09, 2006, 12:13 AM
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?
QuotePerhaps you received SID_NULL?
It shouldn't be the first packet I receive, though.
Who says?
Quote from: warz on July 10, 2006, 01:41 PM
Who says?
I should at least receive the header, anyway. I got a blank chunk of data.
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:
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
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]
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)