• Welcome to Valhalla Legends Archive.
 
Menu

Show posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.

Show posts Menu

Messages - ThePro

#1
Are there any tips on how to find the routine which build the packets?
I already found the routine which copies the data into the send() buffer  (By setting a breakpoint on send() and tracing up) but I need to know how this data is built.

The Problem is it seems I am stuck in an endless loop (Thats a network thread which waits for the data to be sent I guess)

Setting a breakpoint on the data which will be copied in the buffer doesen't help, since olly breaks all the time then when leaving step mode. (Dynamic allocation)
#2
Ah now I got it. :)
Sorry I'm a bit tired since I'm reversing for hours yet...

Let me give another try...

Are there vL members in one of the reversing teams? (Net, gfx, bnet.dll)
#3
Are The vL members in the reversing team?
#4
Are there vL members in the reversing team?
#5
After two years I decided to continue this project.

I'm using mercurial now, which is a very cool version control system.
The whole project will be open source and it will be available on bitbucket.org for everyone.

This is the wiki of the packets I reversed yet:
http://bitbucket.org/thepro/f1-2002-dedicated-server/wiki/packets

If you are good in reversing and/or python and are interested to help me feel free to contact me.
#6
Quote from: Hdx on July 20, 2009, 08:21 AM
Quote from: Hdx on July 20, 2009, 04:52 AM
Quoteff 50 36 00 .P6. - Header
00 00 00 00 .... - Protocol
36 38 58 49 68XI - Platform
52 41 54 53 RATS - Product
d3 00 00 00 .... - Verbyte
00 00 00 00 .... - Language
00 00 00 00 .... - Local IP
00 00 00 00 .... - Time Zone
00 00 00 00 .... - Locale ID
00 00 00 00 .... - Language Id
55 53 41 00 USA. - Country Abreviation
55 6e 69 74 65 64 20 53 74 61 74 65 73 00 United States. - Country
I can't understand you, speak a real language!
If you don't get it after this, I think im going to have to smack you!

This is okay, language can be 0x00.
#7
Currently I'm trying to write a python lib for handling the lockdown for Starcraft locally.
One problem is, that I couldn't find any documentation about the lockdown system, so I downloaded the c sourcecode of skull securtiy, where it is implementated.

When Checkrevision is called, it opens the dll file (lockdown-IX86-xx.dll) using LoadLibrary. On a linux machines this will not work, so am I fucked now, or is there still a way?
#8
Quote from: Yegg on July 07, 2009, 03:16 AM
The < states you want the result in little-endian. Before you used either, the struct module returns a value based on whichever endian your native system uses. Just curious, but what is your processor brand and its specs?
I know, thats the weird thing.

Processors:
Notbook: Intel T2250
First Server: Intel Pentium 4
Second Server: Intel Xeon 3060


All are working with the LittleEndian method. Dont ask me why...
#9
Quote from: Ringo on July 05, 2009, 11:36 PM
Quote from: ThePro on July 05, 2009, 07:14 PM
>>>  'ac80f2db821b8d6c4eb5d4f447026bfce1b8a904'
#Which is wrong. Is has to be DBF280AC6C8D1B82F4D4B54EFC6B024704A9B8E1

Looks fine, apart from each dword having reversed byte order.
Ahhh Thanks alot, Ringo!
I must be an idiot that I didn't see that!

The solution was just changing ONE! byte in the sourcecode:

#original
def _long2bytesBigEndian(n, blocksize=0):
...
s = pack('>I', n 0xffffffffL) + s
...

#changed
def _long2bytesBigEndian(n, blocksize=0):
...
s = pack('<I', n 0xffffffffL) + s
...


Now it works perfectly. :)
#10
Quote from: Hdx on July 05, 2009, 06:31 PM
Using those test values the final hash should be:
DF63B84BC7B60DAD0EFFBFD3094C6C376949CBE3
The first hash of the password should be:
DBF280AC6C8D1B82F4D4B54EFC6B024704A9B8E1

Can you confirm that the 1st hash is the same, if not it's the SHA1 at fault, if it is the same then look into pack (though that *should* work)

Nope, its not:

b1 = crypt.bsha(True) #broken=True -> bsha1
b1.update("funnypw")
b1.hexdigest()
>>>  'ac80f2db821b8d6c4eb5d4f447026bfce1b8a904'
#Which is wrong. Is has to be DBF280AC6C8D1B82F4D4B54EFC6B024704A9B8E1

b1 = crypt.bsha(False) #broken=False -> sha1
b1.update("funnypw")
b1.hexdigest()

