Coverage Report

Created: 2026-08-28 06:16

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