Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions doc/userguide/rules/payload-keywords.rst
Original file line number Diff line number Diff line change
Expand Up @@ -542,6 +542,8 @@ byte_jump
---------

The ``byte_jump`` keyword allows for the ability to select a ``<num of bytes>`` from an ``<offset>`` and moves the detection pointer to that position. Content matches will then be based off the new position.
The ``bitmask`` value is applied to the extracted value before the multiplier. Additionally, the result of the bitmask operation is
right-shifted by the number of trailing zeroes in the bitmask value.

Format::

Expand Down
142 changes: 128 additions & 14 deletions src/detect-bytejump.c
Original file line number Diff line number Diff line change
Expand Up @@ -47,18 +47,20 @@
/**
* \brief Regex for parsing our options
*/
#define PARSE_REGEX "^\\s*" \
"([^\\s,]+\\s*,\\s*[^\\s,]+)" \
"(?:\\s*,\\s*((?:multiplier|post_offset)\\s+[^\\s,]+|[^\\s,]+))?" \
"(?:\\s*,\\s*((?:multiplier|post_offset)\\s+[^\\s,]+|[^\\s,]+))?" \
"(?:\\s*,\\s*((?:multiplier|post_offset)\\s+[^\\s,]+|[^\\s,]+))?" \
"(?:\\s*,\\s*((?:multiplier|post_offset)\\s+[^\\s,]+|[^\\s,]+))?" \
"(?:\\s*,\\s*((?:multiplier|post_offset)\\s+[^\\s,]+|[^\\s,]+))?" \
"(?:\\s*,\\s*((?:multiplier|post_offset)\\s+[^\\s,]+|[^\\s,]+))?" \
"(?:\\s*,\\s*((?:multiplier|post_offset)\\s+[^\\s,]+|[^\\s,]+))?" \
"(?:\\s*,\\s*((?:multiplier|post_offset)\\s+[^\\s,]+|[^\\s,]+))?" \
"(?:\\s*,\\s*((?:multiplier|post_offset)\\s+[^\\s,]+|[^\\s,]+))?" \
"\\s*$"
#define PARSE_REGEX \
"^\\s*" \
"([^\\s,]+\\s*,\\s*[^\\s,]+)" \
"(?:\\s*,\\s*((?:multiplier|post_offset|bitmask)\\s+[^\\s,]+|[^\\s,]+))?" \
"(?:\\s*,\\s*((?:multiplier|post_offset|bitmask)\\s+[^\\s,]+|[^\\s,]+))?" \
"(?:\\s*,\\s*((?:multiplier|post_offset|bitmask)\\s+[^\\s,]+|[^\\s,]+))?" \
"(?:\\s*,\\s*((?:multiplier|post_offset|bitmask)\\s+[^\\s,]+|[^\\s,]+))?" \
"(?:\\s*,\\s*((?:multiplier|post_offset|bitmask)\\s+[^\\s,]+|[^\\s,]+))?" \
"(?:\\s*,\\s*((?:multiplier|post_offset|bitmask)\\s+[^\\s,]+|[^\\s,]+))?" \
"(?:\\s*,\\s*((?:multiplier|post_offset|bitmask)\\s+[^\\s,]+|[^\\s,]+))?" \
"(?:\\s*,\\s*((?:multiplier|post_offset|bitmask)\\s+[^\\s,]+|[^\\s,]+))?" \
"(?:\\s*,\\s*((?:multiplier|post_offset|bitmask)\\s+[^\\s,]+|[^\\s,]+))?" \
"(?:\\s*,\\s*((?:multiplier|post_offset|bitmask)\\s+[^\\s,]+|[^\\s,]+))?" \
"\\s*$"

static DetectParseRegex parse_regex;

