Author | Message | Time |
---|---|---|
Strilanc | I've translated the war3 cd key decoder from mbncsutil into vb.net. But I can't seem to get correct results. The compiled library works fine, so I know I've made an error somewhere. I don't have access to a C# interpreter so I can't see what values should be half way through the computation. This is a learning experience for me, so I'd like to figure out what I'm doing wrong. It may be a blind oversight on my part, or some obscure difference between vb and c#. I did manage to totally rewrite the decodeKeyTable method into something legible, so that's something. I'm not quite sure what my question is, I just want general help. If someone could tell me the true values of srcData before and after the //mult block for a known cd key, that would be very helpful. | January 2, 2008, 2:17 AM |
FrostWraith | I am not going to pretend to know exactly how to help you, but you will need to show some code and how you are testing it to receive any help. | January 2, 2008, 2:52 AM |
Strilanc | Sure thing, I just didn't know if it was appropriate to post so much code so soon. Here is the relevant code: [code] 'in a module 'easy ways to create arrays Public Function bunch(Of x)(ByVal ParamArray xx As x()) As x() Return xx End Function Public Function bytes(ByVal ParamArray bb As Byte()) As Byte() Return bb End Function 'in the decoding class '30 permutations of 0-15 (still the same map, just divided in chunks of 16) Private Shared w3TranslateMap As Byte()() = bunch( _ bytes(&H9, &H4, &H7, &HF, &HD, &HA, &H3, &HB, &H1, &H2, &HC, &H8, &H6, &HE, &H5, &H0), _ bytes(&H9, &HB, &H5, &H4, &H8, &HF, &H1, &HE, &H7, &H0, &H3, &H2, &HA, &H6, &HD, &HC), _ bytes(&HC, &HE, &H1, &H4, &H9, &HF, &HA, &HB, &HD, &H6, &H0, &H8, &H7, &H2, &H5, &H3), _ bytes(&HB, &H2, &H5, &HE, &HD, &H3, &H9, &H0, &H1, &HF, &H7, &HC, &HA, &H6, &H4, &H8), _ bytes(&H6, &H2, &H4, &H5, &HB, &H8, &HC, &HE, &HD, &HF, &H7, &H1, &HA, &H0, &H3, &H9), _ bytes(&H5, &H4, &HE, &HC, &H7, &H6, &HD, &HA, &HF, &H2, &H9, &H1, &H0, &HB, &H8, &H3), _ bytes(&HC, &H7, &H8, &HF, &HB, &H0, &H5, &H9, &HD, &HA, &H6, &HE, &H2, &H4, &H3, &H1), _ bytes(&H3, &HA, &HE, &H8, &H1, &HB, &H5, &H4, &H2, &HF, &HD, &HC, &H6, &H7, &H9, &H0), _ bytes(&HC, &HD, &H1, &HF, &H8, &HE, &H5, &HB, &H3, &HA, &H9, &H0, &H7, &H2, &H4, &H6), _ bytes(&HD, &HA, &H7, &HE, &H1, &H6, &HB, &H8, &HF, &HC, &H5, &H2, &H3, &H0, &H4, &H9), _ bytes(&H3, &HE, &H7, &H5, &HB, &HF, &H8, &HC, &H1, &HA, &H4, &HD, &H0, &H6, &H9, &H2), _ bytes(&HB, &H6, &H9, &H4, &H1, &H8, &HA, &HD, &H7, &HE, &H0, &HC, &HF, &H2, &H3, &H5), _ bytes(&HC, &H7, &H8, &HD, &H3, &HB, &H0, &HE, &H6, &HF, &H9, &H4, &HA, &H1, &H5, &H2), _ bytes(&HC, &H6, &HD, &H9, &HB, &H0, &H1, &H2, &HF, &H7, &H3, &H4, &HA, &HE, &H8, &H5), _ bytes(&H3, &H6, &H1, &H5, &HB, &HC, &H8, &H0, &HF, &HE, &H9, &H4, &H7, &HA, &HD, &H2), _ bytes(&HA, &H7, &HB, &HF, &H2, &H8, &H0, &HD, &HE, &HC, &H1, &H6, &H9, &H3, &H5, &H4), _ bytes(&HA, &HB, &HD, &H4, &H3, &H8, &H5, &H9, &H1, &H0, &HF, &HC, &H7, &HE, &H2, &H6), _ bytes(&HB, &H4, &HD, &HF, &H1, &H6, &H3, &HE, &H7, &HA, &HC, &H8, &H9, &H2, &H5, &H0), _ bytes(&H9, &H6, &H7, &H0, &H1, &HA, &HD, &H2, &H3, &HE, &HF, &HC, &H5, &HB, &H4, &H8), _ bytes(&HD, &HE, &H5, &H6, &H1, &H9, &H8, &HC, &H2, &HF, &H3, &H7, &HB, &H4, &H0, &HA), _ bytes(&H9, &HF, &H4, &H0, &H1, &H6, &HA, &HE, &H2, &H3, &H7, &HD, &H5, &HB, &H8, &HC), _ bytes(&H3, &HE, &H1, &HA, &H2, &HC, &H8, &H4, &HB, &H7, &HD, &H0, &HF, &H6, &H9, &H5), _ bytes(&H7, &H2, &HC, &H6, &HA, &H8, &HB, &H0, &HF, &H4, &H3, &HE, &H9, &H1, &HD, &H5), _ bytes(&HC, &H4, &H5, &H9, &HA, &H2, &H8, &HD, &H3, &HF, &H1, &HE, &H6, &H7, &HB, &H0), _ bytes(&HA, &H8, &HE, &HD, &H9, &HF, &H3, &H0, &H4, &H6, &H1, &HC, &H7, &HB, &H2, &H5), _ bytes(&H3, &HC, &H4, &HA, &H2, &HF, &HD, &HE, &H7, &H0, &H5, &H8, &H1, &H6, &HB, &H9), _ bytes(&HA, &HC, &H1, &H0, &H9, &HE, &HD, &HB, &H3, &H7, &HF, &H8, &H5, &H2, &H4, &H6), _ bytes(&HE, &HA, &H1, &H8, &H7, &H6, &H5, &HC, &H2, &HF, &H0, &HD, &H3, &HB, &H4, &H9), _ bytes(&H3, &H8, &HE, &H0, &H7, &H9, &HF, &HC, &H1, &H6, &HD, &H2, &H5, &HA, &HB, &H4), _ bytes(&H3, &HA, &HC, &H4, &HD, &HB, &H9, &HE, &HF, &H6, &H1, &H7, &H2, &H0, &H5, &H8) _ ) 'Returns the product, public and private keys of a cd key Public Shared Function processWC3Key(ByVal key As String) As Byte()() Const W3_KEYLEN As Integer = 26 Const W3_BUFLEN As Integer = W3_KEYLEN * 2 If key.Length <> W3_KEYLEN Then Throw New Exception("Invalid cd key length.") Dim table(0 To W3_BUFLEN - 1) As Byte Dim cdkey As Char() = key.ToUpper().ToCharArray() 'Fill table with permuted cd key Dim a As Integer = 0, b As Integer = &H21 For i As Integer = 0 To key.Length - 1 a = (b + &H7B5) Mod W3_BUFLEN b = (a + &H7B5) Mod W3_BUFLEN Dim c As Byte = w3KeyMap(Asc(cdkey(i))) '0 to 25 If c = 255 Then Throw New Exception("Invalid cd key character.") table(a) = CByte(c / 5) table(b) = CByte(c Mod 5) Next i 'Compute table values '(this section is very likely incorrect) Dim values(0 To 3) As UInteger Dim n As BigNum = New BigNum(0) For i As Integer = W3_BUFLEN - 1 To 0 Step -1 n += table(i) n *= 5 ReDim Preserve n.bytes(0 To 15) Next i Dim bbb()() As Byte = chopBytes(n.bytes, 4, 4, 4, 4) For i As Integer = 0 To 3 values(i) = CUInt(unpackLong(bbb(i))) Next i decodeKeyTable(values) Dim bbProduct() As Byte = packLong(CLng(values(0) >> &HA)) Dim bbPublic() As Byte = reverseBytes(packLong(values(0) And &HFFFFFF00)) Dim bbPrivate() As Byte = packBytes(packLong(values(1) And &HFFFFFF, 3), packLong(values(2)), packLong(values(3) >> 2, 3)) Return bunch(bbProduct, bbPublic, bbPrivate) End Function 'Almost totally rewritten. Equivalent to an almost word-for-word translation Private Shared Sub decodeKeyTable(ByVal keyTable As UInteger()) Dim keys(0 To 29) As Integer, shifts(0 To 29) As Integer 'Pre-compute key indices and shifts for each row for the first pass For r As Integer = 0 To 29 'note that 0-29 use only 5 bits keys(r) = 3 - (r >> 3) '0 to 3; negation of the 2 high bits of row shifts(r) = (r And &H7) << 2 '0 to 28; 4x the 3 low bits of row Next r 'First pass For r As Integer = 29 To 0 Step -1 Dim perm() As Byte = w3TranslateMap(r) Dim c As Integer = CInt((keyTable(keys(r)) >> shifts(r)) And &HF) '0 to 15; pick 4 adjacent bits of keyTable(keys(r)) 'Jump around the w3TranslateMap columns a bit For r2 As Integer = 29 To 0 Step -1 If r = r2 Then Continue For Dim c2 As Integer = CInt((keyTable(keys(r2)) >> shifts(r2)) And &HF) '0 to 15; pick 4 adjacent bits c = perm(c2 Xor perm(c)) '0 to 15; permute column Next r2 'Replace the 4 adjacent bits we picked in keyTable(keys(r)) with the 4 bits of perm(c) keyTable(keys(r)) = CUInt(keyTable(keys(r)) And Not (&HF << shifts(r))) keyTable(keys(r)) = CUInt(keyTable(keys(r)) Or (CUInt(perm(c)) << shifts(r))) Next r 'Store the keyTable so far Dim keyTableFirstPass(0 To 3) As UInteger keyTable.CopyTo(keyTableFirstPass, 0) 'Second pass For i As Integer = 0 To 119 'note that 0-119 use only 7 bits Dim j As Integer = (i * 11) Mod 120 Dim iKey As Integer = 3 - (i >> 5) '0 to 3; the 2 high bits of i inverted Dim jKey As Integer = 3 - (j >> 5) '0 to 3; the 2 high bits of j inverted Dim iShift As Integer = i And &H1F '0 to 31; the 5 low bits of i Dim jShift As Integer = j And &H1F '0 to 31; the 5 low bits of j 'replace the iShift'th bit of keyTable(iKey) with the jShift'th bit of keyTableFirstPass(jKey) If ((keyTableFirstPass(jKey) >> jShift) And &H1) = 0 Then keyTable(iKey) = CUInt(keyTable(iKey) And Not (CUInt(&H1) << iShift)) 'unset Else keyTable(iKey) = CUInt(keyTable(iKey) Or (CUInt(&H1) << iShift)) 'set End If Next i End Sub [/code] The pack and unpack functions just convert numbers to and from arrays of bytes. I'm testing it by running it in the visual studio interpreter and comparing the public/product keys to the known values I sniffed using wireshark. | January 2, 2008, 4:47 AM |
Strilanc | I've solved the problem. I needed to switch endian-ness before the multiplication step, among other small things. Now I can see that the CD Key is shuffled into a base 5 number, then converted to base 2, then some cryptography stuff, then the keys come right out. | January 4, 2008, 9:21 PM |
Strilanc | As another update, I've massively improved on the code originally provided. It is now trivial to reverse the process and generate a cd key from given public/private/product keys (of course such keys won't necessarily work on BNET because not all public/private key pairs are valid). [code] ''' <summary>Map from cd key characters to their values (invalid characters go to 0xFF)</summary> ''' <remarks>Only 25 characters from the possible 36</remarks> Private Shared ReadOnly w3KeyMap As Byte() = { _ &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, _ &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, _ &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, _ &HFF, &HFF, &H0, &HFF, &H1, &HFF, &H2, &H3, &H4, &H5, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, _ &HFF, &HFF, &H6, &H7, &H8, &H9, &HA, &HB, &HC, &HFF, &HD, &HE, &HFF, &HF, &H10, &HFF, _ &H11, &HFF, &H12, &HFF, &H13, &HFF, &H14, &H15, &H16, &H17, &H18, &HFF, &HFF, &HFF, &HFF, &HFF, _ &HFF, &HFF, &H6, &H7, &H8, &H9, &HA, &HB, &HC, &HFF, &HD, &HE, &HFF, &HF, &H10, &HFF, _ &H11, &HFF, &H12, &HFF, &H13, &HFF, &H14, &H15, &H16, &H17, &H18, &HFF, &HFF, &HFF, &HFF, &HFF, _ &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, _ &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, _ &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, _ &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, _ &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, _ &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, _ &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, _ &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF, &HFF _ } ''' <summary>30 permutations of 0-15</summary> Private Shared ReadOnly w3TranslateMap As Byte()() = { _ array(Of Byte)(&H9, &H4, &H7, &HF, &HD, &HA, &H3, &HB, &H1, &H2, &HC, &H8, &H6, &HE, &H5, &H0), _ array(Of Byte)(&H9, &HB, &H5, &H4, &H8, &HF, &H1, &HE, &H7, &H0, &H3, &H2, &HA, &H6, &HD, &HC), _ array(Of Byte)(&HC, &HE, &H1, &H4, &H9, &HF, &HA, &HB, &HD, &H6, &H0, &H8, &H7, &H2, &H5, &H3), _ array(Of Byte)(&HB, &H2, &H5, &HE, &HD, &H3, &H9, &H0, &H1, &HF, &H7, &HC, &HA, &H6, &H4, &H8), _ array(Of Byte)(&H6, &H2, &H4, &H5, &HB, &H8, &HC, &HE, &HD, &HF, &H7, &H1, &HA, &H0, &H3, &H9), _ array(Of Byte)(&H5, &H4, &HE, &HC, &H7, &H6, &HD, &HA, &HF, &H2, &H9, &H1, &H0, &HB, &H8, &H3), _ array(Of Byte)(&HC, &H7, &H8, &HF, &HB, &H0, &H5, &H9, &HD, &HA, &H6, &HE, &H2, &H4, &H3, &H1), _ array(Of Byte)(&H3, &HA, &HE, &H8, &H1, &HB, &H5, &H4, &H2, &HF, &HD, &HC, &H6, &H7, &H9, &H0), _ array(Of Byte)(&HC, &HD, &H1, &HF, &H8, &HE, &H5, &HB, &H3, &HA, &H9, &H0, &H7, &H2, &H4, &H6), _ array(Of Byte)(&HD, &HA, &H7, &HE, &H1, &H6, &HB, &H8, &HF, &HC, &H5, &H2, &H3, &H0, &H4, &H9), _ array(Of Byte)(&H3, &HE, &H7, &H5, &HB, &HF, &H8, &HC, &H1, &HA, &H4, &HD, &H0, &H6, &H9, &H2), _ array(Of Byte)(&HB, &H6, &H9, &H4, &H1, &H8, &HA, &HD, &H7, &HE, &H0, &HC, &HF, &H2, &H3, &H5), _ array(Of Byte)(&HC, &H7, &H8, &HD, &H3, &HB, &H0, &HE, &H6, &HF, &H9, &H4, &HA, &H1, &H5, &H2), _ array(Of Byte)(&HC, &H6, &HD, &H9, &HB, &H0, &H1, &H2, &HF, &H7, &H3, &H4, &HA, &HE, &H8, &H5), _ array(Of Byte)(&H3, &H6, &H1, &H5, &HB, &HC, &H8, &H0, &HF, &HE, &H9, &H4, &H7, &HA, &HD, &H2), _ array(Of Byte)(&HA, &H7, &HB, &HF, &H2, &H8, &H0, &HD, &HE, &HC, &H1, &H6, &H9, &H3, &H5, &H4), _ array(Of Byte)(&HA, &HB, &HD, &H4, &H3, &H8, &H5, &H9, &H1, &H0, &HF, &HC, &H7, &HE, &H2, &H6), _ array(Of Byte)(&HB, &H4, &HD, &HF, &H1, &H6, &H3, &HE, &H7, &HA, &HC, &H8, &H9, &H2, &H5, &H0), _ array(Of Byte)(&H9, &H6, &H7, &H0, &H1, &HA, &HD, &H2, &H3, &HE, &HF, &HC, &H5, &HB, &H4, &H8), _ array(Of Byte)(&HD, &HE, &H5, &H6, &H1, &H9, &H8, &HC, &H2, &HF, &H3, &H7, &HB, &H4, &H0, &HA), _ array(Of Byte)(&H9, &HF, &H4, &H0, &H1, &H6, &HA, &HE, &H2, &H3, &H7, &HD, &H5, &HB, &H8, &HC), _ array(Of Byte)(&H3, &HE, &H1, &HA, &H2, &HC, &H8, &H4, &HB, &H7, &HD, &H0, &HF, &H6, &H9, &H5), _ array(Of Byte)(&H7, &H2, &HC, &H6, &HA, &H8, &HB, &H0, &HF, &H4, &H3, &HE, &H9, &H1, &HD, &H5), _ array(Of Byte)(&HC, &H4, &H5, &H9, &HA, &H2, &H8, &HD, &H3, &HF, &H1, &HE, &H6, &H7, &HB, &H0), _ array(Of Byte)(&HA, &H8, &HE, &HD, &H9, &HF, &H3, &H0, &H4, &H6, &H1, &HC, &H7, &HB, &H2, &H5), _ array(Of Byte)(&H3, &HC, &H4, &HA, &H2, &HF, &HD, &HE, &H7, &H0, &H5, &H8, &H1, &H6, &HB, &H9), _ array(Of Byte)(&HA, &HC, &H1, &H0, &H9, &HE, &HD, &HB, &H3, &H7, &HF, &H8, &H5, &H2, &H4, &H6), _ array(Of Byte)(&HE, &HA, &H1, &H8, &H7, &H6, &H5, &HC, &H2, &HF, &H0, &HD, &H3, &HB, &H4, &H9), _ array(Of Byte)(&H3, &H8, &HE, &H0, &H7, &H9, &HF, &HC, &H1, &H6, &HD, &H2, &H5, &HA, &HB, &H4), _ array(Of Byte)(&H3, &HA, &HC, &H4, &HD, &HB, &H9, &HE, &HF, &H6, &H1, &H7, &H2, &H0, &H5, &H8) _ } ''' <summary>Extracts the product, public and private keys from a wc3 cd key.</summary> ''' <param name="key">The cd key. Dashes and lower case characters are allowed.</param> ''' <returns>Byte arrays for each key: return.v1 = product key, return.v2 = public key, return.v3 = private key</returns> ''' <remarks>All arrays and numbers used are considered as big endian</remarks> Public Shared Function decodeWC3Key(ByVal key As String) As Tuple(Of Byte(), Byte(), Byte()) Const W3_KEYLEN As Integer = 26 Const W3_BUFLEN As Integer = W3_KEYLEN * 2 If key.Length <> W3_KEYLEN Then Throw New ArgumentException("Invalid cd key length.") Dim cdkey As Char() = key.ToUpper().Replace("-", "").ToCharArray() 'Shuffle the cd key into the digits of a base 5 number Dim d As Integer = 33 Dim n_digitsBase5(0 To W3_BUFLEN - 1) As Byte For i As Integer = 0 To W3_KEYLEN - 1 Dim c As Byte = w3KeyMap(Asc(cdkey(i))) 'will be from 0 to 24, or invalid (0xFF) If c = &HFF Then Throw New ArgumentException("Invalid cd key character: " + cdkey(i)) 'get the two base 5 digits of c d = (d + 49) Mod W3_BUFLEN n_digitsBase5(d) = CByte(c \ 5) d = (d + 49) Mod W3_BUFLEN n_digitsBase5(d) = CByte(c Mod 5) Next i 'Convert the shuffled number to base 2 'Note that a 52 digit base 5 number needs at most 121 bits Dim n As BigNum = 0 For i As Integer = W3_BUFLEN - 1 To 0 Step -1 n *= 5 n += n_digitsBase5(i) Next i Dim nbytes() As Byte = n.getBytes(16) 'Convert from bytes to nibbles Dim nibbles(0 To 31) As Byte For i As Integer = 0 To 15 For j As Integer = 0 To 1 nibbles((i << 1) + j) = CByte((nbytes(i) >> (j << 2)) And CUInt(&HF)) Next j Next i 'Permute the nibbles using the translation map For r As Integer = 29 To 0 Step -1 Dim perm() As Byte = w3TranslateMap(r) Dim c As Byte = nibbles(r) 'Permute For r2 As Integer = 29 To 0 Step -1 If r = r2 Then Continue For c = perm(nibbles(r2) Xor perm(c)) Next r2 nibbles(r) = perm(c) Next r 'Convert from nibbles to bits Dim bits(0 To 127) As Boolean For i As Integer = 0 To 31 For j As Integer = 0 To 3 bits(i * 4 + j) = CBool(((nibbles(i) >> j) And &H1) <> 0) Next j Next i 'Swap the bits with their *11 counterpart (mod 120) 'Note that x*11*11 = x*121 = x*1 = x (mod 120) For i As Integer = 0 To 119 Dim j As Integer = (i * 11) Mod 120 If j <= i Then Continue For 'don't swap the same bits twice 'swap bits i and j Dim b As Boolean = bits(i) bits(i) = bits(j) bits(j) = b Next i 'Convert from bits to bytes Dim bb(0 To 15) As Byte For i As Integer = 0 To 15 For j As Integer = 0 To 7 If bits((i << 3) + j) Then bb(i) = bb(i) Or CByte(&H1 << j) Next j Next i 'Extract keys Dim bbProduct() As Byte, bbPublic() As Byte, bbPrivate() As Byte bbProduct = array(Of Byte)(bb(13) >> &H2, 0, 0, 0) bbPublic = array(Of Byte)(bb(10), bb(11), bb(12), 0) bbPrivate = array(Of Byte)(bb(8), bb(9), bb(4), bb(5), bb(6), bb(7), bb(0), bb(1), bb(2), bb(3)) Return New Tuple(Of Byte(), Byte(), Byte())(bbProduct, bbPublic, bbPrivate) End Function [/code] | January 27, 2008, 6:51 PM |