Coverage Report

Created: 2025-11-24 06:34

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/yara/libyara/modules/dotnet/dotnet.c
Line
Count
Source
1
/*
2
Copyright (c) 2015. The YARA Authors. All Rights Reserved.
3
4
Licensed under the Apache License, Version 2.0 (the "License");
5
you may not use this file except in compliance with the License.
6
You may obtain a copy of the License at
7
8
   http://www.apache.org/licenses/LICENSE-2.0
9
10
Unless required by applicable law or agreed to in writing, software
11
distributed under the License is distributed on an "AS IS" BASIS,
12
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
See the License for the specific language governing permissions and
14
limitations under the License.
15
*/
16
17
#include <ctype.h>
18
#include <stdarg.h>
19
#include <stdbool.h>
20
#include <stdio.h>
21
#include <string.h>
22
#include <time.h>
23
#include <yara/dotnet.h>
24
#include <yara/mem.h>
25
#include <yara/modules.h>
26
#include <yara/pe.h>
27
#include <yara/pe_utils.h>
28
#include <yara/simple_str.h>
29
#include <yara/strutils.h>
30
#include <yara/unaligned.h>
31
32
#define MODULE_NAME dotnet
33
34
static uint32_t max_rows(int count, ...)
35
26.9M
{
36
26.9M
  va_list ap;
37
26.9M
  uint32_t biggest;
38
26.9M
  uint32_t x;
39
40
26.9M
  if (count == 0)
41
0
    return 0;
42
43
26.9M
  va_start(ap, count);
44
26.9M
  biggest = va_arg(ap, uint32_t);
45
46
57.7M
  for (int i = 1; i < count; i++)
47
30.8M
  {
48
30.8M
    x = va_arg(ap, uint32_t);
49
30.8M
    biggest = (x > biggest) ? x : biggest;
50
30.8M
  }
51
52
26.9M
  va_end(ap);
53
26.9M
  return biggest;
54
26.9M
}
55
56
static uint32_t read_u32(const uint8_t** data)
57
6.84M
{
58
6.84M
  uint32_t result = yr_le32toh(yr_unaligned_u32(*data));
59
6.84M
  *data += sizeof(uint32_t);
60
6.84M
  return result;
61
6.84M
}
62
63
static uint16_t read_u16(const uint8_t** data)
64
151M
{
65
151M
  uint16_t result = yr_le16toh(yr_unaligned_u16(*data));
66
151M
  *data += sizeof(uint16_t);
67
151M
  return result;
68
151M
}
69
70
static uint8_t read_u8(const uint8_t** data)
71
47.0M
{
72
47.0M
  uint8_t result = **data;
73
47.0M
  *data += sizeof(uint8_t);
74
47.0M
  return result;
75
47.0M
}
76
77
static uint32_t read_index(const uint8_t** data, uint8_t len)
78
106M
{
79
106M
  if (len == 2)
80
102M
    return read_u16(data);
81
3.98M
  else
82
3.98M
    return read_u32(data);
83
106M
}
84
85
// Returns valid offset within the table or NULL
86
const uint8_t* get_table_offset(const TABLE_INFO* tbl, uint32_t index)
87
202M
{
88
  // Indexes to .NET tables are based from 1
89
202M
  if (index < 1 || index > tbl->RowCount)
90
366k
    return NULL;
91
92
201M
  return tbl->Offset + tbl->RowSize * (index - 1);
93
202M
}
94
95
// Given an offset into a #US or #Blob stream, parse the entry at that position.
96
// The offset is relative to the start of the PE file.
97
// if size > 0 then it's valid and readable blob
98
BLOB_PARSE_RESULT dotnet_parse_blob_entry(PE* pe, const uint8_t* offset)
99
3.14M
{
100
3.14M
  BLOB_PARSE_RESULT result = {.size = 0, .length = 0};
101
102
  // Blob size is encoded in the first 1, 2 or 4 bytes of the blob.
103
  //
104
  // If the high bit is not set the length is encoded in one byte.
105
  //
106
  // If the high 2 bits are 10 (base 2) then the length is encoded in
107
  // the rest of the bits and the next byte.
108
  //
109
  // If the high 3 bits are 110 (base 2) then the length is encoded
110
  // in the rest of the bits and the next 3 bytes.
111
  //
112
  // See ECMA-335 II.24.2.4 for details.
113
114
  // Make sure we have at least one byte.
115
116
3.14M
  if (!fits_in_pe(pe, offset, 1))
117
64.4k
    return result;
118
119
3.08M
  if ((*offset & 0x80) == 0x00)
120
2.82M
  {
121
2.82M
    result.length = (uint32_t) (*offset);
122
2.82M
    result.size = 1;
123
2.82M
  }
124
256k
  else if ((*offset & 0xC0) == 0x80)
125
100k
  {
126
    // Make sure we have one more byte.
127
100k
    if (!fits_in_pe(pe, offset, 2))
128
1.24k
      return result;
129
130
    // Shift remaining 6 bits left by 8 and OR in the remaining byte.
131
98.8k
    result.length = ((*offset & 0x3F) << 8) | *(offset + 1);
132
98.8k
    result.size = 2;
133
98.8k
  }
134
155k
  else if (offset + 4 < pe->data + pe->data_size && (*offset & 0xE0) == 0xC0)
135
52.9k
  {
136
    // Make sure we have 3 more bytes.
137
52.9k
    if (!fits_in_pe(pe, offset, 4))
138
0
      return result;
139
140
52.9k
    result.length = ((*offset & 0x1F) << 24) | (*(offset + 1) << 16) |
141
52.9k
                    (*(offset + 2) << 8) | *(offset + 3);
142
52.9k
    result.size = 4;
143
52.9k
  }
144
103k
  else
145
103k
  {
146
    // Return a 0 size as an error.
147
103k
    return result;
148
103k
  }
149
150
  // Check if the length is actually readable
151
2.97M
  if (!fits_in_pe(pe, offset, result.size + result.length))
152
61.1k
  {
153
61.1k
    result.size = 0;
154
61.1k
    return result;
155
61.1k
  }
156
157
2.91M
  return result;
158
2.97M
}
159
160
char* pe_get_dotnet_string(
161
    PE* pe,
162
    const uint8_t* heap_offset,
163
    uint32_t heap_size,
164
    uint32_t string_index)
165
3.68M
{
166
3.68M
  size_t remaining;
167
168
3.68M
  char* start;
169
3.68M
  char* eos;
170
171
  // Start of string must be within boundary
172
3.68M
  if (!(heap_offset + string_index >= pe->data &&
173
3.68M
        heap_offset + string_index < pe->data + pe->data_size &&
174
2.95M
        string_index < heap_size))
175
809k
    return NULL;
176
177
  // Calculate how much until end of boundary, don't scan past that.
178
2.87M
  remaining = (pe->data + pe->data_size) - (heap_offset + string_index);
179
180
  // Search for a NULL terminator from start of string, up to remaining.
181
2.87M
  start = (char*) (heap_offset + string_index);
182
2.87M
  eos = (char*) memmem((void*) start, remaining, "\0", 1);
183
184
  // If no NULL terminator was found or the string is too large, return NULL.
185
2.87M
  if (eos == NULL || eos - start > 1024)
186
447k
    return NULL;
187
188
2.42M
  return start;
189
2.87M
}
190
191
static bool is_nested(uint32_t flags)
192
657k
{
193
  // ECMA 335 II.22.37
194
  // Whether a type is nested can be determined by the value of its
195
  // Flags.Visibility sub-field – it shall be one of the set
196
  // { NestedPublic, NestedPrivate, NestedFamily, NestedAssembly,
197
  // NestedFamANDAssem, NestedFamORAssem }
198
199
657k
  switch (flags & TYPE_ATTR_VISIBILITY_MASK)
200
657k
  {
201
19.4k
  case TYPE_ATTR_NESTED_PRIVATE:
202
44.0k
  case TYPE_ATTR_NESTED_PUBLIC:
203
60.9k
  case TYPE_ATTR_NESTED_FAMILY:
204
88.5k
  case TYPE_ATTR_NESTED_ASSEMBLY:
205
106k
  case TYPE_ATTR_NESTED_FAM_AND_ASSEM:
206
153k
  case TYPE_ATTR_NESTED_FAM_OR_ASSEM:
207
153k
    return true;
208
503k
  default:
209
503k
    return false;
210
657k
  }
211
657k
}
212
213
// ECMA 335 II.23.1.15 Flags for types [TypeAttribute]
214
static const char* get_type_visibility(uint32_t flags)
215
208k
{
216
208k
  switch (flags & TYPE_ATTR_VISIBILITY_MASK)
217
208k
  {
218
13.5k
  case TYPE_ATTR_NESTED_PRIVATE:
219
13.5k
    return "private";
220
17.4k
  case TYPE_ATTR_PUBLIC:
221
31.7k
  case TYPE_ATTR_NESTED_PUBLIC:
222
31.7k
    return "public";
223
13.2k
  case TYPE_ATTR_NESTED_FAMILY:
224
13.2k
    return "protected";
225
109k
  case TYPE_ATTR_NOT_PUBLIC:
226
121k
  case TYPE_ATTR_NESTED_ASSEMBLY:
227
121k
    return "internal";
228
13.1k
  case TYPE_ATTR_NESTED_FAM_AND_ASSEM:
229
13.1k
    return "private protected";
230
16.2k
  case TYPE_ATTR_NESTED_FAM_OR_ASSEM:
231
16.2k
    return "protected internal";
232
0
  default:
233
0
    return "private";
234
208k
  }
235
208k
}
236
237
// ECMA 335 II.23.1.10 Flags for methods [MethodAttributes]
238
static const char* get_method_visibility(uint32_t flags)
239
27.3k
{
240
27.3k
  switch (flags & METHOD_ATTR_ACCESS_MASK)
241
27.3k
  {
242
1.69k
  case METHOD_ATTR_PRIVATE:
243
1.69k
    return "private";
244
1.37k
  case METHOD_ATTR_FAM_AND_ASSEM:
245
1.37k
    return "private protected";
246
1.64k
  case METHOD_ATTR_ASSEM:
247
1.64k
    return "internal";
248
1.64k
  case METHOD_ATTR_FAMILY:
249
1.64k
    return "protected";
250
891
  case METHOD_ATTR_FAM_OR_ASSEM:
251
891
    return "protected internal";
252
1.11k
  case METHOD_ATTR_PUBLIC:
253
1.11k
    return "public";
254
18.9k
  default:
255
18.9k
    return "private";
256
27.3k
  }
257
27.3k
}
258
259
// ECMA 335 II.23.1.15 Flags for types [TypeAttribute]
260
static const char* get_typedef_type(uint32_t flags)
261
742k
{
262
742k
  switch (flags & TYPE_ATTR_CLASS_SEMANTIC_MASK)
263
742k
  {
264
429k
  case TYPE_ATTR_CLASS:
265
429k
    return "class";
266
312k
  case TYPE_ATTR_INTERFACE:
267
312k
    return "interface";
268
0
  default:
269
0
    return NULL;
270
742k
  }
271
742k
}
272
273
// returns allocated string <namespace>.<name>, must be freed
274
static char* create_full_name(const char* name, const char* namespace)
275
819k
{
276
819k
  if (!name || !strlen(name))
277
170k
    return namespace ? yr_strdup(namespace) : NULL;
278
279
  // No namespace -> return name only
280
649k
  if (!namespace || !strlen(namespace))
281
299k
  {
282
    // fix generic names
283
299k
    char* name_copy = yr_strdup(name);
284
299k
    char* end = strchr(name_copy, '`');
285
299k
    if (end)
286
13.0k
      *end = 0;
287
299k
    return name_copy;
288
299k
  }
289
290
349k
  size_t name_len = strlen(name);
291
349k
  size_t namespace_len = strlen(namespace);
292
293
  // <namespace>.<name>
294
349k
  char* full_name = yr_malloc(namespace_len + 1 + name_len + 1);
295
296
349k
  memcpy(full_name, namespace, namespace_len);
297
349k
  full_name[namespace_len] = '.';
298
349k
  memcpy(full_name + namespace_len + 1, name, name_len + 1);
299
300
  // fix generic names
301
349k
  char* end = strchr(full_name, '`');
302
349k
  if (end)
303
14.5k
    *end = 0;
304
305
349k
  return full_name;
306
649k
}
307
308
static bool read_typedef(
309
    const CLASS_CONTEXT* ctx,
310
    const uint8_t* data,
311
    TYPEDEF_ROW* result)
312
2.38M
{
313
2.38M
  uint32_t row_size = ctx->tables->typedef_.RowSize;
314
315
2.38M
  if (fits_in_pe(ctx->pe, data, row_size))
316
1.39M
  {
317
1.39M
    uint8_t ext_size = 2;
318
1.39M
    uint32_t row_count = max_rows(
319
1.39M
        3,
320
1.39M
        ctx->tables->typedef_.RowCount,
321
1.39M
        ctx->tables->typeref.RowCount,
322
1.39M
        ctx->tables->typespec.RowCount);
323
324
1.39M
    if (row_count > (0xFFFF >> 0x02))
325
0
      ext_size = 4;
326
327
1.39M
    result->Flags = read_u32(&data);
328
1.39M
    result->Name = read_index(&data, ctx->index_sizes->string);
329
1.39M
    result->Namespace = read_index(&data, ctx->index_sizes->string);
330
1.39M
    result->Extends = read_index(&data, ext_size);
331
1.39M
    result->Field = read_index(&data, ctx->index_sizes->field);
332
1.39M
    result->Method = read_index(&data, ctx->index_sizes->methoddef);
333
334
1.39M
    return true;
335
1.39M
  }
336
337
982k
  return false;
338
2.38M
}
339
340
static bool read_typeref(
341
    const CLASS_CONTEXT* ctx,
342
    const uint8_t* data,
343
    TYPEREF_ROW* result)
344
10.0k
{
345
10.0k
  uint32_t row_size = ctx->tables->typeref.RowSize;
346
347
10.0k
  if (fits_in_pe(ctx->pe, data, row_size))
348
10.0k
  {
349
10.0k
    uint8_t res_size = 2;
350
10.0k
    uint32_t row_count = max_rows(
351
10.0k
        4,
352
10.0k
        ctx->tables->module.RowCount,
353
10.0k
        ctx->tables->moduleref.RowCount,
354
10.0k
        ctx->tables->assemblyref.RowCount,
355
10.0k
        ctx->tables->typeref.RowCount);
356
357
10.0k
    if (row_count > (0xFFFF >> 0x02))
358
0
      res_size = 4;
359
360
10.0k
    result->ResolutionScope = read_index(&data, res_size);
361
10.0k
    result->Name = read_index(&data, ctx->index_sizes->string);
362
10.0k
    result->Namespace = read_index(&data, ctx->index_sizes->string);
363
364
10.0k
    return true;
365
10.0k
  }
366
367
0
  return false;
368
10.0k
}
369
370
static bool read_interfaceimpl(
371
    const CLASS_CONTEXT* ctx,
372
    const uint8_t* data,
373
    INTERFACEIMPL_ROW* result)
374
4.16M
{
375
4.16M
  uint32_t row_size = ctx->tables->intefaceimpl.RowSize;
376
377
4.16M
  if (fits_in_pe(ctx->pe, data, row_size))
378
2.42M
  {
379
2.42M
    uint32_t interface_size = 2;
380
2.42M
    uint32_t row_count = max_rows(
381
2.42M
        3,
382
2.42M
        ctx->tables->typedef_.RowCount,
383
2.42M
        ctx->tables->typeref.RowCount,
384
2.42M
        ctx->tables->typespec.RowCount);
385
386
2.42M
    if (row_count > (0xFFFF >> 0x02))
387
0
      interface_size = 4;
388
389
2.42M
    result->Class = read_index(&data, ctx->index_sizes->typedef_);
390
2.42M
    result->Interface = read_index(&data, interface_size);
391
392
2.42M
    return true;
393
2.42M
  }
394
395
1.74M
  return false;
396
4.16M
}
397
398
static bool read_methoddef(
399
    const CLASS_CONTEXT* ctx,
400
    const uint8_t* data,
401
    METHODDEF_ROW* result)
402
4.80M
{
403
4.80M
  uint32_t row_size = ctx->tables->methoddef.RowSize;
404
405
4.80M
  if (fits_in_pe(ctx->pe, data, row_size))
406
1.47M
  {
407
1.47M
    result->Rva = read_u32(&data);
408
1.47M
    result->ImplFlags = read_u16(&data);
409
1.47M
    result->Flags = read_u16(&data);
410
1.47M
    result->Name = read_index(&data, ctx->index_sizes->string);
411
1.47M
    result->Signature = read_index(&data, ctx->index_sizes->blob);
412
1.47M
    result->ParamList = read_index(&data, ctx->index_sizes->param);
413
1.47M
    return true;
414
1.47M
  }
415
416
3.33M
  return false;
417
4.80M
}
418
419
static bool read_param(
420
    const CLASS_CONTEXT* ctx,
421
    const uint8_t* data,
422
    PARAM_ROW* result)
423
8.62k
{
424
8.62k
  uint32_t row_size = ctx->tables->param.RowSize;
425
426
8.62k
  if (fits_in_pe(ctx->pe, data, row_size))
427
8.01k
  {
428
8.01k
    result->Flags = read_u16(&data);
429
8.01k
    result->Sequence = read_u16(&data);
430
8.01k
    result->Name = read_index(&data, ctx->index_sizes->string);
431
8.01k
    return true;
432
8.01k
  }
433
434
614
  return false;
435
8.62k
}
436
437
static bool read_genericparam(
438
    const CLASS_CONTEXT* ctx,
439
    const uint8_t* data,
440
    GENERICPARAM_ROW* result)
441
110M
{
442
110M
  uint32_t row_size = ctx->tables->genericparam.RowSize;
443
444
110M
  if (fits_in_pe(ctx->pe, data, row_size))
445
23.0M
  {
446
23.0M
    uint32_t owner_idx_size = 2;
447
23.0M
    uint32_t row_count = max_rows(
448
23.0M
        2, ctx->tables->typedef_.RowCount, ctx->tables->methoddef.RowCount);
449
450
23.0M
    if (row_count > (0xFFFF >> 0x01))
451
0
      owner_idx_size = 4;
452
453
23.0M
    result->Number = read_u16(&data);
454
23.0M
    result->Flags = read_u16(&data);
455
23.0M
    result->Owner = read_index(&data, owner_idx_size);
456
23.0M
    result->Name = read_index(&data, ctx->index_sizes->string);
457
23.0M
    return true;
458
23.0M
  }
459
460
87.2M
  return false;
461
110M
}
462
463
static bool read_typespec(
464
    const CLASS_CONTEXT* ctx,
465
    const uint8_t* data,
466
    TYPESPEC_ROW* result)
