65.9K
CodeProject is changing. Read more.
Home

Analyzing a .NET Executable or DLL without .NET Installed

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.86/5 (54 votes)

Jan 30, 2012

CPOL
viewsIcon

72616

downloadIcon

1439

A pure Win32 API application that can analyze a .NET binary without .NET installed

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:

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

    //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