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:

>>> 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', '
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_d3dx9_', '', '', '', '', '', '', 'AU', '', '', '', '', '', 'Apr2005_', '', '', '', '', '', 'Apr2006_d3dx9_30', '', '', '', '', '', '', 'BDAXP.c
ab', '', '', '', '', '', '', '
DSETUP.dll', 'DXSETUP.exe', '', '', '', '', '', 'Feb2005', '', '', '', '', '', '
b', '', '', '', '', '', '', 'J', '', '', '', '', '', 'Jun2008_X', '', '', '', '', '', 'Mar2008_X3D', '', '', '', '', '', 'Mar2008_d3dx10_37_x64.c
ab', '', '', '', '', '', ''
, '', '', '', '', '', '', 'OCT', '', '', '', '', 'dsetup32.dll', '', 'dxn', '']

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-\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

Listing of "data2" from war_02

F:\Game\war_02\data2>dir /s /b

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.


Sunday, July 14, 2013

Dark Age of Camelot : Credentials plain text

After login screen, the login.dll executable will launch game.dll, with the function ShellExecute, but :
 .text:0041D608         push  offset password  
 .text:0041D60D         push  offset user_name  
 .text:0041D612         jz   short loc_41D640  
 .text:0041D614         movzx  ecx, byte_46254C[eax]  
 .text:0041D61B         push  ecx  
 .text:0041D61C         push  edx  
 .text:0041D61D         lea   eax, unk_4624CC[eax]  
 .text:0041D623         push  eax  
 .text:0041D624         push  offset name_dll  
 .text:0041D629         lea   ecx, [esp+3968h+CmdLine]  
 .text:0041D630         push  offset aSSDDSS ; "%s %s %d %d %s %s"  
 .text:0041D635         push  ecx       ; char *  
 .text:0041D636         call  _sprintf  
Yes login and password are passed on the command line in plain text ...

Saturday, July 13, 2013

Dark Age of Camelot : Connection

Today I wanted to understand the protocol for sending credentials to Dark Age of Camelot server.
Here is the schema :

When a client connect on a DAOC server, he will receive the following packet :
 [  0000]  1B 1B 00 08 00 65 00 00  00 11 00 00  
 [  0000]  1B 1B : Version 
 [  0002]  00 08 : Size  
 [  0004]  00 65 : Message Type   
 [  0006]  00 00 : Unknow  
 [  0008]  00 11 : Version Client ?(0x11)   
 [  000A]  00 00 : Unknow_02  
All the further communications will be encrypted using a rc4 key generated by the client :
  v0 = 0;  
   key[v0++] = rand();  
  while ( v0 < 59 );  
This key is then RSA encrypted and exchanged with the server.
DAOC client (client.dll), use a old version of libtomcrypt, here is an implementation of the code used for generating the buffer used to send the rc4 key to the server.
 unsigned char daoc_pub[] =   
      0x91, 0x00, 0x00, 0x00, 0x01, 0xC1, 0x00, 0x00, 0x00, 0x02, 0x15, 0xB3,  
      0x4E, 0xAF, 0x3A, 0x93, 0xA3, 0xC7, 0x4A, 0x6A, 0xFD, 0x69, 0x55, 0x45,  
      0x1D, 0x38, 0x6A, 0x8D, 0xA1, 0xDF, 0x70, 0x1F, 0x84, 0x93, 0x23, 0xE7,  
      0x95, 0x7F, 0xFD, 0xC5, 0x78, 0xCD, 0x42, 0x58, 0x71, 0x6B, 0xA4, 0xB5,  
      0x4D, 0xDD, 0xF1, 0xC6, 0xB9, 0xAE, 0xF2, 0x41, 0x65, 0xF7, 0xD9, 0x4D,  
      0x9C, 0xC5, 0xD6, 0xEE, 0x0D, 0x98, 0xFC, 0x23, 0x7E, 0x94, 0x84, 0xE2,  
      0xD1, 0x27, 0x8C, 0x67, 0xFC, 0xB6, 0x2C, 0x5D, 0xD6, 0x60, 0xA6, 0xA9,  
      0xC3, 0xA5, 0x04, 0x11, 0xFF, 0xFE, 0x9B, 0x90, 0x27, 0x69, 0x6A, 0x60,  
      0x1D, 0x89, 0x6F, 0xFD, 0x55, 0x96, 0x4A, 0xEA, 0x97, 0x34, 0x8F, 0x69,  
      0x79, 0xBF, 0x93, 0x26, 0x18, 0xB4, 0x7C, 0x7C, 0xD5, 0xAD, 0x0B, 0xC9,  
      0xC5, 0xB7, 0x8F, 0x06, 0xB4, 0x37, 0x67, 0x94, 0xE0, 0x2A, 0x7E, 0x38,  
      0x2F, 0x28, 0x60, 0x8A, 0xDC, 0x89, 0x7D, 0x08, 0xDD, 0xBE, 0x38, 0x34,  
      0xF5, 0x78, 0xD8, 0x81, 0x58, 0x9C, 0x2B, 0x03, 0x1A, 0xE0, 0xE3, 0xF3,  
      0x19, 0xE3, 0x63, 0x81, 0xE3, 0x7C, 0xE0, 0x5D, 0xBC, 0x8E, 0x9C, 0xDC,  
      0x93, 0x74, 0x24, 0xE0, 0xF4, 0x96, 0x65, 0xFA, 0x90, 0x21, 0x06, 0x03,  
      0xD2, 0x5A, 0xC3, 0x51, 0xBF, 0x5D, 0x03, 0xB2, 0xCD, 0xD3, 0xF1, 0x6E,  
      0xCB, 0xB0, 0x25, 0x71, 0x4B, 0xC6, 0x00, 0x44, 0x7A, 0xE7, 0x03, 0x00,  
      0x00, 0x00, 0x01, 0x00, 0x01  
 void daoc_rsa_encrypt_key(unsigned char *buf, int inlen, 
                  unsigned char *output, unsigned long *outlen)  
      rsa_key key;  
      int prng_idx;  
      prng_state statesprng;  
      int res;  
      unsigned char rsa_in[196];  
      unsigned long y, rsa_size;

      if (rsa_import(daoc_pub, sizeof (daoc_pub), &key) != CRYPT_OK)  
           fprintf(stderr, "[-] rsa_import() failed\n");  
      prng_idx = find_prng("sprng");  
      if (prng_idx == -1)   
           fprintf(stderr, "[-] find_prng() failed\n");  
      y = 193;  
      res = rsa_pad(buf, inlen, rsa_in, &y, prng_idx, &statesprng);  
      if (res != CRYPT_OK)  
           fprintf(stderr, "[-] rsa_pad() failed : %d\n", res);  
      rsa_size = *outlen - 2;  
      res = rsa_exptmod(rsa_in, y, output + 2, &rsa_size, PK_PUBLIC, &key);  
      if (res != CRYPT_OK)  
           fprintf(stderr, "[-] rsa_exptmod() failed : %d\n", res);  
      *output = 0;  
      *(output + 1) = rsa_size;  
      *outlen = rsa_size + 2;  