Expand Down Expand Up @@ -209,13 +211,27 @@ bool DetectBytejumpDoMatch(DetectEngineThreadCtx *det_ctx, const Signature *s,
SCLogDebug("VAL: (%" PRIu64 " x %" PRIu32 ") + %" PRIi32 " + %" PRId32, val, data->multiplier,
extbytes, data->post_offset);

/* Apply the bitmask value and bitmask shift count; note, the bitmask is applied
* before the multiplier per snort */
if (data->flags & DETECT_BYTEJUMP_BITMASK) {
SCLogDebug("[before bitmask] val: %" PRIi64 " bitmask_value: %" PRIx32, val,
data->bitmask_value);
val &= data->bitmask_value;
if (val && data->bitmask_shift_count) {
val = val >> data->bitmask_shift_count;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is wrong, not the value should be shifted, but bitmask_value

Copy link
Contributor Author

@jlucovsky jlucovsky Jan 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The value val is being right shifted by the according to the bitmask_shift_count calculated during setup. During setup, the bitmask value is right shifted to count the number of trailing 0 bits.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I leave this for others to review as this shift does not make any sense to me

}
SCLogDebug("[after bitmask] val: %" PRIi64 " bitmask_value: %" PRIx32, val,
data->bitmask_value);
}

/* Adjust the jump value based on flags */
val *= data->multiplier;
if (flags & DETECT_BYTEJUMP_ALIGN) {
if ((val % 4) != 0) {
val += 4 - (val % 4);
}
}

val += data->post_offset;
SCLogDebug("val: %" PRIi64 " post_offset: %" PRIi32, val, data->post_offset);

Expand Down Expand Up @@ -263,7 +279,7 @@ static DetectBytejumpData *DetectBytejumpParse(
DetectEngineCtx *de_ctx, const char *optstr, char **nbytes_str, char **offset)
{
DetectBytejumpData *data = NULL;
char args[10][64];
char args[11][64];
int res = 0;
size_t pcre2len;
int numargs = 0;
Expand All @@ -277,7 +293,7 @@ static DetectBytejumpData *DetectBytejumpParse(

/* Execute the regex and populate args with captures. */
int ret = DetectParsePcreExec(&parse_regex, &match, optstr, 0, 0);
if (ret < 2 || ret > 10) {
if (ret < 2 || ret > 11) {
SCLogError("parse error, ret %" PRId32 ", string \"%s\"", ret, optstr);
goto error;
}
Expand Down Expand Up @@ -417,6 +433,17 @@ static DetectBytejumpData *DetectBytejumpParse(
goto error;
}
SCLogDebug("post_offset: %s [%d]", optstr, data->post_offset);
} else if (strncasecmp("bitmask ", args[i], 8) == 0) {
data->flags |= DETECT_BYTEJUMP_BITMASK;
if (ByteExtractStringUint32(&data->bitmask_value, 0, 0, args[i] + strlen("bitmask ")) <=
0) {
SCLogError("Malformed bitmask: %s", optstr);
goto error;
}
if (data->bitmask_value == 0) {
SCLogError("Invalid bitmask value: %d", data->bitmask_value);
goto error;
}
} else if (strcasecmp("dce", args[i]) == 0) {
data->flags |= DETECT_BYTEJUMP_DCE;
} else {
Expand Down Expand Up @@ -448,6 +475,17 @@ static DetectBytejumpData *DetectBytejumpParse(
}
}

data->bitmask_shift_count = 0;
if (data->flags & DETECT_BYTEJUMP_BITMASK) {
uint32_t bmask = data->bitmask_value;
while (!(bmask & 0x1)) {
bmask = bmask >> 1;
data->bitmask_shift_count++;
}
SCLogDebug("bitmask value 0x%x -> shift value %d", data->bitmask_value,
data->bitmask_shift_count);
}

pcre2_match_data_free(match);
return data;

Expand Down Expand Up @@ -1004,6 +1042,80 @@ static int DetectBytejumpTestParse14(void)
PASS;
}

static int DetectBytejumpTestParse15(void)
{
DetectEngineCtx *de_ctx = DetectEngineCtxInit();
FAIL_IF_NULL(de_ctx);
de_ctx->flags |= DE_QUIET;

Signature *s =
DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any "
"(msg:\"Testing bytejump_body\"; "
"dce_iface:3919286a-b10c-11d0-9ba8-00c04fd92ef5; "
"dce_stub_data; "
"content:\"one\"; distance:0; "
"byte_jump:4,0,align,multiplier 2, "
"post_offset -16,relative,bitmask 0x8f40,dce; sid:1;)");
FAIL_IF_NULL(s);
SigMatch *sm = DetectBufferGetFirstSigMatch(s, g_dce_stub_data_buffer_id);
FAIL_IF_NULL(sm);
FAIL_IF_NOT(sm->type == DETECT_CONTENT);
FAIL_IF_NULL(sm->next);
sm = sm->next;
FAIL_IF_NOT(sm->type == DETECT_BYTEJUMP);

DetectBytejumpData *bd = (DetectBytejumpData *)sm->ctx;
FAIL_IF_NOT(bd->flags & DETECT_BYTEJUMP_DCE);
FAIL_IF_NOT(bd->flags & DETECT_BYTEJUMP_RELATIVE);
FAIL_IF(bd->flags & DETECT_BYTEJUMP_STRING);
FAIL_IF(bd->flags & DETECT_BYTEJUMP_BIG);
FAIL_IF(bd->flags & DETECT_BYTEJUMP_LITTLE);

FAIL_IF_NOT(bd->flags & DETECT_BYTEJUMP_BITMASK);
FAIL_IF_NOT(bd->bitmask_value == 0x8f40);
FAIL_IF_NOT(bd->bitmask_shift_count == 6);

DetectEngineCtxFree(de_ctx);
PASS;
}

static int DetectBytejumpTestParse16(void)
{
DetectEngineCtx *de_ctx = DetectEngineCtxInit();
FAIL_IF_NULL(de_ctx);
de_ctx->flags |= DE_QUIET;

Signature *s =
DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any "
"(msg:\"Testing bytejump_body\"; "
"dce_iface:3919286a-b10c-11d0-9ba8-00c04fd92ef5; "
"dce_stub_data; "
"content:\"one\"; distance:0; "
"byte_jump:4,0,align,multiplier 2, "
"post_offset -16,relative,bitmask 5304,dce; sid:1;)");
FAIL_IF_NULL(s);
SigMatch *sm = DetectBufferGetFirstSigMatch(s, g_dce_stub_data_buffer_id);
FAIL_IF_NULL(sm);
FAIL_IF_NOT(sm->type == DETECT_CONTENT);
FAIL_IF_NULL(sm->next);
sm = sm->next;
FAIL_IF_NOT(sm->type == DETECT_BYTEJUMP);

DetectBytejumpData *bd = (DetectBytejumpData *)sm->ctx;
FAIL_IF_NOT(bd->flags & DETECT_BYTEJUMP_DCE);
FAIL_IF_NOT(bd->flags & DETECT_BYTEJUMP_RELATIVE);
FAIL_IF(bd->flags & DETECT_BYTEJUMP_STRING);
FAIL_IF(bd->flags & DETECT_BYTEJUMP_BIG);
FAIL_IF(bd->flags & DETECT_BYTEJUMP_LITTLE);

FAIL_IF_NOT(bd->flags & DETECT_BYTEJUMP_BITMASK);
FAIL_IF_NOT(bd->bitmask_value == 5304);
FAIL_IF_NOT(bd->bitmask_shift_count == 3);

DetectEngineCtxFree(de_ctx);
PASS;
}

