Coverage Report

Created: 2026-02-26 06:40

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