Search code examples
x86disassemblyportable-executable

ResourceEntries RVAs of a Resource Table need relocation upon copying it to a different image/PE?


I've succesfully built a basic JAVA disassembler, simply because the already built open source one (pecoff4j and its forks) didn't help despite being excellent. I've already managed to parse all the PE header, compared it to Lord PE and PE Explorer, and everything seems to be in place, at least for x86 executables. I'm doing this for a certain reason. I want to copy the Resource Directory of the PE to another PE. I (try to) do this by first parsing everything in the header until all my variables are populated. I will take LORD PE executable as an example. Below is the 3rd directory entry, aka the resource directory.

ResourceDirectorySize: 0x16E50, ResourceDirectoryVirtualAddress: 0x1F000

I keep these values in mind, and proceed with the section header parsing. First section is this:

Name: .text
VirtualSize: 0x000172D0
Virtualaddress: 0x00001000
SizeOfRawData: 0x00017400
PointedToRawData: 0x00000400

... And eventually reach to the one I need:

Name: .rsrc
VirtualSize: 0x00016E50
Virtualaddress: 0x0001F000
SizeOfRawData: 0x00017000
PointedToRawData: 0x0001C000

Since the VirtualSize and VirtualAddress are the same as the ones I mentioned above, I decide this is the resource table. So I blindly copy the range 0x1C000 to 0x33000 to the other executable, and also update the ResourceDirectorySize and VirtualAddress of the PE header.

I also update several other things, like size of image, initialized data, number of sections to reflect the new executable state.


And my question: Do the ResourceEntries need to be relocated in order to reflect the new Virtual Address of the ResourceDirectory?


Solution

  • the Resource Directory containing internal PIMAGE_RESOURCE_DATA_ENTRY structures:

    typedef struct _IMAGE_RESOURCE_DATA_ENTRY {
        DWORD   OffsetToData;
        DWORD   Size;
        DWORD   CodePage;
        DWORD   Reserved;
    } IMAGE_RESOURCE_DATA_ENTRY, *PIMAGE_RESOURCE_DATA_ENTRY;
    

    the OffsetToData member is actually RVA - so relative offset from image base (when file mapped as image). if you copy resource directory to new image - you need fix OffsetToData fields in all IMAGE_RESOURCE_DATA_ENTRY. so resource directory must be relocated to new RVA.

    let we have:

    • oldRVA - rva where rsrc placed in current image.
    • newRVA - rva where rsrc will be placed in new image.

    so we need fix OffsetToData = newRVA + (OffsetToData - oldRVA) or

    DWORD Diff = newRVA - oldRVA; // calc once
    OffsetToData += Diff;
    

    so code for copy rsrc can be next

    void RelocateRSRC(PBYTE prsrc, PIMAGE_RESOURCE_DIRECTORY pird, LONG Delta)
    {
        if (DWORD NumberOfEntries = pird->NumberOfNamedEntries + pird->NumberOfIdEntries)
        {
            PIMAGE_RESOURCE_DIRECTORY_ENTRY pirde = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(pird + 1);
    
            do 
            {
                if (pirde->DataIsDirectory)
                {
                    RelocateRSRC(prsrc,
                        (PIMAGE_RESOURCE_DIRECTORY)(prsrc + pirde->OffsetToDirectory),
                        Delta);
                } 
                else 
                {
                    PIMAGE_RESOURCE_DATA_ENTRY data = 
                        (PIMAGE_RESOURCE_DATA_ENTRY)(prsrc + pirde->OffsetToData);
    
                    data->OffsetToData += Delta;
                }
    
            } while (pirde++, --NumberOfEntries);
        }
    }
    
    BOOL CopyRSRC(PVOID ImageBase, BOOLEAN MappedAsImage, ULONG NewRva, void** pprsrc)
    {
        *pprsrc = 0;
    
        ULONG Size;
        if (PIMAGE_RESOURCE_DIRECTORY pird = (PIMAGE_RESOURCE_DIRECTORY)
            RtlImageDirectoryEntryToData(ImageBase, TRUE, IMAGE_DIRECTORY_ENTRY_RESOURCE, &Size))
        {
            NewRva -= RtlPointerToOffset(ImageBase, pird);
    
            if (!MappedAsImage)
            {
                pird = (PIMAGE_RESOURCE_DIRECTORY)
                    RtlImageDirectoryEntryToData(ImageBase, FALSE, IMAGE_DIRECTORY_ENTRY_RESOURCE, &Size);
            }
    
            if (PBYTE prsrc = new BYTE[Size])
            {
                *pprsrc = prsrc;
                memcpy(prsrc, pird, Size);
                RelocateRSRC(prsrc, (PIMAGE_RESOURCE_DIRECTORY)prsrc, NewRva);
                return TRUE;
            }
    
            return FALSE;
        }
    
        return TRUE;
    }