• Welcome to Valhalla Legends Archive.
 

Rudimentary Warden information

Started by iago, February 28, 2008, 05:07 PM

Previous topic - Next topic
|

Ringo

Quote from: Hdx on March 30, 2008, 11:49 PM
HUmm, with new information come to light about your identity, I have conclude that you are not worth helping and that with all the information that is available, you do not have a reason to be confused.
If I didnt know better, I would say replaced is aka NeSucks trying to get his load/flood bot working again ::)

Quote from: Archangel. on March 31, 2008, 01:23 AM
you should be thanking Andy.
lol :)

Quote from: Andy on March 31, 2008, 01:53 AM
Find out where, and if it's in a loop, maybe throw in a DoEvents?
I can think of better ways of fixing a loop that never exits :)


replaced: As said above, try reading the documentation iago posted, to gain a better understanding of the process rather than just copying the code andy posted.

iago

And if you can't find what I posted in this thread (hint: the first post), try http://www.skullsecurity.org/wiki/index.php/Starcraft_Warden
This'll make an interesting test for broken AV:
QuoteX5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*


Archangel.

ahhh!!

I would suggest to make a new topic for supporting replaced?

this topic is just making longer and losing its original track.

Its not nice to start reading this topic then ending with this kind of questions :(.
aka: Archangel, i can't login into the account or request the password, weird problem.

replaced

Quote from: Ringo on March 31, 2008, 08:01 AM

If I didnt know better, I would say replaced is aka NeSucks trying to get his load/flood bot working again ::)


lol :)


I can think of better ways of fixing a loop that never exits :)


replaced: As said above, try reading the documentation iago posted, to gain a better understanding of the process rather than just copying the code andy posted.


haha to late newb, I already got warden working and its going to be implemented in my bot.

LISTEN UP PPL, ringo has cracked the sc key algorithm and i got keys from him  ;D
thx ringo now i got 9k working sc

ringo liked to scan the product 2 keys

I need warden in genocide so other ppl can load to, I just use war3 and d2 keys right now

Barabajagal

Have fun with that, but I don't think mass loading on VB6 with all that work (reading from files, decrypting a lot, etc...) is the smartest idea...

iago

Quote from: Andy on March 31, 2008, 01:40 PM
Have fun with that, but I don't think mass loading on VB6 with all that work (reading from files, decrypting a lot, etc...) is the smartest idea...
I think the bigger problem is that, as soon as somebody abuses bots again, Blizzard is going to update their Warden modules and everybody will be back to square one again.

That, or they'll actually ban users that don't return the proper checksum.

Either way, painful for bot makers. :)
This'll make an interesting test for broken AV:
QuoteX5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*


MysT_DooM

I thought after all this time nes, you wouldn't have to resort to something like this to get what you wanted done :\


vb6, something about that combination of numbers and letters is sexy

Ringo

Quote from: replaced on March 31, 2008, 12:12 PM
haha to late newb, I already got warden working and its going to be implemented in my bot.

LISTEN UP PPL, ringo has cracked the sc key algorithm and i got keys from him  ;D
thx ringo now i got 9k working sc

ringo liked to scan the product 2 keys

I need warden in genocide so other ppl can load to, I just use war3 and d2 keys right now
Thought it was you, lol :) you stick out like a saw thum.
If your talking about those cdkeys I posted (and then deleted) from the starcraft cdkey topic on this forum, everyone has them :-\
We all know how you really got most of your cdkeys, with ur back door'ed load bot.
Aside, no one cares.

Quote from: Andy on March 31, 2008, 01:40 PM
Have fun with that, but I don't think mass loading on VB6 with all that work (reading from files, decrypting a lot, etc...) is the smartest idea...
I dont think posting full vb6 code for it was a good idea, but thats just me. It just allows ppl to avoid the reall learning required :)
But your code looked very messy/slow, so your probly right :D
Theres little wrong with VB6, tbh. I got SHA1 to run 17% faster in vb6 than blizzards SHA1 runs in d2.

Quote from: iago on March 31, 2008, 01:45 PM
I think the bigger problem is that, as soon as somebody abuses bots again, Blizzard is going to update their Warden modules and everybody will be back to square one again.

That, or they'll actually ban users that don't return the proper checksum.

Either way, painful for bot makers. :)

Hopefully, im bored :D
I dont think many people have implemented this yet anyway, at least, not many public release's (afaik)
But if/when that time comes, im sure nes (Replaced) will get off on thinking he caused it. Im supprised they havent updated already.