>>> '20edff4fc4edcc25e85b3f4e867008fb8ef8dac2'
#Which is correct, as you can see here: http://www.tech-faq.com/sha-1-generator.shtml


It seems this lib doesen't work. Since aton was releasing the module I'm sure it was working for him, so thats the confisuing part o_O.
I'm using an intel 32Bit core dou processor, maybe he wrote this lib for a 64Bit processor!?

Here's the code of Atons pythonmodule I used:

import struct
import socket

def _long2bytesBigEndian(n, blocksize=0):
    """Convert a long integer to a byte string.

    If optional blocksize is given and greater than zero, pad the front
    of the byte string with binary zeros so that the length is a multiple
    of blocksize.
    """

    # After much testing, this algorithm was deemed to be the fastest.
    s = ''
    pack = struct.pack
    while n > 0:
        s = pack('>I', n & 0xffffffffL) + s
        n = n >> 32

    # Strip off leading zeros.
    for i in range(len(s)):
        if s[i] <> '\000':
            break
    else:
        # Only happens when n == 0.
        s = '\000'
        i = 0

    s = s[i:]

    # Add back some pad bytes. This could be done more efficiently
    # w.r.t. the de-padding being done above, but sigh...
    if blocksize > 0 and len(s) % blocksize:
        s = (blocksize - len(s) % blocksize) * '\000' + s

    return s


def _bytelist2longBigEndian(list):
    "Transform a list of characters into a list of longs."

    imax = len(list)/4
    hl = [0L] * imax

    j = 0
    i = 0
    while i < imax:
        b0 = long(ord(list[j])) << 24
        b1 = long(ord(list[j+1])) << 16
        b2 = long(ord(list[j+2])) << 8
        b3 = long(ord(list[j+3]))
        hl[i] = b0 | b1 | b2 | b3
        i = i+1
        j = j+4

    return hl


def _rotateLeft(x, n):
    "Rotate x (32 bit) left n bits circularly."

    return (x << n) | (x >> (32-n))

# ======================================================================
# The SHA transformation functions
#
# ======================================================================

def f0_19(B, C, D):
    return (B & C) | ((~ B) & D)

def f20_39(B, C, D):
    return B ^ C ^ D

def f40_59(B, C, D):
    return (B & C) | (B & D) | (C & D)

def f60_79(B, C, D):
    return B ^ C ^ D

f = [f0_19, f20_39, f40_59, f60_79]

# Constants to be used
K = [
    0x5A827999L, # ( 0 <= t <= 19)
    0x6ED9EBA1L, # (20 <= t <= 39)
    0x8F1BBCDCL, # (40 <= t <= 59)
    0xCA62C1D6L  # (60 <= t <= 79)
    ]