467
2.14M
{
468
2.14M
  uint32_t row_size = ctx->tables->typespec.RowSize;
469
470
2.14M
  if (fits_in_pe(ctx->pe, data, row_size))
471
2.11M
  {
472
2.11M
    result->Signature = read_index(&data, ctx->index_sizes->blob);
473
2.11M
    return true;
474
2.11M
  }
475
476
30.9k
  return false;
477
2.14M
}
478
479
static bool read_nestedclass(
480
    const CLASS_CONTEXT* ctx,
481
    const uint8_t* data,
482
    NESTEDCLASS_ROW* result)
483
78.0M
{
484
78.0M
  uint32_t row_size = ctx->tables->nestedclass.RowSize;
485
486
78.0M
  if (fits_in_pe(ctx->pe, data, row_size))
487
20.9M
  {
488
20.9M
    result->NestedClass = read_index(&data, ctx->index_sizes->typedef_);
489
20.9M
    result->EnclosingClass = read_index(&data, ctx->index_sizes->typedef_);
490
20.9M
    return true;
491
20.9M
  }
492
493
57.0M
  return false;
494
78.0M
}
495
496
// ECMA-335 II.23.2 blob heap uses variable length encoding of integers
497
static uint32_t read_blob_unsigned(const uint8_t** data, uint32_t* len)
498
6.60M
{
499
6.60M
  if (*len < 1)
500
72.3k
    return 0;
501
502
  // first byte is enough to decode the length
503
  // without worrying about endiannity
504
  // Compressed integers use big-endian order
505
6.53M
  uint8_t first_byte = *(*data);
506
507
  // If the value lies between 0 (0x00) and 127 (0x7F), inclusive, encode as a
508
  // one-byte integer (bit 7 is clear, value held in bits 6 through 0)
509
6.53M
  if (!(first_byte & 0x80))
510
6.29M
  {
511
6.29M
    *data += sizeof(uint8_t);
512
6.29M
    *len -= sizeof(uint8_t);
513
6.29M
    return first_byte;
514
6.29M
  }
515
516
234k
  if (*len < 2)
517
4.19k
    return 0;
518
519
  // If the value lies between 2^8 (0x80) and 2^14 – 1 (0x3FFF), inclusive,
520
  // encode as a 2-byte integer with bit 15 set, bit 14 clear (value held in
521
  // bits 13 through 0)
522
230k
  if ((first_byte & 0xC0) == 0x80)
523
86.9k
  {
524
86.9k
    uint32_t result = yr_be16toh(yr_unaligned_u16(*data));
525
86.9k
    *data += sizeof(uint16_t);
526
86.9k
    *len -= sizeof(uint16_t);
527
    // value is in lower 14 bits
528
86.9k
    return result & 0x3FFF;
529
86.9k
  }
530
531
143k
  if (*len < 4)
532
2.68k
    return 0;
533
534
  // Otherwise, encode as a 4-byte integer, with bit 31 set, bit 30 set,
535
  // bit 29 clear (value held in bits 28 through 0)
536
140k
  if ((first_byte & 0xE0) == 0xC0)
537
36.7k
  {
538
36.7k
    uint32_t result = yr_be32toh(yr_unaligned_u32(*data));
539
36.7k
    *data += sizeof(uint32_t);
540
36.7k
    *len -= sizeof(uint32_t);
541
    // Uses last 29 bits for the result
542
36.7k
    return result & 0x1FFFFFFF;
543
36.7k
  }
544
545
103k
  return 0;
546
140k
}
547
548
// ECMA-335 II.23.2 blob heap uses variable length encoding of integers
549
// Probably wouldn't work on non 2's complement arches?
550
static int32_t read_blob_signed(const uint8_t** data, uint32_t* len)
551
77.0k
{
552
  // Compressed integers use big-endian order!
553
77.0k
  if (*len < 1)
554
10.2k
    return 0;
555
556
  // first byte is enough to decode the length
557
  // without worrying about endiannity
558
66.7k
  int8_t first_byte = *(*data);
559
560
  // Encode as a one-byte integer, bit 7 clear, rotated value in bits 6
561
  // through 0, giving 0x01 (-2^6) to 0x7E (2^6-1).
562
66.7k
  if (!(first_byte & 0x80))
563
43.9k
  {
564
43.9k
    int8_t tmp = first_byte >> 1;
565
    // sign extension in case of negative number
566
43.9k
    if (first_byte & 0x1)
567
10.5k
      tmp |= 0xC0;
568
569
43.9k
    *data += sizeof(uint8_t);
570
43.9k
    *len -= sizeof(uint8_t);
571
572
43.9k
    return (int32_t) tmp;
573
43.9k
  }
574
575
22.8k
  if (*len < 2)
576
1.41k
    return 0;
577
578
  // Encode as a two-byte integer: bit 15 set, bit 14 clear, rotated value
579
  // in bits 13 through 0, giving 0x8001 (-2^13) to 0xBFFE (2^13-1).
580
21.4k
  if ((first_byte & 0xC0) == 0x80)
581
5.04k
  {
582
5.04k
    uint16_t tmp1 = yr_be16toh(yr_unaligned_u16(*data));
583
    // shift and leave top 2 bits clear
584
5.04k
    int16_t tmp2 = (tmp1 >> 1) & 0x3FFF;
585
    // sign extension in case of negative number
586
5.04k
    if (tmp1 & 0x1)
587
1.83k
      tmp2 |= 0xE000;
588
589
5.04k
    *data += sizeof(uint16_t);
590
5.04k
    *len -= sizeof(uint16_t);
591
592
5.04k
    return (int32_t) tmp2;
593
5.04k
  }
594
595
16.3k
  if (*len < 4)
596
2.52k
    return 0;
597
598
  // Encode as a four-byte integer: bit 31 set, 30 set, bit 29 clear,
599
  // rotated value in bits 28 through 0, giving 0xC0000001 (-2^28) to
600
  // 0xDFFFFFFE (2^28-1).
601
13.8k
  if ((first_byte & 0xE0) == 0xC0)
602
5.17k
  {
603
5.17k
    uint32_t tmp1 = yr_be32toh(yr_unaligned_u32(*data));
604
    // shift and leave top 3 bits clear
605
5.17k
    int32_t tmp2 = (tmp1 >> 1) & 0x1FFFFFFF;
606
    // sign extension in case of negative number
607
5.17k
    if (tmp1 & 0x1)
608
3.40k
      tmp2 |= 0xF0000000;
609
610
5.17k
    *data += sizeof(uint32_t);
611
5.17k
    *len -= sizeof(uint32_t);
612
613
5.17k
    return (int32_t) tmp2;
614
5.17k
  }
615
616
8.67k
  return 0;
617
13.8k
}
618
619
// Forward declarations
620
static char* parse_signature_type(
621
    const CLASS_CONTEXT* ctx,
622
    const uint8_t** data,
623
    uint32_t* len,
624
    GENERIC_PARAMETERS* class_gen_params,
625
    GENERIC_PARAMETERS* method_gen_params,
626
    uint32_t depth);
627
628
static char* parse_enclosing_types(
629
    const CLASS_CONTEXT* ctx,
630
    uint32_t nested_idx,
631
    uint32_t depth);
632
633
static char* get_type_def_or_ref_fullname(
634
    const CLASS_CONTEXT* ctx,
635
    uint32_t coded_index,
636
    GENERIC_PARAMETERS* class_gen_params,
637
    GENERIC_PARAMETERS* method_gen_params,
638
    uint32_t depth)  // against loops
639
2.93M
{
640
  // first 2 bits define table, index starts with third bit
641
2.93M
  uint32_t index = coded_index >> 2;
642
2.93M
  if (!index)
643
116k
    return NULL;
644
645
2.81M
  const uint8_t* str_heap = ctx->str_heap;
646
2.81M
  uint32_t str_size = ctx->str_size;
647
648
2.81M
  uint8_t table = coded_index & 0x3;
649
2.81M
  if (table == 0)  // TypeDef
650
525k
  {
651
525k
    const uint8_t* data = get_table_offset(&ctx->tables->typedef_, index);
652
525k
    if (!data)
653
87.7k
      return NULL;
654
655
437k
    TYPEDEF_ROW def_row;
656
437k
    bool result = read_typedef(ctx, data, &def_row);
657
437k
    if (result)
658
437k
    {
659
437k
      const char* name = pe_get_dotnet_string(
660
437k
          ctx->pe, str_heap, str_size, def_row.Name);
661
437k
      const char* namespace = pe_get_dotnet_string(
662
437k
          ctx->pe, str_heap, str_size, def_row.Namespace);
663
664
437k
      char* result = NULL;
665
      // Type might be nested, try to find correct namespace
666
437k
      if (is_nested(def_row.Flags))
667
61.2k
      {
668
61.2k
        char* nested_namespace = parse_enclosing_types(ctx, index, 1);
669
61.2k
        char* tmp = create_full_name(namespace, nested_namespace);
670
61.2k
        result = create_full_name(name, tmp);
671
61.2k
        yr_free(nested_namespace);
672
61.2k
        yr_free(tmp);
673
61.2k
      }
674
375k
      else
675
375k
        result = create_full_name(name, namespace);
676
677
437k
      return result;
678
437k
    }
679
437k
  }
680
2.28M
  else if (table == 1)  // TypeRef
681
97.8k
  {
682
97.8k
    const uint8_t* data = get_table_offset(&ctx->tables->typeref, index);
683
97.8k
    if (!data)
684
87.7k
      return NULL;
685
686
10.0k
    TYPEREF_ROW ref_row;
687
10.0k
    bool result = read_typeref(ctx, data, &ref_row);
688
10.0k
    if (result)
689
10.0k
    {
690
10.0k
      const char* name = pe_get_dotnet_string(
691
10.0k
          ctx->pe, str_heap, str_size, ref_row.Name);
692
10.0k
      const char* namespace = pe_get_dotnet_string(
693
10.0k
          ctx->pe, str_heap, str_size, ref_row.Namespace);
694
695
10.0k
      return create_full_name(name, namespace);
696
10.0k
    }
697
10.0k
  }
698
2.19M
  else if (table == 2)  // TypeSpec
699
2.16M
  {
700
2.16M
    const uint8_t* data = get_table_offset(&ctx->tables->typespec, index);
701
2.16M
    if (!data)
702
20.4k
      return NULL;
703
704
2.14M
    TYPESPEC_ROW spec_row;
705
2.14M
    bool result = read_typespec(ctx, data, &spec_row);
706
2.14M
    if (result)
707
2.11M
    {
708
2.11M
      const uint8_t* sig_data = ctx->blob_heap + spec_row.Signature;
709
710
      // Read the blob entry with the data
711
2.11M
      BLOB_PARSE_RESULT blob_res = dotnet_parse_blob_entry(ctx->pe, sig_data);
712
2.11M
      sig_data += blob_res.size;
713
2.11M
      uint32_t sig_len = blob_res.length;
714
715
      // Valid blob
716
2.11M
      if (blob_res.size)
717
2.09M
        return parse_signature_type(
718
2.09M
            ctx, &sig_data, &sig_len, class_gen_params, NULL, depth);
719
2.11M
    }
720
2.14M
  }
721
71.8k
  return NULL;
722
2.81M
}
723
724
static char* parse_signature_type(
725
    const CLASS_CONTEXT* ctx,
726
    const uint8_t** data,
727
    uint32_t* len,
728
    GENERIC_PARAMETERS* class_gen_params,
729
    GENERIC_PARAMETERS* method_gen_params,
730
    uint32_t depth  // against loops
731
)
732
53.6M
{
733
  // If at least first type fits and we are not too nested
734
53.6M
  if (*len < 1 || !fits_in_pe(ctx->pe, *data, 1) || depth > MAX_TYPE_DEPTH)
735
7.09M
    return NULL;
736
737
53.6M
  bool class = false;
738
46.5M
  uint32_t coded_index, index;
739
46.5M
  char* tmp = NULL;
740
46.5M
  char* ret_type = NULL;
741
742
46.5M
  uint8_t type = read_u8(data);
743
46.5M
  *len -= 1;
744
745
46.5M
  switch (type)
746
46.5M
  {
747
668k
  case TYPE_VOID:
748
668k
    ret_type = "void";
749
668k
    break;
750
751
1.05M
  case TYPE_BOOL:
752
1.05M
    ret_type = "bool";
753
1.05M
    break;
754
755
86.2k
  case TYPE_CHAR:
756
86.2k
    ret_type = "char";
757
86.2k
    break;
758
759
815k
  case TYPE_I1:
760
815k
    ret_type = "sbyte";
761
815k
    break;
762
763
917k
  case TYPE_U1:
764
917k
    ret_type = "byte";
765
917k
    break;
766
767
556k
  case TYPE_I2:
768
556k
    ret_type = "short";
769
556k
    break;
770
771
31.6k
  case TYPE_U2:
772
31.6k
    ret_type = "ushort";
773
31.6k
    break;
774
775
694k
  case TYPE_I4:
776
694k
    ret_type = "int";
777
694k
    break;
778
779
36.5k
  case TYPE_U4:
780
36.5k
    ret_type = "uint";
781
36.5k
    break;
782
783
119k
  case TYPE_I8:
784
119k
    ret_type = "long";
785
119k
    break;
786
787
116k
  case TYPE_U8:
788
116k
    ret_type = "ulong";
789
116k
    break;
790
791
10.6k
  case TYPE_R4:
792
10.6k
    ret_type = "float";
793
10.6k
    break;
794
795
394k
  case TYPE_R8:
796
394k
    ret_type = "double";
797
394k
    break;
798
799
31.3k
  case TYPE_STRING:
800
31.3k
    ret_type = "string";
801
31.3k
    break;
802
803
319k
  case TYPE_TYPEDREF:
804
319k
    ret_type = "TypedReference";
805
319k
    break;
806
807
816k
  case TYPE_I:
808
816k
    ret_type = "IntPtr";
809
816k
    break;
810
811
87.2k
  case TYPE_U:
812
87.2k
    ret_type = "UIntPtr";
813
87.2k
    break;
814
815
212k
  case TYPE_PTR:  // Ptr followed by type
816
212k
    tmp = parse_signature_type(
817
212k
        ctx, data, len, class_gen_params, method_gen_params, depth + 1);
818
212k
    if (tmp)
819
86.6k
    {
820
86.6k
      SIMPLE_STR* ss = sstr_new(NULL);
821
86.6k
      if (!ss)
822
0
      {
823
0
        yr_free(tmp);
824
0
        break;
825
0
      }
826
86.6k
      bool res = sstr_appendf(ss, "Ptr<%s>", tmp);
827
86.6k
      if (res)
828
86.6k
        ret_type = sstr_move(ss);
829
830
86.6k
      yr_free(tmp);
831
86.6k
      sstr_free(ss);
832
86.6k
      return ret_type;
833
86.6k
    }
834
125k
    break;
835
836
138k
  case TYPE_BYREF:
837
    // ByRef followed by type
838
138k
    tmp = parse_signature_type(
839
138k
        ctx, data, len, class_gen_params, method_gen_params, depth + 1);
840
138k
    if (tmp)
841
18.1k
    {
842
18.1k
      SIMPLE_STR* ss = sstr_new(NULL);
843
18.1k
      if (!ss)
844
0
      {
845
0
        yr_free(tmp);
846
0
        break;
847
0
      }
848
18.1k
      bool res = sstr_appendf(ss, "ref %s", tmp);
849
18.1k
      if (res)
850
18.1k
        ret_type = sstr_move(ss);
851
852
18.1k
      yr_free(tmp);
853
18.1k
      sstr_free(ss);
854
18.1k
      return ret_type;
855
18.1k
    }
856
120k
    break;
857
858
2.57M
  case TYPE_VALUETYPE:  // ValueType
859
2.71M
  case TYPE_CLASS:      // Class
860
    // followed by TypeDefOrRefOrSpecEncoded index
861
2.71M
    coded_index = read_blob_unsigned(data, len);
862
2.71M
    return get_type_def_or_ref_fullname(
863
2.71M
        ctx, coded_index, class_gen_params, method_gen_params, depth + 1);
864
0
    break;
865
866
34.0k
  case TYPE_VAR:   // Generic class var
867
179k
  case TYPE_MVAR:  // Generic method var
868
179k
    index = read_blob_unsigned(data, len);
869
179k
    class = type == TYPE_VAR;
870
    // return class generic var or method generic var
871
179k
    if (class && class_gen_params && index < class_gen_params->len)
872
202
      ret_type = class_gen_params->names[index];
873
179k
    else if (!class && method_gen_params && index < method_gen_params->len)
874
361
      ret_type = method_gen_params->names[index];
875
179k
    break;
876
877
567k
  case TYPE_ARRAY:
878
567k
  {
879
    // Array -> Type -> Rank -> NumSizes -> Size -> NumLobound -> LoBound
880
567k
    char* tmp = parse_signature_type(
881
567k
        ctx, data, len, class_gen_params, method_gen_params, depth + 1);
882
567k
    if (!tmp)
883
440k
      break;
884
885
127k
    int32_t* sizes = NULL;
886
127k
    int32_t* lo_bounds = NULL;
887
888
    // Read number of dimensions
889
127k
    uint32_t rank = read_blob_unsigned(data, len);
890
127k
    if (!rank || rank > MAX_ARRAY_RANK)
891
77.7k
      goto cleanup;
892
893
    // Read number of specified sizes
894
49.2k
    uint32_t num_sizes = read_blob_unsigned(data, len);
895
49.2k
    if (num_sizes > rank)
896
1.12k
      goto cleanup;
897
48.1k
    sizes = yr_malloc(sizeof(int64_t) * num_sizes);
898
48.1k
    if (!sizes)
899
0
      goto cleanup;
900
901
199k
    for (uint32_t i = 0; i < num_sizes; ++i)
902
151k
    {
903
151k
      sizes[i] = (int64_t) read_blob_unsigned(data, len);
904
151k
    }
905
906
    // Read number of specified lower bounds
907
48.1k
    uint32_t num_lowbounds = read_blob_unsigned(data, len);
908
48.1k
    lo_bounds = yr_malloc(sizeof(int32_t) * num_lowbounds);
909
48.1k
    if (!lo_bounds || num_lowbounds > rank)
910
4.68k
      goto cleanup;
911
912
120k
    for (uint32_t i = 0; i < num_lowbounds; ++i)
913
77.0k
    {
914
77.0k
      lo_bounds[i] = read_blob_signed(data, len);
915
916
      // Adjust higher bound according to lower bound
917
77.0k
      if (num_sizes > i && lo_bounds[i] != 0)
918
18.3k
        sizes[i] += lo_bounds[i] - 1;
919
77.0k
    }
920
921
    // Build the resulting array type
922
43.4k
    SIMPLE_STR* ss = sstr_new(NULL);
923
43.4k
    if (!ss)
924
0
      goto cleanup;
925
926
43.4k
    sstr_appendf(ss, "%s[", tmp);
927
928
1.90M
    for (uint32_t i = 0; i < rank; ++i)
929
1.86M
    {
930
1.86M
      if (num_sizes > i || num_lowbounds > i)
931
193k
      {
932
193k
        if (num_lowbounds > i && lo_bounds[i] != 0)
933
38.1k
          sstr_appendf(ss, "%d...", lo_bounds[i]);
934
193k
        if (num_sizes > i)
935
141k
          sstr_appendf(ss, "%d", sizes[i]);
936
193k
      }
937
1.86M
      if (i + 1 != rank)
938
1.82M
        sstr_appendf(ss, ",");
939
1.86M
    }
940
43.4k
    bool res = sstr_appendf(ss, "]");
941
43.4k
    if (res)
942
43.4k
      ret_type = sstr_move(ss);
943
944
43.4k
    yr_free(sizes);
945
43.4k
    yr_free(lo_bounds);
946
43.4k
    yr_free(tmp);
947
43.4k
    sstr_free(ss);
948
43.4k
    return ret_type;
949
950
83.5k
  cleanup:
951
83.5k
    yr_free(sizes);
952
83.5k
    yr_free(lo_bounds);
953
83.5k
    yr_free(tmp);
954
83.5k
  }
955
0
  break;
956
957
102k
  case TYPE_GENERICINST:
958
102k
  {
959
102k
    tmp = parse_signature_type(
960
102k
        ctx, data, len, class_gen_params, method_gen_params, depth + 1);
961
962
102k
    if (!tmp)
963
51.2k
      break;
964
965
51.3k
    uint32_t gen_count = read_blob_unsigned(data, len);
966
967
    // Sanity check for corrupted files
968
51.3k
    if (gen_count > MAX_GEN_PARAM_COUNT)
969
2.06k
    {
970
2.06k
      yr_free(tmp);
971
2.06k
      break;
972
2.06k
    }
973
974
49.2k
    SIMPLE_STR* ss = sstr_new(NULL);
975
49.2k
    if (!ss)
976
0
    {
977
0
      yr_free(tmp);
978
0
      break;
979
0
    }
980
49.2k
    sstr_appendf(ss, "%s<", tmp);
981
49.2k
    yr_free(tmp);
982
983
3.24M
    for (int i = 0; i < gen_count; i++)
984
3.19M
    {
985
3.19M
      char* param_type = parse_signature_type(
986
3.19M
          ctx, data, len, class_gen_params, method_gen_params, depth + 1);
987
988
3.19M
      if (param_type != NULL)
989
509k
      {
990
509k
        if (i > 0)
991
502k
          sstr_appendf(ss, ",");
992
993
509k
        sstr_appendf(ss, "%s", param_type);
994
509k
        yr_free(param_type);
995
509k
      }
996
3.19M
    }
997
49.2k
    bool res = sstr_appendf(ss, ">");
998
49.2k
    if (res)
999
49.2k
      ret_type = sstr_move(ss);
1000
1001
49.2k
    sstr_free(ss);
1002
49.2k
    return ret_type;
1003
49.2k
  }
1004
0
  break;
1005
1006
2.03M
  case TYPE_FNPTR:
1007
2.03M
    if (*len > 0)
1008
2.03M
    {  // Flags -> ParamCount -> RetType -> Param -> Sentinel ->Param
1009
      // Skip flags
1010
2.03M
      (*data)++;
1011
2.03M
      (*len)--;
1012
1013
2.03M
      uint32_t param_count = read_blob_unsigned(data, len);
1014
1015
      // Sanity check for corrupted files
1016
2.03M
      if (param_count > MAX_PARAM_COUNT)
1017
1.26k
      {
1018
1.26k
        yr_free(tmp);
1019
1.26k
        break;
1020
1.26k
      }
1021
1022
2.03M
      tmp = parse_signature_type(
1023
2.03M
          ctx, data, len, class_gen_params, method_gen_params, depth + 1);
1024
1025
2.03M
      if (!tmp)
1026
1.47M
        break;
1027
1028
558k
      SIMPLE_STR* ss = sstr_new(NULL);
1029
558k
      if (!ss)
1030
0
      {
1031
0
        yr_free(tmp);
1032
0
        break;
1033
0
      }
1034
1035
558k
      sstr_appendf(ss, "FnPtr<%s(", tmp);
1036
558k
      yr_free(tmp);
1037
1038
44.5M
      for (int i = 0; i < param_count; i++)
1039
43.9M
      {
1040
43.9M
        char* param_type = parse_signature_type(
1041
43.9M
            ctx, data, len, class_gen_params, method_gen_params, depth + 1);
1042
1043
43.9M
        if (param_type != NULL)
1044
6.44M
        {
1045
6.44M
          if (i > 0)
1046
6.37M
            sstr_appendf(ss, ", ");
1047
1048
6.44M
          sstr_appendf(ss, "%s", param_type);
1049
6.44M
          yr_free(param_type);
1050
6.44M
        }
1051
43.9M
      }
1052
1053
558k
      if (sstr_appendf(ss, ")>"))
1054
558k
        ret_type = sstr_move(ss);
1055
1056
558k
      sstr_free(ss);
1057
558k
      return ret_type;
1058
558k
    }
1059
511
    break;
1060
1061
107k
  case TYPE_OBJECT:
1062
107k
    ret_type = "object";
1063
107k
    break;
1064
1065
115k
  case TYPE_SZARRAY:
1066
    // Single dimensional array followed by type
1067
115k
    tmp = parse_signature_type(
1068
115k
        ctx, data, len, class_gen_params, method_gen_params, depth + 1);
1069
115k
    if (tmp)
1070
17.1k
    {
1071
17.1k
      SIMPLE_STR* ss = sstr_newf("%s[]", tmp);
1072
17.1k
      if (ss)
1073
17.1k
        ret_type = sstr_move(ss);
1074
1075
17.1k
      yr_free(tmp);
1076
17.1k
      sstr_free(ss);
1077
17.1k
      return ret_type;
1078
17.1k
    }
1079
97.9k
    break;
1080
1081
386k
  case TYPE_CMOD_REQD:  // Req modifier
1082
657k
  case TYPE_CMOD_OPT:   // Opt modifier
1083
657k
  {
1084
    // What is point of these
1085
    // Right now ignore them...
1086
657k
    read_blob_unsigned(data, len);
1087
657k
    return parse_signature_type(
1088
657k
        ctx, data, len, class_gen_params, method_gen_params, depth + 1);
1089
386k
  }
1090
0
  break;
1091
1092
33.0M
  default:
1093
33.0M
    break;
1094
46.5M
  }
1095
1096
42.4M
  if (ret_type)
1097
6.86M
    return yr_strdup(ret_type);
1098
35.5M
  else
1099
35.5M
    return NULL;
1100
42.4M
}
1101
1102
static void parse_type_parents(
1103
    const CLASS_CONTEXT* ctx,
1104
    uint32_t extends,
1105
    uint32_t type_idx,
1106
    uint32_t out_idx,  // Class idx in output array
1107
    GENERIC_PARAMETERS* class_gen_params)
