Coverage Report

Created: 2025-07-23 06:45

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