ubuntu下端口关联检测

前端之家收集整理的这篇文章主要介绍了ubuntu下端口关联检测前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

端口检测目的意义:

在网络入侵中,端口扫描几乎是所有网络入侵过程中的必然前奏,入侵者首先通过端口扫描来发现目标主机的操作系统类型,提供的服务和系统所使用的软件版本及其他各种相关信息,然后采取针对性的攻击手段。

因此对端口扫描进行检测,发现可能出现的攻击行为,可有效的配合入侵检测。

实现背景:

端口关联检测中最麻烦的部分应该是根据端口找所关联的进程,在网上找了很长时间,相关资料很少很少,倒是有个博主在讨论服务器netstat -anp无法获取部分所对应的进程pid和程序名是因为socket文件的inode太大,超过INT_MAX,对netstat源码 的实现提到了端口关联进程。故根据 安卓源码和netstat 源码 简单实现了对端口关联的检测。

相关知识简介:

Ubuntu下网络连接文件简介

由于本实验检测的是在ipv4ipv6下的tcpudp本连接中端口的关联情况,所以用到以下4文件

1/proc/net/tcp文件,这里记录的是ipv4下所有tcp连接的情况。

2proc/net/tcp6文件,这里记录的是ipv6下所有tcp连接的情况。

3/proc/net/udp文件,这里记录的是ipv4下所有udp连接的情况。

4proc/net/udp6文件,这里记录的是ipv6下所有udp连接的情况。

4文件都用到以下数据:

local_address 0101007F:0035本地IP(网络字节序):本地端口(网络字节序)

rem_address 00000000:0000远端IP(网络字节序):远端端口(网络字节序)

st 0A套接字状态,不同套接字对应不同的值

enum {

TCP_ESTABLISHED = 1,

TCP_SYN_SENT,// 2

TCP_SYN_RECV,// 3

TCP_FIN_WAIT1,// 4

TCP_FIN_WAIT2,// 5

TCP_TIME_WAIT,// 6

TCP_CLOSE,// 7

TCP_CLOSE_WAIT,// 8

TCP_LAST_ACK,// 9

TCP_LISTEN,// 0A

TCP_CLOSING,// 1 /* Now a valid state */

TCP_MAX_STATES /* leave at the end! */

};

tx_queue:rx_queue 00000000:00000000发送队列中的数据长度:状态是ESTABLISHED,表示的时接受队列中的数据长度,状态是LISTEN,表示已完成队列的长度

tr tm->when 00:00000000定时器类型,0表示没有启动定时器。1表示重传定时器,4表示持续定时器,2表示连接定时器、FIN_WAIT_2定时器或TCP保活定时器,3表 示TIME_WAIT定时器 retrnsmt 00000000超时重传次数

uid 0用户id

timeout 0持续定时器或者保活定时器周期性发送出去但未被确认的TCP段数目,收到ACK后清零。

inode 11864 1 0000000000000000 100 0 0 10 0

11864套接字对应的inode

1 sock结构的引用数

0000000000000000 sock结构的实例地址

100 RTO,单位是clock_t

0用来计算延时确认的估值

0快速确认数和是否启用的标志位的或运算结果

10当前拥塞窗口大小

0 如果满启动阀值大于0x7ffffff显示-1,否则表示慢启动阀值

Socket文件inode分析

socket文件inode存在于LinuxVFS虚拟文件系统中。VFS是一个异构文件系统之上的软件粘合层,可以让open()read()write()等系统调用不用关心底层的存储介质和文件系统类型就可以工作。通过VFS,一个抽象的通用访问接口屏蔽了底层文件系统和物理介质的差异性,每一种类型的文件系统代码都隐藏了实现的细节。对于VFS层和内核的其它部分而言,每一种类型的文件系统看起来都是一样的。

VFS inode和磁盘文件系统中的inode不同。比如ext2文件系统,它的inode位于磁盘上划分的块组中,每个inode 128字节。在分割扇区时,系统会先做出一堆inode供以后使用,inode数量关系着系统中可以建立的文件及目录总数。磁盘上的每个文件都有且仅有一个inode,即使文件中没有数据,inode也存在。

