https://mp.weixin.qq.com/s/TSlIqCP53nxhi5Vq4TFRnw

企业安全建设—网络镜像流量分析的一些方法与思路
原创 2017-12-18 elknot 先知安全技术社区br/>Author:elknot@360corpsec
0x00 概述
这篇稿子其实被约了很长时间了,因为一直没时间去写,最近由于年底了,需要整理一些东西出来,所以现在就先写到这里了。
由于本人本科学的网络工程,所以对网络协议这一块的了解还算略懂,网络协议分析其实在网络安全领域里面算是一个比较古老的技术了,原理其实就是我们大学计算机网络基础课里面学的。但是实际知道企业里面,尤其是网络设施复杂,数据量较大的IDC网络,协议分析是比较痛苦的,一方面是性能问题,另一方面是储存和可视化问题。
首先先来点科普,网络镜像流量分析需要完成的几项工作:

端口镜像

数据包捕获和抓取

数据包分析

会话还原和重组

应用层协议分析

可视化展示

其实这个实验环境是可以在家模拟的,搞一个Mikrotik RouterBoard然后做端口镜像倒到数据包采集板里面,之后去分析就OK了,这里就需要简单的来说一下网络方面的知识了。(基础知识扎实的可以跳过)
由于我们现在的网络协议栈走的都是TCP/IP协议栈,按照TCP/IP协议栈的规范,TCP/IP协议栈的模型是四层模型(主机接口层、网络层、传输层和应用层,有些地方因为教材版本的差异主机接口层会被描述为数据链路层、传输层会被描述为运输层,但是实际不影响其功能的描述)。

主机接口层层是物理传输通道,可使用多种传输介质传输,可建立在任何物理传输网上。比如光纤、双绞线等

网络层:其主要功能是要完成网络中主机间“分组”(Packet)的传输。

传输层:其主要任务是向上一层提供可靠的端到端(End-to-End)服务,确保“报文”无差错、有序、不丢失、无重复地传输。它向高层屏蔽了下层数据通信的细节,是计算机通信体系结构中最关键的一层。

应用层:应用层确定进程间通信的性质,以满足用户的需要。

他们的数据存放方式依次为:(比特流、帧)、分组、会话、应用。

科普时间结束,回归正题,企业内网由于众所周知的原因吧,所以本文上所有的代码全部基于实验代码(包捕获方法采用libpcap,包解析方法采用libnids),与实际生产环境(包捕获方法推荐DNA模式的PF_RING,包分析的方法建议采用nDPI)要区分开,针对IDC的流量其实我们可以去设计这么一个架构来解决问题:

这个图其实大致的流程就是:用户在访问互联网业务的时候,流量经过入口路由,然后将其做镜像,使其可以直接访问业务系统,同时还能被我们分析。首先分析的时候由于数据包还是比特流的状态,所以我们需要将比特通过数据包采集器来还原成能用的东西,之后将数据移交至数据包分析服务器,对其进行深度分析,将分析结果存放至数据库,然后就是检索、规则匹配、可视化、etc一大堆常规的操作,如果有接口的话,可以尝试在DashBoard区域放置和IDC集群相关ACL的操作,直接将恶意的IP拉黑。

0x01 数据包的捕获
这里抓包我们的实验代码使用libpcap来编写,但是生产环境中应使用PF_RING,如果有更高的需求请移步Intel DPDK或者是腾讯的F-Stack。
libpcap是一个网络数据包捕获函数库,功能非常强大,Linux下著名的tcpdump就是以它为基础的。
其实libpcap使用的方法还是比较简单的,上一个简单的samplecode。

#include
#include
int main()
{
char errBuf[PCAP_ERRBUF_SIZE], * device;
device = pcap_lookupdev(errBuf);
if(device)
{
printf("success: device: %s\n", device);
}
else
{
printf("error: %s\n", errBuf);
}
return 0;
}
使用gcc编译,需要添加编译选项:

gcc -g -W all -o test test.c -lpcap
上面这段代码的就是libpcap打开网络设备的方式,实际上我们在使用libpcap的时候要经过4个步骤:打开网络设备、设置过滤规则、捕获数据、关闭设备。
libpcap里面有几个比较重要的API:

pcap_lookupdev():函数用于查找网络设备,返回可被 pcap_open_live() 函数调用的网络设备名指针。
pcap_lookupnet():函数获得指定网络设备的网络号和掩码。
pcap_open_live(): 函数用于打开网络设备,并且返回用于捕获网络数据包的数据包捕获描述字。对于此网络设备的操作都要基于此网络设备描述字。
pcap_compile(): 函数用于将用户制定的过滤策略编译到过滤程序中。
pcap_setfilter():函数用于设置过滤器。
pcap_loop():函数 pcap_dispatch() 函数用于捕获数据包,捕获后还可以进行处理,此外 pcap_next() 和 pcap_next_ex() 两个函数也可以用来捕获数据包。
pcap_close():函数用于关闭网络设备,释放资源。
我们用一段代码来熟悉一下这些API:

#include
#include
#include
#include

int main()
{
char errBuf[PCAP_ERRBUF_SIZE], * devStr;

/* get a device */
devStr = pcap_lookupdev(errBuf);

if(devStr)
{
    printf("success: device: %s\n", devStr);
}
else
{
    printf("error: %s\n", errBuf);
    exit(1);
}
/* open a device, wait until a packet arrives */
pcap_t * device = pcap_open_live(devStr, 65535, 1, 0, errBuf);
 if(!device)
 {
    printf("error: pcap_open_live(): %s\n", errBuf);
    exit(1);
}
/* wait a packet to arrive */
struct pcap_pkthdr packet;
const u_char * pktStr = pcap_next(device, &packet);

if(!pktStr)
{
    printf("did not capture a packet!\n");
    exit(1);
}
printf("Packet length: %d\n", packet.len);
printf("Number of bytes: %d\n", packet.caplen);
printf("Recieved time: %s\n", ctime((const time_t *)&packet.ts.tv_sec)); 
pcap_close(device);
return 0;

}
这个其实就是一个抓包的简单实现,实际上我们会一直抓包,而不是抓一个,同时要把数据包里面的内容进行输出,所以我们这里就需要循环捕获数据包,这时候就需要编写以下的代码来完成操作。

#include
#include
#include
#include
#include

#define BUFSIZE 1514

struct ether_header
{
unsigned char ether_dhost[6]; //目的mac
unsigned char ether_shost[6]; //源mac
unsigned short ether_type; //以太网类型
};

/***回调函数****/
void ethernet_protocol_callback(unsigned char argument,const struct pcap_pkthdr packet_heaher,const unsigned char packet_content)
{
unsigned char
mac_string; //
struct ether_header ethernet_protocol;
unsigned short ethernet_type; //以太网类型
printf("----------------------------------------------------\n");
printf("%s\n", ctime((time_t
)&(packet_heaher->ts.tv_sec))); //转换时间
ethernet_protocol = (struct ether_header *)packet_content;

mac_string = (unsigned char *)ethernet_protocol->ether_shost;//获取源mac地址
printf("Mac Source Address is %02x:%02x:%02x:%02x:%02x:%02x\n",*(mac_string+0),*(mac_string+1),*(mac_string+2),*(mac_string+3),*(mac_string+4),*(mac_string+5));
mac_string = (unsigned char *)ethernet_protocol->ether_dhost;//获取目的mac
printf("Mac Destination Address is %02x:%02x:%02x:%02x:%02x:%02x\n",*(mac_string+0),*(mac_string+1),*(mac_string+2),*(mac_string+3),*(mac_string+4),*(mac_string+5));

ethernet_type = ntohs(ethernet_protocol->ether_type);//获得以太网的类型
printf("Ethernet type is :%04x\n",ethernet_type);
switch(ethernet_type)
{
    case 0x0800:printf("The network layer is IP protocol\n");break;//ip
    case 0x0806:printf("The network layer is ARP protocol\n");break;//arp
    case 0x0835:printf("The network layer is RARP protocol\n");break;//rarp
    default:break;
}
usleep(800*1000);

}

