Coverage Report

Created: 2025-08-29 07:07

/src/yara/libyara/exefiles.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
Copyright (c) 2007-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 <limits.h>
31
#include <yara/elf.h>
32
#include <yara/endian.h>
33
#include <yara/exec.h>
34
#include <yara/pe.h>
35
#include <yara/utils.h>
36
37
#ifndef NULL
38
#define NULL 0
39
#endif
40
41
#ifndef MIN
42
0
#define MIN(x, y) ((x < y) ? (x) : (y))
43
#endif
44
45
PIMAGE_NT_HEADERS32 yr_get_pe_header(
46
    const uint8_t* buffer,
47
    size_t buffer_length)
48
0
{
49
0
  PIMAGE_DOS_HEADER mz_header;
50
0
  PIMAGE_NT_HEADERS32 pe_header;
51
52
0
  size_t headers_size = 0;
53
54
0
  if (buffer_length < sizeof(IMAGE_DOS_HEADER))
55
0
    return NULL;
56
57
0
  mz_header = (PIMAGE_DOS_HEADER) buffer;
58
59
0
  if (yr_le16toh(mz_header->e_magic) != IMAGE_DOS_SIGNATURE)
60
0
    return NULL;
61
62
0
  if ((int32_t) yr_le32toh(mz_header->e_lfanew) < 0)
63
0
    return NULL;
64
65
0
  headers_size = yr_le32toh(mz_header->e_lfanew) +
66
0
                 sizeof(pe_header->Signature) + sizeof(IMAGE_FILE_HEADER);
67
68
0
  if (buffer_length < headers_size)
69
0
    return NULL;
70
71
0
  pe_header = (PIMAGE_NT_HEADERS32)(buffer + yr_le32toh(mz_header->e_lfanew));
72
73
0
  headers_size += sizeof(IMAGE_OPTIONAL_HEADER32);
74
75
0
  if (yr_le32toh(pe_header->Signature) == IMAGE_NT_SIGNATURE &&
76
0
      (yr_le16toh(pe_header->FileHeader.Machine) == IMAGE_FILE_MACHINE_I386 ||
77
0
       yr_le16toh(pe_header->FileHeader.Machine) == IMAGE_FILE_MACHINE_AMD64) &&
78
0
      buffer_length > headers_size)
79
0
  {
80
0
    return pe_header;
81
0
  }
82
0
  else
83
0
  {
84
0
    return NULL;
85
0
  }
86
0
}
87
88
uint64_t yr_pe_rva_to_offset(
89
    PIMAGE_NT_HEADERS32 pe_header,
90
    uint64_t rva,
91
    size_t buffer_length)
92
0
{
93
0
  int i = 0;
94
0
  PIMAGE_SECTION_HEADER section;
95
0
  DWORD section_rva;
96
0
  DWORD section_offset;
97
98
0
  section = IMAGE_FIRST_SECTION(pe_header);
99
0
  section_rva = 0;
100
0
  section_offset = 0;
101
102
0
  while (i < MIN(yr_le16toh(pe_header->FileHeader.NumberOfSections), 60))
103
0
  {
104
0
    if ((uint8_t*) section - (uint8_t*) pe_header +
105
0
            sizeof(IMAGE_SECTION_HEADER) <
106
0
        buffer_length)
107
0
    {
108
0
      if (rva >= yr_le32toh(section->VirtualAddress) &&
109
0
          section_rva <= yr_le32toh(section->VirtualAddress))
110
0
      {
111
0
        section_rva = yr_le32toh(section->VirtualAddress);
112
0
        section_offset = yr_le32toh(section->PointerToRawData);
113
0
      }
114
115
0
      section++;
116
0
      i++;
117
0
    }
118
0
    else
119
0
    {
120
0
      return 0;
121
0
    }
122
0
  }
123
124
0
  return section_offset + (rva - section_rva);
125
0
}
126
127
int yr_get_elf_type(const uint8_t* buffer, size_t buffer_length)
128
0
{
129
0
  elf_ident_t* elf_ident;
130
131
0
  if (buffer_length < sizeof(elf_ident_t))
132
0
    return 0;
133
134
0
  elf_ident = (elf_ident_t*) buffer;
135
136
0
  if (yr_le32toh(elf_ident->magic) != ELF_MAGIC)
137
0
  {
138
0
    return 0;
139
0
  }
140
141
0
  switch (elf_ident->_class)
142
0
  {
143
0
  case ELF_CLASS_32:
144
0
    if (buffer_length < sizeof(elf32_header_t))
145
0
    {
146
0
      return 0;
147
0
    }
148
0
    break;
149
0
  case ELF_CLASS_64:
150
0
    if (buffer_length < sizeof(elf64_header_t))
151
0
    {
152
0
      return 0;
153
0
    }
154
0
    break;
155
0
  default:
156
    /* Unexpected class */
157
0
    return 0;
158
0
  }
159
160
0
  return elf_ident->_class;
161
0
}
162
163
static uint64_t yr_elf_rva_to_offset_32(
164
    elf32_header_t* elf_header,
165
    uint64_t rva,
166
    size_t buffer_length)
