osquery源码解读之auditd设置

说明

上一篇通过分析osquery中的socket_events的实现对linux中的auditd的一些简单使用以及auditd日志文件的结构进行了讲解,本篇文章聚焦于osquery中的audit的实现。

前言

在之前的文章osquery源码解读之分析socket_events中已经说到,在osquery接管了auditd服务之后,就会增加三条规则:

1
2
3
-a always,exit -S connect
-a always,exit -S bind
-a always,exit -S execve

在正常情况下如果我们要设置audit的规则,要么在audit.rules中添加规则要么就通过auditctl增加。但是如果我们使用osquery,osquery也能够增加规则。通过分析osquery这部分的源代码来了解osquery是如何通过代码来给auditd增减规则的。在osquery源码解读之分析socket_events中,在osquery采用的是event publisher/subscriber的模式,也说到了socket_events数据解析是通过接受audit过滤之后的数据。而socket_event.cpp(osquery/tables/events/linux/socket_events.cpp)其实就是一个订阅者的模式。

通过以上的分析,我们知道在osquery中存在对audit规则进行设置以及event的发布者的功能。接下来我们将一一进行分析。

osquery源码解读之分析socket_events中也简单地提到了,系统中的内核kaudit和用户态的消息传递采用的就是netlink。auditdnetlink.cpp(osquery/events/linux/auditdnetlink.cpp)就是用于获取netlink之后设置规则。
auditdnetlink.cpp存在如下的函数:

  • AuditdNetlink::getEvents()
  • AuditdNetlinkReader::AuditdNetlinkReader
  • AuditdNetlinkReader::start()
  • AuditdNetlinkReader::stop()
  • AuditdNetlinkReader::acquireMessages()
  • AuditdNetlinkReader::configureAuditService()
  • AuditdNetlinkReader::clearAuditConfiguration()
  • AuditdNetlinkReader::deleteAuditRule
  • AuditdNetlinkReader::restoreAuditServiceConfiguration()
  • AuditdNetlinkReader::acquireHandle()
  • AuditdNetlinkParser::AuditdNetlinkParser(AuditdContextRef context)
  • AuditdNetlinkParser::start()
  • AuditdNetlinkParser::ParseAuditReply
  • AuditdNetlinkParser::AdjustAuditReply(audit_reply& reply)

函数的功能很多都表明了这个函数的作用。我们先从start()开始分析。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
void AuditdNetlinkReader::start() {
int counter_to_next_status_request = 0;
const int status_request_countdown = 1000;
while (!interrupted()) {
if (auditd_context_->acquire_handle) {
if (FLAGS_audit_debug) {
std::cout << "(re)acquiring the audit handle.." << std::endl;
}

NetlinkStatus netlink_status = acquireHandle();

if (netlink_status == NetlinkStatus::Disabled ||
netlink_status == NetlinkStatus::Error) {
std::this_thread::sleep_for(std::chrono::seconds(5));
continue;
}

auditd_context_->acquire_handle = false;
counter_to_next_status_request = status_request_countdown;
}

if (counter_to_next_status_request == 0) {
errno = 0;

if (audit_request_status(audit_netlink_handle_) <= 0) {
if (errno == ENOBUFS) {
VLOG(1) << "Warning: Failed to request audit status (ENOBUFS). "
"Retrying again later";

} else {
VLOG(1) << "Error: Failed to request audit status. Requesting a "
"handle reset";

auditd_context_->acquire_handle = true;
}
}

counter_to_next_status_request = status_request_countdown;
} else {
--counter_to_next_status_request;
}

if (!acquireMessages()) {
auditd_context_->acquire_handle = true;
}
}
}

