Coverage Report

Created: 2025-07-23 06:46

/src/yara/libyara/modules/dotnet/dotnet.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
Copyright (c) 2015. The YARA Authors. All Rights Reserved.
3
4
Licensed under the Apache License, Version 2.0 (the "License");
5
you may not use this file except in compliance with the License.
6
You may obtain a copy of the License at
7
8
   http://www.apache.org/licenses/LICENSE-2.0
9
10
Unless required by applicable law or agreed to in writing, software
11
distributed under the License is distributed on an "AS IS" BASIS,
12
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
See the License for the specific language governing permissions and
14
limitations under the License.
15
*/
16
17
#include <ctype.h>
18
#include <stdarg.h>
19
#include <stdbool.h>
20
#include <stdio.h>
21
#include <string.h>
22
#include <time.h>
23
#include <yara/dotnet.h>
24
#include <yara/mem.h>
25
#include <yara/modules.h>
26
#include <yara/pe.h>
27
#include <yara/pe_utils.h>
28
#include <yara/simple_str.h>
29
#include <yara/strutils.h>
30
#include <yara/unaligned.h>
31
32
#define MODULE_NAME dotnet
33
34
static uint32_t max_rows(int count, ...)
35
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)
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
    const char* name = pe_get_dotnet_string(
1366
0
        ctx->pe, str_heap, str_size, row.Name);
1367
1368
    // Ignore invalid/empty names
1369
0
    if (!name || !*name)
1370
0
      continue;
1371
1372
    // Try to find generic params for the method
1373
0
    GENERIC_PARAMETERS method_gen_params = {0};
1374
0
    parse_generic_params(ctx, true, methodlist + idx, &method_gen_params);
1375
1376
    // Read the blob entry with signature data
1377
0
    const uint8_t* sig_data = ctx->blob_heap + row.Signature;
1378
1379
0
    BLOB_PARSE_RESULT blob_res = dotnet_parse_blob_entry(ctx->pe, sig_data);
1380
0
    sig_data += blob_res.size;
1381
0
    uint32_t sig_len = blob_res.length;
1382
0
    uint32_t param_count = 0;
1383
1384
0
    char* return_type = NULL;
1385
    // If there is valid blob and at least minimum to parse
1386
    // (flags, paramCount, retType) parse these basic information
1387
0
    if (blob_res.size && sig_len >= 3)
1388
0
    {
1389
0
      uint8_t flags = read_u8(&sig_data);
1390
0
      sig_len -= 1;
1391
0
      if (flags & SIG_FLAG_GENERIC)
1392
        // Generic param count, ignored as we get the
1393
        // information from generic param table
1394
0
        (void) read_blob_unsigned(&sig_data, &sig_len);
1395
1396
      // Regular param count
1397
0
      param_count = read_blob_unsigned(&sig_data, &sig_len);
1398
0
      return_type = parse_signature_type(
1399
0
          ctx, &sig_data, &sig_len, class_gen_params, &method_gen_params, 0);
1400
0
    }
1401
0
    else  // Error, skip
1402
0
      goto clean_next;
1403
1404
    // Sanity check for corrupted files
1405
0
    if (!return_type || param_count > MAX_PARAM_COUNT)
1406
0
      goto clean_next;
1407
1408
0
    result = parse_method_params(
1409
0
        ctx,
1410
0
        row.ParamList,
1411
0
        out_idx,
1412
0
        class_idx,
1413
0
        param_count,
1414
0
        sig_data,
1415
0
        sig_len,
1416
0
        class_gen_params,
1417
0
        &method_gen_params);
1418
1419
0
    if (!result)
1420
0
      goto clean_next;
1421
1422
0
    const char* visibility = get_method_visibility(row.Flags);
1423
0
    uint32_t stat = (row.Flags & METHOD_ATTR_STATIC) != 0;
1424
0
    uint32_t final = (row.Flags & METHOD_ATTR_FINAL) != 0;
1425
0
    uint32_t virtual = (row.Flags & METHOD_ATTR_VIRTUAL) != 0;
1426
0
    uint32_t abstract = (row.Flags & METHOD_ATTR_ABSTRACT) != 0;
1427
1428
0
    YR_OBJECT* out_obj = ctx->pe->object;
1429
0
    yr_set_string(
1430
0
        name, out_obj, "classes[%i].methods[%i].name", class_idx, out_idx);
1431
0
    yr_set_string(
1432
0
        visibility,
1433
0
        out_obj,
1434
0
        "classes[%i].methods[%i].visibility",
1435
0
        class_idx,
1436
0
        out_idx);
1437
0
    yr_set_integer(
1438
0
        stat, out_obj, "classes[%i].methods[%i].static", class_idx, out_idx);
1439
0
    yr_set_integer(
1440
0
        virtual,
1441
0
        out_obj,
1442
0
        "classes[%i].methods[%i].virtual",
1443
0
        class_idx,
1444
0
        out_idx);
1445
0
    yr_set_integer(
1446
0
        final, out_obj, "classes[%i].methods[%i].final", class_idx, out_idx);
1447
0
    yr_set_integer(
1448
0
        abstract,
1449
0
        out_obj,
1450
0
        "classes[%i].methods[%i].abstract",
1451
0
        class_idx,
1452
0
        out_idx);
1453
0
    yr_set_integer(
1454
0
        method_gen_params.len,
1455
0
        out_obj,
1456
0
        "classes[%i].methods[%i].number_of_generic_parameters",
1457
0
        class_idx,
1458
0
        out_idx);
1459
1460
0
    for (uint32_t i = 0; i < method_gen_params.len; ++i)
1461
0
    {
1462
0
      yr_set_string(
1463
0
          method_gen_params.names[i],
1464
0
          ctx->pe->object,
1465
0
          "classes[%i].methods[%i].generic_parameters[%i]",
1466
0
          class_idx,
1467
0
          out_idx,
1468
0
          i);
1469
0
    }
1470
1471
    // Unset return type for constructors for FileInfo compatibility
1472
0
    if (strcmp(name, ".ctor") != 0 && strcmp(name, ".cctor") != 0)
1473
0
    {
1474
0
      yr_set_string(
1475
0
          return_type,
1476
0
          out_obj,
1477
0
          "classes[%i].methods[%i].return_type",
1478
0
          class_idx,
1479
0
          out_idx);
1480
0
    }
1481
1482
0
    out_idx++;
1483
0
  clean_next:
1484
0
    yr_free(return_type);
1485
0
    yr_free(method_gen_params.names);
1486
0
  }
1487
1488
0
  yr_set_integer(
1489
0
      out_idx, ctx->pe->object, "classes[%i].number_of_methods", class_idx);
1490
0
}
1491
1492
// Walks NestedClass table, returns enclosing type fullname or NULL
1493
static char* parse_enclosing_types(
1494
    const CLASS_CONTEXT* ctx,
1495
    uint32_t nested_idx,
1496
    uint32_t depth)