VFS inode只存在于内存中,可以通过inode缓存访问。每个文件VFS中都有相应的inode结点,包括普通文件、目录以及特殊文件,如socketpipe等。在需要某个文件的时候系统会在内存中为其建立相应的inode数据结构,建立的inode结构将形成一个链表,可以通过遍历这个链表去得到我们需要的文件结点。但是,VFS对于不同文件inode号分配方式是不同的。

具体如下:

对于ext2等磁盘文件系统中的普通文件,每个文件在磁盘上都有对应的inode,该inode号也是已知的。在访问这些文件时,VFS会在内存中为这个文件分配VFS inode,将在磁盘文件系统中确定的inode号赋给inode结构。可见,一般普通文件inode号都不会太大。

对于socket等特殊文件来说,并没有像磁盘文件一样的inode号的来源,内核在实现中维护了一个unsigned long型的静态变量来保存目前分配的inode号的最大值,新分配的inode号在此基础上加1来实现。这个静态变量的值会一直增大而不会减小,直至机器重启。

具体实现步骤:

1.通过网络文件查找获取端口数据

查找/proc/net/tcp/proc/net/udp/proc/net/tcp6/proc/net/udp6,从文件数据中提取所需要的本地ip,本地port,远端ip,远端port套接字状态,发送队列中的数据长度,接收队列中的数据长度,以及套接字对应的inode

2.获取端口所关联的进程

  • 首先遍历所有的/proc/PID/fd目录,如果某个进程非本账号所有,则无权访问/proc/PID目录,跳过该PID,访问下一个。
  • 当某个/proc/PID/fd目录中有一个或多个socket句柄时,则读该/proc/PID目录下的cmdline文件获取该进程的所执行程序的名称;同时,记录/proc/PID/fd目录下所有socket句柄的inode号。
  • 当所有进程遍历完成后,根据在网络文件proc/net/{tcp,udp,tcp6,udp6}获取inode节点号,匹配你记录的所有socket句柄的inode后,若相等,则拥有该socket句柄的进程即为该端口关联的进程。
代码块分析

1.socket文件inode信息结构:

static struct prg_node {

struct prg_node *next;

int inode;

char name[PROGNAME_WIDTH];

}*prg_hash[PRG_HASH_SIZE];

name数组里存储进程pid以及进程名

2.ipv4 ipv6ip地址 网络字节序到主机字节序的转化。

static void addr2str(int,const void *,char * );

3./proc/net/tcp/proc/net/udp中连接数据的获取输出

static void ipv4(const char *,const char *);

4./proc/net/tcp6/proc/net/udp6中连接数据的获取输出

static void ipv6(const char *,const char *);

5.prg_node结构的信息存储于链表中。

static void prg_cache_add(int,char * );

6.遍历链表查看是否存在相等的inode节点,若有,将节点关联进程信息返回。

static const char *prg_cache_get(int );

7.清空链表。

static void prg_cache_clear( );

8.筛选类型为socket0000的句柄,并使用形-参返回对应的inode节点号。

static void extract_type_1_socket_inode(const char a[],long * );

static void extract_type_2_socket_inode(const char a[],long * );

9.实现遍历/proc/pid/fd和查看/proc/pid/cmdline获取信息。

static void prg_cache_load( );


代码如下:
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include <ctype.h>
#include <fcntl.h>
#include <netdb.h>
#include <paths.h>
#include <pwd.h>
#include <getopt.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <dirent.h>
#define PROGNAME_WIDTH 20


#define PROGNAME_WIDTHs PROGNAME_WIDTH1(PROGNAME_WIDTH)
#define PROGNAME_WIDTH1(s) PROGNAME_WIDTH2(s)
#define PROGNAME_WIDTH2(s) #s

#define PRG_HASH_SIZE 211

