The above example is to illustrate a simple point. The assembler does not do extensive error checks. We have two files, p2.il and p3.il that contain the same class xxx and function xyz. In the file p1.il, we have the same .class extern twice and we get no error. The second class extern refers to a file p3.dll that does not have a file directive at the top level.

 

No errors at all at assemble time which means that the assembler ignores our indiscretions. Also at runtime the xyz method gets called from p2.dll. Change the order of the class externs and watch the assembler complain. Remember the old adage buy at your own risk applies here also. No helping hand from the assembler. No hand holding also.

 

A assembly and a module lets us group constructs together and each play a different role in the .Net world. A set or a group of files is what we call an assembly. It is the abstract entity that we call a manifest that keeps track of all the files present.

 

The assembly table only store for us the Version, name, culture and security requirements that we have not touched upon yet. The assembly manifest must keep track of not only the files but also the cryptographic hash of each file. The manifest is computed from the metadata as mentioned earlier.

 

The runtime also needs to know which types are defined by other files and can be exported out of this assembly. This is achieved using the class extern directive.

 

Obviously if the type is defined in the same file that has the assembly directive, its attributes like public, private etc  decide whether it can be exported from this assembly or not. We could use digital signatures and a public key to compute it with the Manifest.

 

The problem with digital signatures is that for some reason it did not set the Thames on fire. The major difference between assemblies and modules is that assemblies comprise modules. A module consists of a single file that adheres to the rules of the .net world. It can either be a dll or a exe file, it must contain executable code.

 

There is one file in the list of files that carries the assembly directive and it is this file that gives us a list of other modules that make up the assembly. The file that carries the assembly directive is also a module. If the assembly is a dll, then we do not need a function that has the entrypoint directive.

 

However if it is a exe file, then the file that contains the entrypoint directive should also contain the assembly directive or manifest. The concept of namespaces may exist in programming language, the CLI does not understand what we are talking about.

 

Type names are always specified using the full name which is relative to the assembly that we have created them in. If the file is say a text file or a bmp file or a video file and does not contain metadata, will not have the module directive. It is normally not a common practice that many assemblies can refer to the same module but nothing stops us from doing so.

 

The advantage is that once a dll is loaded into memory, the next assembly that references the dll will not result in the dll being loaded again in memory.

 

Program13.csc

public void abc(string [] args)

{

ReadPEStructures(args);

DisplayPEStructures();

ReadandDisplayImportAdressTable();

ReadandDisplayCLRHeader();

ReadStreamsData();

FillTableSizes();

ReadTablesIntoStructures();

DisplayTablesForDebugging();

ReadandDisplayVTableFixup();

ReadandDisplayExportAddressTableJumps();

DisplayModuleRefs();

DisplayAssembleyRefs();

DisplayAssembley();

DisplayFileTable();

DisplayClassExtern();

}

 

public void DisplayClassExtern ()

{

if (ExportedTypeStruct == null)

return;

for ( int ii = 1 ; ii < ExportedTypeStruct.Length ; ii++)

{

string ss1 = GetTypeAttributeFlagsForClassExtern(ExportedTypeStruct[ii].flags );

string ss = GetString(ExportedTypeStruct[ii].nspace) ;

if ( ss.Length != 0)

ss = ss + ".";

ss = ss + NameReserved(GetString(ExportedTypeStruct[ii].name));

int table = ExportedTypeStruct[ii].coded & 0x03;

int index = ExportedTypeStruct[ii].coded >> 2;

if ( index != 0)

{

Console.Write(".class extern /*27{0}*/ {1}" , ii.ToString("X6") , ss1 );

Console.WriteLine(ss);

Console.WriteLine("{");

if ( table == 0)

{

Console.WriteLine("  .file {0}/*26{1}*/ " , NameReserved(GetString(FileStruct[index].name)) , index.ToString("X6"));

}

if ( table == 2)

{

Console.WriteLine("  .comtype '{0}' /*27{1}*/ " , NameReserved(GetString(ExportedTypeStruct[index].name)) , index.ToString("X6"));

}

if ( ExportedTypeStruct[ii].typedefindex != 0)

Console.WriteLine("  .class 0x{0}", ExportedTypeStruct[ii].typedefindex.ToString("X8"));

Console.WriteLine("}");

}

}

}

public string GetTypeAttributeFlagsForClassExtern(int typeattributeflags )

{

typeattributeflags = typeattributeflags & 0x07;

string visibiltymaskstring="";

if ( typeattributeflags == 1)

visibiltymaskstring = "public ";

if ( typeattributeflags == 2)

visibiltymaskstring = "nested public ";

return visibiltymaskstring;

}

 

e.il

.file mscorlib.dll

.file jj.dll

.assembly e

{

}

.file aa.dll

.file bb

.class extern o1

{

.file jj.dll

}

.file jj.dll

.class  extern public jjj

{

.file aa.dll

}

.class  extern nested public kkk

{

.file aa.dll

}

.class  extern public lll

{

.class extern kkk

}

.class  extern public ooo

{

.class extern nnn

}

.class  extern public ppp

{

.class extern jjj

}

.class  extern public mmm

{

.class extern System.Object

}

.class  extern public System.Object

{

.file mscorlib.dll

}

.class  extern public nnn

{

.file jj.dll

}

.class extern a1

{

.file jj.dll

.class 56

 

}

.class zzz

{

.method static void abc()

{

.entrypoint

ret

}

}

 

Output

.assembly extern /*23000001*/ mscorlib

{

  .ver 0:0:0:0

}

.assembly /*20000001*/ e

{

  .ver 0:0:0:0

}

.file /*26000001*/ mscorlib.dll

.file /*26000002*/ jj.dll

.file /*26000003*/ aa.dll

.file /*26000004*/ bb

.class extern /*27000001*/ o1

