Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
Introduction
This article is about the internal format of .NET Manifest Resources (or better the ".resources"
files contained in it). I don't know if the code can be useful to you or not (probably not), but I like
to write about undocumented stuff. In fact, this article is nothing sensational, I just wrote it
'cause I haven't found any documentation about this subject on the net, not even in the .NET
MetaData specifics: Partition II MetaData.doc.
Some time ago I wrote a PE Editor called CFF Explorer ('cause I needed to) with the support for
.NET MetaData, since there wasn't such a tool. The only tool I could find was Asmex (which you
can find on codeproject), but the problem with that tool is that you cannot modify the MetaData
fields and, moreover, it relies still on the .NET Framework. And I don't say this to criticize Asmex,
which is surely useful, but because I needed something different. Anyway I wrote a resource
viewer for the PE Editor and wanted to show the MetaData resources as well. So, in order to do
that, avoiding to use an external .NET Assembly, I had to analyze the Manifest Resource format.
Let's take a look at the Manifest Resources contained in a .NET assembly:
As you can see, there can be various types of files. Reading a bitmap, for example, is very simple:
every Manifest Resource begins with a dword that tells us the size of the actual embedded
ntcore.com/files/manifestres.htm
1/8
2/9/12
resource... And that's it... After that, we have our bitmap. Ok, but what about those ".resources"
files? The article is all about them. In this shot there's a ".resources" file for every dialog in that
.NET Assembly, this means every resource of a dialog is contained in the dialog's own ".resources"
file.
Handling these compiled resources files is, using the .NET Framework, very easy. You can convert
them into XML files (".resx" files) through a utility called Resgen.exe (downloadable on the MSDN)
or simply use the members of the System.Resources namespace (who can also handle ".resx" files)
to do whatever you want. You can create one, for example (the code is taken from the MSDN):
using System;
using System.Resources;
public class WriteResources {
public static void Main(string[] args) {
// Creates a resource writer.
IResourceWriter writer = new ResourceWriter("myResources.resources");
// Adds resources to the resource writer.
writer.AddResource("String 1", "First String");
writer.AddResource("String 2", "Second String");
writer.AddResource("String 3", "Third String");
// Writes the resources to the file or stream, and closes it.
writer.Close();
}
}
Through the use of ResourceWriter it's a very easy task to create resources files. What about
reading them?
using System;
using System.Resources;
using System.Collections;
public class ReadResources {
public static void Main(string[] args) {
// Opens a resource reader and gets an enumerator from it.
IResourceReader reader = new ResourceReader("myResources.resources");
IDictionaryEnumerator en = reader.GetEnumerator();
// Goes through the enumerator, printing out the key and value pairs.
while (en.MoveNext()) {
Console.WriteLine();
Console.WriteLine("Name: {0}", en.Key);
Console.WriteLine("Value: {0}", en.Value);
}
reader.Close();
}
}
Very easy indeed. Through the IResourceReader interface we can ask for an enumerator which
gives us every resource name and value. It's also possible to load directly a specific resource from
a file etc etc. So, as you can see, the Framework provides everything we need to play around
with resources files. Anyway, for those of you who are still interested in knowing the internal
format, go on reading.
ntcore.com/files/manifestres.htm
2/8
2/9/12
3/8
2/9/12
};
The use of this class is very simple, you just copy/paste the class and all its members to your
project and use it someway like this:
void main()
{
TCHAR FileName[MAX_PATH];
_tprintf(_T("Resources File to open:\n"));
_tscanf(_T("%s"), FileName);
//
// Open and read file
//
// ...
CResourcesFile ResFile;
if (ResFile.ProcessResourcesFile(BaseAddress, FileSize) == FALSE)
{
VirtualFree(BaseAddress, 0, MEM_RELEASE);
return;
}
_tprintf(_T("\n\nFile: %s\n"), FileName);
_tprintf(_T("Version: %d\n"), ResFile.Version);
_tprintf(_T("Number of resources: %d\n"), ResFile.NumberOfResources);
_tprintf(_T("Number of types: %d\n"), ResFile.NumberOfTypes);
_tprintf(_T("\nList resources:\n\n"));
WCHAR ResName[1024];
for (UINT x = 0; x < ResFile.NumberOfResources; x++)
{
DWORD Offset;
INT TypeIndex = 0;
if (ResFile.GetResourceInfo(x, ResName, 1024, &Offset, &TypeIndex))
{
_tprintf(_T("Name: %S - Offset: %08X - TypeIndex: %d\n"), ResName,
Offset, TypeIndex);
}
}
VirtualFree(BaseAddress, 0, MEM_RELEASE);
getch();
}
The first thing which has to be done is to process a resources file with the ProcessResourcesFile
function:
BOOL CResourcesFile::ProcessResourcesFile(BYTE *pAddress, UINT uSize)
{
BYTE *ptr = pAddress;
//
// Collect basic information: pointer and size of the file
//
ntcore.com/files/manifestres.htm
4/8
2/9/12
pBaseAddress = ptr;
Size = uSize;
//
// Read the magic number, its value has to be: 0xBEEFCACE
//
DWORD MagicNumber;
MagicNumber = *(DWORD *) ptr;
ptr += sizeof (DWORD);
if (MagicNumber != RESOURCES_MAGIC_NUMBER)
return FALSE;
DWORD NumberOfReaderTypes;
NumberOfReaderTypes = *(DWORD *) ptr;
ptr += sizeof (DWORD);
DWORD SizeOfReaderTypes;
SizeOfReaderTypes = *(DWORD *) ptr;
ptr += sizeof (DWORD);
//
// Skip ReaderTypes
//
ptr += SizeOfReaderTypes;
//
Version = *(DWORD *) ptr;
ptr += sizeof (DWORD);
//
// Read number of resources
//
NumberOfResources = *(DWORD *) ptr;
ptr += sizeof (DWORD);
//
// Read number of types
//
NumberOfTypes = *(DWORD *) ptr;
ptr += sizeof (DWORD);
//
// Skip Types: (CHAR *Type;) * NumOfTypes
// (Save position)
//
pTypes = ptr;
for (UINT x = 0; x < NumberOfTypes; x++)
{
INT StringSize = 0;
ntcore.com/files/manifestres.htm
5/8
2/9/12
UINT ValueSize = 0;
if (!DecodeInt(ptr, &StringSize, &ValueSize))
return FALSE;
ptr += ValueSize;
ptr += StringSize;
}
//
// Alignes position
//
DWORD Position = (DWORD) (((ULONG_PTR) ptr) - ((ULONG_PTR) pBaseAddress));
DWORD Aligned = Position & 7;
if (Aligned != 0)
{
ptr += (8 - Aligned);
}
//
// Skip name hashes
//
ptr += (sizeof (DWORD) * NumberOfResources);
//
// Skip name positions (first save location)
//
pNamesOffsets = ptr;
ptr += (sizeof (DWORD) * NumberOfResources);
//
// Read Data Section Offset
//
DWORD DataSectionOffset;
DataSectionOffset = *(DWORD *) ptr;
ptr += sizeof (DWORD);
pDataSection = (BYTE *) (DataSectionOffset + ((ULONG_PTR) pBaseAddress));
//
// Save names position
//
pNames = ptr;
return TRUE;
}
The ReadName is just a shorter version of the GetResourceInfo, so let's just see the
GetResourceInfo and skip the rest:
//
// Collect Resource Info
ntcore.com/files/manifestres.htm
6/8
2/9/12
//
BOOL CResourcesFile::GetResourceInfo(UINT nResource, WCHAR *Str, UINT Len,
DWORD *Offset, INT *TypeIndex)
{
//
// Read name
//
DWORD NameOffset = *(DWORD *) ((nResource * sizeof (DWORD)) +
((ULONG_PTR) pNamesOffsets));
if (NameOffset > (DWORD) (((ULONG_PTR) pNames) - ((ULONG_PTR) pDataSection)))
return FALSE;
ZeroMemory(Str, Len * sizeof (WCHAR));
BYTE *ptr = (BYTE *) (NameOffset + ((ULONG_PTR) pNames));
INT NameSize = 0;
UINT ValueSize = 0;
if (!DecodeInt(ptr, &NameSize, &ValueSize))
return FALSE;
ptr += ValueSize;
memcpy(Str, ptr, NameSize);
ptr += NameSize;
//
// After reading the name
//
DWORD DataOffset = *(DWORD *) ptr;
BYTE *pData = (BYTE *) (DataOffset + ((ULONG_PTR) pDataSection));
//
// Collect info
//
if (Offset) *Offset = (DWORD) (((ULONG_PTR) pData) ((ULONG_PTR) pBaseAddress));
if (TypeIndex)
{
*TypeIndex = 0;
ValueSize = 0;
if (!DecodeInt(pData, TypeIndex, &ValueSize))
return FALSE;
}
return TRUE;
}
That's all, I hope it's useful.
Post Scriptum
ntcore.com/files/manifestres.htm
7/8
2/9/12
Of course that's not everything, if you want to handle resources, you have to threat them
according to their type. This means you have to get their type from the TypeIndex. To retrieve
the type string is very simple, instead of skipping all the types, you just skip the ones who come
before the TypeIndex one and read the string which follows. In my CFF Explorer I support some
types of resources (and display only those one) for example Bitmaps, Icons and PNGs.
Have fun!
Daniel Pistelli
ntcore.com/files/manifestres.htm
8/8