Coverage Report

Created: 2025-07-11 06:08

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