Discovering Zip password with signature search

05-06-2017 - 4 minutes, 27 seconds -
reverse engineering

Introduction

After playing an interesting RTS game Syrian Warfare, I wanted to take a look at the internals of this game. Curiously, the game files with extension .pak were actually zip archives that contained ZipCrypto encrypted files. The developers of the game stated that in the future modding tools would be released, but I took it as a challenge to figure out the password.

Signature search

I used signsrch to scan the game's DLLs for ZipCrypto signatures. This tools searches binary files for specific byte patterns, and in case of ZipCrypto the byte patterns are integer constants in the password update function. In one of the DLLs, zfs.dll, the following signatures were found:

C:\Users\sbobovyc\Tools\signsrch>signsrch.exe -e "C:\Steam\steamapps\common\Syri
an Warfare\bin\zfs.dll"

Signsrch 0.2.4
by Luigi Auriemma
e-mail: aluigi@autistici.org
web:    aluigi.org
  optimized search function by Andrew http://www.team5150.com/~andrew/
  disassembler engine by Oleh Yuschuk

- open file "C:\Steam\steamapps\common\Syrian Warfare\bin\zfs.dll"
- 90112 bytes allocated
- load signatures
- open file C:\Users\sbobovyc\Tools\signsrch\signsrch.sig
- 3075 signatures in the database
- start 4 threads
- start signatures scanning:

  offset   num  description [bits.endian.size]
  --------------------------------------------
  100021a6 3052 function where is handled the ZipCrypto password [32.le.12&]
  1000220e 1847 Zip Crypto [32.le.16&]
  1000e328 641  CRC-32-IEEE 802.3 [crc32.0x04c11db7 le rev int_min.1024]
  1000e328 648  CRC-32-IEEE 802.3 [crc32.0xedb88320 lenorev 1.1024]
  100130ca 2545 anti-debug: IsDebuggerPresent [..17]
  100172b0 3032 PADDINGXXPADDING [..16]

- 6 signatures found in the file in 0 seconds
- done

The function where the ZipCrypto password is handled in the DLL looks like this: zipcrypto_update_keys

Compare with a C++ implementation of the password update function found in CryptoSetPassword() in ZipCrypto.cpp:

STDMETHODIMP CCipher::CryptoSetPassword(const Byte *password, UInt32 passwordLen)
{
  Keys[0] = 0x12345678;
  Keys[1] = 0x23456789;
  Keys[2] = 0x34567890;
  UInt32 i;
  for (i = 0; i < passwordLen; i++)
    UpdateKeys(password[i]);
  for (i = 0; i < 3; i++)
    Keys2[i] = Keys[i];
  return S_OK;
}

Starting the game with a debugger caused the game to crash. The game executable (SyrianWarfare.exe) resides in bin directory and is normally started by start.cmd batch script. After further investigation, I realized that the game looks for files in the current working directory which of course differs based on whether it's started by the batch script or by a debugger. Copying all the pak, war, and txt files into /bin fixed the crash problem.

$ tree -L 1
.
├── _CommonRedist
├── basis
├── bin
├── effects.war
├── effectstex.war
├── lands.pak
├── lang.pak
├── langs.pak
├── main.pak
├── models.pak
├── OST
├── sounds.pak
├── start.cmd
├── steam_appid.txt
├── textures.pak
└── workdir.root

Once the game started up inside the debugger, I let it run so that the DLL would be loaded into memory. I then found the address of the function which signsrch found earlier, put a breakpoint on the function entry, restarted the game and let the game execute till the breakpoint was hit.

zip_crypto_password

Examining the values in the registers at the breakpoint, it is clear that register ESI contains the plaintext password needed to decrypt the files in lands.pak. The same password is used to decrypt files in every other .pak file. Having what I wanted, I became curious if I could find the password in game memory. Searching the game memory yielded zero results. I backtraced the execution from the "CryptoSetPassword" function in zfs.dll to a place in SyrianWarfare.exe where the obfuscated password is de-obfuscated. The obfuscated password m,ew0rdk1a;ldsdj is stored in the .rdata section of the executable.

zip_crypto_obfuscated

Looking at the code listing, one can see that in lines 10 and 16, the third and tenth characters are substituted with n and s while at line 22 the fifteenth character is decremented by one resulting in the plaintext password of m,nw0rdk1s;ldscj.

00408A01 | 68 08 8D 41 00           | push syrianwarfare.418D08                                             | 418D08:&"m,ew0rdk1a;ldsdj"
00408A06 | 8D 4C 24 20              | lea ecx,dword ptr ss:[esp+20]                                         |
00408A0A | FF 15 E8 12 41 00        | call dword ptr ds:[<&stlp_std::basic_string<char,stlp_std::char_trait |
00408A10 | 8D 54 24 2C              | lea edx,dword ptr ss:[esp+2C]                                         | [esp+2C]:".pak"
00408A14 | C7 44 24 70 00 00 00 00  | mov dword ptr ss:[esp+70],0                                           |
00408A1C | 8D 44 24 1C              | lea eax,dword ptr ss:[esp+1C]                                         |
00408A20 | 39 54 24 30              | cmp dword ptr ss:[esp+30],edx                                         |
00408A24 | 74 04                    | je syrianwarfare.408A2A                                               |
00408A26 | 8B 44 24 1C              | mov eax,dword ptr ss:[esp+1C]                                         |
00408A2A | C6 40 02 6E              | mov byte ptr ds:[eax+2],6E                                            | 6E:'n'
00408A2E | 8D 44 24 2C              | lea eax,dword ptr ss:[esp+2C]                                         | [esp+2C]:".pak"
00408A32 | 39 44 24 30              | cmp dword ptr ss:[esp+30],eax                                         |
00408A36 | 8D 44 24 1C              | lea eax,dword ptr ss:[esp+1C]                                         |
00408A3A | 74 04                    | je syrianwarfare.408A40                                               |
00408A3C | 8B 44 24 1C              | mov eax,dword ptr ss:[esp+1C]                                         |
00408A40 | C6 40 09 73              | mov byte ptr ds:[eax+9],73                                            | 73:'s'
00408A44 | 8D 4C 24 2C              | lea ecx,dword ptr ss:[esp+2C]                                         | [esp+2C]:".pak"
00408A48 | 8D 44 24 1C              | lea eax,dword ptr ss:[esp+1C]                                         |
00408A4C | 39 4C 24 30              | cmp dword ptr ss:[esp+30],ecx                                         |
00408A50 | 74 04                    | je syrianwarfare.408A56                                               |
00408A52 | 8B 44 24 1C              | mov eax,dword ptr ss:[esp+1C]                                         |
00408A56 | FE 48 0E                 | dec byte ptr ds:[eax+E]                                               |
00408A59 | 6A 24                    | push 24                                                               |
00408A5B | E8 F4 55 00 00           | call <syrianwarfare.operator new>                                     |

Notes

signsrch http://aluigi.altervista.org/mytoolz.htm Retrieving ZIP passwords from games - the debugger way http://zenhax.com/viewtopic.php?f=4&t=59