{

  .file jj.dll/*26000002*/

}

.class extern /*27000002*/ public jjj

{

  .file aa.dll/*26000003*/

}

.class extern /*27000003*/ nested public kkk

{

  .file aa.dll/*26000003*/

}

.class extern /*27000004*/ public lll

{

  .comtype 'kkk' /*27000003*/

}

.class extern /*27000006*/ public ppp

{

  .comtype 'jjj' /*27000002*/

}

.class extern /*27000008*/ public System.Object

{

  .file mscorlib.dll/*26000001*/

}

.class extern /*27000009*/ public nnn

{

  .file jj.dll/*26000002*/

}

.class extern /*2700000A*/ a1

{

  .file jj.dll/*26000002*/

  .class 0x00000038

}

 

 

In this program we simply display the directive class extern. First we as always add a function DisplayClassExtern to the abc function. We then in a loop iterate through all the rows in the ExportedType table. The first thing we need to figure out is the export attributes that can have one of two values, public or nested public.

 

These specify the visibility or who all can see and thus use this type. We have a function GetTypeAttributeFlagsForClassExtern to do the job for us and return the string for us. The visibility attributes are stored in the first three bits. Thus we bit wise and the parameter passed by 7 as the other bits specify other attributes.

 

We then check whether the value is 1 or 2 which tells us whether the public or nested public attributes are on. We then need the name or more precisely the dotted name of the extern type. This is stored in two fields name and nspace.

 

We first get the name of the namespace and then if the namespace is present we add a dot and then add the name of the class name. The next field coded is most important as it tells us what directives are used in the braces. We could if you remember use .file and .class extern.

 

The coded field tells us the name of the table and the row in that table. The row in the table must be non zero if the directive class extern has to be displayed. Why this is so will be explained in a short while by using a practical example.

 

Thus the entire directive will not be displayed if the variable index is zero even though the dotted name has a non null value. Now that the index is non zero we then check the table that the index refers to. If its value is zero, it refers to the file table, 2 means that it refers to the Exported Type table.

 

A value of 1 means the Assembly Ref table and for some reason we have found no way to simulate this value. Now we take each individual table that make up the coded index. The first is the file table with a value of 0. Here we simply use the index variable as a index into the FileStruct table and display the name of the file stored there.

 

We also need to display the row number of the file table that is available to us in the index variable. A table with number 2 means that we have used the class extern directive and lets understand it with a specific reference to the class lll in the file e.il. Here we had written the class extern directive  with the name of the class kkk.

 

The class kkk happens to be another class extern with a file directive. This gets converted to a directive comtype whose explanation is no where to be found. Also ilasm does not understand such a directive. All that we know is that we have used a class name that is a class defined somewhere else as we have used the class extern directive.

 

This class name kkk is stored in the ExportedType table and use the index variable to fetch its name. We are not allowed to use a type name like zzz in the class extern as it is a type we are creating in the current file. The if statement which checks the value of the typedefindex as its always zero unless we add the .class directive. All that this directive does is fills up the typedefindex field with the row number of the type table which is present in the other module. As we will explain much later this is only a hint and nobody checks the value we write after the class directive.

 

Looking at the file e.il, the first three class extern classes o1,jjj and kkk simply use file directive. The difference is the visibility attributes. The class ooo is not present in the output at all even though it is there in the e.il file.

 

Lets investigate. For this class we have used the class extern directive with the class name nnn. This class in turn is stated to be in the file aa.dll. Thus as class ooo does nothing but refers to class nnn, there is no use for its independent existence and hence ilasm being a smart cookie ignores it and does not place it in the Exported Type table.

 

Makes sense as the class is simply an alias for another class.  This class ppp is  similar in concept to the class ppp that uses the undocumented comtype directive. The class mmm is also not found as its an alias for the class System.Object. no point cluttering up this table with classes that do nothing.

 

Whenever we use the class extern directive we should use the assembly directive also but we will not get a error if we do not. Obviously there is internally one manifest module only and all types exported should be present in this manifest.

 

The rational for this table is that after some tool reads this table, he/she can figure out all the types that others can use from this assembly. This manifest module will therefore contain all the types exported from all the modules that make up the assembly. Unfortunately this manifest module is also called the assembly.

 

Thus each time we create a type in any module of an assembly, this table of ours gets one row added. In other words, every type created in another module has a entry in the TypeDef table and this row number will be placed in the Exported Type table. The TypeDefID or typedefindex field in our case is always zero as mentioned earlier unless of course we use the class directive.

 

The point we are making is that this is the first time we are referring to a row in another module and these are called foreign tokens. The fact of the matter is that the assembler is too lazy to go the other modules and figure out what the type def row indexes are.

 

The type name is stored in the name and namespace fields and there was a plan to use the typedefid field if the above search failed. The specs at one place say that the implementation coded index can contain only the File and Exported Type table and at a another place adds the Assembly Ref table also.

 

For the file table the visibility mask has to be public and not nested public but we do not get an error if we do make such a mistake. The specs also say that if the table is the Exported type then the visibility mask has to be nested public. No error again if we break the rule. Why have rules when no one checks for them.

 

Program14.csc

public void abc(string [] args)

{

ReadPEStructures(args);

DisplayPEStructures();

ReadandDisplayImportAdressTable();

ReadandDisplayCLRHeader();

ReadStreamsData();

FillTableSizes();

ReadTablesIntoStructures();

DisplayTablesForDebugging();

ReadandDisplayVTableFixup();

ReadandDisplayExportAddressTableJumps();

DisplayModuleRefs();

DisplayAssembleyRefs();

DisplayAssembley();

DisplayFileTable();

DisplayClassExtern();

DisplayResources();

}

public void DisplayResources()

