User:Kmcguire
Reading CLR PE32 File
Useful Defines, External API, References
#define UINT8_AT(x) (*((uint8_t*)(x)))
#define UINT16_AT(x) (*((uint16_t*)(x)))
#define UINT32_AT(x) (*((uint32_t*)(x)))
Also the memcpy, memset, malloc, and free from the C standard library are used. I also use printf but only for diagnostic purposes.
Also, the page here has been very useful: http://ntcore.com/files/dotnetformat.htm.
Portable Executable 32 File Header And NT Header Structure
typedef struct {
uint32_t signature;
uint16_t cputype;
uint16_t objcnt;
uint32_t tdstamp;
uint32_t reserved1;
uint32_t reserved2;
uint16_t nthdrsize;
uint16_t flags;
uint16_t reserved3;
uint16_t lmajor;
uint16_t lminor;
uint16_t reserved4;
uint32_t reserved5;
uint32_t reserved6;
uint32_t entrypointrva;
uint32_t reserved7;
uint32_t reserved8;
uint32_t imagebase;
uint32_t objectalign;
uint32_t filealign;
uint16_t osmajor;
uint16_t osminor;
uint16_t usermajor;
uint16_t userminor;
uint16_t subsysmajor;
uint16_t subsysminor;
uint32_t reserved9;
uint32_t imagesize;
uint32_t hdrsize;
uint32_t filechecksum;
uint16_t subsystem;
uint16_t dllflags;
uint32_t stackreservesize;
uint32_t stackcommitsize;
uint32_t heapreservesize;
uint32_t heapcommitsize;
uint32_t reserved10;
uint32_t _rvasizes;
struct {
uint32_t rva;
uint32_t size;
} tables[];
} win32_pe32_fhdr;
#define WIN32_PE32_TABLE_EXPORT 0x00
#define WIN32_PE32_TABLE_IMPORT 0x01
#define WIN32_PE32_TABLE_RESOURCE 0x02
#define WIN32_PE32_TABLE_EXCEPTION 0x03
#define WIN32_PE32_TABLE_SECURITY 0x04
#define WIN32_PE32_TABLE_FIXUP 0x05
#define WIN32_PE32_TABLE_DEBUG 0x06
#define WIN32_PE32_TABLE_IMAGEDESC 0x07
#define WIN32_PE32_TABLE_MACHSPEC 0x08
#define WIN32_PE32_TABLE_THREADLOC 0x09
#define WIN32_PE32_TABLE_UNKNOWN1 0x0A
#define WIN32_PE32_TABLE_UNKNOWN2 0x0B
#define WIN32_PE32_TABLE_IMPORTADDR 0x0C
#define WIN32_PE32_TABLE_UNKNOWN3 0x0D
#define WIN32_PE32_TABLE_CLRRTHDR 0x0E
Accessing PE32 Headers
win32_pe32_fhdr *fhdr;
win32_pe32_ohdr *ohdr;
uint32_t pe_off;
/* get pe32 header offset */
pe_off = ((uint32_t*)(buffer + 0x3c))[0];
printf("peoff:%x\n", pe_off);
if(memcmp(buffer + pe_off, "PE\0\0", 4) == 0)
{
printf("Yes, this is a PE header.\n");
}else{
printf("No, this is not a PE header.\n");
}
fhdr = (win32_pe32_fhdr*)(buffer + pe_off);
printf("imagebase: %x\n", fhdr->imagebase);
Common Language Runtime Header
typedef struct {
uint32_t rva;
uint32_t size;
} win32_clr20_datadir;
/* http://ntcore.com/files/dotnetformat.htm */
typedef struct {
uint32_t cb;
uint16_t rtmajor;
uint16_t rtminor;
win32_clr20_datadir meta;
uint32_t flags;
uint32_t entrypointrva; /* entry point rva */
win32_clr20_datadir resources; /* resources */
win32_clr20_datadir strongnamsig; /* strong name signature */
win32_clr20_datadir codemantab; /* code manager table */
win32_clr20_datadir vtabfixups; /* vtable fixups */
win32_clr20_datadir exaddrtabjumps; /* export address table jumps */
} win32_clr20_hdr;
Accessing CLR Header
printf("CLRRTHDR_RVA:%x\nCLRRTHDR_SIZE:%x\n",
fhdr->tables[WIN32_PE32_TABLE_CLRRTHDR].rva,
fhdr->tables[WIN32_PE32_TABLE_CLRRTHDR].size);
clr_rva = fhdr->tables[WIN32_PE32_TABLE_CLRRTHDR].rva;
clr_size = fhdr->tables[WIN32_PE32_TABLE_CLRRTHDR].size;
/*
The array of objects follow the headers. But, my header does
not include the size for the data dictionary array. So to
include that we get the number of entries in the data dictionary
and multiple this by eight since each one is eight bytes in PE32.
*/
ohdr = (win32_pe32_ohdr*)(buffer + pe_off + sizeof(win32_pe32_fhdr) + (fhdr->_rvasizes * 8));
/* find the object that the CLRRTHDR is in and point header there */
for(x = 0; x < fhdr->objcnt; ++x)
{
printf("object rva %x %s\n", ohdr[x].rva, &ohdr[x].name[0]);
if(clr_rva >= ohdr[x].rva)
{
/* calculating relative to file offset not memory */
clr_off = ohdr[x].poff + (clr_rva - ohdr[x].rva);
printf("found object for CLRRTHDR, with rva %x\n", ohdr[x].rva);
break;
}
}
clr_hdr = (uint8_t*)(buffer + clr_off);
Accessing CLR Meta-Data Header
printf("clr-metatable:%x [size:%x]\n", clr_hdr->meta.rva, clr_hdr->meta.size);
meta_rva = clr_hdr->meta.rva;
meta_size = clr_hdr->meta.size;
/* find the object that the metadata is in and point header there */
for(x = 0; x < fhdr->objcnt; ++x)
{
printf("object rva %x %s\n", ohdr[x].rva, &ohdr[x].name[0]);
if(meta_rva >= ohdr[x].rva)
{
/* calculating relative to file offset not memory */
meta_off = ohdr[x].poff + (meta_rva - ohdr[x].rva);
printf("found object for CLR-METADATA, with rva %x\n", ohdr[x].rva);
break;
}
}
/* offset is relative to our buffer so that (buffer + meta_off) is start and meta_size is size */
CLR Meta-Data Container Structures
typedef struct {
/*
The meta-data contains streams. There are:
#~
#GUID
#US
#Strings
Each of these is stored in this structure.
*/
uint8_t *name; /* ASCII name */
uint8_t *data; /* if non-zero is buffer */
uint32_t size; /* size in bytes */
} win_clr20_meta_stream;
typedef struct {
/*
This structure holds the table once it has been
read from the meta-data #~ stream. It holds the
rows in a fixed static grid. You can get the size
of each row (which is constant), and the size of
each field in the row. However, there is no naming
per field.
*/
uint32_t rowcnt;
uint8_t rsize; /* row size in bytes */
uint8_t *fsize; /* size of each field from left to right in bytes */
uint8_t *rows; /* array of pointers to row data for each row */
} win_clr20_meta_table;
typedef struct {
/*
This is the main structure for the meta-data. It
should allow access to other information from this
structure.
*/
uint16_t strmcnt; /* the number of array items below */
win_clr20_meta_stream *strms; /* an array just like they are read in */
win_clr20_meta_stream strmbyid[5]; /* copy of strms, but indexed by WIN_CLR20_META_ */
uint8_t heapoffsetsizes; /* heap offset sizes */
uint32_t valid[2]; /* qword(64bit) tells which tables are present */
uint32_t sorted[2]; /* qword(64bit) tells which tables are sorted */
win_clr20_meta_table tables[64]; /* all the known tables that are supported */
} win_clr20_meta;
/*
A quick bit guide to find the right one.
*/
#define CLR20_META_HEAPOFFSETSIZES_STRING 0x01
#define CLR20_META_HEAPOFFSETSIZES_GUID 0x02
#define CLR20_META_HEAPOFFSETSIZES_BLOB 0x03
/*
The identifier for each stream. This is
specific to win_clr20_meta. These values
are internal to this code.
*/
#define WIN_CLR20_META_TABLES 0x00
#define WIN_CLR20_META_USTRINGS 0x01
#define WIN_CLR20_META_STRINGS 0x02
#define WIN_CLR20_META_BLOB 0x03
#define WIN_CLR20_META_GUID 0x04
Read CLR Meta-Data Streams Into Memory
/* read clr metadata */
int xxxx_clr_meta_read(uint8_t *buffer, uint32_t len, win_clr20_meta *meta)
{
uint16_t ver_major;
uint16_t ver_minor;
uint16_t ver_strlen;
uint16_t strmcnt;
uint32_t x;
uint32_t y;
uint16_t strm_off;
uint16_t strm_size;
uint8_t *_buffer;
_buffer = buffer;
if(UINT32_AT(buffer) != 0x424A5342)
{
printf("invalid clr metadata signature\n");
return 0;
}
ver_major = UINT16_AT(buffer + 4 + 0);
ver_minor = UINT16_AT(buffer + 4 + 2);
printf("meta data ver:%x.%x\n", ver_major, ver_minor);
ASSERT((ver_major == 1) && (ver_minor == 1));
/* jump over signature, version, and reserved */
buffer += 4 + 4 + 4;
/* display and jump over version string */
ver_strlen = UINT32_AT(buffer);
ASSERT((ver_strlen & 0x3) == 0x0); /* has to be multiple of 4 */
printf("ver_strlen:%u\n", ver_strlen);
printf("ver:%s\n", buffer + 4);
buffer += 4 + ver_strlen;
/* jump over flags; 16 bit field; supposed to always be zero;
but apparently it is not always zero oddly enough..
ASSERT(UINT16_AT(buffer) != 0x0); */
buffer += 2;
strmcnt = UINT16_AT(buffer);
buffer += 2;
printf("MetaDataStreamCount:%u\n", strmcnt);
/* read each metadata stream header */
meta->strmcnt = strmcnt;
meta->strms = (win_clr20_meta_stream*)malloc(sizeof(win_clr20_meta_stream) * strmcnt);
for(x = 0; x < strmcnt; ++x)
{
/* read offset and size then copy into newly allocated buffer */
printf("off:%x,size:%x,name:%s\n", UINT32_AT(buffer), UINT32_AT(buffer + 4), buffer + 8);
meta->strms[x].data = (uint8_t*)malloc(UINT32_AT(buffer + 4));
memcpy(meta->strms[x].data, _buffer + UINT32_AT(buffer), UINT32_AT(buffer + 4));
buffer += 4 + 4;
/* read stream name (stop at zero byte) */
y = 0;
while(buffer[y] != 0x00)
{
++y;
}
++y;
y = ((y & 3) > 0 ? (y & (~3)) + 4 : y);
meta->strms[x].name = (uint8_t*)malloc(y + 1);
memcpy(meta->strms[x].name, buffer, y + 1);
/* advance to next offset and size pair */
buffer += y;
}
/* give each stream a home at a consistent index */
for(x = 0; x < meta->strmcnt; ++x)
{
if(strcmp(meta->strms[x].name, "#~") == 0)
{
meta->strmbyid[WIN_CLR20_META_TABLES] = meta->strms[x];
}
if(strcmp(meta->strms[x].name, "#US") == 0)
{
meta->strmbyid[WIN_CLR20_META_USTRINGS] = meta->strms[x];
}
if(strcmp(meta->strms[x].name, "#Strings") == 0)
{
meta->strmbyid[WIN_CLR20_META_STRINGS] = meta->strms[x];
}
if(strcmp(meta->strms[x].name, "#Blob") == 0)
{
meta->strmbyid[WIN_CLR20_META_BLOB] = meta->strms[x];
}
if(strcmp(meta->strms[x].name, "#GUID") == 0)
{
meta->strmbyid[WIN_CLR20_META_GUID] = meta->strms[x];
}
}
return 1;
}
Meta-Data #~ Stream Table Field Descriptions
This allows us to write a function to interpret the table data that follows the header in the #~ meta-data stream. The indexes can vary in size by what tables they reference so we have to create a letter for each specific index which refers to specific tables so that during run-time we can calculate the index size needed.
In my opinion this was done to conserve space, but I am not sure.
/*
The best way I could think of was to create a large table
that has 64 entries. Since at most with 2.0 there can be
a maximum of 64 tables. At this time not all of these are
used. So I only populate the indexes that are for a specific
table.
What I do is describe the fields in a row for each table. Since
each table has different fields per row each entry is different.
Also, most of the fields are index types. And since the index
size can change to, apparently conserve space, I use these identifiers
so that when I actually go to use them I calculate their size before
hand. The '1', '2', and '4' are always this size.
1 - 8 bit field
2 - 16 bit field
4 - 32 bit field
S - index to String table
G - index to GUID table
R - resolution scope index (Module, ModuleRef, AssemblyRef, TypeRef)
T - TypeDefOrRef index (TypeRef, TypeDef, TypeSpec)
F - Field table index
M - MethodDef table index
B - Blob table index
P - Param table index
D - TypeDef table index (different from T)
E - MemberRefParent index (TypeRef, ModuleRef, MethodDef, TypeSpec, TypeDef)
H - HasConstant index (Param, Field, Property)
C - HasCustomAttribute index (**AnyTable** Except It's Self)
I - CustomAttributeType index (MethodDef, MethodRef)
O - HasFieldMarshal index (Field, Param)
A - HasDesclSecurityIndex (TypeDef, MethodDef, Assembly)
V - Event table index
Y - Property table index
S - HasSemantics index (Event, Property)
U - MemberForwarded index (Field, MethodDef)
B - Implementation index (File, AssemblyRef, ExportedType)
X - CustomAttributeType index (MethodDef, MethodRef)
W - ResolutionScope index (Module, ModuleRef, AssemblyRef, TypeRef)
N - ModuleRef table index
J - AssemblyRef table index
K - TypeOrMethodDef index (TypeDef, MethodDef)
L - GenericParam table index
*/
/*
This is the table's ID.
*/
#define CLR20_META_TMODULE 0
#define CLR20_META_TTYPEREF 1
#define CLR20_META_TTYPEDEF 2
#define CLR20_META_TFIELD 4
#define CLR20_META_TMETHODDEF 6
#define CLR20_META_TPARAM 8
#define CLR20_META_TIFACEIMPL 9 /* interface implementation table */
#define CLR20_META_TMEMBERREF 10
#define CLR20_META_TCONSTANT 11
#define CLR20_META_TCUSTOMATTRI 12
#define CLR20_META_TFIELDMARSHAL 13
#define CLR20_META_TDECLSECURITY 14
#define CLR20_META_TCLASSLAYOUT 15
#define CLR20_META_TFIELDLAYOUT 16
#define CLR20_META_TSTANDALONGSIG 17
#define CLR20_META_TEVENTMAP 18
#define CLR20_META_TEVENT 20
#define CLR20_META_TPROPERTY 23
#define CLR20_META_TMETHODSEMAN 24
#define CLR20_META_TMETHODIMPL 25
#define CLR20_META_TMODULEREF 26
#define CLR20_META_TIMPLMAP 28
#define CLR20_META_TFIELDRVA 29
#define CLR20_META_TASSEMBLY 32
#define CLR20_META_TASSEMBLYPROC 33
#define CLR20_META_TASSEMBLYOS 34
#define CLR20_META_TASSEMBLYREF 35
#define CLR20_META_TASSEMBLYREFPROC 36
#define CLR20_META_TASSEMBLYREFOS 37
#define CLR20_META_TFILE 38
#define CLR20_META_TEXPORTEDTYPE 39
#define CLR20_META_TMANIFESTRES 40
#define CLR20_META_TNESTEDCLASS 41
#define CLR20_META_TGENERICPARAM 42
#define CLR20_META_TGENERICPARAMCONST 44 /* generic parameter contraint */
#define CLR20_META_TPROPERTYMAP 21
#define CLR20_META_TTYPESPEC 27
#define CLR20_META_TMETHODREF 63 /* WHERE IS THIS ONE??? */
/*
This is the field description structure. It is created at
compile time and is accessed during run-time to determine
the row length so that it can be copied into a more suitable
structure for dynamic access.
*/
uint8_t *WIN_CLR20_META_TFIELDS[64] =
{
[CLR20_META_TMODULE] = "2SGGG",
[CLR20_META_TTYPEREF] = "RSS", /* META_TTYPEREF */
[CLR20_META_TTYPEDEF] = "4SSTFM", /* META_TTYPEDEF */
[CLR20_META_TFIELD] = "2SB", /* META_TFIELD */
[CLR20_META_TMETHODDEF] = "422SBP", /* META_TMETHODDEF */
[CLR20_META_TPARAM] = "22S", /* META_TPARAM */
[CLR20_META_TIFACEIMPL] = "DT", /* META_TIFACEIMPL */
[CLR20_META_TMEMBERREF] = "ESB", /* META_TMEMBERREF */
[CLR20_META_TCONSTANT] = "11HB", /* META_TCONSTANT */
[CLR20_META_TCUSTOMATTRI] = "CIB", /* META_TCUSTOMATTRI */
[CLR20_META_TFIELDMARSHAL] = "OB", /* META_TFIELDMARSHAL */
[CLR20_META_TDECLSECURITY] = "2AB", /* META_TDECLSECURITY */
[CLR20_META_TCLASSLAYOUT] = "24D", /* META_TCLASSLAYOUT */
[CLR20_META_TFIELDLAYOUT] = "4F",
[CLR20_META_TSTANDALONGSIG] = "B",
[CLR20_META_TEVENTMAP] = "DV",
[CLR20_META_TEVENT] = "2ST",
[CLR20_META_TPROPERTYMAP] = "DY",
[CLR20_META_TPROPERTY] = "2SB",
[CLR20_META_TMETHODSEMAN] = "2MS",
[CLR20_META_TMETHODIMPL] = "DII",
[CLR20_META_TMODULEREF] = "S",
[CLR20_META_TTYPESPEC] = "B",
[CLR20_META_TIMPLMAP] = "2USN",
[CLR20_META_TFIELDRVA] = "4F",
[CLR20_META_TASSEMBLY] = "422224BSS",
[CLR20_META_TASSEMBLYPROC] = "4",
[CLR20_META_TASSEMBLYOS] = "444",
[CLR20_META_TASSEMBLYREF] = "22224BSSB",
[CLR20_META_TASSEMBLYREFPROC] = "4J",
[CLR20_META_TASSEMBLYREFOS] = "444J",
[CLR20_META_TFILE] = "4SB",
[CLR20_META_TEXPORTEDTYPE] = "44SSB",
[CLR20_META_TMANIFESTRES] = "44SB",
[CLR20_META_TNESTEDCLASS] = "DD",
[CLR20_META_TGENERICPARAM] = "22KS",
[CLR20_META_TGENERICPARAMCONST]="LT"
};
Reading Meta-Data #~ Stream Tables
int xxxx_clr_meta_process(win_clr20_meta *meta)
/*
Processes the CLR meta-data by inflating the stream into a
binary structure which can be more easily accessed later.
All of the meta-data is stored in a stream like format. It
is difficult to randomly access the data in this format as
it lacks any block like structure. This function will read
the stream and create a block like structure which can be
easily accessed in a random manner.
*/
{
uint32_t x;
uint32_t y;
uint32_t z;
uint32_t w;
uint32_t v;
uint8_t *tbuf; /* table stream buffer */
uint8_t ver_major; /* major version */
uint8_t ver_minor; /* minor version */
uint8_t fsize[26]; /* field size for each type in bytes */
memset(fsize, 0, sizeof(fsize));
memset(meta->tables, 0, sizeof(meta->tables));
/* We are going to convert the stream into a structure */
tbuf = meta->strmbyid[WIN_CLR20_META_TABLES].data;
/* The first DWORD is supposed to be reserved and zero. */
ASSERT(UINT32_AT(tbuf) == 0);
tbuf += 4;
/* The major and minor version. */
ver_major = UINT8_AT(tbuf + 0);
ver_minor = UINT8_AT(tbuf + 1);
tbuf += 2;
/* http://ntcore.com/files/dotnetformat.htm
The heap offset sizes. This field is very important, it's a byte that
tells us the size that indexes into the "#String", "#GUID" and "#Blob"
streams will have. I paste you the description and the bit mask from
the SDK
Bit mask Description
0x01 Size of “#String” stream >= 2^16.
0x02 Size of “#GUID” stream >= 2^16
0x04 Size of “#Blob” stream >= 2^16.
If zero index are 16 bits and if one index are 32 bits.
*/
meta->heapoffsetsizes = UINT8_AT(tbuf++);
/* Reserved byte which should be zero.. */
/* ASSERT(UINT8_AT(tbuf) == 0); */
++tbuf;
/* http://ntcore.com/files/dotnetformat.htm
It's a bitmask-qword that tells us which MetaData Tables are present in
the assembly. Of course, since this is a qword the maximum number of
tables is 64. However, most of the tables aren't defined yet. So, the
high bits of this qword are always 0.
*/
meta->valid[0] = UINT32_AT(tbuf + 0);
meta->valid[1] = UINT32_AT(tbuf + 4);
tbuf += 8;
printf("TablesValid:%04x%04x\n", meta->valid[0], meta->valid[1]);
/* Also a bitmask-qword. It tells us which tables are sorted. */
meta->sorted[0] = UINT32_AT(tbuf + 0);
meta->sorted[1] = UINT32_AT(tbuf + 4);
tbuf += 8;
/* http://ntcore.com/files/dotnetformat.htm
Following there's an array of dwords with the number of rows for each
present table. Ok this has to be explained. For every table there can
be n rows. Let's say we have three tables: A, B and C. And the Valid
mask tells us that the B table is not present, but A and C are. In
this case there will be 2 dwords (not three), one for the rows in the
A table and one for the C table. The B table rows are skipped since
there is no B table in the assembly.
*/
for(y = 0; y < 2; ++y)
{
for(x = 0; x < 32; ++x)
{
if(((meta->valid[y] >> x) & 0x1) == 0x1)
{
/* This table is present. */
printf("Table[%u] Present With %u Rows\n", x + (y * 32), UINT32_AT(tbuf));
meta->tables[x + (y * 32)].rowcnt = UINT32_AT(tbuf);
tbuf += 4;
}else{
meta->tables[x + (y * 32)].rowcnt = 0;
}
}
}
/* I need to calculate the size of any indexes that are used by
the dynamic field format table defined during compile-time.
Once the size of these indexes are defined then we can continue
by loading the actual data for the meta-data tables.
*/
fsize['1'] = 1;
fsize['2'] = 2;
fsize['4'] = 4;
/* Either a 4 byte or 2 byte index needed depending on bit set. */
fsize['S'] = (meta->heapoffsetsizes & CLR20_META_HEAPOFFSETSIZES_STRING) ? 4 : 2;
fsize['G'] = (meta->heapoffsetsizes & CLR20_META_HEAPOFFSETSIZES_GUID) ? 4 : 2;
fsize['B'] = (meta->heapoffsetsizes & CLR20_META_HEAPOFFSETSIZES_BLOB) ? 4 : 2;
/* We or the row counts and if we have left something larger than
0xffff then this means that one of the tables is needs a 32 bit
index instead of a 16 bit one.
*/
fsize['R'] = (meta->tables[CLR20_META_TMODULE].rowcnt |
meta->tables[CLR20_META_TMODULEREF].rowcnt |
meta->tables[CLR20_META_TASSEMBLYREF].rowcnt |
meta->tables[CLR20_META_TTYPEREF].rowcnt) > 0xffff ? 4 : 2;
fsize['T'] = (meta->tables[CLR20_META_TTYPEREF].rowcnt |
meta->tables[CLR20_META_TTYPEDEF].rowcnt |
meta->tables[CLR20_META_TTYPESPEC].rowcnt) > 0xffff ? 4 : 2;
fsize['F'] = meta->tables[CLR20_META_TFIELD].rowcnt > 0xffff ? 4 : 2;
fsize['M'] = meta->tables[CLR20_META_TMETHODDEF].rowcnt > 0xffff ? 4 : 2;
fsize['P'] = meta->tables[CLR20_META_TPARAM].rowcnt > 0xffff ? 4 : 2;
fsize['D'] = meta->tables[CLR20_META_TTYPEDEF].rowcnt > 0xffff ? 4 : 2;
fsize['E'] = (meta->tables[CLR20_META_TTYPEREF].rowcnt |
meta->tables[CLR20_META_TMODULEREF].rowcnt |
meta->tables[CLR20_META_TMETHODDEF].rowcnt |
meta->tables[CLR20_META_TTYPESPEC].rowcnt |
meta->tables[CLR20_META_TTYPEDEF].rowcnt) > 0xffff ? 4 : 2;
fsize['H'] = (meta->tables[CLR20_META_TPARAM].rowcnt |
meta->tables[CLR20_META_TFIELD].rowcnt |
meta->tables[CLR20_META_TPROPERTY].rowcnt) > 0xffff ? 4 : 2;
/* A bit of a special case here. It can reference any table but it's self. */
for(x = 0, y = 0; x < 64; ++x)
{
if(x != CLR20_META_TCUSTOMATTRI)
{
y = y | meta->tables[x].rowcnt;
}
}
fsize['C'] = y > 0xffff ? 4 : 2;
/* Oddly, the document says there is a MethodRef table? Where? If there
does exist one then it should be considered for 'K' here. Since at
the moment I have no idea what to do but just ignore it.
*/
fsize['I'] = (meta->tables[CLR20_META_TMETHODDEF].rowcnt |
meta->tables[CLR20_META_TMETHODREF].rowcnt) > 0xffff ? 4 : 2;
fsize['O'] = (meta->tables[CLR20_META_TFIELD].rowcnt |
meta->tables[CLR20_META_TPARAM].rowcnt) > 0xffff ? 4 : 2;
fsize['A'] = (meta->tables[CLR20_META_TTYPEDEF].rowcnt |
meta->tables[CLR20_META_TMETHODDEF].rowcnt |
meta->tables[CLR20_META_TASSEMBLY].rowcnt) > 0xffff ? 4 : 2;
fsize['V'] = meta->tables[CLR20_META_TEVENT].rowcnt > 0xffff ? 4 : 2;
fsize['Y'] = meta->tables[CLR20_META_TPROPERTY].rowcnt > 0xffff ? 4 : 2;
fsize['S'] = (meta->tables[CLR20_META_TEVENT].rowcnt |
meta->tables[CLR20_META_TPROPERTY].rowcnt) > 0xffff ? 4 : 2;
fsize['U'] = (meta->tables[CLR20_META_TFIELD].rowcnt |
meta->tables[CLR20_META_TMETHODDEF].rowcnt) > 0xffff ? 4 : 2;
fsize['B'] = (meta->tables[CLR20_META_TFILE].rowcnt |
meta->tables[CLR20_META_TASSEMBLYREF].rowcnt |
meta->tables[CLR20_META_TEXPORTEDTYPE].rowcnt) > 0xffff ? 4 : 2;
fsize['X'] = (meta->tables[CLR20_META_TMETHODDEF].rowcnt |
meta->tables[CLR20_META_TMETHODREF].rowcnt) > 0xffff ? 4 : 2;
fsize['W'] = (meta->tables[CLR20_META_TMODULE].rowcnt |
meta->tables[CLR20_META_TMODULEREF].rowcnt |
meta->tables[CLR20_META_TASSEMBLYREF].rowcnt |
meta->tables[CLR20_META_TTYPEREF].rowcnt) > 0xffff ? 4 : 2;
fsize['N'] = meta->tables[CLR20_META_TMODULEREF].rowcnt > 0xffff ? 4 : 2;
fsize['J'] = meta->tables[CLR20_META_TASSEMBLYREF].rowcnt > 0xffff ? 4 : 2;
fsize['K'] = (meta->tables[CLR20_META_TTYPEDEF].rowcnt |
meta->tables[CLR20_META_TMETHODDEF].rowcnt) > 0xffff ? 4 : 2;
fsize['L'] = meta->tables[CLR20_META_TGENERICPARAM].rowcnt > 0xffff ? 4 : 2;
/* http://ntcore.com/files/dotnetformat.htm
As you can see, some numbers are missing, that's because some tables,
as I said before, are not defined yet. It's important you understand
how the tables are stored. A table is made of an array of rows; a row
is a structure (let's call it this way for the moment to make things
easier). After the rows of a given table end, the rows of the next
table follow. The problem with a row (remember, think of it like a
structure) is that some of its fields aren't always of the same size
and they change from assembly to assembly, so you have to calculate
them dynamically.
*/
for(y = 0; y < 2; ++y)
{
for(x = 0; x < 32; ++x)
{
if(((meta->valid[y] >> x) & 0x1) == 0x1)
{
/* the table identifier */
w = x + (y * 32);
for(z = 0; WIN_CLR20_META_TFIELDS[w][z] != 0; ++z);
meta->tables[w].fsize = (uint8_t*)malloc(z);
for(v = 0, z = 0; WIN_CLR20_META_TFIELDS[w][z] != 0; ++z)
{
v += fsize[WIN_CLR20_META_TFIELDS[w][z]];
meta->tables[w].fsize[z] = fsize[WIN_CLR20_META_TFIELDS[w][z]];
}
meta->tables[w].fcnt = z;
/* read the number of rows specified by row size */
meta->tables[w].rsize = v;
meta->tables[w].rows = (uint8_t*)malloc(meta->tables[w].rowcnt * v);
memcpy(meta->tables[w].rows, tbuf, meta->tables[w].rowcnt * v);
tbuf += meta->tables[w].rowcnt * v;
printf("read %u rows of %u bytes each for a total of %u.\n",
meta->tables[w].rowcnt, v, meta->tables[w].rowcnt * v);
}
}
}
return 1;
}
The tables have now been read into their own structure. The data for all the rows is pointed to by meta->tables[w].rows.
Displaying The Method Definition Table
Now that you have located the PE32 headers which allowed you to find the CLR run-time header which you then used to read the meta-data streams and finally the meta-data tables from the #~ stream you can now display the method definition table.
I wrote a utility function to access a row and field in each table. The function works like below:
win_clr20_meta_table *tmp;
tmp = &meta->tables[CLR20_META_TMETHODDEF];
for(x = 0; x < tmp->rowcnt; ++x)
{
td = tmp->rows + (x * tmp->rsize);
cilvm_clr_treadrowfield(tmp, x, CLR20_TMETHODDEF_RVA, &z);
printf("rva:%x ", z);
cilvm_clr_treadrowfield(tmp, x, CLR20_TMETHODDEF_IMPLFLAGS, &z);
printf("implflags:%x ", z);
cilvm_clr_treadrowfield(tmp, x, CLR20_TMETHODDEF_FLAGS, &z);
printf("flags:%x ", z);
cilvm_clr_treadrowfield(tmp, x, CLR20_TMETHODDEF_NAME, &z);
printf("Name:%x ", z);
cilvm_clr_treadrowfield(tmp, x, CLR20_TMETHODDEF_SIGNATURE, &z);
printf("Signature:%x ", z);
cilvm_clr_treadrowfield(tmp, x, CLR20_TMETHODDEF_PARAMLIST, &z);
printf("ParamList:%x ", z);
printf("\n");
}
But, looks like this:
int cilvm_clr_treadrowfield(win_clr20_meta_table *table, uint32_t rndx, uint32_t fndx, void *ret)
/*
This will read a field and row both specified by an index starting from zero.
To check this function for success you should ensure that the return value is non-zero. This
function may fail if you try to access past the last row or last field. Or, somehow pass a
negative index.
*/
{
uint8_t x;
uint8_t *o;
if(rndx >= table->rowcnt)
{
return 0;
}
o = table->rows + (table->rsize * rndx);
if(fndx >= table->fcnt)
{
return 0;
}
for(x = 0; x < fndx; ++x)
{
o += table->fsize[x];
}
memcpy(ret, o, table->fsize[fndx]);
return 1;
}