前言你有没有想过公司网络里的防火墙挡住了大部分攻击但有些攻击是藏在正常流量里的——比如SQL注入、缓冲区溢出它们看起来就是普通的HTTP请求。入侵检测系统IDS 是防火墙的搭档专门分析流量内容发现攻击特征。今天我们从零实现一个入侵检测系统的核心功能· 数据包捕获Libpcap模拟· 规则解析Snort规则语法· 模式匹配AC自动机· 协议解析HTTP/HTTP头· 日志与告警---一、IDS核心原理1. 入侵检测架构┌─────────────────────────────────────────────────────────────┐│ 网络数据包 ││ (混杂模式接收) │└─────────────────────────────────────────────────────────────┘│▼┌─────────────────────────────────────────────────────────────┐│ 包捕获引擎 ││ (Libpcap / PF_RING) │└─────────────────────────────────────────────────────────────┘│▼┌─────────────────────────────────────────────────────────────┐│ 协议解析器 ││ (IP/TCP/UDP/HTTP/DNS) │└─────────────────────────────────────────────────────────────┘│▼┌─────────────────────────────────────────────────────────────┐│ 规则引擎 ││ ┌──────────────┐ ┌──────────────┐ ││ │ AC自动机 │ │ 正则匹配 │ ││ │ 模式匹配 │ │ 深度检测 │ ││ └──────────────┘ └──────────────┘ │└─────────────────────────────────────────────────────────────┘│┌───────┴───────┐▼ ▼┌─────────────┐ ┌─────────────┐│ 告警输出 │ │ 日志存储 ││ (Alert) │ │ (Log) │└─────────────┘ └─────────────┘2. Snort规则格式alert tcp $HOME_NET any - $EXTERNAL_NET 80(msg:SQL Injection; content:select; nocase;pcre:/select.*from/i; classtype:web-application-attack;)字段 说明action alert/log/pass/drop/rejectprotocol tcp/udp/icmp/ipsrc_ip 源IP可用变量src_port 源端口dir - (单向) 或 (双向)dst_ip 目标IPdst_port 目标端口options 检测选项content/pcre等---二、完整代码实现1. 基础数据结构c#include stdio.h#include stdlib.h#include string.h#include unistd.h#include pthread.h#include time.h#include errno.h#include arpa/inet.h#include netinet/ip.h#include netinet/tcp.h#include netinet/udp.h#include netinet/ip_icmp.h#include net/if.h#include sys/socket.h#include sys/time.h#define MAX_RULES 10000#define MAX_PAYLOAD_SIZE 65536#define MAX_MSG_LEN 256#define MAX_VARS 64// 动作类型typedef enum {ACT_ALERT 0,ACT_LOG,ACT_PASS,ACT_DROP,ACT_REJECT} action_type_t;// 规则选项typedef struct rule_option {char content[256];int content_len;int nocase; // 不区分大小写char pcre_pattern[256]; // 正则表达式char msg[MAX_MSG_LEN]; // 告警消息char classtype[64]; // 攻击类型int sid; // 规则IDint rev; // 版本号struct rule_option *next;} rule_option_t;// 规则结构typedef struct snort_rule {action_type_t action;uint8_t protocol; // IPPROTO_TCP/UDP/ICMPstruct in_addr src_ip;struct in_addr src_mask;uint16_t src_port;int src_port_any;struct in_addr dst_ip;struct in_addr dst_mask;uint16_t dst_port;int dst_port_any;int direction; // 1:单向, 2:双向rule_option_t *options;int priority; // 优先级int enabled;struct snort_rule *next;} snort_rule_t;// 告警信息typedef struct alert {time_t timestamp;struct in_addr src_ip;uint16_t src_port;struct in_addr dst_ip;uint16_t dst_port;char msg[MAX_MSG_LEN];char classtype[64];int sid;struct alert *next;} alert_t;// 入侵检测系统typedef struct ids_engine {snort_rule_t *rules;alert_t *alerts;int rule_count;int alert_count;int max_alerts;pthread_mutex_t mutex;int running;pthread_t capture_thread;pthread_t analysis_thread;} ids_engine_t;// 数据包结构typedef struct packet {struct timeval ts;struct in_addr src_ip;struct in_addr dst_ip;uint8_t protocol;uint16_t src_port;uint16_t dst_port;uint8_t *payload;int payload_len;int ip_id;} packet_t;2. 规则解析c// 创建IDS引擎ids_engine_t *ids_create(int max_alerts) {ids_engine_t *ids malloc(sizeof(ids_engine_t));memset(ids, 0, sizeof(ids_engine_t));ids-max_alerts max_alerts;ids-running 1;pthread_mutex_init(ids-mutex, NULL);printf([IDS] 入侵检测引擎初始化完成\n);return ids;}// 解析IP地址void parse_ip(const char *str, struct in_addr *ip, struct in_addr *mask) {if (strcmp(str, any) 0 || strcmp(str, $ANY) 0) {ip-s_addr 0;mask-s_addr 0;return;}if (strcmp(str, $HOME_NET) 0) {inet_pton(AF_INET, 192.168.1.0, ip);inet_pton(AF_INET, 255.255.255.0, mask);return;}if (strcmp(str, $EXTERNAL_NET) 0) {inet_pton(AF_INET, 0.0.0.0, ip);inet_pton(AF_INET, 0.0.0.0, mask);return;}// 支持 CIDR: 192.168.1.0/24char *slash strchr(str, /);if (slash) {int len slash - str;char ip_str[32];strncpy(ip_str, str, len);ip_str[len] \0;inet_pton(AF_INET, ip_str, ip);int bits atoi(slash 1);mask-s_addr htonl(~((1 (32 - bits)) - 1));} else {inet_pton(AF_INET, str, ip);mask-s_addr 0xFFFFFFFF;}}// 解析端口uint16_t parse_port(const char *str, int *is_any) {if (strcmp(str, any) 0 || strcmp(str, $ANY) 0) {*is_any 1;return 0;}// 支持范围: 1-1024char *dash strchr(str, -);if (dash) {// 简化只取第一个*is_any 0;return atoi(str);}*is_any 0;return atoi(str);}// 添加规则void ids_add_rule(ids_engine_t *ids, const char *rule_str) {pthread_mutex_lock(ids-mutex);snort_rule_t *rule malloc(sizeof(snort_rule_t));memset(rule, 0, sizeof(snort_rule_t));rule-enabled 1;// 简化解析: alert tcp $HOME_NET any - $EXTERNAL_NET 80 (msg:...; content:...;)char action[16], protocol[16], src_ip[64], src_port[16], dir[8], dst_ip[64], dst_port[16];char options_buf[1024];// 使用sscanf解析简化if (sscanf(rule_str, %s %s %s %s %s %s %s (%[^)]),action, protocol, src_ip, src_port, dir, dst_ip, dst_port, options_buf) 8) {printf(规则解析失败: %s\n, rule_str);free(rule);pthread_mutex_unlock(ids-mutex);return;}// 动作if (strcmp(action, alert) 0) rule-action ACT_ALERT;else if (strcmp(action, log) 0) rule-action ACT_LOG;else if (strcmp(action, pass) 0) rule-action ACT_PASS;// 协议if (strcmp(protocol, tcp) 0) rule-protocol IPPROTO_TCP;else if (strcmp(protocol, udp) 0) rule-protocol IPPROTO_UDP;else if (strcmp(protocol, icmp) 0) rule-protocol IPPROTO_ICMP;else rule-protocol 0;// IPparse_ip(src_ip, rule-src_ip, rule-src_mask);parse_ip(dst_ip, rule-dst_ip, rule-dst_mask);// 端口rule-src_port parse_port(src_port, rule-src_port_any);rule-dst_port parse_port(dst_port, rule-dst_port_any);// 方向rule-direction (strcmp(dir, -) 0) ? 1 : 2;// 解析选项rule_option_t *opt malloc(sizeof(rule_option_t));memset(opt, 0, sizeof(rule_option_t));// msgchar *msg_start strstr(options_buf, msg:\);if (msg_start) {msg_start 5;char *msg_end strchr(msg_start, );if (msg_end) {int len msg_end - msg_start;if (len MAX_MSG_LEN - 1) {strncpy(opt-msg, msg_start, len);opt-msg[len] \0;}}}// contentchar *content_start strstr(options_buf, content:\);if (content_start) {content_start 9;char *content_end strchr(content_start, );if (content_end) {int len content_end - content_start;if (len 256) {strncpy(opt-content, content_start, len);opt-content[len] \0;opt-content_len len;}}}// nocaseif (strstr(options_buf, nocase)) {opt-nocase 1;}// sidchar *sid_start strstr(options_buf, sid:);if (sid_start) {opt-sid atoi(sid_start 4);}// classtypechar *class_start strstr(options_buf, classtype:);if (class_start) {class_start 10;char *class_end strchr(class_start, ;);if (class_end) {int len class_end - class_start;if (len 64) {strncpy(opt-classtype, class_start, len);opt-classtype[len] \0;}}}rule-options opt;rule-next ids-rules;ids-rules rule;ids-rule_count;pthread_mutex_unlock(ids-mutex);printf([规则] 添加规则 SID:%d, 动作:%s, 协议:%s\n,opt-sid, action, protocol);}3. 模式匹配AC自动机核心c// AC自动机节点typedef struct ac_node {unsigned char child[256]; // 子节点索引int fail; // 失败指针int output; // 输出规则IDint depth;} ac_node_t;#define MAX_AC_NODES 100000typedef struct ac_automaton {ac_node_t nodes[MAX_AC_NODES];int node_count;int pattern_count;} ac_automaton_t;ac_automaton_t *g_ac NULL;// 初始化AC自动机void ac_init() {g_ac malloc(sizeof(ac_automaton_t));memset(g_ac, 0, sizeof(ac_automaton_t));g_ac-node_count 1;// 初始化根节点memset(g_ac-nodes[0].child, 0, sizeof(g_ac-nodes[0].child));g_ac-nodes[0].fail 0;g_ac-nodes[0].output -1;g_ac-nodes[0].depth 0;}// 插入模式串int ac_insert(const unsigned char *pattern, int len, int rule_id) {int node 0;for (int i 0; i len; i) {unsigned char c pattern[i];if (!g_ac-nodes[node].child[c]) {if (g_ac-node_count MAX_AC_NODES) return -1;int new_node g_ac-node_count;memset(g_ac-nodes[new_node].child, 0, sizeof(g_ac-nodes[new_node].child));g_ac-nodes[new_node].fail 0;g_ac-nodes[new_node].output -1;g_ac-nodes[new_node].depth g_ac-nodes[node].depth 1;g_ac-nodes[node].child[c] new_node;}node g_ac-nodes[node].child[c];}g_ac-nodes[node].output rule_id;g_ac-pattern_count;return 0;}// 构建失败指针BFSvoid ac_build() {int queue[MAX_AC_NODES];int head 0, tail 0;// 第一层节点入队for (int i 0; i 256; i) {int child g_ac-nodes[0].child[i];if (child) {g_ac-nodes[child].fail 0;queue[tail] child;}}while (head tail) {int node queue[head];for (int c 0; c 256; c) {int child g_ac-nodes[node].child[c];if (child) {// 设置失败指针int fail g_ac-nodes[node].fail;while (fail !g_ac-nodes[fail].child[c]) {fail g_ac-nodes[fail].fail;}if (g_ac-nodes[fail].child[c]) {g_ac-nodes[child].fail g_ac-nodes[fail].child[c];} else {g_ac-nodes[child].fail 0;}queue[tail] child;}}}}// AC自动机搜索void ac_search(const unsigned char *text, int len, int *match_rule_ids, int *match_count) {int node 0;*match_count 0;for (int i 0; i len; i) {unsigned char c text[i];while (node !g_ac-nodes[node].child[c]) {node g_ac-nodes[node].fail;}if (g_ac-nodes[node].child[c]) {node g_ac-nodes[node].child[c];}// 检查当前节点是否有输出if (g_ac-nodes[node].output 0) {match_rule_ids[(*match_count)] g_ac-nodes[node].output;}// 检查失败指针链int fail_node g_ac-nodes[node].fail;while (fail_node g_ac-nodes[fail_node].output 0) {fail_node g_ac-nodes[fail_node].fail;}if (fail_node g_ac-nodes[fail_node].output 0) {match_rule_ids[(*match_count)] g_ac-nodes[fail_node].output;}}}4. 协议解析c// HTTP请求头解析typedef struct http_info {char method[16];char uri[256];char version[16];char host[256];char user_agent[256];char content_type[64];int content_length;char *body;int body_len;} http_info_t;// 解析HTTP请求int parse_http(const uint8_t *data, int len, http_info_t *http) {memset(http, 0, sizeof(http_info_t));http-content_length -1;char *ptr (char*)data;char *end ptr len;// 解析请求行char *line_end strstr(ptr, \r\n);if (!line_end) return -1;// GET /index.html HTTP/1.1sscanf(ptr, %s %s %s, http-method, http-uri, http-version);ptr line_end 2;// 解析头部while (ptr end) {line_end strstr(ptr, \r\n);if (!line_end) break;char *colon strchr(ptr, :);if (colon) {char *key ptr;int key_len colon - ptr;char *value colon 1;while (*value ) value;int value_len line_end - value;if (strncasecmp(key, Host, key_len) 0) {strncpy(http-host, value, value_len);http-host[value_len] \0;} else if (strncasecmp(key, User-Agent, key_len) 0) {strncpy(http-user_agent, value, value_len);http-user_agent[value_len] \0;} else if (strncasecmp(key, Content-Type, key_len) 0) {strncpy(http-content_type, value, value_len);http-content_type[value_len] \0;} else if (strncasecmp(key, Content-Length, key_len) 0) {http-content_length atoi(value);}}ptr line_end 2;}// bodyhttp-body ptr;http-body_len end - ptr;return 0;}5. 规则匹配引擎c// 检查IP是否匹配int ip_matches(struct in_addr ip, struct in_addr pattern, struct in_addr mask) {uint32_t ip_net ip.s_addr mask.s_addr;uint32_t pattern_net pattern.s_addr mask.s_addr;return ip_net pattern_net || pattern.s_addr 0;}// 检查端口是否匹配int port_matches(uint16_t port, uint16_t rule_port, int any) {return any || port rule_port;}// 规则匹配snort_rule_t *ids_match_rule(ids_engine_t *ids, packet_t *pkt) {pthread_mutex_lock(ids-mutex);snort_rule_t *rule ids-rules;while (rule) {if (!rule-enabled) {rule rule-next;continue;}// 协议匹配if (rule-protocol rule-protocol ! pkt-protocol) {rule rule-next;continue;}// IP匹配if (!ip_matches(pkt-src_ip, rule-src_ip, rule-src_mask) ||!ip_matches(pkt-dst_ip, rule-dst_ip, rule-dst_mask)) {rule rule-next;continue;}// 端口匹配if (pkt-protocol IPPROTO_TCP || pkt-protocol IPPROTO_UDP) {if (!port_matches(pkt-src_port, rule-src_port, rule-src_port_any) ||!port_matches(pkt-dst_port, rule-dst_port, rule-dst_port_any)) {rule rule-next;continue;}}// 选项匹配rule_option_t *opt rule-options;if (opt opt-content[0]) {// 使用AC自动机搜索int match_ids[10];int match_count;ac_search(pkt-payload, pkt-payload_len, match_ids, match_count);// 检查是否匹配当前规则int found 0;for (int i 0; i match_count; i) {if (match_ids[i] opt-sid) {found 1;break;}}if (!found) {rule rule-next;continue;}}pthread_mutex_unlock(ids-mutex);return rule;}pthread_mutex_unlock(ids-mutex);return NULL;}6. 告警处理c// 产生告警void ids_alert(ids_engine_t *ids, packet_t *pkt, snort_rule_t *rule, int triggered) {pthread_mutex_lock(ids-mutex);// 如果告警队列满了删除最旧的if (ids-alert_count ids-max_alerts) {alert_t *old ids-alerts;ids-alerts old-next;free(old);ids-alert_count--;}alert_t *alert malloc(sizeof(alert_t));alert-timestamp time(NULL);alert-src_ip pkt-src_ip;alert-src_port pkt-src_port;alert-dst_ip pkt-dst_ip;alert-dst_port pkt-dst_port;if (rule-options) {strcpy(alert-msg, rule-options-msg);strcpy(alert-classtype, rule-options-classtype);alert-sid rule-options-sid;}alert-next ids-alerts;ids-alerts alert;ids-alert_count;pthread_mutex_unlock(ids-mutex);printf([告警] [%s] %s SID:%d\n,alert-classtype, alert-msg, alert-sid);char src_str[32], dst_str[32];inet_ntop(AF_INET, pkt-src_ip, src_str, sizeof(src_str));inet_ntop(AF_INET, pkt-dst_ip, dst_str, sizeof(dst_str));printf( %s:%d - %s:%d\n, src_str, pkt-src_port, dst_str, pkt-dst_port);}7. 测试代码cvoid test_ids() {printf( 入侵检测系统测试 \n\n);// 初始化AC自动机ac_init();// 创建IDS引擎ids_engine_t *ids ids_create(1000);// 添加规则ids_add_rule(ids, alert tcp $HOME_NET any - $EXTERNAL_NET 80 (msg:\SQL Injection\; content:\select\; nocase; sid:1001; classtype:web-application-attack;));ids_add_rule(ids, alert tcp $HOME_NET any - $EXTERNAL_NET 80 (msg:\XSS Attack\; content:\script\; nocase; sid:1002; classtype:web-application-attack;));ids_add_rule(ids, alert tcp $HOME_NET any - $EXTERNAL_NET 80 (msg:\Buffer Overflow\; content:\AAAAAAAAA\; sid:1003; classtype:attempted-admin;));// 插入模式到AC自动机ac_insert((unsigned char*)select, 6, 1001);ac_insert((unsigned char*)SELECT, 6, 1001);ac_insert((unsigned char*)script, 7, 1002);ac_insert((unsigned char*)SCRIPT, 7, 1002);ac_insert((unsigned char*)AAAAAAAAA, 9, 1003);ac_build();// 模拟包packet_t pkt;pkt.src_ip.s_addr inet_addr(192.168.1.100);pkt.dst_ip.s_addr inet_addr(8.8.8.8);pkt.protocol IPPROTO_TCP;pkt.src_port 54321;pkt.dst_port 80;// 测试正常流量char normal[] GET /index.html HTTP/1.1\r\nHost: example.com\r\n\r\n;pkt.payload (uint8_t*)normal;pkt.payload_len strlen(normal);snort_rule_t *rule ids_match_rule(ids, pkt);if (rule) {ids_alert(ids, pkt, rule, 1);} else {printf(正常请求: 未触发规则 ✅\n);}// 测试SQL注入char sql_attack[] GET /search?qselect%20*%20from%20users HTTP/1.1\r\nHost: example.com\r\n\r\n;pkt.payload (uint8_t*)sql_attack;pkt.payload_len strlen(sql_attack);rule ids_match_rule(ids, pkt);if (rule) {ids_alert(ids, pkt, rule, 1);}// 测试XSS攻击char xss_attack[] GET /comment?textscriptalert(1)/script HTTP/1.1\r\nHost: example.com\r\n\r\n;pkt.payload (uint8_t*)xss_attack;pkt.payload_len strlen(xss_attack);rule ids_match_rule(ids, pkt);if (rule) {ids_alert(ids, pkt, rule, 1);}// 测试缓冲区溢出char overflow[1024];memset(overflow, A, 500);sprintf(overflow 500, \r\nHost: example.com\r\n\r\n);pkt.payload (uint8_t*)overflow;pkt.payload_len strlen(overflow);rule ids_match_rule(ids, pkt);if (rule) {ids_alert(ids, pkt, rule, 1);}printf(\n引擎统计:\n);printf( 规则数: %d\n, ids-rule_count);printf( 告警数: %d\n, ids-alert_count);printf( AC节点数: %d\n, g_ac-node_count);free(ids);free(g_ac);}int main() {test_ids();return 0;}---三、编译和运行bashgcc -o ids ids.c -lpthread -lm./ids---四、Snort vs 本实现特性 本实现 Snort包捕获 模拟 Libpcap/PF_RING规则数量 少量 数万AC自动机 ✅ ✅HTTP解析 基础 完整协议栈 基础 完整性能 演示级 生产级(10Gbps)---五、总结通过这篇文章你学会了· IDS的核心架构包捕获→协议解析→规则匹配→告警· Snort规则语法· AC自动机多模式匹配· HTTP协议解析· 告警生成与日志入侵检测系统是网络安全的核心组件。掌握它你就理解了Snort、Suricata的底层设计。下一篇预告《从零实现一个安全沙箱文件行为分析系统》---评论区分享一下你对入侵检测的理解