1108
208k
{
1109
  // Find the parent class
1110
208k
  char* parent = get_type_def_or_ref_fullname(
1111
208k
      ctx, extends, class_gen_params, NULL, 0);
1112
1113
208k
  uint32_t base_type_idx = 0;
1114
208k
  if (parent)
1115
25.5k
  {
1116
25.5k
    yr_set_string(
1117
25.5k
        parent,
1118
25.5k
        ctx->pe->object,
1119
25.5k
        "classes[%i].base_types[%i]",
1120
25.5k
        out_idx,
1121
25.5k
        base_type_idx++);
1122
1123
25.5k
    yr_free(parent);
1124
25.5k
  }
1125
1126
  // linear search for every interface that the class implements
1127
4.37M
  for (uint32_t idx = 0; idx < ctx->tables->intefaceimpl.RowCount; ++idx)
1128
4.16M
  {
1129
4.16M
    const uint8_t* data = get_table_offset(&ctx->tables->intefaceimpl, idx + 1);
1130
4.16M
    if (!data)
1131
0
      break;
1132
1133
4.16M
    INTERFACEIMPL_ROW row = {0};
1134
4.16M
    bool result = read_interfaceimpl(ctx, data, &row);
1135
4.16M
    if (!result)
1136
1.74M
      continue;
1137
1138
    // We found the inherited interface
1139
2.42M
    if (row.Class == type_idx)
1140
2.61k
    {
1141
2.61k
      char* inteface = get_type_def_or_ref_fullname(
1142
2.61k
          ctx, row.Interface, class_gen_params, NULL, 0);
1143
2.61k
      if (inteface)
1144
904
      {
1145
904
        yr_set_string(
1146
904
            inteface,
1147
904
            ctx->pe->object,
1148
904
            "classes[%i].base_types[%i]",
1149
904
            out_idx,
1150
904
            base_type_idx++);
1151
1152
904
        yr_free(inteface);
1153
904
      }
1154
2.61k
    }
1155
2.42M
  }
1156
208k
  yr_set_integer(
1157
208k
      base_type_idx,
1158
208k
      ctx->pe->object,
1159
208k
      "classes[%i].number_of_base_types",
1160
208k
      out_idx);
1161
208k
}
1162
1163
// Returns true if all parameters were correctly parsed
1164
static bool parse_method_params(
1165
    const CLASS_CONTEXT* ctx,
1166
    uint32_t param_list,
1167
    uint32_t method_idx,  // used for output
1168
    uint32_t class_idx,
1169
    uint32_t param_count,
1170
    const uint8_t* sig_data,
1171
    uint32_t sig_len,
1172
    GENERIC_PARAMETERS* class_gen_params,
1173
    GENERIC_PARAMETERS* method_gen_params)
1174
56.5k
{
1175
56.5k
  if (!param_list)  // NULL
1176
13.7k
    return true;
1177
1178
42.7k
  const uint8_t* str_heap = ctx->str_heap;
1179
42.7k
  uint32_t str_size = ctx->str_size;
1180
1181
  // Array to hold all the possible parameters
1182
42.7k
  PARAMETERS* params = yr_calloc(param_count, sizeof(PARAMETERS));
1183
1184
42.7k
  if (params == NULL && param_count > 0)
1185
0
    return false;
1186
1187
162k
  for (uint32_t idx = 0; idx < param_count; ++idx)
1188
148k
  {
1189
148k
    const uint8_t* data = get_table_offset(
1190
148k
        &ctx->tables->param, param_list + idx);
1191
1192
148k
    char* name = NULL;
1193
148k
    bool alloc = false;  // Flag if name needs freeing
1194
1195
148k
    if (data)  // We need param table mostly just for the param name
1196
8.62k
    {
1197
8.62k
      PARAM_ROW row = {0};
1198
8.62k
      bool result = read_param(ctx, data, &row);
1199
1200
8.62k
      if (!result)
1201
614
      {  // Cleanup and return
1202
1.58k
        for (uint32_t j = 0; j < idx; ++j)
1203
970
        {
1204
970
          if (params[j].alloc)
1205
0
            yr_free(params[j].name);
1206
1207
970
          yr_free(params[j].type);
1208
970
        }
1209
614
        yr_free(params);
1210
614
        return false;
1211
614
      }
1212
1213
8.01k
      name = pe_get_dotnet_string(ctx->pe, str_heap, str_size, row.Name);
1214
8.01k
    }
1215
140k
    else  // We can reconstruct their type from the signature
1216
          // and give them default name
1217
140k
    {
1218
140k
      alloc = true;
1219
140k
      SIMPLE_STR* ss = sstr_newf("P_%lu", idx);
1220
140k
      if (ss)
1221
140k
      {
1222
140k
        name = sstr_move(ss);
1223
140k
        sstr_free(ss);
1224
140k
      }
1225
140k
    }
1226
1227
148k
    char* type = parse_signature_type(
1228
148k
        ctx, &sig_data, &sig_len, class_gen_params, method_gen_params, 0);
1229
1230
148k
    params[idx].alloc = alloc;
1231
148k
    params[idx].name = name;
1232
148k
    params[idx].type = type;
1233
1234
148k
    if (!type)  // If any param fails, whole parsing is aborted
1235
28.6k
    {
1236
120k
      for (uint32_t j = 0; j <= idx; ++j)
1237
91.6k
      {
1238
91.6k
        if (params[j].alloc)
1239
87.9k
          yr_free(params[j].name);
1240
1241
91.6k
        yr_free(params[j].type);
1242
91.6k
      }
1243
28.6k
      yr_free(params);
1244
28.6k
      return false;
1245
28.6k
    }
1246
148k
  }
1247
1248
  // If we got all of them correctly, write to output and cleanup
1249
13.5k
  YR_OBJECT* out_obj = ctx->pe->object;
1250
13.5k
  yr_set_integer(
1251
13.5k
      param_count,
1252
13.5k
      out_obj,
1253
13.5k
      "classes[%i].methods[%i].number_of_parameters",
1254
13.5k
      class_idx,
1255
13.5k
      method_idx);
1256
1257
69.1k
  for (uint32_t i = 0; i < param_count; ++i)
1258
55.6k
  {
1259
55.6k
    yr_set_string(
1260
55.6k
        params[i].name,
1261
55.6k
        out_obj,
1262
55.6k
        "classes[%i].methods[%i].parameters[%i].name",
1263
55.6k
        class_idx,
1264
55.6k
        method_idx,
1265
55.6k
        i);
1266
55.6k
    yr_set_string(
1267
55.6k
        params[i].type,
1268
55.6k
        out_obj,
1269
55.6k
        "classes[%i].methods[%i].parameters[%i].type",
1270
55.6k
        class_idx,
1271
55.6k
        method_idx,
1272
55.6k
        i);
1273
55.6k
    if (params[i].alloc)
1274
52.3k
      yr_free(params[i].name);
1275
1276
55.6k
    yr_free(params[i].type);
1277
55.6k
  }
1278
1279
13.5k
  yr_free(params);
1280
13.5k
  return true;
1281
42.7k
}
1282
1283
// Walks GenericParam table, finds all generic params for the MethodDef or
1284
// TypeDef entry and allocates buffer with the Generic param names into result
1285
static void parse_generic_params(
1286
    const CLASS_CONTEXT* ctx,
1287
    bool method,  // true means MethodDef, false TypeDef index
1288
    uint32_t gen_idx,
1289
    GENERIC_PARAMETERS* result)
1290
1.05M
{
1291
1.05M
  const uint8_t* str_heap = ctx->str_heap;
1292
1.05M
  uint32_t str_size = ctx->str_size;
1293
1294
1.05M
  result->names = NULL;
1295
1.05M
  result->len = 0;
1296
1297
  // Walk the GenericParam table to find GenParameters of the class/method
1298
111M
  for (uint32_t idx = 0; idx < ctx->tables->genericparam.RowCount; ++idx)
1299
110M
  {
1300
110M
    const uint8_t* data = get_table_offset(&ctx->tables->genericparam, idx + 1);
1301
110M
    if (!data)
1302
0
      goto cleanup;
1303
1304
110M
    GENERICPARAM_ROW row = {0};
1305
110M
    bool read_result = read_genericparam(ctx, data, &row);
1306
110M
    if (!read_result)
1307
87.2M
      continue;
1308
1309
    // TypeOrMethodDef coded index
1310
23.0M
    uint8_t table = row.Owner & 0x1;
1311
    // 0 == TypeDef 1 == MethodDef
1312
    // Check if it's generic param of the type we want
1313
23.0M
    if (table == method && (row.Owner >> 1) == gen_idx)
1314
4.61k
    {
1315
4.61k
      char* name = pe_get_dotnet_string(ctx->pe, str_heap, str_size, row.Name);
1316
      // name must be valid string
1317
4.61k
      if (!name || !*name)  // ERROR
1318
1.86k
        goto cleanup;
1319
1320
2.75k
      result->len += 1;
1321
2.75k
      char** tmp = yr_realloc(result->names, result->len * sizeof(char*));
1322
2.75k
      if (!tmp)
1323
0
        goto cleanup;
1324
1325
      // Update the collection
1326
2.75k
      result->names = tmp;
1327
2.75k
      result->names[result->len - 1] = name;
1328
2.75k
    }
1329
23.0M
  }
1330
1.05M
  return;
1331
1332
1.05M
cleanup:
1333
1.86k
  yr_free(result->names);
1334
1.86k
  result->names = NULL;
1335
1.86k
  result->len = 0;
1336
1.86k
}
1337
1338
static void parse_methods(
1339
    const CLASS_CONTEXT* ctx,
1340
    uint32_t methodlist,
1341
    uint32_t method_count,
1342
    uint32_t class_idx,  // class index in the YARA output
1343
    GENERIC_PARAMETERS* class_gen_params)
