分析没有安装 .NET 的 .NET 可执行文件或 DLL






4.86/5 (54投票s)
一个纯 Win32 API 应用程序,可以在没有安装 .NET 的情况下分析 .NET 二进制文件
背景
.NET 可执行文件可以很容易地进行分析,并且可以枚举每个类。然而,有些用户更喜欢不安装 .NET Framework,因为他们不需要它,或者因为它比较庞大。似乎存在一种需求,允许非 .NET 应用程序分析 .NET 可执行文件。
应用程序
该应用程序的工作原理非常简单。你运行它。
你选择一个可执行文件。
如果它是一个 .NET 可执行文件,你将能够看到该文件内所有类的 treeview
视图。
如果它不是一个 .NET 可执行文件,将会弹出以下消息
Using the Code
GetDotNetClassName
在打开可执行文件二进制文件后使用
CPEParser objPEParser;
list<string> lString= objPEParser.GetDotNetClassName(ofn.lpstrFile);
请注意,如果列表包含 0 个项目,我们可以确定该可执行文件不是 .NET 文件,而是 Win32 文件。
这是 GetDotNetClassName
函数
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;
}
与本文一起提供的 源代码 演示了确切的实现方式,并且也包含了 可执行文件。
Haephrati-Net-Analyze-src.zip
历史
- 2012 年 1 月 30 日:初始版本