Valhalla Legends Forums Archive | Battle.net Bot Development | [RESOLVED] W/LD SHA1 please help!

AuthorMessageTime
Ringo
So, I found a broken SHA function on this forum years ago, and optimized it (tryed to). so not sure who takes credits for the orginal vb6 port of broken SHA1, but thanks to them!
Also thanks to iago/rob and warz for releaseing warden/lockdown SHA1 code!
Lately i have broken it up and rewrote alot of it, in an attempt to support the warden and lockdown version of broken SHA1, but im having no luck!
It works with normal BNCS cdkey/password hashing just fine, but when i try to add the mystery padding and 64bit int, it seems to return a hash that doesnt match up to iago's test hash's
Im pretty sure my 64bit int math is bad. (maths has never been my strong point)
But could also be the mystery buffer. :(
Below is my code, hopefully somone can point out where im going wrong, and/or explain how the mystery padding and 64bit int takes effect, since that big int has me confused the most.

thanks in advance!

Oh and just a side note, I struggle to read C, so if you brake up an example in a native language (non-vb), please explain it step by step, thanks.

[code]
Public Function BSHA1(ByVal S As String, _
                      Optional ByVal bRE As Boolean = False, _
                      Optional ByVal bPad As Boolean = False) As String
   
    'bRE = Reverse Endian
    'bPad = lockdown/warden padding (mystery buffer)
   
    Dim LW    As Long 'low word
    Dim HW    As Long 'high word
    Dim B(21) As Long 'hash buffer
   
    '//Init the seeds
    B(0) = &H67452301
    B(1) = &HEFCDAB89
    B(2) = &H98BADCFE
    B(3) = &H10325476
    B(4) = &HC3D2E1F0
   
    '//Update the string buffer (to be hashed)
    Call SHA1Update(bRE, LW, HW, B(), S)
   
    '//If padding (lockdown/warden version)
    If bPad Then
        '//init the mystery buffer
        S = String(64, 0)
        Mid(S, 1, 1) = Chr(&H80)
       
        '//Switch the low/high words
        Dim LW2 As Long
        Dim HW2 As Long
        Call rEndian(HW, LW2)
        Call rEndian(LW, HW2)
       
        '//Update mystery buffer to the hash buffer (B)
        Call SHA1Update(bRE, LW, HW, B(), S)
       
        '//Update the 64bit int to the hash buffer (B)
        S = MakeDWORD(LW2) & MakeDWORD(HW2)
        Call SHA1Update(bRE, LW, HW, B(), S)
    End If
   
    '//Return the broken SHA1 hash
    BSHA1 = String(20, 0)
    Call CopyMemory(ByVal BSHA1, B(0), 20)
End Function


Private Sub SHA1Update(ByVal bRE As Boolean, _
                       ByRef LW As Long, _
                       ByRef HW As Long, _
                       ByRef B() As Long, _
                       ByVal S As String)
    Dim i As Long
    If ((Len(S) Mod 64) <> 0) Then
        '//buffer the string so its divisible by 64 (0x40)
        S = S & String(64 - (Len(S) Mod 64), 0)
    End If
    For i = 1 To Len(S) Step 64
        '//do the thingy ding with the low/high word
        LW = LW + RS(64, 29)
        HW = HW + LS(64, 3)
        '//copy chunk of the string into the long array to be hashed
        Call CopyMemory(B(5), ByVal Mid$(S, i, 64), 64)
        '//transform
        Call SHA1Transform(bRE, B)
    Next i
End Sub


Private Sub SHA1Transform(ByVal bRE As Boolean, ByRef p() As Long)
    Dim hB(80) As Long
    Dim A      As Long
    Dim B      As Long
    Dim C      As Long
    Dim D      As Long
    Dim E      As Long
    Dim G      As Long
    Dim i      As Long
    If bRE Then 'reverse endian
        For i = 0 To 15: Call rEndian(p(i + 5), hB(i)): Next i
    Else
        For i = 0 To 15: hB(i) = p(i + 5): Next i
    End If
    For i = 16 To 79
       hB(i) = LSC(1, (hB(i - 16) Xor hB(i - 8) Xor hB(i - 14) Xor hB(i - 3)) And 31)
    Next
    A = p(0)
    B = p(1)
    C = p(2)
    D = p(3)
    E = p(4)
    For i = 0 To 19
        G = Add(Add(Add(Add(hB(i), E), LSC(A, 5)), ((B And C) Or ((Not B) And D))), &H5A827999)
        E = D: D = C: C = LSC(B, 30): B = A: A = G
    Next i
    For i = 20 To 39
        G = Add(Add(Add(Add(hB(i), E), LSC(A, 5)), (D Xor C Xor B)), &H6ED9EBA1)
        E = D: D = C: C = LSC(B, 30): B = A: A = G
    Next i
    For i = 40 To 59
        G = Add(Add(Add(Add(hB(i), E), LSC(A, 5)), (C And B) Or (D And C) Or (D And B)), &H8F1BBCDC)
        E = D: D = C: C = LSC(B, 30): B = A: A = G
    Next i
    For i = 60 To 79
        G = Add(Add(Add(Add(hB(i), E), LSC(A, 5)), (D Xor C Xor B)), &HCA62C1D6)
        E = D: D = C: C = LSC(B, 30): B = A: A = G
    Next i
    p(0) = Add(p(0), A)
    p(1) = Add(p(1), B)
    p(2) = Add(p(2), C)
    p(3) = Add(p(3), D)
    p(4) = Add(p(4), E)
End Sub



' ~~~~~~ Extra functions ~~~~~~
Private Sub rEndian(ByVal V As Long, ByRef N As Long)
    '//dirty byte order switching
    Dim S As String * 4
    Call CopyMemory(ByVal S, V, 4)
    S = StrReverse(S)
    Call CopyMemory(N, ByVal S, 4)
End Sub
Private Function LSC(ByVal N As Long, ByVal S As Long) As Long
    'left shift circle
    LSC = (LS(N, S) Or RS(N, (32 - S)))
End Function
Private Function RS(ByVal N As Long, ByVal S As Long) As Long
    'right shift bits
    If (S < 0) Or (S > 31) Then
        RS = 0
    ElseIf (S = 0) Then
        RS = N
    Else
       If ((N And &H80000000) = &H80000000) Then
           RS = ((N And &H7FFFFFFF) \ (2 ^ S)) Or (2 ^ (31 - S))
       Else
           RS = Int(CDbl(N) / CDbl(2 ^ S))
       End If
    End If
End Function
Private Function LS(ByVal N As Long, ByVal S As Long) As Long
    'left shift bits
    If (S < 0) Or (S > 31) Then
        LS = 0
    ElseIf S = 0 Then
        LS = N
    Else
        N = N And (2 ^ (32 - S) - 1)
        LS = WDbl(CDbl(N) * CDbl(WDbl(2 ^ S)))
    End If
End Function
Private Function WDbl(ByVal N As Double) As Long
    'wrap a double back to a long
    If N > &H7FFFFFFF Then
        N = N - 4294967296#
    ElseIf N < &H80000000 Then
        N = N + 4294967296#
    End If
    WDbl = N
End Function
Private Function Add(ByVal N1 As Long, ByVal N2 As Long, Optional ByVal D As Double) As Long
    'add 2 longs to a double, then wrap round
    D = N1
    D = D + N2
    Add = WDbl(D)
End Function
[/code]
March 4, 2008, 1:52 PM
FrOzeN
I just picked up on one thing that may benefit your code. You could use the two Winsock API functions htons() and htonl(). They stand for Host to Network Byte Order (Short/Long). Which basically converts a short (16-bit) / long (32-bit) to big endian.

[code]Public Declare Function htonl Lib "ws2_32.dll" (ByVal hostlong As Long) As Long
Public Declare Function htons Lib "ws2_32.dll" (ByVal hostshort As Integer) As Integer[/code]

Also, I don't remember much about this. But when using CopyMemory() on strings in VB6, do you need to convert them to and from Unicode?

[code]strAscii = Format(strUnicode, vbFromUnicode)
strUnicode = Format(strAscii, vbUnicode)[/code]
March 4, 2008, 2:48 PM
Ringo
[quote author=FrOzeN link=topic=17367.msg176789#msg176789 date=1204642125]
I just picked up on one thing that may benefit your code. You could use the two Winsock API functions htons() and htonl(). They stand for Host to Network Byte Order (Short/Long). Which basically converts a short (16-bit) / long (32-bit) to big endian.

[code]Public Declare Function htonl Lib "ws2_32.dll" (ByVal hostlong As Long) As Long
Public Declare Function htons Lib "ws2_32.dll" (ByVal hostshort As Integer) As Integer[/code]

Also, I don't remember much about this. But when using CopyMemory() on strings in VB6, do you need to convert them to and from Unicode?

[code]strAscii = Format(strUnicode, vbFromUnicode)
strUnicode = Format(strAscii, vbUnicode)[/code]
[/quote]
hi, thanks.
I havent thought much about big endian converting, but i may use the htonl() function at a later date, to speed that process up a little. :)
afaik, unicode shouldnt be a problem with rtlmovememory, i think it does it automaticly (not sure exacly how, but it works as desired when copying string to other forms of data types)
I cant really say much more on the unicode subject, i dont have much exp with it, but i do know its not an issue with rtlmovememory (never experianced a problem so far anyway)
My main issue is how to manage/hash the mystery buffer and the 64bit integer that seems to be computed each update.
Thanks again m8

EDIT: Have some output dumps here, if this makes it any easyer to diagnose the problem:
[code]
String to be hashed:
"The quick brown fox jumps over the lazy dog"


The block of data to be updated:
Low Word = 00 00 00 00
High Word = 00 02 00 00
54 68 65 20 71 75 69 63 6B 20 62 72 6F 77 6E 20
66 6F 78 20 6A 75 6D 70 73 20 6F 76 65 72 20 74
68 65 20 6C 61 7A 79 20 64 6F 67 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

Block of data after being converted into big endian (in transform sub):
20 65 68 54 63 69 75 71 72 62 20 6B 20 6E 77 6F
20 78 6F 66 70 6D 75 6A 76 6F 20 73 74 20 72 65
6C 20 65 68 20 79 7A 61 00 67 6F 64 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

Result of transforming the block of above data:
F8 0E 4E 84 1E 6C 8D 83 9D 47 D0 BE 23 0F 1C D1
BD 34 45 EF
[/code]


Now the mystery buffer's turn:
[code]
The block of data to be updated:
Low Word = 00 00 00 00
High Word = 00 04 00 00
80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

Block of data after being converted into big endian (in transform sub):
00 00 00 80 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

Result of transforming the block of above data:
D5 93 F2 AB D5 F8 CB 77 E9 70 93 51 07 60 6C 00
94 79 2B D0
[/code]

now the 64bit ints turn to be updated:
Note, i did not copy after updating the mystery buffer, as iagos code copys them before hand (i have also tryed before/after)
[code]
Int64 To update = 00 00 02 00 00 00 00 00
Update:
00 00 02 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

Transform Dump
00 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

After/final Hash result:
E7 40 D1 53 15 EA BD 9A 56 3A 5E 26 AD 7E 89 D2
D3 4F 3E 81
[/code]

Can anyone compare this with iagos code/outputs and see where mines going wrong?

If i switch the low/high word around before updating i get:
[code]
5A E4 56 B2 33 62 25 45 A4 5C D9 C5 66 CC 97 E8 30 78 F4 D0
[/code]
If i update the 64bit AFTER updating the mystery buffer, with out switching the 64int, i get:
[code]
E2 69 B0 68 FE 8D 99 63 BB CE 9A 10 CE 54 DE 53 77 FD 43 9B
[/code]
if i then switch the 64 int i get:
[code]
06 49 38 1A 31 E1 55 CF A8 44 C6 F3 77 8B F3 43 9B 2D D0 81
[/code]
But none of them come close to what its ment to be:
[code]
c6e1d42f fc282d7a e19e84ed 39e776bb 12eb931b
[/code]
Any ideas anyone?
March 4, 2008, 3:10 PM
Ringo
Ah, rob just pointed out its bulk standard SHA1 and not standard broken SHA1 -- silly me :D
I will rewrite all of this to support bulk standard SHA1 as well, then see if it solves the problem :)
March 4, 2008, 9:16 PM
Ringo
yay, finaly!
turned out warden uses 100% bulk standard SHA1 (i had to switch the byte order of dwords inorder to get correct standard SHA1 result, then noticed my test hash matched iagos!)

