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.
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::vectorThat 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...::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; }