167
0
{
168
  // if the binary is an executable then prefer the program headers to resolve
169
  // the offset
170
0
  if (yr_le16toh(elf_header->type) == ELF_ET_EXEC)
171
0
  {
172
0
    int i;
173
0
    elf32_program_header_t* program;
174
0
    if (yr_le32toh(elf_header->ph_offset) == 0 ||
175
0
        yr_le16toh(elf_header->ph_entry_count == 0))
176
0
      return 0;
177
178
    // check to prevent integer wraps
179
0
    if (ULONG_MAX - yr_le16toh(elf_header->ph_entry_count) <
180
0
        sizeof(elf32_program_header_t) * yr_le16toh(elf_header->ph_entry_count))
181
0
      return 0;
182
183
    // check that 'ph_offset' doesn't wrap when added to the
184
    // size of entries.
185
0
    if (ULONG_MAX - yr_le32toh(elf_header->ph_offset) <
186
0
        sizeof(elf32_program_header_t) * yr_le16toh(elf_header->ph_entry_count))
187
0
      return 0;
188
189
    // ensure we don't exceed the buffer size
190
0
    if (yr_le32toh(elf_header->ph_offset) +
191
0
            sizeof(elf32_program_header_t) *
192
0
                yr_le16toh(elf_header->ph_entry_count) >
193
0
        buffer_length)
194
0
      return 0;
195
196
0
    program =
197
0
        (elf32_program_header_t*) ((uint8_t*) elf_header + yr_le32toh(elf_header->ph_offset));
198
199
0
    for (i = 0; i < yr_le16toh(elf_header->ph_entry_count); i++)
200
0
    {
201
0
      if (rva >= yr_le32toh(program->virt_addr) &&
202
0
          rva < yr_le32toh(program->virt_addr) + yr_le32toh(program->mem_size))
203
0
      {
204
0
        return yr_le32toh(program->offset) +
205
0
               (rva - yr_le32toh(program->virt_addr));
206
0
      }
207
208
0
      program++;
209
0
    }
210
0
  }
211
0
  else
212
0
  {
213
0
    int i;
214
0
    elf32_section_header_t* section;
215
216
0
    if (yr_le32toh(elf_header->sh_offset) == 0 ||
217
0
        yr_le16toh(elf_header->sh_entry_count == 0))
218
0
      return 0;
219
220
    // check to prevent integer wraps
221
222
0
    if (ULONG_MAX - yr_le16toh(elf_header->sh_entry_count) <
223
0
        sizeof(elf32_section_header_t) * yr_le16toh(elf_header->sh_entry_count))
224
0
      return 0;
225
226
    // check that 'sh_offset' doesn't wrap when added to the
227
    // size of entries.
228
229
0
    if (ULONG_MAX - yr_le32toh(elf_header->sh_offset) <
230
0
        sizeof(elf32_section_header_t) * yr_le16toh(elf_header->sh_entry_count))
231
0
      return 0;
232
233
0
    if (yr_le32toh(elf_header->sh_offset) +
234
0
            sizeof(elf32_section_header_t) *
235
0
                yr_le16toh(elf_header->sh_entry_count) >
236
0
        buffer_length)
237
0
      return 0;
238
239
0
    section = (elf32_section_header_t*)
240
0
      ((unsigned char*) elf_header + yr_le32toh(elf_header->sh_offset));
241
242
0
    for (i = 0; i < yr_le16toh(elf_header->sh_entry_count); i++)
243
0
    {
244
0
      if (yr_le32toh(section->type) != ELF_SHT_NULL &&
245
0
          yr_le32toh(section->type) != ELF_SHT_NOBITS &&
246
0
          rva >= yr_le32toh(section->addr) &&
247
0
          rva < yr_le32toh(section->addr) + yr_le32toh(section->size))
248
0
      {
249
        // prevent integer wrapping with the return value
250
251
0
        if (ULONG_MAX - yr_le32toh(section->offset) <
252
0
            (rva - yr_le32toh(section->addr)))
253
0
          return 0;
254
0
        else
255
0
          return yr_le32toh(section->offset) +
256
0
                 (rva - yr_le32toh(section->addr));
257
0
      }
258
259
0
      section++;
260
0
    }
261
0
  }
262
263
0
  return 0;
264
0
}
265
266
static uint64_t yr_elf_rva_to_offset_64(
267
    elf64_header_t* elf_header,
268
    uint64_t rva,
269
    size_t buffer_length)