int main(int argc, char argv[])
{
char error_content[100]; //出错信息
pcap_t
pcap_handle;
unsigned char mac_string;
unsigned short ethernet_type; //以太网类型
char
net_interface = NULL; //接口名字
struct pcap_pkthdr protocol_header;
struct ether_header *ethernet_protocol;

//获取网络接口
net_interface = pcap_lookupdev(error_content);
if(NULL == net_interface)
{
    perror("pcap_lookupdev");
    exit(-1);
}

pcap_handle = pcap_open_live(net_interface,BUFSIZE,1,0,error_content);//打开网络接口

if(pcap_loop(pcap_handle,-1,ethernet_protocol_callback,NULL) < 0)
{
    perror("pcap_loop");
}

pcap_close(pcap_handle);
return 0;

}
其实libpcap本身还支持BPF过滤器语法,由于我们这里不是一个讲编程和网络的地方,所以我们这里就不在详细阐述了,上面的代码来自于http://blog.csdn.net/tennysonsky/article/details/44811899 ,大家可以去这里看详细的libpcap的使用。

0x02 数据包分析
刚刚只进行了数据包捕获的模块,但是实际上还会进行分析详细的数据包和流量,所以这时候我们需要用到另外一个库,另一个库叫做libnids,也是一个比较常见的库。 libnids的英文意思是 Network Intrusion Detect System library,即网络***监测系统函数库。它是在前面介绍的两种C函数接口库libnet和libpcap的基础上开发的,封装了开发NIDS所需的许 多通用型函数。linids提供的接口函数监视流经本地的所有网络通信,检查数据包等。除此之外,还具有重组TCP数据段、处理IP分片包和监测TCP端 口扫描的功能。利用libnids接口函数库,NIDS开发者不需要再编写底层的网络处理代码,只需专注于NIDS本身功能的实现即可。
在使用libnids对数据包进行重组和会话还原的时候,IP分片重组和TCP会话重组是必须得知道的。首先先来说IP分片,在libnids中,我们需要定义void ip_frag_func(struct ip * a_packet)作为回调函数,在调用nids_init()函数初始化后,使用nids的函数进行注册nids_register_ip_frag(ip_frag_func),这样回调函数ip_frag_func会在适当的时候由libnids调用,参数a_packet指针将指向接收到的数据报。
我们通过一段代码来说明一下libnids如何使用:

#include
#include
#include
#include
#include
#include
#include
#include "nids.h"

#define int_ntoa(x) inet_ntoa(((struct in_addr )&x))

// struct tuple4 包含TCP连接的地址和端口号
// 下面的辅助函数生成一个类似10.0.0.1,1024,10.0.0.2,23的字符串

char *adres (struct tuple4 addr)
{
static char buf[256];
strcpy (buf, int_ntoa (addr.saddr));
sprintf (buf + strlen (buf), ",%i,", addr.source);
strcat (buf, int_ntoa (addr.daddr));
sprintf (buf + strlen (buf), ",%i", addr.dest);
return buf;
}