The differnce between BNCS broken SHA1 and standard/warden SHA1 is, the left shift circulating when setting the dwords in the transform sub:
[code]
    If bS Then 'standard SHA1
        For i = 16 To 79
            hB(i) = LSC((hB(i - 16) Xor hB(i - 8) Xor hB(i - 14) Xor hB(i - 3)), 1)
        Next i
    Else
        For i = 16 To 79
            hB(i) = LSC(1, (hB(i - 16) Xor hB(i - 8) Xor hB(i - 14) Xor hB(i - 3)) And 31)
        Next i
    End If
[/code]
And the padding method in the update routine:
(Fully explains the "mystery buffer" and the 64bit int)
[code]
    If bS Then
        '//Standard SHA1 padding
        S = S & Chr(128) & _
                String((128 - (Len(S) Mod 64) - 9) Mod 64, 0) & _
                String(4, 0) & _
                StrReverse(MakeDWORD((Len(S) * 8)))
    Else
        If ((Len(S) Mod 64) <> 0) Then
            '//buffer the string so its divisible by 64 (0x40)
            S = S & String(64 - (Len(S) Mod 64), 0)
        End If
    End If
[/code]
And byte order reverseing with the dwords:
[code]
    If bRE Then 'reverse endian
        For i = 0 To 15: Call rEndian(P(i + 5), hB(i)): Next i
    Else
        For i = 0 To 15: hB(i) = P(i + 5): Next i
    End If