{

if ( ManifestResourceStruct == null)

return;

for ( int ii = 1 ; ii < ManifestResourceStruct.Length ; ii++)

{

string flags = GetManifestResourceAttributes(ManifestResourceStruct[ii].flags);

Console.WriteLine(".mresource /*28{0}*/ {1}{2}" ,  ii.ToString("X6") , flags, NameReserved(GetString(ManifestResourceStruct[ii].name)) );

 Console.WriteLine("{");

string table = GetManifestResourceTable(ManifestResourceStruct[ii].coded);

int index = GetManifestResourceValue(ManifestResourceStruct[ii].coded);

if ( table == "AssemblyRef")

Console.WriteLine("  .assembly extern {0} /*23{1}*/ " , NameReserved(GetString(AssemblyRefStruct[index].name)) , index.ToString("X6"));

else if ( table == "File" && index > 0)

Console.WriteLine("  .file {0}/*26{1}*/  at 0x{2}" , NameReserved(GetString(FileStruct[index].name)) , index.ToString("X6") ,ManifestResourceStruct[ii].offset.ToString("X8") );

else

Console.WriteLine("  // WARNING: managed resource file {0} created",NameReserved(GetString(ManifestResourceStruct[ii].name) ) );

Console.WriteLine("}");

}

}

public int GetManifestResourceValue(int manifiestvalue)

{

return manifiestvalue>> 2;

}

public string GetManifestResourceTable(int manifiestvalue)

{

string returnstring = "";

short tag = (short)(manifiestvalue & (short)0x03);

if ( tag  == 0)

returnstring = returnstring + "File";

if ( tag  == 1)

returnstring = returnstring + "AssemblyRef";

return returnstring;

}

public string GetManifestResourceAttributes(int manifiestvalue)

{

string returnstring="";

if ( (manifiestvalue & 0x001) == 0x001)

returnstring = returnstring + "public ";

if ( (manifiestvalue & 0x002) == 0x002)

returnstring = returnstring + "private ";

return returnstring;

}

 

e.il

.assembly e

{

}

.assembly extern a1

{

}

.file aa.dll

.mresource r1

{

}

.mresource public r1

{

}

.mresource private r2

{

.assembly extern a1

}

.mresource public r3

{

.file aa.dll at 12

}

.mresource public r4

{

.file aa.dll at 12

.assembly extern a1

}

.class zzz

{

.method static void abc()

{

.entrypoint

ret

}

}

 

Output

.assembly extern /*23000001*/ a1

{

  .ver 0:0:0:0

}

.assembly extern /*23000002*/ mscorlib

{

  .ver 0:0:0:0

}

.assembly /*20000001*/ e

{

  .ver 0:0:0:0

}

.file /*26000001*/ aa.dll

.mresource /*28000001*/ r1

{

  // WARNING: managed resource file r1 created

}

.mresource /*28000002*/ public r1

{

  // WARNING: managed resource file r1 created

}

.mresource /*28000003*/ private r2

{

  .assembly extern a1 /*23000001*/

}

.mresource /*28000004*/ public r3

{

  .file aa.dll/*26000001*/  at 0x0000000C

}

.mresource /*28000005*/ public r4

{

  .assembly extern a1 /*23000001*/

}

 

The above program displays all the resources that we have. We have added a function  DisplayResources that displays all the rows from the Manifest Resource table. This table gets filled up by the mresource directive. The first field is the flags field which gives us the visibility mask that is similar to the class extern directive.

 

The only difference is that the two valid values are public and private. We use the function GetManifestResourceAttributes to return one of these values. None of these visibility attributes is mandatory. We display the visibility attributes along with the mresource directive and the row number in the Manifest Resource table.

 

Nearly every table has a coded index and the Resource table does not lag behind. In this case the coded index either points to the File table if the value is 0 or assembly ref table whose value is 1. The specs call this the Implementation coded index which also included the Exported type table but for resources it is only the above two.

 

We use the GetManifestResourceTable function to return the table name and the function GetManifestResourceValue to return the row number after right shifting the coded index by two bits. In the mresource directive we can either write the Assembley extern directive or the file directive or both or none.

 

If we use the assembly extern directive, then the index variable is the row number of the Assembly Ref table and we display the name of the assembly. If we had had used the file directive and this indexes a row number larger than or equal to 1, then we display the file name along with the position of the file where the resource starts.

 

The index variable is the row number in the File table and the offset within the file is stored in the offset field of the Resource table. Finally if we have used the file directive and the index variable is zero, then within comments we display a warning stating that a file with the same name as the mresource directive has been created.

 

If we specify no directive, we get no error and the above warning is displayed. If we use both, the assembly directive is used and not the file directive. As always no error checks for the file directive or the offset. An assembly can have lots of different data items associated with it.

 

If we ever want to name a item of data, we use the manifest resource to do so. If we do not have a assembly directive, it is perfectly legal to use the mresource directive, but we will not be able to execute the assembly.

 

The reason we specify public or private for a manifest resource is so that the assembly knows whether this item can be exported or seen outside this assembly or should remain visible only within the assembly if it is flagged private.

 

If the resource is stored in the file and that file is not a module, it can be a text file for example, then we would need a separate file directive, declaring that file. In this case the byte offset will be zero.

 

If the resource is defined in another assembly, we would need to have a assembly extern directive at the top level before we can use the assembly extern directive within the mresource directive. The offset field is normally a valid offset which is relative from the resource data directory entry in the COR header.

 

But as said earlier, this error check is not done at present. If the index is null, it means that the resource is stored in the current file and hence the warning.

 

Program15.csc

public void abc(string [] args)

{

ReadPEStructures(args);

DisplayPEStructures();

ReadandDisplayImportAdressTable();

ReadandDisplayCLRHeader();

ReadStreamsData();

FillTableSizes();

ReadTablesIntoStructures();

DisplayTablesForDebugging();

ReadandDisplayVTableFixup();

ReadandDisplayExportAddressTableJumps();

DisplayModuleRefs();

DisplayAssembleyRefs();

DisplayAssembley();

DisplayFileTable();

DisplayClassExtern();

DisplayResources();

DisplayModuleAndMore();

}