/**
* \test DetectByteJumpTestPacket01 is a test to check matches of
* byte_jump and byte_jump relative works if the previous keyword is pcre
Expand Down Expand Up @@ -1215,6 +1327,8 @@ static void DetectBytejumpRegisterTests(void)
UtRegisterTest("DetectBytejumpTestParse12", DetectBytejumpTestParse12);
UtRegisterTest("DetectBytejumpTestParse13", DetectBytejumpTestParse13);
UtRegisterTest("DetectBytejumpTestParse14", DetectBytejumpTestParse14);
UtRegisterTest("DetectBytejumpTestParse15", DetectBytejumpTestParse15);
UtRegisterTest("DetectBytejumpTestParse16", DetectBytejumpTestParse16);

UtRegisterTest("DetectByteJumpTestPacket01", DetectByteJumpTestPacket01);
UtRegisterTest("DetectByteJumpTestPacket02", DetectByteJumpTestPacket02);
Expand Down
3 changes: 3 additions & 0 deletions src/detect-bytejump.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
#define DETECT_BYTEJUMP_END BIT_U16(8) /**< "from_end" jump */
#define DETECT_BYTEJUMP_NBYTES_VAR BIT_U16(9) /**< nbytes string*/
#define DETECT_BYTEJUMP_OFFSET_VAR BIT_U16(10) /**< byte extract value enabled */
#define DETECT_BYTEJUMP_BITMASK BIT_U16(11) /**< bitmask value */

typedef struct DetectBytejumpData_ {
uint8_t nbytes; /**< Number of bytes to compare */
Expand All @@ -50,6 +51,8 @@ typedef struct DetectBytejumpData_ {
int32_t offset; /**< Offset in payload to extract value */
int32_t post_offset; /**< Offset to adjust post-jump */
uint16_t multiplier; /**< Multiplier for nbytes (multiplier n)*/
uint32_t bitmask_value; /**< bitmask value */
uint32_t bitmask_shift_count; /**< bitmask shift value*/
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

u8 should be enough as it is < 32 ;-)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will change.

} DetectBytejumpData;

/* prototypes */
Expand Down
Loading