#define PRG_HASHIT(x) ((x) % PRG_HASH_SIZE)

#ifndef LINE_MAX
#define LINE_MAX 4096
#endif

#define PATH_PROC	   "/proc"
#define PATH_FD_SUFF	"fd"
#define PATH_FD_SUFFl       strlen(PATH_FD_SUFF)
#define PATH_PROC_X_FD      PATH_PROC "/%s/" PATH_FD_SUFF
#define PATH_CMDLINE	"cmdline"
#define PATH_CMDLINEl       strlen(PATH_CMDLINE)

#define PRG_LOCAL_ADDRESS "local_address"
#define PRG_INODE	 "inode"
#define PRG_SOCKET_PFX    "socket:["
#define PRG_SOCKET_PFXl (strlen(PRG_SOCKET_PFX))
#define PRG_SOCKET_PFX2   "[0000]:"
#define PRG_SOCKET_PFX2l  (strlen(PRG_SOCKET_PFX2))
static char prg_cache_loaded = 0;

typedef union iaddr iaddr;
typedef union iaddr6 iaddr6;

static struct prg_node {
    struct prg_node *next;
    int inode;
    char name[PROGNAME_WIDTH];
} *prg_hash[PRG_HASH_SIZE];


union iaddr {
    unsigned u;
    unsigned char b[4];
};

union iaddr6 {
    struct {
        unsigned a;//unsigned int a; 无符号整型
        unsigned b;
        unsigned c;
        unsigned d;
    } u;
    unsigned char b[16];
};

static const char *state2str(unsigned state)
{
    switch(state){
    case 0x1: return "ESTABLISHED";
    case 0x2: return "SYN_SENT";
    case 0x3: return "SYN_RECV";
    case 0x4: return "FIN_WAIT1";
    case 0x5: return "FIN_WAIT2";
    case 0x6: return "TIME_WAIT";
    case 0x7: return "CLOSE";
    case 0x8: return "CLOSE_WAIT";
    case 0x9: return "LAST_ACK";
    case 0xA: return "LISTEN";
    case 0xB: return "CLOSING";
    default: return "UNKNOWN";
    }
}

/* addr + : + port + \0 */
#define ADDR_LEN INET6_ADDRSTRLEN + 1 + 5 + 1

static void addr2str(int af,const void *addr,char *buf)
{
    if (inet_ntop(af,addr,buf,ADDR_LEN) == NULL) 
        *buf = '\0';
      return;
    
}
static void prg_cache_add(int inode,char *name)
{
    unsigned hi = PRG_HASHIT(inode);
    struct prg_node **pnp,*pn;

    prg_cache_loaded=2;
    for (pnp=prg_hash+hi;(pn=*pnp);pnp=&pn->next) {
	if (pn->inode==inode) {
	    /* Some warning should be appropriate here
	       as we got multiple processes for one i-node */
	    return;
	}
    }
    if (!(*pnp=malloc(sizeof(**pnp)))) 
	return;
    pn=*pnp;
    pn->next=NULL;
    pn->inode=inode;
    if (strlen(name)>sizeof(pn->name)-1) 
	name[sizeof(pn->name)-1]='\0';
    strcpy(pn->name,name);
}

static const char *prg_cache_get(int inode)
{
    unsigned hi=PRG_HASHIT(inode);
    struct prg_node *pn;

    for (pn=prg_hash[hi];pn;pn=pn->next)
	if (pn->inode==inode) return(pn->name);
    return("-");
}

static void prg_cache_clear(void)
{
    struct prg_node **pnp,*pn;

    if (prg_cache_loaded == 2)
	for (pnp=prg_hash;pnp<prg_hash+PRG_HASH_SIZE;pnp++)
	    while ((pn=*pnp)) {
		*pnp=pn->next;
		free(pn);
	    }
    prg_cache_loaded=0;
}