public void DisplayModuleAndMore()

{

Console.WriteLine(".module {0}" , NameReserved(GetString(ModuleStruct[1].Name)));

Console.Write("// MVID: ");

DisplayGuid(ModuleStruct[1].Mvid);

Console.WriteLine();

Console.WriteLine(".imagebase 0x{0}" , ImageBase.ToString("x8"));

Console.WriteLine(".subsystem 0x{0}" , subsystem.ToString("X8"));

Console.WriteLine(".file alignment {0}" , filea);

Console.WriteLine(".corflags 0x{0}" , corflags.ToString("x8"));

Console.WriteLine("// Image base: 0x03000000");

}

public void DisplayGuid (int guidindex)

{

Console.Write("{");

Console.Write("{0}{1}{2}{3}", guid[guidindex+2].ToString("X2") , guid[guidindex+1].ToString("X2") , guid[guidindex].ToString("X2") , guid[guidindex-1].ToString("X2"));

Console.Write("-{0}{1}-",guid[guidindex+4].ToString("X2") , guid[guidindex+3].ToString("X2"));

Console.Write("{0}{1}-",guid[guidindex+6].ToString("X2") , guid[guidindex+5].ToString("X2"));

Console.Write("{0}{1}-",guid[guidindex+7].ToString("X2") , guid[guidindex+8].ToString("X2"));

Console.Write("{0}{1}{2}{3}{4}{5}",guid[guidindex+9].ToString("X2"),guid[guidindex+10].ToString("X2"),guid[guidindex+11].ToString("X2"),guid[guidindex+12].ToString("X2"),guid[guidindex+13].ToString("X2"),guid[guidindex+14].ToString("X2"));

Console.Write("}");

}

 

e.il

.assembly e

{

}

.module aaaa

.class zzz

{

.method static void abc()

{

.entrypoint

ret

}

}

 

output

.module aaaa

// MVID: {EDBE9E84-F6DE-468C-B8CB-0CB099FD1EA4}

.imagebase 0x00400000

.subsystem 0x00000003

.file alignment 512

.corflags 0x00000001

// Image base: 0x03000000

 

This one is a small program and all that it does is adds one more function call DisplayModuleAndMore to the abc function. The .module directive is optional and this adds one record to the Module table. As mentioned earlier we can have only one module directive and not two and if we do not, one gets added for us automatically.

 

We simply display the directive module and follow this with the words MVID. We then call a function DisplayGuid that displays a guid for us. Every application needs to be uniquely identified and the assembler gives it a unique 128 bit number stored in the field Mvid. Each time we regenerate our assembly, this number changes.

 

The reason it is a 128 bit number is because such a number is unique across time and space. The problem with the guid is that it is displayed in a  certain manner and the function DisplayGuid simply displays the bytes from the offset of the guid stream passed as a parameter.

 

For example we display the third byte first followed by the second etc. This guid is calculated using the ISO/IEC standard 11578:1996. The full form of a GUID is a Globally Unique IDentifier. It is a concept used by CORBA and OLE in the past.

 

The VES (Virtual Execution System or Runtime) does not make any use of the Guid but debuggers should use this number to uniquely identify the module. The name of the file is not the physical file name but the logical name stored in the metadata.

 

The module table is the first one the designers of the metadata thought of as they gave it a number of 0. The generation field is reserved and has a value of zero. EncId and EncBaseId are also reserved but they are indexes into th Guid heap and also have a value of 0. Both Mvid and the name field have to have a non null value.

 

If you remember earlier we had figured some instance variables like file alignment and subsystem. We are simply displaying these values here. The Imagebase, subsystem and file alignment variables we have already displayed earlier. The ImageBase is once again displayed within comments with a constant value.

 

Some points that we missed about these values we will explain now. The corflags directive was not written by us and the CLI expects the value to be 1. For backwards compatibility the least 3 significant bits are reserved. Thus the values from 8 to 65535 will be used by future versions.

 

The guys who create experimental and or non standard versions have the blessings of the .Net standard to use values larger than 65535. The subsystem directive is used only when we execute the assembly and thus dll’s have no use for it. This may be a 32 bit number but it can have only two possible values.

 

A value of 2 means that the program should be run using whatever conventions are fit for a GUI applications. A value of 3 is for a console application. At this point in time there is no third environment for a application to execute. The file alignment can only be values that are a multiple of 512 bytes.

 

Program16.csc

string [] vtfixuparray;

 

public void abc(string [] args)

{

ReadPEStructures(args);

DisplayPEStructures();

ReadandDisplayImportAdressTable();

ReadandDisplayCLRHeader();

ReadStreamsData();

FillTableSizes();

ReadTablesIntoStructures();

DisplayTablesForDebugging();

ReadandDisplayVTableFixup();

ReadandDisplayExportAddressTableJumps();

DisplayModuleRefs();

DisplayAssembleyRefs();

DisplayAssembley();

DisplayFileTable();

DisplayClassExtern();

DisplayResources();

DisplayModuleAndMore();

DispalyVtFixup();

}

 

public void ReadandDisplayVTableFixup()