void tcp_callback (struct tcp_stream *a_tcp, void ** this_time_not_needed)
{
char buf[1024];
strcpy (buf, adres (a_tcp->addr)); // we put conn params into buf
if (a_tcp->nids_state == NIDS_JUST_EST)
{
// 由a_tcp描述的连接已经建立
// 这里我们决定是否希望跟踪这个流
// 例子条件: if (a_tcp->addr.dest!=23) return;
// 在本程序中我们跟踪所有的流所以。。。。。:
a_tcp->client.collect++; // 我们需要客户端接收到的数据.......
a_tcp->server.collect++; // 我们需要服务器接收到的数据.......
a_tcp->server.collect_urg++; // 我们需要服务器接收到的紧急数据.......

#ifdef WE_WANT_URGENT_DATA_RECEIVED_BY_A_CLIENT

    a_tcp->client.collect_urg++; // 如果我们不增加这个值,当紧急数据到达
    // 时我们不会被通知。

#endif
fprintf (stderr, "%s establishedn", buf);
return;
}
if (a_tcp->nids_state == NIDS_CLOSE)
{
// 连接已经正常结束
fprintf (stderr, "%s closingn", buf);
return;
}
if (a_tcp->nids_state == NIDS_RESET)
{
// 连接已经通过RST关闭。
fprintf (stderr, "%s resetn", buf);
return;
}

if (a_tcp->nids_state == NIDS_DATA)
{
    // 新的数据已经到达,必须判断其数据流向
    // 判断其是否紧急数据
    struct half_stream *hlf;

    if (a_tcp->server.count_new_urg)
    {
        // 紧急数据的新字节已经到达
        strcat(buf,"(urgent->)");
        buf[strlen(buf)+1]=0;
        buf[strlen(buf)]=a_tcp->server.urgdata;
        write(1,buf,strlen(buf));
        return;
    }

    // 我们不必必须检查是否客户端的紧急数据已经到达
    // 因为我们没有增加a_tcp->client.collect_urg的值
    // 因此,我们还有一些正常的数据关心
    if (a_tcp->client.count_new)
    {
        //客户端的新数据
        hlf = &a_tcp->client; // 现在我们将处理hlf变量
        // 这个变量指向客户端一边的连接。
        strcat (buf, "(<-)"); // 数据的符号方向
    }
    else
    {
        hlf = &a_tcp->server; // 类似的
        strcat (buf, "(->)");
    }
    fprintf(stderr,"%s",buf); // 我们打印连接参数(saddr, daddr, sport, dport)
    // 和数据流向(-> or <-)

    write(2,hlf->data,hlf->count_new); // 我们打印最新到达的数据
}
return ;

}

int main ()
{
// 这里我们可以改变Libnids的params,例如 nids_params.n_hosts=256;
if (!nids_init ())
{
fprintf(stderr,"%sn",nids_errbuf);
exit(1);
}
nids_register_tcp (tcp_callback);
nids_run ();
return 0;
}
0x02 应用层协议分析
这里我们就完成了一个简单的TCP会话重组,但是实际上我们需要分析应用层中的协议,所以我这里放两个samplecode用来分析smtp协议和pop3协议。
samplecode1:

#include
#include
#include"nids.h"
#include"string.h"
#include
#include
char ascii_string[10000];
char char_to_ascii(char ch)
{
char
string;
ascii_string[0]=0;
string = ascii_string;
if(isgraph(ch))
string++=ch;
else if(ch==' ')
string++=ch;
else if(ch=='\n'||ch=='\r')
string++=ch;
else
string++='.';
*string=0;
return ascii_string;
}

