Coverage Report

Created: 2025-08-29 07:08

/src/yara/libyara/rules.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
Copyright (c) 2013. The YARA Authors. All Rights Reserved.
3
4
Redistribution and use in source and binary forms, with or without modification,
5
are permitted provided that the following conditions are met:
6
7
1. Redistributions of source code must retain the above copyright notice, this
8
list of conditions and the following disclaimer.
9
10
2. Redistributions in binary form must reproduce the above copyright notice,
11
this list of conditions and the following disclaimer in the documentation and/or
12
other materials provided with the distribution.
13
14
3. Neither the name of the copyright holder nor the names of its contributors
15
may be used to endorse or promote products derived from this software without
16
specific prior written permission.
17
18
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
22
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
25
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
*/
29
30
#include <assert.h>
31
#include <ctype.h>
32
#include <string.h>
33
#include <yara/compiler.h>
34
#include <yara/error.h>
35
#include <yara/filemap.h>
36
#include <yara/globals.h>
37
#include <yara/mem.h>
38
#include <yara/proc.h>
39
#include <yara/rules.h>
40
#include <yara/scan.h>
41
#include <yara/scanner.h>
42
#include <yara/utils.h>
43
44
YR_API int yr_rules_define_integer_variable(
45
    YR_RULES* rules,
46
    const char* identifier,
47
    int64_t value)
48
0
{
49
0
  YR_EXTERNAL_VARIABLE* external;
50
51
0
  if (identifier == NULL)
52
0
    return ERROR_INVALID_ARGUMENT;
53
54
0
  external = rules->ext_vars_table;
55
56
0
  while (!EXTERNAL_VARIABLE_IS_NULL(external))
57
0
  {
58
0
    if (strcmp(external->identifier, identifier) == 0)
59
0
    {
60
0
      if (external->type != EXTERNAL_VARIABLE_TYPE_INTEGER)
61
0
        return ERROR_INVALID_EXTERNAL_VARIABLE_TYPE;
62
63
0
      external->value.i = value;
64
0
      return ERROR_SUCCESS;
65
0
    }
66
67
0
    external++;
68
0
  }
69
70
0
  return ERROR_INVALID_ARGUMENT;
71
0
}
72
73
YR_API int yr_rules_define_boolean_variable(
74
    YR_RULES* rules,
75
    const char* identifier,
76
    int value)
77
0
{
78
0
  YR_EXTERNAL_VARIABLE* external;
79
80
0
  if (identifier == NULL)
81
0
    return ERROR_INVALID_ARGUMENT;
82
83
0
  external = rules->ext_vars_table;
84
85
0
  while (!EXTERNAL_VARIABLE_IS_NULL(external))
86
0
  {
87
0
    if (strcmp(external->identifier, identifier) == 0)
88
0
    {
89
0
      if (external->type != EXTERNAL_VARIABLE_TYPE_BOOLEAN)
90
0
        return ERROR_INVALID_EXTERNAL_VARIABLE_TYPE;
91
92
0
      external->value.i = value;
93
0
      return ERROR_SUCCESS;
94
0
    }
95
96
0
    external++;
97
0
  }
98
99
0
  return ERROR_INVALID_ARGUMENT;
100
0
}
101
102
YR_API int yr_rules_define_float_variable(
103
    YR_RULES* rules,
104
    const char* identifier,
105
    double value)
106
0
{
107
0
  YR_EXTERNAL_VARIABLE* external;
108
109
0
  if (identifier == NULL)
110
0
    return ERROR_INVALID_ARGUMENT;
111
112
0
  external = rules->ext_vars_table;
113
114
0
  while (!EXTERNAL_VARIABLE_IS_NULL(external))
115
0
  {
116
0
    if (strcmp(external->identifier, identifier) == 0)
117
0
    {
118
0
      if (external->type != EXTERNAL_VARIABLE_TYPE_FLOAT)
119
0
        return ERROR_INVALID_EXTERNAL_VARIABLE_TYPE;
120
121
0
      external->value.f = value;
122
0
      return ERROR_SUCCESS;
123
0
    }
124
125
0
    external++;
126
0
  }
127
128
0
  return ERROR_INVALID_ARGUMENT;
129
0
}
130
131
YR_API int yr_rules_define_string_variable(
132
    YR_RULES* rules,
133
    const char* identifier,
134
    const char* value)