1497
0
{
1498
0
  if (depth > MAX_NAMESPACE_DEPTH)
1499
0
    return NULL;
1500
1501
0
  const uint8_t* str_heap = ctx->str_heap;
1502
0
  uint32_t str_size = ctx->str_size;
1503
1504
0
  for (uint32_t idx = 0; idx < ctx->tables->nestedclass.RowCount; ++idx)
1505
0
  {
1506
0
    const uint8_t* nested_data = get_table_offset(
1507
0
        &ctx->tables->nestedclass, idx + 1);
1508
1509
0
    NESTEDCLASS_ROW nested_row = {0};
1510
0
    bool read_result = read_nestedclass(ctx, nested_data, &nested_row);
1511
0
    if (!read_result)
1512
0
      continue;
1513
1514
    // We found enclosing class, get the namespace
1515
0
    if (nested_row.NestedClass == nested_idx)
1516
0
    {
1517
0
      const uint8_t* typedef_data = get_table_offset(
1518
0
          &ctx->tables->typedef_, nested_row.EnclosingClass);
1519
1520
0
      TYPEDEF_ROW typedef_row = {0};
1521
0
      bool result = read_typedef(ctx, typedef_data, &typedef_row);
1522
0
      if (!result)
1523
0
        break;
1524
1525
0
      const char* name = pe_get_dotnet_string(
1526
0
          ctx->pe, str_heap, str_size, typedef_row.Name);
1527
1528
      // Skip the Module pseudo class
1529
0
      if (name && strcmp(name, "<Module>") == 0)
1530
0
        break;
1531
1532
0
      const char* namespace = pe_get_dotnet_string(
1533
0
          ctx->pe, str_heap, str_size, typedef_row.Namespace);
1534
1535
      // Type might be further nested, try to find correct namespace,
1536
      // check for self-reference
1537
0
      if (is_nested(typedef_row.Flags) &&
1538
0
          nested_row.EnclosingClass != nested_row.NestedClass)
1539
0
      {
1540
0
        char* nested_namespace = parse_enclosing_types(
1541
0
            ctx, nested_row.EnclosingClass, depth + 1);
1542
1543
0
        char* tmp = create_full_name(namespace, nested_namespace);
1544
0
        char* fullname = create_full_name(name, tmp);
1545
0
        yr_free(nested_namespace);
1546
0
        yr_free(tmp);
1547
0
        return fullname;
1548
0
      }
1549
1550
0
      return create_full_name(name, namespace);
1551
0
    }
1552
0
  }
1553
1554
0
  return NULL;
1555
0
}
1556
1557
// Parses and reconstructs user defined types with their methods and base types
1558
static void parse_user_types(const CLASS_CONTEXT* ctx)
1559
0
{
1560
0
  const uint8_t* str_heap = ctx->str_heap;
1561
0
  uint32_t str_size = ctx->str_size;
1562
1563
  // Index for output tracking, we can't use
1564
  // offset as some classes can get skipped
1565
0
  uint32_t out_idx = 0;
1566
  // skip first class as it's module pseudo class -> start at index 1
1567
0
  for (uint32_t idx = 0; idx < ctx->tables->typedef_.RowCount; ++idx)
1568
0
  {
1569
0
    YR_OBJECT* out_obj = ctx->pe->object;
1570
    // Tables indexing starts at 1
1571
0
    const uint8_t* data = get_table_offset(&ctx->tables->typedef_, idx + 1);
1572
1573
0
    TYPEDEF_ROW row = {0};
1574
0
    bool result = read_typedef(ctx, data, &row);
1575
0
    if (!result)
1576
0
      continue;
1577
1578
0
    const char* name = pe_get_dotnet_string(
1579
0
        ctx->pe, str_heap, str_size, row.Name);
1580
0
    const char* type = get_typedef_type(row.Flags);
1581
1582
    // Ignore invalid types and invalid (empty) names
1583
0
    if (!name || !*name || !type)
1584
0
      continue;
1585
1586
    // If the type is generic, it will include ` at the end of a name
1587
    // with number of generic arguments, just use the part before that
1588
0
    const char* end = strchr(name, '`');
1589
    // If the name will turn out empty, skip it and skip Module pseudo class
1590
0
    if (end == name || strcmp(name, "<Module>") == 0)
1591
0
      continue;
1592
1593
0
    if (end)
1594
0
      yr_set_sized_string(
1595
0
          name, end - name, out_obj, "classes[%i].name", out_idx);
1596
0
    else
1597
0
      yr_set_string(name, out_obj, "classes[%i].name", out_idx);
1598
1599
0
    char* fullname = NULL;
1600
0
    char* namespace = pe_get_dotnet_string(
1601
0
        ctx->pe, str_heap, str_size, row.Namespace);
1602
1603
    // Type might be nested, if so -> find correct namespace
1604
0
    if (is_nested(row.Flags))
1605
0
    {
1606
0
      char* nested_namespace = parse_enclosing_types(ctx, idx + 1, 1);
1607
0
      namespace = create_full_name(namespace, nested_namespace);
1608
0
      yr_set_string(namespace, out_obj, "classes[%i].namespace", out_idx);
1609
0
      fullname = create_full_name(name, namespace);
1610
0
      yr_free(nested_namespace);
1611
0
      yr_free(namespace);
1612
0
    }
1613
0
    else
1614
0
    {
1615
0
      yr_set_string(namespace, out_obj, "classes[%i].namespace", out_idx);
1616
0
      fullname = create_full_name(name, namespace);
1617
0
    }
1618
1619
0
    const char* visibility = get_type_visibility(row.Flags);
1620
0
    uint32_t abstract = (row.Flags & TYPE_ATTR_ABSTRACT) != 0;
1621
0
    uint32_t sealed = (row.Flags & TYPE_ATTR_SEALED) != 0;
1622
1623
0
    yr_set_string(fullname, out_obj, "classes[%i].fullname", out_idx);
1624
0
    yr_set_string(visibility, out_obj, "classes[%i].visibility", out_idx);
1625
0
    yr_set_string(type, out_obj, "classes[%i].type", out_idx);
1626
0
    yr_set_integer(abstract, out_obj, "classes[%i].abstract", out_idx);
1627
0
    yr_set_integer(sealed, out_obj, "classes[%i].sealed", out_idx);
1628
1629
0
    yr_free(fullname);
1630
1631
    // Find if type has any Generic parameters
1632
0
    GENERIC_PARAMETERS gen_params = {0};
1633
0
    parse_generic_params(ctx, false, idx + 1, &gen_params);
1634
1635
0
    yr_set_integer(
1636
0
        gen_params.len,
1637
0
        out_obj,
1638
0
        "classes[%i].number_of_generic_parameters",
1639
0
        out_idx);
1640
1641
0
    for (uint32_t i = 0; i < gen_params.len; ++i)
1642
0
    {
1643
0
      yr_set_string(
1644
0
          gen_params.names[i],
1645
0
          out_obj,
1646
0
          "classes[%i].generic_parameters[%i]",
1647
0
          out_idx,
1648
0
          i);
1649
0
    }
1650
    // Find type and interfaces the type inherits
1651
0
    parse_type_parents(ctx, row.Extends, idx + 1, out_idx, &gen_params);
1652
1653
    // To get the number of methods, we must peek where the MethodList
1654
    // of the next type is, then there is next.MethodList - this.MethodList
1655
    // number of methods, or if there is no following type,
1656
    // the rest of the MethodDef table is used
1657
0
    uint32_t method_count = 0;
1658
    // If there is next method
1659
0
    if (idx + 1 < ctx->tables->typedef_.RowCount)
1660
0
    {
1661
0
      const uint8_t* data = get_table_offset(&ctx->tables->typedef_, idx + 2);
1662
1663
0
      TYPEDEF_ROW next_row = {0};
1664
0
      result = read_typedef(ctx, data, &next_row);
1665
1666
      // overflow check
1667
0
      if (result && next_row.Method >= row.Method)
1668
0
        method_count = next_row.Method - row.Method;
1669
0
    }
1670
    // overflow check - use the rest of the methods in the table
1671
0
    else if (ctx->tables->methoddef.RowCount >= row.Method)
1672
0
    {
1673
0
      method_count = ctx->tables->methoddef.RowCount + 1 - row.Method;
1674
0
    }
1675
1676
    // Sanity check for corrupted files
1677
0
    if (method_count <= MAX_METHOD_COUNT)
1678
0
      parse_methods(ctx, row.Method, method_count, out_idx, &gen_params);
1679
1680
0
    yr_free(gen_params.names);
1681
0
    out_idx++;
1682
0
  }
1683
1684
0
  yr_set_integer(out_idx, ctx->pe->object, "number_of_classes");
1685
0
}
1686
1687
void dotnet_parse_guid(
1688
    PE* pe,
1689
    int64_t metadata_root,
1690
    PSTREAM_HEADER guid_header)
1691
0
{
1692
  // GUIDs are 16 bytes each, converted to hex format plus separators and NULL.
1693
0
  char guid[37];
1694
0
  int i = 0;
1695
1696
0
  const uint8_t* guid_offset = pe->data + metadata_root +
1697
0
                               yr_le32toh(guid_header->Offset);
1698
1699
0
  DWORD guid_size = yr_le32toh(guid_header->Size);
1700
1701
  // Limit the number of GUIDs to 16.
1702
0
  guid_size = yr_min(guid_size, 256);
1703
1704
  // Parse GUIDs if we have them. GUIDs are 16 bytes each.
1705
0
  while (guid_size >= 16 && fits_in_pe(pe, guid_offset, 16))
1706
0
  {
1707
0
    sprintf(
1708
0
        guid,
1709
0
        "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
1710
0
        yr_le32toh(*(uint32_t*) guid_offset),
1711
0
        yr_le16toh(*(uint16_t*) (guid_offset + 4)),
1712
0
        yr_le16toh(*(uint16_t*) (guid_offset + 6)),
1713
0
        *(guid_offset + 8),
1714
0
        *(guid_offset + 9),
1715
0
        *(guid_offset + 10),
1716
0
        *(guid_offset + 11),
1717
0
        *(guid_offset + 12),
1718
0
        *(guid_offset + 13),
1719
0
        *(guid_offset + 14),
1720
0
        *(guid_offset + 15));
1721
1722
0
    guid[(16 * 2) + 4] = '\0';
1723
1724
0
    yr_set_string(guid, pe->object, "guids[%i]", i);
1725
1726
0
    i++;
1727
0
    guid_size -= 16;
1728
0
    guid_offset += 16;
1729
0
  }
1730
1731
0
  yr_set_integer(i, pe->object, "number_of_guids");
1732
0
}
1733
1734
void dotnet_parse_us(PE* pe, int64_t metadata_root, PSTREAM_HEADER us_header)
1735
0
{
1736
0
  BLOB_PARSE_RESULT blob_result;
1737
0
  int i = 0;
1738
1739
0
  const uint32_t ush_sz = yr_le32toh(us_header->Size);
1740
1741
0
  const uint8_t* offset = pe->data + metadata_root +
1742
0
                          yr_le32toh(us_header->Offset);
1743
0
  const uint8_t* end_of_header = offset + ush_sz;
1744
1745
  // Make sure the header size is larger than 0 and its end is not past the
1746
  // end of PE.
1747
0
  if (ush_sz == 0 || !fits_in_pe(pe, offset, ush_sz))
1748
0
    return;
1749
1750
  // The first entry MUST be single NULL byte.
1751
0
  if (*offset != 0x00)
1752
0
    return;
1753
1754
0
  offset++;
1755
1756
0
  while (offset < end_of_header)
1757
0
  {
1758
0
    blob_result = dotnet_parse_blob_entry(pe, offset);
1759
1760
0
    if (blob_result.size == 0)
1761
0
      break;
1762
1763
0
    offset += blob_result.size;
1764
    // There is an additional terminal byte which is 0x01 under certain
1765
    // conditions - when any top bit in utf16 top byte is set.
1766
    // The exact conditions are not relevant to our parsing but are
1767
    // documented in ECMA-335 II.24.2.4.
1768
0
    if (blob_result.length > 0)
1769
0
      blob_result.length--;
1770
1771
    // Avoid empty strings, which usually happen as padding at the end of the
1772
    // stream.
1773
0
    if (blob_result.length > 0 && fits_in_pe(pe, offset, blob_result.length))
1774
0
    {
1775
0
      yr_set_sized_string(
1776
0
          (char*) offset,
1777
0
          blob_result.length,
1778
0
          pe->object,
1779
0
          "user_strings[%i]",
1780
0
          i);
1781
1782
0
      offset += blob_result.length;
1783
0
      i++;
1784
0
    }
1785
0
  }
1786
1787
0
  yr_set_integer(i, pe->object, "number_of_user_strings");
1788
0
}
1789
1790
STREAMS dotnet_parse_stream_headers(
1791
    PE* pe,
1792
    int64_t offset,
1793
    int64_t metadata_root,
1794
    DWORD num_streams)
