dll文件判断方法——UPX加壳器中isdll变量
前文提到UPX通过变量isdll判断是否为dll文件,因此UPX源码中搜索isdll字段,找到isdll的来源并观察UPX判断逻辑。
一、UPX代码分析
在src/pefile.cpp中观察到变量isdll的变化,首先默认为为非dll文件(isdll=false)

然后在函数readPeHeader中,有阅读PE文件头部以确定isdll值的语句

对代码进行分析:
fi->readx(&ih, sizeof(ih));此部分调用fi->readx函数阅读PE文件头部,并将相关信息保存在结构体ih(IMAGE_HEADER)中if (31 < (unsigned) ih.subsystem) {throwCantPack("bad ih.subsystem 0x%x", (unsigned) ih.subsystem);}此部分代码检查子系统(ih.subsystem)是否合法(>31)。子系统用于描述程序的运行环境,比如是否图形界面程序、控制台程序,或者是EFI相关的程序;Windows平台的PE文件子系统类型,基本都定义在0到31之间。因此此语句能够有效识别不支持或异常的子系统类型。isefi = ((1u << ih.subsystem) & ((1u << IMAGE_SUBSYSTEM_EFI_APPLICATION) | (1u << IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER) | (1u << IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER) | (1u << IMAGE_SUBSYSTEM_EFI_ROM))) != 0;此部分代码判断是否为EFI文件,通过位运算检查子系统类型是否为EFI相关的类型。其中1u << ih.subsystem将1左移,目的是将1放置到子系统值对应的位置,生成一个二进制数,方便与后面所有EFI相关类型的预变量。EFI子系统的标准定义正为以下四个:IMAGE_SUBSYSTEM_EFI_APPLICATION:EFI应用程序,值为10IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER:EFI引导服务驱动,值为11IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER:EFI运行时驱动,值为12IMAGE_SUBSYSTEM_EFI_ROM:EFI ROM(只读存储器,固件类型),值为13isdll = !isefi && (ih.flags & IMAGE_FILE_DLL) != 0;此部分代码判断是否为DLL文件。如果不是EFI文件(!isefi),并且PE头的标志位中包含IAMGE_FILE_DLL标志,则isdll变量设置为true,表示该文件是一个DLL文件。use_dep_hack &= !isefi;use_clear_dirty_stack &= !isefi;此两句代码确保如果是EFI类型的子系统,use_dep_hack和use_clear_dirty_stack被禁用。具体这些标志的作用不在此代码中体现,可能与文件安全或加载策略相关,比如依赖项修复和栈清理。
二、dll文件判断
根据isdll赋值的方法,写出判断dll文件的程序。对一些样本进行测试,基本验证了其正确性。代码如下(也可见附件isdll.cpp):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 | #include <windows.h>#include <iostream>#include <stdexcept>// 函数:检查文件是否为DLL文件bool isDllFile(const char* filePath) { // 打开文件,使用 CreateFileA 以支持窄字符串 HANDLE file = CreateFileA(filePath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (file == INVALID_HANDLE_VALUE) { throw std::runtime_error("无法打开文件"); } // 创建文件映射 HANDLE fileMapping = CreateFileMapping(file, NULL, PAGE_READONLY, 0, 0, NULL); if (fileMapping == NULL) { CloseHandle(file); throw std::runtime_error("无法创建文件映射"); } // 映射文件到内存 LPVOID fileBase = MapViewOfFile(fileMapping, FILE_MAP_READ, 0, 0, 0); if (fileBase == NULL) { CloseHandle(fileMapping); CloseHandle(file); throw std::runtime_error("无法映射文件"); } // 获取DOS头(IMAGE_DOS_HEADER) PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER)fileBase; if (dosHeader->e_magic != IMAGE_DOS_SIGNATURE) { // 不合法的PE文件 UnmapViewOfFile(fileBase); CloseHandle(fileMapping); CloseHandle(file); throw std::runtime_error("文件不是有效的PE文件"); } // 获取NT头(IMAGE_NT_HEADERS) PIMAGE_NT_HEADERS ntHeaders = (PIMAGE_NT_HEADERS)((BYTE*)fileBase + dosHeader->e_lfanew); if (ntHeaders->Signature != IMAGE_NT_SIGNATURE) { // 不合法的PE文件 UnmapViewOfFile(fileBase); CloseHandle(fileMapping); CloseHandle(file); throw std::runtime_error("文件不是有效的PE文件"); } // 检查标志位(Flags)中的 IMAGE_FILE_DLL 位 bool isDll = (ntHeaders->FileHeader.Characteristics & IMAGE_FILE_DLL) != 0; // 释放资源 UnmapViewOfFile(fileBase); CloseHandle(fileMapping); CloseHandle(file); return isDll;}int main() { const char* filePath = "test.dll"; // 使用窄字符串 try { if (isDllFile(filePath)) { std::cout << "该文件是DLL文件。" << std::endl; } else { std::cout << "该文件不是DLL文件。" << std::endl; } } catch (const std::runtime_error& e) { std::cerr << "错误:" << e.what() << std::endl; } return 0;} |