The client will send the rc4 key block encrypted with rsa, with the following type of packet :
 [ 0000] 1B 1B : Version   
 [ 0002] 00 C7 : Size   
 [ 0004] 01 54 : Message Type (ROL8(340))  
 [ 0006] 00    : Checksum (NULL because sym key is not yet set)   
 [ 0007] 00    : Sequence number of the paquet
 [ 0008] 00 C1 : Size encrypted RSA key  
 [ 000A] XX XX : RSA encrypted key  
Then the client can communicate the credentials of the user using the following paquet :
 [ 0000] 1B 1B : Version   
 [ 0002] 00 1D : Size   
 [ 0004] 01 2C : Message Type (ROL8(300))  
 [ 0006] 21    : Checksum  
 [ 0007] 01    : Sequence number of the paquet  
 [ 0008] 00 06 : Name Size  
 [ 000A] XX XX : Str_Name[Name Size]  
 [ 00XX] 00 02 : Password Size  
 [ 00XX] XX XX : Str_Password[Password Size]  
 [ 00XX] 00 09 : Unknow  
 [ 00XX] 00    : Unknow_02  
This paquet will be encrypted using rc4 algorithm and the key generated, but not on all paquet ! It will start at position 0x0006.

Creating library signatures for IDA

 C:\ida\flair61\bin\win>pcf tomcrypt.lib  
 tomcrypt.lib: skipped 25, total 566  
 C:\ida\flair61\bin\win>sigmake.exe tomcrypt.pat tomcrypt  
 tomcrypt.sig: modules/leaves: 541/485, COLLISIONS: 3  
 See the documentation to learn how to resolve collisions.  
Resolve collisions and it's ok (collisions problems can be found in file *.exc)

Thursday, July 11, 2013

IDA : Add structur

Declaring field of structure when they are big is a pain in the ass into IDA, idapython to the rescue !
 size = 4242  
 struc_id = GetStrucIdByName(name)  
 struct = AddStrucEx(-1, name, 0)  
 for i in xrange(0, size / 4):  
      AddStrucMember(struct, "field_" + str(i), i * 4, 0x20000400, -1, 4)  
 if (size % 4) != 0:  
      AddStrucMember(struct,"field_" + str(size / 4),(size / 4) * 4,0x000400,-1,size % 4)  

Wednesday, July 10, 2013

Age Of Empires III : Resources anti-cheat

If one day, you try to scan the memory of age3.exe for finding your resources like wood, or food, you will not find them in memory, because they use a lame "anti-cheat" technique.
All the value are xored with those different "secret" dword :
 .data:00BBE74C Key_XOR_Resources dd 2848AC4Fh            ; // GOLD  
 .data:00BBE750         dd 94F83A35h      ; // WOOD  
 .data:00BBE754         dd 8BD84C3Fh      ; // meat  
 .data:00BBE758         dd 0AB12FBAFh     ; ??
 .data:00BBE75C         dd 20B35BCAh      ; ??
 .data:00BBE760         dd 0F9ABC42Ah     ; // XP  
 .data:00BBE764         dd 0B1A1CFDAh     ; // LEVEL  
 .data:00BBE768         dd 0F2E48210h  

Wednesday, July 3, 2013

Anno 1602

This post is an introduction to the game Anno 1602, that I'm going to study during the next blog post.

Anno 1602: Creation of a New World is a game with both real time strategyand city building elements, developed by Max Design. It's a game like "Caesar", "Colonization", "Constructor", "Exploration".
This is game is an Abandonware, so you can download it for free.
 PS C:\> Get-ChildItem "C:\Games\ANNO 1602 Version Gold" -Include *.* -Recurse | Select-Object Extension | Sort-Object -Property Extension -Unique  

  • .BSH
  • .cod
  • .COL
  • .dat
  • .dll
  • .EXE
  • .GAD
  • .INC
  • .pdf
  • .rtf
  • .SCP
  • .smk
  • .szm
  • .szs
  • .WAV
  • .ZEI