在网络安全渗透测试的前期信息收集阶段,子域名探测是核心环节之一。我们日常访问的主域名背后,往往隐藏着大量业务子域名,这些子域名可能对应着测试环境、管理后台、API服务等关键资产。本文要解析的这款多线程子域名暴力破解工具,就是专为渗透测试人员设计的高效信息收集利器,它能通过字典枚举的方式,快速批量扫描目标域名的有效子域名,同时支持IPv4、IPv6双栈解析与多格式报告输出。

一、工具核心功能与应用场景

这款工具是典型的子域名字典爆破工具,核心逻辑简单直接:将预设字典中的单词与目标主域名拼接,生成待检测的子域名,再通过DNS解析验证域名是否有效。

它的核心能力体现在这几点:

  1. 多线程并发:突破单线程速度限制,大幅提升扫描效率;
  2. 双栈解析:同时识别IPv4和IPv6地址,覆盖全网络环境;
  3. 泛解析检测:自动识别域名泛解析,避免无效结果干扰;
  4. 报告输出:支持普通文本、CSV格式报告,方便后续整理;
  5. 轻量化运行:依赖少、编译简单,适配Linux/Unix环境。

适用场景:渗透测试初期资产梳理、企业内部域名资产盘点、网络安全研究中的域名信息收集。


二、核心技术知识点(零基础也能看懂)

在理解工具原理前,我们先搞懂3个必备的基础知识点,这是工具能运行的核心支撑:

1. 子域名与DNS解析

子域名:admin.baidu.com中,admin是子域名,baidu.com是主域名;
DNS解析:把域名翻译成IP地址的过程,工具就是通过这个操作判断子域名是否真实存在。

2. 泛解析(万能解析)

部分域名会设置泛解析:无论输入什么非法子域名,都会解析到同一个IP。
比如输入random123.baidu.com也能得到IP,这类无效结果会干扰扫描,工具会自动识别并过滤。

3. 多线程

单线程:一次只能扫描1个子域名,速度慢;
多线程:同时开启多个任务并行扫描,效率随线程数成倍提升。

4. 关键C语言技术

  • POSIX线程库(pthread):实现多线程并发;
  • getaddrinfo:跨平台DNS解析函数,支持IPv4/IPv6;
  • 互斥锁:解决多线程同时输出、写入文件的冲突问题;
  • 内存管理:安全申请/释放内存,避免程序崩溃。

三、工具整体设计思路(流程原理图)

工具的运行逻辑非常清晰,分为初始化→检测→扫描→收尾四大步骤,用流程图表示如下:

启动程序 → 解析命令行参数(域名、线程数、字典、报告)
          ↓
检测目标域名是否开启泛解析(生成随机子域名测试)
          ↓
创建多线程 → 线程读取字典 → 拼接子域名 → DNS解析
          ↓
过滤泛解析无效结果 → 输出有效域名与IP
          ↓
所有线程结束 → 关闭文件/释放内存 → 程序退出

核心设计原则:简单高效、并发安全、结果精准、易用性强


四、代码核心原理逐段解析

struct dns_discovery_args {
    FILE * reg_report;
    FILE * csv_report;
    char * domain;
    int nthreads;
    struct addrinfo * wildcard;
};

...
void resolve_lookup(const char * hostname)
{
    struct addrinfo * res, hints;

    memset(&hints, 0, sizeof hints);
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_flags |= AI_CANONNAME;

    if (getaddrinfo(hostname, NULL, &hints, &res) == 0) {
        pthread_mutex_lock(&mutexsum);

	if (!compare_hosts(res, dd_args.wildcard))
	    print_resolve_lookup(hostname, res);

    	freeaddrinfo(res);
        pthread_mutex_unlock(&mutexsum);
    }
}

void dns_discovery(FILE * file, const char * domain)
{
    char line[LEN];
    char hostname[MAX];

    while (fgets(line, sizeof line, file) != NULL) {
        chomp(line);
        snprintf(hostname, sizeof hostname, "%s.%s", line, domain);
        resolve_lookup(hostname);
    }
}

void *dns_discovery_thread(void * args)
{
    FILE * wordlist = (FILE *) args;
    dns_discovery(wordlist, dd_args.domain);
    /*pthread_exit((void *) 0);*/
    return NULL;	
}

int main(int argc, char ** argv) 
{
...

    if (atexit(cleanup) != 0) {
        fprintf(stderr, "Cannot set exit function\n");
        return EXIT_FAILURE;
    }

    wordlist = parse_args(argc, argv);   
    wildcard_detect();

    if (dd_args.wildcard) {
        snprintf(hostname, sizeof hostname, "*.%s", dd_args.domain);
        print_resolve_lookup(hostname, dd_args.wildcard);
    }

    threads = (pthread_t *) ck_malloc(dd_args.nthreads * sizeof(pthread_t)); 
 
    for (i = 0; i < dd_args.nthreads; i++) {
        if (pthread_create(&threads[i], NULL, dns_discovery_thread, (void *)wordlist) != 0)
            error("pthread_create");
    }
    for (i = 0; i < dd_args.nthreads; i++) {
        pthread_join(threads[i], NULL);
    }
  
    free(threads);
    fclose(wordlist);
    return EXIT_SUCCESS;
}
...

