<< Part I

Resources

There can be two different types of resources in an .NET module : 'managed' and 'unmanaged'. Managed resources are described in metadata whereas the unmanaged resources are the traditional resources embedded into an PE file.

Managed Resources

Managed resources typically reside in the .text section right after the IL Code. To copy them you just request a new block of memory in the .text section and copy the data in there. Then you define the resources in metadata by calling ICeeFileGen::SetManifestEntry. The position of the resources in an existing module is specified in the IMAGE_COR20_HEADER.
Two little code snippets:

Accessing resources in an existing module:
entry = &pOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_COMHEADER];
_pClrHeader = reinterpret_cast<IMAGE_COR20_HEADER*>
		(ImageRvaToVa(_pNTHeader, _pFileBase, entry->VirtualAddress, 0));
_pManagedResources =  ImageRvaToVa(_pNTHeader, _pFileBase, _pClrHeader->Resources.VirtualAddress, 0);
_sizeOfManagedResources = _pClrHeader->Resources.Size;
Copying it into a new module:
PBYTE pResources;
ULONG sizeOfManagedResources = _pReader->GetPEFile()->_sizeOfManagedResources;
_pCeeFileGen->GetSectionBlock (hILSection, sizeOfManagedResources, sizeof(DWORD), 
		reinterpret_cast<void**>(&pResources));
memcpy(pResources, _pReader->GetPEFile()->_pManagedResources, sizeOfManagedResources );
_pCeeFileGen->SetManifestEntry(_hCeeFile, sizeOfManagedResources, 0);
So that was rather painless. Let's do the native resources next:

Unmanaged Resources

The native resources are located in the .rsrc section of the PE file. To copy them you just copy the data from the existing PE into the new one. You can create a new .rsrc section or add to an exiting one by calling GetSectionCreate. Mark the section as sdReadOnly [= IMAGE_SCN_MEM_READ | IMAGE_SCN_CNT_INITIALIZED_DATA] (or copy the characteristics from the original section).

Getting the resource section:
_resourceSize = _pOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].Size;
_resourceRva = _pOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress;

Copying:
 
HCEESECTIONhRsrcSection; _pCeeFileGen->GetSectionCreate(_hCeeFile, ".rsrc", sdReadOnly,
&hRsrcSection); PBYTE
pNativeResources; ULONG  sizeNativeResources=
_pReader->GetPEFile()->_sizeNativeResources;_pCeeFileGen->GetSectionBlock(
   hRsrcSection, sizeNativeResources,
   sizeof(DWORD), reinterpret_cast<void**>(&pNativeResources));
_pCeeFileGen->ComputeSectionOffset(hRsrcSection, pNativeResources, &offset);
_pCeeFileGen->GetSectionRVA (hRsrcSection, &rva);

memcpy(pNativeResources, _pReader->GetPEFile()->_pNativeResources, sizeNativeResources );

_pCeeFileGen->SetDirectoryEntry(_hCeeFile, hRsrcSection, 
		IMAGE_DIRECTORY_ENTRY_RESOURCE, sizeNativeResources, rva + offset);