{

if ( MethodStruct == null)

return;

if ( vtablerva != 0)

{

long save ;

long position = ConvertRVA(vtablerva) ;

if ( position == -1)

return;

mfilestream.Position = position;

Console.WriteLine("// VTableFixup Directory:");

int count1 = vtablesize/8;

vtfixuparray = new string[count1];

for ( int ii = 0 ; ii < count1 ; ii++)

{

vtfixuparray[ii] = ".vtfixup ";

int fixuprva = mbinaryreader.ReadInt32();

Console.WriteLine("//   IMAGE_COR_VTABLEFIXUP[{0}]:" , ii);

Console.WriteLine("//       RVA:               {0}",fixuprva.ToString("x8"));

short count = mbinaryreader.ReadInt16();

Console.WriteLine("//       Count:             {0}", count.ToString("x4"));

short type = mbinaryreader.ReadInt16();

Console.WriteLine("//       Type:              {0}", type.ToString("x4"));

save = mfilestream.Position;

mfilestream.Position = ConvertRVA(fixuprva) ;

int i1 ;

long [] val = new long[count] ;

for ( i1 = 0 ; i1 < count ; i1++)

{

if ( (type&0x01)  == 0x01)

val[i1] = mbinaryreader.ReadInt32();

if ( (type&0x02)  == 0x02)

val[i1] = mbinaryreader.ReadInt64();

if ( (type&0x01)  == 0x01 )

Console.WriteLine("//         [{0}]            ({1})",i1.ToString("x4") , val[i1].ToString("X8"));

if ( (type&0x02)  == 0x02)

Console.WriteLine("//         [{0}]            (         {1})",i1.ToString("x4") , (val[i1]&0xffffffff).ToString("X"));

}

mfilestream.Position = save;

vtfixuparray[ii] = vtfixuparray[ii] + "[" + (i1).ToString("X") + "] ";

if ( (type&0x01)  == 0x01)

vtfixuparray[ii] = vtfixuparray[ii] + "int32 ";

if ( (type&0x02)  == 0x02)

vtfixuparray[ii] = vtfixuparray[ii] + "int64 ";

if ( (type&0x04)  == 0x04)

vtfixuparray[ii] = vtfixuparray[ii] + "fromunmanaged ";

vtfixuparray[ii] = vtfixuparray[ii] + "at D_" + fixuprva.ToString("X8");

vtfixuparray[ii] = vtfixuparray[ii] + " //";

for ( i1 = 0 ; i1 < count ; i1++)

{

if ( (type&0x01)  == 0x01)

vtfixuparray[ii] = vtfixuparray[ii]  + " " + val[i1].ToString("X8");

if ( (type&0x02)  == 0x02)

vtfixuparray[ii] = vtfixuparray[ii] + " " + val[i1].ToString("X16");

}

}

Console.WriteLine();

}

}

 

public void DispalyVtFixup()

{

if (vtfixuparray == null)

return;

for ( int ii = 0 ; ii < vtfixuparray.Length ; ii++)

Console.WriteLine(vtfixuparray[ii]);

}

 

e.il

.class public a11111

{

.method public static void  adf() cil managed

{

.entrypoint

}

.method  public int64  a1() cil managed

{

}

.method  public int64  a2() cil managed

{

}

.method  public int64  a3() cil managed

{

}

.method  public int64  a4() cil managed

{

}

.method  public int64  a5() cil managed

{

}

.method  public int64  a6() cil managed

{

}

.method  public int64  a7() cil managed

{

}

}

.vtfixup [1] int32 at D_00008010

.vtfixup [1] int32 fromunmanaged at D_00008020

.vtfixup [1] int64 at D_00008030

.vtfixup [1] int64 fromunmanaged at D_00008040

.vtfixup [0] int64 at D_00008050

.vtfixup [2] int64 int64 at D_00008060

.data D_00008010 = bytearray ( 01 00 00 06)

.data D_00008020 = bytearray ( 02 00 00 06)

.data D_00008030 = bytearray ( 03 00 00 06)

.data D_00008040 = bytearray ( 04 00 00 06)

.data D_00008050 = bytearray ( 05 00 00 06)

.data D_00008060 = bytearray ( 06 00 00 06 00 00 00 00 07 00 00 06 00 00 00 00)

 

Output

.vtfixup [1] int32 at D_00004000 // 06000001

.vtfixup [1] int32 fromunmanaged at D_00004004 // 06000002

.vtfixup [1] int64 at D_00004008 // 0600000406000003

.vtfixup [1] int64 fromunmanaged at D_0000400C // 0600000506000004

.vtfixup [0] int64 at D_00004010 //

.vtfixup [2] int64 at D_00004014 // 0000000006000006 0000000006000007

 

If you look at program7.csc carefully, we displayed the directive vtfixup. If you looked at the output of that program very carefully, you would have realized that it was all in comments. We did not display the actual directive vtfixup at that time.

 

We have added a instance array vtfixuparray and we also call a function DispalyVtFixup in the function abc to display the directive. We have also added some more code in the ReadandDisplayVTableFixup function that will populate the array vtfixuparray and then display it later at the end.

 

As explained before the count1 variable is a count of the items and we use this to give us an array of the desired size. Creating an array of size is not an error and the array just does not get created. We use the same variable count1 to also create the array val to store the individual values.

 

Depending upon the type being a int32 or int64 we read either 4 or 8 bytes into the corresponding val array. We then create the entire string into the vtfixuparray and use the type variable to determine the width of the table. The fixuprva gives us the data address which we concatenate with a D_.

 

Then we place the comments and after this we need the values that we wrote in the bytearray. These depend upon the count value we specified in the square brackets. Thus we use the same loop again and concatenate the bytearray values, reading either 4 or 8 bytes depending as we said on the width of the table.

 

When we look at the DispalyVtFixup function, it simply displays the members of the vtfixuparray.

 

 

Program17.csc

public void abc(string [] args)

{

ReadPEStructures(args);

DisplayPEStructures();

ReadandDisplayImportAdressTable();

ReadandDisplayCLRHeader();

ReadStreamsData();

FillTableSizes();

ReadTablesIntoStructures();

DisplayTablesForDebugging();

ReadandDisplayVTableFixup();

ReadandDisplayExportAddressTableJumps();

DisplayModuleRefs();

DisplayAssembleyRefs();

DisplayAssembley();

DisplayFileTable();

DisplayClassExtern();

DisplayResources();

DisplayModuleAndMore();

DispalyVtFixup();

DisplayTypeDefs();

}

 

