love&war ( .wdf )

Extraction and unpacking of game archives and compression, encryption, obfuscation, decoding of unknown files
yianti
Posts: 8
Joined: Tue Dec 16, 2014 5:27 am

love&war ( .wdf )

Post by yianti »

Hey guys,
here is a 2D game ,name is: love & war
Hope someone can help me to look at the WDF files.

http://www.mediafire.com/download/fcw6h ... 4z/res.zip

Very Very Thanks~
yianti
Posts: 8
Joined: Tue Dec 16, 2014 5:27 am

Re: love&war ( .wdf )

Post by yianti »

help me ,plz!
yianti
Posts: 8
Joined: Tue Dec 16, 2014 5:27 am

Re: love&war ( .wdf )

Post by yianti »

PLZ,i will pay for it~anybody help me~
aluigi
Site Admin
Posts: 12984
Joined: Wed Jul 30, 2014 9:32 pm

Re: love&war ( .wdf )

Post by aluigi »

It seems a chaotic format at a first look: no tables, no known file formats, sparse information.
Ekey
Posts: 1383
Joined: Sat Aug 09, 2014 2:34 pm

Re: love&war ( .wdf )

Post by Ekey »

This game using WindSoul Engine. Archives don't contains file names (only hashes) + encrypted tables and data.
yianti
Posts: 8
Joined: Tue Dec 16, 2014 5:27 am

Re: love&war ( .wdf )

Post by yianti »

Thank you for replys
Is there any way to extract it?
here is the game : http://downloadlz.shangyoo.com/agame/LZ ... 150825.exe
I will donate if you can help me~
thanks!!!
Ekey
Posts: 1383
Joined: Sat Aug 09, 2014 2:34 pm

Re: love&war ( .wdf )

Post by Ekey »

Header always 16 bytes and encrypted. Encryption is classical random seed + xor. Here terrible code

Code: Select all

static unsigned long pKey[3] = {
       0x00000000, 0xDEADBEEF, 0xBEEFDEAD};


Code: Select all

int Decrypt_Core(int mKey, int pBuffer, int dwSize)
{
  int result;
  int mSize;
  char nResult;
  unsigned int dwSeed;

  result = pBuffer;
  mSize = dwSize - 1;
  if ( dwSize != 1 )
  {
    do
    {
      nResult = *(BYTE *)result;
      dwSeed = 0x343FD * *(DWORD *)(mKey + 8) + 0x269EC3;
      *(DWORD *)(mKey + 8) = dwSeed;
      ++result;
      *(BYTE *)(result - 1) ^= *(BYTE *)(*(DWORD *)mKey + mKey + 4) + (unsigned __int8)(dwSeed >> 16);
      *(BYTE *)(*(DWORD *)mKey + mKey + 4) += nResult;
      --mSize;
      *(DWORD *)mKey = ((unsigned __int8)*(DWORD *)mKey + 1) & 3;
    }
    while ( mSize );
  }
  return result;
}


Decrypt header

Code: Select all

Decrypt_Core((int)pKey, (int)pBuffer, 16);


Header after:

Code: Select all

struct WDFHeader
{
   uint32_t   dwID; //PFDW
   uint32_t   dwUnknown; //Posible hash or version
   uint32_t   dwTotalFiles;
   uint32_t   dwTableOffset; //Note: Table also encrypted
};


Decrypt entry table

Code: Select all

void DecryptTable(unsigned char *pScrBuffer, int dwSize)
{   DWORD dwBlockSize = 16;
    DWORD dwNext = 0;
    DWORD dwBlocks = dwSize / dwBlockSize;

    for (DWORD i = 0; i < dwBlocks; i++)
    {
        Decrypt_Core((int)pKey, (int)pScrBuffer + dwNext, 16);
        dwNext = dwNext + 16;
    }
}


Entry after

Code: Select all

struct WDFEntry
{
   uint32_t   dwHash; //Hash of file name
   uint32_t   dwSize;
   uint32_t   dwOffset;
   uint32_t   dwNull;
};


Hash algorithm

Code: Select all

