nmap-1.51 源码解析 - IP 解析流程 && chatgpt妙用
字数 643 2025-08-11 21:26:18

Nmap IP解析流程深度解析

1. 参数解析与初始化

Nmap的IP解析流程始于main函数中的参数处理:

while((arg = getopt(argc, fakeargv, "Ab:DdFfhiL:lM:Nno:Pp:qrRS:sT:tUuw:Vv")) != EOF)

关键点

  • 使用getopt解析命令行参数
  • 参数存储在fakeargv而非直接使用argv
  • fakeargv是动态分配的副本,确保安全性
char **fakeargv = (char **) safe_malloc(sizeof(char *) * (argc + 1));
for(i=0; i < argc; i++) {
    fakeargv[i] = safe_malloc(strlen(argv[i]) + 1);
    strncpy(fakeargv[i], argv[i], strlen(argv[i]) + 1);
}
fakeargv[argc] = NULL;

2. IP地址接收流程

IP地址通过以下循环处理:

while(optind < argc) {
    while((currenths = nexthost(fakeargv[optind], lookahead, o.ptime)) && currenths->host.s_addr) {
        // 扫描处理
    }
}

关键点

  • optind标记当前参数位置
  • nexthost()函数负责IP解析
  • 解析结果存储在hoststruct结构中

3. nexthost函数解析

nexthost()是IP解析的核心函数:

struct hoststruct *nexthost(char *hostexp, int lookahead, int pingtimeout) {
    static struct hoststruct *hostbatch = NULL;
    static struct targets targets;
    
    if (!hostbatch)
        hostbatch = safe_malloc((lookahead + 1) * sizeof(struct hoststruct));
    
    if (!targets_valid) {
        if (!parse_targets(&targets, hostexp))
            return NULL;
        targets_valid = 1;
        lasthostexp = hostexp;
    }
    // 后续处理...
}

关键数据结构

struct targets {
    // 用于'/mask'格式的IP范围
    unsigned int netmask;
    unsigned int maskformat;
    struct in_addr start;
    struct in_addr currentaddr;
    struct in_addr end;
    
    // 用于'[1-7,16,91-95,200-]'格式的IP范围
    unsigned char addresses[4][256];
    unsigned int current[4];
    unsigned char last[4];
};

4. parse_targets函数深度解析

parse_targets()是实际执行IP解析的核心函数:

4.1 初始化处理

int parse_targets(struct targets *targets, char *h) {
    char *hostexp = strdup(h);  // 复制输入字符串
    char *addy[5] = {NULL};     // 用于分割IP的四个部分
    addy[0] = hostexp;          // 初始指向整个字符串
    // ...
}

4.2 掩码提取

target_net = strtok(hostexp, "/");
targets->netmask = (s = strtok(NULL,"")) ? atoi(s) : 32;  // 默认32位掩码

if (targets->netmask < 0 || targets->netmask > 32) {
    printf("Illegal netmask value (%d)...\n", targets->netmask);
    targets->netmask = 32;  // 非法掩码则使用默认值
}

4.3 域名检测

for(i=0; *(hostexp + i); i++) {
    if (isupper((int)*(hostexp + i)) || islower((int)*(hostexp + i))) {
        namedhost = 1;  // 包含字母则为域名
        break;
    }
}

4.4 域名解析处理

if (targets->netmask != 32 || namedhost) {
    targets->maskformat = 1;
    if (!inet_aton(target_net, &(targets->start))) {  // 不是有效IP
        if ((target = gethostbyname(target_net))) {    // 尝试解析域名
            memcpy(&(targets->start), target->h_addr_list[0], sizeof(struct in_addr));
        } else {
            fprintf(stderr, "Failed to resolve given hostname/IP: %s\n", target_net);
            free(hostexp);
            return 0;
        }
    }
    // 计算IP范围
    longtmp = ntohl(targets->start.s_addr);
    targets->start.s_addr = longtmp & (unsigned long)(0 - (1<<(32 - targets->netmask)));
    targets->end.s_addr = longtmp | (unsigned long)((1<<(32 - targets->netmask)) - 1);
    targets->currentaddr = targets->start;
    // ...
}

4.5 复杂IP范围解析

对于类似138.[1-7,16,91-95,200-].12.1的复杂IP范围:

else {
    i=0;
    targets->maskformat = 0;
    // 分割IP为四个部分
    while(*++r) {
        if (*r == '.' && i < 4) {
            *r = '\0';
            addy[++i] = r + 1;
        }
        // 处理其他特殊字符...
    }
    
    // 处理每个IP段
    for(i=0; i < 4; i++) {
        j=0;
        while((s = strchr(addy[i], ','))) {
            *s = '\0';
            // 解析单个范围
            if (*addy[i] == '*') {
                start = 0; end = 255;
            } else if (*addy[i] == '-') {
                start = 0;
                end = addy[i]+1 ? atoi(addy[i]+1) : 255;
            } else {
                start = end = atoi(addy[i]);
                if ((r = strchr(addy[i], '-')) && *(r+1))
                    end = atoi(r+1);
                else if (r && !*(r+1))
                    end = 255;
            }
            // 存储解析结果
            for(k=start; k <= end; k++)
                targets->addresses[i][j++] = k;
            addy[i] = s + 1;
        }
        // 处理最后一个范围...
        targets->last[i] = j - 1;
    }
}

