Coverage Report

Created: 2023-06-07 07:18

/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
  uint8_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
    uint8_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
    uint16_t tmp2 = (tmp1 >> 1) & 0x3FFF;
585
    // sign extension in case of negative number
586
0
    if (tmp1 & 0x1)
587
0
      tmp2 |= 0xC000;
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
    uint32_t tmp2 = (tmp1 >> 1) & 0x1FFFFFFF;
606
    // sign extension in case of negative number
607
0
    if (tmp1 & 0x1)
608
0
      tmp2 |= 0xE0000000;
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
    uint32_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
    sizes = yr_malloc(sizeof(uint32_t) * num_sizes);
896
0
    if (!sizes || num_sizes > rank)
897
0
      goto cleanup;
898
899
0
    for (uint32_t i = 0; i < num_sizes; ++i)
900
0
    {
901
0
      sizes[i] = read_blob_unsigned(data, len);
902
0
    }
903
904
    // Read number of specified lower bounds
905
0
    uint32_t num_lowbounds = read_blob_unsigned(data, len);
906
0
    lo_bounds = yr_malloc(sizeof(int32_t) * num_lowbounds);
907
0
    if (!lo_bounds || num_lowbounds > rank)
908
0
      goto cleanup;
909
910
0
    for (uint32_t i = 0; i < num_lowbounds; ++i)
911
0
    {
912
0
      lo_bounds[i] = read_blob_signed(data, len);
913
914
      // Adjust higher bound according to lower bound
915
0
      if (num_sizes > i)
916
0
        sizes[i] += lo_bounds[i];
917
0
    }
918
919
    // Build the resulting array type
920
0
    SIMPLE_STR* ss = sstr_new(NULL);
921
0
    if (!ss)
922
0
      goto cleanup;
923
924
0
    sstr_appendf(ss, "%s[", tmp);
925
926
0
    for (uint32_t i = 0; i < rank; ++i)
927
0
    {
928
0
      if (num_sizes > i || num_lowbounds > i)
929
0
      {
930
0
        if (num_lowbounds > i && lo_bounds[i] != 0)
931
0
          sstr_appendf(ss, "%lu...", lo_bounds[i]);
932
0
        if (num_sizes > i && sizes[i] != 0)
933
0
          sstr_appendf(ss, "%lu", sizes[i]);
934
0
      }
935
0
      if (i + 1 != rank)
936
0
        sstr_appendf(ss, ",");
937
0
    }
938
0
    bool res = sstr_appendf(ss, "]");
939
0
    if (res)
940
0
      ret_type = sstr_move(ss);
941
942
0
    yr_free(sizes);
943
0
    yr_free(lo_bounds);
944
0
    yr_free(tmp);
945
0
    sstr_free(ss);
946
0
    return ret_type;
947
948
0
  cleanup:
949
0
    yr_free(sizes);
950
0
    yr_free(lo_bounds);
951
0
    yr_free(tmp);
952
0
  }
953
0
  break;
954
955
0
  case TYPE_GENERICINST:
956
0
  {
957
0
    tmp = parse_signature_type(
958
0
        ctx, data, len, class_gen_params, method_gen_params, depth + 1);
959
960
0
    if (!tmp)
961
0
      break;
962
963
0
    uint32_t gen_count = read_blob_unsigned(data, len);
964
965
    // Sanity check for corrupted files
966
0
    if (gen_count > MAX_GEN_PARAM_COUNT)
967
0
    {
968
0
      yr_free(tmp);
969
0
      break;
970
0
    }
971
972
0
    SIMPLE_STR* ss = sstr_new(NULL);
973
0
    if (!ss)
974
0
    {
975
0
      yr_free(tmp);
976
0
      break;
977
0
    }
978
0
    sstr_appendf(ss, "%s<", tmp);
979
0
    yr_free(tmp);
980
981
0
    for (int i = 0; i < gen_count; i++)
982
0
    {
983
0
      char* param_type = parse_signature_type(
984
0
          ctx, data, len, class_gen_params, method_gen_params, depth + 1);
985
986
0
      if (param_type != NULL)
987
0
      {
988
0
        if (i > 0)
989
0
          sstr_appendf(ss, ",");
990
991
0
        sstr_appendf(ss, "%s", param_type);
992
0
        yr_free(param_type);
993
0
      }
994
0
    }
995
0
    bool res = sstr_appendf(ss, ">");
996
0
    if (res)
997
0
      ret_type = sstr_move(ss);
998
999
0
    sstr_free(ss);
1000
0
    return ret_type;
1001
0
  }
1002
0
  break;
1003
1004
0
  case TYPE_FNPTR:
1005
0
    if (*len > 0)
1006
0
    {  // Flags -> ParamCount -> RetType -> Param -> Sentinel ->Param
1007
      // Skip flags
1008
0
      (*data)++;
1009
0
      (*len)--;
1010
1011
0
      uint32_t param_count = read_blob_unsigned(data, len);
1012
1013
      // Sanity check for corrupted files
1014
0
      if (param_count > MAX_PARAM_COUNT)
1015
0
      {
1016
0
        yr_free(tmp);
1017
0
        break;
1018
0
      }
1019
1020
0
      tmp = parse_signature_type(
1021
0
          ctx, data, len, class_gen_params, method_gen_params, depth + 1);
1022
1023
0
      if (!tmp)
1024
0
        break;
1025
1026
0
      SIMPLE_STR* ss = sstr_new(NULL);
1027
0
      if (!ss)
1028
0
      {
1029
0
        yr_free(tmp);
1030
0
        break;
1031
0
      }
1032
1033
0
      sstr_appendf(ss, "FnPtr<%s(", tmp);
1034
0
      yr_free(tmp);
1035
1036
0
      for (int i = 0; i < param_count; i++)
1037
0
      {
1038
0
        char* param_type = parse_signature_type(
1039
0
            ctx, data, len, class_gen_params, method_gen_params, depth + 1);
1040
1041
0
        if (param_type != NULL)
1042
0
        {
1043
0
          if (i > 0)
1044
0
            sstr_appendf(ss, ", ");
1045
1046
0
          sstr_appendf(ss, "%s", param_type);
1047
0
          yr_free(param_type);
1048
0
        }
1049
0
      }
1050
1051
0
      if (sstr_appendf(ss, ")>"))
1052
0
        ret_type = sstr_move(ss);
1053
1054
0
      sstr_free(ss);
1055
0
      return ret_type;
1056
0
    }
1057
0
    break;
1058
1059
0
  case TYPE_OBJECT:
1060
0
    ret_type = "object";
1061
0
    break;
1062
1063
0
  case TYPE_SZARRAY:
1064
    // Single dimensional array followed by type
1065
0
    tmp = parse_signature_type(
1066
0
        ctx, data, len, class_gen_params, method_gen_params, depth + 1);
1067
0
    if (tmp)
1068
0
    {
1069
0
      SIMPLE_STR* ss = sstr_newf("%s[]", tmp);
1070
0
      if (ss)
1071
0
        ret_type = sstr_move(ss);
1072
1073
0
      yr_free(tmp);
1074
0
      sstr_free(ss);
1075
0
      return ret_type;
1076
0
    }
1077
0
    break;
1078
1079
0
  case TYPE_CMOD_REQD:  // Req modifier
1080
0
  case TYPE_CMOD_OPT:   // Opt modifier
1081
0
  {
1082
    // What is point of these
1083
    // Right now ignore them...
1084
0
    read_blob_unsigned(data, len);
1085
0
    return parse_signature_type(
1086
0
        ctx, data, len, class_gen_params, method_gen_params, depth + 1);
1087
0
  }
1088
0
  break;
1089
1090
0
  default:
1091
0
    break;
1092
0
  }
1093
1094
0
  if (ret_type)
1095
0
    return yr_strdup(ret_type);
1096
0
  else
1097
0
    return NULL;
1098
0
}
1099
1100
static void parse_type_parents(
1101
    const CLASS_CONTEXT* ctx,
1102
    uint32_t extends,
1103
    uint32_t type_idx,
1104
    uint32_t out_idx,  // Class idx in output array
1105
    GENERIC_PARAMETERS* class_gen_params)
1106
0
{
1107
  // Find the parent class
1108
0
  char* parent = get_type_def_or_ref_fullname(
1109
0
      ctx, extends, class_gen_params, NULL, 0);
1110
1111
0
  uint32_t base_type_idx = 0;
1112
0
  if (parent)
1113
0
  {
1114
0
    yr_set_string(
1115
0
        parent,
1116
0
        ctx->pe->object,
1117
0
        "classes[%i].base_types[%i]",
1118
0
        out_idx,
1119
0
        base_type_idx++);
1120
1121
0
    yr_free(parent);
1122
0
  }
1123
1124
  // linear search for every interface that the class implements
1125
0
  for (uint32_t idx = 0; idx < ctx->tables->intefaceimpl.RowCount; ++idx)
1126
0
  {
1127
0
    const uint8_t* data = get_table_offset(&ctx->tables->intefaceimpl, idx + 1);
1128
0
    if (!data)
1129
0
      break;
1130
1131
0
    INTERFACEIMPL_ROW row = {0};
1132
0
    bool result = read_interfaceimpl(ctx, data, &row);
1133
0
    if (!result)
1134
0
      continue;
1135
1136
    // We found the inherited interface
1137
0
    if (row.Class == type_idx)
1138
0
    {
1139
0
      char* inteface = get_type_def_or_ref_fullname(
1140
0
          ctx, row.Interface, class_gen_params, NULL, 0);
1141
0
      if (inteface)
1142
0
      {
1143
0
        yr_set_string(
1144
0
            inteface,
1145
0
            ctx->pe->object,
1146
0
            "classes[%i].base_types[%i]",
1147
0
            out_idx,
1148
0
            base_type_idx++);
1149
1150
0
        yr_free(inteface);
1151
0
      }
1152
0
    }
1153
0
  }
1154
0
  yr_set_integer(
1155
0
      base_type_idx,
1156
0
      ctx->pe->object,
1157
0
      "classes[%i].number_of_base_types",
1158
0
      out_idx);
1159
0
}
1160
1161
// Returns true if all parameters were correctly parsed
1162
static bool parse_method_params(
1163
    const CLASS_CONTEXT* ctx,
1164
    uint32_t param_list,
1165
    uint32_t method_idx,  // used for output
1166
    uint32_t class_idx,
1167
    uint32_t param_count,
1168
    const uint8_t* sig_data,
1169
    uint32_t sig_len,
1170
    GENERIC_PARAMETERS* class_gen_params,
1171
    GENERIC_PARAMETERS* method_gen_params)
1172
0
{
1173
0
  if (!param_list)  // NULL
1174
0
    return true;
1175
1176
0
  const uint8_t* str_heap = ctx->str_heap;
1177
0
  uint32_t str_size = ctx->str_size;
1178
1179
  // Array to hold all the possible parameters
1180
0
  PARAMETERS* params = yr_calloc(param_count, sizeof(PARAMETERS));
1181
1182
0
  if (!params)
1183
0
    return false;
1184
1185
0
  for (uint32_t idx = 0; idx < param_count; ++idx)
1186
0
  {
1187
0
    const uint8_t* data = get_table_offset(
1188
0
        &ctx->tables->param, param_list + idx);
1189
1190
0
    char* name = NULL;
1191
0
    bool alloc = false;  // Flag if name needs freeing
1192
1193
0
    if (data)            // We need param table mostly just for the param name
1194
0
    {
1195
0
      PARAM_ROW row = {0};
1196
0
      bool result = read_param(ctx, data, &row);
1197
1198
0
      if (!result)
1199
0
      {  // Cleanup and return
1200
0
        for (uint32_t j = 0; j < idx; ++j)
1201
0
        {
1202
0
          if (params[j].alloc)
1203
0
            yr_free(params[j].name);
1204
1205
0
          yr_free(params[j].type);
1206
0
        }
1207
0
        yr_free(params);
1208
0
        return false;
1209
0
      }
1210
1211
0
      name = pe_get_dotnet_string(ctx->pe, str_heap, str_size, row.Name);
1212
0
    }
1213
0
    else  // We can reconstruct their type from the signature
1214
          // and give them default name
1215
0
    {
1216
0
      alloc = true;
1217
0
      SIMPLE_STR* ss = sstr_newf("P_%lu", idx);
1218
0
      if (ss)
1219
0
      {
1220
0
        name = sstr_move(ss);
1221
0
        sstr_free(ss);
1222
0
      }
1223
0
    }
1224
1225
0
    char* type = parse_signature_type(
1226
0
        ctx, &sig_data, &sig_len, class_gen_params, method_gen_params, 0);
1227
1228
0
    params[idx].alloc = alloc;
1229
0
    params[idx].name = name;
1230
0
    params[idx].type = type;
1231
1232
0
    if (!type)  // If any param fails, whole parsing is aborted
1233
0
    {
1234
0
      for (uint32_t j = 0; j <= idx; ++j)
1235
0
      {
1236
0
        if (params[j].alloc)
1237
0
          yr_free(params[j].name);
1238
1239
0
        yr_free(params[j].type);
1240
0
      }
1241
0
      yr_free(params);
1242
0
      return false;
1243
0
    }
1244
0
  }
1245
1246
  // If we got all of them correctly, write to output and cleanup
1247
0
  YR_OBJECT* out_obj = ctx->pe->object;
1248
0
  yr_set_integer(
1249
0
      param_count,
1250
0
      out_obj,
1251
0
      "classes[%i].methods[%i].number_of_parameters",
1252
0
      class_idx,
1253
0
      method_idx);
1254
1255
0
  for (uint32_t i = 0; i < param_count; ++i)
1256
0
  {
1257
0
    yr_set_string(
1258
0
        params[i].name,
1259
0
        out_obj,
1260
0
        "classes[%i].methods[%i].parameters[%i].name",
1261
0
        class_idx,
1262
0
        method_idx,
1263
0
        i);
1264
0
    yr_set_string(
1265
0
        params[i].type,
1266
0
        out_obj,
1267
0
        "classes[%i].methods[%i].parameters[%i].type",
1268
0
        class_idx,
1269
0
        method_idx,
1270
0
        i);
1271
0
    if (params[i].alloc)
1272
0
      yr_free(params[i].name);
1273
1274
0
    yr_free(params[i].type);
1275
0
  }
1276
1277
0
  yr_free(params);
1278
0
  return true;
1279
0
}
1280
1281
// Walks GenericParam table, finds all generic params for the MethodDef or
1282
// TypeDef entry and allocates buffer with the Generic param names into result
1283
static void parse_generic_params(
1284
    const CLASS_CONTEXT* ctx,
1285
    bool method,  // true means MethodDef, false TypeDef index
1286
    uint32_t gen_idx,
1287
    GENERIC_PARAMETERS* result)
