Introduction
This article describes how to add to a Win32 API, C++ application, the ability to browse for executables (.exe) files, and to determine whether they are .NET ones, and if so, to
analyze their classes and display a list of them, without having .NET installed on the machine running it.
Background
.NET executables can be easily analyzed, and each class can be enumerated. However, some users prefer not to install the .NET Framework because they don't need it or because
it is quite heavy. There seems to be a need to allow applications that are not .NET based, to analyze .NET executables.
The Application
The application works very simple. You run it.

You select an executable.
If it is a .NET executable, you will be able to see a tree view of all the classes within this file.

If it is not a .NET executable, the following message will popup:

Using the code
GetDotNetClassName is used after opening the executable binary file:
CPEParser objPEParser;
list<string> lString= objPEParser.GetDotNetClassName(ofn.lpstrFile);
Please note that if the list has 0 items, we can determine that the executable is not a .NET one, but Win32.
Here is the GetDotNetClassName function:
list<string> CPEParser::GetDotNetClassName(string filePathName)
{
list<string> lString;
HANDLE hFile = CreateFile(filePathName.c_str(),
GENERIC_READ, FILE_SHARE_READ, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
HANDLE hMapFile = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, "NetExe");
SYSTEM_INFO systemInfo;
GetSystemInfo(&systemInfo);
char *pFileBase = (char *)MapViewOfFile(hMapFile, FILE_MAP_READ, 0, 0, 0);
IMAGE_DOS_HEADER *pImageDosHeader = reinterpret_cast<IMAGE_DOS_HEADER *>(pFileBase);
IMAGE_NT_HEADERS *pImageNTHeader =
reinterpret_cast<IMAGE_NT_HEADERS *>(pFileBase + pImageDosHeader->e_lfanew);
IMAGE_FILE_HEADER *pImageFileHeader =
reinterpret_cast<IMAGE_FILE_HEADER *>(&pImageNTHeader->FileHeader);
IMAGE_OPTIONAL_HEADER *pImageOpHeader =
reinterpret_cast<IMAGE_OPTIONAL_HEADER *>(&pImageNTHeader->OptionalHeader);
IMAGE_DATA_DIRECTORY *entry =
&pImageOpHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR];
if(entry->Size == 0 || entry->Size < sizeof(IMAGE_COR20_HEADER) ||
entry->VirtualAddress == 0)
{
return lString;
}
IMAGE_COR20_HEADER *pClrHeader = reinterpret_cast<IMAGE_COR20_HEADER *>(
ImageRvaToVa(pImageNTHeader, pFileBase, entry->VirtualAddress, 0));
char *pMetaDataAddress = reinterpret_cast<char *>(ImageRvaToVa(
pImageNTHeader, pFileBase, pClrHeader->MetaData.VirtualAddress, 0));
int mdSignature = *(reinterpret_cast<int *>(pMetaDataAddress));
short majorVersion = *(reinterpret_cast<short *>(pMetaDataAddress + 4));
short minorVersion = *(reinterpret_cast<short *>(pMetaDataAddress + 6));
int reserved = *(reinterpret_cast<int *>(pMetaDataAddress + 8));
int length = *(reinterpret_cast<int *>(pMetaDataAddress + 12));
string version;
for(int i = 16; i < (length + 16); i++)
{
version.append(1, *(reinterpret_cast<char *>(pMetaDataAddress + i)));
}
int reserved2 = *(reinterpret_cast<short *>(pMetaDataAddress + 16 + length));
int streams = *(reinterpret_cast<short *>(pMetaDataAddress + 18 + length));
int i16Length = 20 + length;
list<StreamHeader> lStreamHeader;
GetStreamHeaders(pMetaDataAddress, i16Length, lStreamHeader, streams);
char *pMetaDataTable = pMetaDataAddress + GetMetaData(lStreamHeader, "#~")->offset;
int reserved3 = *(reinterpret_cast<int *>(pMetaDataTable));
char majorVersion1 = *(pMetaDataTable + 4);
char minorVersion1 = *(pMetaDataTable + 5);
char HeapOffSetSize = *(pMetaDataTable + 6);
char reserved4 = *(pMetaDataTable + 7);
int valid1 = *(reinterpret_cast<int *>(pMetaDataTable + 8));
int valid2 = *(reinterpret_cast<int *>(pMetaDataTable + 12));
int sort1 = *(reinterpret_cast<int *>(pMetaDataTable + 16));
int sort2 = *(reinterpret_cast<int *>(pMetaDataTable + 20));
int MetadataTable[64];
i16Length = 24;
for(int i = 0; i < 32 ; i++)
{
if((valid1 >> i) & 1)
{
MetadataTable[i] = *(reinterpret_cast<int *>(pMetaDataTable + i16Length));
i16Length += 4;
}
else
{
MetadataTable[i] = 0;
}
}
for(int i = 0; i < 32 ; i++)
{
if((valid2 >> i) & 1)
{
MetadataTable[i + 32] = *(reinterpret_cast<int *>(pMetaDataTable + i16Length));
i16Length += 4;
}
else
{
MetadataTable[i + 32] = 0;
}
}
if(MetadataTable[0] != 0)
{
int rowSize = 2 + ((HeapOffSetSize & 0x01) ? 4: 2) + 3 * ((HeapOffSetSize & 0x02)? 4 : 2);
i16Length += rowSize * MetadataTable[0];
}
if(MetadataTable[1] != 0)
{
int MaxRow = MetadataTable[0] + MetadataTable[26] + MetadataTable[35] + MetadataTable[01];
int rowSize = ((MaxRow < 65536)? 2 : 4) + 2 * ((HeapOffSetSize & 0x01) ? 4: 2);
i16Length += rowSize * MetadataTable[1];
}
list<TypeDef> lTypeDef;
if(MetadataTable[2] != 0)
{
int flagSize = 4;
int szTypeName = ((HeapOffSetSize & 0x01) ? 4: 2);
int MaxRow = MetadataTable[2] + MetadataTable[1] + MetadataTable[27];
int szExtend = (MaxRow < 65536)? 2 : 4;
int szField = (MetadataTable[4] < 65536)? 2 : 4;
int szMethod = (MetadataTable[6] < 65536)? 2 : 4;
TypeDef sTypeDef;
for(int i = 0; i < MetadataTable[2]; i++)
{
sTypeDef.iFlag = *(reinterpret_cast<int *>(pMetaDataTable + i16Length));
i16Length += flagSize;
if(szTypeName == 2)
sTypeDef.iTypeName = *(reinterpret_cast<short *>(pMetaDataTable + i16Length));
else
sTypeDef.iTypeName = *(reinterpret_cast<int *>(pMetaDataTable + i16Length));
i16Length += szTypeName;
if(szTypeName == 2)
sTypeDef.iTypeNameSpace = *(reinterpret_cast<short *>(pMetaDataTable + i16Length));
else
sTypeDef.iTypeNameSpace = *(reinterpret_cast<int *>(pMetaDataTable + i16Length));
i16Length += szTypeName;
if(szExtend == 2)
sTypeDef.iExtends = *(reinterpret_cast<short *>(pMetaDataTable + i16Length));
else
sTypeDef.iExtends = *(reinterpret_cast<int *>(pMetaDataTable + i16Length));
i16Length += szExtend;
if(szField == 2)
sTypeDef.iFieldList = *(reinterpret_cast<short *>(pMetaDataTable + i16Length));
else
sTypeDef.iFieldList = *(reinterpret_cast<int *>(pMetaDataTable + i16Length));
i16Length += szField;
if(szMethod == 2)
sTypeDef.iMethodList = *(reinterpret_cast<short *>(pMetaDataTable + i16Length));
else
sTypeDef.iMethodList = *(reinterpret_cast<int *>(pMetaDataTable + i16Length));
i16Length+= szMethod;
lTypeDef.push_back(sTypeDef);
}
}
char *pstrStart = pMetaDataAddress + GetMetaData(lStreamHeader, "#Strings")->offset;
for(list<TypeDef>::iterator iteType = lTypeDef.begin();
iteType != lTypeDef.end(); iteType++)
{
lString.push_back(ReadString(pstrStart + (*iteType).iTypeName));
}
UnmapViewOfFile(pFileBase);
CloseHandle(hMapFile);
CloseHandle(hFile);
return lString;
}
The Download Haephrati-Net-Alanyze.zip source code which accompanies this article demonstrates the exact way to implement that, and the executable is included as well.
Michael Haephrati , CodeProject MVP 2013
Michael Haephrati, born in 1964, an entrepreneur, inventor and a musician. Haephrati worked on many ventures starting from HarmonySoft, designing Rashumon, the first Graphical Multi-lingual word processor for Amiga computer.
Worked with
Amdocs and managed several software projects, among them one for the
Ministry of Tourism in New Zealand. During 1995-1996 he worked as a Contractor with
Apple at Cupertino. After returning to Israel, worked as a Project Manager with
Top Image Systems (mostly with
JCC, Nicosia), and then at a research institute made the fist steps developing the credit scoring field in Israel. He founded Target Scoring and developed a credit scoring system named ThiS, based on geographical statistical data, participating VISA CAL, Isracard, Bank Leumi and Bank Discount (Target Scoring, being the VP Business Development of a large Israeli institute).
During 2000, he founded Target Eye, and developed the first remote PC surveillance and monitoring system, named Target Eye.
Other ventures included:
Data Cleansing (as part of the
DataTune system which was implemented in many organizations.