135
0
{
136
0
  YR_EXTERNAL_VARIABLE* external;
137
138
0
  if (identifier == NULL || value == NULL)
139
0
    return ERROR_INVALID_ARGUMENT;
140
141
0
  external = rules->ext_vars_table;
142
143
0
  while (!EXTERNAL_VARIABLE_IS_NULL(external))
144
0
  {
145
0
    if (strcmp(external->identifier, identifier) == 0)
146
0
    {
147
0
      if (external->type != EXTERNAL_VARIABLE_TYPE_STRING &&
148
0
          external->type != EXTERNAL_VARIABLE_TYPE_MALLOC_STRING)
149
0
        return ERROR_INVALID_EXTERNAL_VARIABLE_TYPE;
150
151
0
      if (external->type == EXTERNAL_VARIABLE_TYPE_MALLOC_STRING &&
152
0
          external->value.s != NULL)
153
0
      {
154
0
        yr_free(external->value.s);
155
0
      }
156
157
0
      external->type = EXTERNAL_VARIABLE_TYPE_MALLOC_STRING;
158
0
      external->value.s = yr_strdup(value);
159
160
0
      if (external->value.s == NULL)
161
0
        return ERROR_INSUFFICIENT_MEMORY;
162
0
      else
163
0
        return ERROR_SUCCESS;
164
0
    }
165
166
0
    external++;
167
0
  }
168
169
0
  return ERROR_INVALID_ARGUMENT;
170
0
}
171
172
YR_API int yr_rules_scan_mem_blocks(
173
    YR_RULES* rules,
174
    YR_MEMORY_BLOCK_ITERATOR* iterator,
175
    int flags,
176
    YR_CALLBACK_FUNC callback,
177
    void* user_data,
178
    int timeout)
179
0
{
180
0
  YR_SCANNER* scanner;
181
0
  int result;
182
183
0
  FAIL_ON_ERROR(yr_scanner_create(rules, &scanner));
184
185
0
  yr_scanner_set_callback(scanner, callback, user_data);
186
0
  yr_scanner_set_timeout(scanner, timeout);
187
0
  yr_scanner_set_flags(scanner, flags);
188
189
0
  result = yr_scanner_scan_mem_blocks(scanner, iterator);
190
191
0
  yr_scanner_destroy(scanner);
192
193
0
  return result;
194
0
}
195
196
YR_API int yr_rules_scan_mem(
197
    YR_RULES* rules,
198
    const uint8_t* buffer,
199
    size_t buffer_size,
200
    int flags,
201
    YR_CALLBACK_FUNC callback,
202
    void* user_data,
203
    int timeout)
204
26.6k
{
205
26.6k
  YR_DEBUG_FPRINTF(
206
26.6k
      2,
207
26.6k
      stderr,
208
26.6k
      "+ %s(buffer=%p buffer_size=%zu timeout=%d) {\n",
209
26.6k
      __FUNCTION__,
210
26.6k
      buffer,
211
26.6k
      buffer_size,
212
26.6k
      timeout);
213
214
26.6k
  YR_SCANNER* scanner;
215
26.6k
  int result = ERROR_INTERNAL_FATAL_ERROR;
216
217
26.6k
  GOTO_EXIT_ON_ERROR(yr_scanner_create(rules, &scanner));
218
219
26.6k
  yr_scanner_set_callback(scanner, callback, user_data);
220
26.6k
  yr_scanner_set_timeout(scanner, timeout);
221
26.6k
  yr_scanner_set_flags(scanner, flags);
222
223
26.6k
  result = yr_scanner_scan_mem(scanner, buffer, buffer_size);
224
225
26.6k
  yr_scanner_destroy(scanner);
226
227
26.6k
_exit:
228
229
26.6k
  YR_DEBUG_FPRINTF(
230
26.6k
      2,
231
26.6k
      stderr,
232
26.6k
      ""
233
26.6k
      "} = %d AKA %s // %s()\n",
234
26.6k
      result,
235
26.6k
      yr_debug_error_as_string(result),
236
26.6k
      __FUNCTION__);
237
238
26.6k
  return result;
239
26.6k
}
240
241
YR_API int yr_rules_scan_file(
242
    YR_RULES* rules,
243
    const char* filename,
244
    int flags,
245
    YR_CALLBACK_FUNC callback,
246
    void* user_data,
247
    int timeout)