int WindSoul_hash_v2(const char *mString, unsigned int dwLength, int dwFlag) {
    unsigned int dwStrSize = dwLength;
    unsigned int dwConstant0 = 0x9E3779B9;
    unsigned int dwConstant3 = dwConstant0;
    unsigned int dwHash;
    unsigned int dwSeed;
    dwSeed = dwLength;
    if (dwLength >= 12) {
        unsigned int dwConstant1 = 0xAAAA;
        unsigned int dwConstant2 = 0xAAAB;
        dwConstant1 *= dwLength;
        dwConstant2 *= dwLength;
        dwConstant2 >>= 0x10;
        dwConstant1 += dwConstant2;
        dwConstant1 >>= 0x13;
        unsigned int k = dwConstant1;
        p1:;
        dwHash = mString[7];
        dwSeed = mString[6];
        dwHash <<= 8;
        dwHash += dwSeed;
        dwSeed = mString[5];
        dwHash <<= 8;
        dwHash += dwSeed;
        dwSeed = mString[4];
        dwSeed += dwConstant0;
        dwHash <<= 8;
        dwConstant0 = dwHash + dwSeed;
        dwHash = mString[11];
        dwSeed = mString[10];
        dwHash <<= 8;
        dwHash += dwSeed;
        dwSeed = mString[9];
        dwHash <<= 8;
        dwHash += dwSeed;
        dwSeed = mString[8];
        dwSeed += dwFlag;
        dwHash <<= 8;
        dwFlag = dwHash + dwSeed;
        dwHash = mString[3];
        dwSeed = mString[2];
        dwHash <<= 8;
        dwHash += dwSeed;
        dwSeed = mString[1];
        dwHash <<= 8;
        dwHash += dwSeed;
        dwSeed = mString[0];
        dwHash <<= 8;
        dwHash += dwSeed;
        dwHash -= dwFlag;
        dwHash -= dwConstant0;
        dwConstant0 -= dwFlag;
        dwSeed = dwFlag;
        dwSeed >>= 0x0D;
        dwHash += dwConstant3;
        dwHash ^= dwSeed;
        dwConstant0 -= dwHash;
        dwSeed = dwHash;
        dwSeed <<= 8;
        dwConstant0 ^= dwSeed;
        dwFlag -= dwConstant0;
        dwFlag -= dwHash;
        dwSeed = dwConstant0;
        dwSeed >>= 0x0D;
        dwFlag ^= dwSeed;
        dwHash -= dwFlag;
        dwHash -= dwConstant0;
        dwSeed = dwFlag;
        dwSeed >>= 0x0C;
        dwHash ^= dwSeed;
        dwConstant0 -= dwFlag;
        dwConstant0 -= dwHash;
        dwSeed = dwHash;
        dwSeed <<= 0x10;
        dwConstant0 ^= dwSeed;
        dwFlag -= dwConstant0;
        dwFlag -= dwHash;
        dwSeed = dwConstant0;
        dwSeed >>= 0x5;
        dwFlag ^= dwSeed;
        dwHash -= dwFlag;
        dwHash -= dwConstant0;
        dwSeed = dwFlag;
        dwSeed >>= 0x03;
        dwHash ^= dwSeed;
        dwConstant0 -= dwFlag;
        dwConstant3 = dwHash;
        dwConstant0 -= dwConstant3;
        dwHash <<= 0x0A;
        dwConstant0 ^= dwHash;
        dwFlag -= dwConstant0;
        dwSeed = dwConstant0;
        dwFlag -= dwConstant3;
        dwSeed >>= 0x0F;
        dwFlag ^= dwSeed;
        mString += 0x0C;
        dwLength -= 0x0C;
        k--;
        if (k != 0)
            goto p1;
        dwSeed = dwStrSize;
    }
    dwLength--;
    dwFlag += dwSeed;
    switch (dwLength) {
        case 10:
         dwHash = mString[10];
         dwHash <<= 0x18;
         dwFlag += dwHash;
        case 9:
         dwSeed = mString[9];
         dwSeed <<= 0x10;
         dwFlag += dwSeed;
        case 8:
         dwHash = mString[8];
         dwHash <<= 0x08;
         dwFlag += dwHash;
        case 7:
         dwSeed = mString[7];
         dwSeed <<= 0x18;
         dwConstant0 += dwSeed;
        case 6:
         dwHash = mString[6];
         dwHash <<= 0x10;
         dwConstant0 += dwHash;
        case 5:
         dwSeed = mString[5];
         dwSeed <<= 0x08;
         dwConstant0 += dwSeed;
        case 4:
         dwHash = mString[4];
         dwConstant0 += dwHash;
        case 3:
         dwSeed = mString[3];
         dwSeed <<= 0x18;
         dwConstant3 += dwSeed;
        case 2:
         dwHash = mString[2];
         dwHash <<= 0x10;
         dwConstant3 += dwHash;
        case 1:
         dwSeed = mString[1];
         dwSeed <<= 0x08;
         dwConstant3 += dwSeed;
        case 0:
         dwHash = mString[0];
         dwConstant3 += dwHash;
        default:
         unsigned int c;
         dwConstant3 -= dwFlag;
         dwConstant3 -= dwConstant0;
         dwConstant0 -= dwFlag;
         c = dwFlag;
         c >>= 0x0D;
         dwConstant3 ^= c;
         dwConstant0 -= dwConstant3;
         dwSeed = dwConstant3;
         dwSeed <<= 0x08;
         dwConstant0 ^= dwSeed;
         dwFlag -= dwConstant0;
         dwFlag -= dwConstant3;
         dwHash = dwConstant0;
         dwHash >>= 0x0D;
         dwFlag ^= dwHash;
         dwConstant3 -= dwFlag;
         dwConstant3 -= dwConstant0;
         dwConstant0 -= dwFlag;
         c = dwFlag;
         c >>= 0x0C;
         dwConstant3 ^= c;
         dwConstant0 -= dwConstant3;
         dwSeed = dwConstant3;
         dwSeed <<= 0x10;
         dwConstant0 ^= dwSeed;
         dwFlag -= dwConstant0;
         dwHash = dwConstant0;
         dwFlag -= dwConstant3;
         dwHash >>= 0x05;
         dwFlag ^= dwHash;
         dwHash = dwFlag;
         dwConstant3 -= dwHash;
         dwConstant3 -= dwConstant0;
         c = dwHash;
         dwConstant0 -= dwHash;
         c >>= 0x03;
         dwConstant3 ^= c;
         dwConstant0 -= dwConstant3;
         dwSeed = dwConstant3;
         dwSeed <<= 0x0A;
         dwConstant0 ^= dwSeed;
         dwHash -= dwConstant0;
         dwHash -= dwConstant3;
         dwConstant0 >>= 0x0F;
         dwHash ^= dwConstant0;
    }
    return dwHash;
}


