Coverage Report

Created: 2025-12-23 06:05

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