[/code]
Everything is pretty much the same.
I dont see any point computing a 64bit int, when the chances of dumping a 268-536mb block of data into a SHA1 function is slim to none, so i just managed it in a dword when buffering the orginal data.
Im guessing lockdown uses bulk standard SHA1, but with out byte order reverseing -- [s]need some lockdown SHA1 test hash to verify that tho.[/s]
I also had to redo the right shift function, because once i switched the leftshift circulating arguments, VB love over flow long time.
Big thanks to rob@east for being their to answer any question I had, and pointing me in the right direction with standard SHA1.


Heres some useage code:
[code]

    Const S As String = "The quick brown fox jumps over the lazy dog"
    Dim S2  As String

'//BNCS Broken SHA1
    S2 = BSHA1(S, False, False)
    A0 DB 6E 70 61 60 33 A7 B5 FD DA 37 CE E2 D4 3F 2D A1 02 88

'//Standard/Warden SHA1
    S2 = BSHA1(S, True, True)
    2F D4 E1 C6 7A 2D 28 FC ED 84 9E E1 BB 76 E7 39 1B 93 EB 12

'//Lockdown SHA1 (verifyed -- thx hdx)
    S2 = BSHA1(S, False, True)
    A8 68 FB 6C 0D 95 C4 8D 03 7E 9F 08 CE 6E 42 00 FD 43 5F A4
