記事ディレクトリ
1. suricata ソースコードをダウンロードする
https://github.com/OISF/suricata に移動して、リリース バージョンをダウンロードします。
2. 依存関係をインストールする
1、libhtp
https://github.com/OISF/libhtpからソース コードをダウンロードして
解凍し、ソース コード ディレクトリに移動してlibhtp-0.5.41
実行する 必要があります。
./configure --prefix=/opt/libhtp-0.5.41-ubuntu-x64
make
make install
次に、環境変数を構成し、 を編集し/etc/profile
て追加します。
export LIBHTP_ROOT=/opt/libhtp-0.5.41-ubuntu-x64
export LD_LIBRARY_PATH=$LIBHTP_ROOT/lib:$LD_LIBRARY_PATH
export CPATH=$LIBHTP_ROOT/include:$CPATH
export LIBRARY_PATH=$LIBHTP_ROOT/lib:$LIBRARY_PATH
export PKG_CONFIG_PATH=$LIBHTP_ROOT/lib/pkgconfig:$PKG_CONFIG_PATH
2. その他
apt-get install libpcre3-dev
apt-get install libjansson-dev
apt-get install libyaml-dev
apt-get install libmagic-dev
apt-get install libnss3-dev
apt-get install libcap-ng-dev
apt-get install liblz4-dev
3. コンパイルとインストール
解凍後、ソースコードディレクトリに移動しsuricata-6.0.8
、以下を実行します。
./configure --prefix=/opt/suricata-6.0.8-ubuntu-x64
make
これで suricata のコンパイルが完了しましたが、suricata は独立したプロセスなので、そのままmake install
コマンドを実行すると、指定したディレクトリにソフトウェアがインストールされます/opt/suricata-6.0.8-ubuntu-x64
。のように:
4. suricata をコンポーネントとして使用する
場合によっては、suricata を独立したプロセスとして実行するのではなく、プロセス内のモジュール コンポーネントとして使用したい場合、suricata のソース コードとそれに依存するヘッダー ファイル/ライブラリをメイン プロセスに統合する必要があります。
suricata は C で書かれているため、メイン処理を C++ で実装した場合、suricata が提供するインターフェースを直接呼び出すことができませんが、suricata のソースコードへの変更を最小限に抑えるために、インターフェースプロキシ用のプロキシモジュールを追加することができます。プロキシ モジュールで C++ 互換にすることができます。たとえば、プロキシ モジュールを追加します:
proxy.h
#pragma once
#ifdef __cplusplus
extern "C"
{
#endif
/**
* @brief 启动Suricata
*/
void startSuricata(int argc, char** argv);
/**
* @brief 停止Suricata
*/
void stopSuricata();
#ifdef __cplusplus
}
#endif
proxy.c
#include "proxy.h"
#include <stdio.h>
#include <string.h>
#include "../suricata-6.0.8/src/suricata.h"
void startSuricata(int argc, char** argv)
{
SuricataMain(argc, argv);
}
void stopSuricata()
{
EngineStop();
}
アラーム情報のイベント監視を追加する場合は、次の手順を実行して、suricata-6.0.8/src/
ディレクトリにファイル
alert-define.hを追加します。
#pragma once
#include <stdlib.h>
#ifdef __cplusplus
extern "C"
{
#endif
/**
* @brief 告警信息
*/
typedef struct
{
char timebuf[64]; /* 时间, 格式: "10/16/2022-15:42:45.160103" */
char protocol[32]; /* 协议 */
char srcIp[46]; /* 源IP */
int srcPort; /* 源端口 */
char dstIp[46]; /* 目的IP */
int dstPort; /* 目的端口 */
int priority; /* 等级 */
char classification[512]; /* 分类信息描述 */
char msg[1024]; /* 消息 */
} st_alert_info;
/**
* @brief 告警回调
* @param info 告警信息
* @return 0-不写日志, 1-写日志
*/
typedef int (*alert_callback)(st_alert_info info);
/**
* @brief 设置告警回调
* @param callback 回调
*/
void setAlarmCallback(alert_callback callback);
/**
* @brief 响应告警回调
* @param info 告警信息
* @return 0-不写日志, 1-写日志
*/
int onAlarmCallback(st_alert_info info);
#ifdef __cplusplus
}
#endif
アラート定義.c
#include "alert-define.h"
static alert_callback s_alertCallback = NULL; /* 告警回调 */
void setAlarmCallback(alert_callback callback)
{
s_alertCallback = callback;
}
int onAlarmCallback(st_alert_info info)
{
if (s_alertCallback)
{
return s_alertCallback(info);
}
return 1;
}
alert-fastlog.c
ファイルインターフェイス を変更しますint AlertFastLogger(ThreadVars *tv, void *data, const Packet *p)
。
#include "alert-define.h"
int AlertFastLogger(ThreadVars *tv, void *data, const Packet *p)
{
AlertFastLogThread *aft = (AlertFastLogThread *)data;
int i;
char timebuf[64];
int decoder_event = 0;
CreateTimeString(&p->ts, timebuf, sizeof(timebuf));
char srcip[46], dstip[46];
if (PKT_IS_IPV4(p)) {
PrintInet(AF_INET, (const void *)GET_IPV4_SRC_ADDR_PTR(p), srcip, sizeof(srcip));
PrintInet(AF_INET, (const void *)GET_IPV4_DST_ADDR_PTR(p), dstip, sizeof(dstip));
} else if (PKT_IS_IPV6(p)) {
PrintInet(AF_INET6, (const void *)GET_IPV6_SRC_ADDR(p), srcip, sizeof(srcip));
PrintInet(AF_INET6, (const void *)GET_IPV6_DST_ADDR(p), dstip, sizeof(dstip));
} else {
decoder_event = 1;
}
/* Buffer to store the generated alert strings. The buffer is
* filled with alert strings until it doesn't have room to store
* another full alert, only then is the buffer written. This is
* more efficient for multiple alerts and only slightly slower for
* single alerts.
*/
char alert_buffer[MAX_FASTLOG_BUFFER_SIZE];
char proto[16] = "";
const char *protoptr;
if (SCProtoNameValid(IP_GET_IPPROTO(p))) {
protoptr = known_proto[IP_GET_IPPROTO(p)];
} else {
snprintf(proto, sizeof(proto), "PROTO:%03" PRIu32, IP_GET_IPPROTO(p));
protoptr = proto;
}
uint16_t src_port_or_icmp = p->sp;
uint16_t dst_port_or_icmp = p->dp;
if (IP_GET_IPPROTO(p) == IPPROTO_ICMP || IP_GET_IPPROTO(p) == IPPROTO_ICMPV6) {
src_port_or_icmp = p->icmp_s.type;
dst_port_or_icmp = p->icmp_s.code;
}
for (i = 0; i < p->alerts.cnt; i++) {
const PacketAlert *pa = &p->alerts.alerts[i];
if (unlikely(pa->s == NULL)) {
continue;
}
const char *action = "";
if ((pa->action & ACTION_DROP) && EngineModeIsIPS()) {
action = "[Drop] ";
} else if (pa->action & ACTION_DROP) {
action = "[wDrop] ";
}
/* Create the alert string without locking. */
int size = 0;
if (likely(decoder_event == 0)) {
PrintBufferData(alert_buffer, &size, MAX_FASTLOG_ALERT_SIZE,
"%s %s[**] [%" PRIu32 ":%" PRIu32 ":%"
PRIu32 "] %s [**] [Classification: %s] [Priority: %"PRIu32"]"
" {%s} %s:%" PRIu32 " -> %s:%" PRIu32 "\n", timebuf, action,
pa->s->gid, pa->s->id, pa->s->rev, pa->s->msg, pa->s->class_msg, pa->s->prio,
protoptr, srcip, src_port_or_icmp, dstip, dst_port_or_icmp);
} else {
PrintBufferData(alert_buffer, &size, MAX_FASTLOG_ALERT_SIZE,
"%s %s[**] [%" PRIu32 ":%" PRIu32
":%" PRIu32 "] %s [**] [Classification: %s] [Priority: "
"%" PRIu32 "] [**] [Raw pkt: ", timebuf, action, pa->s->gid,
pa->s->id, pa->s->rev, pa->s->msg, pa->s->class_msg, pa->s->prio);
PrintBufferRawLineHex(alert_buffer, &size, MAX_FASTLOG_ALERT_SIZE,
GET_PKT_DATA(p), GET_PKT_LEN(p) < 32 ? GET_PKT_LEN(p) : 32);
if (p->pcap_cnt != 0) {
PrintBufferData(alert_buffer, &size, MAX_FASTLOG_ALERT_SIZE,
"] [pcap file packet: %"PRIu64"]\n", p->pcap_cnt);
} else {
PrintBufferData(alert_buffer, &size, MAX_FASTLOG_ALERT_SIZE, "]\n");
}
}
/* 构造告警信息 */
st_alert_info info;
memset(&info, 0, sizeof(info));
if (timebuf && strlen(timebuf) > 0)
{
memcpy(info.timebuf, timebuf, strlen(timebuf));
}
if (protoptr && strlen(protoptr) > 0)
{
memcpy(info.protocol, protoptr, strlen(protoptr));
}
if (strlen(srcip) > 0)
{
memcpy(info.srcIp, srcip, strlen(srcip));
}
info.srcPort = src_port_or_icmp;
if (strlen(dstip) > 0)
{
memcpy(info.dstIp, dstip, strlen(dstip));
}
info.dstPort = dst_port_or_icmp;
info.priority = pa->s->prio;
if (pa->s->class_msg && strlen(pa->s->class_msg) > 0)
{
memcpy(info.classification, pa->s->class_msg, strlen(pa->s->class_msg));
}
if (pa->s->msg && strlen(pa->s->msg) > 0)
{
memcpy(info.msg, pa->s->msg, strlen(pa->s->msg));
}
/* 调用告警回调 */
if (onAlarmCallback(info))
{
/* Write the alert to output file */
AlertFastLogOutputAlert(aft, alert_buffer, size);
}
}
return TM_ECODE_OK;
}
main.cpp
ファイル に、次のようなアラーム イベント コールバック設定を追加します。
#include "../suricata-6.0.8/src/alert-define.h"
#include "proxy.h"
/**
* @brief 响应告警回调
* @param info 告警信息
* @return 0-不写日志, 1-写日志
*/
int onAlertCallback(st_alert_info info)
{
INFO_LOG(s_logger, "协议: {}, 源地址: {}:{}, 目的地址: {}:{}, 等级: {}, 类别: {}, 消息: {}", info.protocol, info.srcIp, info.srcPort,
info.dstIp, info.dstPort, info.priority, info.classification, info.msg);
return 1;
}
int main(int argc, char* argv[])
{
setAlarmCallback(onAlertCallback); /* 设置告警回调 */
/* 主循环 */
while (1)
{
utility::PathInfo(Config::getValue(cfgkey::PathLog).toString() + "/suricata").create();
/* 创建参数列表 */
int argCount = 0;
char** argList = NULL;
{
std::lock_guard<std::mutex> locker(s_mutexArgVec);
s_argVec.clear();
s_argVec.emplace_back(argv0);
s_argVec.emplace_back("-c");
s_argVec.emplace_back("/proc_test/suricata.yaml");
s_argVec.emplace_back("-i");
s_argVec.emplace_back("enp10");
INFO_LOG(s_logger, "suricata模块启动参数: {}", utility::StrTool::join(s_argVec, " "));
argList = utility::StrTool::convertToArgv(s_argVec, argCount);
}
/* 启动suricata */
if (argCount > 0 && argList)
{
startSuricata(argCount, argList);
}
/* 销毁删除列表 */
destroyArgv(argCount, argList);
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
return 0;
}
前の手順の後make
、2 つのヘッダー ファイルが src ディレクトリに自動的に生成されます。これらは、後続の操作でプロジェクトに統合する必要があります。
suricata はrust
実装されたモジュールを使用するため、ソース コード ディレクトリは次のとおりです。
そのため、Rustのソースコードを.a
静的ライブラリとしてコンパイルし、C言語呼び出し用に提供する必要があります。前の手順で操作を実行するとmake
、次のような静的ライブラリが Rust ディレクトリに自動的に生成されます。
CMake
次に、これに基づいて次のようなプロジェクト ビルド スクリプト を作成します。
# CMake版本
cmake_minimum_required(VERSION 3.18.0)
# `std::make_unique`要求最低C++14
set(CMAKE_CXX_STANDARD 14)
# 工程名
project(proc_intrusion)
####################################### 添加线程库 #######################################
find_package(Threads REQUIRED)
##########################################################################################
####################################### 添加libhtp库 #######################################
if ("$ENV{LIBHTP_ROOT}" STREQUAL "") # 自动查找
find_path(LIBHTP_INCLUDE_DIR NAMES htp.h)
else () # 如果有手动配置LIBHTP环境变量LIBHTP_ROOT, 则从环境变量中获取
set(LIBHTP_INCLUDE_DIR $ENV{
LIBHTP_ROOT}/include/htp)
endif ()
find_library(LIBHTP_LIBRARIES NAMES htp HINTS ${LIBHTP_INCLUDE_DIR}/../../lib)
set(LIBHTP_ROOT_DIR ${LIBHTP_INCLUDE_DIR}/../../)
message(STATUS "libhtp root dir: ${LIBHTP_ROOT_DIR}")
message(STATUS "libhtp include path: ${LIBHTP_INCLUDE_DIR}")
message(STATUS "libhtp libraries: ${LIBHTP_LIBRARIES}")
if ("${LIBHTP_ROOT_DIR}" STREQUAL "LIBHTP_ROOT_DIR-NOTFOUND" OR
"${LIBHTP_INCLUDE_DIR}" STREQUAL "LIBHTP_INCLUDE_DIR-NOTFOUND" OR
"${LIBHTP_LIBRARIES}" STREQUAL "LIBHTP_LIBRARIES-NOTFOUND")
message(WARNING "libhtp not found")
return()
else ()
include_directories(${LIBHTP_INCLUDE_DIR})
endif ()
##########################################################################################
# 添加宏定义
add_definitions(-DHAVE_CONFIG_H=1)
add_definitions(-DLOCAL_STATE_DIR="/home/proc_intrusion")
# 添加头文件包含目录
include_directories(/usr/include/nss)
include_directories(/usr/include/nspr)
include_directories(suricata-6.0.8/src)
include_directories(suricata-6.0.8/rust/dist)
# 添加suricata源文件
set(proc_files)
get_cxx_files(suricata-6.0.8/src src_list)
list(APPEND proc_files ${src_list})
list(REMOVE_ITEM proc_files ${CMAKE_CURRENT_SOURCE_DIR}/suricata-6.0.8/src/main.c) # 这里去除源码中的主文件
# 添加主进程源文件
get_cxx_files(src src_list)
list(APPEND proc_files ${src_list})
message("proc_files files:")
foreach(filename ${proc_files})
message(" " ${filename})
endforeach()
# 构建可执行文件
add_executable(proc_intrusion ${proc_intrusion})
# 链接依赖库文件
target_link_libraries(proc_intrusion Threads::Threads
${LIBHTP_LIBRARIES}
pcap
dl
rt
m
lz4
magic
cap-ng
jansson
yaml
z
pcre
ssl3
smime3
nss3
nssutil3
plds4
plc4
nspr4
/root/workspace/suricata-6.0.8/rust/target/release/libsuricata.a)