YARA:第十四章-基于JSON文件的威胁分析

文章目录

1.简介

2. 模块方法

3. 声明部分

4. 模块加载与退出加载

5. 模块源码

6. 模块使用

7. 缺陷与不足


1.简介

        YARA是一个用于恶意软件检测和分析的工具,它的作用主要包括以下几个方面:

        (1)恶意软件检测:YARA最核心的功能是检测恶意软件。用户可以编写规则来识别特定的恶意软件特征,如字符串、代码序列、数据结构等。

        (2)自动化威胁识别:通过预定义的规则,YARA可以自动化地识别和分类潜在的威胁,减少人工分析的需求。

        (3)文件分析:YARA可以扫描文件内容,识别出文件中可能包含的恶意代码或数据。

        (4)内存分析:YARA不仅可以分析文件,还可以分析内存中的进程,帮助检测运行中的恶意软件。

        (5)自定义规则:用户可以根据自己的需求编写自定义规则,使得YARA能够识别特定的恶意软件家族或行为模式。

        (6)模块化扩展:YARA支持模块化,用户可以编写和加载自定义模块来扩展YARA的功能,例如处理特定类型的文件或操作系统特性。

        Yara的核心优势之一在于其强大的规则定义和模块化扩展能力。这使得Yara不仅能够灵活地识别和分析各种复杂的恶意软件模式,而且还能通过用户自定义的模块轻松扩展其功能,进一步增强其在安全领域的应用能力。

        本章详细介绍了如何根据Yara模块扩展规范,开发了一个专门用于解析和检测JSON格式文件的自定义模块。JSON文件常作为应用程序的配置文件,其安全性至关重要。因此,检测这些配置文件是否遭到篡改或符合安全规范,是安全威胁分析的关键一环。

        本章参考"YARA:第十三章-编写定制化模块"的内容,开发了自定义的json模块。展示了如何利用Yara规则来检测JSON文件。该模块能够识别文件中的特定键(key)或键值对(key-value pairs),并验证键对应的值是否处于预定义的安全范围内。通过这种方式,可以有效地评估JSON配置文件的安全性,识别潜在的安全风险。

        json模块开发主要包含三个部分,分别是模块函数实现、模块声明部分和模块的加载函数与退出加载函数。

2. 模块方法

        json模块共支持三个方法:

        1、key_value:此方法接受两个字符串参数,分别代表键(key)和值(value)。它的作用是在 JSON 文件中查找与这两个参数匹配的键值对。

        2、has_key:此方法仅需要一个字符串参数,即键名。它的目的是检查 JSON 文件中是否存在指定的键名。

        3、get_value:同样接受一个字符串参数,即键名。此方法不仅检查键名是否存在,还会返回与该键名关联的值。

        三个函数的定义如下所示:

uint64_t key_value(char *key, char *value)
uint64_t has_key(char *key)
uint64_t get_value(char *key)

        三个函数的具体实现如下:

define_function(key_value) {
    json_t* json = ((CacheData*)(yr_module()->data))->json;

    if (json == NULL) {
        return ERROR_INVALID_FILE;
    }

    char* key = string_argument(1);
    char* value = string_argument(2);

    json_t* json_value = json_object_get(json, key);
    if (json_value == NULL) {
        return_integer(0);
    }

    const char* json_val = json_string_value(json_value);
    if (strcmp(json_val, value) == 0) {
        return_integer(1);
    }

    return_integer(0);
}

define_function(get_value) {
    json_t* json = ((CacheData*)(yr_module()->data))->json;

    if (json == NULL) {
        return ERROR_INVALID_FILE;
    }

    char* key = string_argument(1);

    json_t* json_value = json_object_get(json, key);
    if (json_value == NULL) {
        return_integer(0);
    }

    json_int_t json_val = json_integer_value(json_value);

    return_integer(json_val);
}


define_function(has_key) {
    json_t* json = ((CacheData*)(yr_module()->data))->json;

    if (json == NULL) {
        return ERROR_INVALID_FILE;
    }

    char* key = string_argument(1);
    json_t* json_value = json_object_get(json, key);

    if (json_value == NULL) {
        return_integer(0);
    }
    return_integer(1);
}

3. 声明部分

        json模块的声明部分主要是对支持的三个方法的声明,实现如下:

begin_declarations
    declare_function("kv", "ss", "i", key_value);
    declare_function("has_key", "s", "i", has_key);
    declare_function("get_value", "s", "i", get_value);
end_declarations

