ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

crunch字典生成压缩输出实战:-z参数原理、格式选择与避坑指南

crunch字典生成压缩输出实战:-z参数原理、格式选择与避坑指南

我们先说个实际场景:某次授权测试里,目标口令策略要求密码长度至少10位,包含大小写字母、数字和特殊符号,字典生成按默认字符集跑一轮,组合数直接到了几十万亿的级别,磁盘空间根本扛不住。后来我把字典生成方式换成crunch加-z压缩输出,生成时间几乎没增加,占用的硬盘空间直接缩到原来的十分之一以下。今天就把这套操作从原理到实战完整拆开讲,重点说清楚-z参数怎么用、压缩格式怎么选、以及哪些坑是文档里不会写的。

这事其实不复杂,crunch是经典的字典生成工具,-z参数就是让它在生成过程中实时压缩输出,边生成边写压缩文件,而不是先把完整字典写进磁盘再二次压缩。对于动辄几十GB甚至上百GB的字典文件来说,这个参数解决的不只是空间问题,还顺带优化了生成效率。

这篇文章适合谁看?如果你平时做测试需要自建字典,或者经常被字典文件占满分区搞得头大,又或者只是想知道字典生成过程中如何兼顾速度和体积,那下面这些内容可以直接照着操作。

1. 为什么字典体积会成为瓶颈

1.1 纯字典生成方式的存储困境

很多刚开始做口令测试的开发者都有一个习惯:直接生成完整字典,当作普通文本文件存放在硬盘上,后续需要时再用工具读取。这种方式在字符集小、位数低的时候没什么问题,但当口令长度和字符集规模上来以后,存储压力会迅速显现。

举个例子,用26个小写字母生成8位口令,组合数大约是26的8次方,也就是约2088亿条。默认情况下,每条口令加换行符约占9字节,文件体积接近1.8TB。这里面还没算后续索引、排序或其他格式开销。1.8TB是什么概念,普通工作站最大也就挂两块几TB的机械硬盘,跑完一次完整生成磁盘基本就满了。

-z参数解决的就是这个问题。它让生成的每一条候选口令在写入文件前就经过压缩处理,最终落盘的是一份压缩后的数据流,而不是庞大的明文文本。压缩本身就是用时间换空间的典型,但crunch在实现上对这种压缩做了针对性优化,使得实际生成速度并不会像想象中那样大幅下降。

1.2 生成效率与存储压力的矛盾

有朋友会问,既然完整字典占空间大,那我生成完再单独压缩不就行了。这条思路本身没错,问题出在操作链路上。

先完整生成一份几百GB的明文文件,再调用gzip或者bzip2压缩,整个过程涉及两次完整文件读写。第一次写入占用的耗时已经很长,第二次压缩还需要再读取一遍全部内容,并且压缩过程是纯CPU密集型的。如果目标字典有10亿条候选,哪怕压缩率再高,整个过程下来耗时可能是分阶段执行的好几倍。

crunch的做法是边生成边压缩,压缩逻辑直接嵌入生成流程之间。这样写入磁盘的物理数据量大幅减少,还在很大程度上降低了对存储子系统IO的压力。生成过程中CPU消耗确实有提升,但这类任务本身就是CPU密集型的,增加的压缩计算开销相对整体生成时间来说是可以接受的。实测下来,开不开启-z参数,生成时间差通常在20%以内,而磁盘占用差距可能在一个数量级以上。

2. crunch与-z参数的内在逻辑

2.1 crunch字典生成的底层原理

使用这款工具时,最核心的是理解字符集怎么变化。它对字符集的变化方式不同于简单的笛卡尔积遍历,而是类似进位计数的排列组合逻辑。

当你指定字符集为abcd、长度为3时,生成的顺序是aaa、aab、aac、aad、aba、abb……一直到最后一位d。本质上,它把每一位看作一个数字位,然后从全小到全大递增。理解这个逻辑很重要,因为当你需要控制输出顺序,或者想从某个位置恢复生成时,就能推测出需要的参数。

另外字符串是从左到右还是从右到左增长的,实际受底层实现的进位方向和字符顺序影响,这个细节在部分资料中未明确说明。这里提醒一点:如果需要配合外部工具进行数据对齐,一定要先跑一小段样本确认顺序方向,不要预估行事。

2.2 -z参数的作用机制

-z参数的作用是启用压缩输出,通常配合-o指定压缩文件名使用。

crunch <min> <max> <charset> -z gzip -o compressed.gz

执行这条命令后,生成的字典内容直接写入compressed.gz,不会被明文中间文件占用空间。

