Click here to Skip to main content
15,867,453 members
Articles / Desktop Programming / Win32

Analyzing a .NET Executable or DLL without .NET Installed

Rate me:
Please Sign up or sign in to vote.
4.86/5 (59 votes)
15 Feb 2013CPOL 65.4K   1.4K   68   48
A pure Win32 API application that can analyze a .NET binary without .NET installed
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 working of the application is very simple. You run it.

321269/net-analyze.jpg

You select an executable.

If it is a .NET executable, you will be able to see a treeview of all the classes within this file.

321269/net-analyze1.jpg

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

321269/net-analyze2.jpg

Using the Code

GetDotNetClassName is used after opening the executable binary file:

C#
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:

C#
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);

    //read meta data table
    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;
        }
    }

    //read Module Table
    if(MetadataTable[0] != 0)
    {
        int rowSize = 2 + ((HeapOffSetSize & 0x01) ? 4: 2) + 
                      3 * ((HeapOffSetSize & 0x02)? 4 : 2);
        //byte to skip
        i16Length   += rowSize * MetadataTable[0]; 
    }

    //read Typeref Table
    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;
    //read typedef table
    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);
        }
    }

    //now read the strings
    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 source code which accompanies this article demonstrates the exact way to implement that, and the executable is included as well.

Haephrati-Net-Analyze-src.zip

History

  • 30th January, 2012: Initial version

Michael Haephrati , CodeProject MVP 2013

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
CEO Secured Globe, Inc.
United States United States
Michael Haephrati is a music composer, an inventor and an expert specializes in software development and information security, who has built a unique perspective which combines technology and the end user experience. He is the author of a the book Learning C++ , which teaches C++ 20, and was published in August 2022.

He is the CEO of Secured Globe, Inc., and also active at Stack Overflow.

Read our Corporate blog or read my Personal blog.





Comments and Discussions

 
QuestionMetadata (Unmanaged API) Pin
.:floyd:.6-Mar-14 23:50
.:floyd:.6-Mar-14 23:50 
QuestionInteresting, but why? Pin
Rob Philpott7-Jan-14 22:31
Rob Philpott7-Jan-14 22:31 
AnswerRe: Interesting, but why? Pin
Michael Haephrati9-Jan-14 6:37
professionalMichael Haephrati9-Jan-14 6:37 
GeneralMy vote of 5 Pin
alonamir12-Aug-13 22:00
alonamir12-Aug-13 22:00 
GeneralMy vote of 5 Pin
alonbarak14-Jun-13 11:08
alonbarak14-Jun-13 11:08 
GeneralMy vote of 5 Pin
Member 416352421-Feb-13 5:33
Member 416352421-Feb-13 5:33 
GeneralMy vote of 5 Pin
Ruth Aanie20-Feb-13 21:30
Ruth Aanie20-Feb-13 21:30 
QuestionIt's good to see you back. Pin
Pete O'Hanlon19-Feb-13 1:50
subeditorPete O'Hanlon19-Feb-13 1:50 
QuestionDo you not understand that I'm trying to help you here? Pin
Pete O'Hanlon18-Feb-13 9:30
subeditorPete O'Hanlon18-Feb-13 9:30 
QuestionPlease stop. Pin
Pete O'Hanlon18-Feb-13 9:18
subeditorPete O'Hanlon18-Feb-13 9:18 
GeneralMy vote of 3 Pin
Paulo Zemek16-Feb-13 14:28
mvaPaulo Zemek16-Feb-13 14:28 
GeneralMy vote of 5 Pin
BH TAN16-Feb-13 4:53
professionalBH TAN16-Feb-13 4:53 
SuggestionRefinements Pin
Jaime Olivares28-Jan-13 5:26
Jaime Olivares28-Jan-13 5:26 
GeneralMy vote of 5 Pin
liliflower35525-Jan-13 1:17
liliflower35525-Jan-13 1:17 
GeneralMy vote of 5 Pin
resi243125-Jan-13 0:14
resi243125-Jan-13 0:14 
GeneralMy vote of 5 Pin
midulm24-Jan-13 23:08
midulm24-Jan-13 23:08 
GeneralMy vote of 5 Pin
balam198824-Jan-13 22:23
balam198824-Jan-13 22:23 
GeneralMy vote of 5 Pin
evan89724-Jan-13 21:44
evan89724-Jan-13 21:44 
GeneralMy vote of 5 Pin
Ruth Aanie24-Jan-13 19:30
Ruth Aanie24-Jan-13 19:30 
GeneralMy vote of 1 Pin
PJohnMathews23-Jan-13 19:57
PJohnMathews23-Jan-13 19:57 
Questionnot working on exes built with .net4.0 Pin
AnandChavali22-Jan-13 23:36
AnandChavali22-Jan-13 23:36 
GeneralMy vote of 5 Pin
Babs29115-Oct-12 9:22
Babs29115-Oct-12 9:22 
GeneralMy vote of 5 Pin
Rumon200014-Oct-12 3:00
Rumon200014-Oct-12 3:00 
GeneralMy vote of 5 Pin
Jason44413-Oct-12 21:46
Jason44413-Oct-12 21:46 
GeneralMy vote of 5 Pin
Member 951180113-Oct-12 19:36
Member 951180113-Oct-12 19:36 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.