static void extract_type_1_socket_inode(const char lname[],long * inode_p) {

    /* If lname is of the form "socket:[12345]",extract the "12345"
       as *inode_p.  Otherwise,return -1 as *inode_p.
       */

    if (strlen(lname) < PRG_SOCKET_PFXl+3) *inode_p = -1;
    else if (memcmp(lname,PRG_SOCKET_PFX,PRG_SOCKET_PFXl)) *inode_p = -1;
    else if (lname[strlen(lname)-1] != ']') *inode_p = -1;
    else {
        char inode_str[strlen(lname + 1)];  /* e.g. "12345" */
        const int inode_str_len = strlen(lname) - PRG_SOCKET_PFXl - 1;
        char *serr;

        strncpy(inode_str,lname+PRG_SOCKET_PFXl,inode_str_len);
        inode_str[inode_str_len] = '\0';
        *inode_p = strtol(inode_str,&serr,0);
        if (!serr || *serr || *inode_p < 0 || *inode_p >= INT_MAX) 
            *inode_p = -1;
    }
}

static void extract_type_2_socket_inode(const char lname[],long * inode_p) {

    /* If lname is of the form "[0000]:12345",return -1 as *inode_p.
       */

    if (strlen(lname) < PRG_SOCKET_PFX2l+1) *inode_p = -1;
    else if (memcmp(lname,PRG_SOCKET_PFX2,PRG_SOCKET_PFX2l)) *inode_p = -1;
    else {
        char *serr;

        *inode_p=strtol(lname + PRG_SOCKET_PFX2l,0);
        if (!serr || *serr || *inode_p < 0 || *inode_p >= INT_MAX) 
            *inode_p = -1;
    }
}

static void prg_cache_load(void)
{
    char line[LINE_MAX],eacces=0;
    int procfdlen,fd,cmdllen,lnamelen;
    char lname[30],cmdlbuf[512],finbuf[PROGNAME_WIDTH];
    long inode;
    const char *cs,*cmdlp;
    DIR *dirproc=NULL,*dirfd=NULL;
    struct dirent *direproc,*direfd;

    if (prg_cache_loaded ) return;
    prg_cache_loaded=1;
    cmdlbuf[sizeof(cmdlbuf)-1]='\0';
    if (!(dirproc=opendir(PATH_PROC))) goto fail;
    while (errno=0,direproc=readdir(dirproc)) {
#ifdef DIRENT_HAVE_D_TYPE_WORKS
	if (direproc->d_type!=DT_DIR) continue;
#endif
	for (cs=direproc->d_name;*cs;cs++)
	    if (!isdigit(*cs)) 
		break;
	if (*cs) 
	    continue;
	procfdlen=snprintf(line,sizeof(line),PATH_PROC_X_FD,direproc->d_name);
	if (procfdlen<=0 || procfdlen>=sizeof(line)-5) 
	    continue;
	errno=0;
	dirfd=opendir(line);
	if (! dirfd) {
	    if (errno==EACCES) 
		eacces=1;
	    continue;
	}
	line[procfdlen] = '/';
	cmdlp = NULL;
	while ((direfd = readdir(dirfd))) {
#ifdef DIRENT_HAVE_D_TYPE_WORKS
	    if (direfd->d_type!=DT_LNK) 
		continue;
#endif
	    if (procfdlen+1+strlen(direfd->d_name)+1>sizeof(line)) 
		continue;
	    memcpy(line + procfdlen - PATH_FD_SUFFl,PATH_FD_SUFF "/",PATH_FD_SUFFl+1);
	    strcpy(line + procfdlen + 1,direfd->d_name);
	    lnamelen=readlink(line,lname,sizeof(lname)-1);
            lname[lnamelen] = '\0';  /*make it a null-terminated string*/

            extract_type_1_socket_inode(lname,&inode);

            if (inode < 0) extract_type_2_socket_inode(lname,&inode);

            if (inode < 0) continue;

	    if (!cmdlp) {
		if (procfdlen - PATH_FD_SUFFl + PATH_CMDLINEl >= 
		    sizeof(line) - 5) 
		    continue;
		strcpy(line + procfdlen-PATH_FD_SUFFl,PATH_CMDLINE);
		fd = open(line,O_RDONLY);
		if (fd < 0) 
		    continue;
		cmdllen = read(fd,cmdlbuf,sizeof(cmdlbuf) - 1);
		if (close(fd)) 
		    continue;
		if (cmdllen == -1) 
		    continue;
		if (cmdllen < sizeof(cmdlbuf) - 1) 
		    cmdlbuf[cmdllen]='\0';
		if ((cmdlp = strrchr(cmdlbuf,'/'))) 
		    cmdlp++;
		else 
		    cmdlp = cmdlbuf;
	    }

	    snprintf(finbuf,sizeof(finbuf),"%s/%s",direproc->d_name,cmdlp);
	    prg_cache_add(inode,finbuf);
	}
	closedir(dirfd); 
	dirfd = NULL;
    }
    if (dirproc) 
	closedir(dirproc);
    if (dirfd) 
	closedir(dirfd);
    if (!eacces) 
	return;
    if (prg_cache_loaded == 1) {
    fail:
	fprintf(stderr,("(No info could be read for \"-p\": geteuid()=%d but you should be root.)\n"),geteuid());
    }
    else
	fprintf(stderr,("(Not all processes could be identified,non-owned process info\n"
			 " will not be shown,you would have to be root to see it all.)\n"));
}