其中关键的代码是NetlinkStatus netlink_status = acquireHandle();用于获取netlinkhander,跟进代码分析;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
NetlinkStatus AuditdNetlinkReader::acquireHandle() noexcept {
auto L_GetNetlinkStatus = [](int netlink_handle) -> NetlinkStatus {
if (netlink_handle <= 0) {
return NetlinkStatus::Error;
}

errno = 0;
if (audit_request_status(netlink_handle) < 0 && errno != ENOBUFS) {
VLOG(1) << "Failed to query the audit netlink status";
return NetlinkStatus::Error;
}

auto enabled = audit_is_enabled(netlink_handle);

if (enabled == AUDIT_IMMUTABLE || getuid() != 0 ||
!FLAGS_audit_allow_config) {
return NetlinkStatus::ActiveImmutable;

} else if (enabled == AUDIT_ENABLED) {
return NetlinkStatus::ActiveMutable;

} else if (enabled == AUDIT_DISABLED) {
return NetlinkStatus::Disabled;

} else {
return NetlinkStatus::Error;
}
};

if (audit_netlink_handle_ != -1) {
audit_close(audit_netlink_handle_);
audit_netlink_handle_ = -1;
}

audit_netlink_handle_ = audit_open();
if (audit_netlink_handle_ <= 0) {
VLOG(1) << "Failed to acquire the netlink handle";

audit_netlink_handle_ = -1;
return NetlinkStatus::Error;
}
.......
NetlinkStatus netlink_status = L_GetNetlinkStatus(audit_netlink_handle_);
if (FLAGS_audit_allow_config &&
(netlink_status != NetlinkStatus::ActiveMutable &&
netlink_status != NetlinkStatus::ActiveImmutable)) {
if (audit_set_enabled(audit_netlink_handle_, AUDIT_ENABLED) < 0) {
VLOG(1) << "Failed to enable the audit service";

audit_close(audit_netlink_handle_);
audit_netlink_handle_ = -1;

return NetlinkStatus::Error;
}

if (FLAGS_audit_debug) {
std::cout << "Audit service enabled" << std::endl;
}
}

if (FLAGS_audit_allow_config) {
if (FLAGS_audit_force_reconfigure) {
if (!clearAuditConfiguration()) {
audit_netlink_handle_ = -1;
return NetlinkStatus::Error;
}
}

if (!configureAuditService()) {
return NetlinkStatus::ActiveImmutable;
}

if (FLAGS_audit_debug) {
std::cout << "Audit service configured" << std::endl;
}
}

return NetlinkStatus::ActiveMutable;
}