248
0
{
249
0
  YR_MAPPED_FILE mfile;
250
251
0
  int result = yr_filemap_map(filename, &mfile);
252
253
0
  if (result == ERROR_SUCCESS)
254
0
  {
255
0
    result = yr_rules_scan_mem(
256
0
        rules, mfile.data, mfile.size, flags, callback, user_data, timeout);
257
258
0
    yr_filemap_unmap(&mfile);
259
0
  }
260
261
0
  return result;
262
0
}
263
264
YR_API int yr_rules_scan_fd(
265
    YR_RULES* rules,
266
    YR_FILE_DESCRIPTOR fd,
267
    int flags,
268
    YR_CALLBACK_FUNC callback,
269
    void* user_data,
270
    int timeout)
271
0
{
272
0
  YR_MAPPED_FILE mfile;
273
274
0
  int result = yr_filemap_map_fd(fd, 0, 0, &mfile);
275
276
0
  if (result == ERROR_SUCCESS)
277
0
  {
278
0
    result = yr_rules_scan_mem(
279
0
        rules, mfile.data, mfile.size, flags, callback, user_data, timeout);
280
281
0
    yr_filemap_unmap_fd(&mfile);
282
0
  }
283
284
0
  return result;
285
0
}
286
287
YR_API int yr_rules_scan_proc(
288
    YR_RULES* rules,
289
    int pid,
290
    int flags,
291
    YR_CALLBACK_FUNC callback,
292
    void* user_data,
293
    int timeout)