1288
0
{
1289
0
  const uint8_t* str_heap = ctx->str_heap;
1290
0
  uint32_t str_size = ctx->str_size;
1291
1292
0
  result->names = NULL;
1293
0
  result->len = 0;
1294
1295
  // Walk the GenericParam table to find GenParameters of the class/method
1296
0
  for (uint32_t idx = 0; idx < ctx->tables->genericparam.RowCount; ++idx)
1297
0
  {
1298
0
    const uint8_t* data = get_table_offset(&ctx->tables->genericparam, idx + 1);
1299
0
    if (!data)
1300
0
      goto cleanup;
1301
1302
0
    GENERICPARAM_ROW row = {0};
1303
0
    bool read_result = read_genericparam(ctx, data, &row);
1304
0
    if (!read_result)
1305
0
      continue;
1306
1307
    // TypeOrMethodDef coded index
1308
0
    uint8_t table = row.Owner & 0x1;
1309
    // 0 == TypeDef 1 == MethodDef
1310
    // Check if it's generic param of the type we want
1311
0
    if (table == method && (row.Owner >> 1) == gen_idx)
1312
0
    {
1313
0
      char* name = pe_get_dotnet_string(ctx->pe, str_heap, str_size, row.Name);
1314
      // name must be valid string
1315
0
      if (!name || !*name)  // ERROR
1316
0
        goto cleanup;
1317
1318
0
      result->len += 1;
1319
0
      char** tmp = yr_realloc(result->names, result->len * sizeof(char*));
1320
0
      if (!tmp)
1321
0
        goto cleanup;
1322
1323
      // Update the collection
1324
0
      result->names = tmp;
1325
0
      result->names[result->len - 1] = name;
1326
0
    }
1327
0
  }
1328
0
  return;
1329
1330
0
cleanup:
1331
0
  yr_free(result->names);
1332
0
  result->names = NULL;
1333
0
  result->len = 0;
1334
0
}
1335
1336
static void parse_methods(
1337
    const CLASS_CONTEXT* ctx,
1338
    uint32_t methodlist,
1339
    uint32_t method_count,
1340
    uint32_t class_idx,  // class index in the YARA output
1341
    GENERIC_PARAMETERS* class_gen_params)
1342
0
{
1343
0
  if (!methodlist)
1344
0
    return;
1345
1346
0
  const uint8_t* str_heap = ctx->str_heap;
1347
0
  uint32_t str_size = ctx->str_size;
1348
1349
0
  uint32_t out_idx = 0;
1350
0
  for (uint32_t idx = 0; idx < method_count; ++idx)
1351
0
  {
1352
0
    const uint8_t* data = get_table_offset(
1353
0
        &ctx->tables->methoddef, methodlist + idx);
1354
1355
0
    if (!data)
1356
0
      break;
1357
1358
0
    METHODDEF_ROW row = {0};
1359
0
    bool result = read_methoddef(ctx, data, &row);
1360
0
    if (!result)
1361
0
      continue;
1362
1363
0
    const char* name = pe_get_dotnet_string(
1364
0
        ctx->pe, str_heap, str_size, row.Name);
1365
1366
    // Ignore invalid/empty names
1367
0
    if (!name || !*name)
1368
0
      continue;
1369
1370
    // Try to find generic params for the method
1371
0
    GENERIC_PARAMETERS method_gen_params = {0};
1372
0
    parse_generic_params(ctx, true, methodlist + idx, &method_gen_params);
1373
1374
    // Read the blob entry with signature data
1375
0
    const uint8_t* sig_data = ctx->blob_heap + row.Signature;
1376
1377
0
    BLOB_PARSE_RESULT blob_res = dotnet_parse_blob_entry(ctx->pe, sig_data);
1378
0
    sig_data += blob_res.size;
1379
0
    uint32_t sig_len = blob_res.length;
1380
0
    uint32_t param_count = 0;
1381
1382
0
    char* return_type = NULL;
1383
    // If there is valid blob and at least minimum to parse
1384
    // (flags, paramCount, retType) parse these basic information
1385
0
    if (blob_res.size && sig_len >= 3)
1386
0
    {
1387
0
      uint8_t flags = read_u8(&sig_data);
1388
0
      sig_len -= 1;
1389
0
      if (flags & SIG_FLAG_GENERIC)
1390
        // Generic param count, ignored as we get the
1391
        // information from generic param table
1392
0
        (void) read_blob_unsigned(&sig_data, &sig_len);
1393
1394
      // Regular param count
1395
0
      param_count = read_blob_unsigned(&sig_data, &sig_len);
1396
0
      return_type = parse_signature_type(
1397
0
          ctx, &sig_data, &sig_len, class_gen_params, &method_gen_params, 0);
1398
0
    }
1399
0
    else  // Error, skip
1400
0
      goto clean_next;
1401
1402
    // Sanity check for corrupted files
1403
0
    if (!return_type || param_count > MAX_PARAM_COUNT)
1404
0
      goto clean_next;
1405
1406
0
    result = parse_method_params(
1407
0
        ctx,
1408
0
        row.ParamList,
1409
0
        out_idx,
1410
0
        class_idx,
1411
0
        param_count,
1412
0
        sig_data,
1413
0
        sig_len,
1414
0
        class_gen_params,
1415
0
        &method_gen_params);
1416
1417
0
    if (!result)
1418
0
      goto clean_next;
1419
1420
0
    const char* visibility = get_method_visibility(row.Flags);
1421
0
    uint32_t stat = (row.Flags & METHOD_ATTR_STATIC) != 0;
1422
0
    uint32_t final = (row.Flags & METHOD_ATTR_FINAL) != 0;
1423
0
    uint32_t virtual = (row.Flags & METHOD_ATTR_VIRTUAL) != 0;
1424
0
    uint32_t abstract = (row.Flags & METHOD_ATTR_ABSTRACT) != 0;
1425
1426
0
    YR_OBJECT* out_obj = ctx->pe->object;
1427
0
    yr_set_string(
1428
0
        name, out_obj, "classes[%i].methods[%i].name", class_idx, out_idx);
1429
0
    yr_set_string(
1430
0
        visibility,
1431
0
        out_obj,
1432
0
        "classes[%i].methods[%i].visibility",
1433
0
        class_idx,
1434
0
        out_idx);
1435
0
    yr_set_integer(
1436
0
        stat, out_obj, "classes[%i].methods[%i].static", class_idx, out_idx);
1437
0
    yr_set_integer(
1438
0
        virtual,
1439
0
        out_obj,
1440
0
        "classes[%i].methods[%i].virtual",
1441
0
        class_idx,
1442
0
        out_idx);
1443
0
    yr_set_integer(
1444
0
        final, out_obj, "classes[%i].methods[%i].final", class_idx, out_idx);
1445
0
    yr_set_integer(
1446
0
        abstract,
1447
0
        out_obj,
1448
0
        "classes[%i].methods[%i].abstract",
1449
0
        class_idx,
1450
0
        out_idx);
1451
0
    yr_set_integer(
1452
0
        method_gen_params.len,
1453
0
        out_obj,
1454
0
        "classes[%i].methods[%i].number_of_generic_parameters",
1455
0
        class_idx,
1456
0
        out_idx);
1457
1458
0
    for (uint32_t i = 0; i < method_gen_params.len; ++i)
1459
0
    {
1460
0
      yr_set_string(
1461
0
          method_gen_params.names[i],
1462
0
          ctx->pe->object,
1463
0
          "classes[%i].methods[%i].generic_parameters[%i]",
1464
0
          class_idx,
1465
0
          out_idx,
1466
0
          i);
1467
0
    }
1468
1469
    // Unset return type for constructors for FileInfo compatibility
1470
0
    if (strcmp(name, ".ctor") != 0 && strcmp(name, ".cctor") != 0)
1471
0
    {
1472
0
      yr_set_string(
1473
0
          return_type,
1474
0
          out_obj,
1475
0
          "classes[%i].methods[%i].return_type",
1476
0
          class_idx,
1477
0
          out_idx);
1478
0
    }
1479
1480
0
    out_idx++;
1481
0
  clean_next:
1482
0
    yr_free(return_type);
1483
0
    yr_free(method_gen_params.names);
1484
0
  }
1485
1486
0
  yr_set_integer(
1487
0
      out_idx, ctx->pe->object, "classes[%i].number_of_methods", class_idx);
1488
0
}
1489
1490
// Walks NestedClass table, returns enclosing type fullname or NULL
1491
static char* parse_enclosing_types(
1492
    const CLASS_CONTEXT* ctx,
1493
    uint32_t nested_idx,
1494
    uint32_t depth)