class bsha:
    "broken sha for blizzard stuff"

    digest_size = digestsize = 20

    def __init__(self, broken=True):
        "Initialisation."
        self.broken=broken
       
        # Initial message length in bits(!).
        #self.length = 0L
        self.count = [0, 0]

        # Initial empty message as a sequence of bytes (8 bit characters).
        self.input = []

        # Call a separate init function, that can be used repeatedly
        # to start from scratch on the same object.
        self.init()
       

    def init(self):
        "Initialize the message-digest and set all fields to zero."

        #self.length = 0L
        self.input = []

        # Initial 160 bit message digest (5 times 32 bit).
        self.H0 = 0x67452301L
        self.H1 = 0xEFCDAB89L
        self.H2 = 0x98BADCFEL
        self.H3 = 0x10325476L
        self.H4 = 0xC3D2E1F0L

    def _transform(self, W):
        if self.broken:
            for t in range(0, 16): W[t]=socket.htonl(W[t])
           
           
        for t in range(16, 80):
            if self.broken:
                W.append(_rotateLeft(1, (W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16])&31) & 0xffffffffL)
            else:
                W.append(_rotateLeft(W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16], 1) & 0xffffffffL)

        A = self.H0
        B = self.H1
        C = self.H2
        D = self.H3
        E = self.H4

        """
        This loop was unrolled to gain about 10% in speed
        for t in range(0, 80):
            TEMP = _rotateLeft(A, 5) + f[t/20] + E + W[t] + K[t/20]
            E = D
            D = C
            C = _rotateLeft(B, 30) & 0xffffffffL
            B = A
            A = TEMP & 0xffffffffL
        """

        for t in range(0, 20):
            TEMP = _rotateLeft(A, 5) + ((B & C) | ((~ B) & D)) + E + W[t] + K[0]
            E = D
            D = C
            C = _rotateLeft(B, 30) & 0xffffffffL
            B = A
            A = TEMP & 0xffffffffL

        for t in range(20, 40):
            TEMP = _rotateLeft(A, 5) + (B ^ C ^ D) + E + W[t] + K[1]
            E = D
            D = C
            C = _rotateLeft(B, 30) & 0xffffffffL
            B = A
            A = TEMP & 0xffffffffL

        for t in range(40, 60):
            TEMP = _rotateLeft(A, 5) + ((B & C) | (B & D) | (C & D)) + E + W[t] + K[2]
            E = D
            D = C
            C = _rotateLeft(B, 30) & 0xffffffffL
            B = A
            A = TEMP & 0xffffffffL

        for t in range(60, 80):
            TEMP = _rotateLeft(A, 5) + (B ^ C ^ D)  + E + W[t] + K[3]
            E = D
            D = C
            C = _rotateLeft(B, 30) & 0xffffffffL
            B = A
            A = TEMP & 0xffffffffL


        self.H0 = (self.H0 + A) & 0xffffffffL
        self.H1 = (self.H1 + B) & 0xffffffffL
        self.H2 = (self.H2 + C) & 0xffffffffL
        self.H3 = (self.H3 + D) & 0xffffffffL
        self.H4 = (self.H4 + E) & 0xffffffffL
   

    # Down from here all methods follow the Python Standard Library
    # API of the sha module.

    def update(self, inBuf):
        """Add to the current message.

        Update the md5 object with the string arg. Repeated calls
        are equivalent to a single call with the concatenation of all
        the arguments, i.e. m.update(a); m.update(b) is equivalent
        to m.update(a+b).

        The hash is immediately calculated for all full blocks. The final
        calculation is made in digest(). It will calculate 1-2 blocks,
        depending on how much padding we have to add. This allows us to
        keep an intermediate value for the hash, so that we only need to
        make minimal recalculation if we call update() to add more data
        to the hashed string.
        """

        leninBuf = long(len(inBuf))

        # Compute number of bytes mod 64.
        index = (self.count[1] >> 3) & 0x3FL

        # Update number of bits.
        self.count[1] = self.count[1] + (leninBuf << 3)
        if self.count[1] < (leninBuf << 3):
            self.count[0] = self.count[0] + 1
        self.count[0] = self.count[0] + (leninBuf >> 29)

        partLen = 64 - index

        if leninBuf >= partLen:
            self.input[index:] = list(inBuf[:partLen])
            self._transform(_bytelist2longBigEndian(self.input))
            i = partLen
            while i + 63 < leninBuf:
                self._transform(_bytelist2longBigEndian(list(inBuf[i:i+64])))
                i = i + 64
            else:
                self.input = list(inBuf[i:leninBuf])
        else:
            i = 0
            self.input = self.input + list(inBuf)


    def digest(self):
        """Terminate the message-digest computation and return digest.

        Return the digest of the strings passed to the update()
        method so far. This is a 20-byte string which may contain
        non-ASCII characters, including null bytes.
        """

        H0 = self.H0
        H1 = self.H1
        H2 = self.H2
        H3 = self.H3
        H4 = self.H4
        input = [] + self.input
        count = [] + self.count
       
        index = (self.count[1] >> 3) & 0x3fL
       
        if index < 56:
            padLen = 56 - index
        else:
            padLen = 120 - index

        if self.broken:
            padding = ['\000'] * 64
        else:
            padding = ['\200'] + ['\000'] * 63
       
        self.update(padding[:padLen])

       
        if self.broken:
            bits = _bytelist2longBigEndian(self.input[:56]) + [0L,0L]
        else:
            # Append length
            bits = _bytelist2longBigEndian(self.input[:56])+count
       
        self._transform(bits)

        # Store state in digest.
        digest = _long2bytesBigEndian(self.H0, 4) + \
                 _long2bytesBigEndian(self.H1, 4) + \
                 _long2bytesBigEndian(self.H2, 4) + \
                 _long2bytesBigEndian(self.H3, 4) + \
                 _long2bytesBigEndian(self.H4, 4)

        self.H0 = H0
        self.H1 = H1
        self.H2 = H2
        self.H3 = H3
        self.H4 = H4
        self.input = input
        self.count = count

        return digest


    def hexdigest(self):
        """Terminate and return digest in HEX form.

        Like digest() except the digest is returned as a string of
        length 32, containing only hexadecimal digits. This may be
        used to exchange the value safely in email or other non-
        binary environments.
        """
        return ''.join(['%02x' % ord(c) for c in self.digest()])




# ======================================================================
# Mimic Python top-level functions from standard library API
# for consistency with the md5 module of the standard library.
# ======================================================================

# These are mandatory variables in the module. They have constant values
# in the SHA standard.

