CS.RIN.RU - Steam Underground Community
http://cs.rin.ru/forum/

Counter-Strike Online
http://cs.rin.ru/forum/viewtopic.php?f=15&t=49738
Page 2 of 7

Author:  d4op [ Wednesday, 23 Jul 2008, 19:45 ]
Post subject:  Re: Counter-Strike Online

... The registraion for beta users is full.

Author:  RessourectoR [ Thursday, 24 Jul 2008, 02:40 ]
Post subject:  Re: Counter-Strike Online

The whole idea of this game is kinda silly.
Oh noes, we azns suck at fps games, we want rpg and rts, so we turn Counter-Strike into an rpg!
lol, no offense to asians. ::D

Author:  KeReMiD4O [ Thursday, 24 Jul 2008, 09:48 ]
Post subject:  Re: Counter-Strike Online

Yeah so true...
So any progress on opening the nar files? Or anything on the game xD

Author:  d4op [ Thursday, 24 Jul 2008, 15:53 ]
Post subject:  Re: Counter-Strike Online

not really

4E 41 52 00 -> Start of NAR file
00 00 00 01 -> File version

76 40 4E 41 52 00 -> end of NAR file

Author:  ChrisTX [ Friday, 25 Jul 2008, 00:52 ]
Post subject:  Re: Counter-Strike Online

lol, so CS:O = CS:CZ + new weapons + new shiny UI + game guard + NAR + War3FT Mod? :lol:

Author:  Tom [ Friday, 25 Jul 2008, 02:37 ]
Post subject:  Re: Counter-Strike Online

CSO is War Rock =P

well, if the chinese like it, let them play it, i also have an account, i like it.

Bt either way i get 200-400 ping XD

Author:  RBPFC1 [ Friday, 25 Jul 2008, 09:04 ]
Post subject:  Re: Counter-Strike Online

Will debug the NAR Format today :p

Author:  Nyerk! [ Friday, 25 Jul 2008, 21:33 ]
Post subject:  Re: Counter-Strike Online

Tom: yes, 300 ping is normal .. but i must say, its still playable... but there is another problem, i cant setup rate, cl_cmdrate etc :( stupid console

RBPFC1: nice! i hope you find a way how edit them :)

Author:  Da_FileServer [ Saturday, 26 Jul 2008, 03:06 ]
Post subject:  Re: Counter-Strike Online

OK, here are my findings. Note that all data is stored in little endian, unless otherwise specified.

1. Magic Field - Read first 4 bytes of file and make sure they are equal to 'NAR\0' (or 0x0052414E).
2. Version Field - Read the next 4 bytes of file and make sure they are equal to 0x01000000 (or 0x00000001 using big-endian).
3. Seek to end of file.
4. End Magic Field - Seek back 4 bytes and check to make sure they are equal to 'NAR\0' (or 0x0052414E).
5. Header Length - Seek back another 4 bytes and XOR that with 0x4076551F.
6. Encoded & Compressed Directory - Seek back Header Length bytes and read Header Length bytes.
7. Compressed Directory - XOR the Encoded & Compressed Directory using the following repeating byte pattern: 0x19, 0x5B, 0x7B, 0x2C, 0x65, 0x5E, 0x79, 0x25, 0x6E, 0x4B, 0x07, 0x21, 0x62, 0x7F, 0x00, 0x29.
8. Directory - Use bzip2 to decompress the Compressed Directory (the data matches the bzip2 file format).

EDIT: Here is some information about the compressed & encoded directory. Once it has been decoded and decompressed, this is the file format:
[code=text=c]
  1. enum DirectoryEntryType
  2. {
  3.     Raw = 0,
  4.     Encoded = 1,
  5.     EncodedAndCompressed = 2,
  6. };
  7.  
  8. struct DirectoryHeader
  9. {
  10.     DWORD version; // always 1
  11.     DWORD type; // always 0 (not sure)
  12.     DWORD directory_size; // size of following DirectoryEntries data
  13.     DWORD is_readable; // always 1 (not sure)
  14. };
  15. struct DirectoryEntries
  16. {
  17.     DWORD entry_count;
  18.     struct DirectoryEntry
  19.     {
  20.         USHORT name_size;
  21.         CHAR name[name_size];
  22.         DWORD type; // use DirectoryEntryType as reference
  23.         DWORD offset; // absolute offset in archive
  24.         DWORD compressed_size; // size in archive
  25.         DWORD uncompressed_size; // size of extracted file
  26.         DWORD last_modified_time; // appears to be a time_t value
  27.         DWORD crc32; // crc32 of the data as stored currently in archive
  28.     } entries[entry_count];
  29. };
[/code]
I haven't figured out how the files are encoded yet. But I'm pretty close to completely breaking it.