1495
0
{
1496
0
  if (depth > MAX_NAMESPACE_DEPTH)
1497
0
    return NULL;
1498
1499
0
  const uint8_t* str_heap = ctx->str_heap;
1500
0
  uint32_t str_size = ctx->str_size;
1501
1502
0
  for (uint32_t idx = 0; idx < ctx->tables->nestedclass.RowCount; ++idx)
1503
0
  {
1504
0
    const uint8_t* nested_data = get_table_offset(
1505
0
        &ctx->tables->nestedclass, idx + 1);
1506
1507
0
    NESTEDCLASS_ROW nested_row = {0};
1508
0
    bool read_result = read_nestedclass(ctx, nested_data, &nested_row);
1509
0
    if (!read_result)
1510
0
      continue;
1511
1512
    // We found enclosing class, get the namespace
1513
0
    if (nested_row.NestedClass == nested_idx)
1514
0
    {
1515
0
      const uint8_t* typedef_data = get_table_offset(
1516
0
          &ctx->tables->typedef_, nested_row.EnclosingClass);
1517
1518
0
      TYPEDEF_ROW typedef_row = {0};
1519
0
      bool result = read_typedef(ctx, typedef_data, &typedef_row);
1520
0
      if (!result)
1521
0
        break;
1522
1523
0
      const char* name = pe_get_dotnet_string(
1524
0
          ctx->pe, str_heap, str_size, typedef_row.Name);
1525
1526
      // Skip the Module pseudo class
1527
0
      if (name && strcmp(name, "<Module>") == 0)
1528
0
        break;
1529
1530
0
      const char* namespace = pe_get_dotnet_string(
1531
0
          ctx->pe, str_heap, str_size, typedef_row.Namespace);
1532
1533
      // Type might be further nested, try to find correct namespace,
1534
      // check for self-reference
1535
0
      if (is_nested(typedef_row.Flags) &&
1536
0
          nested_row.EnclosingClass != nested_row.NestedClass)
1537
0
      {
1538
0
        char* nested_namespace = parse_enclosing_types(
1539
0
            ctx, nested_row.EnclosingClass, depth + 1);
1540
1541
0
        char* tmp = create_full_name(namespace, nested_namespace);
1542
0
        char* fullname = create_full_name(name, tmp);
1543
0
        yr_free(nested_namespace);
1544
0
        yr_free(tmp);
1545
0
        return fullname;
1546
0
      }
1547
1548
0
      return create_full_name(name, namespace);
1549
0
    }
1550
0
  }
1551
1552
0
  return NULL;
1553
0
}
1554
1555
// Parses and reconstructs user defined types with their methods and base types
1556
static void parse_user_types(const CLASS_CONTEXT* ctx)
1557
0
{
1558
0
  const uint8_t* str_heap = ctx->str_heap;
1559
0
  uint32_t str_size = ctx->str_size;
1560
1561
  // Index for output tracking, we can't use
1562
  // offset as some classes can get skipped
1563
0
  uint32_t out_idx = 0;
1564
  // skip first class as it's module pseudo class -> start at index 1
1565
0
  for (uint32_t idx = 0; idx < ctx->tables->typedef_.RowCount; ++idx)
1566
0
  {
1567
0
    YR_OBJECT* out_obj = ctx->pe->object;
1568
    // Tables indexing starts at 1
1569
0
    const uint8_t* data = get_table_offset(&ctx->tables->typedef_, idx + 1);
1570
1571
0
    TYPEDEF_ROW row = {0};
1572
0
    bool result = read_typedef(ctx, data, &row);
1573
0
    if (!result)
1574
0
      continue;
1575
1576
0
    const char* name = pe_get_dotnet_string(
1577
0
        ctx->pe, str_heap, str_size, row.Name);
1578
0
    const char* type = get_typedef_type(row.Flags);
1579
1580
    // Ignore invalid types and invalid (empty) names
1581
0
    if (!name || !*name || !type)
1582
0
      continue;
1583
1584
    // If the type is generic, it will include ` at the end of a name
1585
    // with number of generic arguments, just use the part before that
1586
0
    const char* end = strchr(name, '`');
1587
    // If the name will turn out empty, skip it and skip Module pseudo class
1588
0
    if (end == name || strcmp(name, "<Module>") == 0)
1589
0
      continue;
1590
1591
0
    if (end)
1592
0
      yr_set_sized_string(
1593
0
          name, end - name, out_obj, "classes[%i].name", out_idx);
1594
0
    else
1595
0
      yr_set_string(name, out_obj, "classes[%i].name", out_idx);
1596
1597
0
    char* fullname = NULL;
1598
0
    char* namespace = pe_get_dotnet_string(
1599
0
        ctx->pe, str_heap, str_size, row.Namespace);
1600
1601
    // Type might be nested, if so -> find correct namespace
1602
0
    if (is_nested(row.Flags))
1603
0
    {
1604
0
      char* nested_namespace = parse_enclosing_types(ctx, idx + 1, 1);
1605
0
      namespace = create_full_name(namespace, nested_namespace);
1606
0
      yr_set_string(namespace, out_obj, "classes[%i].namespace", out_idx);
1607
0
      fullname = create_full_name(name, namespace);
1608
0
      yr_free(nested_namespace);
1609
0
      yr_free(namespace);
1610
0
    }
1611
0
    else
1612
0
    {
1613
0
      yr_set_string(namespace, out_obj, "classes[%i].namespace", out_idx);
1614
0
      fullname = create_full_name(name, namespace);
1615
0
    }
1616
1617
0
    const char* visibility = get_type_visibility(row.Flags);
1618
0
    uint32_t abstract = (row.Flags & TYPE_ATTR_ABSTRACT) != 0;
1619
0
    uint32_t sealed = (row.Flags & TYPE_ATTR_SEALED) != 0;
1620
1621
0
    yr_set_string(fullname, out_obj, "classes[%i].fullname", out_idx);
1622
0
    yr_set_string(visibility, out_obj, "classes[%i].visibility", out_idx);
1623
0
    yr_set_string(type, out_obj, "classes[%i].type", out_idx);
1624
0
    yr_set_integer(abstract, out_obj, "classes[%i].abstract", out_idx);
1625
0
    yr_set_integer(sealed, out_obj, "classes[%i].sealed", out_idx);
1626
1627
0
    yr_free(fullname);
1628
1629
    // Find if type has any Generic parameters
1630
0
    GENERIC_PARAMETERS gen_params = {0};
1631
0
    parse_generic_params(ctx, false, idx + 1, &gen_params);
1632
1633
0
    yr_set_integer(
1634
0
        gen_params.len,
1635
0
        out_obj,
1636
0
        "classes[%i].number_of_generic_parameters",
1637
0
        out_idx);
1638
1639
0
    for (uint32_t i = 0; i < gen_params.len; ++i)
1640
0
    {
1641
0
      yr_set_string(
1642
0
          gen_params.names[i],
1643
0
          out_obj,
1644
0
          "classes[%i].generic_parameters[%i]",
1645
0
          out_idx,
1646
0
          i);
1647
0
    }
1648
    // Find type and interfaces the type inherits
1649
0
    parse_type_parents(ctx, row.Extends, idx + 1, out_idx, &gen_params);
1650
1651
    // To get the number of methods, we must peek where the MethodList
1652
    // of the next type is, then there is next.MethodList - this.MethodList
1653
    // number of methods, or if there is no following type,
1654
    // the rest of the MethodDef table is used
1655
0
    uint32_t method_count = 0;
1656
    // If there is next method
1657
0
    if (idx + 1 < ctx->tables->typedef_.RowCount)
1658
0
    {
1659
0
      const uint8_t* data = get_table_offset(&ctx->tables->typedef_, idx + 2);
1660
1661
0
      TYPEDEF_ROW next_row = {0};
1662
0
      result = read_typedef(ctx, data, &next_row);
1663
1664
      // overflow check
1665
0
      if (result && next_row.Method >= row.Method)
1666
0
        method_count = next_row.Method - row.Method;
1667
0
    }
1668
    // overflow check - use the rest of the methods in the table
1669
0
    else if (ctx->tables->methoddef.RowCount >= row.Method)
1670
0
    {
1671
0
      method_count = ctx->tables->methoddef.RowCount + 1 - row.Method;
1672
0
    }
1673
1674
    // Sanity check for corrupted files
1675
0
    if (method_count <= MAX_METHOD_COUNT)
1676
0
      parse_methods(ctx, row.Method, method_count, out_idx, &gen_params);
1677
1678
0
    yr_free(gen_params.names);
1679
0
    out_idx++;
1680
0
  }
1681
1682
0
  yr_set_integer(out_idx, ctx->pe->object, "number_of_classes");
1683
0
}
1684
1685
void dotnet_parse_guid(
1686
    PE* pe,
1687
    int64_t metadata_root,
1688
    PSTREAM_HEADER guid_header)
1689
0
{
1690
  // GUIDs are 16 bytes each, converted to hex format plus separators and NULL.
1691
0
  char guid[37];
1692
0
  int i = 0;
1693
1694
0
  const uint8_t* guid_offset = pe->data + metadata_root +
1695
0
                               yr_le32toh(guid_header->Offset);
1696
1697
0
  DWORD guid_size = yr_le32toh(guid_header->Size);
1698
1699
  // Limit the number of GUIDs to 16.
1700
0
  guid_size = yr_min(guid_size, 256);
1701
1702
  // Parse GUIDs if we have them. GUIDs are 16 bytes each.
1703
0
  while (guid_size >= 16 && fits_in_pe(pe, guid_offset, 16))
1704
0
  {
1705
0
    sprintf(
1706
0
        guid,
1707
0
        "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
1708
0
        yr_le32toh(*(uint32_t*) guid_offset),
1709
0
        yr_le16toh(*(uint16_t*) (guid_offset + 4)),
1710
0
        yr_le16toh(*(uint16_t*) (guid_offset + 6)),
1711
0
        *(guid_offset + 8),
1712
0
        *(guid_offset + 9),
1713
0
        *(guid_offset + 10),
1714
0
        *(guid_offset + 11),
1715
0
        *(guid_offset + 12),
1716
0
        *(guid_offset + 13),
1717
0
        *(guid_offset + 14),
1718
0
        *(guid_offset + 15));
1719
1720
0
    guid[(16 * 2) + 4] = '\0';
1721
1722
0
    yr_set_string(guid, pe->object, "guids[%i]", i);
1723
1724
0
    i++;
1725
0
    guid_size -= 16;
1726
0
    guid_offset += 16;
1727
0
  }
1728
1729
0
  yr_set_integer(i, pe->object, "number_of_guids");
1730
0
}
1731
1732
void dotnet_parse_us(PE* pe, int64_t metadata_root, PSTREAM_HEADER us_header)
1733
0
{
1734
0
  BLOB_PARSE_RESULT blob_result;
1735
0
  int i = 0;
1736
1737
0
  const uint32_t ush_sz = yr_le32toh(us_header->Size);
1738
1739
0
  const uint8_t* offset = pe->data + metadata_root +
1740
0
                          yr_le32toh(us_header->Offset);
1741
0
  const uint8_t* end_of_header = offset + ush_sz;
1742
1743
  // Make sure the header size is larger than 0 and its end is not past the
1744
  // end of PE.
1745
0
  if (ush_sz == 0 || !fits_in_pe(pe, offset, ush_sz))
1746
0
    return;
1747
1748
  // The first entry MUST be single NULL byte.
1749
0
  if (*offset != 0x00)
1750
0
    return;
1751
1752
0
  offset++;
1753
1754
0
  while (offset < end_of_header)
1755
0
  {
1756
0
    blob_result = dotnet_parse_blob_entry(pe, offset);
1757
1758
0
    if (blob_result.size == 0)
1759
0
      break;
1760
1761
0
    offset += blob_result.size;
1762
    // There is an additional terminal byte which is 0x01 under certain
1763
    // conditions - when any top bit in utf16 top byte is set.
1764
    // The exact conditions are not relevant to our parsing but are
1765
    // documented in ECMA-335 II.24.2.4.
1766
0
    if (blob_result.length > 0)
1767
0
      blob_result.length--;
1768
1769
    // Avoid empty strings, which usually happen as padding at the end of the
1770
    // stream.
1771
0
    if (blob_result.length > 0 && fits_in_pe(pe, offset, blob_result.length))
1772
0
    {
1773
0
      yr_set_sized_string(
1774
0
          (char*) offset,
1775
0
          blob_result.length,
1776
0
          pe->object,
1777
0
          "user_strings[%i]",
1778
0
          i);
1779
1780
0
      offset += blob_result.length;
1781
0
      i++;
1782
0
    }
1783
0
  }
1784
1785
0
  yr_set_integer(i, pe->object, "number_of_user_strings");
1786
0
}
1787
1788
STREAMS dotnet_parse_stream_headers(
1789
    PE* pe,
1790
    int64_t offset,
1791
    int64_t metadata_root,
1792
    DWORD num_streams)
1793
0
{
1794
0
  PSTREAM_HEADER stream_header;
1795
0
  STREAMS headers;
1796
1797
0
  char* start;
1798
0
  char* eos;
1799
0
  char stream_name[DOTNET_STREAM_NAME_SIZE + 1];
1800
0
  unsigned int i;
1801
1802
0
  memset(&headers, '\0', sizeof(STREAMS));
1803
0
  headers.metadata_root = metadata_root;
1804
1805
0
  stream_header = (PSTREAM_HEADER) (pe->data + offset);
1806
1807
0
  for (i = 0; i < num_streams; i++)
1808
0
  {
1809
0
    if (!struct_fits_in_pe(pe, stream_header, STREAM_HEADER))
1810
0
      break;
1811
1812
0
    start = (char*) stream_header->Name;
1813
1814
0
    if (!fits_in_pe(pe, start, DOTNET_STREAM_NAME_SIZE))
1815
0
      break;
1816
1817
0
    eos = (char*) memmem((void*) start, DOTNET_STREAM_NAME_SIZE, "\0", 1);
1818
1819
0
    if (eos == NULL)
1820
0
      break;
1821
1822
0
    strncpy(stream_name, stream_header->Name, DOTNET_STREAM_NAME_SIZE);
1823
0
    stream_name[DOTNET_STREAM_NAME_SIZE] = '\0';
1824
1825
0
    yr_set_string(stream_name, pe->object, "streams[%i].name", i);
1826
1827
    // Offset is relative to metadata_root.
1828
0
    yr_set_integer(
1829
0
        metadata_root + yr_le32toh(stream_header->Offset),
1830
0
        pe->object,
1831
0
        "streams[%i].offset",
1832
0
        i);
1833
1834
0
    yr_set_integer(
1835
0
        yr_le32toh(stream_header->Size), pe->object, "streams[%i].size", i);
1836
1837
    // Store necessary bits to parse these later. Not all tables will be
1838
    // parsed, but are referenced from others. For example, the #Strings
1839
    // stream is referenced from various tables in the #~ heap.
1840
    //
1841
    // #- is not documented but it represents unoptimized metadata stream. It
1842
    // may contain additional tables such as FieldPtr, ParamPtr, MethodPtr or
1843
    // PropertyPtr for indirect referencing. We already take into account these
1844
    // tables and they do not interfere with anything we parse in this module.
1845
1846
0
    if ((strncmp(stream_name, "#~", 2) == 0 ||
1847
0
         strncmp(stream_name, "#-", 2) == 0) &&
1848
0
        headers.tilde == NULL)
1849
0
      headers.tilde = stream_header;
1850
0
    else if (strncmp(stream_name, "#GUID", 5) == 0)
1851
0
      headers.guid = stream_header;
1852
0
    else if (strncmp(stream_name, "#Strings", 8) == 0 && headers.string == NULL)
1853
0
      headers.string = stream_header;
1854
0
    else if (strncmp(stream_name, "#Blob", 5) == 0 && headers.blob == NULL)
1855
0
      headers.blob = stream_header;
1856
0
    else if (strncmp(stream_name, "#US", 3) == 0 && headers.us == NULL)
1857
0
      headers.us = stream_header;
1858
1859
    // Stream name is padded to a multiple of 4.
1860
0
    stream_header = (PSTREAM_HEADER) ((uint8_t*) stream_header +
1861
0
                                      sizeof(STREAM_HEADER) +
1862
0
                                      strlen(stream_name) + 4 -
1863
0
                                      (strlen(stream_name) % 4));
1864
0
  }
1865
1866
0
  yr_set_integer(i, pe->object, "number_of_streams");
1867
1868
0
  return headers;
1869
0
}
1870
1871
// This is the second pass through the data for #~. The first pass collects
1872
// information on the number of rows for tables which have coded indexes.
1873
// This pass uses that information and the index_sizes to parse the tables
1874
// of interest.
1875
//
1876
// Because the indexes can vary in size depending upon the number of rows in
1877
// other tables it is impossible to use static sized structures. To deal with
1878
// this hardcode the sizes of each table based upon the documentation (for the
1879
// static sized portions) and use the variable sizes accordingly.
1880
1881
void dotnet_parse_tilde_2(
1882
    PE* pe,
1883
    PTILDE_HEADER tilde_header,
1884
    int64_t resource_base,
1885
    ROWS rows,
1886
    INDEX_SIZES index_sizes,
1887
    PSTREAMS streams)