代码较长,我们分步进行分析:

  1. 首先定义了L_GetNetlinkStatus(int netlink_handle)函数,作用是根据netlink的fd来判断netlink的状态,状态分别有AUDIT_IMMUTABLE(根据Disable auditd immutable mode without rebooting的说法,当处于netlink处于AUDIT_IMMUTABLE模式时需要重启规则才能够生效)、AUDIT_ENABLEDAUDIT_DISABLED。其中最为关键的函数是auto enabled = audit_is_enabled(netlink_handle)(audit_is_enabled是位于libaudit中,作用就是根据netlink的句柄知道netlink的状态),根据audit_is_enabled的实现知道This function will return 0 if auditing is NOT enabled and 1 if enabled, and -1 on error.。那么AUDIT_IMMUTABLE,AUDIT_ENABLED,AUDIT_DISABLEDError分别对应此函数值的返回值的2,1,0,-1。这就是L_GetNetlinkStatus()获得netlink状态的流程。
  2. 在第一步讲了通过L_GetNetlinkStatus()根据netlink_handle获取netlink的状态,那么第二部就是如何获取netlink_handle了。audit_netlink_handle_ = audit_open();通过audit_open()获取状态。根据audit_open的说明,audit_open creates a NETLINK_AUDIT socket for communication with the kernel part of the Linux Audit Subsystem,含义大致就是创建了一个NETLINK_AUDIT用于和内核的audit服务进行通信,返回值就是audit_netlink_handle_
  3. 利用NetlinkStatus netlink_status = L_GetNetlinkStatus(audit_netlink_handle_);综合前面2步,获取当前netlink的状态,判断是否满足当前要求。
  4. 接下来就是函数的核心部分了。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    if (FLAGS_audit_allow_config) {
    if (FLAGS_audit_force_reconfigure) {
    if (!clearAuditConfiguration()) {
    audit_netlink_handle_ = -1;
    return NetlinkStatus::Error;
    }
    }

    if (!configureAuditService()) {
    return NetlinkStatus::ActiveImmutable;
    }

    if (FLAGS_audit_debug) {
    std::cout << "Audit service configured" << std::endl;
    }
    }
  5. FLAGS_audit_allow_configFLAGS_audit_force_reconfigure,两个参数的作用表示是否需要清空之前系统中存在的audit的规则。

  6. clearAuditConfiguration()在1的基础上,调用clearAuditConfiguration()清空规则。删除方法大致如下:
    1. audit_get_reply(audit_netlink_handle_, &reply, GET_REPLY_NONBLOCKING, 0) 得到当前auditd中所有设置的规则;
    2. 调用deleteAuditRule(const AuditRuleDataObject &rule_object)函数完成删除工作。
  7. deleteAuditRule()的主要代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    bool AuditdNetlinkReader::deleteAuditRule(const AuditRuleDataObject &rule_object) {
    ....
    auto rule_data =reinterpret_cast<const struct audit_rule_data *>(rule_object.data());
    struct audit_message request = {};
    request.nlh.nlmsg_len = static_cast<__u32>(NLMSG_SPACE(rule_object.size()));
    request.nlh.nlmsg_type = AUDIT_DEL_RULE;
    request.nlh.nlmsg_flags = NLM_F_REQUEST;
    std::memcpy(NLMSG_DATA(&request.nlh), rule_data, rule_object.size());

    .....
    bytes_sent = sendto(audit_netlink_handle_,
    &request,
    request.nlh.nlmsg_len,
    0,
    reinterpret_cast<struct sockaddr *>(&address),
    .....

    由于代码较长,仅仅显示部分代码,可以看到其实最终是利用sendto()方法来删除规则;

    1. configureAuditService(),在清空了系统中已经存在的规则之后,利用configureAuditService()设置osquery需要的规则。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      bool AuditdNetlinkReader::configureAuditService() noexcept {

      // Want to set a min sane buffer and maximum number of events/second min.
      // This is normally controlled through the audit config, but we must
      // enforce sane minimums: -b 8192 -e 100
      audit_set_backlog_wait_time(audit_netlink_handle_, 1);
      audit_set_backlog_limit(audit_netlink_handle_, 4096);
      audit_set_failure(audit_netlink_handle_, AUDIT_FAIL_SILENT);

      // Request only the highest priority of audit status messages.
      set_aumessage_mode(MSG_QUIET, DBG_NO);

      //
      // Audit rules
      //

      // Rules required by the socket_events table
      if (FLAGS_audit_allow_sockets) {
      VLOG(1) << "Enabling audit rules for the socket_events table";

      for (int syscall : SocketEventSubscriber::GetSyscallSet()) {
      monitored_syscall_list_.insert(syscall);
      }
      }

      // Rules required by the process_events table
      if (FLAGS_audit_allow_process_events) {
      VLOG(1) << "Enabling audit rules for the process_events table";

      for (int syscall : AuditProcessEventSubscriber::GetSyscallSet()) {
      monitored_syscall_list_.insert(syscall);
      }
      }

      // Rules required by the process_file_events table
      if (FLAGS_audit_allow_fim_events) {
      VLOG(1) << "Enabling audit rules for the process_file_events table";

      for (int syscall : ProcessFileEventSubscriber::GetSyscallSet()) {
      monitored_syscall_list_.insert(syscall);
      }
      }

      // Attempt to add each one of the rules we collected
      for (int syscall_number : monitored_syscall_list_) {
      audit_rule_data rule = {};
      audit_rule_syscall_data(&rule, syscall_number);

      // clang-format off
      int rule_add_error = audit_add_rule_data(audit_netlink_handle_, &rule,
      // We want to be notified when we exit from the syscall
      AUDIT_FILTER_EXIT,

      // Always audit this syscall event
      AUDIT_ALWAYS
      );
      ......
    2. 首先会设置audit的基本规则

      1
      2
      3
      audit_set_backlog_wait_time(audit_netlink_handle_, 1);
      audit_set_backlog_limit(audit_netlink_handle_, 4096);
      audit_set_failure(audit_netlink_handle_, AUDIT_FAIL_SILENT);

      audit_set_backlog_wait_time,表示当当event日志数量达到backlog_limit限制时,内核的等待时间;audit_set_backlog_limit,表示在audit events的缓冲区中能够存储的events数量上限;audit_set_failure中表示当events达到backlog limit上限时的操作(0,忽略;1,使用printk记录事件;2,调用panic function)。

    3. 根据不同的设定FLAGS_audit_allow_socketsFLAGS_audit_allow_process_eventsFLAGS_audit_allow_fim_events增加响应的规则。由于这三者的处理方法都一样,以FLAGS_audit_allow_sockets为例进行说明;

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      //osquery/events/linux/auditeventpublisher.h
      std::set<int> monitored_syscall_list_;
      ....
      //osquery/events/linux/auditdnetlink.cpp
      if (FLAGS_audit_allow_sockets) {
      for (int syscall : SocketEventSubscriber::GetSyscallSet()) {
      monitored_syscall_list_.insert(syscall);
      }
      }
      ......
      //osquery/tables/events/linux/socket_events.cpp
      const std::set<int> &SocketEventSubscriber::GetSyscallSet() noexcept {
      //__NR_bind, __NR_connect 都是Linux中系统自行定义的函数
      static const std::set<int> syscall_set = {__NR_bind, __NR_connect};
      return syscall_set;
      }

      asm/unistd_64.h对所有的系统调用函数都有定义。在/arch/sh/include/uapi/asm/unistd_64.h中同样有说明,实例如下:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      /* Non-multiplexed socket family */
      #define __NR_socket 220
      #define __NR_bind 221
      #define __NR_connect 222
      #define __NR_listen 223
      #define __NR_accept 224
      #define __NR_getsockname 225
      #define __NR_getpeername 226
      #define __NR_socketpair 227
      #define __NR_send 228
      #define __NR_sendto 229
      #define __NR_recv 230
      #define __NR_recvfrom 231
      #define __NR_shutdown 232
      #define __NR_setsockopt 233
      #define __NR_getsockopt 234
      #define __NR_sendmsg 235
      #define __NR_recvmsg 236

      所以在GetSyscallSet()中返回的__NR_bind__NR_connect分别是221222,之后保存到monitored_syscall_list_变量中

    4. 提取monitored_syscall_list_中的规则:

      1
      2
      3
      4
      for (int syscall_number : monitored_syscall_list_) {
      audit_rule_data rule = {};
      audit_rule_syscall_data(&rule, syscall_number);
      ....

      audit_rule_syscall_data()将每一个syscall_number变为audit_rule_data规则,实现参考audit_rule_syscall_data

    5. 设置规则,int rule_add_error = audit_add_rule_data(audit_netlink_handle_, &rule,AUDIT_FILTER_EXIT,AUDIT_ALWAYS);,audit_add_rule_data位于libaudit.h中,就是用来添加过滤规则的。如此,最终就得到了如下的规则。

      1
      2
      3
      -a always,exit -S connect
      -a always,exit -S bind
      -a always,exit -S execve
  8. 以上就是整个规则的设定,在设置完规则之后,接下来就是接受audit审计之后的数据了。调用acquireMessages(),关键代码是:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    bool AuditdNetlinkReader::acquireMessages() noexcept {
    ...
    audit_reply reply = {};
    ssize_t len = recvfrom(audit_netlink_handle_,
    &reply.msg,
    sizeof(reply.msg),
    0,
    reinterpret_cast<struct sockaddr *>(&nladdr),
    &nladdrlen);
    ....
    read_buffer_[events_received] = reply;
    if (events_received != 0) {
    std::unique_lock <std::mutex> lock(
    auditd_context_->unprocessed_records_mutex);

    auditd_context_->unprocessed_records.reserve(
    auditd_context_->unprocessed_records.size() + events_received);

    auditd_context_->unprocessed_records.insert(
    auditd_context_->unprocessed_records.end(),
    read_buffer_.begin(),
    std::next(read_buffer_.begin(), events_received));

    auditd_context_->unprocessed_records_cv.notify_all();
    }
    ...
    }

    首先调用recvfrom()函数从audit_netlink_handle_接受数据,将数据全部填充至reply中;之后将reply中的数据全部放入auditd_context_中。

  9. AuditdNetlinkParser::ParseAuditReply()在第8步中说道利用acquireMessages()将数据全部保存至auditd_context_,那么ParseAuditReply()就是来解析auditd_context_中的数据的。部分代码如下所示:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    bool AuditdNetlinkParser::ParseAuditReply(const audit_reply &reply, AuditEventRecord &event_record) noexcept {
    ....
    // Parse the record header
    event_record.type = reply.type;
    boost::string_ref message_view(reply.message,static_cast<unsigned int>(reply.len));
    auto preamble_end = message_view.find("): ");
    if (preamble_end == std::string::npos) {
    return false;
    }

    event_record.time =tryTo<unsigned long int>(message_view.substr(6, 10).to_string(), 10).takeOr(event_record.time);
    event_record.audit_id = message_view.substr(6, preamble_end - 6).to_string();
    1. 通过boost::string_ref message_view(reply.message,static_cast<unsigned int>(reply.len));将type中的数据全部保存至message_view中。
    2. 之后通过message_view解析得到event_record.timeevent_record.audit_id。我们以一个实际的例子来进行说明。

      1
      type=SYSCALL msg=audit(1544195763.393:260010): arch=c000003e syscall=42 success=no exit=-115 a0=3 a1=7ffccb794910 a2=10 a3=7ffccb7941e0 items=0 ppid=53240 pid=17096 auid=1000 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts5 ses=1 comm="curl" exe="/usr/bin/curl" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=(null)

      其中event_record.time =tryTo<unsigned long int>(message_view.substr(6, 10).to_string(), 10).takeOr(event_record.time);,中得到的就是1544195763event_record.audit_id = message_view.substr(6, preamble_end - 6).to_string();得到的就是1544195763.393:260010。所以其实ParseAuditReply()中的message_view数据也像audit.log中的数据一样需要自己来对数据进行解析分割然后填充至对应的表中;

以上就是整个的osquery设定audit的规则以及获取audit的数据并解析的过程。

AuditEventPublisher

osquery/events/linux/auditeventpublisher.cpp此文件就是audit event的消息发布者。通过分析源代码,我们来看看他是如何发布event消息的。

GetEventRecord

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const AuditEventRecord* GetEventRecord(const AuditEvent& event,
int record_type) noexcept {
auto it = std::find_if(event.record_list.begin(),
event.record_list.end(),
[record_type](const AuditEventRecord& record) -> bool {
return (record.type == record_type);
});

if (it == event.record_list.end()) {
return nullptr;
}

return &(*it);
};

通过GetEventRecord()函数所有的AuditEventRecord消息,而这些消息都是由之前AuditdNetlinkParser::ParseAuditReply()解析得到的。

ProcessEvents

由于AuditEventPublisher::ProcessEvents(AuditEventContextRef event_context,const std::vector<AuditEventRecord>& record_list,AuditTraceContext& trace_context)的函数实现较长,我们仅以其中的关键部分为例来进行说明。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
for (const auto& audit_event_record : record_list) {
auto audit_event_it = trace_context.find(audit_event_record.audit_id); //将audit_id转换为audit_event_it
if (audit_event_record.type >= AUDIT_FIRST_USER_MSG &&audit_event_record.type <= AUDIT_LAST_USER_MSG) {
....
} else if (selinux_event_set.find(audit_event_record.type) !=selinux_event_set.end()) {
...
} else if (audit_event_record.type == AUDIT_SYSCALL) {
if (audit_event_it != trace_context.end()) {
VLOG(1) << "Received a duplicated event.";
trace_context.erase(audit_event_it);
}

AuditEvent audit_event;
audit_event.type = AuditEvent::Type::Syscall;

// Estimate based on the process_file_events_tests.cpp records
audit_event.record_list.reserve(4U);

通过遍历record_list得到audit_event_record并根据其type分别进行不同处理,我们这里仅仅只关注AUDIT_SYSCALL类型。通过检测audit_event_it是否在trace_context来避免重复处理消息。接下来就是对原始的audit的日志解码了。

  1. data.executable_path = DecodeAuditPathValues(raw_executable_path); 获取executable_path
  2. 获取系统调用结果syscall_status

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    std::string syscall_status;
    GetStringFieldFromMap(
    syscall_status, audit_event_record.fields, "success", "yes");

    // By discarding this event, we will also automatically discard any other
    // attached record
    // 如果 syscall_status 不是success,则自动丢弃,但是这样会导致很多的服务都会存在问题,很多的日志都会被丢掉
    if (syscall_status != "yes") {
    continue;
    }
  3. 获取pid

    1
    2
    3
    4
    5
    std::uint64_t process_id;
    if (!GetIntegerFieldFromMap(process_id, audit_event_record.fields, "pid")) {
    VLOG(1) << "Malformed AUDIT_SYSCALL record received. The process id field is either missing or not valid.";
    continue;
    }

    通过获取audit_event_record中的pid字段的内容。

  4. 同理获取ppid

    1
    2
    3
    4
    std::uint64_t parent_process_id;
    if (!GetIntegerFieldFromMap(parent_process_id, audit_event_record.fields, "ppid")) {
    continue;
    }
  5. 其他的参数以此类推,包括uideuidgidegid都是相同的方法获取。这里就不一一展示了。

  6. 存储数据

    1
    2
    audit_event.record_list.push_back(audit_event_record);
    trace_context[audit_event_record.audit_id] = std::move(audit_event);

    完成了基本的数据解析之后,利用audit_event保留所有解析的数据。trace_context将audit_idaudit_event以键值对的方式保存;

SocketEventSubscriber&AuditProcessEventSubscriber

AuditEventPublisher已经完成了消息的发布,那么SocketEventSubscriberAuditProcessEventSubscriber通过订阅的方式就能够获取自己需要的消息了;

1
2
REGISTER(SocketEventSubscriber,"event_subscriber", "socket_events");
REGISTER(AuditProcessEventSubscriber, "event_subscriber", "process_events");

他们的消息处理方法是:
socket_events.cpp

1
2
3
4
5
6
7
8
9
Status SocketEventSubscriber::ProcessEvents(std::vector <Row> &emitted_row_list,const std::vector <AuditEvent> &event_list) noexcept {
emitted_row_list.clear();
emitted_row_list.reserve(event_list.size());
// 判断是否是Syscall
for (const auto &event : event_list) {
if (event.type != AuditEvent::Type::Syscall) {
continue;
}
....

process_events.cpp

1
2
3
4
5
6
7
8
Status AuditProcessEventSubscriber::ProcessEvents(std::vector<Row>& emitted_row_list,const std::vector<AuditEvent>& event_list) noexcept {
emitted_row_list.clear();
emitted_row_list.reserve(event_list.size());
for (const auto& event : event_list) {
if (event.type != AuditEvent::Type::Syscall) {
continue;
}
.....

可以发现socket_events.cppprocess_events.cpp处理方法基本相同,都是对event_list进行遍历处理,而event_list就是来自于前面AuditEventPublisher所产生的日志;整个流程就完全是连通的啦,如果想了解socket_events.cpp是如何处理数据的,可以参考前一篇文章osquery源码解读之分析socket_events

总结

一个auditd就读了这么长时间,怎么时候才能够如同庖丁解牛一般对Linux了如指掌啊。

拥有快速学习能⼒的⽩帽子,是不能有短板的。有的只是⼤量的标准板和⼏块长板。

以上