Coverage Report

Created: 2026-01-09 06:04

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