Sunday, November 2, 2014

Warhammer Online (PART I)

In this series of article, we are going to play/study the well known game: Warhammer Online. All online (official) server are down since December 18th, 2013. That's why we are going to see if we can continue to play this game by extracting resources, understanding game protocol, etc ... When a game is dead you can still have fun with it by reverse engineering it.

In order to follow those articles, we will use the 2 original ISO file generated from the 2 original game DVD. After gathering the two ISO files, we will extract them:

F:\Game>"C:\Program Files\7-Zip\7z.exe" x war_01.iso -oF:\Game\war_01 -r -y
[...]
F:\Game>"C:\Program Files\7-Zip\7z.exe" x war_02.iso -oF:\Game\war_02 -r -y

We will now use the following python script in order to extract useful information:

F:\Game>C:\Python27\python.exe
[...]
>>> import filecmp
>>> cmp = filecmp.dircmp(".\war_01", ".\war_02")
>>> cmp.report_full_closure()

Here you can find the result of this diff operation:

diff .\war_01 .\war_02
Only in .\war_01 : ['data1']
Only in .\war_02 : ['data2']
Identical files : ['WARInstall.ico', 'WARLauncher.exe', 'WARLauncher.ico']
Differing files : ['AUTORUN.INF']
Common subdirectories : ['help', 'pictures', 'setup', 'trailers']

diff .\war_01\trailers .\war_02\trailers
Identical files : ['trailer1.flv']
Common subdirectories : ['de', 'en', 'es', 'fr', 'it']

diff .\war_01\trailers\fr .\war_02\trailers\fr
Identical files : ['trailer2.flv']

diff .\war_01\trailers\de .\war_02\trailers\de
Identical files : ['trailer2.flv']

diff .\war_01\trailers\en .\war_02\trailers\en
Identical files : ['trailer2.flv']

diff .\war_01\trailers\it .\war_02\trailers\it
Identical files : ['trailer2.flv']

diff .\war_01\trailers\es .\war_02\trailers\es
Identical files : ['trailer2.flv']

diff .\war_01\pictures .\war_02\pictures
Common subdirectories : ['large', 'thumbs']

diff .\war_01\pictures\large .\war_02\pictures\large
Only in .\war_01\pictures\large : ['Thumbs.db']
Identical files : ['013008_CA03.jpg', '013008_CA05.jpg', '013008_CA12.jpg', '0507_CA_01.jpg', '0507_CA_05.jpg', '0507_CA_08.jpg', '0507_CA_10.jpg', 'CA0707_04.jpg', 'CA
0707_25.jpg', 'CA0707_28.jpg', 'CA0707_37.jpg', 'CA0707_38.jpg', 'CA0802_14.jpg', 'CA0802_15.jpg', 'CA0806_01.jpg', 'CA0806_02.jpg', 'CA0806_03.jpg', 'CA0806_04.jpg', '
CA0806_05.jpg', 'CA0806_06.jpg', 'CA0806_07.jpg', 'CA0806_08.jpg', 'CA0806_09.jpg', 'CAT_1107_08.jpg', 'CAT_1107_10.jpg', 'CA_May_2006_11.jpg', 'CncArt0907_18.jpg', 'Cn
cArt0907_30.jpg', 'CncArt0907_32.jpg', 'CncArt0907_36.jpg', 'CncArt0907_37.jpg', 'ConArt0807_67.jpg', 'ConArt0807_68.jpg', 'ConArt20070905_15.jpg', 'KnightOfTzeentch.jp
g', 'NPC_Beastmaster.jpg', 'NPC_Felix.jpg', 'NPC_Gotrek.jpg', 'NPC_GrandTheogonistVolkmar.jpg', 'NPC_ThyrusGormann.jpg', 'SlaaneshChampion.jpg', 'SlaaneshofZealots.jpg'
, 'SlimeHound.jpg', 'WarriorofTzeentch.jpg', 'birdhead.jpg', 'ceat0301.jpg', 'ceat0304.jpg', 'ceat0305.jpg', 'lordofchange.jpg', 'marauderofzeentch.jpg']