1888
0
{
1889
0
  PMODULE_TABLE module_table;
1890
0
  PASSEMBLY_TABLE assembly_table;
1891
0
  PASSEMBLYREF_TABLE assemblyref_table;
1892
0
  PFIELDRVA_TABLE fieldrva_table;
1893
0
  PMANIFESTRESOURCE_TABLE manifestresource_table;
1894
0
  PMODULEREF_TABLE moduleref_table;
1895
0
  PCUSTOMATTRIBUTE_TABLE customattribute_table;
1896
0
  PCONSTANT_TABLE constant_table;
1897
0
  DWORD resource_size, implementation;
1898
1899
  // To save important data for future processing, initialize everything to 0
1900
0
  TABLES tables = {0};
1901
1902
0
  char* name;
1903
0
  char typelib[MAX_TYPELIB_SIZE + 1];
1904
0
  unsigned int i;
1905
0
  int bit_check;
1906
0
  int matched_bits = 0;
1907
1908
0
  int64_t metadata_root = streams->metadata_root;
1909
0
  int64_t resource_offset, field_offset;
1910
0
  uint32_t row_size, row_count, counter, str_heap_size;
1911
1912
0
  const uint8_t* string_offset;
1913
0
  const uint8_t* blob_offset;
1914
1915
0
  uint32_t num_rows = 0;
1916
0
  uint32_t valid_rows = 0;
1917
0
  uint32_t* row_offset = NULL;
1918
0
  uint8_t* table_offset = NULL;
1919
0
  uint8_t* row_ptr = NULL;
1920
1921
  // These are pointers and row sizes for tables of interest to us for special
1922
  // parsing. For example, we are interested in pulling out any CustomAttributes
1923
  // that are GUIDs so we need to be able to walk these tables. To find GUID
1924
  // CustomAttributes you need to walk the CustomAttribute table and look for
1925
  // any row with a Parent that indexes into the Assembly table and Type indexes
1926
  // into the MemberRef table. Then you follow the index into the MemberRef
1927
  // table and check the Class to make sure it indexes into TypeRef table. If it
1928
  // does you follow that index and make sure the Name is "GuidAttribute". If
1929
  // all that is valid then you can take the Value from the CustomAttribute
1930
  // table to find out the index into the Blob stream and parse that.
1931
  //
1932
  // Luckily we can abuse the fact that the order of the tables is guaranteed
1933
  // consistent (though some may not exist, but if they do exist they must exist
1934
  // in a certain order). The order is defined by their position in the Valid
1935
  // member of the tilde_header structure. By the time we are parsing the
1936
  // CustomAttribute table we have already recorded the location of the TypeRef
1937
  // and MemberRef tables, so we can follow the chain back up from
1938
  // CustomAttribute through MemberRef to TypeRef.
1939
1940
0
  uint8_t* typeref_ptr = NULL;
1941
0
  uint8_t* memberref_ptr = NULL;
1942
0
  uint32_t typeref_row_size = 0;
1943
0
  uint32_t memberref_row_size = 0;
1944
0
  uint8_t* typeref_row = NULL;
1945
0
  uint8_t* memberref_row = NULL;
1946
1947
0
  DWORD type_index;
1948
0
  DWORD class_index;
1949
0
  BLOB_PARSE_RESULT blob_result;
1950
0
  DWORD blob_index;
1951
0
  DWORD blob_length;
1952
1953
  // These are used to determine the size of coded indexes, which are the
1954
  // dynamically sized columns for some tables. The coded indexes are
1955
  // documented in ECMA-335 Section II.24.2.6.
1956
0
  uint8_t index_size, index_size2;
1957
1958
  // Number of rows is the number of bits set to 1 in Valid.
1959
  // Should use this technique:
1960
  // http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetKernighan
1961
  // Count number of Rows size entries in header to skip over them
1962
0
  for (i = 0; i < 64; i++)
1963
0
    valid_rows += ((yr_le64toh(tilde_header->Valid) >> i) & 0x01);
1964
1965
0
  row_offset = (uint32_t*) (tilde_header + 1);
1966
0
  table_offset = (uint8_t*) row_offset;
1967
0
  table_offset += sizeof(uint32_t) * valid_rows;
1968
1969
  // Sometimes files have some sort of padding after, from DnSpy source
1970
  // it's denoted by EXTRA_DATA 0x40 flag in heapflags
1971
  // We then need to offset by 4 bytes, otherwise the analysis is wrong
1972
  // https://github.com/dnSpy/dnSpy/blob/2b6dcfaf602fb8ca6462b8b6237fdfc0c74ad994/dnSpy/dnSpy/Hex/Files/DotNet/TablesHeaderDataImpl.cs
1973
  // example: 1c2246af11000c3ce6b05ed6ba25060cbb00273c599428b98cf4013bdd82892f
1974
0
  if (tilde_header->HeapSizes & HEAP_EXTRA_DATA)
1975
0
    table_offset += 4;
1976
1977
0
#define DOTNET_STRING_INDEX(Name)                       \
1978
0
  index_sizes.string == 2 ? yr_le16toh(Name.Name_Short) \
1979
0
                          : yr_le32toh(Name.Name_Long)
1980
1981
0
  string_offset = pe->data + metadata_root +
1982
0
                  yr_le32toh(streams->string->Offset);
1983
1984
0
  str_heap_size = yr_le32toh(streams->string->Size);
1985
1986
  // Now walk again this time parsing out what we care about.
1987
0
  for (bit_check = 0; bit_check < 64; bit_check++)
1988
0
  {
1989
    // If the Valid bit is not set for this table, skip it...
1990
0
    if (!((yr_le64toh(tilde_header->Valid) >> bit_check) & 0x01))
1991
0
      continue;
1992
1993
0
    if (!fits_in_pe(pe, row_offset + matched_bits, sizeof(uint32_t)))
1994
0
      return;
1995
1996
0
    num_rows = yr_le32toh(*(row_offset + matched_bits));
1997
1998
    // Make sure that num_rows has a reasonable value. For example
1999
    // edc05e49dd3810be67942b983455fd43 sets a large value for number of
2000
    // rows for the BIT_MODULE section.
2001
0
    if (num_rows > 10000)
2002
0
      return;
2003
2004
    // Those tables which exist, but that we don't care about must be
2005
    // skipped.
2006
    //
2007
    // Sadly, given the dynamic sizes of some columns we can not have well
2008
    // defined structures for all tables and use them accordingly. To deal
2009
    // with this manually move the table_offset pointer by the appropriate
2010
    // number of bytes as described in the documentation for each table.
2011
    //
2012
    // The table structures are documented in ECMA-335 Section II.22.
2013
2014
0
    switch (bit_check)
2015
0
    {
2016
0
    case BIT_MODULE:
2017
0
      module_table = (PMODULE_TABLE) table_offset;
2018
2019
0
      if (!struct_fits_in_pe(pe, module_table, MODULE_TABLE))
2020
0
        break;
2021
2022
0
      name = pe_get_dotnet_string(
2023
0
          pe,
2024
0
          string_offset,
2025
0
          str_heap_size,
2026
0
          DOTNET_STRING_INDEX(module_table->Name));
2027
2028
0
      if (name != NULL)
2029
0
        yr_set_string(name, pe->object, "module_name");
2030
2031
0
      row_size = 2 + index_sizes.string + (index_sizes.guid * 3);
2032
2033
0
      tables.module.Offset = table_offset;
2034
0
      tables.module.RowCount = num_rows;
2035
0
      tables.module.RowSize = row_size;
2036
2037
0
      table_offset += row_size * num_rows;
2038
0
      break;
2039
2040
0
    case BIT_TYPEREF:
2041
0
      row_count = max_rows(
2042
0
          4,
2043
0
          yr_le32toh(rows.module),
2044
0
          yr_le32toh(rows.moduleref),
2045
0
          yr_le32toh(rows.assemblyref),
2046
0
          yr_le32toh(rows.typeref));
2047
2048
0
      if (row_count > (0xFFFF >> 0x02))
2049
0
        index_size = 4;
2050
0
      else
2051
0
        index_size = 2;
2052
2053
0
      row_size = (index_size + (index_sizes.string * 2));
2054
0
      typeref_row_size = row_size;
2055
0
      typeref_ptr = table_offset;
2056
2057
0
      tables.typeref.Offset = table_offset;
2058
0
      tables.typeref.RowCount = num_rows;
2059
0
      tables.typeref.RowSize = row_size;
2060
2061
0
      table_offset += row_size * num_rows;
2062
0
      break;
2063
2064
0
    case BIT_TYPEDEF:
2065
0
      row_count = max_rows(
2066
0
          3,
2067
0
          yr_le32toh(rows.typedef_),
2068
0
          yr_le32toh(rows.typeref),
2069
0
          yr_le32toh(rows.typespec));
2070
2071
0
      if (row_count > (0xFFFF >> 0x02))
2072
0
        index_size = 4;
2073
0
      else
2074
0
        index_size = 2;
2075
2076
0
      row_size = 4 + (index_sizes.string * 2) + index_size + index_sizes.field +
2077
0
                 index_sizes.methoddef;
2078
2079
0
      tables.typedef_.Offset = table_offset;
2080
0
      tables.typedef_.RowCount = num_rows;
2081
0
      tables.typedef_.RowSize = row_size;
2082
2083
0
      table_offset += row_size * num_rows;
2084
0
      break;
2085
2086
0
    case BIT_FIELDPTR:
2087
      // This one is not documented in ECMA-335.
2088
0
      table_offset += (index_sizes.field) * num_rows;
2089
0
      break;
2090
2091
0
    case BIT_FIELD:
2092
0
      table_offset += (2 + (index_sizes.string) + index_sizes.blob) * num_rows;
2093
0
      break;
2094
2095
0
    case BIT_METHODDEFPTR:
2096
      // This one is not documented in ECMA-335.
2097
0
      table_offset += (index_sizes.methoddef) * num_rows;
2098
0
      break;
2099
2100
0
    case BIT_METHODDEF:
2101
0
      row_size = 4 + 2 + 2 + index_sizes.string + index_sizes.blob +
2102
0
                 index_sizes.param;
2103
2104
0
      tables.methoddef.Offset = table_offset;
2105
0
      tables.methoddef.RowCount = num_rows;
2106
0
      tables.methoddef.RowSize = row_size;
2107
0
      table_offset += row_size * num_rows;
2108
0
      break;
2109
2110
0
    case BIT_PARAM:
2111
0
      row_size = 2 + 2 + index_sizes.string;
2112
2113
0
      tables.param.Offset = table_offset;
2114
0
      tables.param.RowCount = num_rows;
2115
0
      tables.param.RowSize = row_size;
2116
2117
0
      table_offset += row_size * num_rows;
2118
0
      break;
2119
2120
0
    case BIT_INTERFACEIMPL:
2121
0
      row_count = max_rows(
2122
0
          3,
2123
0
          yr_le32toh(rows.typedef_),
2124
0
          yr_le32toh(rows.typeref),
2125
0
          yr_le32toh(rows.typespec));
2126
2127
0
      if (row_count > (0xFFFF >> 0x02))
2128
0
        index_size = 4;
2129
0
      else
2130
0
        index_size = 2;
2131
2132
0
      row_size = index_sizes.typedef_ + index_size;
2133
2134
0
      tables.intefaceimpl.Offset = table_offset;
2135
0
      tables.intefaceimpl.RowCount = num_rows;
2136
0
      tables.intefaceimpl.RowSize = row_size;
2137
2138
0
      table_offset += row_size * num_rows;
2139
0
      break;
2140
2141
0
    case BIT_MEMBERREF:
2142
0
      row_count = max_rows(
2143
0
          4,
2144
0
          yr_le32toh(rows.methoddef),
2145
0
          yr_le32toh(rows.moduleref),
2146
0
          yr_le32toh(rows.typeref),
2147
0
          yr_le32toh(rows.typespec));
2148
2149
0
      if (row_count > (0xFFFF >> 0x03))
2150
0
        index_size = 4;
2151
0
      else
2152
0
        index_size = 2;
2153
2154
0
      row_size = (index_size + index_sizes.string + index_sizes.blob);
2155
0
      memberref_row_size = row_size;
2156
0
      memberref_ptr = table_offset;
2157
0
      table_offset += row_size * num_rows;
2158
0
      break;
2159
2160
0
    case BIT_CONSTANT:
2161
0
      row_count = max_rows(
2162
0
          3,
2163
0
          yr_le32toh(rows.param),
2164
0
          yr_le32toh(rows.field),
2165
0
          yr_le32toh(rows.property));
2166
2167
0
      if (row_count > (0xFFFF >> 0x02))
2168
0
        index_size = 4;
2169
0
      else
2170
0
        index_size = 2;
2171
2172
      // Using 'i' is insufficent since we may skip certain constants and
2173
      // it would give an inaccurate count in that case.
2174
0
      counter = 0;
2175
0
      row_size = (1 + 1 + index_size + index_sizes.blob);
2176
0
      row_ptr = table_offset;
2177
2178
0
      for (i = 0; i < num_rows; i++)
2179
0
      {
2180
0
        if (!fits_in_pe(pe, row_ptr, row_size))
2181
0
          break;
2182
2183
0
        constant_table = (PCONSTANT_TABLE) row_ptr;
2184
2185
        // Only look for constants of type string.
2186
0
        if (yr_le32toh(constant_table->Type) != TYPE_STRING)
2187
0
        {
2188
0
          row_ptr += row_size;
2189
0
          continue;
2190
0
        }
2191
2192
        // Get the blob offset and pull it out of the blob table.
2193
0
        blob_offset = ((uint8_t*) constant_table) + 2 + index_size;
2194
2195
0
        if (index_sizes.blob == 4)
2196
0
          blob_index = *(DWORD*) blob_offset;
2197
0
        else
2198
          // Cast the value (index into blob table) to a 32bit value.
2199
0
          blob_index = (DWORD) (*(WORD*) blob_offset);
2200
2201
        // Everything checks out. Make sure the index into the blob field
2202
        // is valid (non-null and within range).
2203
0
        blob_offset = pe->data + metadata_root +
2204
0
                      yr_le32toh(streams->blob->Offset) + blob_index;
2205
2206
0
        blob_result = dotnet_parse_blob_entry(pe, blob_offset);
2207
2208
0
        if (blob_result.size == 0)
2209
0
        {
2210
0
          row_ptr += row_size;
2211
0
          continue;
2212
0
        }
2213
2214
0
        blob_length = blob_result.length;
2215
0
        blob_offset += blob_result.size;
2216
2217
        // Quick sanity check to make sure the blob entry is within bounds.
2218
0
        if (blob_offset + blob_length >= pe->data + pe->data_size)
2219
0
        {
2220
0
          row_ptr += row_size;
2221
0
          continue;
2222
0
        }
2223
2224
0
        yr_set_sized_string(
2225
0
            (char*) blob_offset,
2226
0
            blob_result.length,
2227
0
            pe->object,
2228
0
            "constants[%i]",
2229
0
            counter);
2230
2231
0
        counter++;
2232
0
        row_ptr += row_size;
2233
0
      }
2234
2235
0
      yr_set_integer(counter, pe->object, "number_of_constants");
2236
0
      table_offset += row_size * num_rows;
2237
0
      break;
2238
2239
0
    case BIT_CUSTOMATTRIBUTE:
2240
      // index_size is size of the parent column.
2241
0
      row_count = max_rows(
2242
0
          21,
2243
0
          yr_le32toh(rows.methoddef),
2244
0
          yr_le32toh(rows.field),
2245
0
          yr_le32toh(rows.typeref),
2246
0
          yr_le32toh(rows.typedef_),
2247
0
          yr_le32toh(rows.param),
2248
0
          yr_le32toh(rows.interfaceimpl),
2249
0
          yr_le32toh(rows.memberref),
2250
0
          yr_le32toh(rows.module),
2251
0
          yr_le32toh(rows.property),
2252
0
          yr_le32toh(rows.event),
2253
0
          yr_le32toh(rows.standalonesig),
2254
0
          yr_le32toh(rows.moduleref),
2255
0
          yr_le32toh(rows.typespec),
2256
0
          yr_le32toh(rows.assembly),
2257
0
          yr_le32toh(rows.assemblyref),
2258
0
          yr_le32toh(rows.file),
2259
0
          yr_le32toh(rows.exportedtype),
2260
0
          yr_le32toh(rows.manifestresource),
2261
0
          yr_le32toh(rows.genericparam),
2262
0
          yr_le32toh(rows.genericparamconstraint),
2263
0
          yr_le32toh(rows.methodspec));
2264
2265
0
      if (row_count > (0xFFFF >> 0x05))
2266
0
        index_size = 4;
2267
0
      else
2268
0
        index_size = 2;
2269
2270
      // index_size2 is size of the type column.
2271
0
      row_count = max_rows(
2272
0
          2, yr_le32toh(rows.methoddef), yr_le32toh(rows.memberref));
2273
2274
0
      if (row_count > (0xFFFF >> 0x03))
2275
0
        index_size2 = 4;
2276
0
      else
2277
0
        index_size2 = 2;
2278
2279
0
      row_size = (index_size + index_size2 + index_sizes.blob);
2280
2281
0
      if (typeref_ptr != NULL && memberref_ptr != NULL)
2282
0
      {
2283
0
        row_ptr = table_offset;
2284
2285
0
        for (i = 0; i < num_rows; i++)
2286
0
        {
2287
0
          if (!fits_in_pe(pe, row_ptr, row_size))
2288
0
            break;
2289
2290
          // Check the Parent field.
2291
0
          customattribute_table = (PCUSTOMATTRIBUTE_TABLE) row_ptr;
2292
2293
0
          if (index_size == 4)
2294
0
          {
2295
            // Low 5 bits tell us what this is an index into. Remaining bits
2296
            // tell us the index value.
2297
            // Parent must be an index into the Assembly (0x0E) table.
2298
0
            if ((*(DWORD*) customattribute_table & 0x1F) != 0x0E)
2299
0
            {
2300
0
              row_ptr += row_size;
2301
0
              continue;
2302
0
            }
2303
0
          }
2304
0
          else
2305
0
          {
2306
            // Low 5 bits tell us what this is an index into. Remaining bits
2307
            // tell us the index value.
2308
            // Parent must be an index into the Assembly (0x0E) table.
2309
0
            if ((*(WORD*) customattribute_table & 0x1F) != 0x0E)
2310
0
            {
2311
0
              row_ptr += row_size;
2312
0
              continue;
2313
0
            }
2314
0
          }
2315
2316
          // Check the Type field.
2317
0
          customattribute_table = (PCUSTOMATTRIBUTE_TABLE) (row_ptr +
2318
0
                                                            index_size);
2319
2320
0
          if (index_size2 == 4)
2321
0
          {
2322
            // Low 3 bits tell us what this is an index into. Remaining bits
2323
            // tell us the index value. Only values 2 and 3 are defined.
2324
            // Type must be an index into the MemberRef table.
2325
0
            if ((*(DWORD*) customattribute_table & 0x07) != 0x03)
2326
0
            {
2327
0
              row_ptr += row_size;
2328
0
              continue;
2329
0
            }
2330
2331
0
            type_index = *(DWORD*) customattribute_table >> 3;
2332
0
          }
2333
0
          else
2334
0
          {
2335
            // Low 3 bits tell us what this is an index into. Remaining bits
2336
            // tell us the index value. Only values 2 and 3 are defined.
2337
            // Type must be an index into the MemberRef table.
2338
0
            if ((*(WORD*) customattribute_table & 0x07) != 0x03)
2339
0
            {
2340
0
              row_ptr += row_size;
2341
0
              continue;
2342
0
            }
2343
2344
            // Cast the index to a 32bit value.
2345
0
            type_index = (DWORD) ((*(WORD*) customattribute_table >> 3));
2346
0
          }
2347
2348
0
          if (type_index > 0)
2349
0
            type_index--;
2350
2351
          // Now follow the Type index into the MemberRef table.
2352
0
          memberref_row = memberref_ptr + (memberref_row_size * type_index);
2353
2354
0
          if (!fits_in_pe(pe, memberref_row, memberref_row_size))
2355
0
            break;
2356
2357
0
          if (index_sizes.memberref == 4)
2358
0
          {
2359
            // Low 3 bits tell us what this is an index into. Remaining bits
2360
            // tell us the index value. Class must be an index into the
2361
            // TypeRef table.
2362
0
            if ((*(DWORD*) memberref_row & 0x07) != 0x01)
2363
0
            {
2364
0
              row_ptr += row_size;
2365
0
              continue;
2366
0
            }
2367
2368
0
            class_index = *(DWORD*) memberref_row >> 3;
2369
0
          }
2370
0
          else
2371
0
          {
2372
            // Low 3 bits tell us what this is an index into. Remaining bits
2373
            // tell us the index value. Class must be an index into the
2374
            // TypeRef table.
2375
0
            if ((*(WORD*) memberref_row & 0x07) != 0x01)
2376
0
            {
2377
0
              row_ptr += row_size;
2378
0
              continue;
2379
0
            }
2380
2381
            // Cast the index to a 32bit value.
2382
0
            class_index = (DWORD) (*(WORD*) memberref_row >> 3);
2383
0
          }
2384
2385
0
          if (class_index > 0)
2386
0
            class_index--;
2387
2388
          // Now follow the Class index into the TypeRef table.
2389
0
          typeref_row = typeref_ptr + (typeref_row_size * class_index);
2390
2391
0
          if (!fits_in_pe(pe, typeref_row, typeref_row_size))
2392
0
            break;
2393
2394
          // Skip over the ResolutionScope and check the Name field,
2395
          // which is an index into the Strings heap.
2396
0
          row_count = max_rows(
2397
0
              4,
2398
0
              yr_le32toh(rows.module),
2399
0
              yr_le32toh(rows.moduleref),
2400
0
              yr_le32toh(rows.assemblyref),
2401
0
              yr_le32toh(rows.typeref));
2402
2403
0
          if (row_count > (0xFFFF >> 0x02))
2404
0
            typeref_row += 4;
2405
0
          else
2406
0
            typeref_row += 2;
2407
2408
0
          if (index_sizes.string == 4)
2409
0
          {
2410
0
            name = pe_get_dotnet_string(
2411
0
                pe, string_offset, str_heap_size, *(DWORD*) typeref_row);
2412
0
          }
2413
0
          else
2414
0
          {
2415
0
            name = pe_get_dotnet_string(
2416
0
                pe, string_offset, str_heap_size, *(WORD*) typeref_row);
2417
0
          }
2418
2419
0
          if (name != NULL && strncmp(name, "GuidAttribute", 13) != 0)
2420
0
          {
2421
0
            row_ptr += row_size;
2422
0
            continue;
2423
0
          }
2424
2425
          // Get the Value field.
2426
0
          customattribute_table = (PCUSTOMATTRIBUTE_TABLE) (row_ptr +
2427
0
                                                            index_size +
2428
0
                                                            index_size2);
2429
2430
0
          if (index_sizes.blob == 4)
2431
0
            blob_index = *(DWORD*) customattribute_table;
2432
0
          else
2433
            // Cast the value (index into blob table) to a 32bit value.
2434
0
            blob_index = (DWORD) (*(WORD*) customattribute_table);
2435
2436
          // Everything checks out. Make sure the index into the blob field
2437
          // is valid (non-null and within range).
2438
0
          blob_offset = pe->data + metadata_root +
2439
0
                        yr_le32toh(streams->blob->Offset) + blob_index;
2440
2441
          // If index into blob is 0 or past the end of the blob stream, skip
2442
          // it. We don't know the size of the blob entry yet because that is
2443
          // encoded in the start.
2444
0
          if (blob_index == 0x00 || blob_offset >= pe->data + pe->data_size)
2445
0
          {
2446
0
            row_ptr += row_size;
2447
0
            continue;
2448
0
          }
2449
2450
0
          blob_result = dotnet_parse_blob_entry(pe, blob_offset);
2451
2452
0
          if (blob_result.size == 0)
2453
0
          {
2454
0
            row_ptr += row_size;
2455
0
            continue;
2456
0
          }
2457
2458
0
          blob_length = blob_result.length;
2459
0
          blob_offset += blob_result.size;
2460
2461
          // Quick sanity check to make sure the blob entry is within bounds
2462
          // and its length is at least 3 (2 bytes for the 16 bits prolog and
2463
          // 1 byte for the string length)
2464
0
          if (blob_length < 3 ||
2465
0
              blob_offset + blob_length >= pe->data + pe->data_size)
2466
0
          {
2467
0
            row_ptr += row_size;
2468
0
            continue;
2469
0
          }
2470
2471
          // Custom attributes MUST have a 16 bit prolog of 0x0001
2472
0
          if (*(WORD*) blob_offset != 0x0001)
2473
0
          {
2474
0
            row_ptr += row_size;
2475
0
            continue;
2476
0
          }
2477
2478
          // The next byte after the 16 bit prolog is the length of the string.
2479
0
          blob_offset += 2;
2480
0
          uint8_t str_len = *blob_offset;
2481
2482
          // Increment blob_offset so that it points to the first byte of the
2483
          // string.
2484
0
          blob_offset += 1;
2485
2486
0
          if (blob_offset + str_len > pe->data + pe->data_size)
2487
0
          {
2488
0
            row_ptr += row_size;
2489
0
            continue;
2490
0
          }
2491
2492
0
          if (*blob_offset == 0xFF || *blob_offset == 0x00)
2493
0
          {
2494
0
            typelib[0] = '\0';
2495
0
          }
2496
0
          else
2497
0
          {
2498
0
            strncpy(typelib, (char*) blob_offset, str_len);
2499
0
            typelib[str_len] = '\0';
2500
0
          }
2501
2502
0
          yr_set_string(typelib, pe->object, "typelib");
2503
2504
0
          row_ptr += row_size;
2505
0
        }
2506
0
      }
2507
2508
0
      table_offset += row_size * num_rows;
2509
0
      break;
2510
2511
0
    case BIT_FIELDMARSHAL:
2512
0
      row_count = max_rows(2, yr_le32toh(rows.field), yr_le32toh(rows.param));
2513
2514
0
      if (row_count > (0xFFFF >> 0x01))
2515
0
        index_size = 4;
2516
0
      else
2517
0
        index_size = 2;
2518
2519
0
      table_offset += (index_size + index_sizes.blob) * num_rows;
2520
0
      break;
2521
2522
0
    case BIT_DECLSECURITY:
2523
0
      row_count = max_rows(
2524
0
          3,
2525
0
          yr_le32toh(rows.typedef_),
2526
0
          yr_le32toh(rows.methoddef),
2527
0
          yr_le32toh(rows.assembly));
2528
2529
0
      if (row_count > (0xFFFF >> 0x02))
2530
0
        index_size = 4;
2531
0
      else
2532
0
        index_size = 2;
2533
2534
0
      table_offset += (2 + index_size + index_sizes.blob) * num_rows;
2535
0
      break;
2536
2537
0
    case BIT_CLASSLAYOUT:
2538
0
      table_offset += (2 + 4 + index_sizes.typedef_) * num_rows;
2539
0
      break;
2540
2541
0
    case BIT_FIELDLAYOUT:
2542
0
      table_offset += (4 + index_sizes.field) * num_rows;
2543
0
      break;
2544
2545
0
    case BIT_STANDALONESIG:
2546
0
      table_offset += (index_sizes.blob) * num_rows;
2547
0
      break;
2548
2549
0
    case BIT_EVENTMAP:
2550
0
      table_offset += (index_sizes.typedef_ + index_sizes.event) * num_rows;
2551
0
      break;
2552
2553
0
    case BIT_EVENTPTR:
2554
      // This one is not documented in ECMA-335.
2555
0
      table_offset += (index_sizes.event) * num_rows;
2556
0
      break;
2557
2558
0
    case BIT_EVENT:
2559
0
      row_count = max_rows(
2560
0
          3,
2561
0
          yr_le32toh(rows.typedef_),
2562
0
          yr_le32toh(rows.typeref),
2563
0
          yr_le32toh(rows.typespec));
2564
2565
0
      if (row_count > (0xFFFF >> 0x02))
2566
0
        index_size = 4;
2567
0
      else
2568
0
        index_size = 2;
2569
2570
0
      table_offset += (2 + index_sizes.string + index_size) * num_rows;
2571
0
      break;
2572
2573
0
    case BIT_PROPERTYMAP:
2574
0
      table_offset += (index_sizes.typedef_ + index_sizes.property) * num_rows;
2575
0
      break;
2576
2577
0
    case BIT_PROPERTYPTR:
2578
      // This one is not documented in ECMA-335.
2579
0
      table_offset += (index_sizes.property) * num_rows;
2580
0
      break;
2581
2582
0
    case BIT_PROPERTY:
2583
0
      table_offset += (2 + index_sizes.string + index_sizes.blob) * num_rows;
2584
0
      break;
2585
2586
0
    case BIT_METHODSEMANTICS:
2587
0
      row_count = max_rows(
2588
0
          2, yr_le32toh(rows.event), yr_le32toh(rows.property));
2589
2590
0
      if (row_count > (0xFFFF >> 0x01))
2591
0
        index_size = 4;
2592
0
      else
2593
0
        index_size = 2;
2594
2595
0
      table_offset += (2 + index_sizes.methoddef + index_size) * num_rows;
2596
0
      break;
2597
2598
0
    case BIT_METHODIMPL:
2599
0
      row_count = max_rows(
2600
0
          2, yr_le32toh(rows.methoddef), yr_le32toh(rows.memberref));
2601
2602
0
      if (row_count > (0xFFFF >> 0x01))
2603
0
        index_size = 4;
2604
0
      else
2605
0
        index_size = 2;
2606
2607
0
      table_offset += (index_sizes.typedef_ + (index_size * 2)) * num_rows;
2608
0
      break;
2609
2610
0
    case BIT_MODULEREF:
2611
0
      row_ptr = table_offset;
2612
2613
      // Can't use 'i' here because we only set the string if it is not
2614
      // NULL. Instead use 'counter'.
2615
0
      counter = 0;
2616
2617
0
      for (i = 0; i < num_rows; i++)
2618
0
      {
2619
0
        moduleref_table = (PMODULEREF_TABLE) row_ptr;
2620
2621
0
        if (!struct_fits_in_pe(pe, moduleref_table, MODULEREF_TABLE))
2622
0
          break;
2623
2624
0
        name = pe_get_dotnet_string(
2625
0
            pe,
2626
0
            string_offset,
2627
0
            str_heap_size,
2628
0
            DOTNET_STRING_INDEX(moduleref_table->Name));
2629
2630
0
        if (name != NULL)
2631
0
        {
2632
0
          yr_set_string(name, pe->object, "modulerefs[%i]", counter);
2633
0
          counter++;
2634
0
        }
2635
2636
0
        row_ptr += index_sizes.string;
2637
0
      }
2638
2639
0
      yr_set_integer(counter, pe->object, "number_of_modulerefs");
2640
2641
0
      row_size = index_sizes.string;
2642
2643
0
      tables.moduleref.Offset = table_offset;
2644
0
      tables.moduleref.RowCount = num_rows;
2645
0
      tables.moduleref.RowSize = row_size;
2646
2647
0
      table_offset += row_size * num_rows;
2648
0
      break;
2649
2650
0
    case BIT_TYPESPEC:
2651
0
      row_size = index_sizes.blob;
2652
2653
0
      tables.typespec.Offset = table_offset;
2654
0
      tables.typespec.RowCount = num_rows;
2655
0
      tables.typespec.RowSize = row_size;
2656
2657
0
      table_offset += row_size * num_rows;
2658
0
      break;
2659
2660
0
    case BIT_IMPLMAP:
2661
0
      row_count = max_rows(
2662
0
          2, yr_le32toh(rows.field), yr_le32toh(rows.methoddef));
2663
2664
0
      if (row_count > (0xFFFF >> 0x01))
2665
0
        index_size = 4;
2666
0
      else
2667
0
        index_size = 2;
2668
2669
0
      table_offset += (2 + index_size + index_sizes.string +
2670
0
                       index_sizes.moduleref) *
2671
0
                      num_rows;
2672
0
      break;
2673
2674
0
    case BIT_FIELDRVA:
2675
0
      row_size = 4 + index_sizes.field;
2676
0
      row_ptr = table_offset;
2677
2678
      // Can't use 'i' here because we only set the field offset if it is
2679
      // valid. Instead use 'counter'.
2680
0
      counter = 0;
2681
2682
0
      for (i = 0; i < num_rows; i++)
2683
0
      {
2684
0
        fieldrva_table = (PFIELDRVA_TABLE) row_ptr;
2685
2686
0
        if (!struct_fits_in_pe(pe, fieldrva_table, FIELDRVA_TABLE))
2687
0
          break;
2688
2689
0
        field_offset = pe_rva_to_offset(pe, fieldrva_table->RVA);
2690
2691
0
        if (field_offset >= 0)
2692
0
        {
2693
0
          yr_set_integer(
2694
0
              field_offset, pe->object, "field_offsets[%i]", counter);
2695
0
          counter++;
2696
0
        }
2697
2698
0
        row_ptr += row_size;
2699
0
      }
2700
2701
0
      yr_set_integer(counter, pe->object, "number_of_field_offsets");
2702
2703
0
      table_offset += row_size * num_rows;
2704
0
      break;
2705
2706
0
    case BIT_ENCLOG:
2707
0
      table_offset += (4 + 4) * num_rows;
2708
0
      break;
2709
2710
0
    case BIT_ENCMAP:
2711
0
      table_offset += (4) * num_rows;
2712
0
      break;
2713
2714
0
    case BIT_ASSEMBLY:
2715
0
      row_size =
2716
0
          (4 + 2 + 2 + 2 + 2 + 4 + index_sizes.blob + (index_sizes.string * 2));
2717
2718
0
      if (!fits_in_pe(pe, table_offset, row_size))
2719
0
        break;
2720
2721
0
      row_ptr = table_offset;
2722
0
      assembly_table = (PASSEMBLY_TABLE) table_offset;
2723
2724
0
      yr_set_integer(
2725
0
          yr_le16toh(assembly_table->MajorVersion),
2726
0
          pe->object,
2727
0
          "assembly.version.major");
2728
0
      yr_set_integer(
2729
0
          yr_le16toh(assembly_table->MinorVersion),
2730
0
          pe->object,
2731
0
          "assembly.version.minor");
2732
0
      yr_set_integer(
2733
0
          yr_le16toh(assembly_table->BuildNumber),
2734
0
          pe->object,
2735
0
          "assembly.version.build_number");
2736
0
      yr_set_integer(
2737
0
          yr_le16toh(assembly_table->RevisionNumber),
2738
0
          pe->object,
2739
0
          "assembly.version.revision_number");
2740
2741
      // Can't use assembly_table here because the PublicKey comes before
2742
      // Name and is a variable length field.
2743
2744
0
      if (index_sizes.string == 4)
2745
0
        name = pe_get_dotnet_string(
2746
0
            pe,
2747
0
            string_offset,
2748
0
            str_heap_size,
2749
0
            yr_le32toh(*(DWORD*) (row_ptr + 4 + 2 + 2 + 2 + 2 + 4 +
2750
0
                                  index_sizes.blob)));
2751
0
      else
2752
0
        name = pe_get_dotnet_string(
2753
0
            pe,
2754
0
            string_offset,
2755
0
            str_heap_size,
2756
0
            yr_le16toh(
2757
0
                *(WORD*) (row_ptr + 4 + 2 + 2 + 2 + 2 + 4 + index_sizes.blob)));
2758
2759
0
      if (name != NULL)
2760
0
        yr_set_string(name, pe->object, "assembly.name");
2761
2762
      // Culture comes after Name.
2763
0
      if (index_sizes.string == 4)
2764
0
      {
2765
0
        name = pe_get_dotnet_string(
2766
0
            pe,
2767
0
            string_offset,
2768
0
            str_heap_size,
2769
0
            yr_le32toh(*(DWORD*) (row_ptr + 4 + 2 + 2 + 2 + 2 + 4 +
2770
0
                                  index_sizes.blob + index_sizes.string)));
2771
0
      }
2772
0
      else
2773
0
      {
2774
0
        name = pe_get_dotnet_string(
2775
0
            pe,
2776
0
            string_offset,
2777
0
            str_heap_size,
2778
0
            yr_le16toh(*(WORD*) (row_ptr + 4 + 2 + 2 + 2 + 2 + 4 +
2779
0
                                 index_sizes.blob + index_sizes.string)));
2780
0
      }
2781
2782
      // Sometimes it will be a zero length string. This is technically
2783
      // against the specification but happens from time to time.
2784
0
      if (name != NULL && strlen(name) > 0)
2785
0
        yr_set_string(name, pe->object, "assembly.culture");
2786
2787
0
      table_offset += row_size * num_rows;
2788
0
      break;
2789
2790
0
    case BIT_ASSEMBLYPROCESSOR:
2791
0
      table_offset += (4) * num_rows;
2792
0
      break;
2793
2794
0
    case BIT_ASSEMBLYOS:
2795
0
      table_offset += (4 + 4 + 4) * num_rows;
2796
0
      break;
2797
2798
0
    case BIT_ASSEMBLYREF:
2799
0
      row_size =
2800
0
          (2 + 2 + 2 + 2 + 4 + (index_sizes.blob * 2) +
2801
0
           (index_sizes.string * 2));
2802
2803
0
      row_ptr = table_offset;
2804
2805
0
      for (i = 0; i < num_rows; i++)
2806
0
      {
2807
0
        if (!fits_in_pe(pe, row_ptr, row_size))
2808
0
          break;
2809
2810
0
        assemblyref_table = (PASSEMBLYREF_TABLE) row_ptr;
2811
2812
0
        yr_set_integer(
2813
0
            yr_le16toh(assemblyref_table->MajorVersion),
2814
0
            pe->object,
2815
0
            "assembly_refs[%i].version.major",
2816
0
            i);
2817
0
        yr_set_integer(
2818
0
            yr_le16toh(assemblyref_table->MinorVersion),
2819
0
            pe->object,
2820
0
            "assembly_refs[%i].version.minor",
2821
0
            i);
2822
0
        yr_set_integer(
2823
0
            yr_le16toh(assemblyref_table->BuildNumber),
2824
0
            pe->object,
2825
0
            "assembly_refs[%i].version.build_number",
2826
0
            i);
2827
0
        yr_set_integer(
2828
0
            yr_le16toh(assemblyref_table->RevisionNumber),
2829
0
            pe->object,
2830
0
            "assembly_refs[%i].version.revision_number",
2831
0
            i);
2832
2833
0
        blob_offset = pe->data + metadata_root +
2834
0
                      yr_le32toh(streams->blob->Offset);
2835
2836
0
        if (index_sizes.blob == 4)
2837
0
          blob_offset += yr_le32toh(
2838
0
              assemblyref_table->PublicKeyOrToken.PublicKeyOrToken_Long);
2839
0
        else
2840
0
          blob_offset += yr_le16toh(
2841
0
              assemblyref_table->PublicKeyOrToken.PublicKeyOrToken_Short);
2842
2843
0
        blob_result = dotnet_parse_blob_entry(pe, blob_offset);
2844
0
        blob_offset += blob_result.size;
2845
2846
0
        if (blob_result.size == 0 ||
2847
0
            !fits_in_pe(pe, blob_offset, blob_result.length))
2848
0
        {
2849
0
          row_ptr += row_size;
2850
0
          continue;
2851
0
        }
2852
2853
        // Avoid empty strings.
2854
0
        if (blob_result.length > 0)
2855
0
        {
2856
0
          yr_set_sized_string(
2857
0
              (char*) blob_offset,
2858
0
              blob_result.length,
2859
0
              pe->object,
2860
0
              "assembly_refs[%i].public_key_or_token",
2861
0
              i);
2862
0
        }
2863
2864
        // Can't use assemblyref_table here because the PublicKey comes before
2865
        // Name and is a variable length field.
2866
2867
0
        if (index_sizes.string == 4)
2868
0
          name = pe_get_dotnet_string(
2869
0
              pe,
2870
0
              string_offset,
2871
0
              str_heap_size,
2872
0
              yr_le32toh(
2873
0
                  *(DWORD*) (row_ptr + 2 + 2 + 2 + 2 + 4 + index_sizes.blob)));
2874
0
        else
2875
0
          name = pe_get_dotnet_string(
2876
0
              pe,
2877
0
              string_offset,
2878
0
              str_heap_size,
2879
0
              yr_le16toh(
2880
0
                  *(WORD*) (row_ptr + 2 + 2 + 2 + 2 + 4 + index_sizes.blob)));
2881
2882
0
        if (name != NULL)
2883
0
          yr_set_string(name, pe->object, "assembly_refs[%i].name", i);
2884
2885
0
        row_ptr += row_size;
2886
0
      }
2887
2888
0
      tables.assemblyref.Offset = table_offset;
2889
0
      tables.assemblyref.RowCount = num_rows;
2890
0
      tables.assemblyref.RowSize = row_size;
2891
2892
0
      yr_set_integer(i, pe->object, "number_of_assembly_refs");
2893
0
      table_offset += row_size * num_rows;
2894
0
      break;
2895
2896
0
    case BIT_ASSEMBLYREFPROCESSOR:
2897
0
      table_offset += (4 + index_sizes.assemblyrefprocessor) * num_rows;
2898
0
      break;
2899
2900
0
    case BIT_ASSEMBLYREFOS:
2901
0
      table_offset += (4 + 4 + 4 + index_sizes.assemblyref) * num_rows;
2902
0
      break;
2903
2904
0
    case BIT_FILE:
2905
0
      table_offset += (4 + index_sizes.string + index_sizes.blob) * num_rows;
2906
0
      break;
2907
2908
0
    case BIT_EXPORTEDTYPE:
2909
0
      row_count = max_rows(
2910
0
          3,
2911
0
          yr_le32toh(rows.file),
2912
0
          yr_le32toh(rows.assemblyref),
2913
0
          yr_le32toh(rows.exportedtype));
2914
2915
0
      if (row_count > (0xFFFF >> 0x02))
2916
0
        index_size = 4;
2917
0
      else
2918
0
        index_size = 2;
2919
2920
0
      table_offset += (4 + 4 + (index_sizes.string * 2) + index_size) *
2921
0
                      num_rows;
2922
0
      break;
2923
2924
0
    case BIT_MANIFESTRESOURCE:
2925
      // This is an Implementation coded index with no 3rd bit specified.
2926
0
      row_count = max_rows(
2927
0
          2, yr_le32toh(rows.file), yr_le32toh(rows.assemblyref));
2928
2929
0
      if (row_count > (0xFFFF >> 0x02))
2930
0
        index_size = 4;
2931
0
      else
2932
0
        index_size = 2;
2933
2934
0
      row_size = (4 + 4 + index_sizes.string + index_size);
2935
2936
      // Using 'i' is insufficent since we may skip certain resources and
2937
      // it would give an inaccurate count in that case.
2938
0
      counter = 0;
2939
0
      row_ptr = table_offset;
2940
2941
      // First DWORD is the offset.
2942
0
      for (i = 0; i < num_rows; i++)
2943
0
      {
2944
0
        if (!fits_in_pe(pe, row_ptr, row_size))
2945
0
          break;
2946
2947
0
        manifestresource_table = (PMANIFESTRESOURCE_TABLE) row_ptr;
2948
0
        resource_offset = yr_le32toh(manifestresource_table->Offset);
2949
2950
        // Only set offset if it is in this file (implementation != 0).
2951
        // Can't use manifestresource_table here because the Name and
2952
        // Implementation fields are variable size.
2953
0
        if (index_size == 4)
2954
0
          implementation = yr_le32toh(
2955
0
              *(DWORD*) (row_ptr + 4 + 4 + index_sizes.string));
2956
0
        else
2957
0
          implementation = yr_le16toh(
2958
0
              *(WORD*) (row_ptr + 4 + 4 + index_sizes.string));
2959
2960
0
        if (implementation != 0)
2961
0
        {
2962
0
          row_ptr += row_size;
2963
0
          continue;
2964
0
        }
2965
2966
0
        if (!fits_in_pe(
2967
0
                pe, pe->data + resource_base + resource_offset, sizeof(DWORD)))
2968
0
        {
2969
0
          row_ptr += row_size;
2970
0
          continue;
2971
0
        }
2972
2973
0
        resource_size = yr_le32toh(
2974
0
            *(DWORD*) (pe->data + resource_base + resource_offset));
2975
2976
0
        if (!fits_in_pe(
2977
0
                pe, pe->data + resource_base + resource_offset, resource_size))
2978
0
        {
2979
0
          row_ptr += row_size;
2980
0
          continue;
2981
0
        }
2982
2983
        // Add 4 to skip the size.
2984
0
        yr_set_integer(
2985
0
            resource_base + resource_offset + 4,
2986
0
            pe->object,
2987
0
            "resources[%i].offset",
2988
0
            counter);
2989
2990
0
        yr_set_integer(
2991
0
            resource_size, pe->object, "resources[%i].length", counter);
2992
2993
0
        name = pe_get_dotnet_string(
2994
0
            pe,
2995
0
            string_offset,
2996
0
            str_heap_size,
2997
0
            DOTNET_STRING_INDEX(manifestresource_table->Name));
2998
2999
0
        if (name != NULL)
3000
0
          yr_set_string(name, pe->object, "resources[%i].name", counter);
3001
3002
0
        row_ptr += row_size;
3003
0
        counter++;
3004
0
      }
3005
3006
0
      yr_set_integer(counter, pe->object, "number_of_resources");
3007
3008
0
      table_offset += row_size * num_rows;
3009
0
      break;
3010
3011
0
    case BIT_NESTEDCLASS:
3012
0
      row_size = index_sizes.typedef_ * 2;
3013
3014
0
      tables.nestedclass.Offset = table_offset;
3015
0
      tables.nestedclass.RowCount = num_rows;
3016
0
      tables.nestedclass.RowSize = row_size;
3017
3018
0
      table_offset += row_size * num_rows;
3019
0
      break;
3020
3021
0
    case BIT_GENERICPARAM:
3022
0
      row_count = max_rows(
3023
0
          2, yr_le32toh(rows.typedef_), yr_le32toh(rows.methoddef));
3024
3025
0
      if (row_count > (0xFFFF >> 0x01))
3026
0
        index_size = 4;
3027
0
      else
3028
0
        index_size = 2;
3029
3030
0
      row_size = (2 + 2 + index_size + index_sizes.string);
3031
3032
0
      tables.genericparam.Offset = table_offset;
3033
0
      tables.genericparam.RowCount = num_rows;
3034
0
      tables.genericparam.RowSize = row_size;
3035
3036
0
      table_offset += row_size * num_rows;
3037
0
      break;
3038
3039
0
    case BIT_METHODSPEC:
3040
0
      row_count = max_rows(
3041
0
          2, yr_le32toh(rows.methoddef), yr_le32toh(rows.memberref));
3042
3043
0
      if (row_count > (0xFFFF >> 0x01))
3044
0
        index_size = 4;
3045
0
      else
3046
0
        index_size = 2;
3047
3048
0
      table_offset += (index_size + index_sizes.blob) * num_rows;
3049
0
      break;
3050
3051
0
    case BIT_GENERICPARAMCONSTRAINT:
3052
0
      row_count = max_rows(
3053
0
          3,
3054
0
          yr_le32toh(rows.typedef_),
3055
0
          yr_le32toh(rows.typeref),
3056
0
          yr_le32toh(rows.typespec));
3057
3058
0
      if (row_count > (0xFFFF >> 0x02))
3059
0
        index_size = 4;
3060
0
      else
3061
0
        index_size = 2;
3062
3063
0
      table_offset += (index_sizes.genericparam + index_size) * num_rows;
3064
0
      break;
3065
3066
0
    default:
3067
      // printf("Unknown bit: %i\n", bit_check);
3068
0
      return;
3069
0
    }
3070
3071
0
    matched_bits++;
3072
0
  }
3073
3074
0
  CLASS_CONTEXT class_context = {
3075
0
      .pe = pe,
3076
0
      .tables = &tables,
3077
0
      .index_sizes = &index_sizes,
3078
0
      .str_heap = string_offset,
3079
0
      .str_size = str_heap_size,
3080
0
      .blob_heap = pe->data + streams->metadata_root +
3081
0
                   yr_le32toh(streams->blob->Offset),
3082
0
      .blob_size = yr_le32toh(streams->blob->Size)};
3083
3084
0
  parse_user_types(&class_context);
3085
0
}
3086
3087
// Parsing the #~ stream is done in two parts. The first part (this function)
3088
// parses enough of the Stream to provide context for the second pass. In
3089
// particular it is collecting the number of rows for each of the tables. The
3090
// second part parses the actual tables of interest.
3091
3092
void dotnet_parse_tilde(PE* pe, PCLI_HEADER cli_header, PSTREAMS streams)
3093
0
{
3094
0
  PTILDE_HEADER tilde_header;
3095
0
  int64_t resource_base;
3096
0
  int64_t metadata_root = streams->metadata_root;
3097
0
  uint32_t* row_offset = NULL;
3098
3099
0
  int bit_check;
3100
3101
  // This is used as an offset into the rows and tables. For every bit set in
3102
  // Valid this will be incremented. This is because the bit position doesn't
3103
  // matter, just the number of bits that are set, when determining how many
3104
  // rows and what the table structure is.
3105
0
  int matched_bits = 0;
3106
3107
  // We need to know the number of rows for some tables, because they are
3108
  // indexed into. The index will be either 2 or 4 bytes, depending upon the
3109
  // number of rows being indexed into.
3110
0
  ROWS rows;
3111
0
  INDEX_SIZES index_sizes;
3112
0
  uint32_t heap_sizes;
3113
3114
  // Default all rows to 0. They will be set to actual values later on, if
3115
  // they exist in the file.
3116
0
  memset(&rows, '\0', sizeof(ROWS));
3117
3118
  // Default index sizes are 2. Will be bumped to 4 if necessary.
3119
0
  memset(&index_sizes, 2, sizeof(index_sizes));
3120
3121
0
  tilde_header = (PTILDE_HEADER) (pe->data + metadata_root +
3122
0
                                  yr_le32toh(streams->tilde->Offset));
3123
3124
0
  if (!struct_fits_in_pe(pe, tilde_header, TILDE_HEADER))
3125
0
    return;
3126
3127
0
  heap_sizes = yr_le32toh(tilde_header->HeapSizes);
3128
3129
  // Set index sizes for various heaps.
3130
0
  if (heap_sizes & 0x01)
3131
0
    index_sizes.string = 4;
3132
3133
0
  if (heap_sizes & 0x02)
3134
0
    index_sizes.guid = 4;
3135
3136
0
  if (heap_sizes & 0x04)
3137
0
    index_sizes.blob = 4;
3138
3139
  // Immediately after the tilde header is an array of 32bit values which
3140
  // indicate how many rows are in each table. The tables are immediately
3141
  // after the rows array.
3142
  //
3143
  // Save the row offset.
3144
0
  row_offset = (uint32_t*) (tilde_header + 1);
3145
3146
  // Walk all the bits first because we need to know the number of rows for
3147
  // some tables in order to parse others. In particular this applies to
3148
  // coded indexes, which are documented in ECMA-335 II.24.2.6.
3149
0
  for (bit_check = 0; bit_check < 64; bit_check++)
3150
0
  {
3151
0
    if (!((yr_le64toh(tilde_header->Valid) >> bit_check) & 0x01))
3152
0
      continue;
3153
3154
0
#define ROW_CHECK(name)                                                  \
3155
0
  if (fits_in_pe(pe, row_offset, (matched_bits + 1) * sizeof(uint32_t))) \
3156
0
    rows.name = *(row_offset + matched_bits);
3157
3158
0
#define ROW_CHECK_WITH_INDEX(name)    \
3159
0
  ROW_CHECK(name);                    \
3160
0
  if (yr_le32toh(rows.name) > 0xFFFF) \
3161
0
    index_sizes.name = 4;
3162
3163
0
    switch (bit_check)
3164
0
    {
3165
0
    case BIT_MODULE:
3166
0
      ROW_CHECK_WITH_INDEX(module);
3167
0
      break;
3168
0
    case BIT_MODULEREF:
3169
0
      ROW_CHECK_WITH_INDEX(moduleref);
3170
0
      break;
3171
0
    case BIT_ASSEMBLYREF:
3172
0
      ROW_CHECK_WITH_INDEX(assemblyref);
3173
0
      break;
3174
0
    case BIT_ASSEMBLYREFPROCESSOR:
3175
0
      ROW_CHECK_WITH_INDEX(assemblyrefprocessor);
3176
0
      break;
3177
0
    case BIT_TYPEREF:
3178
0
      ROW_CHECK_WITH_INDEX(typeref);
3179
0
      break;
3180
0
    case BIT_METHODDEF:
3181
0
      ROW_CHECK_WITH_INDEX(methoddef);
3182
0
      break;
3183
0
    case BIT_MEMBERREF:
3184
0
      ROW_CHECK_WITH_INDEX(memberref);
3185
0
      break;
3186
0
    case BIT_TYPEDEF:
3187
0
      ROW_CHECK_WITH_INDEX(typedef_);
3188
0
      break;
3189
0
    case BIT_TYPESPEC:
3190
0
      ROW_CHECK_WITH_INDEX(typespec);
3191
0
      break;
3192
0
    case BIT_FIELD:
3193
0
      ROW_CHECK_WITH_INDEX(field);
3194
0
      break;
3195
0
    case BIT_PARAM:
3196
0
      ROW_CHECK_WITH_INDEX(param);
3197
0
      break;
3198
0
    case BIT_PROPERTY:
3199
0
      ROW_CHECK_WITH_INDEX(property);
3200
0
      break;
3201
0
    case BIT_INTERFACEIMPL:
3202
0
      ROW_CHECK_WITH_INDEX(interfaceimpl);
3203
0
      break;
3204
0
    case BIT_EVENT:
3205
0
      ROW_CHECK_WITH_INDEX(event);
3206
0
      break;
3207
0
    case BIT_STANDALONESIG:
3208
0
      ROW_CHECK(standalonesig);
3209
0
      break;
3210
0
    case BIT_ASSEMBLY:
3211
0
      ROW_CHECK_WITH_INDEX(assembly);
3212
0
      break;
3213
0
    case BIT_FILE:
3214
0
      ROW_CHECK(file);
3215
0
      break;
3216
0
    case BIT_EXPORTEDTYPE:
3217
0
      ROW_CHECK(exportedtype);
3218
0
      break;
3219
0
    case BIT_MANIFESTRESOURCE:
3220
0
      ROW_CHECK(manifestresource);
3221
0
      break;
3222
0
    case BIT_GENERICPARAM:
3223
0
      ROW_CHECK_WITH_INDEX(genericparam);
3224
0
      break;
3225
0
    case BIT_GENERICPARAMCONSTRAINT:
3226
0
      ROW_CHECK(genericparamconstraint);
3227
0
      break;
3228
0
    case BIT_METHODSPEC:
3229
0
      ROW_CHECK_WITH_INDEX(methodspec);
3230
0
      break;
3231
0
    default:
3232
0
      break;
3233
0
    }
3234
3235
0
    matched_bits++;
3236
0
  }
3237
3238
  // This is used when parsing the MANIFEST RESOURCE table.
3239
0
  resource_base = pe_rva_to_offset(
3240
0
      pe, yr_le32toh(cli_header->Resources.VirtualAddress));
3241
3242
0
  dotnet_parse_tilde_2(
3243
0
      pe, tilde_header, resource_base, rows, index_sizes, streams);
3244
0
}
3245
3246
static bool dotnet_is_dotnet(PE* pe)
3247
0
{
3248
0
  PIMAGE_DATA_DIRECTORY directory = pe_get_directory_entry(
3249
0
      pe, IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR);
3250
3251
0
  if (!directory)
3252
0
    return false;
3253
3254
0
  int64_t offset = pe_rva_to_offset(pe, yr_le32toh(directory->VirtualAddress));
3255
3256
0
  if (offset < 0 || !struct_fits_in_pe(pe, pe->data + offset, CLI_HEADER))
3257
0
    return false;
3258
3259
0
  CLI_HEADER* cli_header = (CLI_HEADER*) (pe->data + offset);
3260
3261
0
  if (yr_le32toh(cli_header->Size) != sizeof(CLI_HEADER))
3262
0
    return false;
3263
3264
0
  int64_t metadata_root = pe_rva_to_offset(
3265
0
      pe, yr_le32toh(cli_header->MetaData.VirtualAddress));
3266
0
  offset = metadata_root;
3267
3268
0
  if (!struct_fits_in_pe(pe, pe->data + metadata_root, NET_METADATA))
3269
0
    return false;
3270
3271
0
  NET_METADATA* metadata = (NET_METADATA*) (pe->data + metadata_root);
3272
3273
0
  if (yr_le32toh(metadata->Magic) != NET_METADATA_MAGIC)
3274
0
    return false;
3275
3276
  // Version length must be between 1 and 255, and be a multiple of 4.
3277
  // Also make sure it fits in pe.
3278
0
  uint32_t md_len = yr_le32toh(metadata->Length);
3279
0
  if (md_len == 0 || md_len > 255 || md_len % 4 != 0 ||
3280
0
      !fits_in_pe(pe, pe->data + offset + sizeof(NET_METADATA), md_len))
3281
0
  {
3282
0
    return false;
3283
0
  }
3284
3285
0
  if (IS_64BITS_PE(pe))
3286
0
  {
3287
0
    if (yr_le32toh(OptionalHeader(pe, NumberOfRvaAndSizes)) <
3288
0
        IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR)
3289
0
      return false;
3290
0
  }
3291
0
  else if (!(pe->header->FileHeader.Characteristics & IMAGE_FILE_DLL))  // 32bit
3292
0
  {
3293
    // Check first 2 bytes of the Entry point are equal to 0xFF 0x25
3294
0
    int64_t entry_offset = pe_rva_to_offset(
3295
0
        pe, yr_le32toh(pe->header->OptionalHeader.AddressOfEntryPoint));
3296
3297
0
    if (entry_offset < 0 || !fits_in_pe(pe, pe->data + entry_offset, 2))
3298
0
      return false;
3299
3300
0
    const uint8_t* entry_data = pe->data + entry_offset;
3301
0
    if (!(entry_data[0] == 0xFF && entry_data[1] == 0x25))
3302
0
      return false;
3303
0
  }
3304
3305
0
  return true;
3306
0
}
3307
3308
void dotnet_parse_com(PE* pe)
3309
0
{
3310
0
  PIMAGE_DATA_DIRECTORY directory;
3311
0
  PCLI_HEADER cli_header;
3312
0
  PNET_METADATA metadata;
3313
0
  int64_t metadata_root, offset;
3314
0
  char* end;
3315
0
  STREAMS headers;
3316
0
  WORD num_streams;
3317
0
  uint32_t md_len;
3318
3319
0
  if (!dotnet_is_dotnet(pe))
3320
0
  {
3321
0
    yr_set_integer(0, pe->object, "is_dotnet");
3322
0
    return;
3323
0
  }
3324
3325
0
  yr_set_integer(1, pe->object, "is_dotnet");
3326
3327
0
  directory = pe_get_directory_entry(pe, IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR);
3328
0
  if (directory == NULL)
3329
0
    return;
3330
3331
0
  offset = pe_rva_to_offset(pe, yr_le32toh(directory->VirtualAddress));
3332
3333
0
  if (offset < 0 || !struct_fits_in_pe(pe, pe->data + offset, CLI_HEADER))
3334
0
    return;
3335
3336
0
  cli_header = (PCLI_HEADER) (pe->data + offset);
3337
3338
0
  offset = metadata_root = pe_rva_to_offset(
3339
0
      pe, yr_le32toh(cli_header->MetaData.VirtualAddress));
3340
3341
0
  if (!struct_fits_in_pe(pe, pe->data + offset, NET_METADATA))
3342
0
    return;
3343
3344
0
  metadata = (PNET_METADATA) (pe->data + offset);
3345
3346
  // Version length must be between 1 and 255, and be a multiple of 4.
3347
  // Also make sure it fits in pe.
3348
0
  md_len = yr_le32toh(metadata->Length);
3349
3350
0
  if (md_len == 0 || md_len > 255 || md_len % 4 != 0 ||
3351
0
      !fits_in_pe(pe, pe->data + offset + sizeof(NET_METADATA), md_len))
3352
0
  {
3353
0
    return;
3354
0
  }
3355
3356
  // The length includes the NULL terminator and is rounded up to a multiple of
3357
  // 4. We need to exclude the terminator and the padding, so search for the
3358
  // first NULL byte.
3359
0
  end = (char*) memmem((void*) metadata->Version, md_len, "\0", 1);
3360
3361
0
  if (end != NULL)
3362
0
    yr_set_sized_string(
3363
0
        metadata->Version, (end - metadata->Version), pe->object, "version");
3364
3365
  // The metadata structure has some variable length records after the version.
3366
  // We must manually parse things from here on out.
3367
  //
3368
  // Flags are 2 bytes (always 0).
3369
0
  offset += sizeof(NET_METADATA) + md_len + 2;
3370
3371
  // 2 bytes for Streams.
3372
0
  if (!fits_in_pe(pe, pe->data + offset, 2))
3373
0
    return;
3374
3375
0
  num_streams = (WORD) * (pe->data + offset);
3376
0
  offset += 2;
3377
3378
0
  headers = dotnet_parse_stream_headers(pe, offset, metadata_root, num_streams);
3379
3380
0
  if (headers.guid != NULL)
3381
0
    dotnet_parse_guid(pe, metadata_root, headers.guid);
3382
3383
  // Parse the #~ stream, which includes various tables of interest.
3384
  // These tables reference the blob and string streams, so we need to ensure
3385
  // those are not NULL also.
3386
0
  if (headers.tilde != NULL && headers.string != NULL && headers.blob != NULL)
3387
0
    dotnet_parse_tilde(pe, cli_header, &headers);
3388
3389
0
  if (headers.us != NULL)
3390
0
    dotnet_parse_us(pe, metadata_root, headers.us);
3391
0
}
3392
3393
0
begin_declarations
3394
0
  declare_integer("is_dotnet");
