-
Notifications
You must be signed in to change notification settings - Fork 5.4k
Expand file tree
/
Copy pathPEBinaryReader.cs
More file actions
134 lines (117 loc) · 4.26 KB
/
PEBinaryReader.cs
File metadata and controls
134 lines (117 loc) · 4.26 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Diagnostics;
using System.IO;
using System.Reflection.Metadata;
using System.Runtime.CompilerServices;
using System.Text;
namespace System.Reflection.PortableExecutable
{
/// <summary>
/// Simple BinaryReader wrapper to:
///
/// 1) throw BadImageFormat instead of EndOfStream or ArgumentOutOfRange.
/// 2) limit reads to a subset of the base stream.
///
/// Only methods that are needed to read PE headers are implemented.
/// </summary>
internal readonly struct PEBinaryReader
{
private readonly long _startOffset;
private readonly long _maxOffset;
private readonly BinaryReader _reader;
public PEBinaryReader(Stream stream, int size)
{
Debug.Assert(size >= 0 && size <= (stream.Length - stream.Position));
_startOffset = stream.Position;
_maxOffset = _startOffset + size;
_reader = new BinaryReader(stream, Encoding.UTF8, leaveOpen: true);
}
public int Offset
{
get { return (int)(_reader.BaseStream.Position - _startOffset); }
set
{
CheckBounds(_startOffset, value);
_reader.BaseStream.Seek(_startOffset + value, SeekOrigin.Begin);
}
}
private byte[] ReadBytes(int count)
{
CheckBounds(_reader.BaseStream.Position, count);
return _reader.ReadBytes(count);
}
public byte ReadByte()
{
CheckBounds(sizeof(byte));
return _reader.ReadByte();
}
public short ReadInt16()
{
CheckBounds(sizeof(short));
return _reader.ReadInt16();
}
public ushort ReadUInt16()
{
CheckBounds(sizeof(ushort));
return _reader.ReadUInt16();
}
public int ReadInt32()
{
CheckBounds(sizeof(int));
return _reader.ReadInt32();
}
public uint ReadUInt32()
{
CheckBounds(sizeof(uint));
return _reader.ReadUInt32();
}
public ulong ReadUInt64()
{
CheckBounds(sizeof(ulong));
return _reader.ReadUInt64();
}
/// <summary>
/// Reads a fixed-length byte block as a null-padded UTF-8 encoded string.
/// The padding is not included in the returned string.
///
/// Note that it is legal for UTF-8 strings to contain NUL; if NUL occurs
/// between non-NUL codepoints, it is not considered to be padding and
/// is included in the result.
/// </summary>
public string ReadNullPaddedUTF8(int byteCount)
{
byte[] bytes = ReadBytes(byteCount);
int nonPaddedLength = 0;
for (int i = bytes.Length; i > 0; --i)
{
if (bytes[i - 1] != 0)
{
nonPaddedLength = i;
break;
}
}
return Encoding.UTF8.GetString(bytes, 0, nonPaddedLength);
}
private void CheckBounds(uint count)
{
Debug.Assert(count <= sizeof(long)); // Error message assumes we're trying to read constant small number of bytes.
Debug.Assert(_reader.BaseStream.Position >= 0 && _maxOffset >= 0);
// Add cannot overflow because the worst case is (ulong)long.MaxValue + uint.MaxValue < ulong.MaxValue.
if ((ulong)_reader.BaseStream.Position + count > (ulong)_maxOffset)
{
Throw.ImageTooSmall();
}
}
private void CheckBounds(long startPosition, int count)
{
Debug.Assert(startPosition >= 0 && _maxOffset >= 0);
// Add cannot overflow because the worst case is (ulong)long.MaxValue + uint.MaxValue < ulong.MaxValue.
// Negative count is handled by overflow to greater than maximum size = int.MaxValue.
if ((ulong)startPosition + unchecked((uint)count) > (ulong)_maxOffset)
{
Throw.ImageTooSmallOrContainsInvalidOffsetOrCount();
}
}
}
}