Coverage Report

Created: 2026-05-30 06:36

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