Archangel.

I agree with Ringo.
Quote
I dont think posting full vb6 code for it was a good idea

I hope bnet will update warden module.
aka: Archangel, i can't login into the account or request the password, weird problem.

Barabajagal

Ya... maybe I'll just let full descriptions be the most that's given from now on.

replaced

LoL, I figured out that the code posted wasn't the problem -- my code was   :'(

herzog_zwei

It's been a long time since I've looked at Warden (maybe a year?) but other than the info that iago has given out, the rest of Warden can't be discussed as Warden itself.  Some of it may have changed since then but it should be pretty close.  The Warden engine is just an infrastructure that loads up the initial mini-programs into memory and executes queries specific to the modules loaded up.  In order to talk about the Warden modules, you have to indicate which module it is since all the parsing is done inside the module and not in the Warden engine itself.

Initially Warden didn't contain checksums in the 02 replies but I think it's standard in all the Warden modules now.  I believe all the Warden modules for all the games used to be the same but all compiled differently and have different hashes (WoW Warden modules used to contain the detection methods for D2 and even contained the D2Client.dll string).  Generally there are hundreds/thousands of modules for a given version of the Warden modules and each one is compiled differently and the 02 protocol for each is different.  The 02 format I've seen has always been 02 <cmd1> <cmd2> ... <cmdn>, though they can change it at any time.  It took a long time, but they eventually switched it up so cmd1 ... cmdn weren't always in the same order, and they eventually split it up so each packet won't always have all n commands (it'll be n1, n2, n3, etc commands per packet out of m total queries).  The number of queries I saw for SC was around 2-4.  In D2, there are probably around 10-20.  For WoW, there were probably over 20-30 when I last checked.

How the to encrypt/decrypt messages, perform hashes, calculate checksums, etc is irrelevant, as it is all described in the Warden module and can change at any time.  I've never seen them change the encryption/decryption routines but they've modified the other stuff in the past.  The other protocol commands (00, 01, etc) can be changed as well so you can't assume those will remain the same after loading the initial module.  That's the beauty of it and why it's tough for most people to figure out a safe, general solution to it, and it's also why it's a big security concern since it's running code that you generally won't be able to check (nor will file scanners detect any malicious code since the format isn't coded into the virus scanners).  From what I saw, they've implemented enough in the Warden engine that it'd be hard for a man in the middle attacker to inject their own malicious Warden module into your client.

In WoW, the session keys from both the server and the client are used to initialize the initial RC4 tables.

The SHA1 used for initializing the tables is standard.

Ribose

