Python安全开发之子域名扫描器(含详细注释)

核心代码
python 复制代码
import socket
import argparse
import threading
from concurrent.futures import ThreadPoolExecutor
import time

# 全局锁对象,用于线程安全地访问共享资源found_subdomains
found_subdomain_lock = threading.Lock()
# 存储找到的子域名结果的全局列表
found_subdomains = []

def check_subdomain(domain, subdomain):
    """
    检查指定的子域名是否存在(通过DNS解析)
    
    参数:
        domain (str): 主域名,如 "baidu.com"
        subdomain (str): 要检查的子域名前缀,如 "www" 或 "mail"
    
    功能:
        1. 构造完整的子域名(如 www.baidu.com)
        2. 尝试进行DNS解析获取IP地址
        3. 如果解析成功,记录结果并打印
        4. 处理各种可能的异常情况
    """
    # 构造完整的子域名
    full_domain = f"{subdomain}.{domain}"
    try:
        # 尝试通过socket.gethostbyname进行DNS解析
        ip_address = socket.gethostbyname(full_domain)
        # 格式化输出结果,保持对齐美观
        result = f"[+] Found: {full_domain:<30} -> {ip_address}"
        print(result)
        # 使用锁确保多线程环境下对共享列表的安全访问
        with found_subdomain_lock:
            found_subdomains.append(result)
    except socket.gaierror:
        # socket.gaierror表示DNS解析失败(域名不存在)
        # 这是最常见的情况,因为大多数子域名都不存在
        pass
    except Exception as e:
        # 捕获其他可能的异常并打印错误信息
        print(f"[!] 扫描子域名 {full_domain} 时发生错误:{e}")

def main():
    """
    主函数:解析命令行参数,读取字典文件,启动多线程扫描
    
    功能流程:
        1. 解析用户提供的命令行参数
        2. 读取子域名字典文件
        3. 使用线程池并发执行子域名检查
        4. 输出扫描结果并可选择性保存到文件
    """
    # 创建命令行参数解析器
    parser = argparse.ArgumentParser(description="子域名扫描")
    # 添加必需的域名参数
    parser.add_argument("-d", "--domain", required=True, help="目标域名")
    # 添加必需的字典文件参数
    parser.add_argument("-w", "--wordlist", required=True, help="字典文件")
    # 添加线程数参数,默认为10
    parser.add_argument("-t", "--threads", type=int, default=10, help="线程数")
    # 添加可选的输出文件参数
    parser.add_argument("-o", "--output", help="输出文件")
    # 解析命令行参数
    args = parser.parse_args()

    # 提取参数值
    domain = args.domain
    wordlist_path = args.wordlist
    num_threads = args.threads

    # 打印扫描配置信息
    print("-"*50)
    print(f"[+] 域名:{domain}")
    print(f"[+] 字典文件:{wordlist_path}")
    print(f"[+] 线程数:{num_threads}")
    print(f"[+]时间:{time.strftime('%Y-%m-%d %H:%M:%S')}")
    print("-"*50)

    # 尝试打开并读取字典文件
    try:
        with open(wordlist_path, "r") as wordlist_file:
            # 使用列表推导式读取所有非空行并去除首尾空白字符
            # 列表推导式说明:
            # [line.strip() for line in wordlist_file if line.strip()]
            # 相当于以下传统循环代码:
            # subdomains = []
            # for line in wordlist_file:
            #     stripped_line = line.strip()
            #     if stripped_line:  # 如果去除空白后不为空
            #         subdomains.append(stripped_line)
            # 
            # 列表推导式的优势:
            # 1. 语法更简洁
            # 2. 执行效率更高
            # 3. 可读性更好(对于熟悉Python的开发者)
            subdomains = [line.strip() for line in wordlist_file if line.strip()]
            print(f"[+] 待扫描的子域名数:{len(subdomains)}")
    except FileNotFoundError:
        print(f"[!] 字典文件 {wordlist_path} 不存在")
        return

    # 使用ThreadPoolExecutor创建线程池,限制最大工作线程数
    with ThreadPoolExecutor(max_workers=num_threads) as executor:
        # 使用executor.map并发执行check_subdomain函数
        # lambda函数用于适配参数,固定domain参数,变化subdomains中的每个子域名
        executor.map(lambda sub: check_subdomain(domain, sub), subdomains)

    # 扫描完成后的结果处理
    print("-"*50)
    print("[+] 扫描完成")
    if found_subdomains:
        print(f"[+] 扫描完成,找到 {len(found_subdomains)} 个子域名")

        # 对结果进行排序以便更好地查看
        full_result = sorted(found_subdomains)
        if args.output:
            print(f"[+] 保存结果到 {args.output}")
            with open(args.output, "w") as f:
                for result in full_result:
                    f.write(result + "\n")
            print("[-] 结果保存完毕")
    else:
        print("[-] 未找到任何子域名")
        print("-"*50)

if __name__ == "__main__":
    main()

1、列表推导式 [line.strip() for line in wordlist_file if line.strip()] 的详细解释:

  • 这是一个高效的Python惯用法,用于从文件中读取所有非空行
  • line.strip() 去除每行首尾的空白字符(包括换行符)
  • if line.strip() 过滤掉空行(去除空白后为空的行)
  • 整体相当于一个过滤和转换的组合操作

2、线程安全:使用 threading.Lock 确保多个线程不会同时修改 found_subdomains 列表

3、异常处理:区分了 socket.gaierror(常见的DNS解析失败)和其他异常

4、并发处理:使用 ThreadPoolExecutor 实现多线程并发扫描,提高扫描效率

相关推荐
dashizhi201522 分钟前
共享文件禁止拖动本地磁盘、共享文件禁止另存为、禁止打印共享文件、禁止复制共享文件的方法
运维·服务器·网络·安全·电脑
网教盟人才服务平台28 分钟前
AI 全面重塑网络攻防生态,智能安全进入深度对抗时代
网络·人工智能·安全
炸炸鱼.29 分钟前
Python 操作 MySQL 数据库
android·数据库·python·adb
_深海凉_1 小时前
LeetCode热题100-颜色分类
python·算法·leetcode
菩提小狗1 小时前
每日安全情报报告 · 2026-04-12
网络安全·漏洞·cve·安全情报·每日安全
AC赳赳老秦2 小时前
OpenClaw email技能:批量发送邮件、自动回复,高效处理工作邮件
运维·人工智能·python·django·自动化·deepseek·openclaw
zhaoshuzhaoshu2 小时前
Python 语法之数据结构详细解析
python
AI问答工程师2 小时前
Meta Muse Spark 的"思维压缩"到底是什么?我用 Python 复现了核心思路(附代码)
人工智能·python
qq_260241232 小时前
将盾CDN:网络安全人才培养的困境与破局之道
安全·web安全
zfan5203 小时前
python对Excel数据处理(1)
python·excel·pandas