diff .\war_01\pictures\thumbs .\war_02\pictures\thumbs
Identical files : ['013008_CA03s.jpg', '013008_CA05s.jpg', '013008_CA12s.jpg', '0507_CA_01s.jpg', '0507_CA_05s.jpg', '0507_CA_08s.jpg', '0507_CA_10s.jpg', 'CA0707_04s.j
pg', 'CA0707_25s.jpg', 'CA0707_28s.jpg', 'CA0707_37s.jpg', 'CA0707_38s.jpg', 'CA0802_14s.jpg', 'CA0802_15s.jpg', 'CA0806_01s.jpg', 'CA0806_02s.jpg', 'CA0806_03s.jpg', '
CA0806_04s.jpg', 'CA0806_05s.jpg', 'CA0806_06s.jpg', 'CA0806_07s.jpg', 'CA0806_08s.jpg', 'CA0806_09s.jpg', 'CAT_1107_08s.jpg', 'CAT_1107_10s.jpg', 'CA_May_2006_11s.jpg'
, 'CncArt0907_18s.jpg', 'CncArt0907_30s.jpg', 'CncArt0907_32s.jpg', 'CncArt0907_36s.jpg', 'CncArt0907_37s.jpg', 'ConArt0807_67s.jpg', 'ConArt0807_68s.jpg', 'ConArt20070
905_15s.jpg', 'KnightOfTzeentchs.jpg', 'NPC_Beastmasters.jpg', 'NPC_Felixs.jpg', 'NPC_Gotreks.jpg', 'NPC_GrandTheogonistVolkmars.jpg', 'NPC_ThyrusGormanns.jpg', 'Slaane
shChampions.jpg', 'SlaaneshofZealotss.jpg', 'SlimeHounds.jpg', 'WarriorofTzeentchs.jpg', 'birdheads.jpg', 'ceat0301s.jpg', 'ceat0304s.jpg', 'ceat0305s.jpg', 'lordofchan
ges.jpg', 'marauderofzeentchs.jpg']

diff .\war_01\setup .\war_02\setup
Common subdirectories : ['directx', 'war']

diff .\war_01\setup\war .\war_02\setup\war
Identical files : ['war.exe']

diff .\war_01\setup\directx .\war_02\setup\directx
Identical files : ['APR2007_XACT_x64.cab', 'APR2007_XACT_x86.cab', 'APR2007_d3dx10_33_x64.cab', 'APR2007_d3dx10_33_x86.cab', 'APR2007_d3dx9_33_x64.cab', 'APR2007_d3dx9_
33_x86.cab', 'APR2007_xinput_x64.cab', 'APR2007_xinput_x86.cab', 'AUG2006_XACT_x64.cab', 'AUG2006_XACT_x86.cab', 'AUG2006_xinput_x64.cab', 'AUG2006_xinput_x86.cab', 'AU
G2007_XACT_x64.cab', 'AUG2007_XACT_x86.cab', 'AUG2007_d3dx10_35_x64.cab', 'AUG2007_d3dx10_35_x86.cab', 'AUG2007_d3dx9_35_x64.cab', 'AUG2007_d3dx9_35_x86.cab', 'Apr2005_
d3dx9_25_x64.cab', 'Apr2005_d3dx9_25_x86.cab', 'Apr2006_MDX1_x86.cab', 'Apr2006_MDX1_x86_Archive.cab', 'Apr2006_XACT_x64.cab', 'Apr2006_XACT_x86.cab', 'Apr2006_d3dx9_30
_x64.cab', 'Apr2006_d3dx9_30_x86.cab', 'Apr2006_xinput_x64.cab', 'Apr2006_xinput_x86.cab', 'Aug2005_d3dx9_27_x64.cab', 'Aug2005_d3dx9_27_x86.cab', 'BDANT.cab', 'BDAXP.c
ab', 'DEC2006_XACT_x64.cab', 'DEC2006_XACT_x86.cab', 'DEC2006_d3dx10_00_x64.cab', 'DEC2006_d3dx10_00_x86.cab', 'DEC2006_d3dx9_32_x64.cab', 'DEC2006_d3dx9_32_x86.cab', '
DSETUP.dll', 'DXSETUP.exe', 'Dec2005_d3dx9_28_x64.cab', 'Dec2005_d3dx9_28_x86.cab', 'FEB2007_XACT_x64.cab', 'FEB2007_XACT_x86.cab', 'Feb2005_d3dx9_24_x64.cab', 'Feb2005
_d3dx9_24_x86.cab', 'Feb2006_XACT_x64.cab', 'Feb2006_XACT_x86.cab', 'Feb2006_d3dx9_29_x64.cab', 'Feb2006_d3dx9_29_x86.cab', 'JUN2006_XACT_x64.cab', 'JUN2006_XACT_x86.ca
b', 'JUN2007_XACT_x64.cab', 'JUN2007_XACT_x86.cab', 'JUN2007_d3dx10_34_x64.cab', 'JUN2007_d3dx10_34_x86.cab', 'JUN2007_d3dx9_34_x64.cab', 'JUN2007_d3dx9_34_x86.cab', 'J
un2005_d3dx9_26_x64.cab', 'Jun2005_d3dx9_26_x86.cab', 'Jun2008_X3DAudio_x64.cab', 'Jun2008_X3DAudio_x86.cab', 'Jun2008_XACT_x64.cab', 'Jun2008_XACT_x86.cab', 'Jun2008_X
Audio_x64.cab', 'Jun2008_XAudio_x86.cab', 'Jun2008_d3dx10_38_x64.cab', 'Jun2008_d3dx10_38_x86.cab', 'Jun2008_d3dx9_38_x64.cab', 'Jun2008_d3dx9_38_x86.cab', 'Mar2008_X3D
Audio_x64.cab', 'Mar2008_X3DAudio_x86.cab', 'Mar2008_XACT_x64.cab', 'Mar2008_XACT_x86.cab', 'Mar2008_XAudio_x64.cab', 'Mar2008_XAudio_x86.cab', 'Mar2008_d3dx10_37_x64.c
ab', 'Mar2008_d3dx10_37_x86.cab', 'Mar2008_d3dx9_37_x64.cab', 'Mar2008_d3dx9_37_x86.cab', 'NOV2007_X3DAudio_x64.cab', 'NOV2007_X3DAudio_x86.cab', 'NOV2007_XACT_x64.cab'
, 'NOV2007_XACT_x86.cab', 'Nov2007_d3dx10_36_x64.cab', 'Nov2007_d3dx10_36_x86.cab', 'Nov2007_d3dx9_36_x64.cab', 'Nov2007_d3dx9_36_x86.cab', 'OCT2006_XACT_x64.cab', 'OCT
2006_XACT_x86.cab', 'OCT2006_d3dx9_31_x64.cab', 'OCT2006_d3dx9_31_x86.cab', 'Oct2005_xinput_x64.cab', 'Oct2005_xinput_x86.cab', 'dsetup32.dll', 'dxdllreg_x86.cab', 'dxn
t.cab', 'dxupdate.cab']

diff .\war_01\help .\war_02\help
Identical files : ['help_de.html', 'help_en.html', 'help_es.html', 'help_fr.html', 'help_it.html']
Common subdirectories : ['css', 'images']

diff .\war_01\help\images .\war_02\help\images
Identical files : ['Title_Help_de.jpg', 'Title_Help_en.jpg', 'Title_Help_es.jpg', 'Title_Help_fr.jpg', 'Title_Help_it.jpg', 'btn.jpg', 'footer.jpg', 'header.jpg', 'lb.j
pg', 'lm.jpg', 'lt.jpg', 'mb.jpg', 'mt.jpg', 'rb.jpg', 'rm.jpg', 'rt.jpg']
Differing files : ['Thumbs.db']

diff .\war_01\help\css .\war_02\help\css
Identical files : ['styles.css']

The only interesting result is the two directory ".\war_01\data1" ; ".\war_02\data2" that are totally different. One other interesting file is the executable that we can find in ".\war_01\setup\war\war.exe" or ".\war_02\setup\war\war.exe".

We use ProtectionID in order to scan the file "war.exe", and we have the following result:

Scanning -> C:\Documents and Settings\vg4fun\Bureau\war.exe
File Type : 32-Bit Exe (Subsystem : Win GUI / 2), Size : 427094 (068456h) Byte(s)
-> File has 373846 (05B456h) bytes of appended data starting at offset 0D000h
[File Heuristics] -> Flag : 00000000000001001100000000100100 (0x0004C024)
[-= Installer =-] Inno Setup v5.2.1 Module

We now know that they use InnoSetup installer, we will use innounp (an innosetup unpacker) to unpack information related to the installation.

F:\Game\war_01\setup\war>C:\Users\vg4fun\Downloads\innounp040\innounp.exe -x -m war.exe
; Version detected: 5201
#3 {app}\user\UserSettings,1.xml
Reading slice F:\Game\war_01\setup\war\war.exe
#4 {app}\user\UserSettings,2.xml
#5 {app}\user\UserSettings,3.xml
#6 {app}\user\UserSettings,4.xml
#7 {app}\user\UserSettings,5.xml
#8 embedded\CompiledCode.bin
#9 embedded\WizardImage.bmp
#10 embedded\WizardSmallImage.bmp
#11 embedded\en.isl
#12 embedded\de.isl
#13 embedded\fr.isl
#14 embedded\it.isl
#15 embedded\es.isl
#16 install_script.iss

The interesting file "CompiledCode.bin", which is the code made by the RemObjects Pascal Script compiler can be extracted using ROPS Disassembler:

F:\Game\war_01\setup\war\embedded>C:\Users\vg4fun\Downloads\rops-3.0.53.935-disasm\disasm.exe CompiledCode.bin out.txt

In fact the only interesting "disassembly" stuff is:

 [48] CALC Base[2] + ['\..\..\data1\']
 [48] CALC Base[2] + ['\..\..\data2\']

All the installed files to the desired installation directory are stored inside the folder "data1" and "data2" from the respective ISO.

Listing of "data1" from war_01

F:\Game\war_01\data1>dir /s /b
F:\Game\war_01\data1\art.myp
F:\Game\war_01\data1\audio.myp
F:\Game\war_01\data1\binkw32.dll
F:\Game\war_01\data1\data.myp
F:\Game\war_01\data1\interface.myp
F:\Game\war_01\data1\libpatchui.dll
F:\Game\war_01\data1\mft.myp
F:\Game\war_01\data1\miles
F:\Game\war_01\data1\mss32.dll
F:\Game\war_01\data1\patch.cfg
F:\Game\war_01\data1\patch.html
F:\Game\war_01\data1\patch.myp
F:\Game\war_01\data1\patchui_win.dll
F:\Game\war_01\data1\pb
F:\Game\war_01\data1\video.myp
F:\Game\war_01\data1\video_french.myp
F:\Game\war_01\data1\video_german.myp
F:\Game\war_01\data1\video_italian.myp
F:\Game\war_01\data1\video_spanish.myp
F:\Game\war_01\data1\vo_english.myp
F:\Game\war_01\data1\WAR.exe
F:\Game\war_01\data1\war.jpg
F:\Game\war_01\data1\warpatch.exe
F:\Game\war_01\data1\miles\mssdolby.flt
F:\Game\war_01\data1\miles\mssds3d.flt
F:\Game\war_01\data1\miles\mssdsp.flt
F:\Game\war_01\data1\miles\msseax.flt
F:\Game\war_01\data1\miles\mssmp3.asi
F:\Game\war_01\data1\miles\msssrs.flt
F:\Game\war_01\data1\miles\mssvoice.asi
F:\Game\war_01\data1\pb\dll
F:\Game\war_01\data1\pb\htm
F:\Game\war_01\data1\pb\pbag.dll
F:\Game\war_01\data1\pb\pbcl.dll
F:\Game\war_01\data1\pb\pbns.dat
F:\Game\war_01\data1\pb\dll\wa001399.dll
F:\Game\war_01\data1\pb\dll\wc002056.dll
F:\Game\war_01\data1\pb\htm\wa001399.htm
F:\Game\war_01\data1\pb\htm\wc002056.htm

Listing of "data2" from war_02

F:\Game\war_02\data2>dir /s /b
F:\Game\war_02\data2\art2.myp
F:\Game\war_02\data2\world.myp

We can guess that directory ".\miles" is related to "The Miles Sound System", which is not interesting in our case.
And the directory ".\pb" is related to PunkBuster, the famous Anti-Cheat system.
DLLs "binkw32.dll" and "mss32.dll" are from "RAD Game Tools", again not interesting.
Everything else is related to the real game resources.

We will see in the next articles what is the purpose of all those files.

References