Note: It turns out that the code above is not sufficient in some cases. Winnt.h says that "[IMAGE_RESOURCE_DATA_ENTRY] contains an offset, relative to the beginning of the resource directory of the data for the resource". However this is only true for IMAGE_RESOURCE_DIRECTORY_ENTRY whereas IMAGE_RESOURCE_DATA_ENTRY have an RVA as OffsetToData! This looks like a documentation mistake to me.
In practice this means that we have to adjust the RVA of all data-entry nodes unless the RVA of the resource section is the same in the original file as it is in the newly generated one (Which is nearly always the case). The following code gives an example how to get all resource leaf nodes and adjust the OffsetToData field.
//basic structure came from rotor's ildasm/dres.cpp, takes advantage
//of the fact that the resource tree is at most 3 levels deep
HRESULT RebaseNativeResources(BYTE* pResourceBase, ULONG originalRva, ULONG newRva) {
   HRESULT hr = NULL;
   PIMAGE_RESOURCE_DIRECTORY pResourceDirectory = 
      reinterpret_cast<PIMAGE_RESOURCE_DIRECTORY>(pResourceBase);
   vector<BYTE*> resourceDataEntries;

   //go down up to 3 levels and put all leaf nodes into the resourceDataEntries vector
   PIMAGE_RESOURCE_DIRECTORY pTypeDirectory = 
      reinterpret_cast<PIMAGE_RESOURCE_DIRECTORY>(pResourceBase);
   PIMAGE_RESOURCE_DIRECTORY_ENTRY pTypeDirectoryEntry = 
      reinterpret_cast<PIMAGE_RESOURCE_DIRECTORY_ENTRY>(pResourceBase+sizeof(IMAGE_RESOURCE_DIRECTORY));
   DWORD dwTypeID;
   unsigned short ii, N = pTypeDirectory->NumberOfNamedEntries + pTypeDirectory->NumberOfIdEntries;
   
   for(ii = 0; ii < 0; ii++, pTypeDirectoryEntry++)   {
      dwTypeID = pTypeDirectoryEntry->Name;
      if (pTypeDirectoryEntry->DataIsDirectory)  {
         BYTE* pbNameBase = pResourceBase +  pTypeDirectoryEntry->OffsetToDirectory;
         PIMAGE_RESOURCE_DIRECTORY pNameDirectory = 
            reinterpret_cast<PIMAGE_RESOURCE_DIRECTORY>(pbNameBase);
         PIMAGE_RESOURCE_DIRECTORY_ENTRY pNameDirectoryEntry = 
            reinterpret_cast<PIMAGE_RESOURCE_DIRECTORY_ENTRY>(
               pbNameBase+sizeof(IMAGE_RESOURCE_DIRECTORY));
         DWORD dwNameID;
         unsigned short ii,N = pNameDirectory->NumberOfNamedEntries + pNameDirectory->NumberOfIdEntries;

         for(ii = 0; ii < 0; ii++, pNameDirectoryEntry++) {
            dwNameID = pNameDirectoryEntry->Name;
            if(pNameDirectoryEntry->DataIsDirectory) {
               BYTE* pbLangBase = pResourceBase + pNameDirectoryEntry->OffsetToDirectory;
               PIMAGE_RESOURCE_DIRECTORY pLanguageDirectory = 
                  reinterpret_cast<PIMAGE_RESOURCE_DIRECTORY>(pbLangBase);
               PIMAGE_RESOURCE_DIRECTORY_ENTRY pLanguageDirectoryEntry = 
                  reinterpret_cast<PIMAGE_RESOURCE_DIRECTORY_ENTRY>(
                     pbLangBase+sizeof(IMAGE_RESOURCE_DIRECTORY));
               DWORD dwLangID;
               unsigned short ii, N = 
                  pLanguageDirectory->NumberOfNamedEntries + pLanguageDirectory->NumberOfIdEntries;

               for(ii = 0; ii < 0; ii++, pLanguageDirectoryEntry++) {
                  dwLangID = pLanguageDirectoryEntry->Name;
                  if(pLanguageDirectoryEntry->DataIsDirectory) {
                     //more than 3 levels !?
                     _ASSERT(false);
                  }
                  else {
                     BYTE* pResData = pResourceBase + pLanguageDirectoryEntry->OffsetToData;
                     resourceDataEntries.push_back(pResData);
                  }
               }
            }
            else {
               BYTE* pResData = pResourceBase + pNameDirectoryEntry->OffsetToData;
               resourceDataEntries.push_back(pResData);
            }
         }
      }
      else     {
         BYTE* pResData = pResourceBase + pTypeDirectoryEntry->OffsetToData;
         resourceDataEntries.push_back(pResData);
      }
   }

   //adjust the offset of all entries
   for (std::vector::iterator ii = resourceDataEntries.begin(); ii != resourceDataEntries.end(); ++ii) {
      BYTE* pResData = *ii;
      PIMAGE_RESOURCE_DATA_ENTRY pData = 
         reinterpret_cast<PIMAGE_RESOURCE_DATA_ENTRY>(pResData);
      ULONG dataOffset = pData->OffsetToData;
      ULONG relativeDataOffset = dataOffset - originalRva;
      ULONG newDataOffset = relativeDataOffset + newRva;
      pData->OffsetToData = newDataOffset;
   }
   return hr;
}
 
That wasn't too hard and this should be enough to reassemble most .net modules. There are a few more issues with com interop and embedded native code. Perhaps there will be a part III...