mirror of https://github.com/OISF/suricata
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
108 lines
5.2 KiB
ReStructuredText
108 lines
5.2 KiB
ReStructuredText
Detection Inspection Tracing
|
|
============================
|
|
|
|
The content-inspection engine (``DetectEngineContentInspection``) can emit a
|
|
human-readable trace of every keyword it evaluates against a buffer. For each
|
|
inspected keyword it prints the signature being evaluated, the keyword and its
|
|
parameters, and -- on a match or a definitive no-match -- a hexdump of the
|
|
buffer windowed around the current inspection offset, with the bytes the
|
|
keyword matched called out in the dump. It is a developer aid for answering
|
|
"why did (or didn't) this rule match this traffic?".
|
|
|
|
The facility is intended for interactive debugging with a single pcap and a
|
|
small rule set. It writes directly to ``stdout`` and is not meant for
|
|
production traffic.
|
|
|
|
Enabling
|
|
--------
|
|
|
|
Tracing is compiled in only when Suricata is configured with::
|
|
|
|
./configure --enable-detect-trace
|
|
|
|
This defines the ``DETECT_TRACE`` preprocessor macro. When the macro is not
|
|
defined the trace call sites expand to nothing, so a normal build carries no
|
|
overhead at all.
|
|
|
|
Even in a trace-enabled build the output stays off until it is switched on at
|
|
runtime through an environment variable::
|
|
|
|
SURICATA_DETECT_TRACE=1 suricata -S rules.rules -r input.pcap -k none -l ./log
|
|
|
|
When the variable is unset, each trace call is guarded by a single predictable
|
|
branch, so a trace-enabled build can be used normally with negligible cost.
|
|
|
|
Output
|
|
------
|
|
|
|
Each inspected keyword produces a block such as::
|
|
|
|
------------------------------------------------------------------------------
|
|
TRACE (4) MSG: trace demo deep body
|
|
TRACE (4) Mode: state AppProto: http_any Buffer: file_data Match Type: content Recursion: 1/3000
|
|
TRACE (4) Inspecting content:"</html>" (len 7)
|
|
TRACE (4) -> MATCH! <- (content)
|
|
TRACE (4) post-detect offset: 4259
|
|
TRACE buffer window +4000..+4259 of 4259 (match +4252..+4259, cursor +4259 past end):
|
|
00000FA0 3E 20 6C 6F 77 3C 62 72 20 2F 3E 3C 65 6D 3E 50 > low<br /><em>P
|
|
...
|
|
00001080 0D 0A 0D 0A 09 09 3C 2F 64 69 76 3E 0D 0A 0D 0A ......</ div>....
|
|
>00001090 09 3C 2F 62 6F 64 79 3E 0D 0A 0D 0A 3C 2F 68 74 .</body> ....</ht
|
|
>000010A0 6D 6C 3E ml>
|
|
|
|
Reading the dump
|
|
~~~~~~~~~~~~~~~~
|
|
|
|
Row offsets are absolute within the inspected buffer, so they can be compared
|
|
directly against the ``post-detect offset`` and against the ``offset``,
|
|
``depth`` and ``distance`` values printed for the keyword. Only a window around
|
|
the current offset is dumped -- 256 bytes on either side -- so that a large
|
|
stream or body buffer does not flood the terminal; the header line gives the
|
|
window bounds and the buffer's full length.
|
|
|
|
Two things are called out in the dump. The bytes the keyword just matched are
|
|
shown as a span, and the offset the next keyword will start from is shown as a
|
|
single cursor byte. They are distinct because a content match leaves the
|
|
inspection offset just *past* the bytes it matched, so ``match +4252..+4259``
|
|
and ``cursor +4259`` in the example above describe the seven bytes of
|
|
``</html>`` and the position after them. A match span is only reported for a
|
|
positive, non-negated ``content``; every other keyword, and every no-match,
|
|
leaves the offset as a bare cursor with no span to show. Rows containing either
|
|
marker carry a ``>`` in the left gutter.
|
|
|
|
When ``stdout`` is a terminal the span and the cursor are colored, in both the
|
|
hex and the ascii column. Otherwise -- output redirected to a file or a pager,
|
|
or ``NO_COLOR`` set in the environment -- the whole trace is emitted without
|
|
escape sequences and the markers appear on a row of their own beneath the hex
|
|
bytes, with ``^`` under the matched span and ``*`` under the cursor::
|
|
|
|
TRACE buffer window +0..+27 of 27 (cursor +10) [^ match, * cursor]:
|
|
>00000000 2F 66 69 6C 65 32 70 63 61 70 2F 62 6F 64 79 74 /file2pc ap/bodyt
|
|
**
|
|
00000010 65 78 74 74 65 73 74 2E 74 78 74 exttest. txt
|
|
|
|
The ``Buffer`` name is only shown when Suricata is additionally built with
|
|
``--enable-profiling`` (the current inspection buffer id is only tracked on the
|
|
thread context in profiling builds); otherwise it is reported as ``(null)``.
|
|
|
|
Per-keyword parameter detail is printed for ``content``, ``pcre``, ``isdataat``,
|
|
``byte_test``, ``byte_jump`` and ``base64_decode``. Other keywords are still
|
|
traced by name via the ``Match Type`` field.
|
|
|
|
Implementation
|
|
--------------
|
|
|
|
All of the tracing lives in ``src/detect-engine-inspect-trace.{c,h}`` and is
|
|
invoked from ``DetectEngineContentInspection`` through the ``DETECT_TRACE_*``
|
|
macros, which are no-ops unless ``DETECT_TRACE`` is defined. To keep the trace
|
|
readable, ``content`` values are rendered from the parsed ``DetectContentData``
|
|
(no signature re-parsing), and the original ``pcre`` pattern text is retained on
|
|
the ``DetectParseRegex`` only in trace builds.
|
|
|
|
The dump is written by ``DetectTraceHexdump()`` rather than by the shared
|
|
``PrintRawDataFp()`` helper, since it needs absolute row offsets and per-byte
|
|
markers that the shared dumper does not provide. Color is decided once in
|
|
``DetectTraceInit()``: the ANSI sequences are held in file-scope pointers that
|
|
stay empty strings unless ``stdout`` is a tty and ``NO_COLOR`` is unset, so the
|
|
same format strings produce both forms of output.
|