1795
0
{
1796
0
  PSTREAM_HEADER stream_header;
1797
0
  STREAMS headers;
1798
1799
0
  char* start;
1800
0
  char* eos;
1801
0
  char stream_name[DOTNET_STREAM_NAME_SIZE + 1];
1802
0
  unsigned int i;
1803
1804
0
  memset(&headers, '\0', sizeof(STREAMS));
1805
0
  headers.metadata_root = metadata_root;
1806
1807
0
  stream_header = (PSTREAM_HEADER) (pe->data + offset);
1808
1809
0
  for (i = 0; i < num_streams; i++)
1810
0
  {
1811
0
    if (!struct_fits_in_pe(pe, stream_header, STREAM_HEADER))
1812
0
      break;
1813
1814
0
    start = (char*) stream_header->Name;
1815
1816
0
    if (!fits_in_pe(pe, start, DOTNET_STREAM_NAME_SIZE))
1817
0
      break;
1818
1819
0
    eos = (char*) memmem((void*) start, DOTNET_STREAM_NAME_SIZE, "\0", 1);
1820
1821
0
    if (eos == NULL)
1822
0
      break;
1823
1824
0
    strncpy(stream_name, stream_header->Name, DOTNET_STREAM_NAME_SIZE);
1825
0
    stream_name[DOTNET_STREAM_NAME_SIZE] = '\0';
1826
1827
0
    yr_set_string(stream_name, pe->object, "streams[%i].name", i);
1828
1829
    // Offset is relative to metadata_root.
1830
0
    yr_set_integer(
1831
0
        metadata_root + yr_le32toh(stream_header->Offset),
1832
0
        pe->object,
1833
0
        "streams[%i].offset",
1834
0
        i);
1835
1836
0
    yr_set_integer(
1837
0
        yr_le32toh(stream_header->Size), pe->object, "streams[%i].size", i);
1838
1839
    // Store necessary bits to parse these later. Not all tables will be
1840
    // parsed, but are referenced from others. For example, the #Strings
1841
    // stream is referenced from various tables in the #~ heap.
1842
    //
1843
    // #- is not documented but it represents unoptimized metadata stream. It
1844
    // may contain additional tables such as FieldPtr, ParamPtr, MethodPtr or
1845
    // PropertyPtr for indirect referencing. We already take into account these
1846
    // tables and they do not interfere with anything we parse in this module.
1847
1848
0
    if ((strncmp(stream_name, "#~", 2) == 0 ||
1849
0
         strncmp(stream_name, "#-", 2) == 0) &&
1850
0
        headers.tilde == NULL)
1851
0
      headers.tilde = stream_header;
1852
0
    else if (strncmp(stream_name, "#GUID", 5) == 0)
1853
0
      headers.guid = stream_header;
1854
0
    else if (strncmp(stream_name, "#Strings", 8) == 0 && headers.string == NULL)
1855
0
      headers.string = stream_header;
1856
0
    else if (strncmp(stream_name, "#Blob", 5) == 0 && headers.blob == NULL)
1857
0
      headers.blob = stream_header;
1858
0
    else if (strncmp(stream_name, "#US", 3) == 0 && headers.us == NULL)
1859
0
      headers.us = stream_header;
1860
1861
    // Stream name is padded to a multiple of 4.
1862
0
    stream_header = (PSTREAM_HEADER) ((uint8_t*) stream_header +
1863
0
                                      sizeof(STREAM_HEADER) +
1864
0
                                      strlen(stream_name) + 4 -
1865
0
                                      (strlen(stream_name) % 4));
1866
0
  }
1867
1868
0
  yr_set_integer(i, pe->object, "number_of_streams");
1869
1870
0
  return headers;
1871
0
}
1872
1873
// This is the second pass through the data for #~. The first pass collects
1874
// information on the number of rows for tables which have coded indexes.
1875
// This pass uses that information and the index_sizes to parse the tables
1876
// of interest.
1877
//
1878
// Because the indexes can vary in size depending upon the number of rows in
1879
// other tables it is impossible to use static sized structures. To deal with
1880
// this hardcode the sizes of each table based upon the documentation (for the
1881
// static sized portions) and use the variable sizes accordingly.
1882
1883
void dotnet_parse_tilde_2(
1884
    PE* pe,
1885
    PTILDE_HEADER tilde_header,
1886
    int64_t resource_base,
1887
    ROWS rows,
1888
    INDEX_SIZES index_sizes,
1889
    PSTREAMS streams)