EDIT2: Figured out what the unknown was before the CRC32. It's a simple time_t value (seconds since 1970-01-01). It's most likely the last modified time.

EDIT3: Yay, I think I finally got it. After 12 hours of disassembly I think I've cracked the file format. The files are encoded with an interesting XOR cipher, which is based of off the file path.
[code=text=c]
  1. // This code is rough, but should do the job.
  2. unsigned int python_hash(const unsigned char *data, unsigned int length)
  3. {
  4.     unsigned int hash = 0;
  5.     unsigned int i;
  6.     for (i = 0; i < length; ++i)
  7.     {
  8.         hash = (hash * 1000003) ^ data[i];
  9.     }
  10.     return (hash ^ length);
  11. }
  12. void generate_xor_key(const char *filename, unsigned int filename_length, unsigned char key[16])
  13. {
  14.     unsigned int seed = python_hash((unsigned char *)filename, filename_length);
  15.     // This is a random number generator, using the above hash as a seed.
  16.     unsigned int i;
  17.     for (i = 0; i < 16; ++i)
  18.     {
  19.         seed = (seed * 0x41C64E6D) + 12345;
  20.         // Steal the first byte from seed and store into result.
  21.         key[i] = seed & 0xFF;
  22.     }
  23. }
[/code]
So you call generate_xor_key with the filename exactly as it's stored in the directory entry structure (e.g., "/resource/bg_w415t203.tga" with a length of 25, no null terminator). Then simply XOR the encoded data over and over again using the key for each 16-byte chunk (or less if at end of file) until you've decoded the whole file.

See later posts for decompression code. Note that the data must be decoded first, and then decompressed.

PS: Thanks to the people who supplied resource.nar and filesystem_nar.dll and RBPFC1 for some extended information. This was quite challenging.

Author:  RBPFC1 [ Saturday, 26 Jul 2008, 16:13 ]
Post subject:  Re: Counter-Strike Online

   DWORD unknown; // 1
   DWORD unknown; // 0
   DWORD directory_size; // number of bytes in directory minus 16
   DWORD unknown; // 1


   DWORD DirectoryVersion; // 1
   DWORD DirectoryType; // Can be 0 or 1 havent figured out waht they exactly mean
   DWORD directory_size; // number of bytes in directory minus 16
   DWORD bIsReadable; // If this file is actually allowed for current reading, i think its for their internal update system.


There are still some open parts for me but i have my eyes on it :p

Edit: Those 16 bytes seem to be a custom checksum for the Directory Table.
It is using some xor stuff too but atm I'm a bit stuck at that part.

It seems to calculate like CRC using bit movement and then xor it via a value to 16 bytes. Once I got it i will post it here.

Edit 2: You forgot to say actually what data ;) "5. XOR that data using the following repeating byte pattern: 0x19, 0x5B, 0x7B, 0x2C, 0x65, 0x5E, 0x79, 0x25, 0x6E, 0x4B, 0x07, 0x21, 0x62, 0x7F, 0x00, 0x29."

So I will do it. The Data Length is actually the Data between Position from Offset and the value from Step 3. Because you will move Actual Position - Data Length.

Edit 3: Heres the class I've wrote so far: http://rafb.net/p/mZRIeM89.html Its not release ready so don't tell me its ugly :D

Author:  Da_FileServer [ Saturday, 26 Jul 2008, 22:38 ]
Post subject:  Re: Counter-Strike Online

I updated my post above to make it easier to understand (I also added a couple of steps in the beginning that you, RBPFC1, and others probably didn't automatically assume). And EDIT3 describes how the files are encoded (if they're encoded). Though I need more archive files to be able to analyze this format more. (I'll download CSO sometime, but it's damn slow.)

Author:  RBPFC1 [ Sunday, 27 Jul 2008, 09:42 ]
Post subject:  Re: Counter-Strike Online

I've checked cstrike.nar and it seems the FileType is always 1. But it doesnt look like Bzip2 this time, since the Data does not start with "BZh"

Edit: If the FileType is 1 it seems that Compressed Size matches the Original File Size. So FileType 1 is basicly Encoded only.

Edit 2: If FileType is 0 It is RAW Data like u described but about the FileType 1 I'm not sure, it doesnt seem to have a bzip2 header.