3395
0
  declare_string("version");
3396
0
  declare_string("module_name");
3397
3398
0
  begin_struct_array("streams")
3399
0
    declare_string("name");
3400
0
    declare_integer("offset");
3401
0
    declare_integer("size");
3402
0
  end_struct_array("streams")
3403
3404
0
  declare_integer("number_of_streams");
3405
3406
0
  declare_string_array("guids");
3407
0
  declare_integer("number_of_guids");
3408
3409
0
  begin_struct_array("resources")
3410
0
    declare_integer("offset");
3411
0
    declare_integer("length");
3412
0
    declare_string("name");
3413
0
  end_struct_array("resources")
3414
3415
0
  declare_integer("number_of_resources");
3416
3417
0
  begin_struct_array("classes")
3418
0
    declare_string("fullname");
3419
0
    declare_string("name");
3420
0
    declare_string("namespace");
3421
0
    declare_string("visibility");
3422
0
    declare_string("type");
3423
0
    declare_integer("abstract");
3424
0
    declare_integer("sealed");
3425
3426
0
    declare_integer("number_of_generic_parameters");
3427
0
    declare_string_array("generic_parameters");
3428
3429
0
    declare_integer("number_of_base_types");
3430
0
    declare_string_array("base_types");
3431
3432
0
    declare_integer("number_of_methods");