1344
175k
{
1345
175k
  if (!methodlist)
1346
49.4k
    return;
1347
1348
126k
  const uint8_t* str_heap = ctx->str_heap;
1349
126k
  uint32_t str_size = ctx->str_size;
1350
1351
126k
  uint32_t out_idx = 0;
1352
4.92M
  for (uint32_t idx = 0; idx < method_count; ++idx)
1353
4.82M
  {
1354
4.82M
    const uint8_t* data = get_table_offset(
1355
4.82M
        &ctx->tables->methoddef, methodlist + idx);
1356
1357
4.82M
    if (!data)
1358
27.9k
      break;
1359
1360
4.80M
    METHODDEF_ROW row = {0};
1361
4.80M
    bool result = read_methoddef(ctx, data, &row);
1362
4.80M
    if (!result)
1363
3.33M
      continue;
1364
1365
1.47M
    const char* name = pe_get_dotnet_string(
1366
1.47M
        ctx->pe, str_heap, str_size, row.Name);
1367
1368
    // Ignore invalid/empty names
1369
1.47M
    if (!name || !*name)
1370
621k
      continue;
1371
1372
    // Try to find generic params for the method
1373
848k
    GENERIC_PARAMETERS method_gen_params = {0};
1374
848k
    parse_generic_params(ctx, true, methodlist + idx, &method_gen_params);
1375
1376
    // Read the blob entry with signature data
1377
848k
    const uint8_t* sig_data = ctx->blob_heap + row.Signature;
1378
1379
848k
    BLOB_PARSE_RESULT blob_res = dotnet_parse_blob_entry(ctx->pe, sig_data);
1380
848k
    sig_data += blob_res.size;
1381
848k
    uint32_t sig_len = blob_res.length;
1382
848k
    uint32_t param_count = 0;
1383
1384
848k
    char* return_type = NULL;
1385
    // If there is valid blob and at least minimum to parse
1386
    // (flags, paramCount, retType) parse these basic information
1387
848k
    if (blob_res.size && sig_len >= 3)
1388
424k
    {
1389
424k
      uint8_t flags = read_u8(&sig_data);
1390
424k
      sig_len -= 1;
1391
424k
      if (flags & SIG_FLAG_GENERIC)
1392
        // Generic param count, ignored as we get the
1393
        // information from generic param table
1394
163k
        (void) read_blob_unsigned(&sig_data, &sig_len);
1395
1396
      // Regular param count
1397
424k
      param_count = read_blob_unsigned(&sig_data, &sig_len);
1398
424k
      return_type = parse_signature_type(
1399
424k
          ctx, &sig_data, &sig_len, class_gen_params, &method_gen_params, 0);
1400
424k
    }
1401
424k
    else  // Error, skip
1402
424k
      goto clean_next;
1403
1404
    // Sanity check for corrupted files
1405
424k
    if (!return_type || param_count > MAX_PARAM_COUNT)
1406
367k
      goto clean_next;
1407
1408
56.5k
    result = parse_method_params(
1409
56.5k
        ctx,
1410
56.5k
        row.ParamList,
1411
56.5k
        out_idx,
1412
56.5k
        class_idx,
1413
56.5k
        param_count,
1414
56.5k
        sig_data,
1415
56.5k
        sig_len,
1416
56.5k
        class_gen_params,
1417
56.5k
        &method_gen_params);
1418
1419
56.5k
    if (!result)
1420
29.2k
      goto clean_next;
1421
1422
27.3k
    const char* visibility = get_method_visibility(row.Flags);
1423
27.3k
    uint32_t stat = (row.Flags & METHOD_ATTR_STATIC) != 0;
1424
27.3k
    uint32_t final = (row.Flags & METHOD_ATTR_FINAL) != 0;
1425
27.3k
    uint32_t virtual = (row.Flags & METHOD_ATTR_VIRTUAL) != 0;
1426
27.3k
    uint32_t abstract = (row.Flags & METHOD_ATTR_ABSTRACT) != 0;
1427
1428
27.3k
    YR_OBJECT* out_obj = ctx->pe->object;
1429
27.3k
    yr_set_string(
1430
27.3k
        name, out_obj, "classes[%i].methods[%i].name", class_idx, out_idx);
1431
27.3k
    yr_set_string(
1432
27.3k
        visibility,
1433
27.3k
        out_obj,
1434
27.3k
        "classes[%i].methods[%i].visibility",
1435
27.3k
        class_idx,
1436
27.3k
        out_idx);
1437
27.3k
    yr_set_integer(
1438
27.3k
        stat, out_obj, "classes[%i].methods[%i].static", class_idx, out_idx);
1439
27.3k
    yr_set_integer(
1440
27.3k
        virtual,
1441
27.3k
        out_obj,
1442
27.3k
        "classes[%i].methods[%i].virtual",
1443
27.3k
        class_idx,
1444
27.3k
        out_idx);
1445
27.3k
    yr_set_integer(
1446
27.3k
        final, out_obj, "classes[%i].methods[%i].final", class_idx, out_idx);
1447
27.3k
    yr_set_integer(
1448
27.3k
        abstract,
1449
27.3k
        out_obj,
1450
27.3k
        "classes[%i].methods[%i].abstract",
1451
27.3k
        class_idx,
1452
27.3k
        out_idx);
1453
27.3k
    yr_set_integer(
1454
27.3k
        method_gen_params.len,
1455
27.3k
        out_obj,
1456
27.3k
        "classes[%i].methods[%i].number_of_generic_parameters",
1457
27.3k
        class_idx,
1458
27.3k
        out_idx);
1459
1460
28.1k
    for (uint32_t i = 0; i < method_gen_params.len; ++i)
1461
860
    {
1462
860
      yr_set_string(
1463
860
          method_gen_params.names[i],
1464
860
          ctx->pe->object,
1465
860
          "classes[%i].methods[%i].generic_parameters[%i]",
1466
860
          class_idx,
1467
860
          out_idx,
1468
860
          i);
1469
860
    }
1470
1471
    // Unset return type for constructors for FileInfo compatibility
1472
27.3k
    if (strcmp(name, ".ctor") != 0 && strcmp(name, ".cctor") != 0)
1473
25.5k
    {
1474
25.5k
      yr_set_string(
1475
25.5k
          return_type,
1476
25.5k
          out_obj,
1477
25.5k
          "classes[%i].methods[%i].return_type",
1478
25.5k
          class_idx,
1479
25.5k
          out_idx);
1480
25.5k
    }
1481
1482
27.3k
    out_idx++;
1483
848k
  clean_next:
1484
848k
    yr_free(return_type);
1485
848k
    yr_free(method_gen_params.names);
1486
848k
  }
1487
1488
126k
  yr_set_integer(
1489
126k
      out_idx, ctx->pe->object, "classes[%i].number_of_methods", class_idx);
1490
126k
}
1491
1492
// Walks NestedClass table, returns enclosing type fullname or NULL
1493
static char* parse_enclosing_types(
1494
    const CLASS_CONTEXT* ctx,
1495
    uint32_t nested_idx,
1496
    uint32_t depth)
1497
152k
{
1498
152k
  if (depth > MAX_NAMESPACE_DEPTH)
1499
862
    return NULL;
1500
1501
151k
  const uint8_t* str_heap = ctx->str_heap;
1502
151k
  uint32_t str_size = ctx->str_size;
1503
1504
78.2M
  for (uint32_t idx = 0; idx < ctx->tables->nestedclass.RowCount; ++idx)
1505
78.0M
  {
1506
78.0M
    const uint8_t* nested_data = get_table_offset(
1507
78.0M
        &ctx->tables->nestedclass, idx + 1);
1508
1509
78.0M
    NESTEDCLASS_ROW nested_row = {0};
1510
78.0M
    bool read_result = read_nestedclass(ctx, nested_data, &nested_row);
1511
78.0M
    if (!read_result)
1512
57.0M
      continue;
1513
1514
    // We found enclosing class, get the namespace
1515
20.9M
    if (nested_row.NestedClass == nested_idx)
1516
13.8k
    {
1517
13.8k
      const uint8_t* typedef_data = get_table_offset(
1518
13.8k
          &ctx->tables->typedef_, nested_row.EnclosingClass);
1519
1520
13.8k
      TYPEDEF_ROW typedef_row = {0};
1521
13.8k
      bool result = read_typedef(ctx, typedef_data, &typedef_row);
1522
13.8k
      if (!result)
1523
2.67k
        break;
1524
1525
11.2k
      const char* name = pe_get_dotnet_string(
1526
11.2k
          ctx->pe, str_heap, str_size, typedef_row.Name);
1527
1528
      // Skip the Module pseudo class
1529
11.2k
      if (name && strcmp(name, "<Module>") == 0)
1530
238
        break;
1531
1532
10.9k
      const char* namespace = pe_get_dotnet_string(
1533
10.9k
          ctx->pe, str_heap, str_size, typedef_row.Namespace);
1534
1535
      // Type might be further nested, try to find correct namespace,
1536
      // check for self-reference
1537
10.9k
      if (is_nested(typedef_row.Flags) &&
1538
10.3k
          nested_row.EnclosingClass != nested_row.NestedClass)
1539
9.64k
      {
1540
9.64k
        char* nested_namespace = parse_enclosing_types(
1541
9.64k
            ctx, nested_row.EnclosingClass, depth + 1);
1542
1543
9.64k
        char* tmp = create_full_name(namespace, nested_namespace);
1544
9.64k
        char* fullname = create_full_name(name, tmp);
1545
9.64k
        yr_free(nested_namespace);
1546
9.64k
        yr_free(tmp);
1547
9.64k
        return fullname;
1548
9.64k
      }
1549
1550
1.33k
      return create_full_name(name, namespace);
1551
10.9k
    }
1552
20.9M
  }
1553
1554
140k
  return NULL;
1555
151k
}
1556
1557
// Parses and reconstructs user defined types with their methods and base types
1558
static void parse_user_types(const CLASS_CONTEXT* ctx)
1559
2.85k
{
1560
2.85k
  const uint8_t* str_heap = ctx->str_heap;
1561
2.85k
  uint32_t str_size = ctx->str_size;
1562
1563
  // Index for output tracking, we can't use
1564
  // offset as some classes can get skipped
1565
2.85k
  uint32_t out_idx = 0;
1566
  // skip first class as it's module pseudo class -> start at index 1
1567
1.72M
  for (uint32_t idx = 0; idx < ctx->tables->typedef_.RowCount; ++idx)
1568
1.72M
  {
1569
1.72M
    YR_OBJECT* out_obj = ctx->pe->object;
1570
    // Tables indexing starts at 1
1571
1.72M
    const uint8_t* data = get_table_offset(&ctx->tables->typedef_, idx + 1);
1572
1573
1.72M
    TYPEDEF_ROW row = {0};
1574
1.72M
    bool result = read_typedef(ctx, data, &row);
1575
1.72M
    if (!result)
1576
979k
      continue;
1577
1578
742k
    const char* name = pe_get_dotnet_string(
1579
742k
        ctx->pe, str_heap, str_size, row.Name);
1580
742k
    const char* type = get_typedef_type(row.Flags);
1581
1582
    // Ignore invalid types and invalid (empty) names
1583
742k
    if (!name || !*name || !type)
1584
528k
      continue;
1585
1586
    // If the type is generic, it will include ` at the end of a name
1587
    // with number of generic arguments, just use the part before that
1588
213k
    const char* end = strchr(name, '`');
1589
    // If the name will turn out empty, skip it and skip Module pseudo class
1590
213k
    if (end == name || strcmp(name, "<Module>") == 0)
1591
4.58k
      continue;
1592
1593
208k
    if (end)
1594
15.9k
      yr_set_sized_string(
1595
208k
          name, end - name, out_obj, "classes[%i].name", out_idx);
1596
193k
    else
1597
208k
      yr_set_string(name, out_obj, "classes[%i].name", out_idx);
1598
1599
208k
    char* fullname = NULL;
1600
208k
    char* namespace = pe_get_dotnet_string(
1601
208k
        ctx->pe, str_heap, str_size, row.Namespace);
1602
1603
    // Type might be nested, if so -> find correct namespace
1604
208k
    if (is_nested(row.Flags))
1605
81.7k
    {
1606
81.7k
      char* nested_namespace = parse_enclosing_types(ctx, idx + 1, 1);
1607
81.7k
      namespace = create_full_name(namespace, nested_namespace);
1608
81.7k
      yr_set_string(namespace, out_obj, "classes[%i].namespace", out_idx);
1609
81.7k
      fullname = create_full_name(name, namespace);
1610
81.7k
      yr_free(nested_namespace);
1611
81.7k
      yr_free(namespace);
1612
81.7k
    }
1613
127k
    else
1614
127k
    {
1615
127k
      yr_set_string(namespace, out_obj, "classes[%i].namespace", out_idx);
1616
127k
      fullname = create_full_name(name, namespace);
1617
127k
    }
1618
1619
208k
    const char* visibility = get_type_visibility(row.Flags);
1620
208k
    uint32_t abstract = (row.Flags & TYPE_ATTR_ABSTRACT) != 0;
1621
208k
    uint32_t sealed = (row.Flags & TYPE_ATTR_SEALED) != 0;
1622
1623
208k
    yr_set_string(fullname, out_obj, "classes[%i].fullname", out_idx);
1624
208k
    yr_set_string(visibility, out_obj, "classes[%i].visibility", out_idx);
1625
208k
    yr_set_string(type, out_obj, "classes[%i].type", out_idx);
1626
208k
    yr_set_integer(abstract, out_obj, "classes[%i].abstract", out_idx);
1627
208k
    yr_set_integer(sealed, out_obj, "classes[%i].sealed", out_idx);
1628
1629
208k
    yr_free(fullname);
1630
1631
    // Find if type has any Generic parameters
1632
208k
    GENERIC_PARAMETERS gen_params = {0};
1633
208k
    parse_generic_params(ctx, false, idx + 1, &gen_params);
1634
1635
208k
    yr_set_integer(
1636
208k
        gen_params.len,
1637
208k
        out_obj,
1638
208k
        "classes[%i].number_of_generic_parameters",
1639
208k
        out_idx);
1640
1641
209k
    for (uint32_t i = 0; i < gen_params.len; ++i)
1642
925
    {
1643
925
      yr_set_string(
1644
925
          gen_params.names[i],
1645
925
          out_obj,
1646
925
          "classes[%i].generic_parameters[%i]",
1647
925
          out_idx,
1648
925
          i);
1649
925
    }
1650
    // Find type and interfaces the type inherits
1651
208k
    parse_type_parents(ctx, row.Extends, idx + 1, out_idx, &gen_params);
1652
1653
    // To get the number of methods, we must peek where the MethodList
1654
    // of the next type is, then there is next.MethodList - this.MethodList
1655
    // number of methods, or if there is no following type,
1656
    // the rest of the MethodDef table is used
1657
208k
    uint32_t method_count = 0;
1658
    // If there is next method
1659
208k
    if (idx + 1 < ctx->tables->typedef_.RowCount)
1660
207k
    {
1661
207k
      const uint8_t* data = get_table_offset(&ctx->tables->typedef_, idx + 2);
1662
1663
207k
      TYPEDEF_ROW next_row = {0};
1664
207k
      result = read_typedef(ctx, data, &next_row);
1665
1666
      // overflow check
1667
207k
      if (result && next_row.Method >= row.Method)
1668
128k
        method_count = next_row.Method - row.Method;
1669
207k
    }
1670
    // overflow check - use the rest of the methods in the table
1671
1.53k
    else if (ctx->tables->methoddef.RowCount >= row.Method)
1672
1.24k
    {
1673
1.24k
      method_count = ctx->tables->methoddef.RowCount + 1 - row.Method;
1674
1.24k
    }
1675
1676
    // Sanity check for corrupted files
1677
208k
    if (method_count <= MAX_METHOD_COUNT)
1678
175k
      parse_methods(ctx, row.Method, method_count, out_idx, &gen_params);
1679
1680
208k
    yr_free(gen_params.names);
1681
208k
    out_idx++;
1682
208k
  }
1683
1684
2.85k
  yr_set_integer(out_idx, ctx->pe->object, "number_of_classes");
1685
2.85k
}
1686
1687
void dotnet_parse_guid(
1688
    PE* pe,
1689
    int64_t metadata_root,
1690
    PSTREAM_HEADER guid_header)
1691
543
{
1692
  // GUIDs are 16 bytes each, converted to hex format plus separators and NULL.
1693
543
  char guid[37];
1694
543
  int i = 0;
1695
1696
543
  const uint8_t* guid_offset = pe->data + metadata_root +
1697
543
                               yr_le32toh(guid_header->Offset);
1698
1699
543
  DWORD guid_size = yr_le32toh(guid_header->Size);
1700
1701
  // Limit the number of GUIDs to 16.
1702
543
  guid_size = yr_min(guid_size, 256);
1703
1704
  // Parse GUIDs if we have them. GUIDs are 16 bytes each.
1705
2.58k
  while (guid_size >= 16 && fits_in_pe(pe, guid_offset, 16))
1706
2.03k
  {
1707
2.03k
    sprintf(
1708
2.03k
        guid,
1709
2.03k
        "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
1710
2.03k
        yr_le32toh(*(uint32_t*) guid_offset),
1711
2.03k
        yr_le16toh(*(uint16_t*) (guid_offset + 4)),
1712
2.03k
        yr_le16toh(*(uint16_t*) (guid_offset + 6)),
1713
2.03k
        *(guid_offset + 8),
1714
2.03k
        *(guid_offset + 9),
1715
2.03k
        *(guid_offset + 10),
1716
2.03k
        *(guid_offset + 11),
1717
2.03k
        *(guid_offset + 12),
1718
2.03k
        *(guid_offset + 13),
1719
2.03k
        *(guid_offset + 14),
1720
2.03k
        *(guid_offset + 15));
1721
1722
2.03k
    guid[(16 * 2) + 4] = '\0';
1723
1724
2.03k
    yr_set_string(guid, pe->object, "guids[%i]", i);
1725
1726
2.03k
    i++;
1727
2.03k
    guid_size -= 16;
1728
2.03k
    guid_offset += 16;
1729
2.03k
  }
1730
1731
543
  yr_set_integer(i, pe->object, "number_of_guids");
1732
543
}
1733
1734
void dotnet_parse_us(PE* pe, int64_t metadata_root, PSTREAM_HEADER us_header)
1735
978
{
1736
978
  BLOB_PARSE_RESULT blob_result;
1737
978
  int i = 0;
1738
1739
978
  const uint32_t ush_sz = yr_le32toh(us_header->Size);
1740
1741
978
  const uint8_t* offset = pe->data + metadata_root +
1742
978
                          yr_le32toh(us_header->Offset);
1743
978
  const uint8_t* end_of_header = offset + ush_sz;
1744
1745
  // Make sure the header size is larger than 0 and its end is not past the
1746
  // end of PE.
1747
978
  if (ush_sz == 0 || !fits_in_pe(pe, offset, ush_sz))
1748
293
    return;
1749
1750
  // The first entry MUST be single NULL byte.
1751
685
  if (*offset != 0x00)
1752
205
    return;
1753
1754
480
  offset++;
1755
1756
28.7k
  while (offset < end_of_header)
1757
28.5k
  {
1758
28.5k
    blob_result = dotnet_parse_blob_entry(pe, offset);
1759
1760
28.5k
    if (blob_result.size == 0)
1761
283
      break;
1762
1763
28.2k
    offset += blob_result.size;
1764
    // There is an additional terminal byte which is 0x01 under certain
1765
    // conditions - when any top bit in utf16 top byte is set.
1766
    // The exact conditions are not relevant to our parsing but are
1767
    // documented in ECMA-335 II.24.2.4.
1768
28.2k
    if (blob_result.length > 0)
1769
20.1k
      blob_result.length--;
1770
1771
    // Avoid empty strings, which usually happen as padding at the end of the
1772
    // stream.
1773
28.2k
    if (blob_result.length > 0 && fits_in_pe(pe, offset, blob_result.length))
1774
19.6k
    {
1775
19.6k
      yr_set_sized_string(
1776
19.6k
          (char*) offset,
1777
19.6k
          blob_result.length,
1778
19.6k
          pe->object,
1779
19.6k
          "user_strings[%i]",
1780
19.6k
          i);
1781
1782
19.6k
      offset += blob_result.length;
1783
19.6k
      i++;
1784
19.6k
    }
1785
28.2k
  }
1786
1787
480
  yr_set_integer(i, pe->object, "number_of_user_strings");
1788
480
}
1789
1790
STREAMS dotnet_parse_stream_headers(
1791
    PE* pe,
1792
    int64_t offset,
1793
    int64_t metadata_root,
1794
    DWORD num_streams)