Edit 3:
bool Decompress(char * szDecompress, unsigned int uBufSize, const char * cszData, unsigned int uDataSize)
{
   char * szData = (char*)cszData;
   char * szBuffer = szDecompress;
   char * szBuf = szBuffer;

   unsigned int uReadLength = ((*szData++ & 0x1F) + 1);
   unsigned int uReadOffset = 0;

   memcpy(szBuffer, szData, uReadLength);
   szBuffer += uReadLength;
   szData += uReadLength;
   szBuf += uReadLength;

   while( (unsigned int)(szData - cszData) < uDataSize )
   {
      // These will describe what Data have to be copied to the current Location.
      uReadLength = ((*szData++ >> 5) & 0x1F) + 2;

      // TODO: Fix Me!
      // Bogus Part: 33 + 1 = 44 & 0x1F = 2
      uReadOffset = ((*szData++ + 1) & 0x1F);

      // Copy Data from existing Data to current Position.
      szBuffer -= uReadOffset;
      memcpy(szBuf, szBuffer, uReadLength);
      szBuf += uReadLength;
      szBuffer = szBuf;

      // Read the next Data.
      uReadLength = ((*szData++ & 0x1F) + 1);
      memcpy(szBuffer, szData, uReadLength);
      szBuffer += uReadLength;
      szData += uReadLength;
      szBuf += uReadLength;
   }

   return true;
}


This code will decompress the Data, but my code is a bit bogus I cant figure out what to do on the Bogus Part. It works till the *szData has the value 33.
If anyone can help hook me up :P

Author:  Da_FileServer [ Tuesday, 29 Jul 2008, 03:57 ]
Post subject:  Re: Counter-Strike Online

Here is a fully working decompressor for anybody who wants to use it (credits to RBPFC and me are highly appreciated):
[code=text=c]
  1. void Decompress(const BYTE *lpSource, DWORD nSourceLength, BYTE *lpDestination, DWORD lpDestinationLength)
  2. {
  3.     // This is an LZ77-based compression algorithm. I haven't been able to
  4.     // figure out if it's a known implementation or a proprietary one. (It
  5.     // does not match deflate, LZMA, or LZW.)
  6.  
  7.     ATLENSURE(lpSource != 0);
  8.     ATLENSURE(lpDestination != 0);
  9.  
  10.     const BYTE *const lpSourceEndPosition = lpSource + nSourceLength;
  11.     const BYTE *lpSourcePosition = lpSource;
  12.  
  13.     BYTE *const lpDestinationEndPosition = lpDestination + lpDestinationLength;
  14.     BYTE *lpDestinationPosition = lpDestination;
  15.  
  16.     while ((lpSourcePosition + 2) <= lpSourceEndPosition &&
  17.         lpDestinationPosition < lpDestinationEndPosition)
  18.     {
  19.         USHORT uReadOperation = *lpSourcePosition >> 5;
  20.         USHORT uReadLength = *lpSourcePosition & 0x1F;
  21.         ++lpSourcePosition;
  22.         if (uReadOperation == 0)
  23.         {
  24.             ++uReadLength;
  25.  
  26.             ATLENSURE((lpSourcePosition + uReadLength) <= lpSourceEndPosition);
  27.             const BYTE *const lpCopyEndPosition = lpDestinationPosition + uReadLength;
  28.             ATLENSURE(lpCopyEndPosition <= lpDestinationEndPosition);
  29.             while (lpDestinationPosition != lpCopyEndPosition)
  30.                 *(lpDestinationPosition++) = *(lpSourcePosition++);
  31.         }
  32.         else
  33.         {
  34.             if (uReadOperation == 7)
  35.                 uReadOperation += *(lpSourcePosition++);
  36.             uReadOperation += 2;
  37.  
  38.             ATLENSURE((lpSourcePosition + 1) <= lpSourceEndPosition);
  39.             uReadLength = ((uReadLength << 8) | *(lpSourcePosition++)) + 1;
  40.             ATLENSURE((lpDestination + uReadLength) <= lpDestinationPosition);
  41.  
  42.             const BYTE *const lpCopyEndPosition = lpDestinationPosition + uReadOperation;
  43.             ATLENSURE(lpCopyEndPosition <= lpDestinationEndPosition);
  44.             const BYTE *lpCopyPosition = lpDestinationPosition - uReadLength;
  45.             while (lpDestinationPosition != lpCopyEndPosition)
  46.                 *(lpDestinationPosition++) = *(lpCopyPosition++);
  47.         }
  48.     }
  49.  
  50.     ATLASSERT(lpSourcePosition == lpSourceEndPosition);
  51.     ATLASSERT(lpDestinationPosition == lpDestinationEndPosition);
  52. }
[/code]

Author:  KeReMiD4O [ Tuesday, 29 Jul 2008, 17:15 ]
Post subject:  Re: Counter-Strike Online

How does it work :laughing:

Author:  Steve Jobs [ Tuesday, 29 Jul 2008, 17:24 ]
Post subject:  Re: Counter-Strike Online

This topic is fucking leet. RBPFC1 told that he is going to release a library. I hope someone can make a GUI for this.

Page 2 of 7 All times are UTC + 3 hours
Powered by phpBB® Forum Software © phpBB Group
https://www.phpbb.com/