dll文件判断方法——UPX加壳器中isdll变量

前文提到UPX通过变量isdll判断是否为dll文件,因此UPX源码中搜索isdll字段,找到isdll的来源并观察UPX判断逻辑。

一、UPX代码分析

src/pefile.cpp中观察到变量isdll的变化,首先默认为为非dll文件(isdll=false)

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

对代码进行分析:

  1. fi->readx(&ih, sizeof(ih));此部分调用fi->readx函数阅读PE文件头部,并将相关信息保存在结构体ih(IMAGE_HEADER)中
  2. if (31 < (unsigned) ih.subsystem) {throwCantPack("bad ih.subsystem 0x%x", (unsigned) ih.subsystem);}此部分代码检查子系统(ih.subsystem)是否合法(>31)。子系统用于描述程序的运行环境,比如是否图形界面程序、控制台程序,或者是EFI相关的程序;Windows平台的PE文件子系统类型,基本都定义在0到31之间。因此此语句能够有效识别不支持或异常的子系统类型。
  3. 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应用程序,值为10
    IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER:EFI引导服务驱动,值为11
    IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER:EFI运行时驱动,值为12
    IMAGE_SUBSYSTEM_EFI_ROM:EFI ROM(只读存储器,固件类型),值为13
  4. isdll = !isefi && (ih.flags & IMAGE_FILE_DLL) != 0;此部分代码判断是否为DLL文件。如果不是EFI文件(!isefi),并且PE头的标志位中包含IAMGE_FILE_DLL标志,则isdll变量设置为true,表示该文件是一个DLL文件。
  5. use_dep_hack &= !isefi;use_clear_dirty_stack &= !isefi;此两句代码确保如果是EFI类型的子系统,use_dep_hackuse_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;
}

上传的附件 isdll.cpp