Alright, so I attempted to convert this all to C# (from iago's notes, mostly, which are in Java), and now it compiles and doesn't error, but it incorrectly decrypts it.
My first thought is that I converted the WardenRandom class wrong...

    /// <summary>This structure is used to create the decryption base for the simple encryption class.</summary>
    internal class WardenRandom
    {
        private int Position;
        private byte[] Data1;
        private byte[] Data2;
        private byte[] Data3;

        public WardenRandom(byte[] seed)
        {
            Data1 = new byte[0x14];
            Data2 = new byte[0x14];
            Data3 = new byte[0x14];

            int length1 = (int) seed.Length >> 1;   //2
            int length2 = seed.Length - length1;    //2

            byte[] seed1 = new byte[length1];
            byte[] seed2 = new byte[length2];

            for (int i = 0; i < length1; i++)
                seed1[i] = seed[i];
            for (int i = 0; i < length2; i++)
                seed2[i] = seed[i + length1];

            Data2 = new ByteFromIntArray(true).getByteArray(WardenSHA1.hash(seed1));

            Data3 = new ByteFromIntArray(true).getByteArray(WardenSHA1.hash(seed2));

            update();

            Position = 0;
        }

        private void update()
        {
            WardenSHA1 wsha1 = new WardenSHA1();
            wsha1.update(Data2);
            wsha1.update(Data1);
            wsha1.update(Data3);
            Data1 = new ByteFromIntArray(true).getByteArray(wsha1.digest());
        }

        public byte[] GetBytes(int a_iCount)
        {
            byte[] m_bBytes = new byte[a_iCount];
            for (int i = 0; i < a_iCount; i++)
                m_bBytes[i] = GetByte();
            return m_bBytes;
        }

        private byte GetByte()
        {
            int m_iPos = Position;
            byte m_bVal = Data1[m_iPos];
            m_iPos++;
            if (m_iPos >= 0x14)
            {
                m_iPos = 0;
                update();
            }
            Position = m_iPos;
            return m_bVal;
        }
    }

    /// <summary>This class hashes Warden style.</summary>
    internal class WardenSHA1
    {
        private int[] bitlen = new int[2];
        private int[] state = new int[0x15];

        public static int[] hash(byte[] data)
        {
            return WardenSHA1.hash(byteArrayToCharArray(data));
        }

        public static int[] hash(char[] data)
        {
            WardenSHA1 ctx = new WardenSHA1();
            ctx.update(data);
            return ctx.digest();
        }

        public static int[] hash(String data)
        {
            return WardenSHA1.hash(data.ToCharArray());
        }

        public WardenSHA1()
        {
            bitlen[0] = 0;
            bitlen[1] = 0;
            unchecked
            {
                state[0] = (int) 0x67452301;
                state[1] = (int) 0xEFCDAB89;
                state[2] = (int) 0x98BADCFE;
                state[3] = (int) 0x10325476;
                state[4] = (int) 0xC3D2E1F0;
            }
        }

        private static int reverseEndian(int i)
        {
            unchecked
            {
                i = ((int) (i << 24) & (int) 0xFF000000) |
                      ((int) (i << 8) & (int) 0x00FF0000) |
                      ((int) (i >> 8) & (int) 0x0000FF00) |
                      ((int) (i >> 24) & (int) 0x000000FF);
            }
            return i;
        }

        public int[] digest()
        {
            byte[] vars;
            int len;
            char[] MysteryBuffer;
            int[] temp_vars = new int[2];

            temp_vars[0] = WardenSHA1.reverseEndian(bitlen[1]);
            temp_vars[1] = WardenSHA1.reverseEndian(bitlen[0]);

            len = ((-9 - (bitlen[0] >> 3)) & 0x3F) + 1;

            vars = (new ByteFromIntArray(true)).getByteArray(temp_vars);
            MysteryBuffer = new char[len];

            MysteryBuffer[0] = (char) 0x80;
            for (int x = 1; x < len; x++)
                MysteryBuffer[x] = (char) 0;

            update(MysteryBuffer);
            update(byteArrayToCharArray(vars));

            int[] hash = new int[5];
            for (int x = 0; x < 5; x++)
                hash[x] = WardenSHA1.reverseEndian(state[x]);

            return hash;
        }

        public void update(byte[] data)
        {
            this.update(WardenSHA1.byteArrayToCharArray(data));
        }

        public void update(char[] data)
        {
            int a = 0, b = 0, c = 0, x = 0, len = data.Length;
            c = len >> 29;
            b = len << 3;

            a = (bitlen[0] / 8) & 0x3F;

            if (bitlen[0] + b < bitlen[0] || bitlen[0] + b < b)
                bitlen[1]++;
            bitlen[0] += b;
            bitlen[1] += c;

            len += a;
            x = -a;
            ByteFromIntArray bfia = new ByteFromIntArray(true);

            if (len >= 0x40)
            {
                if (a > 0)
                {
                    while (a < 0x40)
                    {
                        bfia.insertByte(state, a + 0x14, (byte) data[a + x]);
                        a++;
                    }
                    transform(state);
                    len -= 0x40;
                    x += 0x40;
                    a = 0;
                }
                if (len >= 0x40)
                {
                    b = len;
                    for (int i = 0; i < b / 0x40; i++)
                    {
                        for (int y = 0; y < 0x40; y++)
                            bfia.insertByte(state, y + 0x14, (byte) data[x + y]);
                        transform(state);
                        len -= 0x40;
                        x += 0x40;
                    }
                }
            }
            while (a < len)
            {
                bfia.insertByte(state, 20 + a, (byte) data[a + x]);
                a++;
            }
            return;
        }

        private static void transform(int[] hashBuffer)
        {
            int[] buf = new int[0x50];
            int dw, a, b, c, d, e, p, i;

            for (i = 5; i < hashBuffer.Length; i++)
                hashBuffer[i] = WardenSHA1.reverseEndian(hashBuffer[i]);

            for (i = 0; i < 0x10; i++)
                buf[i] = hashBuffer[i + 5];

            for (i = 0; i < 0x40; i++)
            {
                dw = buf[i + 13] ^ buf[i + 8] ^ buf[i + 0] ^ buf[i + 2];
                buf[i + 16] = (dw >> 0x1f) | (dw << 1);
            }

            a = hashBuffer[0];
            b = hashBuffer[1];
            c = hashBuffer[2];
            d = hashBuffer[3];
            e = hashBuffer[4];

            p = 0;

            i = 0x14;
            do
            {
                dw = ((a << 5) | (a >> 0x1b)) + ((~b & d) | (c & b)) + e + buf[p++] + 0x5a827999;
                e = d;
                d = c;
                c = (b >> 2) | (b << 0x1e);
                b = a;
                a = dw;
            }
            while (--i > 0);

            i = 0x14;
            do
            {
                dw = (d ^ c ^ b) + e + ((a << 5) | (a >> 0x1b)) + buf[p++] + 0x6ED9EBA1;
                e = d;
                d = c;
                c = (b >> 2) | (b << 0x1e);
                b = a;
                a = dw;
            }
            while (--i > 0);

            i = 0x14;
            do
            {
                dw = ((c & b) | (d & c) | (d & b)) + e + ((a << 5) | (a >> 0x1b)) + buf[p++] - 0x70E44324;
                e = d;
                d = c;
                c = (b >> 2) | (b << 0x1e);
                b = a;
                a = dw;
            }
            while (--i > 0);

            i = 0x14;
            do
            {
                dw = ((a << 5) | (a >> 0x1b)) + e + (d ^ c ^ b) + buf[p++] - 0x359D3E2A;
                e = d;
                d = c;
                c = (b >> 2) | (b << 0x1e);
                b = a;
                a = dw;
            }
            while (--i > 0);

            hashBuffer[0] += a;
            hashBuffer[1] += b;
            hashBuffer[2] += c;
            hashBuffer[3] += d;
            hashBuffer[4] += e;
        }

        public void pad(int amount)
        {
            char[] emptybuffer = new char[0x1000];
            for (int x = 0; x < 0x1000; x++)
                emptybuffer[x] = '\0';
            while (amount > 0x1000)
            {
                update(emptybuffer);
                amount -= 0x1000;
            }
            emptybuffer = new char[amount];
            for (int x = 0; x < amount; x++)
                emptybuffer[x] = '\0';
            update(emptybuffer);
        }

        public bool hash_file(String filename)
        {
            try
            {
                update(byteArrayToCharArray(File.ReadAllBytes(filename)));
            }
            catch (Exception e)
            {
                //System.out.println("lockdown_SHA1.hash_file(" + filename + ") Failed: " + e.toString());
                return false;
            }
            return true;
        }

        private static char[] byteArrayToCharArray(byte[] a)
        {
            char[] buff = new char[a.Length];
            for (int x = 0; x < a.Length; x++)
                buff[x] = (char) (a[x] & 0x000000FF);
            return buff;
        }
    }

    /// <summary></summary>
    internal class ByteFromIntArray
    {
        /*
         * iago
         * ByteFromIntArray.java
         * Created on May 21, 2004, 11:39 AM
         * This is a class to take care of treating an array of ints like a an array of bytes.
         * Note that this always works in Little Endian
         */
        private bool littleEndian;

        public static ByteFromIntArray LITTLEENDIAN = new ByteFromIntArray(true);
        public static ByteFromIntArray BIGENDIAN = new ByteFromIntArray(false);

        public ByteFromIntArray(bool littleEndian)
        {
            this.littleEndian = littleEndian;
        }

        public byte getByte(int[] array, int location)
        {
            if ((location / 4) >= array.Length)
                throw new ArgumentOutOfRangeException("location = " + location + ", number of bytes = " + (array.Length * 4));

            int theInt = location / 4; // rounded
            int theByte = location % 4; // remainder


            // reverse the byte to simulate little endian
            if (littleEndian)
                theByte = 3 - theByte;

            // I was worried about sign-extension here, but then I realized that they are being
            // put into a byte anyway so it wouldn't matter.
            if (theByte == 0)
                return (byte) ((array[theInt] & 0x000000FF) >> 0);
            else if (theByte == 1)
                return (byte) ((array[theInt] & 0x0000FF00) >> 8);
            else if (theByte == 2)
                return (byte) ((array[theInt] & 0x00FF0000) >> 16);
            else if (theByte == 3)
                return (byte) ((array[theInt] & 0xFF000000) >> 24);

            return 0;
        }


        /** This function is used to insert the byte into a specified spot in
         * an int array.  This is used to simulate pointers used in C++.
         * Note that this works in little endian only.
         * @param intBuffer The buffer to insert the int into.
         * @param b The byte we're inserting.
         * @param location The location (which byte) we're inserting it into.
         * @return The new array - this is returned for convenience only.
         */
        public int[] insertByte(int[] intBuffer, int location, byte b)
        {
            // Get the location in the array and in the int
            int theInt = location / 4;
            int theByte = location % 4;

            // If we're using little endian reverse the hex position
            if (littleEndian == false)
                theByte = 3 - theByte;

            int replaceInt = intBuffer[theInt];

            // Creating a new variable here because b is a byte and I need an int
            int newByte = b << (8 * theByte);
            unchecked
            {
                switch (theByte)
                {
                    case 0: replaceInt &= (int) 0xFFFFFF00; break;
                    case 1: replaceInt &= (int) 0xFFFF00FF; break;
                    case 2: replaceInt &= (int) 0xFF00FFFF; break;
                    case 3: replaceInt &= (int) 0x00FFFFFF; break;
                }
            }

            replaceInt = replaceInt | newByte;

            intBuffer[theInt] = replaceInt;

            return intBuffer;
        }


        public byte[] getByteArray(int[] array)
        {
            byte[] newArray = new byte[array.Length * 4];

            int pos = 0;
            for (int i = 0; i < array.Length; i++)
            {
                if (littleEndian)
                {
                    newArray[pos++] = (byte) ((array[i] >> 0) & 0xFF);
                    newArray[pos++] = (byte) ((array[i] >> 8) & 0xFF);
                    newArray[pos++] = (byte) ((array[i] >> 16) & 0xFF);
                    newArray[pos++] = (byte) ((array[i] >> 24) & 0xFF);
                }
                else
                {
                    newArray[pos++] = (byte) ((array[i] >> 24) & 0xFF);
                    newArray[pos++] = (byte) ((array[i] >> 16) & 0xFF);
                    newArray[pos++] = (byte) ((array[i] >> 8) & 0xFF);
                    newArray[pos++] = (byte) ((array[i] >> 0) & 0xFF);
                }
            }

            return newArray;
        }

        public byte[] getByteArray(int integer)
        {
            int[] temp = new int[1];
            temp[0] = integer;
            return getByteArray(temp);
        }
    }