digest_size = digestsize = 20
blocksize = 1
#11
Quote from: aton on June 30, 2009, 06:32 AM
my conversion of the bncsutil function:

import struct
import bsha1

def doubleHashPassword(password, clienttoken, servertoken):
    b1=bsha1.bsha()
    b1.update(password)
    b2=bsha1.bsha()
    b2.update(struct.pack("I", clienttoken)+struct.pack("I", servertoken)+b1.digest())
    return b2.digest()


works with the bsha1 module i posted here:

http://forum.valhallalegends.com/index.php?topic=17932.0

Hm, this doesn't work for me.
That what I've done:

import crypt
import struct

b1 = crypt.bsha()
b1.update(password.lower())
b2 = crypt.bsha()
b2.update(struct.pack("I", client_token) + struct.pack("I", server_token) + b1.digest()
password_hash = b2.digest()



I get a hash with a length of 20, but it is different from the hash of BNLS, so I always get an invalid password response from bnet.

My hash:
ClientToken: 45038296
ServerToken: 257613900
Password: funnypw

Hash from lib (which is wrong): 2bce05fd6ce14a7dc726b1a81fc9eb63a87c00dd
Aton, did you login successfully with that?
Do you get the same wrong hash?
If no, any Ideas what I made wrong?
#12
Quote from: Hdx
xeph, you're a moron, 'Local Hashing' can't be 'outdated'. Quit trying to get code from other people, and please stop posting.
I think he wanted to say, that local hashing isn't usual this days, because everyone uses BNLS/JBLS.

Quote from: xpeh
How nice, you'll be unable to log on with your game client..
This dont have to be a disadvantage. If you never plan to use a specific account with a blizzard client then it is a very good idea to choose a case sensitive password because only people hwo use 3rd party clients would be able to get your account by guessing your password, what increases the security alot.

Quote from: Hdx
If you have questions on a particular aspect rather then 10 different ones Tongue I'd be more then happy to answer.
Good to know, I will keep that in mind. :)
This are my first questions:

1. Arent you the guy hwo made this JBLS server?
2. If yes, will you add Warden support to JBLS?
3. What happend to JBLS.org, it seems to be down!?
#13
Quote from: xpeh on July 02, 2009, 09:19 AM
Hello.

Can you share your bot lib, or whole bot?
It's not stable right now, so I still have to fix some things but I will release the source when its done. ;)


Quote
You can watch any open source bot lib in language that you understand to get clear what you need to do. For example, BNCSUtil you mentioned.
LOL! I didn't know that BNCSutil is open source these days. When I looked a few years for it it wasn't. I found a c++ implementation which I will now convert to python.
When it's done I will realease the module here. ;)


Quote
Hashing cdkeys with external server is highly questional.
1. It can be easily made local. There were no changes in algorithm since ancient times.
2. Who knows what happens to CDKeys you send?


Yea, thats another reason. I heard that some guy of a JBLS server has stolen CD-Keys by logging the traffic.
#14
Hey there!

Recently I've written a battle.net bot in python which runs on my linux server 24/7.
At the moment I'm using BNLS to deal with the logon sequence. But since I want to be independet of the availability of the BNLS servers I want to do this stuff by hand as a second option.

I wrote my first battle.net bot in 2003(?) using C++ on a Windows machine. I remember that there was a library called BNCSUtil.dll which did some hashing stuff for me.

Since my new bot is running on a linux machine I am not able to use this .dll anymore so it seems I have to do write a python hashing module, which I would public when it's done of course.


Now my questions:

1. (DWORD) EXE Hash
What does the battle.net excactly wants here? I remember that you originaly had to put the CRC32 hash of the Starcraft.exe file in here, but since some patch it is some value of the memory right!?

2. (DWORD) CD-key's public value
What is that? What does this "decoded cd-key" stuff mean? How to decode?

3. (DWORD) [5] Hashed Key Data

BnetDocs say:
Quote
The data that should be hashed for 'Hashed Key Data' is:

   1. Client Token
   2. Server Token
   3. Key Product (from decoded CD key)
   4. Key Public (from decoded CD key)
   5. (DWORD) 0
   6. Key Private (from decoded CD key)

1. clear
2. clear
3. decded CD key? (Keyproduct = 0x01 for Starcraft?)
4./6. public,private? sounds like an asymmetric encryption


I know that the hasing is done by "broken sha-1".
I also know that the password is hashed twice but I don't know the right order.

it is like:

hashed_pw = bsha1(password)
double_hash = bsha1( ? + hashed_pw)
   
4. last but not least, how to deal with the MPQ-Version and the Formula?


#15
erm, I am serious. :)