public void DisplayTypeDefs ()

{

if ( TypeDefStruct.Length != 2)

{

Console.WriteLine("//");

Console.WriteLine("// ============== CLASS STRUCTURE DECLARATION ==================");

Console.WriteLine("//");

writenamespace = true;

for ( int i = 2 ; i < TypeDefStruct.Length ; i++)

{

if ( GetString(TypeDefStruct[i].name) == "_Deleted"  && streamnames[0] == "#-")

{

continue;

}

if ( ! IsTypeNested(i) )

{

DisplayOneTypePrototype(i);

}

}

}

}

public bool IsTypeNested (int typeindex)

{

if (NestedClassStruct == null)

return false;

for ( int ii = 1 ;  ii< NestedClassStruct.Length ; ii++)

{

if ( NestedClassStruct[ii].nestedclass == typeindex)

return true;

}

return false;

}

public void DisplayOneTypePrototype (int typedefindex)

{

DisplayOneTypeDefStart(typedefindex);

DisplayNestedTypesPrototypes(typedefindex);

DisplayOneTypeDefEnd(typedefindex);

}

public void DisplayOneTypeDefStart (int typerow)

{

string namespacename = NameReserved(GetString(TypeDefStruct[typerow].nspace));

if (  namespacename != "")

{

if ( writenamespace )

{

Console.WriteLine(".namespace {0}" , namespacename );

Console.WriteLine("{"  );

spacefornamespace = 2;

spacesforrest = 4;

}

}

string typestring = "";

if ( IsTypeNested(typerow))

typestring = typestring + CreateSpaces(spacesfornested);

typestring = typestring + CreateSpaces(spacefornamespace);

typestring = typestring + ".class /*02" + typerow.ToString("X6") + "*/ ";

string attributeflags = GetTypeAttributeFlags(TypeDefStruct[typerow].flags , typerow);

Console.WriteLine("{0}{1}{2}" , typestring ,  attributeflags , NameReserved(GetString(TypeDefStruct[typerow].name)));

string tablename = GetTypeDefOrRefTable(TypeDefStruct[typerow].cindex);

int index = GetTypeDefOrRefValue(TypeDefStruct[typerow].cindex);

string typeextends = "";

if ( tablename == "TypeRef" )

{

typeextends = DisplayTypeRefExtends(index);

}

if ( tablename == "TypeDef" )

{

typeextends =  GetNestedTypeAsString(index) + DisplayTypeDefExtends(index);

}

if ( typeextends.Length != 0)

{

typestring = "";

if ( IsTypeNested(typerow))

typestring = typestring + CreateSpaces(spacesfornested);

typestring = typestring + CreateSpaces(spacefornamespace);

typestring = typestring + "       extends " + typeextends;

Console.WriteLine(typestring);

}

string interfacestring = DisplayAllInterfaces(typerow);

if ( interfacestring.Length != 0)

{

typestring = "";

if ( IsTypeNested(typerow))

typestring = typestring + CreateSpaces(spacesfornested);

typestring = typestring + CreateSpaces(spacefornamespace);

typestring = typestring + "       implements " + interfacestring;

Console.Write(typestring);

}

typestring = "";

if ( IsTypeNested(typerow))

typestring = typestring + CreateSpaces(spacesfornested);

typestring = typestring + CreateSpaces(spacefornamespace);

typestring = typestring + "{";

Console.WriteLine(typestring);

}

public string GetTypeAttributeFlags (int typeattributeflags , int typeindex)

{

string returnstring = "";

int visibiltymask = typeattributeflags & 0x07;

string visibiltymaskstring="";

if ( visibiltymask == 0)

visibiltymaskstring = "private ";

if ( visibiltymask == 1)

visibiltymaskstring = "public ";

if ( visibiltymask == 2)

visibiltymaskstring = "nested public ";

if ( visibiltymask == 3)

visibiltymaskstring = "nested private ";

if ( visibiltymask == 4)

visibiltymaskstring = "nested family ";

if ( visibiltymask == 5)

visibiltymaskstring = "nested assembly ";

if ( visibiltymask == 6)

visibiltymaskstring = "nested famandassem ";

if ( visibiltymask == 7)

visibiltymaskstring = "nested famorassem ";

int classlayoutmask = typeattributeflags & 0x18;

string classlayoutstring = "";

if ( classlayoutmask == 0)

classlayoutstring = "auto ";

if ( classlayoutmask == 0x08)

classlayoutstring = "sequential ";

if ( classlayoutmask == 0x10)

classlayoutstring = "explicit ";

string interfacestring = "";

if ( (typeattributeflags & 0x20) == 0x20)

interfacestring  = "interface ";

string abstractstring = "";

if ( (typeattributeflags & 0x80) == 0x80)

abstractstring  = "abstract ";

string sealedstring = "";

if ( (typeattributeflags & 0x100) == 0x100)

sealedstring  = "sealed ";

string specialnamestring = "";

if ( (typeattributeflags & 0x400) == 0x400)

specialnamestring  = "specialname ";

string importstring = "";

if ( (typeattributeflags & 0x1000) == 0x1000)

importstring  = "import ";

string serializablestring = "";

if ( (typeattributeflags & 0x2000) == 0x2000)

serializablestring = "serializable ";

int stringformatmask = typeattributeflags & 0x30000;

string stringformastring = "";

if ( stringformatmask == 0)

stringformastring = "ansi ";

if ( stringformatmask == 0x10000)

stringformastring = "unicode ";

if ( stringformatmask == 0x20000)

stringformastring = "autochar ";

string beforefieldinitstring = "";

if ( (typeattributeflags & 0x00100000) == 0x00100000)

beforefieldinitstring = "beforefieldinit ";

//string rtspecialnamestring = "";

//if ( (typeattributeflags & 0x800) == 0x800)

//rtspecialnamestring = "rtspecialname ";

if ( IsTypeNested(typeindex) )

returnstring = interfacestring + abstractstring + classlayoutstring + stringformastring +  serializablestring + sealedstring + importstring + visibiltymaskstring  + beforefieldinitstring;

else

returnstring = interfacestring + visibiltymaskstring + abstractstring + classlayoutstring + stringformastring + importstring + serializablestring + sealedstring + specialnamestring + beforefieldinitstring ;

return returnstring;

}