How my implementation works is it creates a new WardenModule(byte[4] { keyhash[0], keyhash[1], keyhash[2], keyhash[3] });
constructor:

        public WardenModule(byte[] seed)
        {
            g_wrRandom = new WardenRandom(seed);
            decryptor = new SimpleCrypto(g_wrRandom.GetBytes(0x10));
            encryptor = new SimpleCrypto(g_wrRandom.GetBytes(0x10));
        }


It then takes the byte[] recieved in 0x5e, and decrypts it, but that doesn't work. :/
[6:45:39 PM] [WARDEN] Received SID_WARDEN!
0000   ff 5e 29 00 d3 a3 92 d4  72 66 98 c1 7b 42 3e ce    ÿ^).Ó£.Ôrf.Á{B>Î
0010   9a 08 33 44 f7 3a 53 a3  dd a7 35 2c d4 9f 8f b1    ..3D÷:S£Ý§5,Ô..±
0020   2b 38 b8 5a 19 1a 59 dc  95                          +8¸Z..YÜ.
[6:45:40 PM] [WARDEN] Decrypted:
0000   ca 04 ff f2 bd 20 44 66  9c 64 fa 58 50 8f bc a5    Ê.ÿò. Df.dúXP..¥
0010   93 a9 26 1e 8d 81 c7 5c  1a 76 d9 89 0e cc c3 bd    .©&...Ç\.vÙ..ÌÃ.
0020   01 72 0a 1d 38                                      .r..8
[6:45:40 PM] [WARDEN] Unknown Warden request.

(it doesn't recognize the first byte as 0x00 (or 1 or 2), because it isn't.)
~Ribose

UserLoser

think your "encryptor" and "decryptor" are backwards.  You're supposed to generate the outgoing key first ("encryptor") then the incoming key ("decryptor")

unless you were using "encryptor" to decrypt your incoming challenge

Ribose


        /// <summary>The SimpleCrypto used to decrypt (key in) packets.</summary>
        private SimpleCrypto decryptor;
        /// <summary>The SimpleCrypto used to encrypt (key out) packets.</summary>
        private SimpleCrypto encryptor;
        public WardenModule(byte[] seed)
        {
            g_wrRandom = new WardenRandom(seed);
            encryptor = new SimpleCrypto(g_wrRandom.GetBytes(0x10));
            decryptor = new SimpleCrypto(g_wrRandom.GetBytes(0x10));
        }
Nope, you're right; I had them backwards. It still doesn't decrypt it right. :(
~Ribose

|