4. 模块加载与退出加载

        模块加载函数的核心作用是初始化JSON文件的处理过程。它首先读取JSON文件的内容,然后利用Jansson库对这些内容进行解析,生成相应的JSON对象。为了有效管理这些数据,我们定义了一个名为CacheData的结构体。该结构体专门设计用于在模块中存储文件内容和解析后的JSON对象,为后续的分析函数提供必要的数据支持。

        在模块的生命周期结束时,退出加载函数扮演着至关重要的角色。它负责释放之前分配的所有资源,包括存储在CacheData结构体中的文件内容和JSON对象。这一步骤确保了程序能够安全、干净地退出,避免内存泄漏和其他潜在的资源管理问题。通过这种方式,我们保证了程序的健壮性和可靠性。

        下面是json模块中加载函数与退出加载函数的实现:

int module_load(
    YR_SCAN_CONTEXT* context,
    YR_OBJECT* module_object,
    void* module_data,
    size_t module_data_size)
{
    YR_MEMORY_BLOCK* block;
    const uint8_t* block_data;

    json_error_t json_error;
    json_t* json;

    char* ptr;

    uint64_t file_size = context->file_size;

    if (file_size == 0)
        return ERROR_SUCCESS;

    module_object->data = yr_malloc(sizeof(CacheData));
    if (module_object->data == NULL) 
        return ERROR_INSUFFICIENT_MEMORY;

    ((CacheData*)module_object->data)->data = yr_malloc(file_size);
    
    if (((CacheData*)(module_object->data))->data == NULL)
        return ERROR_INSUFFICIENT_MEMORY;
    ((CacheData*)(module_object->data))->size = file_size;
    ptr = ((CacheData*)(module_object->data))->data;

    foreach_memory_block(context->iterator, block)
    {
        block_data = block->fetch_data(block);
        if (block_data == NULL || block->size == 0)
            continue;

        memcpy(ptr, (char *)block_data, block->size);
        ptr += block->size;
    }

    json = json_loads(((CacheData*)(module_object->data))->data, 0, &json_error);
    ((CacheData*)(module_object->data))->json = json;
    return ERROR_SUCCESS;
}


int module_unload(YR_OBJECT* module_object)
{

  if(module_object->data != NULL) {
      if (((CacheData*)(module_object->data))->data != NULL) {
          yr_free(((CacheData*)(module_object->data))->data);
      }
      json_decref(((CacheData*)(module_object->data))->json);
      yr_free(module_object->data);
  }
  return ERROR_SUCCESS;
}

5. 模块源码

        下面是整个json模块的源码。自定义模块的编译以及集成步骤可参考YARA:第十三章-编写定制化模块章节内容。除此之外,由于我们使用了libjansson库中函数来解析json文件,因此需要在Makefile文件中的"LIBS"变量后面加上-ljansson来完成编译。

#include <yara/modules.h>
#include <jansson.h>
#include <yara/mem.h>

#define MODULE_NAME json

typedef struct _CacheData {
    char* data;
    int32_t size;
    json_t* json;
}CacheData;


define_function(key_value) {
    json_t* json = ((CacheData*)(yr_module()->data))->json;

    if (json == NULL) {
        return ERROR_INVALID_FILE;
    }

    char* key = string_argument(1);
    char* value = string_argument(2);

    json_t* json_value = json_object_get(json, key);
    if (json_value == NULL) {
        return_integer(0);
    }

    const char* json_val = json_string_value(json_value);
    if (strcmp(json_val, value) == 0) {
        return_integer(1);
    }

    return_integer(0);
}

define_function(get_value) {
    json_t* json = ((CacheData*)(yr_module()->data))->json;

    if (json == NULL) {
        return ERROR_INVALID_FILE;
    }

    char* key = string_argument(1);

    json_t* json_value = json_object_get(json, key);
    if (json_value == NULL) {
        return_integer(0);
    }

    json_int_t json_val = json_integer_value(json_value);

    return_integer(json_val);
}


define_function(has_key) {
    json_t* json = ((CacheData*)(yr_module()->data))->json;

    if (json == NULL) {
        return ERROR_INVALID_FILE;
    }

    char* key = string_argument(1);
    json_t* json_value = json_object_get(json, key);

    if (json_value == NULL) {
        return_integer(0);
    }
    return_integer(1);
}

begin_declarations
    declare_function("kv", "ss", "i", key_value);
    declare_function("has_key", "s", "i", has_key);
    declare_function("get_value", "s", "i", get_value);
end_declarations
 
int module_initialize(YR_MODULE* module)
{
  return ERROR_SUCCESS;
}


