$npx -y skills add killvxk/cybersecurity-skills-zh --skill analyzing-prefetch-files-for-execution-history解析 Windows Prefetch 文件,确定程序执行历史,包括运行次数、时间戳和引用文件,用于取证调查。
| 1 | # 分析 Prefetch 文件获取执行历史 |
| 2 | |
| 3 | ## 适用场景 |
| 4 | - 确定 Windows 系统上执行了哪些程序及执行时间 |
| 5 | - 在恶意软件调查中确认可疑二进制文件的执行情况 |
| 6 | - 建立事件期间应用程序使用的时间线 |
| 7 | - 将程序执行与其他取证制品相关联 |
| 8 | - 识别曾运行过的反取证工具或未经授权的软件 |
| 9 | |
| 10 | ## 前置条件 |
| 11 | - 从取证镜像访问 Windows Prefetch 目录(C:\Windows\Prefetch\) |
| 12 | - PECmd(Eric Zimmerman)、WinPrefetchView 或 python-prefetch 解析器 |
| 13 | - 了解 Prefetch 文件格式(版本 17、23、26、30) |
| 14 | - Windows 系统且已启用 Prefetch(客户端操作系统默认启用,服务器默认禁用) |
| 15 | - 熟悉 Prefetch 命名规范(APPNAME-HASH.pf) |
| 16 | |
| 17 | ## 工作流程 |
| 18 | |
| 19 | ### 步骤 1:从取证镜像提取 Prefetch 文件 |
| 20 | |
| 21 | ```bash |
| 22 | # 挂载取证镜像 |
| 23 | mount -o ro,loop,offset=$((2048*512)) /cases/case-2024-001/images/evidence.dd /mnt/evidence |
| 24 | |
| 25 | # 复制所有 Prefetch 文件 |
| 26 | mkdir -p /cases/case-2024-001/prefetch/ |
| 27 | cp /mnt/evidence/Windows/Prefetch/*.pf /cases/case-2024-001/prefetch/ |
| 28 | |
| 29 | # 统计并列出 Prefetch 文件 |
| 30 | ls -la /cases/case-2024-001/prefetch/ | wc -l |
| 31 | ls -la /cases/case-2024-001/prefetch/ | head -30 |
| 32 | |
| 33 | # 对所有 Prefetch 文件进行哈希以验证完整性 |
| 34 | sha256sum /cases/case-2024-001/prefetch/*.pf > /cases/case-2024-001/prefetch/pf_hashes.txt |
| 35 | |
| 36 | # 注意:Prefetch 文件名格式为 EXECUTABLE_NAME-XXXXXXXX.pf |
| 37 | # 哈希值(XXXXXXXX)基于可执行文件路径 |
| 38 | # 来自不同路径的相同可执行文件会创建不同的 Prefetch 文件 |
| 39 | ``` |
| 40 | |
| 41 | ### 步骤 2:使用 PECmd 解析 Prefetch 文件 |
| 42 | |
| 43 | ```bash |
| 44 | # 使用 Eric Zimmerman 的 PECmd(Windows 或通过 Linux 上的 Mono/Wine) |
| 45 | # 从 https://ericzimmerman.github.io/ 下载 |
| 46 | |
| 47 | # 解析单个 Prefetch 文件 |
| 48 | PECmd.exe -f "C:\cases\prefetch\POWERSHELL.EXE-A]B2C3D4.pf" |
| 49 | |
| 50 | # 解析所有 Prefetch 文件并输出到 CSV |
| 51 | PECmd.exe -d "C:\cases\prefetch\" --csv "C:\cases\analysis\" --csvf prefetch_results.csv |
| 52 | |
| 53 | # 以 JSON 格式输出 |
| 54 | PECmd.exe -d "C:\cases\prefetch\" --json "C:\cases\analysis\" --jsonf prefetch_results.json |
| 55 | |
| 56 | # 每个文件的输出包含: |
| 57 | # - 可执行文件名和路径 |
| 58 | # - 运行次数 |
| 59 | # - 最后运行时间(Windows 10 中最多 8 个时间戳) |
| 60 | # - 执行期间引用的文件和目录 |
| 61 | # - 卷信息(序列号、创建日期) |
| 62 | # - Prefetch 文件创建时间 |
| 63 | ``` |
| 64 | |
| 65 | ### 步骤 3:使用 Python 进行基于 Linux 的分析 |
| 66 | |
| 67 | ```bash |
| 68 | pip install prefetch |
| 69 | |
| 70 | python3 << 'PYEOF' |
| 71 | import os |
| 72 | import json |
| 73 | from datetime import datetime |
| 74 | |
| 75 | # 使用 python 解析 Prefetch 文件 |
| 76 | import struct |
| 77 | |
| 78 | def parse_prefetch(filepath): |
| 79 | """解析 Windows Prefetch 文件。""" |
| 80 | with open(filepath, 'rb') as f: |
| 81 | data = f.read() |
| 82 | |
| 83 | # 检查 MAM 压缩格式(Windows 10) |
| 84 | if data[:4] == b'MAM\x04': |
| 85 | import lznt1 # 或使用 DecompressBuffer |
| 86 | # Windows 10 Prefetch 文件已压缩 |
| 87 | print(f" [压缩的 Win10 格式 - 使用 PECmd 进行完整解析]") |
| 88 | return None |
| 89 | |
| 90 | # 版本 17(XP)、23(Vista/7)、26(8.1)、30(10) |
| 91 | version = struct.unpack('<I', data[0:4])[0] |
| 92 | signature = data[4:8] |
| 93 | |
| 94 | if signature != b'SCCA': |
| 95 | print(f" 无效的 Prefetch 签名") |
| 96 | return None |
| 97 | |
| 98 | file_size = struct.unpack('<I', data[8:12])[0] |
| 99 | exec_name = data[16:76].decode('utf-16-le').strip('\x00') |
| 100 | run_count = struct.unpack('<I', data[208:212])[0] if version >= 23 else struct.unpack('<I', data[144:148])[0] |
| 101 | |
| 102 | result = { |
| 103 | 'version': version, |
| 104 | 'executable': exec_name, |
| 105 | 'file_size': file_size, |
| 106 | 'run_count': run_count, |
| 107 | } |
| 108 | |
| 109 | # 提取最后执行时间戳 |
| 110 | if version == 23: # Vista/7 - 1 个时间戳 |
| 111 | ts = struct.unpack('<Q', data[128:136])[0] |
| 112 | result['last_run'] = filetime_to_datetime(ts) |
| 113 | elif version >= 26: # Win8+ - 最多 8 个时间戳 |
| 114 | timestamps = [] |
| 115 | for i in range(8): |
| 116 | ts = struct.unpack('<Q', data[128+i*8:136+i*8])[0] |
| 117 | if ts > 0: |
| 118 | timestamps.append(filetime_to_datetime(ts)) |
| 119 | result['last_run_times'] = timestamps |
| 120 | |
| 121 | return result |
| 122 | |
| 123 | def filetime_to_datetime(ft): |
| 124 | """将 Windows FILETIME 转换为 datetime 字符串。""" |
| 125 | if ft == 0: |
| 126 | return None |
| 127 | timestamp = (ft - 116444736000000000) / 10000000 |
| 128 | try: |
| 129 | return datetime.utcfromtimestamp(timestamp).strftime('%Y-%m-%d %H:%M:%S UTC') |
| 130 | except (OSError, ValueError): |
| 131 | return None |
| 132 | |
| 133 | # 处理所有 Prefetch 文件 |
| 134 | prefetch_dir = '/cases/case-2024-001/prefetch/' |
| 135 | results = [] |
| 136 | |
| 137 | for filename in sorted(os.listdir(prefetch_dir)): |
| 138 | if filename.lower().endswith('.pf'): |
| 139 | filepath = os.path.join(prefetch_dir, filename) |
| 140 | print(f"\n=== {filename} ===") |
| 141 | result = parse_prefetch(filepath) |
| 142 | if result: |
| 143 | print(f" 可执行文件: {result['executable']}") |
| 144 | print(f" 运行次数: {result['run_count']}") |
| 145 | if 'last_run' in result: |
| 146 | print(f" 最后运行: {result['last_run']}") |
| 147 | elif 'last_run_times' in result: |
| 148 | for i, ts in enumerate(result['last_run_times']): |
| 149 | print(f" 运行时间 {i+1}: {ts}") |
| 150 | results.append(result) |
| 151 | |
| 152 | # 保存结果 |
| 153 | with open('/cases/case-2024-001/analysis/prefetch_analysis.json', 'w') as f: |
| 154 | json.dump(results, f, indent=2) |
| 155 | PYEOF |
| 156 | ``` |
| 157 | |
| 158 | ### 步骤 4:识别可疑执行证据 |
| 159 | |
| 160 | ```bash |
| 161 | # 在 Prefetch 中搜索已知恶意工具名称 |
| 162 | ls /cases/case-2024-001/prefetch/ | grep -iE \ |
| 163 | '(MIMIKATZ|PSEXEC|WMIC|COBALT|BEACON|PWDUMP|PROCDUMP|LAZAGNE|RUBEUS|BLOODHOUND|SHARPHOUND|CERTUTIL|BITSADMIN)' |
| 164 | |
| 165 | # 搜索脚本解释器(潜在的恶意执行) |
| 166 | ls /cases/case-2024-001/prefetch/ | grep -iE \ |
| 167 | '(POWERSHELL|CMD\.EXE|WSCRIPT|CSCRIPT|MSHT |