elif protocol == socket.IPPROTO_UDP: # UDP
_ = ip_data[iph_length:iph_length + 4]
if len(_) < 4:
return
src_port, dst_port = struct.unpack("!HH", _)
_ = _last_udp
_last_udp = (sec, src_ip, src_port, dst_ip, dst_port)
if _ == _last_udp: # skip bursts
return
if src_port != 53 and dst_port != 53: # not DNS
if dst_ip in trails:
trail = dst_ip
elif src_ip in trails:
trail = src_ip
else:
trail = None
if trail:
_ = _last_logged_udp
_last_logged_udp = _last_udp
if _ != _last_logged_udp:
if not any(_ in trails[trail][0] for _ in ("malware",)):
log_event((sec, usec, src_ip, src_port, dst_ip, dst_port, PROTO.UDP, TRAIL.IP, trail, trails[trail][0], trails[trail][1]), packet)
else:
dns_data = ip_data[iph_length + 8:]
# Reference: http://www.ccs.neu.edu/home/amislove/teaching/cs4700/fall09/handouts/project1-primer.pdf
if len(dns_data) > 6:
qdcount = struct.unpack("!H", dns_data[4:6])[0]
if qdcount > 0:
offset = 12
query = ""
while len(dns_data) > offset:
length = ord(dns_data[offset:offset + 1])
if not length:
query = query[:-1]
break
query += get_text(dns_data[offset + 1:offset + length + 1]) + '.'
offset += length + 1
query = query.lower()
if not query or re.search(VALID_DNS_NAME_REGEX, query) is None or any(_ in query for _ in (".intranet.",)) or query.split('.')[-1] in IGNORE_DNS_QUERY_SUFFIXES:
return
parts = query.split('.')
if ord(dns_data[2:3]) & 0xfa == 0x00: # standard query (both recursive and non-recursive)
type_, class_ = struct.unpack("!HH", dns_data[offset + 1:offset + 5])
if len(parts) > 2:
if len(parts) > 3 and len(parts[-2]) <= 3:
domain = '.'.join(parts[-3:])
else:
domain = '.'.join(parts[-2:])
if not _check_domain_whitelisted(domain): # e.g. <hash>.hashserver.cs.trendmicro.com
if (sec - (_subdomains_sec or 0)) > HOURLY_SECS:
_subdomains.clear()
_dns_exhausted_domains.clear()
_subdomains_sec = sec
subdomains = _subdomains.get(domain)
if not subdomains:
subdomains = _subdomains[domain] = _set()
subdomains._start = sec
if not re.search(r"\A\d+\-\d+\-\d+\-\d+\Z", parts[0]):
if sec - subdomains._start > 60:
subdomains._start = sec
subdomains.clear()
elif len(subdomains) < DNS_EXHAUSTION_THRESHOLD:
subdomains.add('.'.join(parts[:-2]))
else:
trail = "(%s).%s" % ('.'.join(parts[:-2]), '.'.join(parts[-2:]))
if re.search(r"bl\b", trail) is None: # generic check for DNSBLs
if not any(_ in subdomains for _ in LOCAL_SUBDOMAIN_LOOKUPS): # generic check for local DNS resolutions
log_event((sec, usec, src_ip, src_port, dst_ip, dst_port, PROTO.UDP, TRAIL.DNS, trail, "potential dns exhaustion (suspicious)", "(heuristic)"), packet)
_dns_exhausted_domains.add(domain)
return各行代码功能及函数总体功能
在函数 `_process_packet` 中,这段代码的功能是处理UDP协议的数据包,并执行一系列的分析和日志记录。这段代码是函数的一部分,用于处理捕获到的单个原始IP层数据包。
代码中的 `elif` 块尝试执行以下操作:
1. **解析UDP头部**:
- `src_port, dst_port = struct.unpack("!HH", ip_data[iph_length:iph_length + 4])`: 使用 `struct.unpack` 函数解析UDP头部,提取源端口和目的端口。
2. **处理UDP包**:
- `_ = _last_udp`: 保存当前UDP包的时间戳和相关信息。
- `_last_udp = (sec, src_ip, src_port, dst_ip, dst_port)`: 更新全局变量 `_last_udp` 为当前UDP包的时间戳和相关信息。
- `if _ == _last_udp: # skip bursts`: 如果当前UDP包与上一次检测到的UDP包相同,则跳过,避免重复检测。
3. **检查是否为DNS查询**:
- `if src_port != 53 and dst_port != 53: # not DNS`: 如果源端口和目的端口都不是53(DNS端口),则继续处理非DNS查询。
- `if dst_ip in trails:`: 如果目标IP地址在轨迹中,则记录事件。
- `elif src_ip in trails:`: 如果源IP地址在轨迹中,则记录事件。
4. **解析DNS查询**:
- `dns_data = ip_data[iph_length + 8:]`: 解析DNS查询数据。
- `if len(dns_data) > 6:`: 如果DNS数据长度大于6字节,则继续解析。
- `qdcount = struct.unpack("!H", dns_data[4:6])[0]`: 解析DNS查询计数。
- `if qdcount > 0:`: 如果查询计数大于0,则继续解析。
5. **提取DNS查询名称**:
- `query = ""`: 初始化查询名称。
- `while len(dns_data) > offset:`: 遍历DNS数据,提取查询名称。
- `query += get_text(dns_data[offset + 1:offset + length + 1]) + '.'`: 添加查询名称。
6. **处理DNS查询名称**:
- `query = query.lower()`: 将查询名称转换为小写。
- `if not query or re.search(VALID_DNS_NAME_REGEX, query) is None or any(_ in query for _ in (".intranet.",)) or query.split('.')[-1] in IGNORE_DNS_QUERY_SUFFIXES:`: 如果查询名称无效,则返回。
7. **解析DNS查询类型和类**:
- `type_, class_ = struct.unpack("!HH", dns_data[offset + 1:offset + 5])`: 解析DNS查询类型和类。
8. **检查子域名**:
- `if len(parts) > 2:`: 如果查询名称的域部分长度大于2,则继续处理。
- `if len(parts) > 3 and len(parts[-2]) <= 3:`: 如果查询名称的域部分长度大于3且最后一个子域名长度小于等于3,则继续处理。
9. **检查域名是否在轨迹中**:
- `if not _check_domain_whitelisted(domain):`: 如果域名不在白名单中,则记录事件。
10. **记录DNS查询事件**:
- `log_event((sec, usec, src_ip, src_port, dst_ip, dst_port, PROTO.UDP, TRAIL.DNS, trail, "potential dns exhaustion (suspicious)", "(heuristic)"), packet)`: 记录DNS查询事件,包括时间戳、源IP、源端口、目标IP、目标端口、协议类型、轨迹类型、轨迹描述和原始数据包。