5. 关键数据结构详解

5.1 targets结构体

struct targets {
    /* 用于'/mask'格式 */
    unsigned int netmask;      // 子网掩码位数
    unsigned int maskformat;   // 格式标识
    struct in_addr start;      // 起始IP
    struct in_addr currentaddr;// 当前IP
    struct in_addr end;        // 结束IP
    
    /* 用于复杂范围格式 */
    unsigned char addresses[4][256]; // 四个IP段的可能值
    unsigned int current[4];   // 当前各段索引
    unsigned char last[4];     // 各段最后一个值的索引
};

5.2 存储示例

对于192.168.1.0-255

addresses[0][] = {192}
addresses[1][] = {168}
addresses[2][] = {1}
addresses[3][] = {0,1,2,...,255}
last[0] = 0, last[1] = 0, last[2] = 0, last[3] = 255

6. 完整parse_targets函数

int parse_targets(struct targets *targets, char *h) {
    int i=0,j=0,k=0, start, end;
    char *r,*s, *target_net;
    char *addy[5] = {NULL};
    char *hostexp = strdup(h);
    struct hostent *target;
    unsigned long longtmp;
    int namedhost = 0;
    
    addy[0] = r = hostexp;
    
    // 分割IP和掩码
    target_net = strtok(hostexp, "/");
    targets->netmask = (s = strtok(NULL,"")) ? atoi(s) : 32;
    
    // 掩码合法性检查
    if (targets->netmask < 0 || targets->netmask > 32) {
        printf("Illegal netmask value (%d)...\n", targets->netmask);
        targets->netmask = 32;
    }
    
    // 检查是否为域名
    for(i=0; *(hostexp + i); i++)
        if (isalpha((int)*(hostexp + i))) { namedhost = 1; break; }
    
    // 处理带掩码或域名的IP
    if (targets->netmask != 32 || namedhost) {
        targets->maskformat = 1;
        if (!inet_aton(target_net, &(targets->start))) {
            if ((target = gethostbyname(target_net))) {
                memcpy(&(targets->start), target->h_addr_list[0], sizeof(struct in_addr));
            } else {
                fprintf(stderr, "Failed to resolve %s\n", target_net);
                free(hostexp); return 0;
            }
        }
        // 计算IP范围
        longtmp = ntohl(targets->start.s_addr);
        targets->start.s_addr = longtmp & (unsigned long)(0 - (1<<(32 - targets->netmask)));
        targets->end.s_addr = longtmp | (unsigned long)((1<<(32 - targets->netmask)) - 1);
        targets->currentaddr = targets->start;
        if (targets->start.s_addr <= targets->end.s_addr) {
            free(hostexp); return 1;
        }
        fprintf(stderr, "Host specification invalid");
        free(hostexp); return 0;
    } 
    // 处理复杂IP范围
    else {
        // ... (如前面所述的复杂IP范围处理代码)
    }
    bzero((char *)targets->current, 4);
    free(hostexp);
    return 1;
}

7. 总结

Nmap的IP解析流程具有以下特点:

  1. 支持多种IP格式:单个IP、IP范围、CIDR表示法和复杂范围表达式
  2. 自动处理域名解析
  3. 严格的输入验证和错误处理
  4. 高效的内存管理和安全措施
  5. 模块化设计,便于扩展和维护

理解这一流程对于网络安全工具开发和网络扫描原理研究具有重要意义。

Nmap IP解析流程深度解析 1. 参数解析与初始化 Nmap的IP解析流程始于 main 函数中的参数处理: 关键点 : 使用 getopt 解析命令行参数 参数存储在 fakeargv 而非直接使用 argv fakeargv 是动态分配的副本,确保安全性 2. IP地址接收流程 IP地址通过以下循环处理: 关键点 : optind 标记当前参数位置 nexthost() 函数负责IP解析 解析结果存储在 hoststruct 结构中 3. nexthost函数解析 nexthost() 是IP解析的核心函数: 关键数据结构 : 4. parse_ targets函数深度解析 parse_targets() 是实际执行IP解析的核心函数: 4.1 初始化处理 4.2 掩码提取 4.3 域名检测 4.4 域名解析处理 4.5 复杂IP范围解析 对于类似 138.[1-7,16,91-95,200-].12.1 的复杂IP范围: 5. 关键数据结构详解 5.1 targets结构体 5.2 存储示例 对于 192.168.1.0-255 : 6. 完整parse_ targets函数 7. 总结 Nmap的IP解析流程具有以下特点: 支持多种IP格式:单个IP、IP范围、CIDR表示法和复杂范围表达式 自动处理域名解析 严格的输入验证和错误处理 高效的内存管理和安全措施 模块化设计,便于扩展和维护 理解这一流程对于网络安全工具开发和网络扫描原理研究具有重要意义。