3433
0
    begin_struct_array("methods")
3434
0
      declare_string_array("generic_parameters");
3435
3436
0
      declare_integer("number_of_generic_parameters");
3437
3438
0
      begin_struct_array("parameters")
3439
0
        declare_string("name");
3440
0
        declare_string("type");
3441
0
      end_struct_array("parameters")
3442
3443
0
      declare_integer("number_of_parameters");
3444
3445
0
      declare_string("return_type");
3446
0
      declare_integer("abstract");
3447
0
      declare_integer("final");
3448
0
      declare_integer("virtual");
3449
0
      declare_integer("static");
3450
0
      declare_string("visibility");
3451
0
      declare_string("name");
3452
0
    end_struct_array("methods")
3453
3454
0
  end_struct_array("classes")
3455
3456
0
  declare_integer("number_of_classes");
3457
3458
0
  begin_struct_array("assembly_refs")
3459
0
    begin_struct("version")
3460
0
      declare_integer("major");
3461
0
      declare_integer("minor");
3462
0
      declare_integer("build_number");
3463
0
      declare_integer("revision_number");
3464
0
    end_struct("version")
3465
0
    declare_string("public_key_or_token");
3466
0
    declare_string("name");
3467
0
  end_struct_array("assembly_refs")
3468
3469
0
  declare_integer("number_of_assembly_refs");