1795
5.72k
{
1796
5.72k
  PSTREAM_HEADER stream_header;
1797
5.72k
  STREAMS headers;
1798
1799
5.72k
  char* start;
1800
5.72k
  char* eos;
1801
5.72k
  char stream_name[DOTNET_STREAM_NAME_SIZE + 1];
1802
5.72k
  unsigned int i;
1803
1804
5.72k
  memset(&headers, '\0', sizeof(STREAMS));
1805
5.72k
  headers.metadata_root = metadata_root;
1806
1807
5.72k
  stream_header = (PSTREAM_HEADER) (pe->data + offset);
1808
1809
105k
  for (i = 0; i < num_streams; i++)
1810
105k
  {
1811
105k
    if (!struct_fits_in_pe(pe, stream_header, STREAM_HEADER))
1812
19
      break;
1813
1814
105k
    start = (char*) stream_header->Name;
1815
1816
105k
    if (!fits_in_pe(pe, start, DOTNET_STREAM_NAME_SIZE))
1817
3.77k
      break;
1818
1819
101k
    eos = (char*) memmem((void*) start, DOTNET_STREAM_NAME_SIZE, "\0", 1);
1820
1821
101k
    if (eos == NULL)
1822
1.46k
      break;
1823
1824
99.7k
    strncpy(stream_name, stream_header->Name, DOTNET_STREAM_NAME_SIZE);
1825
99.7k
    stream_name[DOTNET_STREAM_NAME_SIZE] = '\0';
1826
1827
99.7k
    yr_set_string(stream_name, pe->object, "streams[%i].name", i);
1828
1829
    // Offset is relative to metadata_root.
1830
99.7k
    yr_set_integer(
1831
99.7k
        metadata_root + yr_le32toh(stream_header->Offset),
1832
99.7k
        pe->object,
1833
99.7k
        "streams[%i].offset",
1834
99.7k
        i);
1835
1836
99.7k
    yr_set_integer(
1837
99.7k
        yr_le32toh(stream_header->Size), pe->object, "streams[%i].size", i);
1838
1839
    // Store necessary bits to parse these later. Not all tables will be
1840
    // parsed, but are referenced from others. For example, the #Strings
1841
    // stream is referenced from various tables in the #~ heap.
1842
    //
1843
    // #- is not documented but it represents unoptimized metadata stream. It
1844
    // may contain additional tables such as FieldPtr, ParamPtr, MethodPtr or
1845
    // PropertyPtr for indirect referencing. We already take into account these
1846
    // tables and they do not interfere with anything we parse in this module.
1847
1848
99.7k
    if ((strncmp(stream_name, "#~", 2) == 0 ||
1849
94.6k
         strncmp(stream_name, "#-", 2) == 0) &&
1850
6.04k
        headers.tilde == NULL)
1851
5.51k
      headers.tilde = stream_header;
1852
94.2k
    else if (strncmp(stream_name, "#GUID", 5) == 0)
1853
655
      headers.guid = stream_header;
1854
93.6k
    else if (strncmp(stream_name, "#Strings", 8) == 0 && headers.string == NULL)
1855
5.42k
      headers.string = stream_header;
1856
88.1k
    else if (strncmp(stream_name, "#Blob", 5) == 0 && headers.blob == NULL)
1857
5.41k
      headers.blob = stream_header;
1858
82.7k
    else if (strncmp(stream_name, "#US", 3) == 0 && headers.us == NULL)
1859
978
      headers.us = stream_header;
1860
1861
    // Stream name is padded to a multiple of 4.
1862
99.7k
    stream_header = (PSTREAM_HEADER) ((uint8_t*) stream_header +
1863
99.7k
                                      sizeof(STREAM_HEADER) +
1864
99.7k
                                      strlen(stream_name) + 4 -
1865
99.7k
                                      (strlen(stream_name) % 4));
1866
99.7k
  }
1867
1868
5.72k
  yr_set_integer(i, pe->object, "number_of_streams");
1869
1870
5.72k
  return headers;
1871
5.72k
}
1872
1873
// This is the second pass through the data for #~. The first pass collects
1874
// information on the number of rows for tables which have coded indexes.
1875
// This pass uses that information and the index_sizes to parse the tables
1876
// of interest.
1877
//
1878
// Because the indexes can vary in size depending upon the number of rows in
1879
// other tables it is impossible to use static sized structures. To deal with
1880
// this hardcode the sizes of each table based upon the documentation (for the
1881
// static sized portions) and use the variable sizes accordingly.
1882
1883
void dotnet_parse_tilde_2(
1884
    PE* pe,
1885
    PTILDE_HEADER tilde_header,
1886
    int64_t resource_base,
1887
    ROWS rows,
1888
    INDEX_SIZES index_sizes,
1889
    PSTREAMS streams)