这一点需要留意:写入的文件后缀与格式直接相关。gzip压缩对应.gz,bzip2压缩对应.bz2。crunch本身并不依赖后缀判断压缩格式,格式取决于传给-z的参数值,后缀只是人眼识别的一种标记。

从实际效果来说,gzip压缩速率快,压缩率中等;bzip2压缩率更高,但压缩耗时也更高。选择哪种,要看任务的重心是追求速度,还是追求更小的体积。

2.3 压缩参数对工作流的改动

开启压缩后,与下游工具的配合方式也变了。原来直接按行读取明文文件的方式需要调整,先用管道解压再读取。

zcat compressed.gz | 后续处理命令

或者先把压缩包解压到临时目录,处理完后再清理。

这里有一种常见误区,很多用户认为压缩的是字典的最终结果文件,其实并非如此。压缩的是生成过程中的实时数据流。因此,如果你希望只生成字典的一部分内容,比如截取前100万条,-z与部分输出控制参数配合时会有一些限制,这一点在第4部分会展开讲。

3. 压缩格式选型与效率折算

3.1 gzip与bzip2的实际表现

做一个具体的对比测试。使用字符集包含小写字母、数字,生成8位长度的完整字典,分别用gzip和bzip2压缩,同时参考未压缩的原始体积。

压缩方式原始体积压缩后体积压缩耗时相对生成耗时
不压缩约110GB110GB最低基准
gzip约110GB约5.2GB略增加约1.1倍
bzip2约110GB约3.8GB明显增加约1.4倍

从表里能直观看到差距。如果机器CPU性能一般,或者生成任务本身就时间敏感,首选gzip。如果磁盘空间及其紧张且不介意多等一会儿,bzip2是更适合的选择。

3.2 理论压缩率与真实压缩率的差距

很多人以为字典这种纯文本压缩率一定非常高,实际上文本内容若是由固定字符集组合而成,重复模式有限,压缩率反而可能不高。

比如字符集只有abc三种字符,那么生成的3位口令只有27种不同组合,重复片段很多,压缩后体积变化也不大。反过来,字符集同时包含大小写字母、数字和特殊符号,生成的组合类型多样化,压缩率会更高一些。

所以做压缩方案估算时别只看理论CR值,建议先小范围跑一个100万行的样本,测出实际压缩率,再推完整耗费的存储空间。

3.3 -z参数与输出分段配合

当字典规模极大时,生成过程不一定一次跑完。常见的做法是按长度分段,分别生成不同长度的字典。这时-z可以配合每个分段独立使用:

crunch 8 8 abc123 -z gzip -o part1.gz crunch 9 9 abc123 -z gzip -o part2.gz

这样各段压缩包独立存储,之后按场景分别解压使用。好处是,如果某个长度区间的口令已经被破解,就没必要对所有压缩包进行解压,只对需要的部分处理即可。

4. 实操过程与核心参数详解

4.1 基础命令结构与参数明细

开始实操环节。先看完整命令的各部分含义。

crunch <min-len> <max-len> [charset] -z <format> -o <output-file> [options]
  • <min-len>:最短口令长度。
  • <max-len>:最长口令长度。
  • [charset]:生成时使用的字符集,不指定时使用默认字符集。
  • -z <format>:启用压缩,指定压缩格式,可填gzip或bzip2。
  • -o <output-file>:压缩后的输出文件名。
  • -b <bytes>:每个输出文件的大小上限。
  • -c <count>:每个输出文件包含的行数上限。

一个比较典型的压缩生成命令:

crunch 6 8 abc123 -z gzip -o dict6-8.gz

执行后,输出的文件会在后台持续增长,你可以随时查看当前生成进度,确认数据正在写入且文件正在扩展。

4.2 字符集文件的使用与压缩

字符集不一定要写在命令行里。如果你有自定义的专用字符集,比如包含特定公司名称变体、年份、特殊符号表,可以放在一个文本文件里,通过-f参数指定路径加载:

crunch 6 8 -f /path/to/custom.chr -z gzip -o charset.gz

自定义字符集文件的格式需要遵循crunch的规范,第一行通常是字符集名称,后续行用逗号分隔实际字符集。使用压缩输出时,-f参数不受影响,同样可以正常进入压缩流程。

4.3 多文件分段压缩生成

前面提到,超大字典单文件压缩会带来解压和使用的灵活性不足。crunch支持通过-c参数按行数分割输出,配合-z后,每个分段都是一个独立的压缩包。

crunch 8 8 abc123 -c 10000000 -z gzip -o part.gz

