跳转至

Fscan源码分析

fscan 发现域控的原理

代码实现

NetBIOS.go

senddata1 := []byte{102, 102, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 32, 67, 75, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 0, 0, 33, 0, 1}
    realhost := fmt.Sprintf("%s:137", info.Host)
    conn, err := net.DialTimeout("udp", realhost, time.Duration(common.Timeout)*time.Second)

当扫描到139端口,向137端口发送udp数据以获取NetBios,根据返回数据判别主机信息

func ParseNetBios(input []byte) (netbios NetBiosInfo, err error) {
    if len(input) < 57 {
        err = errNetBIOS
        return
    }
    data := input[57:]
    var num int
    num, err = bytetoint(input[56:57][0])
    if err != nil {
        return
    }
    var msg string
    for i := 0; i < num; i++ {
        if len(data) < 18*i+16 {
            break
        }
        name := string(data[18*i : 18*i+15])
        flag_bit := data[18*i+15 : 18*i+16]
        if GROUP_NAMES[string(flag_bit)] != "" && string(flag_bit) != "\x00" {
            msg += fmt.Sprintf("%s: %s\n", GROUP_NAMES[string(flag_bit)], name)
        } else if UNIQUE_NAMES[string(flag_bit)] != "" && string(flag_bit) != "\x00" {
            msg += fmt.Sprintf("%s: %s\n", UNIQUE_NAMES[string(flag_bit)], name)
        } else if string(flag_bit) == "\x00" || len(data) >= 18*i+18 {
            name_flags := data[18*i+16 : 18*i+18][0]
            if name_flags >= 128 {
                msg += fmt.Sprintf("%s: %s\n", GROUP_NAMES[string(flag_bit)], name)
            } else {
                msg += fmt.Sprintf("%s: %s\n", UNIQUE_NAMES[string(flag_bit)], name)
            }
        } else {
            msg += fmt.Sprintf("%s \n", name)
        }
    }
    if len(msg) == 0 {
        err = errNetBIOS
        return
    }
    err = yaml.Unmarshal([]byte(msg), &netbios)
    if netbios.DomainName != "" {
        netbios.GroupName = netbios.DomainName
    }
    return
}

ParseNetBios函数解析通过NBNS收到的数据,从而根据规则判断主机身份

data := input[57:] 这一行将响应消息的前 57 个字节去掉,类似于协议头

num, err = bytetoint(input[56:57][0]) 这行代码读取第 56 个字节,并将它转换为整数 num。这个字节表示后续的 NetBIOS 名称记录数量。

函数接下来进入一个循环,每次循环处理一个 NetBIOS 名称记录。对于每个名称记录,函数提取 18 字节的数据:15 字节的名称和 1 字节的标志位。名称(name)是记录的 NetBIOS 名称。标志位(flag_bit)用于确定该名称是独特的名称还是组名称。根据标志位来决定如何处理每个名称。如果它匹配 UNIQUE_NAMESGROUP_NAMES 中的一个条目,则将对应的名称和类型添加到输出消息中

    GROUP_NAMES = map[string]string{
        "\x00": "DomainName",
        "\x1C": "DomainControllers",
        "\x1E": "Browser Service Elections",
    }

如果标志位是\x1C那么就判定为域控