1890
5.26k
{
1891
5.26k
  PMODULE_TABLE module_table;
1892
5.26k
  PASSEMBLY_TABLE assembly_table;
1893
5.26k
  PASSEMBLYREF_TABLE assemblyref_table;
1894
5.26k
  PFIELDRVA_TABLE fieldrva_table;
1895
5.26k
  PMANIFESTRESOURCE_TABLE manifestresource_table;
1896
5.26k
  PMODULEREF_TABLE moduleref_table;
1897
5.26k
  PCUSTOMATTRIBUTE_TABLE customattribute_table;
1898
5.26k
  PCONSTANT_TABLE constant_table;
1899
5.26k
  DWORD resource_size, implementation;
1900
1901
  // To save important data for future processing, initialize everything to 0
1902
5.26k
  TABLES tables = {0};
1903
1904
5.26k
  char* name;
1905
5.26k
  char typelib[MAX_TYPELIB_SIZE + 1];
1906
5.26k
  unsigned int i;
1907
5.26k
  int bit_check;
1908
5.26k
  int matched_bits = 0;
1909
1910
5.26k
  int64_t metadata_root = streams->metadata_root;
1911
5.26k
  int64_t resource_offset, field_offset;
1912
5.26k
  uint32_t row_size, row_count, counter, str_heap_size;
1913
1914
5.26k
  const uint8_t* string_offset;
1915
5.26k
  const uint8_t* blob_offset;
1916
1917
5.26k
  uint32_t num_rows = 0;
1918
5.26k
  uint32_t valid_rows = 0;
1919
5.26k
  uint32_t* row_offset = NULL;
1920
5.26k
  uint8_t* table_offset = NULL;
1921
5.26k
  uint8_t* row_ptr = NULL;
1922
1923
  // These are pointers and row sizes for tables of interest to us for special
1924
  // parsing. For example, we are interested in pulling out any CustomAttributes
1925
  // that are GUIDs so we need to be able to walk these tables. To find GUID
1926
  // CustomAttributes you need to walk the CustomAttribute table and look for
1927
  // any row with a Parent that indexes into the Assembly table and Type indexes
1928
  // into the MemberRef table. Then you follow the index into the MemberRef
1929
  // table and check the Class to make sure it indexes into TypeRef table. If it
1930
  // does you follow that index and make sure the Name is "GuidAttribute". If
1931
  // all that is valid then you can take the Value from the CustomAttribute
1932
  // table to find out the index into the Blob stream and parse that.
1933
  //
1934
  // Luckily we can abuse the fact that the order of the tables is guaranteed
1935
  // consistent (though some may not exist, but if they do exist they must exist
1936
  // in a certain order). The order is defined by their position in the Valid
1937
  // member of the tilde_header structure. By the time we are parsing the
1938
  // CustomAttribute table we have already recorded the location of the TypeRef
1939
  // and MemberRef tables, so we can follow the chain back up from
1940
  // CustomAttribute through MemberRef to TypeRef.
1941
1942
5.26k
  uint8_t* typeref_ptr = NULL;
1943
5.26k
  uint8_t* memberref_ptr = NULL;
1944
5.26k
  uint32_t typeref_row_size = 0;
1945
5.26k
  uint32_t memberref_row_size = 0;
1946
5.26k
  uint8_t* typeref_row = NULL;
1947
5.26k
  uint8_t* memberref_row = NULL;
1948
1949
5.26k
  DWORD type_index;
1950
5.26k
  DWORD class_index;
1951
5.26k
  BLOB_PARSE_RESULT blob_result;
1952
5.26k
  DWORD blob_index;
1953
5.26k
  DWORD blob_length;
1954
1955
  // These are used to determine the size of coded indexes, which are the
1956
  // dynamically sized columns for some tables. The coded indexes are
1957
  // documented in ECMA-335 Section II.24.2.6.
1958
5.26k
  uint8_t index_size, index_size2;
1959
1960
  // Number of rows is the number of bits set to 1 in Valid.
1961
  // Should use this technique:
1962
  // http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetKernighan
1963
  // Count number of Rows size entries in header to skip over them
1964
342k
  for (i = 0; i < 64; i++)
1965
336k
    valid_rows += ((yr_le64toh(tilde_header->Valid) >> i) & 0x01);
1966
1967
5.26k
  row_offset = (uint32_t*) (tilde_header + 1);
1968
5.26k
  table_offset = (uint8_t*) row_offset;
1969
5.26k
  table_offset += sizeof(uint32_t) * valid_rows;
1970
1971
  // Sometimes files have some sort of padding after, from DnSpy source
1972
  // it's denoted by EXTRA_DATA 0x40 flag in heapflags
1973
  // We then need to offset by 4 bytes, otherwise the analysis is wrong
1974
  // https://github.com/dnSpy/dnSpy/blob/2b6dcfaf602fb8ca6462b8b6237fdfc0c74ad994/dnSpy/dnSpy/Hex/Files/DotNet/TablesHeaderDataImpl.cs
1975
  // example: 1c2246af11000c3ce6b05ed6ba25060cbb00273c599428b98cf4013bdd82892f
1976
5.26k
  if (tilde_header->HeapSizes & HEAP_EXTRA_DATA)
1977
2.36k
    table_offset += 4;
1978
1979
5.26k
#define DOTNET_STRING_INDEX(Name)                       \
1980
229k
  index_sizes.string == 2 ? yr_le16toh(Name.Name_Short) \
1981
229k
                          : yr_le32toh(Name.Name_Long)
1982
1983
5.26k
  string_offset = pe->data + metadata_root +
1984
5.26k
                  yr_le32toh(streams->string->Offset);
1985
1986
5.26k
  str_heap_size = yr_le32toh(streams->string->Size);
1987
1988
  // Now walk again this time parsing out what we care about.
1989
238k
  for (bit_check = 0; bit_check < 64; bit_check++)
1990
235k
  {
1991
    // If the Valid bit is not set for this table, skip it...
1992
235k
    if (!((yr_le64toh(tilde_header->Valid) >> bit_check) & 0x01))
1993
212k
      continue;
1994
1995
22.9k
    if (!fits_in_pe(pe, row_offset + matched_bits, sizeof(uint32_t)))
1996
21
      return;
1997
1998
22.9k
    num_rows = yr_le32toh(*(row_offset + matched_bits));
1999
2000
    // Make sure that num_rows has a reasonable value. For example
2001
    // edc05e49dd3810be67942b983455fd43 sets a large value for number of
2002
    // rows for the BIT_MODULE section.
2003
22.9k
    if (num_rows > 15000)
2004
2.32k
      return;
2005
2006
    // Those tables which exist, but that we don't care about must be
2007
    // skipped.
2008
    //
2009
    // Sadly, given the dynamic sizes of some columns we can not have well
2010
    // defined structures for all tables and use them accordingly. To deal
2011
    // with this manually move the table_offset pointer by the appropriate
2012
    // number of bytes as described in the documentation for each table.
2013
    //
2014
    // The table structures are documented in ECMA-335 Section II.22.
2015
2016
20.5k
    switch (bit_check)
2017
20.5k
    {
2018
846
    case BIT_MODULE:
2019
846
      module_table = (PMODULE_TABLE) table_offset;
2020
2021
846
      if (!struct_fits_in_pe(pe, module_table, MODULE_TABLE))
2022
105
        break;
2023
2024
741
      name = pe_get_dotnet_string(
2025
741
          pe,
2026
741
          string_offset,
2027
741
          str_heap_size,
2028
741
          DOTNET_STRING_INDEX(module_table->Name));
2029
2030
741
      if (name != NULL)
2031
741
        yr_set_string(name, pe->object, "module_name");
2032
2033
741
      row_size = 2 + index_sizes.string + (index_sizes.guid * 3);
2034
2035
741
      tables.module.Offset = table_offset;
2036
741
      tables.module.RowCount = num_rows;
2037
741
      tables.module.RowSize = row_size;
2038
2039
741
      table_offset += row_size * num_rows;
2040
741
      break;
2041
2042
1.55k
    case BIT_TYPEREF:
2043
1.55k
      row_count = max_rows(
2044
1.55k
          4,
2045
1.55k
          yr_le32toh(rows.module),
2046
1.55k
          yr_le32toh(rows.moduleref),
2047
1.55k
          yr_le32toh(rows.assemblyref),
2048
1.55k
          yr_le32toh(rows.typeref));
2049
2050
1.55k
      if (row_count > (0xFFFF >> 0x02))
2051
236
        index_size = 4;
2052
1.32k
      else
2053
1.32k
        index_size = 2;
2054
2055
1.55k
      row_size = (index_size + (index_sizes.string * 2));
2056
1.55k
      typeref_row_size = row_size;
2057
1.55k
      typeref_ptr = table_offset;
2058
2059
1.55k
      tables.typeref.Offset = table_offset;
2060
1.55k
      tables.typeref.RowCount = num_rows;
2061
1.55k
      tables.typeref.RowSize = row_size;
2062
2063
1.55k
      table_offset += row_size * num_rows;
2064
1.55k
      break;
2065
2066
3.17k
    case BIT_TYPEDEF:
2067
3.17k
      row_count = max_rows(
2068
3.17k
          3,
2069
3.17k
          yr_le32toh(rows.typedef_),
2070
3.17k
          yr_le32toh(rows.typeref),
2071
3.17k
          yr_le32toh(rows.typespec));
2072
2073
3.17k
      if (row_count > (0xFFFF >> 0x02))
2074
134
        index_size = 4;
2075
3.04k
      else
2076
3.04k
        index_size = 2;
2077
2078
3.17k
      row_size = 4 + (index_sizes.string * 2) + index_size + index_sizes.field +
2079
3.17k
                 index_sizes.methoddef;
2080
2081
3.17k
      tables.typedef_.Offset = table_offset;
2082
3.17k
      tables.typedef_.RowCount = num_rows;
2083
3.17k
      tables.typedef_.RowSize = row_size;
2084
2085
3.17k
      table_offset += row_size * num_rows;
2086
3.17k
      break;
2087
2088
298
    case BIT_FIELDPTR:
2089
      // This one is not documented in ECMA-335.
2090
298
      table_offset += (index_sizes.field) * num_rows;
2091
298
      break;
2092
2093
723
    case BIT_FIELD:
2094
723
      table_offset += (2 + (index_sizes.string) + index_sizes.blob) * num_rows;
2095
723
      break;
2096
2097
267
    case BIT_METHODDEFPTR:
2098
      // This one is not documented in ECMA-335.
2099
267
      table_offset += (index_sizes.methoddef) * num_rows;
2100
267
      break;
2101
2102
1.89k
    case BIT_METHODDEF:
2103
1.89k
      row_size = 4 + 2 + 2 + index_sizes.string + index_sizes.blob +
2104
1.89k
                 index_sizes.param;
2105
2106
1.89k
      tables.methoddef.Offset = table_offset;
2107
1.89k
      tables.methoddef.RowCount = num_rows;
2108
1.89k
      tables.methoddef.RowSize = row_size;
2109
1.89k
      table_offset += row_size * num_rows;
2110
1.89k
      break;
2111
2112
839
    case BIT_PARAM:
2113
839
      row_size = 2 + 2 + index_sizes.string;
2114
2115
839
      tables.param.Offset = table_offset;
2116
839
      tables.param.RowCount = num_rows;
2117
839
      tables.param.RowSize = row_size;
2118
2119
839
      table_offset += row_size * num_rows;
2120
839
      break;
2121
2122
1.10k
    case BIT_INTERFACEIMPL:
2123
1.10k
      row_count = max_rows(
2124
1.10k
          3,
2125
1.10k
          yr_le32toh(rows.typedef_),
2126
1.10k
          yr_le32toh(rows.typeref),
2127
1.10k
          yr_le32toh(rows.typespec));
2128
2129
1.10k
      if (row_count > (0xFFFF >> 0x02))
2130
71
        index_size = 4;
2131
1.03k
      else
2132
1.03k
        index_size = 2;
2133
2134
1.10k
      row_size = index_sizes.typedef_ + index_size;
2135
2136
1.10k
      tables.intefaceimpl.Offset = table_offset;
2137
1.10k
      tables.intefaceimpl.RowCount = num_rows;
2138
1.10k
      tables.intefaceimpl.RowSize = row_size;
2139
2140
1.10k
      table_offset += row_size * num_rows;
2141
1.10k
      break;
2142
2143
1.01k
    case BIT_MEMBERREF:
2144
1.01k
      row_count = max_rows(
2145
1.01k
          4,
2146
1.01k
          yr_le32toh(rows.methoddef),
2147
1.01k
          yr_le32toh(rows.moduleref),
2148
1.01k
          yr_le32toh(rows.typeref),
2149
1.01k
          yr_le32toh(rows.typespec));
2150
2151
1.01k
      if (row_count > (0xFFFF >> 0x03))
2152
105
        index_size = 4;
2153
907
      else
2154
907
        index_size = 2;
2155
2156
1.01k
      row_size = (index_size + index_sizes.string + index_sizes.blob);
2157
1.01k
      memberref_row_size = row_size;
2158
1.01k
      memberref_ptr = table_offset;
2159
1.01k
      table_offset += row_size * num_rows;
2160
1.01k
      break;
2161
2162
614
    case BIT_CONSTANT:
2163
614
      row_count = max_rows(
2164
614
          3,
2165
614
          yr_le32toh(rows.param),
2166
614
          yr_le32toh(rows.field),
2167
614
          yr_le32toh(rows.property));
2168
2169
614
      if (row_count > (0xFFFF >> 0x02))
2170
108
        index_size = 4;
2171
506
      else
2172
506
        index_size = 2;
2173
2174
      // Using 'i' is insufficent since we may skip certain constants and
2175
      // it would give an inaccurate count in that case.
2176
614
      counter = 0;
2177
614
      row_size = (1 + 1 + index_size + index_sizes.blob);
2178
614
      row_ptr = table_offset;
2179
2180
400k
      for (i = 0; i < num_rows; i++)
2181
399k
      {
2182
399k
        if (!fits_in_pe(pe, row_ptr, row_size))
2183
282
          break;
2184
2185
399k
        constant_table = (PCONSTANT_TABLE) row_ptr;
2186
2187
        // Only look for constants of type string.
2188
399k
        if (yr_le32toh(constant_table->Type) != TYPE_STRING)
2189
393k
        {
2190
393k
          row_ptr += row_size;
2191
393k
          continue;
2192
393k
        }
2193
2194
        // Get the blob offset and pull it out of the blob table.
2195
6.12k
        blob_offset = ((uint8_t*) constant_table) + 2 + index_size;
2196
2197
6.12k
        if (index_sizes.blob == 4)
2198
1.93k
          blob_index = *(DWORD*) blob_offset;
2199
4.19k
        else
2200
          // Cast the value (index into blob table) to a 32bit value.
2201
4.19k
          blob_index = (DWORD) (*(WORD*) blob_offset);
2202
2203
        // Everything checks out. Make sure the index into the blob field
2204
        // is valid (non-null and within range).
2205
6.12k
        blob_offset = pe->data + metadata_root +
2206
6.12k
                      yr_le32toh(streams->blob->Offset) + blob_index;
2207
2208
6.12k
        blob_result = dotnet_parse_blob_entry(pe, blob_offset);
2209
2210
6.12k
        if (blob_result.size == 0)
2211
3.30k
        {
2212
3.30k
          row_ptr += row_size;
2213
3.30k
          continue;
2214
3.30k
        }
2215
2216
2.82k
        blob_length = blob_result.length;
2217
2.82k
        blob_offset += blob_result.size;
2218
2219
        // Quick sanity check to make sure the blob entry is within bounds.
2220
2.82k
        if (blob_offset + blob_length >= pe->data + pe->data_size)
2221
216
        {
2222
216
          row_ptr += row_size;
2223
216
          continue;
2224
216
        }
2225
2226
2.60k
        yr_set_sized_string(
2227
2.60k
            (char*) blob_offset,
2228
2.60k
            blob_result.length,
2229
2.60k
            pe->object,
2230
2.60k
            "constants[%i]",
2231
2.60k
            counter);
2232
2233
2.60k
        counter++;
2234
2.60k
        row_ptr += row_size;
2235
2.60k
      }
2236
2237
614
      yr_set_integer(counter, pe->object, "number_of_constants");
2238
614
      table_offset += row_size * num_rows;
2239
614
      break;
2240
2241
1.13k
    case BIT_CUSTOMATTRIBUTE:
2242
      // index_size is size of the parent column.
2243
1.13k
      row_count = max_rows(
2244
1.13k
          21,
2245
1.13k
          yr_le32toh(rows.methoddef),
2246
1.13k
          yr_le32toh(rows.field),
2247
1.13k
          yr_le32toh(rows.typeref),
2248
1.13k
          yr_le32toh(rows.typedef_),
2249
1.13k
          yr_le32toh(rows.param),
2250
1.13k
          yr_le32toh(rows.interfaceimpl),
2251
1.13k
          yr_le32toh(rows.memberref),
2252
1.13k
          yr_le32toh(rows.module),
2253
1.13k
          yr_le32toh(rows.property),
2254
1.13k
          yr_le32toh(rows.event),
2255
1.13k
          yr_le32toh(rows.standalonesig),
2256
1.13k
          yr_le32toh(rows.moduleref),
2257
1.13k
          yr_le32toh(rows.typespec),
2258
1.13k
          yr_le32toh(rows.assembly),
2259
1.13k
          yr_le32toh(rows.assemblyref),
2260
1.13k
          yr_le32toh(rows.file),
2261
1.13k
          yr_le32toh(rows.exportedtype),
2262
1.13k
          yr_le32toh(rows.manifestresource),
2263
1.13k
          yr_le32toh(rows.genericparam),
2264
1.13k
          yr_le32toh(rows.genericparamconstraint),
2265
1.13k
          yr_le32toh(rows.methodspec));
2266
2267
1.13k
      if (row_count > (0xFFFF >> 0x05))
2268
569
        index_size = 4;
2269
565
      else
2270
565
        index_size = 2;
2271
2272
      // index_size2 is size of the type column.
2273
1.13k
      row_count = max_rows(
2274
1.13k
          2, yr_le32toh(rows.methoddef), yr_le32toh(rows.memberref));
2275
2276
1.13k
      if (row_count > (0xFFFF >> 0x03))
2277
89
        index_size2 = 4;
2278
1.04k
      else
2279
1.04k
        index_size2 = 2;
2280
2281
1.13k
      row_size = (index_size + index_size2 + index_sizes.blob);
2282
2283
1.13k
      if (typeref_ptr != NULL && memberref_ptr != NULL)
2284
814
      {
2285
814
        row_ptr = table_offset;
2286
2287
665k
        for (i = 0; i < num_rows; i++)
2288
665k
        {
2289
665k
          if (!fits_in_pe(pe, row_ptr, row_size))
2290
506
            break;
2291
2292
          // Check the Parent field.
2293
665k
          customattribute_table = (PCUSTOMATTRIBUTE_TABLE) row_ptr;
2294
2295
665k
          if (index_size == 4)
2296
191k
          {
2297
            // Low 5 bits tell us what this is an index into. Remaining bits
2298
            // tell us the index value.
2299
            // Parent must be an index into the Assembly (0x0E) table.
2300
191k
            if ((*(DWORD*) customattribute_table & 0x1F) != 0x0E)
2301
183k
            {
2302
183k
              row_ptr += row_size;
2303
183k
              continue;
2304
183k
            }
2305
191k
          }
2306
473k
          else
2307
473k
          {
2308
            // Low 5 bits tell us what this is an index into. Remaining bits
2309
            // tell us the index value.
2310
            // Parent must be an index into the Assembly (0x0E) table.
2311
473k
            if ((*(WORD*) customattribute_table & 0x1F) != 0x0E)
2312
452k
            {
2313
452k
              row_ptr += row_size;
2314
452k
              continue;
2315
452k
            }
2316
473k
          }
2317
2318
          // Check the Type field.
2319
29.0k
          customattribute_table = (PCUSTOMATTRIBUTE_TABLE) (row_ptr +
2320
29.0k
                                                            index_size);
2321
2322
29.0k
          if (index_size2 == 4)
2323
1.36k
          {
2324
            // Low 3 bits tell us what this is an index into. Remaining bits
2325
            // tell us the index value. Only values 2 and 3 are defined.
2326
            // Type must be an index into the MemberRef table.
2327
1.36k
            if ((*(DWORD*) customattribute_table & 0x07) != 0x03)
2328
1.04k
            {
2329
1.04k
              row_ptr += row_size;
2330
1.04k
              continue;
2331
1.04k
            }
2332
2333
326
            type_index = *(DWORD*) customattribute_table >> 3;
2334
326
          }
2335
27.6k
          else
2336
27.6k
          {
2337
            // Low 3 bits tell us what this is an index into. Remaining bits
2338
            // tell us the index value. Only values 2 and 3 are defined.
2339
            // Type must be an index into the MemberRef table.
2340
27.6k
            if ((*(WORD*) customattribute_table & 0x07) != 0x03)
2341
17.4k
            {
2342
17.4k
              row_ptr += row_size;
2343
17.4k
              continue;
2344
17.4k
            }
2345
2346
            // Cast the index to a 32bit value.
2347
10.1k
            type_index = (DWORD) ((*(WORD*) customattribute_table >> 3));
2348
10.1k
          }
2349
2350
10.5k
          if (type_index > 0)
2351
8.63k
            type_index--;
2352
2353
          // Now follow the Type index into the MemberRef table.
2354
10.5k
          memberref_row = memberref_ptr + (memberref_row_size * type_index);
2355
2356
10.5k
          if (!fits_in_pe(pe, memberref_row, memberref_row_size))
2357
129
            break;
2358
2359
10.3k
          if (index_sizes.memberref == 4)
2360
0
          {
2361
            // Low 3 bits tell us what this is an index into. Remaining bits
2362
            // tell us the index value. Class must be an index into the
2363
            // TypeRef table.
2364
0
            if ((*(DWORD*) memberref_row & 0x07) != 0x01)
2365
0
            {
2366
0
              row_ptr += row_size;
2367
0
              continue;
2368
0
            }
2369
2370
0
            class_index = *(DWORD*) memberref_row >> 3;
2371
0
          }
2372
10.3k
          else
2373
10.3k
          {
2374
            // Low 3 bits tell us what this is an index into. Remaining bits
2375
            // tell us the index value. Class must be an index into the
2376
            // TypeRef table.
2377
10.3k
            if ((*(WORD*) memberref_row & 0x07) != 0x01)
2378
2.73k
            {
2379
2.73k
              row_ptr += row_size;
2380
2.73k
              continue;
2381
2.73k
            }
2382
2383
            // Cast the index to a 32bit value.
2384
7.65k
            class_index = (DWORD) (*(WORD*) memberref_row >> 3);
2385
7.65k
          }
2386
2387
7.65k
          if (class_index > 0)
2388
5.15k
            class_index--;
2389
2390
          // Now follow the Class index into the TypeRef table.
2391
7.65k
          typeref_row = typeref_ptr + (typeref_row_size * class_index);
2392
2393
7.65k
          if (!fits_in_pe(pe, typeref_row, typeref_row_size))
2394
6
            break;
2395
2396
          // Skip over the ResolutionScope and check the Name field,
2397
          // which is an index into the Strings heap.
2398
7.64k
          row_count = max_rows(
2399
7.64k
              4,
2400
7.64k
              yr_le32toh(rows.module),
2401
7.64k
              yr_le32toh(rows.moduleref),
2402
7.64k
              yr_le32toh(rows.assemblyref),
2403
7.64k
              yr_le32toh(rows.typeref));
2404
2405
7.64k
          if (row_count > (0xFFFF >> 0x02))
2406
261
            typeref_row += 4;
2407
7.38k
          else
2408
7.38k
            typeref_row += 2;
2409
2410
7.64k
          if (index_sizes.string == 4)
2411
2.34k
          {
2412
2.34k
            name = pe_get_dotnet_string(
2413
2.34k
                pe, string_offset, str_heap_size, *(DWORD*) typeref_row);
2414
2.34k
          }
2415
5.30k
          else
2416
5.30k
          {
2417
5.30k
            name = pe_get_dotnet_string(
2418
5.30k
                pe, string_offset, str_heap_size, *(WORD*) typeref_row);
2419
5.30k
          }
2420
2421
7.64k
          if (name != NULL && strncmp(name, "GuidAttribute", 13) != 0)
2422
561
          {
2423
561
            row_ptr += row_size;
2424
561
            continue;
2425
561
          }
2426
2427
          // Get the Value field.
2428
7.08k
          customattribute_table = (PCUSTOMATTRIBUTE_TABLE) (row_ptr +
2429
7.08k
                                                            index_size +
2430
7.08k
                                                            index_size2);
2431
2432
7.08k
          if (index_sizes.blob == 4)
2433
441
            blob_index = *(DWORD*) customattribute_table;
2434
6.64k
          else
2435
            // Cast the value (index into blob table) to a 32bit value.
2436
6.64k
            blob_index = (DWORD) (*(WORD*) customattribute_table);
2437
2438
          // Everything checks out. Make sure the index into the blob field
2439
          // is valid (non-null and within range).
2440
7.08k
          blob_offset = pe->data + metadata_root +
2441
7.08k
                        yr_le32toh(streams->blob->Offset) + blob_index;
2442
2443
          // If index into blob is 0 or past the end of the blob stream, skip
2444
          // it. We don't know the size of the blob entry yet because that is
2445
          // encoded in the start.
2446
7.08k
          if (blob_index == 0x00 || blob_offset >= pe->data + pe->data_size)
2447
2.14k
          {
2448
2.14k
            row_ptr += row_size;
2449
2.14k
            continue;
2450
2.14k
          }
2451
2452
4.94k
          blob_result = dotnet_parse_blob_entry(pe, blob_offset);
2453
2454
4.94k
          if (blob_result.size == 0)
2455
2.00k
          {
2456
2.00k
            row_ptr += row_size;
2457
2.00k
            continue;
2458
2.00k
          }
2459
2460
2.93k
          blob_length = blob_result.length;
2461
2.93k
          blob_offset += blob_result.size;
2462
2463
          // Quick sanity check to make sure the blob entry is within bounds
2464
          // and its length is at least 3 (2 bytes for the 16 bits prolog and
2465
          // 1 byte for the string length)
2466
2.93k
          if (blob_length < 3 ||
2467
2.16k
              blob_offset + blob_length >= pe->data + pe->data_size)
2468
986
          {
2469
986
            row_ptr += row_size;
2470
986
            continue;
2471
986
          }
2472
2473
          // Custom attributes MUST have a 16 bit prolog of 0x0001
2474
1.95k
          if (*(WORD*) blob_offset != 0x0001)
2475
1.08k
          {
2476
1.08k
            row_ptr += row_size;
2477
1.08k
            continue;
2478
1.08k
          }
2479
2480
          // The next byte after the 16 bit prolog is the length of the string.
2481
867
          blob_offset += 2;
2482
867
          uint8_t str_len = *blob_offset;
2483
2484
          // Increment blob_offset so that it points to the first byte of the
2485
          // string.
2486
867
          blob_offset += 1;
2487
2488
867
          if (blob_offset + str_len > pe->data + pe->data_size)
2489
197
          {
2490
197
            row_ptr += row_size;
2491
197
            continue;
2492
197
          }
2493
2494
670
          if (*blob_offset == 0xFF || *blob_offset == 0x00)
2495
425
          {
2496
425
            typelib[0] = '\0';
2497
425
          }
2498
245
          else
2499
245
          {
2500
245
            strncpy(typelib, (char*) blob_offset, str_len);
2501
245
            typelib[str_len] = '\0';
2502
245
          }
2503
2504
670
          yr_set_string(typelib, pe->object, "typelib");
2505
2506
670
          row_ptr += row_size;
2507
670
        }
2508
814
      }
2509
2510
1.13k
      table_offset += row_size * num_rows;
2511
1.13k
      break;
2512
2513
78
    case BIT_FIELDMARSHAL:
2514
78
      row_count = max_rows(2, yr_le32toh(rows.field), yr_le32toh(rows.param));
2515
2516
78
      if (row_count > (0xFFFF >> 0x01))
2517
0
        index_size = 4;
2518
78
      else
2519
78
        index_size = 2;
2520
2521
78
      table_offset += (index_size + index_sizes.blob) * num_rows;
2522
78
      break;
2523
2524
103
    case BIT_DECLSECURITY:
2525
103
      row_count = max_rows(
2526
103
          3,
2527
103
          yr_le32toh(rows.typedef_),
2528
103
          yr_le32toh(rows.methoddef),
2529
103
          yr_le32toh(rows.assembly));
2530
2531
103
      if (row_count > (0xFFFF >> 0x02))
2532
57
        index_size = 4;
2533
46
      else
2534
46
        index_size = 2;
2535
2536
103
      table_offset += (2 + index_size + index_sizes.blob) * num_rows;
2537
103
      break;
2538
2539
52
    case BIT_CLASSLAYOUT:
2540
52
      table_offset += (2 + 4 + index_sizes.typedef_) * num_rows;
2541
52
      break;
2542
2543
341
    case BIT_FIELDLAYOUT:
2544
341
      table_offset += (4 + index_sizes.field) * num_rows;
2545
341
      break;
2546
2547
117
    case BIT_STANDALONESIG:
2548
117
      table_offset += (index_sizes.blob) * num_rows;
2549
117
      break;
2550
2551
55
    case BIT_EVENTMAP:
2552
55
      table_offset += (index_sizes.typedef_ + index_sizes.event) * num_rows;
2553
55
      break;
2554
2555
23
    case BIT_EVENTPTR:
2556
      // This one is not documented in ECMA-335.
2557
23
      table_offset += (index_sizes.event) * num_rows;
2558
23
      break;
2559
2560
83
    case BIT_EVENT:
2561
83
      row_count = max_rows(
2562
83
          3,
2563
83
          yr_le32toh(rows.typedef_),
2564
83
          yr_le32toh(rows.typeref),
2565
83
          yr_le32toh(rows.typespec));
2566
2567
83
      if (row_count > (0xFFFF >> 0x02))
2568
48
        index_size = 4;
2569
35
      else
2570
35
        index_size = 2;
2571
2572
83
      table_offset += (2 + index_sizes.string + index_size) * num_rows;
2573
83
      break;
2574
2575
289
    case BIT_PROPERTYMAP:
2576
289
      table_offset += (index_sizes.typedef_ + index_sizes.property) * num_rows;
2577
289
      break;
2578
2579
27
    case BIT_PROPERTYPTR:
2580
      // This one is not documented in ECMA-335.
2581
27
      table_offset += (index_sizes.property) * num_rows;
2582
27
      break;
2583
2584
277
    case BIT_PROPERTY:
2585
277
      table_offset += (2 + index_sizes.string + index_sizes.blob) * num_rows;
2586
277
      break;
2587
2588
308
    case BIT_METHODSEMANTICS:
2589
308
      row_count = max_rows(
2590
308
          2, yr_le32toh(rows.event), yr_le32toh(rows.property));
2591
2592
308
      if (row_count > (0xFFFF >> 0x01))
2593
0
        index_size = 4;
2594
308
      else
2595
308
        index_size = 2;
2596
2597
308
      table_offset += (2 + index_sizes.methoddef + index_size) * num_rows;
2598
308
      break;
2599
2600
103
    case BIT_METHODIMPL:
2601
103
      row_count = max_rows(
2602
103
          2, yr_le32toh(rows.methoddef), yr_le32toh(rows.memberref));
2603
2604
103
      if (row_count > (0xFFFF >> 0x01))
2605
0
        index_size = 4;
2606
103
      else
2607
103
        index_size = 2;
2608
2609
103
      table_offset += (index_sizes.typedef_ + (index_size * 2)) * num_rows;
2610
103
      break;
2611
2612
188
    case BIT_MODULEREF:
2613
188
      row_ptr = table_offset;
2614
2615
      // Can't use 'i' here because we only set the string if it is not
2616
      // NULL. Instead use 'counter'.
2617
188
      counter = 0;
2618
2619
30.3k
      for (i = 0; i < num_rows; i++)
2620
30.2k
      {
2621
30.2k
        moduleref_table = (PMODULEREF_TABLE) row_ptr;
2622
2623
30.2k
        if (!struct_fits_in_pe(pe, moduleref_table, MODULEREF_TABLE))
2624
94
          break;
2625
2626
30.2k
        name = pe_get_dotnet_string(
2627
30.2k
            pe,
2628
30.2k
            string_offset,
2629
30.2k
            str_heap_size,
2630
30.2k
            DOTNET_STRING_INDEX(moduleref_table->Name));
2631
2632
30.2k
        if (name != NULL)
2633
18.9k
        {
2634
18.9k
          yr_set_string(name, pe->object, "modulerefs[%i]", counter);
2635
18.9k
          counter++;
2636
18.9k
        }
2637
2638
30.2k
        row_ptr += index_sizes.string;
2639
30.2k
      }
2640
2641
188
      yr_set_integer(counter, pe->object, "number_of_modulerefs");
2642
2643
188
      row_size = index_sizes.string;
2644
2645
188
      tables.moduleref.Offset = table_offset;
2646
188
      tables.moduleref.RowCount = num_rows;
2647
188
      tables.moduleref.RowSize = row_size;
2648
2649
188
      table_offset += row_size * num_rows;
2650
188
      break;
2651
2652
545
    case BIT_TYPESPEC:
2653
545
      row_size = index_sizes.blob;
2654
2655
545
      tables.typespec.Offset = table_offset;
2656
545
      tables.typespec.RowCount = num_rows;
2657
545
      tables.typespec.RowSize = row_size;
2658
2659
545
      table_offset += row_size * num_rows;
2660
545
      break;
2661
2662
63
    case BIT_IMPLMAP:
2663
63
      row_count = max_rows(
2664
63
          2, yr_le32toh(rows.field), yr_le32toh(rows.methoddef));
2665
2666
63
      if (row_count > (0xFFFF >> 0x01))
2667
0
        index_size = 4;
2668
63
      else
2669
63
        index_size = 2;
2670
2671
63
      table_offset += (2 + index_size + index_sizes.string +
2672
63
                       index_sizes.moduleref) *
2673
63
                      num_rows;
2674
63
      break;
2675
2676
376
    case BIT_FIELDRVA:
2677
376
      row_size = 4 + index_sizes.field;
2678
376
      row_ptr = table_offset;
2679
2680
      // Can't use 'i' here because we only set the field offset if it is
2681
      // valid. Instead use 'counter'.
2682
376
      counter = 0;
2683
2684
112k
      for (i = 0; i < num_rows; i++)
2685
112k
      {
2686
112k
        fieldrva_table = (PFIELDRVA_TABLE) row_ptr;
2687
2688
112k
        if (!struct_fits_in_pe(pe, fieldrva_table, FIELDRVA_TABLE))
2689
148
          break;
2690
2691
111k
        field_offset = pe_rva_to_offset(pe, fieldrva_table->RVA);
2692
2693
111k
        if (field_offset >= 0)
2694
13.1k
        {
2695
13.1k
          yr_set_integer(
2696
13.1k
              field_offset, pe->object, "field_offsets[%i]", counter);
2697
13.1k
          counter++;
2698
13.1k
        }
2699
2700
111k
        row_ptr += row_size;
2701
111k
      }
2702
2703
376
      yr_set_integer(counter, pe->object, "number_of_field_offsets");
2704
2705
376
      table_offset += row_size * num_rows;
2706
376
      break;
2707
2708
16
    case BIT_ENCLOG:
2709
16
      table_offset += (4 + 4) * num_rows;
2710
16
      break;
2711
2712
265
    case BIT_ENCMAP:
2713
265
      table_offset += (4) * num_rows;
2714
265
      break;
2715
2716
609
    case BIT_ASSEMBLY:
2717
609
      row_size =
2718
609
          (4 + 2 + 2 + 2 + 2 + 4 + index_sizes.blob + (index_sizes.string * 2));
2719
2720
609
      if (!fits_in_pe(pe, table_offset, row_size))
2721
200
        break;
2722
2723
409
      row_ptr = table_offset;
2724
409
      assembly_table = (PASSEMBLY_TABLE) table_offset;
2725
2726
409
      yr_set_integer(
2727
409
          yr_le16toh(assembly_table->MajorVersion),
2728
409
          pe->object,
2729
409
          "assembly.version.major");
2730
409
      yr_set_integer(
2731
409
          yr_le16toh(assembly_table->MinorVersion),
2732
409
          pe->object,
2733
409
          "assembly.version.minor");
2734
409
      yr_set_integer(
2735
409
          yr_le16toh(assembly_table->BuildNumber),
2736
409
          pe->object,
2737
409
          "assembly.version.build_number");
2738
409
      yr_set_integer(
2739
409
          yr_le16toh(assembly_table->RevisionNumber),
2740
409
          pe->object,
2741
409
          "assembly.version.revision_number");
2742
2743
      // Can't use assembly_table here because the PublicKey comes before
2744
      // Name and is a variable length field.
2745
2746
409
      if (index_sizes.string == 4)
2747
68
        name = pe_get_dotnet_string(
2748
68
            pe,
2749
68
            string_offset,
2750
68
            str_heap_size,
2751
68
            yr_le32toh(*(DWORD*) (row_ptr + 4 + 2 + 2 + 2 + 2 + 4 +
2752
68
                                  index_sizes.blob)));
2753
341
      else
2754
341
        name = pe_get_dotnet_string(
2755
341
            pe,
2756
341
            string_offset,
2757
341
            str_heap_size,
2758
341
            yr_le16toh(
2759
341
                *(WORD*) (row_ptr + 4 + 2 + 2 + 2 + 2 + 4 + index_sizes.blob)));
2760
2761
409
      if (name != NULL)
2762
409
        yr_set_string(name, pe->object, "assembly.name");
2763
2764
      // Culture comes after Name.
2765
409
      if (index_sizes.string == 4)
2766
68
      {
2767
68
        name = pe_get_dotnet_string(
2768
68
            pe,
2769
68
            string_offset,
2770
68
            str_heap_size,
2771
68
            yr_le32toh(*(DWORD*) (row_ptr + 4 + 2 + 2 + 2 + 2 + 4 +
2772
68
                                  index_sizes.blob + index_sizes.string)));
2773
68
      }
2774
341
      else
2775
341
      {
2776
341
        name = pe_get_dotnet_string(
2777
341
            pe,
2778
341
            string_offset,
2779
341
            str_heap_size,
2780
341
            yr_le16toh(*(WORD*) (row_ptr + 4 + 2 + 2 + 2 + 2 + 4 +
2781
341
                                 index_sizes.blob + index_sizes.string)));
2782
341
      }
2783
2784
      // Sometimes it will be a zero length string. This is technically
2785
      // against the specification but happens from time to time.
2786
409
      if (name != NULL && strlen(name) > 0)
2787
409
        yr_set_string(name, pe->object, "assembly.culture");
2788
2789
409
      table_offset += row_size * num_rows;
2790
409
      break;
2791
2792
73
    case BIT_ASSEMBLYPROCESSOR:
2793
73
      table_offset += (4) * num_rows;
2794
73
      break;
2795
2796
34
    case BIT_ASSEMBLYOS:
2797
34
      table_offset += (4 + 4 + 4) * num_rows;
2798
34
      break;
2799
2800
911
    case BIT_ASSEMBLYREF:
2801
911
      row_size =
2802
911
          (2 + 2 + 2 + 2 + 4 + (index_sizes.blob * 2) +
2803
911
           (index_sizes.string * 2));
2804
2805
911
      row_ptr = table_offset;
2806
2807
145k
      for (i = 0; i < num_rows; i++)
2808
145k
      {
2809
145k
        if (!fits_in_pe(pe, row_ptr, row_size))
2810
574
          break;
2811
2812
144k
        assemblyref_table = (PASSEMBLYREF_TABLE) row_ptr;
2813
2814
144k
        yr_set_integer(
2815
144k
            yr_le16toh(assemblyref_table->MajorVersion),
2816
144k
            pe->object,
2817
144k
            "assembly_refs[%i].version.major",
2818
144k
            i);
2819
144k
        yr_set_integer(
2820
144k
            yr_le16toh(assemblyref_table->MinorVersion),
2821
144k
            pe->object,
2822
144k
            "assembly_refs[%i].version.minor",
2823
144k
            i);
2824
144k
        yr_set_integer(
2825
144k
            yr_le16toh(assemblyref_table->BuildNumber),
2826
144k
            pe->object,
2827
144k
            "assembly_refs[%i].version.build_number",
2828
144k
            i);
2829
144k
        yr_set_integer(
2830
144k
            yr_le16toh(assemblyref_table->RevisionNumber),
2831
144k
            pe->object,
2832
144k
            "assembly_refs[%i].version.revision_number",
2833
144k
            i);
2834
2835
144k
        blob_offset = pe->data + metadata_root +
2836
144k
                      yr_le32toh(streams->blob->Offset);
2837
2838
144k
        if (index_sizes.blob == 4)
2839
19.2k
          blob_offset += yr_le32toh(
2840
144k
              assemblyref_table->PublicKeyOrToken.PublicKeyOrToken_Long);
2841
125k
        else
2842
125k
          blob_offset += yr_le16toh(
2843
144k
              assemblyref_table->PublicKeyOrToken.PublicKeyOrToken_Short);
2844
2845
144k
        blob_result = dotnet_parse_blob_entry(pe, blob_offset);
2846
144k
        blob_offset += blob_result.size;
2847
2848
144k
        if (blob_result.size == 0 ||
2849
94.1k
            !fits_in_pe(pe, blob_offset, blob_result.length))
2850
50.2k
        {
2851
50.2k
          row_ptr += row_size;
2852
50.2k
          continue;
2853
50.2k
        }
2854
2855
        // Avoid empty strings.
2856
94.1k
        if (blob_result.length > 0)
2857
62.2k
        {
2858
62.2k
          yr_set_sized_string(
2859
62.2k
              (char*) blob_offset,
2860
62.2k
              blob_result.length,
2861
62.2k
              pe->object,
2862
62.2k
              "assembly_refs[%i].public_key_or_token",
2863
62.2k
              i);
2864
62.2k
        }
2865
2866
        // Can't use assemblyref_table here because the PublicKey comes before
2867
        // Name and is a variable length field.
2868
2869
94.1k
        if (index_sizes.string == 4)
2870
31.3k
          name = pe_get_dotnet_string(
2871
31.3k
              pe,
2872
31.3k
              string_offset,
2873
31.3k
              str_heap_size,
2874
31.3k
              yr_le32toh(
2875
31.3k
                  *(DWORD*) (row_ptr + 2 + 2 + 2 + 2 + 4 + index_sizes.blob)));
2876
62.8k
        else
2877
62.8k
          name = pe_get_dotnet_string(
2878
62.8k
              pe,
2879
62.8k
              string_offset,
2880
62.8k
              str_heap_size,
2881
62.8k
              yr_le16toh(
2882
62.8k
                  *(WORD*) (row_ptr + 2 + 2 + 2 + 2 + 4 + index_sizes.blob)));
2883
2884
94.1k
        if (name != NULL)
2885
94.1k
          yr_set_string(name, pe->object, "assembly_refs[%i].name", i);
2886
2887
94.1k
        row_ptr += row_size;
2888
94.1k
      }
2889
2890
911
      tables.assemblyref.Offset = table_offset;
2891
911
      tables.assemblyref.RowCount = num_rows;
2892
911
      tables.assemblyref.RowSize = row_size;
2893
2894
911
      yr_set_integer(i, pe->object, "number_of_assembly_refs");
2895
911
      table_offset += row_size * num_rows;
2896
911
      break;
2897
2898
38
    case BIT_ASSEMBLYREFPROCESSOR:
2899
38
      table_offset += (4 + index_sizes.assemblyrefprocessor) * num_rows;
2900
38
      break;
2901
2902
19
    case BIT_ASSEMBLYREFOS:
2903
19
      table_offset += (4 + 4 + 4 + index_sizes.assemblyref) * num_rows;
2904
19
      break;
2905
2906
23
    case BIT_FILE:
2907
23
      table_offset += (4 + index_sizes.string + index_sizes.blob) * num_rows;
2908
23
      break;
2909
2910
25
    case BIT_EXPORTEDTYPE:
2911
25
      row_count = max_rows(
2912
25
          3,
2913
25
          yr_le32toh(rows.file),
2914
25
          yr_le32toh(rows.assemblyref),
2915
25
          yr_le32toh(rows.exportedtype));
2916
2917
25
      if (row_count > (0xFFFF >> 0x02))
2918
0
        index_size = 4;
2919
25
      else
2920
25
        index_size = 2;
2921
2922
25
      table_offset += (4 + 4 + (index_sizes.string * 2) + index_size) *
2923
25
                      num_rows;
2924
25
      break;
2925
2926
733
    case BIT_MANIFESTRESOURCE:
2927
      // This is an Implementation coded index with no 3rd bit specified.
2928
733
      row_count = max_rows(
2929
733
          2, yr_le32toh(rows.file), yr_le32toh(rows.assemblyref));
2930
2931
733
      if (row_count > (0xFFFF >> 0x02))
2932
0
        index_size = 4;
2933
733
      else
2934
733
        index_size = 2;
2935
2936
733
      row_size = (4 + 4 + index_sizes.string + index_size);
2937
733
      row_ptr = table_offset;
2938
2939
      // First DWORD is the offset.
2940
199k
      for (i = 0; i < num_rows; i++)
2941
199k
      {
2942
199k
        if (!fits_in_pe(pe, row_ptr, row_size))
2943
457
          break;
2944
2945
198k
        manifestresource_table = (PMANIFESTRESOURCE_TABLE) row_ptr;
2946
2947
198k
        if (index_size == 4)
2948
0
          implementation = yr_le32toh(
2949
198k
              *(DWORD*) (row_ptr + 4 + 4 + index_sizes.string));
2950
198k
        else
2951
198k
          implementation = yr_le16toh(
2952
198k
              *(WORD*) (row_ptr + 4 + 4 + index_sizes.string));
2953
2954
198k
        row_ptr += row_size;
2955
2956
198k
        name = pe_get_dotnet_string(
2957
198k
            pe,
2958
198k
            string_offset,
2959
198k
            str_heap_size,
2960
198k
            DOTNET_STRING_INDEX(manifestresource_table->Name));
2961
2962
198k
        if (name != NULL)
2963
198k
          yr_set_string(name, pe->object, "resources[%i].name", i);
2964
2965
        // Only set offset and length if it is in this file, otherwise continue
2966
        // with the next resource.
2967
198k
        if (implementation != 0)
2968
167k
          continue;
2969
2970
31.7k
        resource_offset = yr_le32toh(manifestresource_table->Offset);
2971
2972
31.7k
        if (!fits_in_pe(
2973
31.7k
                pe, pe->data + resource_base + resource_offset, sizeof(DWORD)))
2974
24.4k
          continue;
2975
2976
7.27k
        resource_size = yr_le32toh(
2977
7.27k
            *(DWORD*) (pe->data + resource_base + resource_offset));
2978
2979
        // Add 4 to skip the size.
2980
7.27k
        yr_set_integer(
2981
7.27k
            resource_base + resource_offset + 4,
2982
7.27k
            pe->object,
2983
7.27k
            "resources[%i].offset",
2984
7.27k
            i);
2985
2986
7.27k
        yr_set_integer(resource_size, pe->object, "resources[%i].length", i);
2987
7.27k
      }
2988
2989
733
      yr_set_integer(i, pe->object, "number_of_resources");
2990
2991
733
      table_offset += row_size * num_rows;
2992
733
      break;
2993
2994
734
    case BIT_NESTEDCLASS:
2995
734
      row_size = index_sizes.typedef_ * 2;
2996
2997
734
      tables.nestedclass.Offset = table_offset;
2998
734
      tables.nestedclass.RowCount = num_rows;
2999
734
      tables.nestedclass.RowSize = row_size;
3000
3001
734
      table_offset += row_size * num_rows;
3002
734
      break;
3003
3004
350
    case BIT_GENERICPARAM:
3005
350
      row_count = max_rows(
3006
350
          2, yr_le32toh(rows.typedef_), yr_le32toh(rows.methoddef));
3007
3008
350
      if (row_count > (0xFFFF >> 0x01))
3009
0
        index_size = 4;
3010
350
      else
3011
350
        index_size = 2;
3012
3013
350
      row_size = (2 + 2 + index_size + index_sizes.string);
3014
3015
350
      tables.genericparam.Offset = table_offset;
3016
350
      tables.genericparam.RowCount = num_rows;
3017
350
      tables.genericparam.RowSize = row_size;
3018
3019
350
      table_offset += row_size * num_rows;
3020
350
      break;
3021
3022
155
    case BIT_METHODSPEC:
3023
155
      row_count = max_rows(
3024
155
          2, yr_le32toh(rows.methoddef), yr_le32toh(rows.memberref));
3025
3026
155
      if (row_count > (0xFFFF >> 0x01))
3027
0
        index_size = 4;
3028
155
      else
3029
155
        index_size = 2;
3030
3031
155
      table_offset += (index_size + index_sizes.blob) * num_rows;
3032
155
      break;
3033
3034
36
    case BIT_GENERICPARAMCONSTRAINT:
3035
36
      row_count = max_rows(
3036
36
          3,
3037
36
          yr_le32toh(rows.typedef_),
3038
36
          yr_le32toh(rows.typeref),
3039
36
          yr_le32toh(rows.typespec));
3040
3041
36
      if (row_count > (0xFFFF >> 0x02))
3042
0
        index_size = 4;
3043
36
      else
3044
36
        index_size = 2;
3045
3046
36
      table_offset += (index_sizes.genericparam + index_size) * num_rows;
3047
36
      break;
3048
3049
70
    default:
3050
      // printf("Unknown bit: %i\n", bit_check);
3051
70
      return;
3052
20.5k
    }
3053
3054
20.5k
    matched_bits++;
3055
20.5k
  }
3056
3057
2.85k
  CLASS_CONTEXT class_context = {
3058
2.85k
      .pe = pe,
3059
2.85k
      .tables = &tables,
3060
2.85k
      .index_sizes = &index_sizes,
3061
2.85k
      .str_heap = string_offset,
3062
2.85k
      .str_size = str_heap_size,
3063
2.85k
      .blob_heap = pe->data + streams->metadata_root +
3064
2.85k
                   yr_le32toh(streams->blob->Offset),
3065
2.85k
      .blob_size = yr_le32toh(streams->blob->Size)};
3066
3067
2.85k
  parse_user_types(&class_context);
3068
2.85k
}
3069
3070
// Parsing the #~ stream is done in two parts. The first part (this function)
3071
// parses enough of the Stream to provide context for the second pass. In
3072
// particular it is collecting the number of rows for each of the tables. The
3073
// second part parses the actual tables of interest.
3074
3075
void dotnet_parse_tilde(PE* pe, PCLI_HEADER cli_header, PSTREAMS streams)
3076
5.34k
{
3077
5.34k
  PTILDE_HEADER tilde_header;
3078
5.34k
  int64_t resource_base;
3079
5.34k
  int64_t metadata_root = streams->metadata_root;
3080
5.34k
  uint32_t* row_offset = NULL;
3081
3082
5.34k
  int bit_check;
3083
3084
  // This is used as an offset into the rows and tables. For every bit set in
3085
  // Valid this will be incremented. This is because the bit position doesn't
3086
  // matter, just the number of bits that are set, when determining how many
3087
  // rows and what the table structure is.
3088
5.34k
  int matched_bits = 0;
3089
3090
  // We need to know the number of rows for some tables, because they are
3091
  // indexed into. The index will be either 2 or 4 bytes, depending upon the
3092
  // number of rows being indexed into.
3093
5.34k
  ROWS rows;
3094
5.34k
  INDEX_SIZES index_sizes;
3095
5.34k
  uint32_t heap_sizes;
3096
3097
  // Default all rows to 0. They will be set to actual values later on, if
3098
  // they exist in the file.
3099
5.34k
  memset(&rows, '\0', sizeof(ROWS));
3100
3101
  // Default index sizes are 2. Will be bumped to 4 if necessary.
3102
5.34k
  memset(&index_sizes, 2, sizeof(index_sizes));
3103
3104
5.34k
  tilde_header = (PTILDE_HEADER) (pe->data + metadata_root +
3105
5.34k
                                  yr_le32toh(streams->tilde->Offset));
3106
3107
5.34k
  if (!struct_fits_in_pe(pe, tilde_header, TILDE_HEADER))
3108
84
    return;
3109
3110
5.26k
  heap_sizes = yr_le32toh(tilde_header->HeapSizes);
3111
3112
  // Set index sizes for various heaps.
3113
5.26k
  if (heap_sizes & 0x01)
3114
1.15k
    index_sizes.string = 4;
3115
3116
5.26k
  if (heap_sizes & 0x02)
3117
1.09k
    index_sizes.guid = 4;
3118
3119
5.26k
  if (heap_sizes & 0x04)
3120
943
    index_sizes.blob = 4;
3121
3122
  // Immediately after the tilde header is an array of 32bit values which
3123
  // indicate how many rows are in each table. The tables are immediately
3124
  // after the rows array.
3125
  //
3126
  // Save the row offset.
3127
5.26k
  row_offset = (uint32_t*) (tilde_header + 1);
3128
3129
  // Walk all the bits first because we need to know the number of rows for
3130
  // some tables in order to parse others. In particular this applies to
3131
  // coded indexes, which are documented in ECMA-335 II.24.2.6.
3132
342k
  for (bit_check = 0; bit_check < 64; bit_check++)
3133
336k
  {
3134
336k
    if (!((yr_le64toh(tilde_header->Valid) >> bit_check) & 0x01))
3135
282k
      continue;
3136
3137
54.6k
#define ROW_CHECK(name)                                                  \
3138
54.6k
  if (fits_in_pe(pe, row_offset, (matched_bits + 1) * sizeof(uint32_t))) \
3139
27.5k
    rows.name = *(row_offset + matched_bits);
3140
3141
54.6k
#define ROW_CHECK_WITH_INDEX(name)    \
3142
54.6k
  ROW_CHECK(name);                    \
3143
23.1k
  if (yr_le32toh(rows.name) > 0xFFFF) \
3144
23.1k
    index_sizes.name = 4;
3145
3146
54.6k
    switch (bit_check)
3147
54.6k
    {
3148
1.05k
    case BIT_MODULE:
3149
1.05k
      ROW_CHECK_WITH_INDEX(module);
3150
1.05k
      break;
3151
988
    case BIT_MODULEREF:
3152
988
      ROW_CHECK_WITH_INDEX(moduleref);
3153
988
      break;
3154
1.74k
    case BIT_ASSEMBLYREF:
3155
1.74k
      ROW_CHECK_WITH_INDEX(assemblyref);
3156
1.74k
      break;
3157
583
    case BIT_ASSEMBLYREFPROCESSOR:
3158
583
      ROW_CHECK_WITH_INDEX(assemblyrefprocessor);
3159
583
      break;
3160
1.84k
    case BIT_TYPEREF:
3161
1.84k
      ROW_CHECK_WITH_INDEX(typeref);
3162
1.84k
      break;
3163
2.21k
    case BIT_METHODDEF:
3164
2.21k
      ROW_CHECK_WITH_INDEX(methoddef);
3165
2.21k
      break;
3166
1.42k
    case BIT_MEMBERREF:
3167
1.42k
      ROW_CHECK_WITH_INDEX(memberref);
3168
1.42k
      break;
3169
3.49k
    case BIT_TYPEDEF:
3170
3.49k
      ROW_CHECK_WITH_INDEX(typedef_);
3171
3.49k
      break;
3172
1.19k
    case BIT_TYPESPEC:
3173
1.19k
      ROW_CHECK_WITH_INDEX(typespec);
3174
1.19k
      break;
3175
1.05k
    case BIT_FIELD:
3176
1.05k
      ROW_CHECK_WITH_INDEX(field);
3177
1.05k
      break;
3178
1.22k
    case BIT_PARAM:
3179
1.22k
      ROW_CHECK_WITH_INDEX(param);
3180
1.22k
      break;
3181
960
    case BIT_PROPERTY:
3182
960
      ROW_CHECK_WITH_INDEX(property);
3183
960
      break;
3184
1.52k
    case BIT_INTERFACEIMPL:
3185
1.52k
      ROW_CHECK_WITH_INDEX(interfaceimpl);
3186
1.52k
      break;
3187
629
    case BIT_EVENT:
3188
629
      ROW_CHECK_WITH_INDEX(event);
3189
629
      break;
3190
726
    case BIT_STANDALONESIG:
3191
726
      ROW_CHECK(standalonesig);
3192
726
      break;
3193
1.29k
    case BIT_ASSEMBLY:
3194
1.29k
      ROW_CHECK_WITH_INDEX(assembly);
3195
1.29k
      break;
3196
1.13k
    case BIT_FILE:
3197
1.13k
      ROW_CHECK(file);
3198
1.13k
      break;
3199
590
    case BIT_EXPORTEDTYPE:
3200
590
      ROW_CHECK(exportedtype);
3201
590
      break;
3202
1.36k
    case BIT_MANIFESTRESOURCE:
3203
1.36k
      ROW_CHECK(manifestresource);
3204
1.36k
      break;
3205
1.06k
    case BIT_GENERICPARAM:
3206
1.06k
      ROW_CHECK_WITH_INDEX(genericparam);
3207
1.06k
      break;
3208
586
    case BIT_GENERICPARAMCONSTRAINT:
3209
586
      ROW_CHECK(genericparamconstraint);
3210
586
      break;
3211
818
    case BIT_METHODSPEC:
3212
818
      ROW_CHECK_WITH_INDEX(methodspec);
3213
818
      break;
3214
27.1k
    default:
3215
27.1k
      break;
3216
54.6k
    }
3217
3218
54.6k
    matched_bits++;
3219
54.6k
  }
3220
3221
  // This is used when parsing the MANIFEST RESOURCE table.
3222
5.26k
  resource_base = pe_rva_to_offset(
3223
5.26k
      pe, yr_le32toh(cli_header->Resources.VirtualAddress));
3224
3225
5.26k
  dotnet_parse_tilde_2(
3226
5.26k
      pe, tilde_header, resource_base, rows, index_sizes, streams);
3227
5.26k
}
3228
3229
static bool dotnet_is_dotnet(PE* pe)
3230
6.12k
{
3231
6.12k
  PIMAGE_DATA_DIRECTORY directory = pe_get_directory_entry(
3232
6.12k
      pe, IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR);
3233
3234
6.12k
  if (!directory)
3235
0
    return false;
3236
3237
6.12k
  int64_t offset = pe_rva_to_offset(pe, yr_le32toh(directory->VirtualAddress));
3238
3239
6.12k
  if (offset < 0 || !struct_fits_in_pe(pe, pe->data + offset, CLI_HEADER))
3240
209
    return false;
3241
3242
5.91k
  CLI_HEADER* cli_header = (CLI_HEADER*) (pe->data + offset);
3243
3244
5.91k
  if (yr_le32toh(cli_header->Size) != sizeof(CLI_HEADER))
3245
64
    return false;
3246
3247
5.85k
  int64_t metadata_root = pe_rva_to_offset(
3248
5.85k
      pe, yr_le32toh(cli_header->MetaData.VirtualAddress));
3249
5.85k
  offset = metadata_root;
3250
3251
5.85k
  if (!struct_fits_in_pe(pe, pe->data + metadata_root, NET_METADATA))
3252
5
    return false;
3253
3254
5.84k
  NET_METADATA* metadata = (NET_METADATA*) (pe->data + metadata_root);
3255
3256
5.84k
  if (yr_le32toh(metadata->Magic) != NET_METADATA_MAGIC)
3257
52
    return false;
3258
3259
  // Version length must be between 1 and 255, and be a multiple of 4.
3260
  // Also make sure it fits in pe.
3261
5.79k
  uint32_t md_len = yr_le32toh(metadata->Length);
3262
5.79k
  if (md_len == 0 || md_len > 255 || md_len % 4 != 0 ||
3263
5.74k
      !fits_in_pe(pe, pe->data + offset + sizeof(NET_METADATA), md_len))
3264
56
  {
3265
56
    return false;
3266
56
  }
3267
3268
5.73k
  if (IS_64BITS_PE(pe))
3269
45
  {
3270
45
    if (yr_le32toh(OptionalHeader(pe, NumberOfRvaAndSizes)) <
3271
45
        IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR)
3272
4
      return false;
3273
45
  }
3274
3275
5.73k
  return true;
3276
5.73k
}
3277
3278
void dotnet_parse_com(PE* pe)
3279
6.12k
{
3280
6.12k
  PIMAGE_DATA_DIRECTORY directory;
3281
6.12k
  PCLI_HEADER cli_header;
3282
6.12k
  PNET_METADATA metadata;
3283
6.12k
  int64_t metadata_root, offset;
3284
6.12k
  char* end;
3285
6.12k
  STREAMS headers;
3286
6.12k
  WORD num_streams;
3287
6.12k
  uint32_t md_len;
3288
3289
6.12k
  if (!dotnet_is_dotnet(pe))
3290
390
  {
3291
390
    yr_set_integer(0, pe->object, "is_dotnet");
3292
390
    return;
3293
390
  }
3294
3295
5.73k
  yr_set_integer(1, pe->object, "is_dotnet");
3296
3297
5.73k
  directory = pe_get_directory_entry(pe, IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR);
3298
5.73k
  if (directory == NULL)
3299
0
    return;
3300
3301
5.73k
  offset = pe_rva_to_offset(pe, yr_le32toh(directory->VirtualAddress));
3302
3303
5.73k
  if (offset < 0 || !struct_fits_in_pe(pe, pe->data + offset, CLI_HEADER))
3304
0
    return;
3305
3306
5.73k
  cli_header = (PCLI_HEADER) (pe->data + offset);
3307
3308
5.73k
  offset = metadata_root = pe_rva_to_offset(
3309
5.73k
      pe, yr_le32toh(cli_header->MetaData.VirtualAddress));
3310
3311
5.73k
  if (!struct_fits_in_pe(pe, pe->data + offset, NET_METADATA))
3312
0
    return;
3313
3314
5.73k
  metadata = (PNET_METADATA) (pe->data + offset);
3315
3316
  // Version length must be between 1 and 255, and be a multiple of 4.
3317
  // Also make sure it fits in pe.
3318
5.73k
  md_len = yr_le32toh(metadata->Length);
3319
3320
5.73k
  if (md_len == 0 || md_len > 255 || md_len % 4 != 0 ||
3321
5.73k
      !fits_in_pe(pe, pe->data + offset + sizeof(NET_METADATA), md_len))
3322
0
  {
3323
0
    return;
3324
0
  }
3325
3326
  // The length includes the NULL terminator and is rounded up to a multiple of
3327
  // 4. We need to exclude the terminator and the padding, so search for the
3328
  // first NULL byte.
3329
5.73k
  end = (char*) memmem((void*) metadata->Version, md_len, "\0", 1);
3330
3331
5.73k
  if (end != NULL)
3332
3.31k
    yr_set_sized_string(
3333
5.73k
        metadata->Version, (end - metadata->Version), pe->object, "version");
3334
3335
  // The metadata structure has some variable length records after the version.
3336
  // We must manually parse things from here on out.
3337
  //
3338
  // Flags are 2 bytes (always 0).
3339
5.73k
  offset += sizeof(NET_METADATA) + md_len + 2;
3340
3341
  // 2 bytes for Streams.
3342
5.73k
  if (!fits_in_pe(pe, pe->data + offset, 2))
3343
10
    return;
3344
3345
5.72k
  num_streams = (WORD) * (pe->data + offset);
3346
5.72k
  offset += 2;
3347
3348
5.72k
  headers = dotnet_parse_stream_headers(pe, offset, metadata_root, num_streams);
3349
3350
5.72k
  if (headers.guid != NULL)
3351
543
    dotnet_parse_guid(pe, metadata_root, headers.guid);
3352
3353
  // Parse the #~ stream, which includes various tables of interest.
3354
  // These tables reference the blob and string streams, so we need to ensure
3355
  // those are not NULL also.
3356
5.72k
  if (headers.tilde != NULL && headers.string != NULL && headers.blob != NULL)
3357
5.34k
    dotnet_parse_tilde(pe, cli_header, &headers);
3358
3359
5.72k
  if (headers.us != NULL)
3360
978
    dotnet_parse_us(pe, metadata_root, headers.us);
3361
5.72k
}
3362
3363
7.46k
begin_declarations
3364
7.46k
  declare_integer("is_dotnet");
