恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
基于个人信息自动生成密码猜测字典的Python脚本
首页
资讯中心
/
基于个人信息自动生成密码猜测字典的Python脚本
基于个人信息自动生成密码猜测字典的Python脚本
发布时间:2026/9/23 3:50:46
做安全测试的人多多少少都遇到过这种场景手头有一批密文或者哈希常规密码猜测字典跑完一轮命中率惨不忍睹转头想自己做一份专属字典却不知道从哪里下手。网上的通用字典动辄几个GB看着很唬人但真正命中时靠的往往是运气——因为那是“大家的密码”不是“他的密码”。这里说句大实话绝大多数人设置密码时根本不是在想一个随机字符串而是在拼凑自己的生活细节。比如“姓名拼音生日”、“英文名手机号尾号”、“邮箱前缀年份”、“女友名字1314”这些模式在脑子里根深蒂固。所以当你手里已经掌握了目标对象的个人化信息时完全可以通过一套自动化脚本把这些公开信息转换成一批高度贴近对方习惯的密码候选生成一份个人化密码猜测字典。这就是我今天要分享的项目一个基于个人化信息生成密码猜测字典的自动化脚本。它可以接收目标的一张“信息画像”自动完成特征词提取、数字扩展、常见的密码变形组合、去重裁剪最终产出一份可直接用于密码安全审计、弱密码自检、员工安全意识培训的小而精字典。先说明白它的定位它不是用来取代通用字典的而是用来“补刀”的。通用字典覆盖大众化弱密码个人化字典解决的是“这个人会怎么想密码”的问题。适合在授权测试、内部红队项目、CTF、密码强度评估里使用也适合安全培训时给员工做个直观演示。下面我把这个项目的设计思路、核心实现、踩坑记录和建议边界一次性讲清楚。1. 不是先写代码而是先想清楚“密码是怎么被想出来的”很多人拿到这个需求第一反应是写个脚本处理字符串、做排列组合搞个笛卡尔积。但真正做下来你会发现最难的不是代码而是怎么把“人脑里的密码规则”翻译成机器能执行的组合模板。1.1 为什么通用字典解决不了个人化问题通用字典的典型代表是rockyou、probable_w2l等它们本质上是大规模泄露密码的统计结果。这类字典对“password123”、“qwerty”、“iloveyou”这种全球性弱密码确实有效但一旦遇到中文用户习惯的“zhangwei19920815”、“zw7613”这种结构基本就只能靠猜了。我做过一组经验对比不是严谨论文是多次授权测试的参考值同样是100万级别的字典规模通用字典在针对某个具体目标时的实际命中率可能只有5%到15%但如果手里有几条可靠的个人信息用个人化字典规模砍到几万条时命中率反而能到30%甚至更高。道理很简单——密码不是一个均匀分布的东西它高度集中在每个用户自己的生活语境里。所以这个脚本的核心价值不是“量”而是“准”。它把目标在社交平台、注册资料、公开主页、旧密码习惯里暴露的信息转换成一批对方大概率会用的密码候选。1.2 设计目标要人味不要暴力排列我在设计脚本时给自己定了几个硬性标准第一生成的字典必须带“人味”。不能只是把所有词和数字做笛卡尔积那样出来的东西既多又没用。要把密码习惯抽象成模板比如“基础词年份”、“基础词特殊符号手机尾号”、“姓名生日”这种真实存在的思维路径。第二配置文件必须和代码分离。目标信息经常变如果每次都要改Python代码那就失去了“自动化”的意义。我用YAML配置文件描述一个人脚本只负责读配置、算候选、输出字典。第三字典规模必须可控。组合爆炸是这类脚本最容易踩的坑基础词30个、数字表20个、模板20个最后一算几千万条根本没法用。所以脚本必须内置去重、长度过滤、总量上限、优先级分层这些能力。第四输出格式要干净。生成的字典不只是给人看的最终要丢给hashcat或者其他工具去跑所以换行、编码、去重、排序都不能有坑。1.3 技术选型Python加几个标准库就够了整个脚本我用Python 3写成依赖尽量少核心用到的模块是argparse、itertools、yaml、json、pathlib以及可选的颜色输出和进度条。为什么不用现成的crunch或cewlcrunch确实强大但它擅长的是基于字符集和位数的排列组合它不理解“姓名拼音”和“生日”之间的关系也不懂得“中文名转拼音首字母”这种操作。cewl擅长从网站上抓取关键词但抓回来的是一个个零散的词缺少结构化的身份信息维度更没法按“家庭成员名纪念日”这种逻辑组织。自己写的好处是逻辑完全可控。我需要的是一个能理解“这个人”的工具而不是一个泛用的字符生成器。说白了crunch是给“暴力人”用的我这个脚本是给“懂人的人”用的。2. 个人信息画像把目标已知信息整理成结构化配置个人化字典的上限取决于你手里有多少真实信息。信息整理这一步是整个项目的地基做扎实了后面生成器才有效果。2.1 核心信息维度与配置格式我设计了一套YAML配置格式基本覆盖了做个人化字典时需要的高价值信息。不同字段的重要程度不一样建议在采集阶段先抓高权重的几个比如姓名、生日、手机尾号、旧密码习惯。name: chinese: 张伟 pinyin: zhangwei initials: zw alias: [weiwei, zw92] birthday: 1992-08-15 phone_tail: 7613 email_prefix: zhangwei92 company: ABC科技 school: 北京某大学 family: partner: liyan child: zhangyue pet: mimi hobbies: - basketball - guitar memorable_years: - 2010 - 2014 - 2019 lucky_numbers: - 7 - 1314这套配置里name字段是核心。chinese是中文名pinyin是完整拼音initials是首字母缩写alias放各种昵称和旧网名。birthday会同时派生出多种格式完整八位、六位、月日、年份等。phone_tail可能只有后四位但同样有很高的信息量。company、school、hometown这类归到“组织与地理”维度虽然权重不如姓名生日但很多密码习惯会把公司名、学校名放进去特别是那种被强制要求大小写加数字的场景。family和hobbies这两个字段容易被忽略但实际命中率极高。把伴侣名字、孩子名字、宠物名作为基础词放进字典往往能覆盖到那种“看似复杂实则走心”的密码类型。2.2 中文拼音与别名的自动化处理中文用户的密码习惯和英文用户有明显差异脚本里必须内置几层转换逻辑。第一层是中文名转拼音。既然配置里给了完整拼音和首字母脚本就直接使用。如果只有中文名可以再用pypinyin转但为了减少依赖我推荐直接让使用者填好默认的信息入口。第二层是衍生写法。比如“张伟”(Zhang Wei)实际密码里会出现这些变体zhangwei、zhang_wei、zhangwei、zw、zhw、weizhang、wzhang、wei。这些衍生词不需要每个都让用户手填脚本会在构建基础词表时自动扩展。第三层是别名与旧昵称。很多人真正用来做密码的不是本名而是自己长期使用的网名、邮箱前缀、游戏ID。alias字段一定要尽量收集甚至比本名还重要。我见过不少案例密码里出现的完全是某个早期QQ昵称和真名毫无关系。至于大小写和分隔符不要在这一步处理那个留给后面的变形规则否则词表会膨胀得非常快。2.3 公开信息收集的合规边界这个部分我必须说清楚个人化信息收集只应该来自合法获取的已知信息比如对方自己公开的资料、授权测试中客户明确提供的员工名单、你在CTF题目里拿到的线索、或者安全意识培训中同事自愿提供的信息。千万不要为了生成字典去写爬虫抓人隐私也不要去碰那些需要绕过权限才能拿到的数据。脚本本身是无辜的但数据来源不合规整个测试就变味了。我在实际项目中遇到过客户只给了一个姓名加一个手机号的情况也遇到过从公开渠道合法整理出几十条信息的情况。信息越多字典越准但哪怕只有一条可靠的“姓名年份”也值得把这套流程跑一遍。3. 核心生成逻辑特征词、变形与组合模板这一步是整个脚本的心脏。前面整理的信息画像还只是原料生成器要做的是把原料加工成有实际命中概率的密码候选。3.1 特征词、数字与特殊符号的构建先设计三类基础元素基础词表、数字表、特殊字符表。基础词表的构建逻辑是把配置里所有字段转成可用字符串然后做去重和过滤。比如“张伟”会产生zhangwei、zw、weiwei、zw92、liyan、zhangyue、mimi、basketball、guitar、ABC科技、北京某大学这一批词。数字表则会把生日解析成多种常见格式再把手机尾号、纪念年份、幸运数字汇总进去最后追加一些用户密码中高频出现的数字尾巴如520、1314、521、666、888。特殊字符表就是那一小批常见符号。def build_numbers(cfg): nums set() bd cfg.get(birthday, ) if bd: parts bd.split(-) year, month, day parts[0], parts[1], parts[2] nums.add(year) # 1992 nums.add(year[2:]) # 92 nums.add(f{month}{day}) # 0815 nums.add(f{year}{month}{day}) # 19920815 nums.add(day month) # 1508 nums.add(cfg.get(phone_tail, )) nums.update(cfg.get(memorable_years, [])) nums.update(cfg.get(lucky_numbers, [])) nums.update([123, 520, 1314, 521, 666, 888]) nums.discard() return sorted(nums, keylen, reverseTrue)注意一个细节数字表里有些字段可能是纯数字字符串有些可能是带前导零的月份不要统一转int否则“0815”会变成“815”就少了用户当年输入时的手感。3.2 变形规则与组合模板基础词表构建完成后不是直接拿去和数字表做笛卡尔积必须先做一轮“变形扩展”。常见的变形包括首字母大写、全大写、全小写、leet替换a变成e变成3i变成1o变成0s变成$、末尾补1、末尾补0等。leet替换尤其有用因为很多平台强制要求密码里必须有特殊字符用户的惯用做法就是用替代a、用1替代i。我在脚本里维护了一张映射表leet_map { a: , e: 3, i: 1, o: 0, s: $, g: 9, b: 8 } def apply_leet(word): for src, dst in leet_map.items(): word word.replace(src, dst) return word组合模板是整个脚本的灵魂。我根据大量授权测试的样本习惯归纳出几个命中率明显偏高的模板顺序。注意模板的顺序很重要因为后面的总量截断会优先保留靠前的候选所以一定要把“最像人会用的组合”放在前面。templates [ base, # zhangwei baseyear, # zhangwei1992 / 1992zhangwei basespecialyear, # zhangwei1992 basephone, # zhangwei7613 basespecialphone, # zhangwei#7613 basebase, # zhangweiliyan basespecialbase, # zhangweiliyan yearspecialbase, # 1992zhangwei ]每个模板组合不断追加数字、追加特殊字符、追加家庭成员名不会一次性全部拼满否则生成的字典规模会快速失控。正确做法是先用低阶模板拼出一个小而精的字典测试命中情况后再按需打开更高阶的模板。3.3 去重、截断与输出生成过程中会产生大量重复项比如“zhangwei1992”可能同时来自baseyear和basephone两个模板的跨界组合。所以脚本里用一个set去重再按长度过滤默认过滤掉小于6位和大于20位的候选。这一步不能省因为很多在线系统对密码长度有下限要求太短的候选跑也是白跑。去重后还要面对一个现实问题最终字典可能还是太大。我的做法是引入一个总量上限参数--limit默认值可以设成20万条。达到上限后不再继续扩展用通过“模板顺序词表顺序”保留下来的都是优先级最高的候选。实际使用中我通常先跑一个1万条的小字典试水看看命中情况再决定要不要放开限制。输出格式方面用一行一条、LF换行、UTF-8编码。这点我在后面问题排查部分会详细展开因为Windows环境下很容易踩换行符和编码的坑。4. 完整实现与运行演示一套可复用的Python脚本前面把逻辑讲清楚了这节直接看代码。脚本不复杂去掉注释大概两百多行核心逻辑集中在构建词表、生成候选、去重裁剪三个函数里。4.1 命令行入口与主流程命令行参数只保留四个必要的--config指定YAML配置文件--output指定输出字典文件--limit指定生成数量上限--min-len和--max-len控制长度范围。python3 gen_dict.py -c target.yaml -o dict.txt --limit 200000 --min-len 6 --max-len 20主流程分四步执行加载配置、生成基础元素、遍历组合模板、去重裁剪后写入文件。每步之间用日志把词表大小、数字表大小、生成总量及时打印出来方便判断是哪个环节导致字典膨胀。4.2 一个虚构目标的配置与运行结果完整代码放到这里篇幅会太长拆解核心函数更实用。我用一个虚构人物演示整个流程。假设目标信息如下中文名张伟拼音zhangwei首字母zw昵称weiwei生日1992-08-15手机尾号7613邮箱前缀zhangwei92伴侣liyan宠物mimi爱好basketball和guitar纪念年2019。这个配置喂给脚本后基础词表大致会包含zhangwei、weiwei、zw、zw92、liyan、mimi、basketball、guitar等几十个词。数字表包含1992、92、0815、1508、19920815、7613、2019、520、1314等一批数字。经过变形和组合后生成的字典开头大概长这样zhangwei zhangwei1992 1992zhangwei zhangwei1992 zhangwei0815 zw1992 Zw1992 weiwei2019 liyan1314 zhangyue0713注意看这些候选它们不是随机拼出来的都是按照“人脑里真实的密码公式”推出来的。“liyan1314”这种组合在通用字典里出现的概率几乎为零但在个人化字典里命中率非常高——很多人的密码就是伴侣名字加一个表白数字。我建议你运行完之后先不要急着把整个字典丢进工具跑而是自己人眼扫一遍前面几百条看看有没有那种“目标本人看到会愣一下”的候选。如果一条都没有说明配置里的信息还不够全或者模板顺序需要调整。4.3 与常用密码测试工具的衔接生成字典只是第一步最终要交给工具去跑。我自己最常用的场景是hashcat的离线哈希测试。比如把生成的字典保存为dict.txt哈希文件是hash.txt跑字典模式hashcat -a 0 -m 0 hash.txt dict.txt这里多说一句hashcat这种工具本身是安全审计和密码恢复领域的常规工具用在授权测试、自己搭建的靶场、CTF比赛里是完全没有问题的。字典生成器只是负责给它提供更精准的输入而不是制造攻击能力。也有不少人把这套脚本产出的字典和hydra这类在线测试工具做联动但我建议你在正式用之前先确认清楚目标系统是否具备明确授权测试范围是否覆盖这个目标测试时段是否在允许范围内这些合规步骤比字典本身重要一百倍。5. 实战中的常见问题与排查技巧这部分内容是我反复跑这个脚本后总结出来的每条都是实际踩过的坑值得仔细看一遍。5.1 字典数量爆炸的控制策略最常见的问题是配置信息不多但脚本跑完生成了几千万条候选。原因几乎都是组合模板没有控制好尤其是包含多个基础词的组合模板一旦基础词表里有几十个词笛卡尔积的规模立刻指数级上升。我建议用“分层扩展”策略应对。第一层只生成单个基础词的变形和加数字的模板第二层才加入基础词与基础词组合、特殊字符组合。每层生成的候选先做去重和数量统计如果第一层就已经超出预期总量就不要再展开第二层。脚本里可以通过--limit参数配合分层逻辑实际上就是把爆炸点提前挡住。另一个技巧是优先保留短模板产出的候选。一个8到12位的“姓名生日”候选概率远高于一个20位的“姓名姓氏伴侣名特殊字符四位数字”的超长组合。长度本身就是权重信号短而合理优先输出。5.2 命中率低时的调整思路如果生成出来的字典跑了半天没有任何命中先不要急着加更多模板先做一件事回看配置信息是不是太单薄。个人化字典的准确率直接取决于基础词表的质量词表里只有两三个词再强的生成器也变不出花样。其次检查特殊字符表。很多平台强制要求密码包含特殊字符所以用户一定会给某个基础词加上、!、#这类符号。如果目标的使用场景是这类平台而你的模板里又忽略了“基础词特殊字符年份”这个组合命中率自然会低得很。还有一种情况是顺序问题。比如目标习惯“生日开头姓名结尾”你的模板只生成了“姓名生日”就会漏掉真实候选。我的做法是每个核心组合模板都生成正反两个方向也就是“AB”和“BA”都保留。5.3 编码、换行与兼容性这个坑非常隐蔽。Windows环境下用open函数直接写txt文件时默认会用系统区域的编码很可能在中文环境里写出GBK内容而hashcat在Linux环境里读取时会出现乱码。我最后统一用UTF-8编码写入并且在打开文件时显式指定newline\n避免把Windows的\r\n也写进字典导致某些工具判断候选时出错。with open(output_path, w, encodingutf-8, newline\n) as f: f.write(\n.join(candidates))此外终端输出时如果包含中文字符在Windows的cmd里偶尔会报GBK编码错误解决办法是给终端输出包装一个异常处理或者只打印ASCII字段。我在脚本里把所有日志信息都用英文加数据输出彻底避开编码问题。5.4 自动化集成的工程实践这个脚本设计出来就是为了自动化。我在内部项目里经常把它接进一个pipeline由信息收集模块产出YAML配置脚本读取配置后生成字典最后通知下游工具开始跑测试。工程化的几个关键点是要保证配置、代码、输出路径完全分离脚本内部不要出现任何硬编码的目标信息参数全部走命令行或环境变量方便在不同项目间复用每次生成后记录日志和字典行数便于复盘不同信息维度对命中率的实际贡献。如果你要定时重跑比如每周更新一次目标画像并重新生成字典直接把脚本挂进crontab或者CI的定时任务即可。因为整个脚本是无状态的输入一份配置输出一个字典文件不会留下中间状态非常适合批处理。6. 边界与安全意识这些规则的另一面写到这里我必须把最重要的事情说透这样一套生成密码猜测字典的自动化脚本本质上是一把双刃剑。6.1 授权永远在第一位所有密码猜测、密码恢复、弱密码验证相关的工作都必须建立在明确授权和合法用途的前提下。适合用这个脚本的场景包括你自己拥有的账号做密码找回测试、公司在内部授权范围里做员工弱密码审计、你搭的靶机或者CTF题目、以及安全意识培训中的演示环节。没有授权哪怕只是跑字典这个动作都可能触碰法律红线。我见过有些测试人员最喜欢问的一句话是“这个工具能不能打某某系统”每次听到这种问题第一反应都是先确认你有没有书面授权没有授权就是不能碰这不是胆子小是这行的基本职业道德。6.2 这个脚本对普通用户的警示价值从另一个角度看这个脚本对普通用户也很有教育意义。我给不少团队做过安全意识培训现场演示的效果往往比讲PPT好得多。找一位自愿配合的同事收集几条他公开过的信息生成一份个人化字典再让他自己看看里面有没有“感觉眼熟”的密码候选。很多人看完都会愣住因为他们发现自己觉得“很私密、很安全”的密码其实早就写在了自己公开信息的排列组合里。这也是我一直坚持用密码管理器生成随机长密码的原因。我不能保证所有人都记住一长串随机字符但至少可以用工具把“个人化组合”这条路径彻底断掉。密码的安全边界不该建立在“大家都猜不到我的习惯”上而应该建立在“即使知道我的全部习惯也算不出我的密码”上。再说个小技巧如果你拿这个脚本做培训演示一定先设置好总量限制别让字典膨胀到几十万条培训现场几万条足够震撼了。另外要记得提前跑一遍避免现场因为编码或者路径问题翻车那种尴尬经历过一次就不想再经历第二次。这个项目做到最后我的体会是技术难度真的不高真正值钱的是对“密码是人性问题”这个判断的坚持。当你把目标画像、组合模板、去重策略这些环节一步步跑通之后你会发现它本质上是把“了解一个人”这件事翻译成了机器能处理的规则。理解人然后用工具去验证这个理解这大概就是个人化字典这个项目最有意思的地方。