Unpacking FFXi Files For Static Analysis

Post Reply
User avatar
atom0s
Developer
Posts: 537
Joined: Thu Oct 25, 2012 9:52 am

Unpacking FFXi Files For Static Analysis

Post by atom0s » Fri Jan 02, 2015 11:04 pm

One of the many aspects of reversing Final Fantasy XI be it statically on disk or live in memory requires that parts of the game files are unpacked. Square Enix has taken the time to create an internal packer of the games sensitive data for the PE files (.exe and .dll). In many of the game files, there is a packed section called POL1. This section contains the files actual .text section data, but is packed.

For example, this is the games FFXiMain.dll as of Jan 01, 2015:
Image

In this image you’ll see something strange. The files .text section has a raw size of 0. This is because the actual data does not exist in the file until its unpacked. Instead, the files .text data is actually within the POL1 section. If we load FFXiMain.dll inside of OllyDbg, or any other debugger / disassembler, we will see this at the files current entry point:
Image

Like much of SE’s other packing / encoding methods, the encryption of the section is not very secure or unique.
Some basic shifting and such and the unpacked data is obtained fairly easily.

However, handling this unpacking externally from a static context is a bit more difficult because it involves having to rebuild the PE file on disk after it is unpacked. The .text section must be resized to its proper size containing the new raw data which in turn unaligns all the other sections. So each section must be rebuilt and realigned with the file. Afterward, we must also set the files new size and its new entry point.

Unpacking the file is fairly trivial, we can just steal the decryption method from the games file and use it locally. If we adjust the ASM as needed, we can reuse it like this:

Code: Select all

;*******************************************************************************
; XiUnpack.asm (c) 2014 atom0s [atom0s@live.com]
;
; Unpacks a given POL1 section from a file.
; This function is taken from the FFXiMain.dll file.
;*******************************************************************************
 
.586
.model flat, C
option casemap :none
 
.code
 
    ;*******************************************************************************
    ; @brief Unpacks the given POL1 section.
    ;
    ; @param packed         The packed POL1 section to unpack.
    ; @param unpacked       The unpacked buffer to write the data to.
    ;*******************************************************************************
    XiUnpack PROC
        PUSHAD
        MOV EBP, ESP
        MOV ESI, DWORD PTR SS:[EBP + 024h] ; packed section
        MOV EDI, DWORD PTR SS:[EBP + 028h] ; storage
jmp_6:
        MOV ECX, 8
        MOV BL, BYTE PTR DS:[ESI]
        INC ESI
jmp_5:
        SHL BL, 1
        JNB jmp_1
        MOV AL, BYTE PTR DS:[ESI]
        MOV BYTE PTR DS:[EDI], AL
        INC ESI
        INC EDI
        JMP jmp_2
jmp_1:
        XOR EAX, EAX
        MOV AL, BYTE PTR DS:[ESI]
        INC ESI
        MOV EDX, EAX
        MOV AL, BYTE PTR DS:[ESI]
        MOV AH, DL
        AND EAX, 0FFFh
        JE jmp_3
        INC ESI
        NEG EAX
        SHR EDX, 4
        ADD EDX, 3
jmp_4:
        MOV BH, BYTE PTR DS:[EDI + EAX]
        MOV BYTE PTR DS:[EDI], BH
        INC EDI
        DEC EDX
        JNZ jmp_4
jmp_2:
        LOOP jmp_5
        JMP jmp_6
jmp_3:
        POPAD
        RETN
    XiUnpack ENDP
END
This would allow us to call XiUnpack to unpack the POL1 section like this:

Code: Select all

XiUnpack((DWORD)ptr_to_packed_data, (DWORD)ptr_to_output_buffer);
So the last bit would be now to load the file locally, dump the PE file information including:
  • DOS Header
  • DOS Stub (If it exists.)
  • Nt Headers
  • Section Entries
  • Section Data