If you need the complete source code, please add the WeChat number (c17865354792)

我们结合源码,用大白话拆解工具的实现逻辑,重点看核心模块:

1. 全局配置与宏定义

工具提前定义了固定参数,比如字典默认文件名、最大域名长度、输出函数:

// 定义常量:字典名、域名最大长度、随机字符串长度
#define DEFAULT_WL "wordlist.wl"
#define MAX 512
#define SIZERANDSTR 30

// 封装打印函数:同时输出到控制台和报告文件
#define SAY(args...) fprintf(stdout, args);

作用:统一管理配置,简化代码编写,让输出逻辑更简洁。

2. 核心参数结构体

存储程序运行的所有关键配置,是全局数据中心:

struct dns_discovery_args {
    FILE * reg_report;  // 普通报告文件
    FILE * csv_report;  // CSV报告文件
    char * domain;      // 目标主域名
    int nthreads;       // 线程数量
    struct addrinfo * wildcard; // 泛解析IP信息
};

作用:集中管理所有运行参数,方便多线程共享配置。

3. 命令行参数解析

用户输入的指令(域名、线程数、字典路径)在这里处理:

FILE * parse_args(int argc, char ** argv){
    // 读取目标域名、线程数、字典、报告文件
    // 无参数时自动打印使用说明
}

作用:让工具支持自定义配置,满足不同场景需求。

4. 泛解析检测(核心防误报功能)

工具会生成一个随机字符串+主域名,测试是否能解析出IP:

void wildcard_detect(){
    // 生成随机子域名
    gen_randstr(rand_str, SIZERANDSTR);
    snprintf(hostname, sizeof hostname, "%s.%s", rand_str, dd_args.domain);
    // DNS解析,判断是否存在泛解析
    if(getaddrinfo(hostname, NULL, &hints, &rand_res) == 0)
        dd_args.wildcard = rand_res;
}

作用:提前识别泛解析,后续扫描时直接过滤无效结果。

5. 多线程核心逻辑

这是工具的灵魂,负责并发扫描:

// 创建指定数量的线程
for (i = 0; i < dd_args.nthreads; i++) {
    pthread_create(&threads[i], NULL, dns_discovery_thread, wordlist);
}

// 线程执行函数:读取字典、拼接域名、解析IP
void * dns_discovery_thread(void * args){
    dns_discovery(wordlist, dd_args.domain);
    return NULL;	
}

作用:用多线程替代单线程,让扫描速度提升数倍。

6. DNS解析与结果过滤

拼接子域名后进行DNS解析,并对比泛解析IP,过滤无效结果:

void resolve_lookup(const char * hostname){
    // DNS解析子域名
    if(getaddrinfo(hostname, NULL, &hints, &res) == 0) {
        // 加锁:防止多线程同时输出乱码
        pthread_mutex_lock(&mutexsum);
        // 对比泛解析,非泛解析结果才打印
        if(!compare_hosts(res, dd_args.wildcard))
            print_resolve_lookup(hostname, res);
        pthread_mutex_unlock(&mutexsum);
    }
}

作用:保证结果精准,同时解决多线程并发冲突问题。

7. 安全收尾函数

程序退出时自动关闭文件、释放内存,避免资源泄漏:

void cleanup(){
    fclose(报告文件);
    freeaddrinfo(泛解析数据);
}

作用:保证程序稳定退出,不占用系统资源。



五、最简单测试运行命令

创建:字典文件 wordlist.wl

新建 wordlist.wl,写入几个测试用子域名:

www
mail
admin
test
api
blog
dev

测试 1:基础扫描(推荐)

./subscan baidu.com

测试 2:指定线程 + 字典 + 报告

./subscan baidu.com -t 3 -w wordlist.wl -r result.txt

测试 3:生成 CSV 报告

./subscan qq.com -t 5 -c result.csv

运行成功的样子

DOMAIN: baidu.com
WORDLIST: wordlist.wl

www.baidu.com
IPv4 address: 110.242.68.3

mail.baidu.com
IPv4 address: 123.125.50.23

...

总结

这款多线程子域名探测工具,是网络安全信息收集的经典实用工具。它没有复杂的架构,却用最简洁的C语言代码,实现了高效、精准、稳定的子域名扫描能力。

它的核心价值在于:把复杂的DNS解析、多线程并发、泛解析过滤等技术,封装成一个简单易用的命令行工具,让渗透测试人员无需关注底层原理,就能快速完成子域名资产收集。

Welcome to follow WeChat official account【程序猿编码

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