[/code]

Main code:
[code]
Public Function BSHA1(ByVal S As String, _
                      Optional ByVal bRE As Boolean = False, _
                      Optional ByVal bStandard As Boolean = False) As String
   
    Dim B(21) As Long 'hash buffer
    Dim i    As Long
   
    '//Init the seeds
    B(0) = &H67452301
    B(1) = &HEFCDAB89
    B(2) = &H98BADCFE
    B(3) = &H10325476
    B(4) = &HC3D2E1F0
   
    '//Update the string buffer (to be hashed)
    Call SHA1Update(bRE, bStandard, B(), S)
   
    '//Reverse endian if needed
    If bRE Then
        For i = 0 To 4
            Call rEndian(B(i), B(i))
        Next i
    End If
   
    '//Return the broken SHA1 hash
    BSHA1 = String(20, 0)
    Call CopyMemory(ByVal BSHA1, B(0), 20)
End Function


Private Sub SHA1Update(ByVal bRE As Boolean, _
                      ByVal bS As Boolean, _
                      ByRef B() As Long, _
                      ByVal S As String)
    Dim i As Long
    Dim A As String
    If bS Then
        '//Standard SHA1 padding
        A = Chr(128) & String((128 - (Len(S) Mod 64) - 9) Mod 64, 0)
        If bRE Then
            S = S & A & String(4, 0) & StrReverse(MakeDWORD((Len(S) * 8)))
        Else
            S = S & A & MakeDWORD((Len(S) * 8)) & String(4, 0)
        End If
    Else
        If ((Len(S) Mod 64) <> 0) Then
            '//buffer the string so its divisible by 64 (0x40)
            S = S & String(64 - (Len(S) Mod 64), 0)
        End If
    End If
    For i = 1 To Len(S) Step 64
        '//copy chunk of the string into the long array to be hashed
        Call CopyMemory(B(5), ByVal Mid$(S, i, 64), 64)
        '//transform
        Call SHA1Transform(bRE, bS, B)
    Next i