int module_finalize(YR_MODULE* module)
{
  return ERROR_SUCCESS;
}


int module_load(
    YR_SCAN_CONTEXT* context,
    YR_OBJECT* module_object,
    void* module_data,
    size_t module_data_size)
{
    YR_MEMORY_BLOCK* block;
    const uint8_t* block_data;

    json_error_t json_error;
    json_t* json;

    char* ptr;

    uint64_t file_size = context->file_size;

    if (file_size == 0)
        return ERROR_SUCCESS;

    module_object->data = yr_malloc(sizeof(CacheData));
    if (module_object->data == NULL) 
        return ERROR_INSUFFICIENT_MEMORY;

    ((CacheData*)module_object->data)->data = yr_malloc(file_size);
    
    if (((CacheData*)(module_object->data))->data == NULL)
        return ERROR_INSUFFICIENT_MEMORY;
    ((CacheData*)(module_object->data))->size = file_size;
    ptr = ((CacheData*)(module_object->data))->data;

    foreach_memory_block(context->iterator, block)
    {
        block_data = block->fetch_data(block);
        if (block_data == NULL || block->size == 0)
            continue;

        memcpy(ptr, (char *)block_data, block->size);
        ptr += block->size;
    }

    json = json_loads(((CacheData*)(module_object->data))->data, 0, &json_error);
    ((CacheData*)(module_object->data))->json = json;
    return ERROR_SUCCESS;
}


int module_unload(YR_OBJECT* module_object)
{

  if(module_object->data != NULL) {
      if (((CacheData*)(module_object->data))->data != NULL) {
          yr_free(((CacheData*)(module_object->data))->data);
      }
      json_decref(((CacheData*)(module_object->data))->json);
      yr_free(module_object->data);
  }
  return ERROR_SUCCESS;
}

6. 模块使用

        创建一个json格式的文件作为Yara检测的目标。例如:

// test_json.json
{
    "name": "John Doe",
    "age": 30,
    "is_student": false,
    "email": "john.doe@example.com",
    "skills": ["Python", "JavaScript", "Machine Learning"],
    "address": {
      "street": "123 Main St",
      "city": "Anytown",
      "zip": "12345"
    }
}

        编写Yara规则,规则内容如下:

// json_test.yar
import "console"
import "json"

rule json_rule
{
    meta:
        description = "json module test"

    condition:
       // console.log(json.has_key("name"))   //  终端打印 1
       // console.log(json.get_value("age"))  //  终端打印30
       console.log(json.kv("name", "John Doe"))  // 终端打印1
}

        执行检测的命令是:

yara json_test.yar test_json.json

7. 缺陷与不足

        由于本篇文章中介绍的json模块主要是验证YARA:第十三章-编写定制化模块文章中介绍的自定义模块开发的可行性,因此json模块还有很多不足以缺陷,例如在module_load中应该添加json文件格式的识别,模块支持的方法中键和值的参数类型应该更加丰富等。

        本篇文章旨在验证YARA:第十三章-编写定制化模块文章中介绍的YARA自定义模块开发的可行性,特别是通过实现一个JSON模块来展示这一过程。尽管这一模块在基本功能上已经能够满足需求,但在深入分析和实际应用中,我们发现它仍有一些需要改进的地方。以下是一些关键的优化建议:

        1、增强文件格式识别:在module_load函数中,应增加对JSON文件格式的自动识别机制,确保模块能够正确处理各种格式的JSON数据。

        2、参数类型多样化:目前模块支持的方法在处理键(key)和值(value)时,参数类型较为单一。为了提高模块的灵活性和适用性,建议扩展这些方法,使其能够支持更丰富的数据类型,如数组、对象等。

        3、错误处理机制:在处理JSON文件时,可能会遇到格式错误或解析失败的情况。因此,增强错误处理机制,提供清晰的错误信息和恢复策略,将有助于提高模块的稳定性和用户体验。

        4、性能优化:随着JSON文件大小的增加,解析和处理的效率可能会受到影响。因此,考虑对现有算法进行优化,或者引入更高效的解析技术,以提升模块在处理大型JSON文件时的性能。

通过这些改进,JSON模块将更加健壮、灵活,并能够更好地适应不同的应用场景和需求。


         如果您觉得这篇文章对您有所帮助,或者在阅读过程中有所启发,我将非常感激您的支持。您的打赏不仅是对我努力的认可,也是对知识分享精神的一种鼓励。如果您愿意,可以通过以下方式给予支持:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_只道当时是寻常

打赏不得超过工资的一半呦

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值