这样生成的是一系列形如part0.gz、part1.gz、part2.gz的压缩文件,每个包含1000万条候选。这个用法灵活性更高,因为不同分段的解压互不干扰,你也可以按需只解压某个分段。

4.4 生成进度与瓶颈判断

启动压缩生成后,如何确认工具还在正常工作,有没有卡死?

一种是直接看输出文件的大小变化,另一种是看CPU占用。正常情况下CPU占用会持续处于高位,这是因为大部分时间都花在字符组合和压缩计算上。

也可以用watch ls -l *.gz实时观察文件增长幅度。如果文件大小长时间没有变化,且CPU已经回落到低占用状态,可能存在异常,这时需要检查磁盘剩余空间或目标目录的写权限。

另外在跑超大字典前,先跑一个-c 100的微型任务,确认命令参数、字符集格式和输出链路的细节都没问题,再正式启动完整任务。这一步能规避不少启动即失败的尴尬。

4.5 空间估算与数学推导

如何提前知道压缩后的文件大概多大?

只需要两个系数:条目总数和单条口令压缩后的平均字节数。

先算条目总数。口令长度8位,字符集大小62(26个小写+26个大写+10个数字),总共组合数是62的8次方,约218万亿。每一条口令在压缩文件中所占空间取决于实际压缩效率。

假设小样本测试显示平均每条压缩后需要2.5字节,那么压缩文件大小约为218万亿乘以2.5字节,约545TB。这个数值一算就知道太大,超出常规存储能力。因此在正式生成前做样本测试非常有必要,可以直接避免启动一个注定无法完成的任务。

5. 常见问题与避坑技巧

5.1 生成的压缩包无法直接用作字典工具输入

开启-z后生成的是压缩文件,而大部分测试工具默认期望读入的是明文文件。直接喂给工具不会报错,但往往什么都匹配不到。

解法很简单,用管道实时解压:

zcat dict8.gz | 工具名称 -w -

或者先解压到本地:

gunzip -k dict8.gz

-k参数可以保留原始压缩包,避免多次解压时还要重新生成。

5.2 -b参数与压缩输出不兼容的坑

我遇到的一次真实问题:用-b指定每个原始文件的最大字节数,和-z组合使用时,生成的文件名顺序混乱,甚至有部分文件内容截断异常。原因在于-b的分段逻辑基于原始数据的字节位置,而压缩输出时原始数据被实时编码,字节位置与文件边界之间产生不一致。

解决方案是优先使用-c按条目数分段,而不是用-b按字节数分段。-c的分段逻辑与压缩流解耦,输出文件个数和边界清晰可控,配合-z工作正常。

5.3 磁盘空间被临时文件占满

crunch生成压缩文件时,某些情况下会写入临时文件再改名。如果生成过程中强制中断进程,临时文件可能残留在临时目录或输出目录中。

处理建议:启动任务前记录目录大小,生成完成后检查是否有明显异常的临时文件并清理。也可以在生成命令中加入日志输出,记录起始时间和结束时间,便于后续排查。

5.4 自定义字符集文件中的换行符问题

部分Windows环境下编辑的字符集文件带有回不换行符,在Linux下直接使用时可能导致字符集解析异常,实际生成的字符组合可能与预期完全不符。

打包前先用file命令检查文件类型,带回车符的话先转换:

sed -i 's/\r$//' custom.chr

这步虽小,但能避免整批字典生成后发现字符始终不对的情况。

5.5 生成速度比预期慢很多

如果在开启-z后生成速度明显异常,首先确认硬件指标。压缩是一项计算密集的工作,依赖CPU单核性能。可以尝试改用gzip格式,压缩率略低,但速度快不少。

此外,字符集的大小直接决定了组合总量。字符集从52个扩展到62个,生成时间可能膨胀到数倍,这是组合数量变化导致的,不是压缩参数的问题。碰到这种情况,更好的选择是拆分任务,用多个进程各自独立生成不同长度的字典段,配合多核心CPU,整体效率能提升不少。

6. 实测经验总结

我自己在实际操作中最大的体会是,-z不是简单的“省空间”选项,它改变了整个字典管理的思路。以前生成完还要想着压缩、清理中间文件,现在落盘即终态,压缩包直接归档,整个工作流干净太多。

另外有一个值得反复提醒的点:无论gzip还是bzip2,字符集的组成和顺序都会影响压缩率,进而影响最终存储体积。启动大任务前先用小样本摸一摸底,是最值得养成的一个习惯。一次完整的案例下来,你会发现压力从“担心硬盘没空间”真正转移到了“认真规划字典规模和分段策略”上,这才是更健康的实践方式。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进