static void ipv4(const char *filename,const char *label) {
    FILE *fp = fopen(filename,"r");
    if (fp == NULL) {
        return;
    }
    char buf[BUFSIZ];
    fgets(buf,BUFSIZ,fp);
    while (fgets(buf,fp)){
        char lip[ADDR_LEN];
        char rip[ADDR_LEN];
        iaddr laddr,raddr;
        unsigned lport,rport,state,txq,rxq,num;
		unsigned tr,tmWhen,ret;
		unsigned uid,timeout,inode;
        int n = sscanf(buf," %d: %x:%x %x:%x %x %x:%x %x:%x %x %x %x %d",&num,&laddr.u,&lport,&raddr.u,&rport,&state,&txq,&rxq,&tr,&tmWhen,&ret,&uid,&timeout,&inode);
        if (n == 14) {
            addr2str(AF_INET,&laddr,lip);
            addr2str(AF_INET,&raddr,rip);

			printf("%10d   %-15s   %5d   %-15s   %6d  %6d  %s   %12s     %5d   %15s\n",lport,lip,rip,label,state2str(state),inode,prg_cache_get(inode));
        }
    }
    fclose(fp);
}

static void ipv6(const char *filename,fp)){
        char lip[ADDR_LEN];
        char rip[ADDR_LEN];
        iaddr6 laddr6,raddr6;
        unsigned lport,num;
     	unsigned tr," %d: %8x%8x%8x%8x:%x %8x%8x%8x%8x:%x %x %x:%x %x:%x %x %x %x %d",&laddr6.u.a,&laddr6.u.b,&laddr6.u.c,&laddr6.u.d,&raddr6.u.a,&raddr6.u.b,&raddr6.u.c,&raddr6.u.d,&inode);
        if (n == 20) {
            addr2str(AF_INET6,&laddr6,lip);	
            addr2str(AF_INET6,&raddr6,rip);
     
			printf("%10d   %-15s   %5d   %-15s   %6d  %6d  %s  %12s     %5d   %15s\n",prg_cache_get(inode));
        }
    }
    fclose(fp);
}


int main(int argc,char *argv[])
{
    prg_cache_load();
	printf("Local Port   Local Address     Fport   Foreign Address   Recv-Q  Send-Q  proto        state     inode   Pid/Program name\n");
    ipv4("/proc/net/tcp","tcp");
    ipv4("/proc/net/udp","udp");
    ipv6("/proc/net/tcp6","tcp6");
    ipv6("/proc/net/udp6","udp6");
	prg_cache_clear();
    return 0;
}


运行结果:

猜你在找的Ubuntu相关文章