Coverage Report

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