| 08-31-2008, 12:17 AM | #1 |
There's a value required for hosting maps that I've been trying to compute for some time now. Currently I can only get it by hosting the map with wc3, and grabbing the value with a packet sniffer. I want to be able to compute this thing, so I put some work into it. I've found that it seems to be a non-standard crc32 of war3map.j. It definitely only depends on data inside the mpq files, because I repackaged the whole archive with no compression or encryption and it did not change. Changing a single character in war3map.j changes only one byte of the output. What I'm looking for are suggestions. What non-standard crc32 has blizzard used before? Things like that. |
| 08-31-2008, 05:40 AM | #2 |
It's looking less and less complicated. Changing any 1 bit in the input seems to change only 1 bit in the output. Changing 2 bits seems to change 2 bits (the same as changing both 1 bits for those changes). The changes even increase linearly with the bit number. But I still can't seem to get it quite right. It returns the right value for the map I'm testing on, but not others. The linear increase also seems to break halfway through. What I have so far: Code:
Public Function loopcheck(ByVal s As IO.Stream, Optional ByVal length As Integer = -1) As UInteger
If length = -1 Then length = CInt(s.Length)
Dim mask As UInteger = CUInt(1) << 21
Dim b As New BitBuffer
Dim val As UInteger = 606559388
For repeat As Integer = 0 To length - 1
b.queueByte(CByte(s.ReadByte()))
While b.numBits > 0
If b.takeBit() Then val = val Xor mask
mask <<= 1
If mask = 0 Then mask = CUInt(1)
End While
Next repeat
Return val
End Function |
| 08-31-2008, 06:06 AM | #3 |
It starts at bit 21, moves up 1 bit every bit, and back 3 bits every 32 bits. Still not working for other maps. |
| 08-31-2008, 06:16 AM | #4 |
Stupid Error Fixed. Now it works for all the versions of my test map, but still not all maps. The function has a lot of room for improvement, but since it's not working yet... Code:
Public Function loopcheck(ByVal s As IO.Stream, Optional ByVal length As Integer = -1) As UInteger
If length = -1 Then length = CInt(s.Length)
Dim mask As UInteger = CUInt(1) << 21
Dim b As New BitBuffer
Dim val As UInteger = 890969739
For repeat As Integer = 1 To length
b.queueByte(CByte(s.ReadByte()))
While b.numBits > 0
If b.takeBit() Then val = val Xor mask
mask <<= 1
If mask = 0 Then mask = CUInt(1)
End While
If repeat Mod 4 = 0 Then
mask >>= 1
If mask = 0 Then mask = CUInt(&H80000000L)
mask >>= 1
If mask = 0 Then mask = CUInt(&H80000000L)
mask >>= 1
If mask = 0 Then mask = CUInt(&H80000000L)
End If
Next repeat
Return val
End Function |
| 08-31-2008, 12:13 PM | #5 |
It also uses war3map.doo, war3map.w3e, war3map.wpm. Tried every file now, doesn't use any others present in the test maps. STILL not working on other maps. |
| 08-31-2008, 05:33 PM | #6 |
This sounds like it only takes "meaningful" files into consideration and excludes editor-only files. It probably works in your test map becaue the map doesn't contain all optional files. This leads me to the following lists: Files that definitely matter: war3map.j war3map.w3e war3map.wpm war3map.doo Editor-only files that shouldn't matter: war3map.wtg war3map.wct war3map.w3r war3map.w3c war3map.w3s war3mapUnits.doo (not 100% sure about this one) war3map.w3i (this is not editor-only but every map has it and if your test map doesn't need it for the computation to succeed I guess it shouldn't matter) war3map.shd (same as w3i) Files that might not exist in your map but which might matter: war3map.wts (externalized strings) war3map.imp (imported files list) war3map.w3u (custom units) war3map.w3t (items) war3map.w3a (abilities) war3map.w3b (dests) war3map.w3d (doods) war3map.w3q (upgrades) war3mapMisc.txt (gameplay constants) war3mapSkin.txt (game interface) war3mapExtra.txt (external object data source) Files that probably won't matter since they're rarely used: war3mapMap.blp war3mapMap.b00 war3mapMap.tga war3mapPreview.tga war3mapPath.tga war3map.mmp |
| 08-31-2008, 08:19 PM | #7 |
I'm stilling running into issues with some variations of my test map; removing one byte from war3map.j breaks it. It seems some of the magic numbers are actually calculated. But I've got quite a collection of map keys built up, and the patterns get clearer and clearer. |
| 08-31-2008, 11:40 PM | #8 |
This thing is being devious. If I knew how to use a disassembler or something I could probably just find the function. Right now I'm just feeding dozens of values through it and looking for patterns. The patterns keep breaking my assumptions. I found one that right shifts by 11 where I expected no right shift... still not sure why that is. It would be *much* easier if 'feeding a value' didn't involve logging into battle.net, and creating a game. Also it would help if it didn't crash if I modify war3map.j [it reads it for something or other, I can modify war3map.doo at will at least]. |
| 09-01-2008, 01:14 PM | #9 |
I think it uses common.j and blizzard.j as well, and maybe any *.j it encounters. As far as I know, adding such modified script file into the map file forces clients to redownload a fresh copy of the map. Also, have you tried this on LAN? It might be easier to test it that way. |
| 09-02-2008, 05:17 PM | #10 |
Well I figured out why I was having so much trouble. I started removing files from the mpq so I was dealing with fewer variables. I found I could get the correct value for all the isolated files- except war3map.j. Even the empty war3map.j file gives a non-zero key. I think it's prepending common.j or something else onto war3map.j before running the check. Code:
Public Function loopcheck(ByVal s As IO.Stream, ByVal valwo As Integer, ByVal valbo As Integer, ByVal wordo As Integer, ByVal byteo As Integer, Optional ByVal length As Integer = -1) As UInteger
If length = -1 Then length = CInt(s.Length)
Dim val As UInteger = 0
Dim r As New IO.BinaryReader(s)
For repeat As Integer = 1 To length - 3 Step 4
val = ShiftRotateLeft(val, valwo) Xor ShiftRotateLeft(r.ReadUInt32(), wordo)
Next repeat
For repeat As Integer = 1 To length Mod 4
val = ShiftRotateLeft(val, valbo) Xor ShiftRotateLeft(CUInt(r.ReadByte()), byteo)
Next repeat
Return val
End Function
Public Function loopCheck2(ByVal m As MPQ.Archive) As UInteger
Dim val As UInteger = 0
Dim offset As Integer = 3
Dim ss() As String = {"war3map.j", "war3map.doo", "war3map.wpm", "war3map.w3e"}
For Each s As String In ss
If m.hashTable.contains(s) Then
offset += 3
val = val Xor loopcheck(New MPQ.FileStream(m, s), 3, 3, offset, offset)
If s = "war3map.j" Then val = val Xor CUInt(973126876)
End If
Next s
Return val
End Function |
| 09-02-2008, 11:29 PM | #11 |
I still haven't solve the .j issue, but I started testing other files like pitzermike suggested. war3map.w3u, war3map.w3a, war3map.w3q are definitely included. Haven't tried items, doodads, and destructables yet but the other files all don't matter. |
| 09-03-2008, 05:48 PM | #12 |
Have you tried playing with custom versions of common.j and blizzard.j? |
| 09-04-2008, 10:20 PM | #13 |
Huh, Strange, at first when I hit refresh I saw like 5 of my posts so I thought *crap* and clicked delete on one then they were suddenly all gone. So the original post was: Do stop the map from crashing when you modify war3map.j try to remove the (attributes) file inside the map mpq. |
| 09-04-2008, 10:51 PM | #14 |
I was already fixing the attributes file. The game actually reads the script in war3map.j and validates (at least some of) it. I found out that putting a null character at the start of war3map.j made it not crash, because it stops parsing before it gets to any non-valid data. |
| 09-05-2008, 01:43 AM | #15 |
Custom common.j, blizzard.j, and common.ai all change the value. But there are other files I need but don't know, because the key still isn't zeroed with all these 0-length files. It would help if I knew all the files you could override and all the files it "included" by default. |
