Coverage Report

Created: 2025-07-18 06:53

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