End Sub


Private Sub SHA1Transform(ByVal bRE As Boolean, ByVal bS As Boolean, ByRef P() As Long)
    Dim hB(80) As Long
    Dim A      As Long
    Dim B      As Long
    Dim C      As Long
    Dim D      As Long
    Dim E      As Long
    Dim G      As Long
    Dim i      As Long
    If bRE Then 'reverse endian
        For i = 0 To 15: Call rEndian(P(i + 5), hB(i)): Next i
    Else
        For i = 0 To 15: hB(i) = P(i + 5): Next i
    End If
    If bS Then 'standard SHA1
        For i = 16 To 79
            hB(i) = LSC((hB(i - 16) Xor hB(i - 8) Xor hB(i - 14) Xor hB(i - 3)), 1)
        Next i
    Else
        For i = 16 To 79
            hB(i) = LSC(1, (hB(i - 16) Xor hB(i - 8) Xor hB(i - 14) Xor hB(i - 3)) And 31)
        Next i
    End If
    A = P(0)
    B = P(1)
    C = P(2)
    D = P(3)
    E = P(4)
    For i = 0 To 19
        G = Add(Add(Add(Add(hB(i), E), LSC(A, 5)), ((B And C) Or ((Not B) And D))), &H5A827999)
        E = D: D = C: C = LSC(B, 30): B = A: A = G
    Next i
    For i = 20 To 39
        G = Add(Add(Add(Add(hB(i), E), LSC(A, 5)), (D Xor C Xor B)), &H6ED9EBA1)
        E = D: D = C: C = LSC(B, 30): B = A: A = G
    Next i
    For i = 40 To 59
        G = Add(Add(Add(Add(hB(i), E), LSC(A, 5)), (C And B) Or (D And C) Or (D And B)), &H8F1BBCDC)
        E = D: D = C: C = LSC(B, 30): B = A: A = G
    Next i
    For i = 60 To 79
        G = Add(Add(Add(Add(hB(i), E), LSC(A, 5)), (D Xor C Xor B)), &HCA62C1D6)
        E = D: D = C: C = LSC(B, 30): B = A: A = G
    Next i
    P(0) = Add(P(0), A)
    P(1) = Add(P(1), B)
    P(2) = Add(P(2), C)
    P(3) = Add(P(3), D)
    P(4) = Add(P(4), E)
End Sub




'~~~~~~ Extra functions ~~~~~~~
Private Sub rEndian(ByVal V As Long, ByRef N As Long)
    '//dirty byte order switching
    Dim S As String * 4
    Call CopyMemory(ByVal S, V, 4)
    S = StrReverse(S)
    Call CopyMemory(N, ByVal S, 4)