3365
7.46k
  declare_string("version");
3366
7.46k
  declare_string("module_name");
3367
3368
22.4k
  begin_struct_array("streams")
3369
7.46k
    declare_string("name");
3370
7.46k
    declare_integer("offset");
3371
7.46k
    declare_integer("size");
3372
14.9k
  end_struct_array("streams")
3373
3374
7.46k
  declare_integer("number_of_streams");
3375
3376
14.9k
  declare_string_array("guids");
3377
14.9k
  declare_integer("number_of_guids");
3378
3379
22.4k
  begin_struct_array("resources")
3380
7.46k
    declare_integer("offset");
3381
7.46k
    declare_integer("length");
3382
7.46k
    declare_string("name");
3383
14.9k
  end_struct_array("resources")
3384
3385
7.46k
  declare_integer("number_of_resources");
3386
3387
22.4k
  begin_struct_array("classes")
3388
7.46k
    declare_string("fullname");
3389
7.46k
    declare_string("name");
3390
7.46k
    declare_string("namespace");
3391
7.46k
    declare_string("visibility");
3392
7.46k
    declare_string("type");
3393
7.46k
    declare_integer("abstract");
3394
7.46k
    declare_integer("sealed");
3395
3396
7.46k
    declare_integer("number_of_generic_parameters");