3470
3471
0
  begin_struct("assembly")
3472
0
    begin_struct("version")
3473
0
      declare_integer("major");
3474
0
      declare_integer("minor");
3475
0
      declare_integer("build_number");
3476
0
      declare_integer("revision_number");
3477
0
    end_struct("version")
3478
0
    declare_string("name");
3479
0
    declare_string("culture");
3480
0
  end_struct("assembly")
3481
3482
0
  declare_string_array("modulerefs");
3483
0
  declare_integer("number_of_modulerefs");
3484
0
  declare_string_array("user_strings");
3485
0
  declare_integer("number_of_user_strings");
3486
0
  declare_string("typelib");
3487
0
  declare_string_array("constants");
3488
0
  declare_integer("number_of_constants");
3489
3490
0
  declare_integer_array("field_offsets");
3491
0
  declare_integer("number_of_field_offsets");
3492
0
end_declarations
3493
3494
int module_initialize(YR_MODULE* module)
3495
2
{
3496
2
  return ERROR_SUCCESS;
3497
2
}
3498
3499
int module_finalize(YR_MODULE* module)
3500
0
{
3501
0
  return ERROR_SUCCESS;
3502
0
}
3503
3504
int module_load(
3505
    YR_SCAN_CONTEXT* context,
3506
    YR_OBJECT* module_object,
3507
    void* module_data,
3508
    size_t module_data_size)
