0x00 背景
开源的suricata资源包是没有做加密处理,如果想要保护资源包,需要二次开发修改suricata源码。
本文基于suricata6.0.1 版本https://github.com/OISF/suricata/archive/refs/tags/suricata-6.0.1.zip二开。
0x01 实践
通过debug,跟规则处理相关需要修改2个地方。
1. src/suricata.c
2. src/detect-engine-loader.c
3. src/detect.h 和 configure.ac 应该是没改
修改suricata.c
原 237行下面加了2句:
extern int py_env_init();
extern void py_env_finilize();
修改 void PostConfLoadedDetectSetup(SCInstance *suri) 方法
原2350行后新增1句:
py_env_init();
原2827行新增1句:
py_env_finilize();
修改 src/detect-engine-loader.c
新增2个方法的实现:
PyObject * pModule = NULL;
PyObject * pFunc = NULL;
int py_env_init()
{
const char *module_name = "hello2";
const char *module_method = "test";
Py_Initialize();//调用Py_Initialize()进行初始化
PyRun_SimpleString("import sys");
//PyRun_SimpleString("import logrec");
PyRun_SimpleString("sys.path.append('/opt/ids/objectdir')");
pModule = PyImport_ImportModule(module_name);//调用的Python文件名
if (!pModule) {
SCLogError(SC_ERR_OPENING_RULE_FILE, "Failed to load \"%s\"", module_name);
return -1;
}
pFunc = PyObject_GetAttrString(pModule, module_method);//调用的函数名
//创建参数:
//PyObject *pArgs = PyTuple_New(1);
//PyTuple_SetItem(pArgs, 0, Py_BuildValue("s", "Hello Python!!"));
if (!pFunc) {
SCLogError(SC_ERR_OPENING_RULE_FILE, "Cannot find python function \"%s\"", module_method);
return -1;
}
return 0;
}
void py_env_finilize()
{
Py_DECREF(pFunc);
Py_DECREF(pModule);
Py_Finalize();//调用Py_Finalize,和Py_Initialize相对应的.
}
上面代码的作用是引入 /opt/ids/objectdir 路径下的 hello2.py 并执行它里面的 test() 方法。
再根据源代码 detect-engine-loader.c中的方法DetectLoadSigFile() 新增一个方法:DetectLoadEncryptSigFile() 用于读取加密文件:
static void GetContentFromSigFile(char *content)
{
PyObject *pReturn = NULL;
//pReturn=PyEval_CallObject(pFunc, pArgs);//调用函数,并传入参数pArgs
if (pFunc && PyCallable_Check(pFunc)) {
pReturn=PyEval_CallObject(pFunc, NULL);
if (pReturn) {
char *result = NULL;
PyArg_Parse(pReturn, "s", &result);
if (result) {
strncpy(content, result, strlen(result) > SIG_RULE_MAX_BUFFER_SIZE ? SIG_RULE_MAX_BUFFER_SIZE : strlen(result));
} else {
SCLogError(SC_ERR_OPENING_RULE_FILE, "Result parse error");
}
Py_DECREF(pReturn);
} else {
SCLogError(SC_ERR_OPENING_RULE_FILE, "Call python method failed");
}
}
}
static int my_split(char **dst, char *src, char const *sep)
{
int num = 0;
char *tmp = NULL;
for (dst[num] =strtok_r(src, sep, &tmp); dst[num] != NULL; dst[++num] = strtok_r(NULL, sep, &tmp));
return num;
}
static int DetectLoadEncryptSigFile(DetectEngineCtx *de_ctx, char *sig_file,
int *goodsigs, int *badsigs)
{
Signature *sig = NULL;
int good = 0, bad = 0;
char line[DETECT_MAX_RULE_SIZE] = "";
size_t offset = 0;
int lineno = 0, multiline = 0;
char *p[SIG_RULE_MAX_NUM];
const char *token = "\r\n";
int i;
char *content = (char *)malloc(SIG_RULE_MAX_BUFFER_SIZE);
(*goodsigs) = 0;
(*badsigs) = 0;
memset(content, 0, SIG_RULE_MAX_BUFFER_SIZE);
GetContentFromSigFile(content);
if (content == NULL) {
SCLogError(SC_ERR_OPENING_RULE_FILE, "opening rule file %s:", sig_file);
return -1;
}
int num = my_split(p, content, token);
//while(fgets(line + offset, (int)sizeof(line) - offset, fp) != NULL) {
for (i = 0; i < num; i++) {
strncpy(line + offset, p[i], (int)sizeof(line) - offset);
lineno++;
size_t len = strlen(line);
/* ignore comments and empty lines */
if (line[0] == '\n' || line [0] == '\r' || line[0] == ' ' || line[0] == '#' || line[0] == '\t')
continue;
/* Check for multiline rules. */
while (len > 0 && isspace((unsigned char)line[--len]));
if (line[len] == '\\') {
multiline++;
offset = len;
if (offset < sizeof(line) - 1) {
/* We have room for more. */
continue;
}
/* No more room in line buffer, continue, rule will fail
* to parse. */
}
/* Check if we have a trailing newline, and remove it */
len = strlen(line);
if (len > 0 && (line[len - 1] == '\n' || line[len - 1] == '\r')) {
line[len - 1] = '\0';
}
/* Reset offset. */
offset = 0;
de_ctx->rule_file = sig_file;
de_ctx->rule_line = lineno - multiline;
sig = DetectEngineAppendSig(de_ctx, line);
if (sig != NULL) {
if (rule_engine_analysis_set || fp_engine_analysis_set) {
RetrieveFPForSig(de_ctx, sig);
if (fp_engine_analysis_set) {
EngineAnalysisFP(de_ctx, sig, line);
}
if (rule_engine_analysis_set) {
EngineAnalysisRules(de_ctx, sig, line);
}
}
SCLogDebug("signature %"PRIu32" loaded", sig->id);
good++;
} else {
if (!de_ctx->sigerror_silent) {
SCLogError(SC_ERR_INVALID_SIGNATURE, "error parsing signature \"%s\" from "
"file %s at line %"PRId32"", line, sig_file, lineno - multiline);
if (!SigStringAppend(&de_ctx->sig_stat, sig_file, line, de_ctx->sigerror, (lineno - multiline))) {
SCLogError(SC_ERR_MEM_ALLOC, "Error adding sig \"%s\" from "
"file %s at line %"PRId32"", line, sig_file, lineno - multiline);
}
if (de_ctx->sigerror) {
de_ctx->sigerror = NULL;
}
}
if (rule_engine_analysis_set) {
EngineAnalysisRulesFailure(line, sig_file, lineno - multiline);
}
if (!de_ctx->sigerror_ok) {
bad++;
}
}
multiline = 0;
}
free(content);
*goodsigs = good;
*badsigs = bad;
return 0;
}
0x02 后记
本文只是抛砖引玉,hello2里面的代码读者也可以自行发挥想象力去编写,目的是读取加密的规则库。分享一下我的设计理念,这份代码使用了python调用c编译的so而已 :) 以后有机会再细讲吧~~