public string DisplayTypeDefExtends (int typedefindex)

{

if ( typedefindex == 0)

return "";

string name = NameReserved(GetString(TypeDefStruct[typedefindex].name));

string returnstring = NameReserved(GetString(TypeDefStruct[typedefindex].nspace));

if ( returnstring.Length != 0)

returnstring = returnstring + ".";

returnstring = returnstring + name  + "/* 02" + typedefindex.ToString("X6") + " */";

return returnstring;

}

public string GetNestedTypeAsString(int rowindex)

{

string netsedtypestring = "";

string namespaceandnameparent2 = "";

string namespaceandnameparent3= "";

if ( IsTypeNested(rowindex) )

{

int rowindexparent = GetParentForNestedType(rowindex);

if ( IsTypeNested(rowindexparent) )

{

int rowindexparentparent = GetParentForNestedType(rowindexparent);

if ( IsTypeNested(rowindexparentparent) )

{

int rowindexp3 = GetParentForNestedType(rowindexparentparent);

string nameparent3 = NameReserved(GetString(TypeDefStruct[rowindexp3].name));

namespaceandnameparent3= NameReserved(GetString(TypeDefStruct[rowindexp3].nspace));

if ( namespaceandnameparent3.Length != 0)

namespaceandnameparent3 = namespaceandnameparent3 + ".";

namespaceandnameparent3= namespaceandnameparent3 + nameparent3 + "/* 02" + rowindexp3.ToString("X6") + " *//";

}

string nameparent2 = NameReserved(GetString(TypeDefStruct[rowindexparentparent].name));

namespaceandnameparent2 = NameReserved(GetString(TypeDefStruct[rowindexparentparent].nspace));

if ( namespaceandnameparent2.Length != 0)

namespaceandnameparent2 = namespaceandnameparent2 + ".";

namespaceandnameparent2 = namespaceandnameparent3 + namespaceandnameparent2 + nameparent2 + "/* 02" + rowindexparentparent.ToString("X6") + " *//";

}

string nameparent1 = NameReserved(GetString(TypeDefStruct[rowindexparent].name));

netsedtypestring = NameReserved(GetString(TypeDefStruct[rowindexparent].nspace));

if ( netsedtypestring.Length != 0)

netsedtypestring = netsedtypestring + ".";

netsedtypestring = namespaceandnameparent2 + netsedtypestring + nameparent1 + "/* 02" + rowindexparent.ToString("X6") + " *//";

}

return netsedtypestring;

}

public int GetParentForNestedType (int typeindex)

{

int ii = 0;

if ( NestedClassStruct == null)

return 0;

for ( ii = 0 ; ii < NestedClassStruct.Length - 1 ; ii++)

{

if ( typeindex == NestedClassStruct[ii].nestedclass )

break;

}

return NestedClassStruct[ii].enclosingclass;

}

public string DisplayTypeRefExtends (int typerefindex)

{

string returnstring = "";

int resolutionscope = TypeRefStruct[typerefindex].resolutionscope;

string resolutionscopetable = GetResolutionScopeTable(resolutionscope);

int resolutionscopeindex = GetResolutionScopeValue(resolutionscope);

string dummy = "";

if ( resolutionscopetable == "Module")

{

}

if ( resolutionscopetable == "AssemblyRef")

{

returnstring = "[" + NameReserved(GetString(AssemblyRefStruct[resolutionscopeindex].name)) ;

returnstring = returnstring + "/* 23" + resolutionscopeindex.ToString("X6") + " */]";

}

if ( resolutionscopetable == "ModuleRef")

{

returnstring = "[.module " + NameReserved(GetString(ModuleRefStruct[resolutionscopeindex].name))  ;

returnstring = returnstring + "/* 1A" + resolutionscopeindex.ToString("X6") + " */]";

}

if ( resolutionscopetable == "TypeRef")

{

int resolutionscopeindex1 = GetResolutionScopeValue(TypeRefStruct[resolutionscopeindex].resolutionscope );

string resolutionscopetable1 = GetResolutionScopeTable(TypeRefStruct[resolutionscopeindex].resolutionscope );

if ( resolutionscopetable1 == "AssemblyRef")

{

dummy = "[" + NameReserved(GetString(AssemblyRefStruct[resolutionscopeindex1].name)) + "/* 23" + resolutionscopeindex1.ToString("X6") + " */]";

string nspace1 = NameReserved(GetString(TypeRefStruct[resolutionscopeindex].nspace));

if ( nspace1 != "")

nspace1 = nspace1 + ".";

dummy = dummy + nspace1 + NameReserved(GetString(TypeRefStruct[resolutionscopeindex].name)) + "/* 01" + resolutionscopeindex.ToString("X6") + " *//";

}

}

int namespaceindex = TypeRefStruct[typerefindex].nspace;

string nspace = NameReserved(GetString(namespaceindex));

returnstring = returnstring + nspace ;

if ( nspace.Length  != 0)

returnstring = returnstring + ".";

int nameindex = TypeRefStruct[typerefindex].name;

returnstring = dummy + returnstring + NameReserved(GetString(nameindex)) + "/* 01" + typerefindex.ToString("X6") + " */";

return returnstring;

}