//+++++++++++++++++++++++++++++++++++++++++++++++++
// 下面分析pop3协议的回调函数
//+++++++++++++++++++++++++++++++++++++++++++++++++
void pop3_protocol_callback(struct tcp_stream *pop3_connection,void *arg)
{
int i;
char address_string[1024];
char content[65535];
struct tuple4 ip_and_port;
ip_and_port=pop3_connection->addr;
strcpy(address_string,inet_ntoa(
((struct in_addr)&(ip_and_port.saddr))));
sprintf(address_string+strlen(address_string),":%i",ip_and_port.source);
strcat(address_string,"<------------>");
strcat(address_string,inet_ntoa(
((struct in_addr)&(ip_and_port.daddr))));
sprintf(address_string+strlen(address_string),":%i",ip_and_port.dest);
strcat(address_string,"\n");
switch (pop3_connection->nids_state)
{
case NIDS_JUST_EST:/
POP3客户端和服务器建立连接/
if(pop3_connection->addr.dest==110)
{
pop3_connection->client.collect++;/
pop3客户端接收数据/
pop3_connection->server.collect++;/
pop3服务器接收数据/
pop3_connection->client.collect_urg++;/
pop3客户端接收紧急数据/
pop3_connection->server.collect_urg++;/
pop3服务器接收紧急数据/
printf("%spop3客户端和服务器建立连接\n",address_string);
}
case NIDS_CLOSE:/
POP3协议连接正常关闭/
printf("---------------------------------------------------\n");
printf("%sPOP3协议连接正常关闭\n",address_string);
return;
case NIDS_RESET:/
POP3协议连接被RESET关闭/
printf("---------------------------------------------------\n");
printf("%sPOP3协议连接被RESET关闭\n",address_string);
return;
case NIDS_DATA:/
POP3协议有新的数据到达/
{
char status_code[5];
struct half_stream
hlf;
if(pop3_connection->server.count_new_urg)/POP3服务器收到新的紧急数据/
{
printf("---------------------------------------------------\n");
strcpy(address_string,inet_ntoa(((struct in_addr)&(ip_and_port.saddr))));
sprintf(address_string+strlen(address_string),":%i",ip_and_port.source);
strcat(address_string,"urgent---->");
strcat(address_string,inet_ntoa(((struct in_addr)&(ip_and_port.daddr))));
sprintf(address_string+strlen(address_string),":%i",ip_and_port.dest);
strcat(address_string,'\n');
address_string[strlen(address_string)+1]=0;
address_string[strlen(address_string)];
pop3_connection->server.urgdata;
printf("%s",address_string);
return;
}
if(pop3_connection->client.count_new_urg)/POP3客户端收到新的紧急数据/
{
printf("---------------------------------------------------\n");
strcpy(address_string,inet_ntoa(((struct in_addr)&(ip_and_port.saddr))));
sprintf(address_string+strlen(address_string),":%i",ip_and_port.source);
strcat(address_string,"<------urgent");
strcat(address_string,inet_ntoa(((struct in_addr)&(ip_and_port.daddr))));
sprintf(address_string+strlen(address_string),":%i",ip_and_port.dest);
strcat(address_string,'\n');
address_string[strlen(address_string)+1]=0;
address_string[strlen(address_string)];
pop3_connection->server.urgdata;
printf("%s",address_string);
return;
}
if(pop3_connection->client.count_new)/POP3客户端收到新的数据/
{
hlf=&pop3_connection->client;
strcpy(address_string,inet_ntoa(((struct in_addr)&(ip_and_port.saddr))));
sprintf(address_string+strlen(address_string),":%i",ip_and_port.source);
strcat(address_string,"<------count_new");
strcat(address_string,inet_ntoa(((struct in_addr)&(ip_and_port.daddr))));
sprintf(address_string+strlen(address_string),":%i",ip_and_port.dest);
strcat(address_string,'\n');
printf("---------------------------------------------------\n");
printf("%s\n",address_string);
memcpy(content,hlf->data,hlf->count_new);
content[hlf->count_new]='\0';
if(strstr(strncpy(status_code,content,4),"+OK"))
printf("操作成功\n");
if(strstr(strncpy(status_code,content,4),"-ERR"))
printf("操作失败\n");
for(i=0;icount_new;i++)
{
printf("%s",char_to_ascii(content[i]));
}
printf("\n");
if(strstr(content,"\n\r.\n\r"))
printf("数据传输结束\n");
}
else/POP3服务器收到新的数据/
{
hlf=&pop3_connection->server;
strcpy(address_string,inet_ntoa(((struct in_addr)&(ip_and_port.saddr))));
sprintf(address_string+strlen(address_string),":%i",ip_and_port.source);
strcat(address_string,"count_new------>");
strcat(address_string,inet_ntoa(((struct in_addr)&(ip_and_port.daddr))));
sprintf(address_string+strlen(address_string),":%i",ip_and_port.dest);
strcat(address_string,'\n');
printf("---------------------------------------------------\n");
printf("%s\n",address_string);
memcpy(content,hlf->data,hlf->count_new);
content[hlf->count_new]='\0';
if(strstr(content,"USER"))
printf("邮件用户名为:\n");
if(strstr(content,"PASS"))
printf("用户密码为:\n");
if(strstr(content,"STAT"))
printf("返回统计资料:\n");
if(strstr(content,"RETR"))
printf("获取邮件:\n");
if(strstr(content,"DELE"))
printf("删除邮件:\n");
if(strstr(content,"QUIT"))
printf("退出连接:\n");
for(i=0;icount_new;i++)
{
printf("%s",char_to_ascii(content[i]));
}
printf("\n");
}
}

default:
    break;
}
return;

}
int main(void)
{
if(nids_init())/Libnids初始化/
{
printf("%s\n",nids_errbuf);
return -1;
}
nids_register_tcp(pop3_protocol_callback);/注册分析Telnet协议的回调函数/
nids_run();/进入循环捕获数据包的状态/
}
samplecode2:

#include
#include
#include"nids.h"
#include"string.h"
#include
#include
char ascii_string[10000];
char char_to_ascii(char ch)
{
char
string;
ascii_string[0] = 0;
string = ascii_string;
if (isgraph(ch))
string++ = ch;
else if (ch == ' ')
string++ = ch;
else if (ch == '\n' || ch == '\r')
string++ = ch;
else
string++ = '.';
string = 0;
return ascii_string;
}
/

下面是分析SMTP协议的回调函数

/
void smtp_protocol_callback(struct tcp_stream
smtp_connection, void *arg)
{
int i;
char address_string[1024];
char content[65535];
char content_urgent[65535];
struct tuple4 ip_and_port = smtp_connection->addr;
strcpy(address_string, inet_ntoa(
((struct in_addr) &(ip_and_port.saddr))));
sprintf(address_string + strlen(address_string), " : %i", ip_and_port.source);
strcat(address_string, " <---> ");
strcat(address_string, inet_ntoa(
((struct in_addr) &(ip_and_port.daddr))));
sprintf(address_string + strlen(address_string), " : %i", ip_and_port.dest);
strcat(address_string, "\n");
switch (smtp_connection->nids_state)
{
case NIDS_JUST_EST:
if (smtp_connection->addr.dest == 25)
{
/
SMTP客户端和SMTP服务器端建立连接 /
smtp_connection->client.collect++;
/
SMTP客户端接收数据 /
smtp_connection->server.collect++;
/
SMTP服务器接收数据 /
smtp_connection->server.collect_urg++;
/
SMTP服务器接收紧急数据 /
smtp_connection->client.collect_urg++;
/
SMTP客户端接收紧急数据 /
printf("%sSMTP发送方与SMTP接收方建立连接\n", address_string);
}
return ;
case NIDS_CLOSE:
/
SMTP客户端与SMTP服务器连接正常关闭 /
printf("--------------------------------\n");
printf("%sSMTP发送方与SMTP接收方连接正常关闭\n", address_string);
return ;
case NIDS_RESET:
/
SMTP客户端与SMTP服务器连接被RST关闭 /
printf("--------------------------------\n");
printf("%sSMTP发送方与SMTP接收方连接被REST关闭\n", address_string);
return ;
case NIDS_DATA:
{
/
SMTP协议接收到新的数据 /
char status_code[4];
struct half_stream
hlf;
if (smtp_connection->server.count_new_urg)
{
/ SMTP服务器接收到新的紧急数据 /
printf("--------------------------------\n");
strcpy(address_string, inet_ntoa(((struct in_addr) &(ip_and_port.saddr))));
sprintf(address_string + strlen(address_string), " : %i", ip_and_port.source);
strcat(address_string, " urgent---> ");
strcat(address_string, inet_ntoa(((struct in_addr) &(ip_and_port.daddr))));
sprintf(address_string + strlen(address_string), " : %i", ip_and_port.dest);
strcat(address_string, "\n");
address_string[strlen(address_string) + 1] = 0;
address_string[strlen(address_string)] = smtp_connection->server.urgdata;
printf("%s", address_string);
return ;
}
if (smtp_connection->client.count_new_urg)
{
/ SMTP客户端接收到新的紧急数据 /
printf("--------------------------------\n");
strcpy(address_string, inet_ntoa(((struct in_addr) &(ip_and_port.saddr))));
sprintf(address_string + strlen(address_string), " : %i", ip_and_port.source);
strcat(address_string, " <--- urgent ");
strcat(address_string, inet_ntoa(((struct in_addr) &(ip_and_port.daddr))));
sprintf(address_string + strlen(address_string), " : %i", ip_and_port.dest);
strcat(address_string, "\n");
address_string[strlen(address_string) + 1] = 0;
address_string[strlen(address_string)] = smtp_connection->client.urgdata;
printf("%s", address_string);
return ;
}
if (smtp_connection->client.count_new)
{
/ SMTP客户端接收到新的数据 /
hlf = &smtp_connection->client;
strcpy(address_string, inet_ntoa(((struct in_addr) &(ip_and_port.saddr))));
sprintf(address_string + strlen(address_string), ":%i", ip_and_port.source);
strcat(address_string, " <--- ");
strcat(address_string, inet_ntoa(((struct in_addr) &(ip_and_port.daddr))));
sprintf(address_string + strlen(address_string), ":%i", ip_and_port.dest);
strcat(address_string, "\n");
printf("--------------------------------\n");
printf("%s", address_string);
memcpy(content, hlf->data, hlf->count_new);
content[hlf->count_new] = '\0';
if (strstr(strncpy(status_code, content, 3), "221"))
printf("连接中止\n");
if (strstr(strncpy(status_code, content, 3), "250"))
printf("操作成功\n");
if (strstr(strncpy(status_code, content, 3), "220"))
printf("表示服务就绪\n");
if (strstr(strncpy(status_code, content, 3), "354"))
printf("开始邮件输入,以\".\"结束\n");
if (strstr(strncpy(status_code, content, 3), "334"))
printf("服务器响应验证\n");
if (strstr(strncpy(status_code, content, 3), "235"))
printf("认证成功可以发送邮件了\n");
for (i = 0; i < hlf->count_new; i++)
{
printf("%s", char_to_ascii(content[i]));
}
printf("\n");
}
else
{
/ SMTP服务器接收到新的数据 /
hlf = &smtp_connection->server;
strcpy(address_string, inet_ntoa(((struct in_addr) &(ip_and_port.saddr))));
sprintf(address_string + strlen(address_string), ":%i", ip_and_port.source);
strcat(address_string, " ---> ");
strcat(address_string, inet_ntoa(((struct in_addr) &(ip_and_port.daddr))));
sprintf(address_string + strlen(address_string), ":%i", ip_and_port.dest);
strcat(address_string, "\n");
printf("--------------------------------\n");
printf("%s", address_string);
memcpy(content, hlf->data, hlf->count_new);
content[hlf->count_new] = '\0';
if (strstr(content, "EHLO"))
printf("HELLO命令\n");
if (strstr(content, "QUIT"))
printf("退出连接\n");
if (strstr(content, "DATA"))
printf("开始传输数据\n");
if (strstr(content, "MAIL FROM"))
printf("发送方邮件地址为\n");
if (strstr(content, "RCPT TO"))
printf("接收方邮件地址为\n");
if (strstr(content, "AUTH"))
printf("请求认证\n");
if (strstr(content, "LOGIN"))
printf("认证机制为LOGIN\n");
for (i = 0; i < hlf->count_new; i++)
{
printf("%s", char_to_ascii(content[i]));
}
printf("\n");
if (strstr(content, "\n."))
printf("数据传输结束\n");
}
}
default:
break;
}
return ;
}
/*

主函数

/
void main()
{
if (!nids_init())
/
Libnids初始化 /
{
printf("%s\n", nids_errbuf);
exit(1);
}
nids_register_tcp(smtp_protocol_callback);
/
注册分析TCP协议的回调函数 /
nids_run();
/
进入循环捕获数据包状态 */
}
到这里的话,基本上我们对协议分析就有了个了解。

0x03 数据DPI分析
DPI(Deep Packet Inspection)是一种基于数据包的深度检测技术,针对不同的网络应用层载荷(例如HTTP、DNS等)进行深度检测,通过对报文的有效载荷检测决定其合法性。
其实安全方面使用DPI主要还是为了做流量方面的分析与取证,从而发现***行为和异常行为。
通常情况下,我们可以再出口流量抓一些流量样本过来使用wireshark分析就可以确定***与否,但是这个的话人力成本较大,如果我们有了DPI,就可以迅速的获得数据包内的信息,这样的话可以减少发现***的时间。
因为这种所有周知的原因,规则什么的不太好说,可以私下交流。

0x04 总结
这可能是今年这个系列的最后一篇文章了,下一篇文章打算写一个基于中间件模板的蜜罐平台的设计,估计看时间得到明年了,到时候再说吧