欢迎光临
我们一直在努力

基于个人化信息生成密码猜测字典的自动化脚本

一、 程序功能总览

核心功能是根据包含用户个人信息的原始数据文件,为每个用户生成一份高度定制化的密码猜测字典。其设计理念是:人们常常使用与自己相关的信息(如姓名、生日、电话、账号等)来创建密码,因此针对特定目标生成密码时,基于其个人信息的字典往往比通用字典更有效。

该程序的主要工作流程可以概括为以下几个阶段:

  • 环境检查:确保依赖的工具(Pydictor)和输入数据文件存在。

  • 数据读取与解析:逐行读取原始数据文件,并从中提取出结构化的用户信息。

  • 基础字典生成:针对单个用户,从其个人信息中提炼出可能被用作密码基础的“关键词”或“词根”。

  • 密码猜测生成:利用 Pydictor 工具,对基础词根进行多种变形和组合,生成大量可能的密码变体。

  • 结果整合与输出:将每个用户的猜测密码集合写入输出文件,并用分隔符隔开,同时提供处理进度和统计信息。

  • 该程序的价值在于它将散乱的非结构化信息(数据文件中的一行文本)自动化、批量化地转化为有针对性的攻击字典,极大地提高了密码破解或安全测试中“猜测”环节的效率和精准度。

    二、 输入输出与全局配置

    在深入分析内部机制前,我们首先审视其外部接口和配置。

    • 输入数据文件​ (DATA_FILE = "datasets.txt"):

      • 格式:每行包含一个用户的若干信息字段,字段以类似 field:value的格式呈现,字段间可能由制表符或空格分隔。支持的字段包括 email, name, account, phone, birth。

      • 示例:一行数据可能看起来像 email:john.doe@example.com name:John|Doe account:johndoe2024 phone:13800138000 birth:19900101。

      • 预处理:程序会忽略以 #开头的注释行和空行。

    • 输出文件​ (OUTPUT_FILE = "answer.txt"):

      • 格式:为每个用户生成一系列密码猜测,每个用户的猜测列表以 <END>行作为结束标记。文件结构如下:

        guess1_user1
        guess2_user1

        <END>
        guess1_user2
        guess2_user2

        <END>

    • 关键全局参数:

      • PYDICTOR_PATH = "./pydictor.py":所依赖的核心密码生成工具路径。

      • MAX_GUESSES = 10000:为每个用户生成的密码猜测数量的上限,防止字典过大,是控制资源消耗的关键参数。

      • MIN_WORD_LEN = 3/ MAX_WORD_LEN = 16:生成的基础词根和最终密码的长度限制,符合常见密码策略。

    • 降级方案​ (WEAK_PASSWORDS):

      • 当无法从用户信息中有效生成密码(如数据无效或提取失败)时,程序会使用一个预定义的常见弱密码列表作为降级方案,确保输出不为空。

    三、 核心数据结构:用户信息字典

    程序的核心在于处理用户信息。它定义了一个内部数据结构(通过 Python 字典实现)来规范化存储从原始数据行中提取的信息。extract_user_info(line)函数负责这个转换过程。

    生成的用户信息字典结构如下:

    {
    'email': 'john.doe@example.com', # 字符串,邮箱地址
    'name_parts': ['John', 'Doe'], # 列表,从`name`字段解析出的姓名部分
    'account': 'johndoe2024', # 字符串,账号名
    'phone': '13800138000', # 字符串,电话号码
    'birth': '19900101' # 字符串,8位数字表示的生日(YYYYMMDD)
    }

    关于 name_parts的精细处理:

    这是数据结构中的一个亮点。原始 name字段允许使用竖线 |分隔多个部分(如 name:John|Doe|Jr.)。该程序并非简单地将整个字符串作为一体,而是:

  • 拆分:通过 split('|')将字符串拆分成多个部分。

  • 清洗:对每个部分进行去除首尾空格的处理。

  • 过滤:只保留长度大于等于2的有效部分。

    这样做的目的是为了更细致地利用姓名中的每一个独立单元(名、姓、中间名等),因为用户可能会使用其中任意一部分或组合来创建密码。这种设计极大地增强了后续生成密码的多样性和针对性。

  • 四、 算法与流程深度剖析

    1. 主控制流程 (main函数)

    主函数是程序的调度中心,其算法流程清晰,体现了模块化设计思想。

  • 环境检查:调用 check_environment()验证 Pydictor 和数据文件的存在性。

  • 数据读取:读取 datasets.txt,过滤空行和注释行,得到待处理行的列表 lines。

  • 循环处理:对于 lines中的每一行(代表一个用户):

    a. 信息提取:调用 extract_user_info(line),失败则标记为“失败”并写入降级密码。

    b. 基础字典生成:将用户信息字典传递给 generate_base_dictionary(user_info),得到基础词根列表。

    c. 密码猜测生成:将基础词根列表和数量限制传递给 generate_passwords(base_words, MAX_GUESSES),得到该用户的最终密码列表。

    d. 结果写入:将密码列表写入文件,末尾追加 <END>分隔符。

  • 进度反馈:使用 create_progress_bar函数在控制台打印美观的进度条,提升用户体验。

  • 统计报告:处理完成后,打印成功/失败用户数,并计算输出文件的大小和总行数。

  • 2. 基础字典生成算法 (generate_base_dictionary函数)

    此函数是“个性化”的关键,它像一个信息炼金术士,从原始信息中提炼出密码的“胚子”。其算法策略是针对每个字段类型进行特定模式的特征提取。

    • 账号​ (account):

      • 保存完整账号。

      • 提取账号的前4位和前6位(如果账号长度允许)。例如 "johndoe2024"会提取出 "john"和 "johndo"。

    • 姓名部分​ (name_parts):

      • 保存每个完整的姓名部分。

      • 提取每个部分的前4个字符,并生成其小写和大写版本。

      • 如果姓名部分包含点号(如 "John.Doe"),会进一步拆分成 "John"和 "Doe"并分别处理。

    • 邮箱​ (email):

      • 提取 @符号前的本地部分。例如 john.doe@example.com提取出 john.doe。

    • 电话​ (phone):

      • 使用正则表达式 r'\\D'移除非数字字符,得到纯数字串。

      • 保存完整数字串、末4位、末6位,以及前4位(如果长度足够)。

    • 生日​ (birth):

      • 假设生日是8位数字 YYYYMMDD。

      • 保存完整生日、去掉世纪的年份(YYMMDD)、完整年份(YYYY)、两位年份(YY)、月日组合(MMDD)和日月组合(DDMM)。

    • 通用弱项补充:额外加入一些常见数字模式,如 "123", "1234", 近年份等。

    最终,所有这些提取出的字符串会经过一个过滤器:长度必须在 [MIN_WORD_LEN, MAX_WORD_LEN]之间,并放入一个集合 (set) 中以自动去除重复项,最终返回去重后的列表。​ 这个集合就是为该用户定制的“密码词根库”。

    3. 密码猜测生成算法 (generate_passwords函数)

    这是程序中最复杂且最强大的部分,它利用外部工具 Pydictor,对基础词根库应用多种密码构造学中常见的规则。其算法核心是多策略组合逼近。

    • 临时工作区:函数开始时创建一个临时目录,所有 Pydictor 的中间文件都生成于此,处理完毕后自动清理 (shutil.rmtree),体现了良好的资源管理。

    • 策略一:组合器​ (-tool combiner)

      • 算法:将基础词根文件同时作为两个输入提供给 Pydictor 的 combiner工具。该工具会生成所有可能的词根两两组合(笛卡尔积)。

      • 示例:如果基础词根有 ["john", "1990"],组合后可能产生 "johnjohn", "john1990", "1990john", "19901990"。

      • 目的:模拟用户将多个个人信息简单拼接的行为,能快速产生大量有意义的候选密码。

    • 策略二:数字扩展​ (-extend)

      • 算法:为每个基础词根的后面添加指定长度的数字后缀。程序循环尝试了添加 1 到 4 位数字。

      • 示例:对词根 "john"进行2位数字扩展,会生成 "john00", "john01", …, "john99"。

      • 目的:模拟用户在密码后添加生日、年份、幸运数字等常见习惯。这是一种非常高效的生成策略。

    • 策略三:1337 替换​ (-leet)

      • 算法:应用“黑话”替换规则,将字母替换为形状相似的数字或符号。例如:a-> @或4, e-> 3, i-> 1, o-> 0, s-> $等。

      • 示例:"password"可能被变形为 "p@ssw0rd", "p@$$w0rd"等。

      • 目的:模拟用户为了满足密码复杂性要求而进行的常见字符替换,极大地增加了字典的覆盖率。

    • 流程控制与集合管理:

    • 所有生成的密码都被添加到一个总集合 all_guesses中,利用集合的自动去重特性避免重复。

    • 在每个策略执行后,检查 all_guesses的大小是否已达到 MAX_GUESSES。如果达到,则提前终止后续策略,有效控制字典膨胀。

    • 如果所有策略生成的密码数量仍不足,则会回退到直接将基础词根加入集合。

    • 函数最后,将集合转换为列表并截取前 MAX_GUESSES项作为最终结果。

    这种算法设计确保了在有限的资源下,优先应用产出率高的策略,从而生成质量高、冗余度低的个性化密码字典。

    五、 工程实现与优化点评析

  • 健壮性:

    • 异常处理:对 Pydictor 的调用进行了超时 (TimeoutExpired) 和错误 (CalledProcessError) 捕获,防止因单个用户处理失败而导致整个程序崩溃。

    • 降级机制:当数据提取或密码生成失败时,使用预定义的弱密码列表,保证了程序的“韧性”。

    • 资源清理:使用 try…finally结构确保临时目录无论处理成功与否都会被删除,避免了磁盘空间泄漏。

  • 可维护性:

    • 模块化:功能被清晰地划分成不同的函数,如 extract_user_info, generate_base_dictionary等,职责单一,便于测试和修改。

    • 常量定义:将路径、限制等Magic Number定义为全局常量,易于查找和调整。

  • 用户体验:

    • 进度指示:create_progress_bar函数提供了直观的图形化进度条,对于处理大量用户时非常有用。

    • 详细日志:在控制台打印每个策略生成的密码数量,让用户对生成过程有清晰的感知。

  • 六、 总结

    一个设计精良、实现稳健的专用密码字典生成工具。它成功地将一个复杂的安全测试任务自动化、智能化。其强大之处不在于发明新算法,而在于高效地整合利用现有工具(Pydictor)和领域知识(密码心理学),构建了一个高度定制化的数据处理管道。

    通过精细的数据结构设计(如用户信息字典和姓名部分列表)、多层次的密码生成策略(组合、扩展、替换)以及严格的流程控制(去重、数量上限),该程序能够从有限的个人信息中挖掘出大量高价值的密码猜测,充分展示了在安全工程中,针对性的策略往往比蛮力更有效的核心思想。它不仅是一个实用的脚本,更是一个关于如何将行为分析应用于密码破解的优秀案例研究。

    源代码

    #!/usr/bin/env python3
    import re
    import subprocess
    import os
    import sys
    import tempfile
    import shutil
    from pathlib import Path

    PYDICTOR_PATH = "./pydictor.py"
    DATA_FILE = "datasets.txt"
    OUTPUT_FILE = "answer.txt"
    MAX_GUESSES = 10000 # 每个用户最大猜测数
    MIN_WORD_LEN = 3 # 最小词长度
    MAX_WORD_LEN = 16 # 最大词长度

    WEAK_PASSWORDS = [
    "123456", "password", "12345678", "qwerty", "123456789",
    "letmein", "1234567", "football", "iloveyou", "admin123",
    "welcome", "monkey", "login", "abc123", "111111",
    "123123", "password123", "1234", "12345", "dragon"
    ]

    def check_environment():

    if not os.path.exists(PYDICTOR_PATH):
    print(f"错误: 未找到pydictor.py,请确认路径: {PYDICTOR_PATH}")
    sys.exit(1)

    if not os.path.exists(DATA_FILE):
    print(f"错误: 未找到数据文件: {DATA_FILE}")
    sys.exit(1)

    def extract_user_info(line):

    if not line or line.startswith('#'):
    return None

    def get_field(pattern, text, default=''):
    match = re.search(pattern, text)
    return match.group(1).strip() if match else default

    email = get_field(r'email:([^\\t\\n]+)', line)
    name_raw = get_field(r'name:([^\\t\\n]+)', line)
    account = get_field(r'account:([^\\t\\n]+)', line)
    phone = get_field(r'phone:([^\\t\\n]+)', line)
    birth = get_field(r'birth:([^\\t\\n]+)', line)

    name_parts = []
    if name_raw:

    parts = [p.strip() for p in name_raw.split('|')]

    name_parts = [p for p in parts if p and len(p) >= 2]

    return {
    'email': email,
    'name_parts': name_parts,
    'account': account,
    'phone': phone,
    'birth': birth
    }

    def generate_base_dictionary(user_info):

    words = set()

    if user_info['account']:
    acc = user_info['account']
    words.add(acc)

    if len(acc) > 3:
    words.add(acc[:4])
    words.add(acc[:6])

    for part in user_info['name_parts']:

    words.add(part)

    if len(part) >= 4:
    words.add(part[:4].lower())
    words.add(part[:4].upper())

    if '.' in part:
    words.update([p.strip() for p in part.split('.') if len(p.strip()) >= 3])

    if user_info['email'] and '@' in user_info['email']:
    prefix = user_info['email'].split('@')[0]
    if len(prefix) >= 3:
    words.add(prefix)

    if user_info['phone']:
    digits = re.sub(r'\\D', '', user_info['phone'])
    if len(digits) >= 4:
    words.add(digits)
    words.add(digits[-4:])
    words.add(digits[-6:])
    if len(digits) >= 8:
    words.add(digits[:4])

    if user_info['birth'] and len(user_info['birth']) == 8:
    birth = user_info['birth']
    year, month, day = birth[:4], birth[4:6], birth[6:]

    words.update([
    birth,
    birth[2:],
    year,
    year[-2:],
    month + day,
    day + month
    ])

    words.update(['123', '1234', '2020', '2021', '2022', '2023', '2024', '0', '1'])

    final_words = {w for w in words if MIN_WORD_LEN <= len(w) <= MAX_WORD_LEN}

    return list(final_words)

    def execute_pydictor(args, temp_output):

    try:
    cmd = [sys.executable, PYDICTOR_PATH] + args + ['–output', temp_output]
    subprocess.run(cmd, check=True, capture_output=True, timeout=30)

    if os.path.exists(temp_output):
    with open(temp_output, 'r', encoding='utf-8') as f:
    return [line.strip() for line in f if line.strip()]
    except subprocess.TimeoutExpired:
    print("pydictor执行超时,跳过…")
    except subprocess.CalledProcessError as e:
    print(f"命令失败: {e.stderr[:80] if e.stderr else str(e)[:80]}")
    except Exception as e:
    print(f"意外错误: {str(e)[:80]}")

    return []

    def generate_passwords(base_words, limit=MAX_GUESSES):

    if not base_words:
    return WEAK_PASSWORDS[:limit]

    temp_dir = tempfile.mkdtemp(prefix='pydictor_')
    all_guesses = set()

    try:
    base_file = os.path.join(temp_dir, 'base.txt')
    with open(base_file, 'w', encoding='utf-8') as f:
    f.write('\\n'.join(base_words))

    if len(base_words) >= 2 and len(all_guesses) < limit:
    temp_out = os.path.join(temp_dir, 'comb.txt')
    cmd = ['-tool', 'combiner', base_file, base_file]
    guesses = execute_pydictor(cmd, temp_out)
    all_guesses.update(guesses)
    print(f" 组合策略: +{len(guesses)}条")

    if len(all_guesses) < limit:
    temp_out = os.path.join(temp_dir, 'ext.txt')

    for digits in range(1, 5):
    cmd = ['-extend', base_file, '0', '9', str(digits), str(digits)]
    guesses = execute_pydictor(cmd, temp_out)
    all_guesses.update(guesses)
    if len(all_guesses) >= limit:
    break
    print(f" ├─ 数字扩展: +{len(guesses)}条")

    if len(all_guesses) < limit:
    temp_out = os.path.join(temp_dir, 'leet.txt')
    cmd = ['-leet', base_file]
    guesses = execute_pydictor(cmd, temp_out)
    all_guesses.update(guesses)
    print(f" ├─ 1337变形: +{len(guesses)}条")

    if len(all_guesses) < limit // 2:
    all_guesses.update(base_words)
    print(f" ├─ 基础词汇: +{len(base_words)}条")

    final_list = list(all_guesses)[:limit]
    return final_list

    finally:

    if os.path.exists(temp_dir):
    shutil.rmtree(temp_dir)

    def create_progress_bar(current, total, bar_length=40):
    """创建进度条"""
    percent = current / total
    filled = int(bar_length * percent)
    bar = '█' * filled + '░' * (bar_length – filled)
    return f'\\r|{bar}| {percent:.1%} ({current}/{total})'

    def main():

    print("=" * 60)

    check_environment()

    print(f"\\n读取数据文件: {DATA_FILE}")
    with open(DATA_FILE, 'r', encoding='utf-8', errors='ignore') as f:
    lines = [l.strip() for l in f if l.strip()]

    print(f" 共 {len(lines)} 行数据")

    successful = 0
    failed = 0

    with open(OUTPUT_FILE, 'w', encoding='utf-8') as out_f:
    for idx, line in enumerate(lines, 1):
    print(f"\\n[{idx}/{len(lines)}] 处理用户…", end=' ')

    user_info = extract_user_info(line)
    if not user_info:
    failed += 1
    print("跳过(无效数据)")

    out_f.write('<END>\\n')
    continue

    base_words = generate_base_dictionary(user_info)

    guesses = generate_passwords(base_words, MAX_GUESSES)

    if guesses:

    for guess in guesses:
    out_f.write(guess + '\\n')
    successful += 1
    print(f"生成 {len(guesses)} 条")
    else:

    for pwd in WEAK_PASSWORDS[:100]:
    out_f.write(pwd + '\\n')
    failed += 1
    print("使用默认密码")

    out_f.write('<END>\\n')

    sys.stdout.write(create_progress_bar(idx, len(lines)))
    sys.stdout.flush()

    print("\\n\\n" + "=" * 60)
    print(" 处理完成!")
    print(f"成功: {successful} 用户")
    print(f" 降级: {failed} 用户")
    print(f"输出文件: {OUTPUT_FILE}")

    if os.path.exists(OUTPUT_FILE):
    size_mb = os.path.getsize(OUTPUT_FILE) / 1024 / 1024
    print(f"文件大小: {size_mb:.2f} MB")

    with open(OUTPUT_FILE, 'r') as f:
    total_lines = sum(1 for _ in f)
    print(f"总行数: {total_lines:,} (含{total_lines // (MAX_GUESSES + 1)}个分隔符)")

    if __name__ == '__main__':
    main()

    赞(0)
    未经允许不得转载:171主机测评 » 基于个人化信息生成密码猜测字典的自动化脚本
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址