l0lsec/fixed-format-combos
GitHub: l0lsec/fixed-format-combos
基于固定格式掩码的多线程字母数字候选值生成器,专为授权密码恢复和安全测试提供高效的大规模候选列表生成与管道输入能力。
Stars: 0 | Forks: 0
# fixed-format-combos
生成匹配固定**格式掩码**的每一个字母数字字符串 —— 例如所有格式为 `ioB35CZc`(小写、小写、大写、数字、数字、大写、大写、小写)的字符串。
对于该掩码(`lludduul`),其 keyspace 为:
```
26^6 × 10^2 = 30,891,577,600 strings ≈ 278 GB on disk (9 bytes each)
```
## 为什么没有 GPU 版本
这项工作是**受限于磁盘而非受限于计算**的。单个 CPU 核心*生成*字符串的速度可达约 10^9 个/秒;整个 keyspace 仅需约 35 秒的纯计算时间。
真正的开销在于向 SSD 写入约 278 GB 的数据。GPU(在 Apple Silicon 上指的是 Metal/MPS —— 没有 CUDA)生成字符串的速度会比 CPU 更快,但每个字节仍然必须传回 CPU 并输出到驱动器,因此 GPU 只会在等待 I/O 时空闲。正确的优化方案是使用**能将 SSD 带宽跑满的多线程写入器**,这正是本工具的实现:keyspace 被划分为连续的区间,每个线程分配一个,并且每个线程在其固定的字节偏移量处执行 `pwrite()` 写入对应的切片(因为记录长度固定,所以 `offset = index × record_size`)。无需加锁,生成单个有序的输出文件,实现完全并行。
## 构建
可移植的 C99 代码,仅使用 POSIX 线程以及 `pwrite`/`ftruncate` —— 可以在 **macOS 和 Linux** 上构建。在 macOS 上,你需要 Xcode 命令行工具(`xcode-select --install`)来获取 `cc`/`clang`;在 Linux 上,任何版本的 `gcc`/`clang` 都可以工作。
```
./build.sh # compiles ./gen (just: cc -O3 -pthread -o gen gen.c)
```
## 生成列表
最简单的方式 —— 助手脚本会先检查剩余磁盘空间,并在写入前询问:
```
./run.sh # mask=lludduul, out=combos.txt, all cores
./run.sh lludduul out.txt 8 # explicit mask / file / thread count
```
或者直接调用二进制程序:
```
./gen lludduul combos.txt # all cores (default)
./gen lludduul combos.txt 8 # 8 threads
```
它会预分配文件并打印实时进度(百分比、字节数、百万字符串/秒)。
## 写入外部存储并拆分为四等份
`gen` 可以写入到任何路径,因此将其指向已挂载的外部驱动器是可行的 —— 但在外部驱动器上,请添加 `--seq` 参数(请参阅下方的[写入策略](#write-strategy-internal-ssd-vs-external-drive)):
```
./gen lludduul /Volumes/MyDrive/combos.txt --seq # one 278 GB file on the drive
```
但是将 278 GB 放在一个文件中很难处理。`split.sh` 会将 keyspace 切分为 N 个相等的部分(默认为 **4 = 四等份**,每份约 69.5 GB),并将它们写入到你指定的目标位置(它会为外部文件系统自动选择 `--seq`):
```
./split.sh # 4 quarters -> ./combos.partNof4.txt
./split.sh lludduul /Volumes/MyDrive # 4 quarters onto the external drive
./split.sh lludduul /Volumes/MyDrive 4 8 # explicit: mask, dest, parts, threads
```
它会先检查目标位置的剩余空间,如果驱动器是 FAT32 格式则会发出警告(受限于每个文件 4 GB 的限制 —— 如果切分后的文件这么大,请将其重新格式化为 exFAT),并在写入前进行询问。每个部分都是 keyspace 的一个连续切片,因此这些文件块**可以逐字节地重新拼接回完全相同的原始文件**:
```
cat /Volumes/MyDrive/combos.part1of4.txt ... combos.part4of4.txt > combos.txt
```
每次写入四分之一(例如写入到较小的驱动器上 —— 填满它,将该文件移出,然后再生成下一个四分之一):
```
PART=1 ./split.sh lludduul /Volumes/MyDrive 4 # just quarter 1
PART=2 ./split.sh lludduul /Volumes/MyDrive 4 # just quarter 2
```
在底层,它使用了 `gen` 的 `--range` 标志,该标志仅将索引空间的一个切片写入文件,而该文件的第一个字节对应的正是该切片的起始索引:
```
./gen lludduul q1.txt --range 0 7722894400 # quarter 1 of 4
./gen lludduul q2.txt --range 7722894400 15445788800 # quarter 2 of 4
```
### 写入策略:内部 SSD vs. 外部驱动器
`gen` 有两种写入文件的方式:
- **多线程(默认)** —— 使用 `ftruncate` 预分配文件,然后让每个线程在固定偏移量处对其负责的切片执行 `pwrite()` 写入。这能充分利用高速磁盘的带宽,也是在 **使用 APFS/HFS+ 的内部 SSD** 上的正确选择。
- **顺序写入(`--seq`)** —— 单线程,不进行预分配,记录严格按照顺序写入。理论上速度较慢,但它是 **exFAT / FAT / NTFS 和外部(USB)驱动器** 的正确选择。
为什么这很重要:多线程路径依赖于一个能够处理针对预分配(稀疏)文件进行大量并发分散写入的文件系统。**exFAT 并不具备这种能力** —— 它不支持稀疏文件,且只有单个序列化驱动程序,因此多线程模式可能会导致该卷*卡死*(在 macOS 上,`fskit` 的 exFAT 驱动程序会阻塞所有对该挂载点的访问,包括 `ls`)。使用 `--seq` 可以完全避免这种情况。
`split.sh` 会根据目标文件系统自动选择写入模式,因此你通常不需要考虑这个问题。如果你有需要,可以使用 `SEQ=1`(强制顺序写入)或 `SEQ=0`(强制多线程)进行覆盖:
```
SEQ=1 ./split.sh lludduul /Volumes/MyDrive 4 # force the sequential path
```
## 不想要 278 GB 的大文件?通过流式传输替代(推荐用于破解工具)
如果你是为了将数据提供给另一个工具,你几乎永远不需要将列表存放在磁盘上 —— 直接通过管道将候选值输入进去,完全跳过这 278 GB 的占用:
```
./gen lludduul - | hashcat -m hashes.txt # hashcat stdin mode
./gen lludduul - | john --stdin ... # John the Ripper
./gen lludduul - | your-program
```
对于掩码情况,更好的方法是使用 hashcat/John,它们能原生生成掩码候选值,并且完全不会触碰磁盘:
```
# hashcat mask attack — 相同的 keyspace,完全不使用 wordlist 文件:
hashcat -a 3 -m hashes.txt ?l?l?u?d?d?u?u?l
```
## 实用工具
```
./gen --count lludduul # 30891577600
./gen --at lludduul 10147241964 # -> ioB35CZc (string at an index)
./gen --index lludduul ioB35CZc # -> 10147241964 (index of a string; use to resume)
```
`--at` / `--index` 允许你通过索引范围恢复部分运行,或者将任务拆分到多台机器上执行。
## 掩码语言
| 字符 | 类别 | 数量 |
|------|------------------|------|
| `l` | 小写字母 a–z | 26 |
| `u` | 大写字母 A–Z | 26 |
| `d` | 数字 0–9 | 10 |
因此 `lludduul` 表示 l, l, u, d, d, u, u, l —— 匹配 `ioB35CZc`。
修改掩码字符串即可更改格式;keyspace 和文件大小会自动更新。(如果你实际格式中的某些位置固定为单个值或较小的字符集,其空间会迅速缩小 —— 欢迎提交 issue,掩码语言可以进行扩展以支持该需求。)
## 注意事项
- 对于固定的掩码,输出顺序是确定性的且按字典序排列的,因此多次运行的结果是可重现的,且逐字节完全相同。
- 经过完整性检查:对于较小的掩码,输出恰好包含 N 行,零重复,零间隙,具有正确的首尾条目,并且与排序后的顺序一致。
## 预期用途
这是一个用于**经授权的**密码恢复和安全测试的候选值生成器 —— 例如破解你拥有或签约评估的哈希值,为 `hashcat`/John 提供输入,或者用于 CTF 任务。请仅在获得明确测试许可的系统与数据上使用它。
## 许可证
基于 [MIT License](LICENSE) 发布。
标签:安全测试, 客户端加密, 密码字典生成, 攻击性安全