持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第17天,点击查看活动详情
源码分析通讯协议
从上一个章节可以看到,发出的消息由两部分组成,第一个部分是一个结构体,标识这个消息的一些基本信息,第二个部分是data数组
struct audit_dispatcher_header
- 先看一下结构体的定义
struct audit_dispatcher_header { uint32_t ver; /* The version of this protocol */ uint32_t hlen; /* Header length */ uint32_t type; /* Message type */ uint32_t size; /* Size of data following the header */ }; - 可以看到,头的消息有四段,分别是
- ver 协议版本号
- hlen 结构体长度,固定为
sizeof(struct audit_dispatcher_header) - type 消息类型
- size 消息长度,这段在下面的data区很有用
ver 协议版本号
- 有两种,测试的时候只遇到了ver2,这些消息是由auditd分发来的消息,具体看现在是否两种协议都使用需要再看auditd的源码了
/usr/include/libaudit.h // Original protocol starts with msg=' #define AUDISP_PROTOCOL_VER 0 // Starts with node and/or type already in the text before msg= // IOW, its preformatted in the audit daemon. #define AUDISP_PROTOCOL_VER2 1 - 两者的区别主要在计算data区域的长度问题上,但这个长度和二进制没有关系,只在使用字符串传递消息时计算的字符串长度,和我们使用的二进制传递消息没有关系
/home/kira/rpmbuild/BUILD/audit-3.0.1/audisp/audispd.c // Protocol 1 is not formatted if (e->hdr.ver == AUDISP_PROTOCOL_VER) { const char *type; /* Get the event formatted */ type = audit_msg_type_to_name(e->hdr.type); if (type == NULL) { snprintf(unknown, sizeof(unknown), "UNKNOWN[%u]", e->hdr.type); type = unknown; } len = asprintf(&v, "type=%s msg=%.*s\n", type, e->hdr.size, e->data); // Protocol 2 events are already formatted } else if (e->hdr.ver == AUDISP_PROTOCOL_VER2) { len = asprintf(&v, "%.*s\n", e->hdr.size, e->data); } else len = 0;
type 消息类型
- 这里的type是由auditd传递给audispd的
/home/kira/rpmbuild/BUILD/audit-3.0.1/src/auditd-dispatch.c int dispatch_event(const struct audit_reply *rep, int protocol_ver) { event_t *e; if (!libdisp_active()) return 0; // Translate event into dispatcher format e = malloc(sizeof(event_t)); if (e == NULL) return -1; e->hdr.ver = protocol_ver; e->hdr.hlen = sizeof(struct audit_dispatcher_header); e->hdr.type = rep->type; - type由宏定义出现在
/usr/include/linux/audit.h只截取了一小部分 ... #define AUDIT_SYSCALL 1300 /* Syscall event */ /* #define AUDIT_FS_WATCH 1301 * Deprecated */ #define AUDIT_PATH 1302 /* Filename path information */ #define AUDIT_IPC 1303 /* IPC record */ #define AUDIT_SOCKETCALL 1304 /* sys_socketcall arguments */ #define AUDIT_CONFIG_CHANGE 1305 /* Audit system configuration change */ #define AUDIT_SOCKADDR 1306 /* sockaddr copied as syscall arg */ #define AUDIT_CWD 1307 /* Current working directory */ #define AUDIT_EXECVE 1309 /* execve arguments */ #define AUDIT_IPC_SET_PERM 1311 /* IPC new permissions record type */ ...
data
- 从这个函数可以看到,其实data的内容只是一段message,这里只是一段字符串消息
/home/kira/rpmbuild/BUILD/audit-3.0.1/src/auditd-dispatch.c int dispatch_event(const struct audit_reply *rep, int protocol_ver) { event_t *e; if (!libdisp_active()) return 0; // Translate event into dispatcher format e = malloc(sizeof(event_t)); if (e == NULL) return -1; e->hdr.ver = protocol_ver; e->hdr.hlen = sizeof(struct audit_dispatcher_header); e->hdr.type = rep->type; // Network originating events have data at rep->message if (protocol_ver == AUDISP_PROTOCOL_VER) { e->hdr.size = rep->msg.nlh.nlmsg_len; memcpy(e->data, (void*)rep->msg.data, e->hdr.size); } else if (protocol_ver == AUDISP_PROTOCOL_VER2) { e->hdr.size = rep->len; memcpy(e->data, (void*)rep->message, e->hdr.size); } else { free(e); return 0; } return libdisp_enqueue(e); } - 同时这里也有一条比较关键的问题,可以看出data的空间是动态分配,并且没有初始化的,也没有在这段消息字符串后面跟上结束符,后面我们处理时,需要手动根据消息的长度加上分隔符
编写对应的接收消息
- 这里是循环收消息的示例代码,创建unix sock的代码可以看看上文
char buff[MAX_AUDIT_MESSAGE_LENGTH + sizeof(struct audit_dispatcher_header)]={0}; while(1) { memset(buff, 0, sizeof(buff)); int len; if((len = read(sockfd, buff, sizeof(buff))) > 0) { printf(">>>>>>>>>>>>>>> recv len = %d <<<<<<<<<<<<<<<\n", len); struct audit_dispatcher_header* audis = (struct audit_dispatcher_header*)buff; // 第一个字段 printf("audis->ver = %u\n", audis->ver); // 版本号,目前只有两个版本 printf("audis->hlen = %u\n", audis->hlen); // 头的长度,固定为 sizeof(struct audit_dispatcher_header) printf("audis->type = %u\n", audis->type); // 消息类型 printf("audis->size = %u\n", audis->size); // 消息长度,注意打断收到的消息,源消息的内存空间是malloc出来的,不截断后续内容不保证 switch (audis->ver) { case AUDISP_PROTOCOL_VER: case AUDISP_PROTOCOL_VER2: buff[sizeof(struct audit_dispatcher_header) + audis->size] = 0; break; default: printf("recv msg format erro\n"); continue; } switch (audis->type) { case AUDIT_CWD: // 这些类型在audit.h中 printf("work path reset"); break; /** * 往后还有非常多的类型,这些类型都存储在audit.h中 * 可以根据type解析对应的msg字符串 */ default: break; } printf("data >\n %s\n", (char*)((unsigned long)buff + sizeof(struct audit_dispatcher_header))); } } - 收到的消息示例,正常收到消息
[kira@localhost source]$ ./test >>>>>>>>>>>>>>> recv len = 8986 <<<<<<<<<<<<<<< audis->ver = 1 audis->hlen = 16 audis->type = 2404 audis->size = 405 data > type=CRYPTO_KEY_USER msg=audit(1654740249.060:238): pid=2396 uid=0 auid=4294967295 ses=4294967295 subj=system_u:system_r:sshd_t:s0-s0:c0.c1023 msg='op=destroy kind=server fp=SHA256:bb:78:fd:e2:ed:6a:22:09:12:7d:6f:90:c2:dc:3a:0d:ca:d0:d2:58:95:79:3e:38:15:97:7d:1f:40:c3:11:49 direction=? spid=2396 suid=0 exe="/usr/sbin/sshd" hostname=? addr=? terminal=? res=success'UID="root" AUID="unset" SUID="root" >>>>>>>>>>>>>>> recv len = 8986 <<<<<<<<<<<<<<< audis->ver = 1 audis->hlen = 16 audis->type = 2407 audis->size = 435 data > type=CRYPTO_SESSION msg=audit(1654740249.062:239): pid=2395 uid=0 auid=4294967295 ses=4294967295 subj=system_u:system_r:sshd_t:s0-s0:c0.c1023 msg='op=start direction=from-server cipher=aes256-ctr ksize=256 mac=hmac-sha2-256 pfs=diffie-hellman-group-exchange-sha256 spid=2396 suid=74 rport=60919 laddr=192.168.58.131 lport=22 exe="/usr/sbin/sshd" hostname=? addr=192.168.58.1 terminal=? res=success'UID="root" AUID="unset" SUID="sshd" >>>>>>>>>>>>>>> recv len = 8986 <<<<<<<<<<<<<<< audis->ver = 1 audis->hlen = 16 audis->type = 2407 audis->size = 435 data > type=CRYPTO_SESSION msg=audit(1654740249.062:240): pid=2395 uid=0 auid=4294967295 ses=4294967295 subj=system_u:system_r:sshd_t:s0-s0:c0.c1023 msg='op=start direction=from-client cipher=aes256-ctr ksize=256 mac=hmac-sha2-256 pfs=diffie-hellman-group-exchange-sha256 spid=2396 suid=74 rport=60919 laddr=192.168.58.131 lport=22 exe="/usr/sbin/sshd" hostname=? addr=192.168.58.1 terminal=? res=success'UID="root" AUID="unset" SUID="sshd"