1890
0
{
1891
0
  PMODULE_TABLE module_table;
1892
0
  PASSEMBLY_TABLE assembly_table;
1893
0
  PASSEMBLYREF_TABLE assemblyref_table;
1894
0
  PFIELDRVA_TABLE fieldrva_table;
1895
0
  PMANIFESTRESOURCE_TABLE manifestresource_table;
1896
0
  PMODULEREF_TABLE moduleref_table;
1897
0
  PCUSTOMATTRIBUTE_TABLE customattribute_table;
1898
0
  PCONSTANT_TABLE constant_table;
1899
0
  DWORD resource_size, implementation;
1900
1901
  // To save important data for future processing, initialize everything to 0
1902
0
  TABLES tables = {0};
1903
1904
0
  char* name;
1905
0
  char typelib[MAX_TYPELIB_SIZE + 1];
1906
0
  unsigned int i;
1907
0
  int bit_check;
1908
0
  int matched_bits = 0;
1909
1910
0
  int64_t metadata_root = streams->metadata_root;
1911
0
  int64_t resource_offset, field_offset;
1912
0
  uint32_t row_size, row_count, counter, str_heap_size;
1913
1914
0
  const uint8_t* string_offset;
1915
0
  const uint8_t* blob_offset;
1916
1917
0
  uint32_t num_rows = 0;
1918
0
  uint32_t valid_rows = 0;
1919
0
  uint32_t* row_offset = NULL;
1920
0
  uint8_t* table_offset = NULL;
1921
0
  uint8_t* row_ptr = NULL;
1922
1923
  // These are pointers and row sizes for tables of interest to us for special
1924
  // parsing. For example, we are interested in pulling out any CustomAttributes
1925
  // that are GUIDs so we need to be able to walk these tables. To find GUID
1926
  // CustomAttributes you need to walk the CustomAttribute table and look for
1927
  // any row with a Parent that indexes into the Assembly table and Type indexes
1928
  // into the MemberRef table. Then you follow the index into the MemberRef
1929
  // table and check the Class to make sure it indexes into TypeRef table. If it
1930
  // does you follow that index and make sure the Name is "GuidAttribute". If
1931
  // all that is valid then you can take the Value from the CustomAttribute
1932
  // table to find out the index into the Blob stream and parse that.
1933
  //
1934
  // Luckily we can abuse the fact that the order of the tables is guaranteed
1935
  // consistent (though some may not exist, but if they do exist they must exist
1936
  // in a certain order). The order is defined by their position in the Valid
1937
  // member of the tilde_header structure. By the time we are parsing the
1938
  // CustomAttribute table we have already recorded the location of the TypeRef
1939
  // and MemberRef tables, so we can follow the chain back up from
1940
  // CustomAttribute through MemberRef to TypeRef.
1941
1942
0
  uint8_t* typeref_ptr = NULL;
1943
0
  uint8_t* memberref_ptr = NULL;
1944
0
  uint32_t typeref_row_size = 0;
1945
0
  uint32_t memberref_row_size = 0;
1946
0
  uint8_t* typeref_row = NULL;
1947
0
  uint8_t* memberref_row = NULL;
1948
1949
0
  DWORD type_index;
1950
0
  DWORD class_index;
1951
0
  BLOB_PARSE_RESULT blob_result;
1952
0
  DWORD blob_index;
1953
0
  DWORD blob_length;
1954
1955
  // These are used to determine the size of coded indexes, which are the
1956
  // dynamically sized columns for some tables. The coded indexes are
1957
  // documented in ECMA-335 Section II.24.2.6.
1958
0
  uint8_t index_size, index_size2;
1959
1960
  // Number of rows is the number of bits set to 1 in Valid.
1961
  // Should use this technique:
1962
  // http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetKernighan
1963
  // Count number of Rows size entries in header to skip over them
1964
0
  for (i = 0; i < 64; i++)
1965
0
    valid_rows += ((yr_le64toh(tilde_header->Valid) >> i) & 0x01);
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 = *(DWORD*) blob_offset;
2199
0
        else
2200
          // Cast the value (index into blob table) to a 32bit value.
2201
0
          blob_index = (DWORD) (*(WORD*) blob_offset);
2202
2203
        // Everything checks out. Make sure the index into the blob field
2204
        // is valid (non-null and within range).
2205
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 ((*(DWORD*) 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 ((*(WORD*) 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 ((*(DWORD*) customattribute_table & 0x07) != 0x03)
2328
0
            {
2329
0
              row_ptr += row_size;
2330
0
              continue;
2331
0
            }
2332
2333
0
            type_index = *(DWORD*) 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 ((*(WORD*) 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) ((*(WORD*) 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 ((*(DWORD*) memberref_row & 0x07) != 0x01)
2365
0
            {
2366
0
              row_ptr += row_size;
2367
0
              continue;
2368
0
            }
2369
2370
0
            class_index = *(DWORD*) memberref_row >> 3;
2371
0
          }
2372
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 ((*(WORD*) 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) (*(WORD*) 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, string_offset, str_heap_size, *(DWORD*) typeref_row);
2414
0
          }
2415
0
          else
2416
0
          {
2417
0
            name = pe_get_dotnet_string(
2418
0
                pe, string_offset, str_heap_size, *(WORD*) typeref_row);
2419
0
          }
2420
2421
0
          if (name != NULL && strncmp(name, "GuidAttribute", 13) != 0)
2422
0
          {
2423
0
            row_ptr += row_size;
2424
0
            continue;
2425
0
          }
2426
2427
          // Get the Value field.
2428
0
          customattribute_table = (PCUSTOMATTRIBUTE_TABLE) (row_ptr +
2429
0
                                                            index_size +
2430
0
                                                            index_size2);
2431
2432
0
          if (index_sizes.blob == 4)
2433
0
            blob_index = *(DWORD*) customattribute_table;
2434
0
          else
2435
            // Cast the value (index into blob table) to a 32bit value.
2436
0
            blob_index = (DWORD) (*(WORD*) customattribute_table);
2437
2438
          // Everything checks out. Make sure the index into the blob field
2439
          // is valid (non-null and within range).
2440
0
          blob_offset = pe->data + metadata_root +
2441
0
                        yr_le32toh(streams->blob->Offset) + blob_index;
2442
2443
          // If index into blob is 0 or past the end of the blob stream, skip
2444
          // it. We don't know the size of the blob entry yet because that is
2445
          // encoded in the start.
2446
0
          if (blob_index == 0x00 || blob_offset >= pe->data + pe->data_size)
2447
0
          {
2448
0
            row_ptr += row_size;
2449
0
            continue;
2450
0
          }
2451
2452
0
          blob_result = dotnet_parse_blob_entry(pe, blob_offset);
2453
2454
0
          if (blob_result.size == 0)
2455
0
          {
2456
0
            row_ptr += row_size;
2457
0
            continue;
2458
0
          }
2459
2460
0
          blob_length = blob_result.length;
2461
0
          blob_offset += blob_result.size;
2462
2463
          // Quick sanity check to make sure the blob entry is within bounds
2464
          // and its length is at least 3 (2 bytes for the 16 bits prolog and
2465
          // 1 byte for the string length)
2466
0
          if (blob_length < 3 ||
2467
0
              blob_offset + blob_length >= pe->data + pe->data_size)
2468
0
          {
2469
0
            row_ptr += row_size;
2470
0
            continue;
2471
0
          }
2472
2473
          // Custom attributes MUST have a 16 bit prolog of 0x0001
2474
0
          if (*(WORD*) blob_offset != 0x0001)
2475
0
          {
2476
0
            row_ptr += row_size;
2477
0
            continue;
2478
0
          }
2479
2480
          // The next byte after the 16 bit prolog is the length of the string.
2481
0
          blob_offset += 2;
2482
0
          uint8_t str_len = *blob_offset;
2483
2484
          // Increment blob_offset so that it points to the first byte of the
2485
          // string.
2486
0
          blob_offset += 1;
2487
2488
0
          if (blob_offset + str_len > pe->data + pe->data_size)
2489
0
          {
2490
0
            row_ptr += row_size;
2491
0
            continue;
2492
0
          }
2493
2494
0
          if (*blob_offset == 0xFF || *blob_offset == 0x00)
2495
0
          {
2496
0
            typelib[0] = '\0';
2497
0
          }
2498
0
          else
2499
0
          {
2500
0
            strncpy(typelib, (char*) blob_offset, str_len);
2501
0
            typelib[str_len] = '\0';
2502
0
          }
2503
2504
0
          yr_set_string(typelib, pe->object, "typelib");
2505
2506
0
          row_ptr += row_size;
2507
0
        }
2508
0
      }
2509
2510
0
      table_offset += row_size * num_rows;
2511
0
      break;
2512
2513
0
    case BIT_FIELDMARSHAL:
2514
0
      row_count = max_rows(2, yr_le32toh(rows.field), yr_le32toh(rows.param));
2515
2516
0
      if (row_count > (0xFFFF >> 0x01))
2517
0
        index_size = 4;
2518
0
      else
2519
0
        index_size = 2;
2520
2521
0
      table_offset += (index_size + index_sizes.blob) * num_rows;
2522
0
      break;
2523
2524
0
    case BIT_DECLSECURITY:
2525
0
      row_count = max_rows(
2526
0
          3,
2527
0
          yr_le32toh(rows.typedef_),
2528
0
          yr_le32toh(rows.methoddef),
2529
0
          yr_le32toh(rows.assembly));
2530
2531
0
      if (row_count > (0xFFFF >> 0x02))
2532
0
        index_size = 4;
2533
0
      else
2534
0
        index_size = 2;
2535
2536
0
      table_offset += (2 + index_size + index_sizes.blob) * num_rows;
2537
0
      break;
2538
2539
0
    case BIT_CLASSLAYOUT:
2540
0
      table_offset += (2 + 4 + index_sizes.typedef_) * num_rows;
2541
0
      break;
2542
2543
0
    case BIT_FIELDLAYOUT:
2544
0
      table_offset += (4 + index_sizes.field) * num_rows;
2545
0
      break;
2546
2547
0
    case BIT_STANDALONESIG:
2548
0
      table_offset += (index_sizes.blob) * num_rows;
2549
0
      break;
2550
2551
0
    case BIT_EVENTMAP:
2552
0
      table_offset += (index_sizes.typedef_ + index_sizes.event) * num_rows;
2553
0
      break;
2554
2555
0
    case BIT_EVENTPTR:
2556
      // This one is not documented in ECMA-335.
2557
0
      table_offset += (index_sizes.event) * num_rows;
2558
0
      break;
2559
2560
0
    case BIT_EVENT:
2561
0
      row_count = max_rows(
2562
0
          3,
2563
0
          yr_le32toh(rows.typedef_),
2564
0
          yr_le32toh(rows.typeref),
2565
0
          yr_le32toh(rows.typespec));
2566
2567
0
      if (row_count > (0xFFFF >> 0x02))
2568
0
        index_size = 4;
2569
0
      else
2570
0
        index_size = 2;
2571
2572
0
      table_offset += (2 + index_sizes.string + index_size) * num_rows;
2573
0
      break;
2574
2575
0
    case BIT_PROPERTYMAP:
2576
0
      table_offset += (index_sizes.typedef_ + index_sizes.property) * num_rows;
2577
0
      break;
2578
2579
0
    case BIT_PROPERTYPTR:
2580
      // This one is not documented in ECMA-335.
2581
0
      table_offset += (index_sizes.property) * num_rows;
2582
0
      break;
2583
2584
0
    case BIT_PROPERTY:
2585
0
      table_offset += (2 + index_sizes.string + index_sizes.blob) * num_rows;
2586
0
      break;
2587
2588
0
    case BIT_METHODSEMANTICS:
2589
0
      row_count = max_rows(
2590
0
          2, yr_le32toh(rows.event), yr_le32toh(rows.property));
2591
2592
0
      if (row_count > (0xFFFF >> 0x01))
2593
0
        index_size = 4;
2594
0
      else
2595
0
        index_size = 2;
2596
2597
0
      table_offset += (2 + index_sizes.methoddef + index_size) * num_rows;
2598
0
      break;
2599
2600
0
    case BIT_METHODIMPL:
2601
0
      row_count = max_rows(
2602
0
          2, yr_le32toh(rows.methoddef), yr_le32toh(rows.memberref));
2603
2604
0
      if (row_count > (0xFFFF >> 0x01))
2605
0
        index_size = 4;
2606
0
      else
2607
0
        index_size = 2;
2608
2609
0
      table_offset += (index_sizes.typedef_ + (index_size * 2)) * num_rows;
2610
0
      break;
2611
2612
0
    case BIT_MODULEREF:
2613
0
      row_ptr = table_offset;
2614
2615
      // Can't use 'i' here because we only set the string if it is not
2616
      // NULL. Instead use 'counter'.
2617
0
      counter = 0;
2618
2619
0
      for (i = 0; i < num_rows; i++)
2620
0
      {
2621
0
        moduleref_table = (PMODULEREF_TABLE) row_ptr;
2622
2623
0
        if (!struct_fits_in_pe(pe, moduleref_table, MODULEREF_TABLE))
2624
0
          break;
2625
2626
0
        name = pe_get_dotnet_string(
2627
0
            pe,
2628
0
            string_offset,
2629
0
            str_heap_size,
2630
0
            DOTNET_STRING_INDEX(moduleref_table->Name));
2631
2632
0
        if (name != NULL)
2633
0
        {
2634
0
          yr_set_string(name, pe->object, "modulerefs[%i]", counter);
2635
0
          counter++;
2636
0
        }
2637
2638
0
        row_ptr += index_sizes.string;
2639
0
      }
2640
2641
0
      yr_set_integer(counter, pe->object, "number_of_modulerefs");
2642
2643
0
      row_size = index_sizes.string;
2644
2645
0
      tables.moduleref.Offset = table_offset;
2646
0
      tables.moduleref.RowCount = num_rows;
2647
0
      tables.moduleref.RowSize = row_size;
2648
2649
0
      table_offset += row_size * num_rows;
2650
0
      break;
2651
2652
0
    case BIT_TYPESPEC:
2653
0
      row_size = index_sizes.blob;
2654
2655
0
      tables.typespec.Offset = table_offset;
2656
0
      tables.typespec.RowCount = num_rows;
2657
0
      tables.typespec.RowSize = row_size;
2658
2659
0
      table_offset += row_size * num_rows;
2660
0
      break;
2661
2662
0
    case BIT_IMPLMAP:
2663
0
      row_count = max_rows(
2664
0
          2, yr_le32toh(rows.field), yr_le32toh(rows.methoddef));
2665
2666
0
      if (row_count > (0xFFFF >> 0x01))
2667
0
        index_size = 4;
2668
0
      else
2669
0
        index_size = 2;
2670
2671
0
      table_offset += (2 + index_size + index_sizes.string +
2672
0
                       index_sizes.moduleref) *
2673
0
                      num_rows;
2674
0
      break;
2675
2676
0
    case BIT_FIELDRVA:
2677
0
      row_size = 4 + index_sizes.field;
2678
0
      row_ptr = table_offset;
2679
2680
      // Can't use 'i' here because we only set the field offset if it is
2681
      // valid. Instead use 'counter'.
2682
0
      counter = 0;
2683
2684
0
      for (i = 0; i < num_rows; i++)
2685
0
      {
2686
0
        fieldrva_table = (PFIELDRVA_TABLE) row_ptr;
2687
2688
0
        if (!struct_fits_in_pe(pe, fieldrva_table, FIELDRVA_TABLE))
2689
0
          break;
2690
2691
0
        field_offset = pe_rva_to_offset(pe, fieldrva_table->RVA);
2692
2693
0
        if (field_offset >= 0)
2694
0
        {
2695
0
          yr_set_integer(
2696
0
              field_offset, pe->object, "field_offsets[%i]", counter);
2697
0
          counter++;
2698
0
        }
2699
2700
0
        row_ptr += row_size;
2701
0
      }
2702
2703
0
      yr_set_integer(counter, pe->object, "number_of_field_offsets");
2704
2705
0
      table_offset += row_size * num_rows;
2706
0
      break;
2707
2708
0
    case BIT_ENCLOG:
2709
0
      table_offset += (4 + 4) * num_rows;
2710
0
      break;
2711
2712
0
    case BIT_ENCMAP:
2713
0
      table_offset += (4) * num_rows;
2714
0
      break;
2715
2716
0
    case BIT_ASSEMBLY:
2717
0
      row_size =
2718
0
          (4 + 2 + 2 + 2 + 2 + 4 + index_sizes.blob + (index_sizes.string * 2));
2719
2720
0
      if (!fits_in_pe(pe, table_offset, row_size))
2721
0
        break;
2722
2723
0
      row_ptr = table_offset;
2724
0
      assembly_table = (PASSEMBLY_TABLE) table_offset;
2725
2726
0
      yr_set_integer(
2727
0
          yr_le16toh(assembly_table->MajorVersion),
2728
0
          pe->object,
2729
0
          "assembly.version.major");
2730
0
      yr_set_integer(
2731
0
          yr_le16toh(assembly_table->MinorVersion),
2732
0
          pe->object,
2733
0
          "assembly.version.minor");
2734
0
      yr_set_integer(
2735
0
          yr_le16toh(assembly_table->BuildNumber),
2736
0
          pe->object,
2737
0
          "assembly.version.build_number");
2738
0
      yr_set_integer(
2739
0
          yr_le16toh(assembly_table->RevisionNumber),
2740
0
          pe->object,
2741
0
          "assembly.version.revision_number");
2742
2743
      // Can't use assembly_table here because the PublicKey comes before
2744
      // Name and is a variable length field.
2745
2746
0
      if (index_sizes.string == 4)
2747
0
        name = pe_get_dotnet_string(
2748
0
            pe,
2749
0
            string_offset,
2750
0
            str_heap_size,
2751
0
            yr_le32toh(*(DWORD*) (row_ptr + 4 + 2 + 2 + 2 + 2 + 4 +
2752
0
                                  index_sizes.blob)));
2753
0
      else
2754
0
        name = pe_get_dotnet_string(
2755
0
            pe,
2756
0
            string_offset,
2757
0
            str_heap_size,
2758
0
            yr_le16toh(
2759
0
                *(WORD*) (row_ptr + 4 + 2 + 2 + 2 + 2 + 4 + index_sizes.blob)));
2760
2761
0
      if (name != NULL)
2762
0
        yr_set_string(name, pe->object, "assembly.name");
2763
2764
      // Culture comes after Name.
2765
0
      if (index_sizes.string == 4)
2766
0
      {
2767
0
        name = pe_get_dotnet_string(
2768
0
            pe,
2769
0
            string_offset,
2770
0
            str_heap_size,
2771
0
            yr_le32toh(*(DWORD*) (row_ptr + 4 + 2 + 2 + 2 + 2 + 4 +
2772
0
                                  index_sizes.blob + index_sizes.string)));
2773
0
      }
2774
0
      else
2775
0
      {
2776
0
        name = pe_get_dotnet_string(
2777
0
            pe,
2778
0
            string_offset,
2779
0
            str_heap_size,
2780
0
            yr_le16toh(*(WORD*) (row_ptr + 4 + 2 + 2 + 2 + 2 + 4 +
2781
0
                                 index_sizes.blob + index_sizes.string)));
2782
0
      }
2783
2784
      // Sometimes it will be a zero length string. This is technically
2785
      // against the specification but happens from time to time.
2786
0
      if (name != NULL && strlen(name) > 0)
2787
0
        yr_set_string(name, pe->object, "assembly.culture");
2788
2789
0
      table_offset += row_size * num_rows;
2790
0
      break;
2791
2792
0
    case BIT_ASSEMBLYPROCESSOR:
2793
0
      table_offset += (4) * num_rows;
2794
0
      break;
2795
2796
0
    case BIT_ASSEMBLYOS:
2797
0
      table_offset += (4 + 4 + 4) * num_rows;
2798
0
      break;
2799
2800
0
    case BIT_ASSEMBLYREF:
2801
0
      row_size =
2802
0
          (2 + 2 + 2 + 2 + 4 + (index_sizes.blob * 2) +
2803
0
           (index_sizes.string * 2));
2804
2805
0
      row_ptr = table_offset;
2806
2807
0
      for (i = 0; i < num_rows; i++)
2808
0
      {
2809
0
        if (!fits_in_pe(pe, row_ptr, row_size))
2810
0
          break;
2811
2812
0
        assemblyref_table = (PASSEMBLYREF_TABLE) row_ptr;
2813
2814
0
        yr_set_integer(
2815
0
            yr_le16toh(assemblyref_table->MajorVersion),
2816
0
            pe->object,
2817
0
            "assembly_refs[%i].version.major",
2818
0
            i);
2819
0
        yr_set_integer(
2820
0
            yr_le16toh(assemblyref_table->MinorVersion),
2821
0
            pe->object,
2822
0
            "assembly_refs[%i].version.minor",
2823
0
            i);
2824
0
        yr_set_integer(
2825
0
            yr_le16toh(assemblyref_table->BuildNumber),
2826
0
            pe->object,
2827
0
            "assembly_refs[%i].version.build_number",
2828
0
            i);
2829
0
        yr_set_integer(
2830
0
            yr_le16toh(assemblyref_table->RevisionNumber),
2831
0
            pe->object,
2832
0
            "assembly_refs[%i].version.revision_number",
2833
0
            i);
2834
2835
0
        blob_offset = pe->data + metadata_root +
2836
0
                      yr_le32toh(streams->blob->Offset);
2837
2838
0
        if (index_sizes.blob == 4)
2839
0
          blob_offset += yr_le32toh(
2840
0
              assemblyref_table->PublicKeyOrToken.PublicKeyOrToken_Long);
2841
0
        else
2842
0
          blob_offset += yr_le16toh(
2843
0
              assemblyref_table->PublicKeyOrToken.PublicKeyOrToken_Short);
2844
2845
0
        blob_result = dotnet_parse_blob_entry(pe, blob_offset);
2846
0
        blob_offset += blob_result.size;
2847
2848
0
        if (blob_result.size == 0 ||
2849
0
            !fits_in_pe(pe, blob_offset, blob_result.length))
2850
0
        {
2851
0
          row_ptr += row_size;
2852
0
          continue;
2853
0
        }
2854
2855
        // Avoid empty strings.
2856
0
        if (blob_result.length > 0)
2857
0
        {
2858
0
          yr_set_sized_string(
2859
0
              (char*) blob_offset,
2860
0
              blob_result.length,
2861
0
              pe->object,
2862
0
              "assembly_refs[%i].public_key_or_token",
2863
0
              i);
2864
0
        }
2865
2866
        // Can't use assemblyref_table here because the PublicKey comes before
2867
        // Name and is a variable length field.
2868
2869
0
        if (index_sizes.string == 4)
2870
0
          name = pe_get_dotnet_string(
2871
0
              pe,
2872
0
              string_offset,
2873
0
              str_heap_size,
2874
0
              yr_le32toh(
2875
0
                  *(DWORD*) (row_ptr + 2 + 2 + 2 + 2 + 4 + index_sizes.blob)));
2876
0
        else
2877
0
          name = pe_get_dotnet_string(
2878
0
              pe,
2879
0
              string_offset,
2880
0
              str_heap_size,
2881
0
              yr_le16toh(
2882
0
                  *(WORD*) (row_ptr + 2 + 2 + 2 + 2 + 4 + index_sizes.blob)));
2883
2884
0
        if (name != NULL)
2885
0
          yr_set_string(name, pe->object, "assembly_refs[%i].name", i);
2886
2887
0
        row_ptr += row_size;
2888
0
      }
2889
2890
0
      tables.assemblyref.Offset = table_offset;
2891
0
      tables.assemblyref.RowCount = num_rows;
2892
0
      tables.assemblyref.RowSize = row_size;
2893
2894
0
      yr_set_integer(i, pe->object, "number_of_assembly_refs");
2895
0
      table_offset += row_size * num_rows;
2896
0
      break;
2897
2898
0
    case BIT_ASSEMBLYREFPROCESSOR:
2899
0
      table_offset += (4 + index_sizes.assemblyrefprocessor) * num_rows;
2900
0
      break;
2901
2902
0
    case BIT_ASSEMBLYREFOS:
2903
0
      table_offset += (4 + 4 + 4 + index_sizes.assemblyref) * num_rows;
2904
0
      break;
2905
2906
0
    case BIT_FILE:
2907
0
      table_offset += (4 + index_sizes.string + index_sizes.blob) * num_rows;
2908
0
      break;
2909
2910
0
    case BIT_EXPORTEDTYPE:
2911
0
      row_count = max_rows(
2912
0
          3,
2913
0
          yr_le32toh(rows.file),
2914
0
          yr_le32toh(rows.assemblyref),
2915
0
          yr_le32toh(rows.exportedtype));
2916
2917
0
      if (row_count > (0xFFFF >> 0x02))
2918
0
        index_size = 4;
2919
0
      else
2920
0
        index_size = 2;
2921
2922
0
      table_offset += (4 + 4 + (index_sizes.string * 2) + index_size) *
2923
0
                      num_rows;
2924
0
      break;
2925
2926
0
    case BIT_MANIFESTRESOURCE:
2927
      // This is an Implementation coded index with no 3rd bit specified.
2928
0
      row_count = max_rows(
2929
0
          2, yr_le32toh(rows.file), yr_le32toh(rows.assemblyref));
2930
2931
0
      if (row_count > (0xFFFF >> 0x02))
2932
0
        index_size = 4;
2933
0
      else
2934
0
        index_size = 2;
2935
2936
0
      row_size = (4 + 4 + index_sizes.string + index_size);
2937
0
      row_ptr = table_offset;
2938
2939
      // First DWORD is the offset.
2940
0
      for (i = 0; i < num_rows; i++)
2941
0
      {
2942
0
        if (!fits_in_pe(pe, row_ptr, row_size))
2943
0
          break;
2944
2945
0
        manifestresource_table = (PMANIFESTRESOURCE_TABLE) row_ptr;
2946
2947
0
        if (index_size == 4)
2948
0
          implementation = yr_le32toh(
2949
0
              *(DWORD*) (row_ptr + 4 + 4 + index_sizes.string));
2950
0
        else
2951
0
          implementation = yr_le16toh(
2952
0
              *(WORD*) (row_ptr + 4 + 4 + index_sizes.string));
2953
2954
0
        row_ptr += row_size;
2955
2956
0
        name = pe_get_dotnet_string(
2957
0
            pe,
2958
0
            string_offset,
2959
0
            str_heap_size,
2960
0
            DOTNET_STRING_INDEX(manifestresource_table->Name));
2961
2962
0
        if (name != NULL)
2963
0
          yr_set_string(name, pe->object, "resources[%i].name", i);
2964
2965
        // Only set offset and length if it is in this file, otherwise continue
2966
        // with the next resource.
2967
0
        if (implementation != 0)
2968
0
          continue;
2969
2970
0
        resource_offset = yr_le32toh(manifestresource_table->Offset);
2971
2972
0
        if (!fits_in_pe(
2973
0
                pe, pe->data + resource_base + resource_offset, sizeof(DWORD)))
2974
0
          continue;
2975
2976
0
        resource_size = yr_le32toh(
2977
0
            *(DWORD*) (pe->data + resource_base + resource_offset));
2978
2979
        // Add 4 to skip the size.
2980
0
        yr_set_integer(
2981
0
            resource_base + resource_offset + 4,
2982
0
            pe->object,
2983
0
            "resources[%i].offset",
2984
0
            i);
2985
2986
0
        yr_set_integer(resource_size, pe->object, "resources[%i].length", i);
2987
0
      }
2988
2989
0
      yr_set_integer(i, pe->object, "number_of_resources");
2990
2991
0
      table_offset += row_size * num_rows;
2992
0
      break;
2993
2994
0
    case BIT_NESTEDCLASS:
2995
0
      row_size = index_sizes.typedef_ * 2;
2996
2997
0
      tables.nestedclass.Offset = table_offset;
2998
0
      tables.nestedclass.RowCount = num_rows;
2999
0
      tables.nestedclass.RowSize = row_size;
3000
3001
0
      table_offset += row_size * num_rows;
3002
0
      break;
3003
3004
0
    case BIT_GENERICPARAM:
3005
0
      row_count = max_rows(
3006
0
          2, yr_le32toh(rows.typedef_), yr_le32toh(rows.methoddef));
3007
3008
0
      if (row_count > (0xFFFF >> 0x01))
3009
0
        index_size = 4;
3010
0
      else
3011
0
        index_size = 2;
3012
3013
0
      row_size = (2 + 2 + index_size + index_sizes.string);
3014
3015
0
      tables.genericparam.Offset = table_offset;
3016
0
      tables.genericparam.RowCount = num_rows;
3017
0
      tables.genericparam.RowSize = row_size;
3018
3019
0
      table_offset += row_size * num_rows;
3020
0
      break;
3021
3022
0
    case BIT_METHODSPEC:
3023
0
      row_count = max_rows(
3024
0
          2, yr_le32toh(rows.methoddef), yr_le32toh(rows.memberref));
3025
3026
0
      if (row_count > (0xFFFF >> 0x01))
3027
0
        index_size = 4;
3028
0
      else
3029
0
        index_size = 2;
3030
3031
0
      table_offset += (index_size + index_sizes.blob) * num_rows;
3032
0
      break;
3033
3034
0
    case BIT_GENERICPARAMCONSTRAINT:
3035
0
      row_count = max_rows(
3036
0
          3,
3037
0
          yr_le32toh(rows.typedef_),
3038
0
          yr_le32toh(rows.typeref),
3039
0
          yr_le32toh(rows.typespec));
3040
3041
0
      if (row_count > (0xFFFF >> 0x02))
3042
0
        index_size = 4;
3043
0
      else
3044
0
        index_size = 2;
3045
3046
0
      table_offset += (index_sizes.genericparam + index_size) * num_rows;
3047
0
      break;
3048
3049
0
    default:
3050
      // printf("Unknown bit: %i\n", bit_check);
3051
0
      return;
3052
0
    }
3053
3054
0
    matched_bits++;
3055
0
  }
3056
3057
0
  CLASS_CONTEXT class_context = {
3058
0
      .pe = pe,
3059
0
      .tables = &tables,
3060
0
      .index_sizes = &index_sizes,
3061
0
      .str_heap = string_offset,
3062
0
      .str_size = str_heap_size,
3063
0
      .blob_heap = pe->data + streams->metadata_root +
3064
0
                   yr_le32toh(streams->blob->Offset),
3065
0
      .blob_size = yr_le32toh(streams->blob->Size)};
3066
3067
0
  parse_user_types(&class_context);
3068
0
}
3069
3070
// Parsing the #~ stream is done in two parts. The first part (this function)
3071
// parses enough of the Stream to provide context for the second pass. In
3072
// particular it is collecting the number of rows for each of the tables. The
3073
// second part parses the actual tables of interest.
3074
3075
void dotnet_parse_tilde(PE* pe, PCLI_HEADER cli_header, PSTREAMS streams)
3076
0
{
3077
0
  PTILDE_HEADER tilde_header;
3078
0
  int64_t resource_base;
3079
0
  int64_t metadata_root = streams->metadata_root;
3080
0
  uint32_t* row_offset = NULL;
3081
3082
0
  int bit_check;
3083
3084
  // This is used as an offset into the rows and tables. For every bit set in
3085
  // Valid this will be incremented. This is because the bit position doesn't
3086
  // matter, just the number of bits that are set, when determining how many
3087
  // rows and what the table structure is.
3088
0
  int matched_bits = 0;
3089
3090
  // We need to know the number of rows for some tables, because they are
3091
  // indexed into. The index will be either 2 or 4 bytes, depending upon the
3092
  // number of rows being indexed into.
3093
0
  ROWS rows;
3094
0
  INDEX_SIZES index_sizes;
3095
0
  uint32_t heap_sizes;
3096
3097
  // Default all rows to 0. They will be set to actual values later on, if
3098
  // they exist in the file.
3099
0
  memset(&rows, '\0', sizeof(ROWS));
3100
3101
  // Default index sizes are 2. Will be bumped to 4 if necessary.
3102
0
  memset(&index_sizes, 2, sizeof(index_sizes));
3103
3104
0
  tilde_header = (PTILDE_HEADER) (pe->data + metadata_root +
3105
0
                                  yr_le32toh(streams->tilde->Offset));
3106
3107
0
  if (!struct_fits_in_pe(pe, tilde_header, TILDE_HEADER))
3108
0
    return;
3109
3110
0
  heap_sizes = yr_le32toh(tilde_header->HeapSizes);
3111
3112
  // Set index sizes for various heaps.
3113
0
  if (heap_sizes & 0x01)
3114
0
    index_sizes.string = 4;
3115
3116
0
  if (heap_sizes & 0x02)
3117
0
    index_sizes.guid = 4;
3118
3119
0
  if (heap_sizes & 0x04)
3120
0
    index_sizes.blob = 4;
3121
3122
  // Immediately after the tilde header is an array of 32bit values which
3123
  // indicate how many rows are in each table. The tables are immediately
3124
  // after the rows array.
3125
  //
3126
  // Save the row offset.
3127
0
  row_offset = (uint32_t*) (tilde_header + 1);
3128
3129
  // Walk all the bits first because we need to know the number of rows for
3130
  // some tables in order to parse others. In particular this applies to
3131
  // coded indexes, which are documented in ECMA-335 II.24.2.6.
3132
0
  for (bit_check = 0; bit_check < 64; bit_check++)
3133
0
  {
3134
0
    if (!((yr_le64toh(tilde_header->Valid) >> bit_check) & 0x01))
3135
0
      continue;
3136
3137
0
#define ROW_CHECK(name)                                                  \
3138
0
  if (fits_in_pe(pe, row_offset, (matched_bits + 1) * sizeof(uint32_t))) \
3139
0
    rows.name = *(row_offset + matched_bits);
3140
3141
0
#define ROW_CHECK_WITH_INDEX(name)    \
3142
0
  ROW_CHECK(name);                    \
3143
0
  if (yr_le32toh(rows.name) > 0xFFFF) \
3144
0
    index_sizes.name = 4;
3145
3146
0
    switch (bit_check)
3147
0
    {
3148
0
    case BIT_MODULE:
3149
0
      ROW_CHECK_WITH_INDEX(module);
3150
0
      break;
3151
0
    case BIT_MODULEREF:
3152
0
      ROW_CHECK_WITH_INDEX(moduleref);
3153
0
      break;
3154
0
    case BIT_ASSEMBLYREF:
3155
0
      ROW_CHECK_WITH_INDEX(assemblyref);
3156
0
      break;
3157
0
    case BIT_ASSEMBLYREFPROCESSOR:
3158
0
      ROW_CHECK_WITH_INDEX(assemblyrefprocessor);
3159
0
      break;
3160
0
    case BIT_TYPEREF:
3161
0
      ROW_CHECK_WITH_INDEX(typeref);
3162
0
      break;
3163
0
    case BIT_METHODDEF:
3164
0
      ROW_CHECK_WITH_INDEX(methoddef);
3165
0
      break;
3166
0
    case BIT_MEMBERREF:
3167
0
      ROW_CHECK_WITH_INDEX(memberref);
3168
0
      break;
3169
0
    case BIT_TYPEDEF:
3170
0
      ROW_CHECK_WITH_INDEX(typedef_);
3171
0
      break;
3172
0
    case BIT_TYPESPEC:
3173
0
      ROW_CHECK_WITH_INDEX(typespec);
3174
0
      break;
3175
0
    case BIT_FIELD:
3176
0
      ROW_CHECK_WITH_INDEX(field);
3177
0
      break;
3178
0
    case BIT_PARAM:
3179
0
      ROW_CHECK_WITH_INDEX(param);
3180
0
      break;
3181
0
    case BIT_PROPERTY:
3182
0
      ROW_CHECK_WITH_INDEX(property);
3183
0
      break;
3184
0
    case BIT_INTERFACEIMPL:
3185
0
      ROW_CHECK_WITH_INDEX(interfaceimpl);
3186
0
      break;
3187
0
    case BIT_EVENT:
3188
0
      ROW_CHECK_WITH_INDEX(event);
3189
0
      break;
3190
0
    case BIT_STANDALONESIG:
3191
0
      ROW_CHECK(standalonesig);
3192
0
      break;
3193
0
    case BIT_ASSEMBLY:
3194
0
      ROW_CHECK_WITH_INDEX(assembly);
3195
0
      break;
3196
0
    case BIT_FILE:
3197
0
      ROW_CHECK(file);
3198
0
      break;
3199
0
    case BIT_EXPORTEDTYPE:
3200
0
      ROW_CHECK(exportedtype);
3201
0
      break;
3202
0
    case BIT_MANIFESTRESOURCE:
3203
0
      ROW_CHECK(manifestresource);
3204
0
      break;
3205
0
    case BIT_GENERICPARAM:
3206
0
      ROW_CHECK_WITH_INDEX(genericparam);
3207
0
      break;
3208
0
    case BIT_GENERICPARAMCONSTRAINT:
3209
0
      ROW_CHECK(genericparamconstraint);
3210
0
      break;
3211
0
    case BIT_METHODSPEC:
3212
0
      ROW_CHECK_WITH_INDEX(methodspec);
3213
0
      break;
3214
0
    default:
3215
0
      break;
3216
0
    }
3217
3218
0
    matched_bits++;
3219
0
  }
3220
3221
  // This is used when parsing the MANIFEST RESOURCE table.
3222
0
  resource_base = pe_rva_to_offset(
3223
0
      pe, yr_le32toh(cli_header->Resources.VirtualAddress));
3224
3225
0
  dotnet_parse_tilde_2(
3226
0
      pe, tilde_header, resource_base, rows, index_sizes, streams);
3227
0
}
3228
3229
static bool dotnet_is_dotnet(PE* pe)
3230
0
{
3231
0
  PIMAGE_DATA_DIRECTORY directory = pe_get_directory_entry(
3232
0
      pe, IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR);
3233
3234
0
  if (!directory)
3235
0
    return false;
3236
3237
0
  int64_t offset = pe_rva_to_offset(pe, yr_le32toh(directory->VirtualAddress));
3238
3239
0
  if (offset < 0 || !struct_fits_in_pe(pe, pe->data + offset, CLI_HEADER))
3240
0
    return false;
3241
3242
0
  CLI_HEADER* cli_header = (CLI_HEADER*) (pe->data + offset);
3243
3244
0
  if (yr_le32toh(cli_header->Size) != sizeof(CLI_HEADER))
3245
0
    return false;
3246
3247
0
  int64_t metadata_root = pe_rva_to_offset(
3248
0
      pe, yr_le32toh(cli_header->MetaData.VirtualAddress));
3249
0
  offset = metadata_root;
3250
3251
0
  if (!struct_fits_in_pe(pe, pe->data + metadata_root, NET_METADATA))
3252
0
    return false;
3253
3254
0
  NET_METADATA* metadata = (NET_METADATA*) (pe->data + metadata_root);
3255
3256
0
  if (yr_le32toh(metadata->Magic) != NET_METADATA_MAGIC)
3257
0
    return false;
3258
3259
  // Version length must be between 1 and 255, and be a multiple of 4.
3260
  // Also make sure it fits in pe.
3261
0
  uint32_t md_len = yr_le32toh(metadata->Length);
3262
0
  if (md_len == 0 || md_len > 255 || md_len % 4 != 0 ||
3263
0
      !fits_in_pe(pe, pe->data + offset + sizeof(NET_METADATA), md_len))
3264
0
  {
3265
0
    return false;
3266
0
  }
3267
3268
0
  if (IS_64BITS_PE(pe))
3269
0
  {
3270
0
    if (yr_le32toh(OptionalHeader(pe, NumberOfRvaAndSizes)) <
3271
0
        IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR)
3272
0
      return false;
3273
0
  }
3274
3275
0
  return true;
3276
0
}
3277
3278
void dotnet_parse_com(PE* pe)
3279
0
{
3280
0
  PIMAGE_DATA_DIRECTORY directory;
3281
0
  PCLI_HEADER cli_header;
3282
0
  PNET_METADATA metadata;
3283
0
  int64_t metadata_root, offset;
3284
0
  char* end;
3285
0
  STREAMS headers;
3286
0
  WORD num_streams;
3287
0
  uint32_t md_len;
3288
3289
0
  if (!dotnet_is_dotnet(pe))
3290
0
  {
3291
0
    yr_set_integer(0, pe->object, "is_dotnet");
3292
0
    return;
3293
0
  }
3294
3295
0
  yr_set_integer(1, pe->object, "is_dotnet");
3296
3297
0
  directory = pe_get_directory_entry(pe, IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR);
3298
0
  if (directory == NULL)
3299
0
    return;
3300
3301
0
  offset = pe_rva_to_offset(pe, yr_le32toh(directory->VirtualAddress));
3302
3303
0
  if (offset < 0 || !struct_fits_in_pe(pe, pe->data + offset, CLI_HEADER))
3304
0
    return;
3305
3306
0
  cli_header = (PCLI_HEADER) (pe->data + offset);
3307
3308
0
  offset = metadata_root = pe_rva_to_offset(
3309
0
      pe, yr_le32toh(cli_header->MetaData.VirtualAddress));
3310
3311
0
  if (!struct_fits_in_pe(pe, pe->data + offset, NET_METADATA))
3312
0
    return;
3313
3314
0
  metadata = (PNET_METADATA) (pe->data + offset);
3315
3316
  // Version length must be between 1 and 255, and be a multiple of 4.
3317
  // Also make sure it fits in pe.
3318
0
  md_len = yr_le32toh(metadata->Length);
3319
3320
0
  if (md_len == 0 || md_len > 255 || md_len % 4 != 0 ||
3321
0
      !fits_in_pe(pe, pe->data + offset + sizeof(NET_METADATA), md_len))
3322
0
  {
3323
0
    return;
3324
0
  }
3325
3326
  // The length includes the NULL terminator and is rounded up to a multiple of
3327
  // 4. We need to exclude the terminator and the padding, so search for the
3328
  // first NULL byte.
3329
0
  end = (char*) memmem((void*) metadata->Version, md_len, "\0", 1);
3330
3331
0
  if (end != NULL)
3332
0
    yr_set_sized_string(
3333
0
        metadata->Version, (end - metadata->Version), pe->object, "version");
3334
3335
  // The metadata structure has some variable length records after the version.
3336
  // We must manually parse things from here on out.
3337
  //
3338
  // Flags are 2 bytes (always 0).
3339
0
  offset += sizeof(NET_METADATA) + md_len + 2;
3340
3341
  // 2 bytes for Streams.
3342
0
  if (!fits_in_pe(pe, pe->data + offset, 2))
3343
0
    return;
3344
3345
0
  num_streams = (WORD) * (pe->data + offset);
3346
0
  offset += 2;
3347
3348
0
  headers = dotnet_parse_stream_headers(pe, offset, metadata_root, num_streams);
3349
3350
0
  if (headers.guid != NULL)
3351
0
    dotnet_parse_guid(pe, metadata_root, headers.guid);
3352
3353
  // Parse the #~ stream, which includes various tables of interest.
3354
  // These tables reference the blob and string streams, so we need to ensure
3355
  // those are not NULL also.
3356
0
  if (headers.tilde != NULL && headers.string != NULL && headers.blob != NULL)
3357
0
    dotnet_parse_tilde(pe, cli_header, &headers);
3358
3359
0
  if (headers.us != NULL)
3360
0
    dotnet_parse_us(pe, metadata_root, headers.us);
3361
0
}
3362
3363
0
begin_declarations
3364
0
  declare_integer("is_dotnet");
3365
0
  declare_string("version");
3366
0
  declare_string("module_name");
3367
3368
0
  begin_struct_array("streams")
3369
0
    declare_string("name");
3370
0
    declare_integer("offset");
3371
0
    declare_integer("size");
3372
0
  end_struct_array("streams")
3373
3374
0
  declare_integer("number_of_streams");
3375
3376
0
  declare_string_array("guids");
3377
0
  declare_integer("number_of_guids");
3378
3379
0
  begin_struct_array("resources")
3380
0
    declare_integer("offset");
3381
0
    declare_integer("length");
3382
0
    declare_string("name");
3383
0
  end_struct_array("resources")
3384
3385
0
  declare_integer("number_of_resources");
3386
3387
0
  begin_struct_array("classes")
3388
0
    declare_string("fullname");
3389
0
    declare_string("name");
3390
0
    declare_string("namespace");
3391
0
    declare_string("visibility");
3392
0
    declare_string("type");
3393
0
    declare_integer("abstract");
3394
0
    declare_integer("sealed");
3395
3396
0
    declare_integer("number_of_generic_parameters");
3397
0
    declare_string_array("generic_parameters");
3398
3399
0
    declare_integer("number_of_base_types");
3400
0
    declare_string_array("base_types");
3401
3402
0
    declare_integer("number_of_methods");
3403
0
    begin_struct_array("methods")
3404
0
      declare_string_array("generic_parameters");
3405
3406
0
      declare_integer("number_of_generic_parameters");
3407
3408
0
      begin_struct_array("parameters")
3409
0
        declare_string("name");
3410
0
        declare_string("type");
3411
0
      end_struct_array("parameters")
3412
3413
0
      declare_integer("number_of_parameters");
3414
3415
0
      declare_string("return_type");
3416
0
      declare_integer("abstract");
3417
0
      declare_integer("final");
3418
0
      declare_integer("virtual");
3419
0
      declare_integer("static");
3420
0
      declare_string("visibility");
3421
0
      declare_string("name");
3422
0
    end_struct_array("methods")
3423
3424
0
  end_struct_array("classes")
3425
3426
0
  declare_integer("number_of_classes");
3427
3428
0
  begin_struct_array("assembly_refs")
3429
0
    begin_struct("version")
3430
0
      declare_integer("major");
3431
0
      declare_integer("minor");
3432
0
      declare_integer("build_number");
3433
0
      declare_integer("revision_number");
3434
0
    end_struct("version")
3435
0
    declare_string("public_key_or_token");
3436
0
    declare_string("name");
3437
0
  end_struct_array("assembly_refs")
3438
3439
0
  declare_integer("number_of_assembly_refs");
3440
3441
0
  begin_struct("assembly")
3442
0
    begin_struct("version")
3443
0
      declare_integer("major");
3444
0
      declare_integer("minor");
3445
0
      declare_integer("build_number");
3446
0
      declare_integer("revision_number");
3447
0
    end_struct("version")
3448
0
    declare_string("name");
3449
0
    declare_string("culture");
3450
0
  end_struct("assembly")
3451
3452
0
  declare_string_array("modulerefs");
3453
0
  declare_integer("number_of_modulerefs");
3454
0
  declare_string_array("user_strings");
3455
0
  declare_integer("number_of_user_strings");
3456
0
  declare_string("typelib");
3457
0
  declare_string_array("constants");
3458
0
  declare_integer("number_of_constants");
3459
3460
0
  declare_integer_array("field_offsets");
3461
0
  declare_integer("number_of_field_offsets");
3462
0
end_declarations
3463
3464
int module_initialize(YR_MODULE* module)
3465
2
{
3466
2
  return ERROR_SUCCESS;
3467
2
}
3468
3469
int module_finalize(YR_MODULE* module)
3470
0
{
3471
0
  return ERROR_SUCCESS;
3472
0
}
3473
3474
int module_load(
3475
    YR_SCAN_CONTEXT* context,
3476
    YR_OBJECT* module_object,
3477
    void* module_data,
3478
    size_t module_data_size)
3479
0
{
3480
0
  YR_MEMORY_BLOCK* block;
3481
0
  YR_MEMORY_BLOCK_ITERATOR* iterator = context->iterator;
3482
0
  const uint8_t* block_data = NULL;
3483
3484
0
  foreach_memory_block(iterator, block)
3485
0
  {
3486
0
    PIMAGE_NT_HEADERS32 pe_header;
3487
3488
0
    block_data = yr_fetch_block_data(block);
3489
3490
0
    if (block_data == NULL)
3491
0
      continue;
3492
3493
0
    pe_header = pe_get_header(block_data, block->size);
3494
3495
0
    if (pe_header != NULL)
3496
0
    {
3497
      // Ignore DLLs while scanning a process
3498
3499
0
      if (!(context->flags & SCAN_FLAGS_PROCESS_MEMORY) ||
3500
0
          !(pe_header->FileHeader.Characteristics & IMAGE_FILE_DLL))
3501
0
      {
3502
0
        PE* pe = (PE*) yr_malloc(sizeof(PE));
3503
3504
0
        if (pe == NULL)
3505
0
          return ERROR_INSUFFICIENT_MEMORY;
3506
3507
0
        pe->data = block_data;
3508
0
        pe->data_size = block->size;
3509
0
        pe->object = module_object;
3510
0
        pe->header = pe_header;
3511
3512
0
        module_object->data = pe;
3513
3514
0
        dotnet_parse_com(pe);
3515
3516
0
        break;
3517
0
      }
3518
0
    }
3519
0
  }
3520
3521
0
  return ERROR_SUCCESS;
3522
0
}
3523
3524
int module_unload(YR_OBJECT* module_object)
3525
0
{
3526
0
  PE* pe = (PE*) module_object->data;
3527
3528
0
  if (pe == NULL)
3529
0
    return ERROR_SUCCESS;
3530
3531
0
  yr_free(pe);
3532
3533
0
  return ERROR_SUCCESS;
3534
0
}