labgeek/HashHarvest
GitHub: labgeek/HashHarvest
一款基于 PyQt5 的桌面应用,能从多种文档格式中递归提取加密哈希值并导出为 CSV 或 JSON,同时提供 SQLite 扫描历史管理。
Stars: 2 | Forks: 0
# HashHarvest
作者:labgeek@gmail.com
`HashHarvest` 是一个 PyQt5 桌面应用程序,用于从文件文件夹中提取加密哈希值。它会递归扫描 PDF、文本、日志、CSV、JSON、XML、Markdown 和 Microsoft Office 文件(Word `.docx`、Excel `.xlsx`、PowerPoint `.pptx`)——使用精确的十六进制长度匹配结合负向零宽断言来检测 MD5、SHA1、SHA256 和 SHA512 值,确保较短的模式永远不会与较长的模式发生冲突。结果会在扫描运行时实时显示,可导出为 CSV 或 JSON,并自动持久化保存到本地 SQLite 数据库中。内置的“扫描历史记录”对话框允许您按日期范围筛选过去的扫描,并将任何以前的结果集重新加载到主 UI 中,以便重新检查或重新导出。
## 支持的文件类型
| 扩展名 | 文本提取方式 |
|-----------|----------------------|
| `.pdf` | 通过 `pypdf` 提取页面文本 |
| `.txt` | 纯文本,UTF-8 编码,回退至 latin-1 |
| `.log` | 纯文本,UTF-8 编码,回退至 latin-1 |
| `.md` | 纯文本,UTF-8 编码,回退至 latin-1 |
| `.csv` | 将所有单元格的值连接为可搜索的文本 |
| `.json` | 递归遍历——所有的键和标量值 |
| `.xml` | 所有元素文本及其尾部文本 |
| `.docx` | Word 文档正文——提取自 `word/document.xml` 的段落文本 |
| `.xlsx` | Excel 单元格文本——包含共享字符串表和内联工作表字符串 |
| `.pptx` | PowerPoint 幻灯片文本——提取自每个 `ppt/slides/slideN.xml` 的段落运行内容 |
扩展名匹配不区分大小写。具有其他扩展名的文件将被忽略。
Office 格式(`.docx`、`.xlsx`、`.pptx`)使用 Python 标准库直接从其底层的 OpenXML zip 包中读取——在运行时不需要第三方 Office 库。在段落或单元格中跨越多个运行拆分的文本会被重新组合,因此被创作工具拆分成多段的哈希值仍然能够被匹配到。范围说明:`.docx` 读取主文档正文(不包括页眉、页脚或脚注);`.xlsx` 读取所有工作表中的字符串单元格(数字单元格永远不会包含哈希字符串);`.pptx` 读取幻灯片正文(不包括演讲者备注或母版)。
## 支持的哈希类型
| 算法 | 十六进制长度 |
|-----------|-----------|
| MD5 | 32 |
| SHA1 | 40 |
| SHA256 | 64 |
| SHA512 | 128 |
## 功能特性
- 在一次扫描中检测所有支持文件类型中的 MD5、SHA1、SHA256 和 SHA512 哈希值。
- 递归目录搜索——包含所选文件夹下的所有受支持文件。
- 多线程提取确保 GUI 在长时间扫描期间保持响应。
- 结果表包含四列:**源文件**、**文件类型**、**哈希类型** 和 **哈希值**。
- 隔行变色,并且每一列都可以通过拖动其标题边框由用户调整大小。
- 较长的源路径会在中间显示省略号(保持驱动器号和文件名可见),并且在悬停时会以工具提示的形式显示完整路径。
- 进度条会根据处理的文件进行更新。
- 扫描摘要面板显示已扫描的文件、找到的哈希数以及跳过的文件。
- **导出 CSV** 按钮可在扫描完成后将结果保存到您选择的 CSV 文件中。
- **导出 JSON** 按钮可在扫描完成后将结果保存到您选择的 JSON 文件中。
- 在扫描成功完成之前,导出按钮处于禁用状态;加载历史扫描记录后会重新启用它们。
- **扫描历史记录** 按钮可打开存储在本地数据库中的、可筛选的过去扫描列表。
- 每次完成的扫描都会自动持久化到本地 SQLite 数据库(`hashharvest.db`)中。
- 历史扫描结果可以重新加载到主 UI 中,并像最新扫描一样进行导出。
- 清除表单按钮可重置所有输入、结果、进度、摘要字段和导出按钮。
- 同一文件内的重复哈希值只会写入一次。
- 被跳过的文件(不可读或格式错误)会被计数,但不会中断扫描。
## 要求
- Python 3
- `pypdf`
- `PyQt5`
Office(`.docx`/`.xlsx`/`.pptx`)解析仅使用 Python 标准库,因此不会增加任何运行时依赖。
从项目根目录安装依赖项:
```
python -m pip install -r requirements.txt
```
## 运行应用程序
```
cd C:\path\to\HashHarvest
python -m hashharvest.main
```
### GUI 控件
| 控件 | 描述 |
|---------|-------------|
| **输入目录** 字段 | 输入或浏览至包含要扫描文件的文件夹。 |
| **选择输入文件夹** | 为输入目录打开一个文件夹选择器。 |
| **哈希类型** 复选框 | 选择要扫描的算法(MD5、SHA1、SHA256、SHA512)。默认全部勾选。 |
| **开始扫描** | 验证输入目录并开始多线程扫描。 |
| **清除表单** | 重置所有字段、结果表、进度条和摘要计数。 |
| **扫描历史记录** | 打开“扫描历史记录”对话框以浏览和重新加载过去的扫描记录。 |
| **导出 CSV** | 打开保存对话框并将当前结果写入 CSV 文件。在扫描成功或加载历史记录后启用。 |
| **导出 JSON** | 打开保存对话框并将当前结果写入 JSON 文件。在扫描成功或加载历史记录后启用。 |
您可以直接在输入目录字段中输入路径,而无需使用文件夹选择器。
## 扫描历史记录
每次扫描完成后,结果都会自动保存到 `hashharvest.db`(位于可执行文件或脚本旁写入的 SQLite 文件)中。点击 **扫描历史记录** 以打开历史记录对话框。
### 历史记录对话框
| 列名 | 描述 |
|--------|-------------|
| 日期 / 时间 | 运行扫描时的时间戳(截断到分钟)。 |
| 目录 | 被扫描的输入目录。 |
| 文件 | 已处理的文件数量。 |
| 找到的哈希数 | 提取的哈希总数。 |
使用 **显示** 下拉菜单按时间范围筛选:
| 选项 | 显示的扫描记录范围 |
|--------|-----------------|
| 今天 | 当天午夜起 |
| 过去 7 天 | 滚动的 7 天时间窗口 |
| 过去 30 天 | 滚动的 30 天时间窗口(默认) |
| 过去 90 天 | 滚动的 90 天时间窗口 |
| 所有时间 | 整个数据库 |
选择一行并点击 **加载选中项** 即可将这些结果恢复到主窗口中。结果表、摘要计数和导出按钮将像实时扫描后一样被完全填充。
## 导出结果
不会自动写入任何文件。在扫描完成后(或加载历史扫描记录后),使用导出按钮以您首选的格式保存结果。
### CSV
列名:`Absolute_Path`、`Hash_Type`、`Hash_Value`。
```
Absolute_Path,Hash_Type,Hash_Value
C:\path\to\report.pdf,MD5,44d88612fea8a8f36de82e1278abb02f
C:\path\to\alerts.log,SHA1,da39a3ee5e6b4b0d3255bfef95601890afd80709
C:\path\to\iocs.json,SHA256,e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
```
### JSON
一个扁平的对象数组,每找到一个哈希值对应一条记录。
```
[
{
"absolute_path": "C:\\path\\to\\report.pdf",
"hash_type": "MD5",
"hash_value": "44d88612fea8a8f36de82e1278abb02f"
},
{
"absolute_path": "C:\\path\\to\\alerts.log",
"hash_type": "SHA1",
"hash_value": "da39a3ee5e6b4b0d3255bfef95601890afd80709"
}
]
```
| 字段 | 描述 |
|-------|-------------|
| `absolute_path` / `Absolute_Path` | 包含哈希值的源文件在文件系统中的完整路径。 |
| `hash_type` / `Hash_Type` | 匹配的算法:`MD5`、`SHA1`、`SHA256` 或 `SHA512`。 |
| `hash_value` / `Hash_Value` | 小写十六进制哈希字符串。 |
## 实现说明
[extractor.py](hashharvest/extractor.py) 中的 `HashHarvest` 没有 GUI 依赖,可以独立使用。
```
from hashharvest.extractor import HashHarvest
extractor = HashHarvest(directory="/path/to/files")
results = extractor.extract()
# 结果: {file_path: (hash_type, hash_value) tuples 的集合}
extractor.export_csv("/path/to/output.csv")
extractor.export_json("/path/to/output.json")
```
文件读取由 [readers.py](hashharvest/readers.py) 处理,该模块也可以直接使用:
```
from hashharvest.readers import read_file, SUPPORTED_EXTENSIONS
text = read_file("/path/to/report.json") # returns extracted text as a string
print(SUPPORTED_EXTENSIONS) # {'.pdf', '.txt', '.log', '.md', '.csv', '.json', '.xml', '.docx', '.xlsx', '.pptx'}
```
数据库持久化由 [persistence/db.py](hashharvest/persistence/db.py) 处理:
```
from hashharvest.persistence.db import HashDatabase
db = HashDatabase("hashharvest.db")
# 检索过去 30 天内的所有扫描
scans = db.get_scans(since="2026-05-01T00:00:00")
# 检索给定 scan id 的每个文件的 hash 行
rows = db.get_results(scan_id=1)
```
### 核心方法 —— HashHarvest
| 方法 | 描述 |
|--------|-------------|
| `dir_exists()` | 如果配置的输入目录存在,则返回 `True`。 |
| `read_dir()` | 递归查找输入目录下所有受支持的文件,并已排序。 |
| `extract(...)` | 运行完整扫描,触发可选的回调,并返回结果。 |
| `export_csv(path)` | 将当前结果写入指定路径的 CSV 文件中。 |
| `export_json(path)` | 将当前结果写入指定路径的 JSON 文件中。 |
`extract()` 接受三个可选的回调:
| 回调 | 签名 | 触发时机 |
|----------|-----------|------------|
| `progress_callback` | `(int)` | 在每个文件处理完毕后触发(0–100)。 |
| `status_callback` | `(str)` | 当文件由于错误被跳过时触发。 |
| `result_callback` | `(file_path, file_type, hash_type, hash_value)` | 每次找到哈希值时触发。 |
### 核心方法 —— HashDatabase
| 方法 | 描述 |
|--------|-------------|
| `save_scan(...)` | 将扫描元数据和所有按文件划分的哈希结果持久化到数据库中。 |
| `get_scans(since=None)` | 返回扫描记录列表,可选择按 ISO 格式的时间戳进行筛选。 |
| `get_results(scan_id)` | 返回给定扫描 ID 对应的按文件划分的所有哈希行。 |
[main.py](hashharvest/main.py) 中的 GUI 将这些回调与运行在 `QThread` 中的 `ScanWorker` 发出的 PyQt5 信号连接在一起。
## 构建独立可执行文件
```
python -m PyInstaller --clean --onefile --windowed --name HashHarvest --hidden-import PyQt5.sip hashharvest/main.py
```
数据库文件(`hashharvest.db`)会在运行时写入到编译后的可执行文件旁边。
## 支持的文件类型
| 扩展名 | 文本提取方式 |
|-----------|----------------------|
| `.pdf` | 通过 `pypdf` 提取页面文本 |
| `.txt` | 纯文本,UTF-8 编码,回退至 latin-1 |
| `.log` | 纯文本,UTF-8 编码,回退至 latin-1 |
| `.md` | 纯文本,UTF-8 编码,回退至 latin-1 |
| `.csv` | 将所有单元格的值连接为可搜索的文本 |
| `.json` | 递归遍历——所有的键和标量值 |
| `.xml` | 所有元素文本及其尾部文本 |
| `.docx` | Word 文档正文——提取自 `word/document.xml` 的段落文本 |
| `.xlsx` | Excel 单元格文本——包含共享字符串表和内联工作表字符串 |
| `.pptx` | PowerPoint 幻灯片文本——提取自每个 `ppt/slides/slideN.xml` 的段落运行内容 |
扩展名匹配不区分大小写。具有其他扩展名的文件将被忽略。
Office 格式(`.docx`、`.xlsx`、`.pptx`)使用 Python 标准库直接从其底层的 OpenXML zip 包中读取——在运行时不需要第三方 Office 库。在段落或单元格中跨越多个运行拆分的文本会被重新组合,因此被创作工具拆分成多段的哈希值仍然能够被匹配到。范围说明:`.docx` 读取主文档正文(不包括页眉、页脚或脚注);`.xlsx` 读取所有工作表中的字符串单元格(数字单元格永远不会包含哈希字符串);`.pptx` 读取幻灯片正文(不包括演讲者备注或母版)。
## 支持的哈希类型
| 算法 | 十六进制长度 |
|-----------|-----------|
| MD5 | 32 |
| SHA1 | 40 |
| SHA256 | 64 |
| SHA512 | 128 |
## 功能特性
- 在一次扫描中检测所有支持文件类型中的 MD5、SHA1、SHA256 和 SHA512 哈希值。
- 递归目录搜索——包含所选文件夹下的所有受支持文件。
- 多线程提取确保 GUI 在长时间扫描期间保持响应。
- 结果表包含四列:**源文件**、**文件类型**、**哈希类型** 和 **哈希值**。
- 隔行变色,并且每一列都可以通过拖动其标题边框由用户调整大小。
- 较长的源路径会在中间显示省略号(保持驱动器号和文件名可见),并且在悬停时会以工具提示的形式显示完整路径。
- 进度条会根据处理的文件进行更新。
- 扫描摘要面板显示已扫描的文件、找到的哈希数以及跳过的文件。
- **导出 CSV** 按钮可在扫描完成后将结果保存到您选择的 CSV 文件中。
- **导出 JSON** 按钮可在扫描完成后将结果保存到您选择的 JSON 文件中。
- 在扫描成功完成之前,导出按钮处于禁用状态;加载历史扫描记录后会重新启用它们。
- **扫描历史记录** 按钮可打开存储在本地数据库中的、可筛选的过去扫描列表。
- 每次完成的扫描都会自动持久化到本地 SQLite 数据库(`hashharvest.db`)中。
- 历史扫描结果可以重新加载到主 UI 中,并像最新扫描一样进行导出。
- 清除表单按钮可重置所有输入、结果、进度、摘要字段和导出按钮。
- 同一文件内的重复哈希值只会写入一次。
- 被跳过的文件(不可读或格式错误)会被计数,但不会中断扫描。
## 要求
- Python 3
- `pypdf`
- `PyQt5`
Office(`.docx`/`.xlsx`/`.pptx`)解析仅使用 Python 标准库,因此不会增加任何运行时依赖。
从项目根目录安装依赖项:
```
python -m pip install -r requirements.txt
```
## 运行应用程序
```
cd C:\path\to\HashHarvest
python -m hashharvest.main
```
### GUI 控件
| 控件 | 描述 |
|---------|-------------|
| **输入目录** 字段 | 输入或浏览至包含要扫描文件的文件夹。 |
| **选择输入文件夹** | 为输入目录打开一个文件夹选择器。 |
| **哈希类型** 复选框 | 选择要扫描的算法(MD5、SHA1、SHA256、SHA512)。默认全部勾选。 |
| **开始扫描** | 验证输入目录并开始多线程扫描。 |
| **清除表单** | 重置所有字段、结果表、进度条和摘要计数。 |
| **扫描历史记录** | 打开“扫描历史记录”对话框以浏览和重新加载过去的扫描记录。 |
| **导出 CSV** | 打开保存对话框并将当前结果写入 CSV 文件。在扫描成功或加载历史记录后启用。 |
| **导出 JSON** | 打开保存对话框并将当前结果写入 JSON 文件。在扫描成功或加载历史记录后启用。 |
您可以直接在输入目录字段中输入路径,而无需使用文件夹选择器。
## 扫描历史记录
每次扫描完成后,结果都会自动保存到 `hashharvest.db`(位于可执行文件或脚本旁写入的 SQLite 文件)中。点击 **扫描历史记录** 以打开历史记录对话框。
### 历史记录对话框
| 列名 | 描述 |
|--------|-------------|
| 日期 / 时间 | 运行扫描时的时间戳(截断到分钟)。 |
| 目录 | 被扫描的输入目录。 |
| 文件 | 已处理的文件数量。 |
| 找到的哈希数 | 提取的哈希总数。 |
使用 **显示** 下拉菜单按时间范围筛选:
| 选项 | 显示的扫描记录范围 |
|--------|-----------------|
| 今天 | 当天午夜起 |
| 过去 7 天 | 滚动的 7 天时间窗口 |
| 过去 30 天 | 滚动的 30 天时间窗口(默认) |
| 过去 90 天 | 滚动的 90 天时间窗口 |
| 所有时间 | 整个数据库 |
选择一行并点击 **加载选中项** 即可将这些结果恢复到主窗口中。结果表、摘要计数和导出按钮将像实时扫描后一样被完全填充。
## 导出结果
不会自动写入任何文件。在扫描完成后(或加载历史扫描记录后),使用导出按钮以您首选的格式保存结果。
### CSV
列名:`Absolute_Path`、`Hash_Type`、`Hash_Value`。
```
Absolute_Path,Hash_Type,Hash_Value
C:\path\to\report.pdf,MD5,44d88612fea8a8f36de82e1278abb02f
C:\path\to\alerts.log,SHA1,da39a3ee5e6b4b0d3255bfef95601890afd80709
C:\path\to\iocs.json,SHA256,e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
```
### JSON
一个扁平的对象数组,每找到一个哈希值对应一条记录。
```
[
{
"absolute_path": "C:\\path\\to\\report.pdf",
"hash_type": "MD5",
"hash_value": "44d88612fea8a8f36de82e1278abb02f"
},
{
"absolute_path": "C:\\path\\to\\alerts.log",
"hash_type": "SHA1",
"hash_value": "da39a3ee5e6b4b0d3255bfef95601890afd80709"
}
]
```
| 字段 | 描述 |
|-------|-------------|
| `absolute_path` / `Absolute_Path` | 包含哈希值的源文件在文件系统中的完整路径。 |
| `hash_type` / `Hash_Type` | 匹配的算法:`MD5`、`SHA1`、`SHA256` 或 `SHA512`。 |
| `hash_value` / `Hash_Value` | 小写十六进制哈希字符串。 |
## 实现说明
[extractor.py](hashharvest/extractor.py) 中的 `HashHarvest` 没有 GUI 依赖,可以独立使用。
```
from hashharvest.extractor import HashHarvest
extractor = HashHarvest(directory="/path/to/files")
results = extractor.extract()
# 结果: {file_path: (hash_type, hash_value) tuples 的集合}
extractor.export_csv("/path/to/output.csv")
extractor.export_json("/path/to/output.json")
```
文件读取由 [readers.py](hashharvest/readers.py) 处理,该模块也可以直接使用:
```
from hashharvest.readers import read_file, SUPPORTED_EXTENSIONS
text = read_file("/path/to/report.json") # returns extracted text as a string
print(SUPPORTED_EXTENSIONS) # {'.pdf', '.txt', '.log', '.md', '.csv', '.json', '.xml', '.docx', '.xlsx', '.pptx'}
```
数据库持久化由 [persistence/db.py](hashharvest/persistence/db.py) 处理:
```
from hashharvest.persistence.db import HashDatabase
db = HashDatabase("hashharvest.db")
# 检索过去 30 天内的所有扫描
scans = db.get_scans(since="2026-05-01T00:00:00")
# 检索给定 scan id 的每个文件的 hash 行
rows = db.get_results(scan_id=1)
```
### 核心方法 —— HashHarvest
| 方法 | 描述 |
|--------|-------------|
| `dir_exists()` | 如果配置的输入目录存在,则返回 `True`。 |
| `read_dir()` | 递归查找输入目录下所有受支持的文件,并已排序。 |
| `extract(...)` | 运行完整扫描,触发可选的回调,并返回结果。 |
| `export_csv(path)` | 将当前结果写入指定路径的 CSV 文件中。 |
| `export_json(path)` | 将当前结果写入指定路径的 JSON 文件中。 |
`extract()` 接受三个可选的回调:
| 回调 | 签名 | 触发时机 |
|----------|-----------|------------|
| `progress_callback` | `(int)` | 在每个文件处理完毕后触发(0–100)。 |
| `status_callback` | `(str)` | 当文件由于错误被跳过时触发。 |
| `result_callback` | `(file_path, file_type, hash_type, hash_value)` | 每次找到哈希值时触发。 |
### 核心方法 —— HashDatabase
| 方法 | 描述 |
|--------|-------------|
| `save_scan(...)` | 将扫描元数据和所有按文件划分的哈希结果持久化到数据库中。 |
| `get_scans(since=None)` | 返回扫描记录列表,可选择按 ISO 格式的时间戳进行筛选。 |
| `get_results(scan_id)` | 返回给定扫描 ID 对应的按文件划分的所有哈希行。 |
[main.py](hashharvest/main.py) 中的 GUI 将这些回调与运行在 `QThread` 中的 `ScanWorker` 发出的 PyQt5 信号连接在一起。
## 构建独立可执行文件
```
python -m PyInstaller --clean --onefile --windowed --name HashHarvest --hidden-import PyQt5.sip hashharvest/main.py
```
数据库文件(`hashharvest.db`)会在运行时写入到编译后的可执行文件旁边。标签:PyQt5, SQLite, 哈希识别, 恶意活动检测, 数据提取, 文件扫描, 桌面应用, 漏洞挖掘, 逆向工具