270
0
{
271
  // if the binary is an executable then prefer the program headers to resolve
272
  // the offset
273
0
  if (yr_le16toh(elf_header->type) == ELF_ET_EXEC)
274
0
  {
275
0
    int i;
276
0
    elf64_program_header_t* program;
277
0
    if (yr_le64toh(elf_header->ph_offset) == 0 ||
278
0
        yr_le16toh(elf_header->ph_entry_count == 0))
279
0
      return 0;
280
281
    // check that 'ph_offset' doesn't wrap when added to the
282
    // size of entries.
283
0
    if (ULONG_MAX - yr_le64toh(elf_header->ph_offset) <
284
0
        sizeof(elf64_program_header_t) * yr_le16toh(elf_header->ph_entry_count))
285
0
      return 0;
286
287
    // ensure we don't exceed the buffer size
288
0
    if (yr_le64toh(elf_header->ph_offset) +
289
0
            sizeof(elf64_program_header_t) *
290
0
                yr_le16toh(elf_header->ph_entry_count) >
291
0
        buffer_length)
292
0
      return 0;
293
294
0
    program =
295
0
        (elf64_program_header_t*) ((uint8_t*) elf_header + yr_le64toh(elf_header->ph_offset));
296
297
0
    for (i = 0; i < yr_le16toh(elf_header->ph_entry_count); i++)
298
0
    {
299
0
      if (rva >= yr_le64toh(program->virt_addr) &&
300
0
          rva < yr_le64toh(program->virt_addr) + yr_le64toh(program->mem_size))
301
0
      {
302
0
        return yr_le64toh(program->offset) +
303
0
               (rva - yr_le64toh(program->virt_addr));
304
0
      }
305
306
0
      program++;
307
0
    }
308
0
  }
309
0
  else
310
0
  {
311
0
    int i;
312
0
    elf64_section_header_t* section;
313
314
0
    if (yr_le64toh(elf_header->sh_offset) == 0 ||
315
0
        yr_le16toh(elf_header->sh_entry_count) == 0)
316
0
      return 0;
317
318
    // check that 'sh_offset' doesn't wrap when added to the
319
    // size of entries.
320
0
    if (ULONG_MAX - yr_le64toh(elf_header->sh_offset) <
321
0
        sizeof(elf64_section_header_t) * yr_le16toh(elf_header->sh_entry_count))
322
0
      return 0;
323
324
0
    if (yr_le64toh(elf_header->sh_offset) +
325
0
            sizeof(elf64_section_header_t) *
326
0
                yr_le16toh(elf_header->sh_entry_count) >
327
0
        buffer_length)
328
0
      return 0;
329
330
0
    section =
331
0
        (elf64_section_header_t*) ((uint8_t*) elf_header + yr_le64toh(elf_header->sh_offset));
332
333
0
    for (i = 0; i < yr_le16toh(elf_header->sh_entry_count); i++)
334
0
    {
335
0
      if (yr_le32toh(section->type) != ELF_SHT_NULL &&
336
0
          yr_le32toh(section->type) != ELF_SHT_NOBITS &&
337
0
          rva >= yr_le64toh(section->addr) &&
338
0
          rva < yr_le64toh(section->addr) + yr_le64toh(section->size))
339
0
      {
340
0
        return yr_le64toh(section->offset) + (rva - yr_le64toh(section->addr));
341
0
      }
342
343
0
      section++;
344
0
    }
345
0
  }
346
347
0
  return 0;
348
0
}
349
350
uint64_t yr_get_entry_point_offset(const uint8_t* buffer, size_t buffer_length)
351
0
{
352
0
  PIMAGE_NT_HEADERS32 pe_header;
353
0
  elf32_header_t* elf_header32;
354
0
  elf64_header_t* elf_header64;
355
356
0
  pe_header = yr_get_pe_header(buffer, buffer_length);
357
358
0
  if (pe_header != NULL)
359
0
  {
360
0
    return yr_pe_rva_to_offset(
361
0
        pe_header,
362
0
        yr_le32toh(pe_header->OptionalHeader.AddressOfEntryPoint),
363
0
        buffer_length - ((uint8_t*) pe_header - buffer));
364
0
  }
365
366
0
  switch (yr_get_elf_type(buffer, buffer_length))
367
0
  {
368
0
  case ELF_CLASS_32:
369
0
    elf_header32 = (elf32_header_t*) buffer;
370
0
    return yr_elf_rva_to_offset_32(
371
0
        elf_header32, yr_le32toh(elf_header32->entry), buffer_length);
372
373
0
  case ELF_CLASS_64:
374
0
    elf_header64 = (elf64_header_t*) buffer;
375
0
    return yr_elf_rva_to_offset_64(
376
0
        elf_header64, yr_le64toh(elf_header64->entry), buffer_length);
377
0
  }
378
379
0
  return YR_UNDEFINED;
380
0
}
381
382
uint64_t yr_get_entry_point_address(
383
    const uint8_t* buffer,
384
    size_t buffer_length,
385
    uint64_t base_address)