Here is a step by step run down of what we would need to do. I will post the source code to this on Github as well for those interested.
  • Load the file locally into memory.
  • Validate the file is a PE file.
  • Validate the file has a packed POL1 section.
  • Obtain the POL1 section for its data and raw size.
  • Obtain the .text section for its virtual size. (This is the size of POL1 unpacked.)
  • Invoke XiUnpack, as seen above, to unpack the data.
  • Begin Rebuilding The Unpacked File
    • Write the original DOS header to the new file.
    • Write the DOS stub, if it exists, to the new file.
    • Write the original NT headers to the new file.
    • Process each section of the file.
      • If .text section, set the size of raw data to its virtual size.
      • Set the sections raw data pointer to the previous sections end. (Realigning the sections.)
      • Realign the sections pointer to raw data and size of raw data to the files section alignment. (Required for Windows to consider the file valid!)
      • Write the section entry to the file.
      • Set the file pointer to the sections raw data offset.
      • Write the sections raw data to the file.
      • Reset the file pointer to the section table for the next section to be written.
      • Reset the file pointer to the NT headers offset.
  • Adjust the files SizeOfImage inside of the NT headers with the new last section information.
  • Rewrite the NT headers to the file with the new image size.
  • Close the new file.
At this point our new file should be all set. The result will look something like this:
Image

As you can see here, our .text section is now properly sized (and aligned) to the file. Following the raw data pointer will take us to the unpacked data from POL1 as well. Each following section has had their raw address set to their new locations following the .text’s section data. Setting the raw data pointer is as simple as (rawSize + rawAddress) of the section previous. .text starts at 0x00001000. The next section will start at (.text->RawSize + .text->RawAddress). And so on for each following section.

Something to keep in mind, the POL1 section is not required after being unpacked. We have reset the entry point of the file to its new location. This allows us to completely remove the section if we wanted. Doing so will require us to fix the .rsrc and .reloc sections to properly align in the file with the POL1 section removed. For this example and source base, I just left the section in place. It does not hurt having it there, it just takes up some extra space.

Some side notes on how certain information is handled..

Aligning The Sections
According to MSDN, ‘SizeOfRawData’ and ‘PointerToRawData’ must be section aligned. It is required to be a multiple of the files ‘FileAlignment’ value found within the NT headers optional header. This is simple to do. Because it needs to be a multiple we want to make sure we are rounding up. In order to do, we would do the following:

Code: Select all

(((in + align - 1) / align) * align)
So as an example, our .text section in FFXiMain.dll would look like this:

Code: Select all

PointerToRawData = (((0x400 + 0x200 - 1) / 0x200) * 0x200); // would equal: 0x400
SizeOfRawData = (((0x2F4C2E + 0x200 - 1) / 0x200) * 0x200); // would equal: 0x2F4E00
Obtaining The Entry Point
With the example code base that is on Github, we will see the following for the entry point:

Code: Select all

auto baseAddressOffset = *(DWORD*)(((DWORD)packed + polSection.Misc.VirtualSize) - 0x51);
auto baseAddressOriginal = ntHeaders.OptionalHeader.AddressOfEntryPoint;
ntHeaders.OptionalHeader.AddressOfEntryPoint = (baseAddressOffset + baseAddressOriginal) + 0x9B;
Here we are reading the original code from the packed file. The first read is the jump offset to the last jump going to the actual original entry point. Next, we obtain the original entry point then we calculate the original from the given offset. The + 0x9B is the offset to the last JMP to the entry point. So we are calculating the difference from the original entry point to the real entry point based on the jump.

I have written an unpacker and rebuilder to do this process here:
https://github.com/atom0s/xiunpack

thyris
Posts: 15
Joined: Wed Mar 25, 2015 2:02 am

Re: Unpacking FFXi Files For Static Analysis

Post by thyris » Sun Apr 05, 2015 5:41 pm

github page not found :(

User avatar
kjLotus
Special Guest
Posts: 1813
Joined: Sun Jul 22, 2012 2:16 pm

Re: Unpacking FFXi Files For Static Analysis

Post by kjLotus » Sun Apr 05, 2015 10:28 pm

thyris wrote:github page not found :(
he removed everything he put on github

Post Reply