3397
14.9k
    declare_string_array("generic_parameters");
3398
3399
14.9k
    declare_integer("number_of_base_types");
3400
14.9k
    declare_string_array("base_types");
3401
3402
14.9k
    declare_integer("number_of_methods");
3403
22.4k
    begin_struct_array("methods")
3404
14.9k
      declare_string_array("generic_parameters");
3405
3406
14.9k
      declare_integer("number_of_generic_parameters");
3407
3408
22.4k
      begin_struct_array("parameters")
3409
7.46k
        declare_string("name");
3410
7.46k
        declare_string("type");
3411
14.9k
      end_struct_array("parameters")
3412
3413
7.46k
      declare_integer("number_of_parameters");
3414
3415
7.46k
      declare_string("return_type");
3416
7.46k
      declare_integer("abstract");
3417
7.46k
      declare_integer("final");
3418
7.46k
      declare_integer("virtual");
3419
7.46k
      declare_integer("static");
3420
7.46k
      declare_string("visibility");
3421
7.46k
      declare_string("name");
3422
14.9k
    end_struct_array("methods")
3423
3424
14.9k
  end_struct_array("classes")
3425
3426
7.46k
  declare_integer("number_of_classes");
3427
3428
22.4k
  begin_struct_array("assembly_refs")
3429
14.9k
    begin_struct("version")
3430
7.46k
      declare_integer("major");
3431
7.46k
      declare_integer("minor");
3432
7.46k
      declare_integer("build_number");
3433
7.46k
      declare_integer("revision_number");
3434
14.9k
    end_struct("version")
3435
7.46k
    declare_string("public_key_or_token");
3436
7.46k
    declare_string("name");
3437
14.9k
  end_struct_array("assembly_refs")
3438
3439
7.46k
  declare_integer("number_of_assembly_refs");
3440
3441
14.9k
  begin_struct("assembly")
3442
14.9k
    begin_struct("version")
3443
7.46k
      declare_integer("major");
3444
7.46k
      declare_integer("minor");
3445
7.46k
      declare_integer("build_number");
3446
7.46k
      declare_integer("revision_number");
3447
14.9k
    end_struct("version")
3448
7.46k
    declare_string("name");
3449
7.46k
    declare_string("culture");
3450
14.9k
  end_struct("assembly")
3451
3452
14.9k
  declare_string_array("modulerefs");
3453
14.9k
  declare_integer("number_of_modulerefs");
3454
14.9k
  declare_string_array("user_strings");
3455
14.9k
  declare_integer("number_of_user_strings");
3456
7.46k
  declare_string("typelib");
3457
14.9k
  declare_string_array("constants");
3458
14.9k
  declare_integer("number_of_constants");
3459
3460
14.9k
  declare_integer_array("field_offsets");
3461
14.9k
  declare_integer("number_of_field_offsets");
3462
7.46k
end_declarations
3463
3464
int module_initialize(YR_MODULE* module)
3465
11
{
3466
11
  return ERROR_SUCCESS;
3467
11
}
3468
3469
int module_finalize(YR_MODULE* module)
3470
0
{
3471
0
  return ERROR_SUCCESS;
3472
0
}
3473
3474
int module_load(
3475
    YR_SCAN_CONTEXT* context,
3476
    YR_OBJECT* module_object,
3477
    void* module_data,
3478
    size_t module_data_size)
3479
7.46k
{
3480
7.46k
  YR_MEMORY_BLOCK* block;
3481
7.46k
  YR_MEMORY_BLOCK_ITERATOR* iterator = context->iterator;
3482
7.46k
  const uint8_t* block_data = NULL;
3483
3484
7.46k
  foreach_memory_block(iterator, block)
3485
7.46k
  {
3486
7.46k
    PIMAGE_NT_HEADERS32 pe_header;
3487
3488
7.46k
    block_data = yr_fetch_block_data(block);
3489
3490
7.46k
    if (block_data == NULL)
3491
0
      continue;
3492
3493
7.46k
    pe_header = pe_get_header(block_data, block->size);
3494
3495
7.46k
    if (pe_header != NULL)
3496
6.12k
    {
3497
      // Ignore DLLs while scanning a process
3498
3499
6.12k
      if (!(context->flags & SCAN_FLAGS_PROCESS_MEMORY) ||
3500
0
          !(pe_header->FileHeader.Characteristics & IMAGE_FILE_DLL))
3501
6.12k
      {
3502
6.12k
        PE* pe = (PE*) yr_malloc(sizeof(PE));
3503
3504
6.12k
        if (pe == NULL)
3505
0
          return ERROR_INSUFFICIENT_MEMORY;
3506
3507
6.12k
        pe->data = block_data;
3508
6.12k
        pe->data_size = block->size;
3509
6.12k
        pe->object = module_object;
3510
6.12k
        pe->header = pe_header;
3511
3512
6.12k
        module_object->data = pe;
3513
3514
6.12k
        dotnet_parse_com(pe);
3515
3516
6.12k
        break;
3517
6.12k
      }
3518
6.12k
    }
3519
7.46k
  }
3520
3521
7.46k
  return ERROR_SUCCESS;
3522
7.46k
}
3523
3524
int module_unload(YR_OBJECT* module_object)
3525
7.46k
{
3526
7.46k
  PE* pe = (PE*) module_object->data;
3527
3528
7.46k
  if (pe == NULL)
3529
1.34k
    return ERROR_SUCCESS;
3530
3531
6.12k
  yr_free(pe);
3532
3533
6.12k
  return ERROR_SUCCESS;
3534
7.46k
}