294
0
{
295
0
  YR_DEBUG_FPRINTF(
296
0
      2, stderr, "+ %s(pid=%d timeout=%d) {\n", __FUNCTION__, pid, timeout);
297
298
0
  YR_MEMORY_BLOCK_ITERATOR iterator;
299
300
0
  int result = yr_process_open_iterator(pid, &iterator);
301
302
0
  if (result == ERROR_SUCCESS)
303
0
  {
304
0
    result = yr_rules_scan_mem_blocks(
305
0
        rules,
306
0
        &iterator,
307
0
        flags | SCAN_FLAGS_PROCESS_MEMORY,
308
0
        callback,
309
0
        user_data,
310
0
        timeout);
311
312
0
    yr_process_close_iterator(&iterator);
313
0
  }
314
315
0
  YR_DEBUG_FPRINTF(
316
0
      2,
317
0
      stderr,
318
0
      "} = %d AKA %s // %s()\n",
319
0
      result,
320
0
      yr_debug_error_as_string(result),
321
0
      __FUNCTION__);
322
323
0
  return result;
324
0
}
325
326
int yr_rules_from_arena(YR_ARENA* arena, YR_RULES** rules)
327
112
{
328
112
  YR_SUMMARY* summary = (YR_SUMMARY*) yr_arena_get_ptr(
329
112
      arena, YR_SUMMARY_SECTION, 0);
330
331
112
  if (summary == NULL)
332
0
    return ERROR_CORRUPT_FILE;
333
334
112
  YR_RULES* new_rules = (YR_RULES*) yr_malloc(sizeof(YR_RULES));
335
336
112
  if (new_rules == NULL)
337
0
    return ERROR_INSUFFICIENT_MEMORY;
338
339
112
  new_rules->no_required_strings = (YR_BITMASK*) yr_calloc(
340
112
      sizeof(YR_BITMASK), YR_BITMASK_SIZE(summary->num_rules));
341
342
112
  if (new_rules->no_required_strings == NULL)
343
0
  {
344
0
    yr_free(new_rules);
345
0
    return ERROR_INSUFFICIENT_MEMORY;
346
0
  }
347
348
  // Now YR_RULES relies on this arena, let's increment the arena's
349
  // reference count so that if the original owner of the arena calls
350
  // yr_arena_destroy the arena is not destroyed.
351
112
  yr_arena_acquire(arena);
352
353
112
  new_rules->arena = arena;
354
112
  new_rules->num_rules = summary->num_rules;
355
112
  new_rules->num_strings = summary->num_strings;
356
112
  new_rules->num_namespaces = summary->num_namespaces;
357
358
112
  new_rules->rules_table = yr_arena_get_ptr(arena, YR_RULES_TABLE, 0);
359
360
112
  new_rules->strings_table = yr_arena_get_ptr(arena, YR_STRINGS_TABLE, 0);
361
362
112
  new_rules->ext_vars_table = yr_arena_get_ptr(
363
112
      arena, YR_EXTERNAL_VARIABLES_TABLE, 0);
364
365
112
  new_rules->ac_transition_table = yr_arena_get_ptr(
366
112
      arena, YR_AC_TRANSITION_TABLE, 0);
367
368
112
  new_rules->ac_match_table = yr_arena_get_ptr(
369
112
      arena, YR_AC_STATE_MATCHES_TABLE, 0);
370
371
112
  new_rules->ac_match_pool = yr_arena_get_ptr(
372
112
      arena, YR_AC_STATE_MATCHES_POOL, 0);
373
374
112
  new_rules->code_start = yr_arena_get_ptr(arena, YR_CODE_SECTION, 0);
375
376
  // If a rule has no required_strings, this means that the condition might
377
  // evaluate to true without any matching strings, and we therefore have to
378
  // mark it as "to be evaluated" from the beginning.
379
214
  for (int i = 0; i < new_rules->num_rules; i++)
380
102
  {
381
102
    if (new_rules->rules_table[i].required_strings == 0)
382
55
      yr_bitmask_set(new_rules->no_required_strings, i);
383
102
  }
384
385
112
  *rules = new_rules;
386
387
112
  return ERROR_SUCCESS;
388
112
}
389
390
YR_API int yr_rules_load_stream(YR_STREAM* stream, YR_RULES** rules)
391
0
{
392
0
  YR_ARENA* arena;
393
394
  // Load the arena's data the stream. We are the owners of the arena.
395
0
  FAIL_ON_ERROR(yr_arena_load_stream(stream, &arena));
396
397
  // Create the YR_RULES object from the arena, this makes YR_RULES owner
398
  // of the arena too.
399
0
  FAIL_ON_ERROR(yr_rules_from_arena(arena, rules));
400
401
  // Release our ownership so that YR_RULES is the single owner. This way the
402
  // arena is destroyed when YR_RULES is destroyed.
403
0
  yr_arena_release(arena);
404
405
0
  return ERROR_SUCCESS;
406
0
}
407
408
YR_API int yr_rules_load(const char* filename, YR_RULES** rules)
409
0
{
410
0
  int result;
411
412
0
  YR_STREAM stream;
413
0
  FILE* fh = fopen(filename, "rb");
414
415
0
  if (fh == NULL)
416
0
    return ERROR_COULD_NOT_OPEN_FILE;
417
418
0
  stream.user_data = fh;
419
0
  stream.read = (YR_STREAM_READ_FUNC) fread;
420
421
0
  result = yr_rules_load_stream(&stream, rules);
422
423
0
  fclose(fh);
424
0
  return result;
425
0
}
426
427
YR_API int yr_rules_save_stream(YR_RULES* rules, YR_STREAM* stream)
428
0
{
429
0
  return yr_arena_save_stream(rules->arena, stream);
430
0
}
431
432
YR_API int yr_rules_save(YR_RULES* rules, const char* filename)
433
0
{
434
0
  int result;
435
436
0
  YR_STREAM stream;
437
0
  FILE* fh = fopen(filename, "wb");
438
439
0
  if (fh == NULL)
440
0
    return ERROR_COULD_NOT_OPEN_FILE;
441
442
0
  stream.user_data = fh;
443
0
  stream.write = (YR_STREAM_WRITE_FUNC) fwrite;
444
445
0
  result = yr_rules_save_stream(rules, &stream);
446
447
0
  fclose(fh);
448
0
  return result;
449
0
}
450
451
static int _uint32_cmp(const void* a, const void* b)
452
0
{
453
0
  return (*(uint32_t*) a - *(uint32_t*) b);
454
0
}
455
456
YR_API int yr_rules_get_stats(YR_RULES* rules, YR_RULES_STATS* stats)
457
0
{
458
0
  memset(stats, 0, sizeof(YR_RULES_STATS));
459
460
0
  stats->ac_tables_size = yr_arena_get_current_offset(
461
0
                              rules->arena, YR_AC_TRANSITION_TABLE) /
462
0
                          sizeof(YR_AC_TRANSITION);
463
464
0
  uint32_t* match_list_lengths = (uint32_t*) yr_malloc(
465
0
      sizeof(uint32_t) * stats->ac_tables_size);
466
467
0
  if (match_list_lengths == NULL)
468
0
    return ERROR_INSUFFICIENT_MEMORY;
469
470
0
  stats->num_rules = rules->num_rules;
471
0
  stats->num_strings = rules->num_strings;
472
473
0
  float match_list_length_sum = 0;
474
0
  int c = 0;
475
476
0
  for (uint32_t i = 0; i < stats->ac_tables_size; i++)
477
0
  {
478
0
    int match_list_length = 0;
479
480
0
    if (rules->ac_match_table[i] != 0)
481
0
    {
482
0
      YR_AC_MATCH* m = &rules->ac_match_pool[rules->ac_match_table[i] - 1];
483
484
0
      while (m != NULL)
485
0
      {
486
0
        match_list_length++;
487
0
        stats->ac_matches++;
488
0
        m = m->next;
489
0
      }
490
0
    }
491
492
0
    if (i == 0)
493
0
      stats->ac_root_match_list_length = match_list_length;
494
495
0
    match_list_length_sum += match_list_length;
496
497
0
    if (match_list_length > 0)
498
0
    {
499
0
      match_list_lengths[c] = match_list_length;
500
0
      c++;
501
0
    }
502
0
  }
503
504
0
  if (c == 0)
505
0
  {
506
0
    yr_free(match_list_lengths);
507
0
    return ERROR_SUCCESS;
508
0
  }
509
510
  // sort match_list_lengths in increasing order for computing percentiles.
511
0
  qsort(match_list_lengths, c, sizeof(match_list_lengths[0]), _uint32_cmp);
512
513
0
  for (int i = 0; i < 100; i++)
514
0
  {
515
0
    if (i < c)
516
0
      stats->top_ac_match_list_lengths[i] = match_list_lengths[c - i - 1];
517
0
    else
518
0
      stats->top_ac_match_list_lengths[i] = 0;
519
0
  }
520
521
0
  stats->ac_average_match_list_length = match_list_length_sum / c;
522
0
  stats->ac_match_list_length_pctls[0] = match_list_lengths[0];
523
0
  stats->ac_match_list_length_pctls[100] = match_list_lengths[c - 1];
524
525
0
  for (int i = 1; i < 100; i++)
526
0
    stats->ac_match_list_length_pctls[i] = match_list_lengths[(c * i) / 100];
527
528
0
  yr_free(match_list_lengths);
529
530
0
  return ERROR_SUCCESS;
531
0
}
532
533
YR_API int yr_rules_destroy(YR_RULES* rules)
534
103
{
535
103
  YR_EXTERNAL_VARIABLE* external = rules->ext_vars_table;
536
537
103
  while (!EXTERNAL_VARIABLE_IS_NULL(external))
538
0
  {
539
0
    if (external->type == EXTERNAL_VARIABLE_TYPE_MALLOC_STRING)
540
0
      yr_free(external->value.s);
541
542
0
    external++;
543
0
  }
544
545
103
  yr_free(rules->no_required_strings);
546
103
  yr_arena_release(rules->arena);
547
103
  yr_free(rules);
548
549
103
  return ERROR_SUCCESS;
550
103
}
551
552
YR_API void yr_rule_disable(YR_RULE* rule)
553
0
{
554
0
  YR_STRING* string;
555
556
0
  rule->flags |= RULE_FLAGS_DISABLED;
557
558
0
  yr_rule_strings_foreach(rule, string)
559
0
  {
560
0
    string->flags |= STRING_FLAGS_DISABLED;
561
0
  }
562
0
}
563
564
YR_API void yr_rule_enable(YR_RULE* rule)
565
0
{
566
0
  YR_STRING* string;
567
568
0
  rule->flags &= ~RULE_FLAGS_DISABLED;
569
570
0
  yr_rule_strings_foreach(rule, string)
571
0
  {
572
0
    string->flags &= ~STRING_FLAGS_DISABLED;
573
0
  }
574
0
}