public string DisplayAllInterfaces (int typeindex)

{

string returnstring = "";

if ( InterfaceImplStruct == null || InterfaceImplStruct.Length == 1)

return "";

for ( int i = 1 ; i < InterfaceImplStruct.Length ; i++)

{

if ( typeindex == InterfaceImplStruct[i].classindex  )

{

string codedtablename = GetTypeDefOrRefTable(InterfaceImplStruct[i].interfaceindex);

int interfaceindex = GetTypeDefOrRefValue(InterfaceImplStruct[i].interfaceindex);

string interfacename = "";

if ( codedtablename == "TypeRef" )

interfacename  = DisplayTypeRefExtends(interfaceindex);

if ( codedtablename == "TypeDef" )

interfacename  = GetNestedTypeAsString(interfaceindex) + DisplayTypeDefExtends(interfaceindex);

returnstring = returnstring + interfacename;

bool nextclassindex ;

if ( i == (InterfaceImplStruct.Length - 1))

nextclassindex = false;

else if ( typeindex != InterfaceImplStruct[i+1].classindex  )

nextclassindex = false;

else

nextclassindex = true;

if ( nextclassindex )

returnstring = returnstring + ",\r\n                  " + CreateSpaces(spacefornamespace+spacesfornested);

else

returnstring = returnstring + "\r\n";

}

}

return returnstring;

}

public string GetTypeDefOrRefTable (int codedvalue)

{

string returnstring = "";

short tag = (short)(codedvalue & (short)0x03);

if ( tag  == 0)

returnstring = returnstring + "TypeDef";

if ( tag  == 1)

returnstring = returnstring + "TypeRef";

if ( tag  == 2)

returnstring = returnstring + "TypeSpec";

return returnstring;

}

public int GetTypeDefOrRefValue(int codedvalue)

{

return codedvalue >> 2;

}

public void DisplayNestedTypesPrototypes (int typedefindex)

{

if (NestedClassStruct == null)

return ;

for ( int ii = 1 ; ii < NestedClassStruct.Length ; ii++)

{

if (NestedClassStruct[ii].enclosingclass == typedefindex)

{

spacesfornested += 2;

DisplayOneTypePrototype(NestedClassStruct[ii].nestedclass  );

spacesfornested -= 2;

}

}

}

public void DisplayOneTypeDefEnd (int typeindex )

{

string dummy = "";

if ( IsTypeNested(typeindex) )

dummy = dummy + CreateSpaces(spacesfornested);

dummy = dummy + CreateSpaces(spacefornamespace);

dummy = dummy + "} // end of class ";

string classname = NameReserved(GetString(TypeDefStruct[typeindex].name));

dummy = dummy + classname ;

Console.WriteLine(dummy);

string namespacename = NameReserved(GetString(TypeDefStruct[typeindex].nspace));

Console.WriteLine();

if (  namespacename != "")

{

string nspace1 = NameReserved(GetString(TypeDefStruct[typeindex].nspace));

int ii;

for ( ii = typeindex + 1 ; ii < TypeDefStruct.Length - 1 ; ii++)

{

if ( IsTypeNested(ii) )

continue;

break;

}

string nspace2 = "";

if ( ii != TypeDefStruct.Length )

nspace2 = NameReserved(GetString(TypeDefStruct[ii].nspace));

if ( nspace1 != nspace2 )

{

if ( lasttypedisplayed == typeindex && notprototype  )

{

Console.WriteLine();

Console.WriteLine("// =============================================================");

Console.WriteLine();

placedend = true;

}

Console.Write("}");

Console.WriteLine(" // end of namespace {0}", namespacename);

spacefornamespace = 0;

spacesforrest = 2;

writenamespace = true;

Console.WriteLine();

}

else

writenamespace = false;

}

}

 

e.il

.class public abstract beforefieldinit a1

{

.method public static void Main()

{

.entrypoint

}

}

.class private sequential unicode explicit sealed specialname rtspecialname interface _Deleted

{

}

.namespace n1

{

.namespace n2

{

.class public autochar a2

{

.class public a3

{

.class a33

{

.class a333

{

.class a3333

{

}

}

}

}

.class a4

{

}

}

}

}

.class a5

{

}

.class a6 extends a5

{

}

.class a7 extends n1.n2.a2/a3/a33/a333

{

}

.class a71 extends n1.n2.a2/a3

{

}

.class a72 extends n1.n2.a2/a3/a33

{

}

 

.class a8 extends [mscorlib]aaa

{

}

.class a9 extends [mscorlib]ppp.aaa/aa

{

}

.module extern bb

.module e.exe

.class a10 extends [.module bb]a11

{

}

.class a12 extends [.module e.exe]a13

{

}

.assembly ee

{

}

.class a14 extends [ee]a13

{

}

.class interface a15

{

}

.class interface a16

{

}

.class a17 implements a15,a16,[mscorlib]aa

{

}

 

Output

.module extern bb /*1A000001*/

.assembly extern /*23000001*/ mscorlib

{

  .ver 0:0:0:0

}

.assembly extern /*23000002*/ ee

{

  .ver 0:0:0:0

}

.assembly /*20000001*/ ee

{

  .ver 0:0:0:0

}

.module e.exe

// MVID: {DB25BB73-A933-4B68-9124-8F5135AC4D55}

.imagebase 0x00400000

.subsystem 0x00000003

.file alignment 512

.corflags 0x00000001

// Image base: 0x03000000

//

// ============== CLASS STRUCTURE DECLARATION ==================

//

.class /*02000002*/ public abstract auto ansi beforefieldinit a1

       extends [mscorlib/* 23000001 */]System.Object/* 01000001 */

{

} // end of class a1

 

.class /*02000003*/ interface private abstract explicit unicode sealed specialname _Deleted

{

} // end of class _Deleted