End Sub
Private Function LSC(ByVal N As Long, ByVal S As Long) As Long
    'left shift circle
    LSC = (LS(N, S) Or RS(N, (32 - S)))
End Function
Private Function RS(ByVal N As Long, ByVal S As Long) As Long
    'right shift bits
    If (S < 0) Or (S > 31) Then
        RS = 0
    ElseIf (S = 0) Then
        RS = N
    Else
        If ((N And &H80000000) = &H80000000) Then
            N = (N And &H7FFFFFFF)
            If (S = 31) Then 'stop over flow when shifting 31bits
                N = N / 2147483648#
            Else
                N = N \ (2 ^ S)
            End If
            RS = N Or (2 ^ (31 - S))
        Else
           RS = Int(CDbl(N) / CDbl(2 ^ S))
        End If
    End If
End Function
Private Function LS(ByVal N As Long, ByVal S As Long) As Long
    'left shift bits
    If (S < 0) Or (S > 31) Then
        LS = 0
    ElseIf S = 0 Then
        LS = N
    Else
        N = N And (2 ^ (32 - S) - 1)
        LS = WDbl(CDbl(N) * CDbl(WDbl(2 ^ S)))
    End If
End Function
Private Function WDbl(ByVal N As Double) As Long
    'wrap a double back to a long
    If N > &H7FFFFFFF Then
        N = N - 4294967296#
    ElseIf N < &H80000000 Then
        N = N + 4294967296#
    End If
    WDbl = N
End Function
Private Function Add(ByVal N1 As Long, ByVal N2 As Long, Optional ByVal D As Double) As Long
    'add 2 longs to a double, then wrap round
    D = N1
    D = D + N2
    Add = WDbl(D)
End Function
[/code]

If anyone wants to know anything about these SHA1 algorithm's, im happy to answer any question I can, I pretty much know them like the back of my hand at the moment ;p fun times!
They are alot simpler than they 1st look!
Also please point put any bugs any of you find, since i havent fulled tested them yet, only vs test hash's of broken sha1, warden sha1, standard sha1 -- lockdown sha1 [s]needs verifying[/s].

Anyway I REALLY must go to bed, and have nightmares about hex-binary! :(
March 5, 2008, 4:30 AM
HdxBmx27
Lockdown_SHA1.java:
[code]Data: The quick brown fox jumps over the lazy dog
6CFB68A8 8DC4950D 89F7E03 426ECE A45F43FD

Data: Peater Piper picked a peck of pickles
68D064CA D465B73C A557CCB3 30D3659B D67095D2

Data: I love you, you love me, Oh dear god I need sleep -.-
758B08DC 9A3A161F B8C009AD 715F5270 1D242A5F[/code]
You might want to take another look at all the differences between Lockdown's and standard.

As far as I recall the only diff from standard is the final function.... but iono *looks more into it tomorrow when he will be completely re-writing it*
~Hdx
March 5, 2008, 8:34 AM
Ringo
Thanks for them test values HDX :)
I woke up abit more today, and fixed a few bugs.
1st was, i wasnt returning reversed endian byte order on the return hash for warden/standard SHA1:
[code]
    If bRE Then
        For i = 0 To 4
            Call rEndian(B(i), B(i))
        Next i
    End If
[/code]
And i was reverseing endian byte order on the stabndard SHA1 padding, and lockdown SHA1 doesnt -- forgot about that.
[code]
        '//Standard SHA1 padding
        A = Chr(128) & String((128 - (Len(S) Mod 64) - 9) Mod 64, 0)
        If bRE Then
            S = S & A & String(4, 0) & StrReverse(MakeDWORD((Len(S) * 8)))
        Else
            S = S & A & MakeDWORD((Len(S) * 8)) & String(4, 0)
        End If
[/code]
Fixed my last post with these changes, so tuch wood, should work with all 3 now (broken, standard/warden, lockdown)
thanks again.
March 5, 2008, 2:13 PM

Search