Example:

Code: Select all

unsigned int dwHash = WindSoul_hash_v2("engine/excludewdf.csv", 21, 0);
printf("%08X\n", dwHash);


Code: Select all

Name: engine/excludewdf.csv
Hash: 0x47333B24


It all what you need to know :). Files data in provided archives not encrypted also not compressed.
Last edited by Ekey on Mon Aug 31, 2015 7:04 pm, edited 2 times in total.
Ekey
Posts: 1383
Joined: Sat Aug 09, 2014 2:34 pm

Re: love&war ( .wdf )

Post by Ekey »

Anyway. Here my tool for unpack.

Image

Notes:
1) Hash function can't work with unicode strings (most file names with korean symbols -> ~90% of all)
2) No detecting types for unknown files

Thats all what i can do.
yianti
Posts: 8
Joined: Tue Dec 16, 2014 5:27 am

Re: love&war ( .wdf )

Post by yianti »

thanks Ekey,
I try to rename the Unknown file to *.png *.dds...
I found these files can be look!
Can you help me to add these format?
thanks for you help! Plz give me you paypal ID!
thanks!!!
Ekey
Posts: 1383
Joined: Sat Aug 09, 2014 2:34 pm

Re: love&war ( .wdf )

Post by Ekey »

yianti wrote:thanks Ekey,
I try to rename the Unknown file to *.png *.dds...
I found these files can be look!

Use http://www.xnview.com/en/xnview/ . You can see all graphics files with this tool.
yianti
Posts: 8
Joined: Tue Dec 16, 2014 5:27 am

Re: love&war ( .wdf )

Post by yianti »

Ekey wrote:
yianti wrote:thanks Ekey,
I try to rename the Unknown file to *.png *.dds...
I found these files can be look!

Use http://www.xnview.com/en/xnview/ . You can see all graphics files with this tool.


thanks Ekey!! plz give me you paypal id! :)
Ekey
Posts: 1383
Joined: Sat Aug 09, 2014 2:34 pm

Re: love&war ( .wdf )

Post by Ekey »

yianti wrote:thanks Ekey!! plz give me you paypal id! :)

Never mind. Just enjoy.
aluigi
Site Admin
Posts: 12984
Joined: Wed Jul 30, 2014 9:32 pm

Re: love&war ( .wdf )

Post by aluigi »

I made a script for supporting various versions of the Netease WDF format:
http://aluigi.org/bms/netease_wdf.bms
howarduong
Posts: 5
Joined: Sat Aug 27, 2022 11:50 am

Re: love&war ( .wdf )

Post by howarduong »

aluigi wrote:I made a script for supporting various versions of the Netease WDF format:
http://aluigi.org/bms/netease_wdf.bms

- GUI mode activated, remember that the tool works also from command-line
where are available various options like folder scanning, filters and so on

- select BMS script. type ? for using the content of clipboard like a script
- select input archives/files, type * for the whole folder and subfolders
- select output folder where extracting files
- open input file D:\Program Files (x86)\Netease\tx1.0.120\res\gui.wdf
- open script D:\Program Files (x86)\Netease\quickbms\bms\netease_wdf.bms
- set output folder D:\Program Files (x86)\Netease\tx1.0.120\res

offset filesize filename
--------------------------------------

Error: the requested amount of bytes to allocate is negative (0xffffffffc5e98997)

Last script line before the error or that produced the error:
66 log MEMORY_FILE OFFSET SIZE

- OFFSET 0x0000000044443444
- SIZE 0xffffffffc5e98997
coverage file 0 0% 16 170769883 . offset 0000000044443444
coverage file -1 0% 0 16 . offset 0000000000000010
coverage file -10 0% 0 906 . offset 0000000000000000

Press ENTER or close the window to quit



I loaded it to extract one of NetEase's games, which are all wdf packages and can't seem to recognize the offset.
game installation package https://mega.nz/file/lYs1AY6Y#6W7ygC1Ui ... cO194VQI3A
Its files are actually suffixed with these types
.cdata
.chunk
.model
.spt
.py
.pyc
.xml
.def
.bmp
.tga
.dds
.jpg
.visual
.mfm
.fx
.wav
.txt
.fdb
.fsb
.fev
.seq
.animation
.anca
.primitives
.gui
.texformat
.fx
.fxo
.fxh
.mfm
.trackers
.opt
.bwp
.texanim
.settings
.timestamps
.stc
.bsp2
.ctree
.ini
.font