C++ 如何以编程方式确定windows可执行文件DLL依赖项?
如何确定哪个DLL是二进制文件取决于使用编程方法C++ 如何以编程方式确定windows可执行文件DLL依赖项?,c++,windows,dll,dependencies,executable,C++,Windows,Dll,Dependencies,Executable,如何确定哪个DLL是二进制文件取决于使用编程方法 要明确的是,我不是在试图确定正在运行的exec的DLL依赖关系,而是确定任何任意exec(可能缺少所需的DLL)的DLL依赖关系。我正在寻找一个在C/C++应用程序中实现的解决方案。这需要我的应用程序在运行时完成,而第三方应用程序(如依赖)无法完成。这是无法确定的。至少不是没有大量的工作。任何二进制文件都可以调用LoadLibrary来加载DLL。即使要扫描代码以查找对LoadLibrary的所有调用,也必须确定使用了哪些字符串来标识库。跟踪字符
要明确的是,我不是在试图确定正在运行的exec的DLL依赖关系,而是确定任何任意exec(可能缺少所需的DLL)的DLL依赖关系。我正在寻找一个在C/C++应用程序中实现的解决方案。这需要我的应用程序在运行时完成,而第三方应用程序(如依赖)无法完成。这是无法确定的。至少不是没有大量的工作。任何二进制文件都可以调用LoadLibrary来加载DLL。即使要扫描代码以查找对LoadLibrary的所有调用,也必须确定使用了哪些字符串来标识库。跟踪字符串在动态内存中的位置将比您想要解决的困难。看看API。它将返回指向结构的指针,您可以使用该指针访问PE文件的各个部分 您可以找到一些描述结构布局的文章,以及。您可以下载文章的源代码 我想这会给你你需要的一切 更新:
我刚刚下载了这篇文章的源代码。如果打开
EXEDUMP.CPP
并查看DumpImportsSection
它应该有您需要的代码。简而言之,您需要为可执行文件使用的每个DLL扫描PE文件的导入部分。然后递归地定位和扫描每个dll,直到找到所有依赖项
当然,应用程序可以使用LoadLibrary系列函数实现必需或可选功能。使用此方法无法检测到这些信息。您可以调用一个DLL为您计算所有这些信息,并将答案作为CString数组传回,怎么样
我能为你做这件事。提供源代码,没有GPL限制。PE文件资源管理器是一个GUI应用程序,使用DLL,也提供源代码(无GPL) 76行代码(别忘了添加Imagehlp.lib作为依赖项):
#包括
#包括“windows.h”//不要删除它
#包括“ImageHlp.h”
#包括“stdafx.h”
模板PIMAGE\U SECTION\U HEADER GetEnclosuringSeCTIONHEADER(DWORD rva,T*pNTHeader)/‘T’==PIMAGE\U NT\U HEADER
{
PIMAGE_SECTION_HEADER SECTION=图像_FIRST_SECTION(pNTHeader);
未签名的i;
对于(i=0;iFileHeader.NumberOfSections;i++,section++)
{
//这三行的愚蠢是因为Watcom的链接器实际上设置了
//Misc.VirtualSize字段为0。(!!!-延迟…!!)
DWORD大小=节->杂项虚拟化;
如果(0==大小)
尺寸=截面->尺寸框架数据;
//RVA是否在此区域内?
如果((rva>=节->虚拟服装)和
(rva<(节->虚拟服装+尺寸)))
返回段;
}
返回0;
}
模板LPVOID GetPtrFromRVA(DWORD rva,T*pNTHeader,PBYTE imageBase)/“T”=PIMAGE\u NT\u头
{
PIMAGE_段_标题段HDR;
内特三角洲;
pSectionHdr=GetEnclosuringSectionHeader(rva,pNTHeader);
如果(!pSectionHdr)
返回0;
增量=(INT)(pSectionHdr->VirtualAddress pSectionHdr->指针到数据);
返回(PVOID)(imageBase+rva-增量);
}
void DumpDllFromPath(wchar\u t*path){
字符名[300];
wcstombs(名称、路径,300);
PLOADED_IMAGE=ImageLoad(名称,0);
如果(图像->文件头->可选头.NumberOfVandSizes>=2){
PIMAGE\u IMPORT\u描述符importDesc=
(PIMAGE\u IMPORT\u描述符)GetPtrFromRVA(
image->FileHeader->OptionalHeader.DataDirectory[1]。VirtualAddress,
图像->文件头,图像->映射地址);
而(1)
{
//查看是否已到达空图像\u导入\u描述符
如果((importDesc->TimeDateStamp==0)和&(importDesc->Name==0))
打破
printf(“%s\n”),GetPtrFromRVA(importDesc->Name,
图像->文件头,
图像->映射地址);
importDesc++;
}
}
图像卸载(图像);
}
//将exe或dll作为参数传递
int _tmain(int argc,_TCHAR*argv[]
{
DumpDllFromPath(argv[1]);
返回0;
}
如果您有目标可执行文件,则可以使用配置文件菜单执行此操作。只需加载可执行文件,告诉它开始评测,它就会列出执行程序时加载的所有模块
为什么在这里投票给我?就我所知,这个答案在技术上是准确的。关于这个答案被否决的原因,我的猜测是:它没有区分隐式依赖(可以确定,请参阅alex2k8的链接)和显式依赖(这就是您所说的)。别灰心,答案是对的一半@jdigital:这个答案不完全正确。这是一个问题的正确答案,要求以编程方式查找可执行映像的所有依赖项。问题是询问一般情况,因此在实例化COM对象时,您必须使用
LoadLibrary
或隐式加载的模块来说明运行时动态链接。Dependency Walker有一种分析模式,它试图解释在运行时加载的二进制文件,但您不能保证100%的代码覆盖率,所以即使这样也不行。不管你喜欢与否,这是正确的答案。谢谢你的建议。特别是源代码示例的链接。这正是我要找的。检查这个。分析只会看到那些模块的加载,而这些模块恰好是您触发的。除非您能保证100%的代码覆盖率,否则您不能确保找到所有依赖项。然后,您还将看到加载的模块,这些模块不是您的程序严格要求的(例如,打开文件保存对话框时的shell扩展名)。
#include <stdio.h>
#include "windows.h" //DONT REMOVE IT
#include "ImageHlp.h"
#include "stdafx.h"
template <class T> PIMAGE_SECTION_HEADER GetEnclosingSectionHeader(DWORD rva, T* pNTHeader) // 'T' == PIMAGE_NT_HEADERS
{
PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION(pNTHeader);
unsigned i;
for ( i=0; i < pNTHeader->FileHeader.NumberOfSections; i++, section++ )
{
// This 3 line idiocy is because Watcom's linker actually sets the
// Misc.VirtualSize field to 0. (!!! - Retards....!!!)
DWORD size = section->Misc.VirtualSize;
if ( 0 == size )
size = section->SizeOfRawData;
// Is the RVA within this section?
if ( (rva >= section->VirtualAddress) &&
(rva < (section->VirtualAddress + size)))
return section;
}
return 0;
}
template <class T> LPVOID GetPtrFromRVA( DWORD rva, T* pNTHeader, PBYTE imageBase ) // 'T' = PIMAGE_NT_HEADERS
{
PIMAGE_SECTION_HEADER pSectionHdr;
INT delta;
pSectionHdr = GetEnclosingSectionHeader( rva, pNTHeader );
if ( !pSectionHdr )
return 0;
delta = (INT)(pSectionHdr->VirtualAddress-pSectionHdr->PointerToRawData);
return (PVOID) ( imageBase + rva - delta );
}
void DumpDllFromPath(wchar_t* path) {
char name[300];
wcstombs(name,path,300);
PLOADED_IMAGE image=ImageLoad(name,0);
if (image->FileHeader->OptionalHeader.NumberOfRvaAndSizes>=2) {
PIMAGE_IMPORT_DESCRIPTOR importDesc=
(PIMAGE_IMPORT_DESCRIPTOR)GetPtrFromRVA(
image->FileHeader->OptionalHeader.DataDirectory[1].VirtualAddress,
image->FileHeader,image->MappedAddress);
while ( 1 )
{
// See if we've reached an empty IMAGE_IMPORT_DESCRIPTOR
if ( (importDesc->TimeDateStamp==0 ) && (importDesc->Name==0) )
break;
printf(" %s\n", GetPtrFromRVA(importDesc->Name,
image->FileHeader,
image->MappedAddress) );
importDesc++;
}
}
ImageUnload(image);
}
//Pass exe or dll as argument
int _tmain(int argc, _TCHAR* argv[])
{
DumpDllFromPath(argv[1]);
return 0;
}