3509
0
{
3510
0
  YR_MEMORY_BLOCK* block;
3511
0
  YR_MEMORY_BLOCK_ITERATOR* iterator = context->iterator;
3512
0
  const uint8_t* block_data = NULL;
3513
3514
0
  foreach_memory_block(iterator, block)
3515
0
  {
3516
0
    PIMAGE_NT_HEADERS32 pe_header;
3517
3518
0
    block_data = block->fetch_data(block);
3519
3520
0
    if (block_data == NULL)
3521
0
      continue;
3522
3523
0
    pe_header = pe_get_header(block_data, block->size);
3524
3525
0
    if (pe_header != NULL)
3526
0
    {
3527
      // Ignore DLLs while scanning a process
3528
3529
0
      if (!(context->flags & SCAN_FLAGS_PROCESS_MEMORY) ||
3530
0
          !(pe_header->FileHeader.Characteristics & IMAGE_FILE_DLL))
3531
0
      {
3532
0
        PE* pe = (PE*) yr_malloc(sizeof(PE));
3533
3534
0
        if (pe == NULL)
3535
0
          return ERROR_INSUFFICIENT_MEMORY;
3536
3537
0
        pe->data = block_data;
3538
0
        pe->data_size = block->size;
3539
0
        pe->object = module_object;
3540
0
        pe->header = pe_header;
3541
3542
0
        module_object->data = pe;
3543
3544
0
        dotnet_parse_com(pe);
3545
3546
0
        break;
3547
0
      }
3548
0
    }
3549
0
  }
3550
3551
0
  return ERROR_SUCCESS;
3552
0
}
3553
3554
int module_unload(YR_OBJECT* module_object)
3555
0
{
3556
0
  PE* pe = (PE*) module_object->data;
3557
3558
0
  if (pe == NULL)
3559
0
    return ERROR_SUCCESS;
3560
3561
0
  yr_free(pe);
3562
3563
0
  return ERROR_SUCCESS;
3564
0
}