386
0
{
387
0
  PIMAGE_NT_HEADERS32 pe_header;
388
389
0
  elf32_header_t* elf_header32;
390
0
  elf64_header_t* elf_header64;
391
392
0
  pe_header = yr_get_pe_header(buffer, buffer_length);
393
394
  // If file is PE but not a DLL.
395
396
0
  if (pe_header != NULL &&
397
0
      !(pe_header->FileHeader.Characteristics & IMAGE_FILE_DLL))
398
0
    return base_address + pe_header->OptionalHeader.AddressOfEntryPoint;
399
400
  // If file is executable ELF, not shared library.
401
402
0
  switch (yr_get_elf_type(buffer, buffer_length))
403
0
  {
404
0
  case ELF_CLASS_32:
405
0
    elf_header32 = (elf32_header_t*) buffer;
406
407
0
    if (elf_header32->type == ELF_ET_EXEC)
408
0
      return base_address + elf_header32->entry;
409
410
0
    break;
411
412
0
  case ELF_CLASS_64:
413
0
    elf_header64 = (elf64_header_t*) buffer;
414
415
0
    if (elf_header64->type == ELF_ET_EXEC)
416
0
      return base_address + elf_header64->entry;
417
418
0
    break;
419
0
  }
420
421
0
  return YR_UNDEFINED;
422
0
}