LIEF: Library to Instrument Executable Formats Version 1.0.0
Loading...
Searching...
No Matches
BinaryStream.hpp
Go to the documentation of this file.
1/* Copyright 2017 - 2026 R. Thomas
2 * Copyright 2017 - 2026 Quarkslab
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#ifndef LIEF_BINARY_STREAM_H
17#define LIEF_BINARY_STREAM_H
18
19#include <cstdint>
20#include <vector>
21#include <cstring>
22#include <string>
23#include <algorithm>
24
26#include "LIEF/errors.hpp"
27#include "LIEF/visibility.h"
28
29namespace LIEF {
30class ASN1Reader;
31
34 public:
35 friend class ASN1Reader;
36
37 enum class STREAM_TYPE {
38 UNKNOWN = 0,
39 VECTOR,
40 MEMORY,
41 SPAN,
42 FILE,
43
44 ELF_DATA_HANDLER,
45 };
46
49 virtual ~BinaryStream() = default;
50 virtual uint64_t size() const = 0;
51
52 STREAM_TYPE type() const {
53 return stype_;
54 }
55
56 bool is_memory_stream() const {
57 return type() == STREAM_TYPE::MEMORY;
58 }
59
60 result<uint64_t> read_uleb128(size_t* size = nullptr) const;
61 result<uint64_t> read_sleb128(size_t* size = nullptr) const;
62
63 result<int64_t> read_dwarf_encoded(uint8_t encoding) const;
64
65 result<std::string> read_string(size_t maxsize = ~static_cast<size_t>(0)) const;
66 result<std::string> peek_string(size_t maxsize = ~static_cast<size_t>(0)) const;
68 peek_string_at(size_t offset,
69 size_t maxsize = ~static_cast<size_t>(0)) const;
70
73
74 result<std::string> read_mutf8(size_t maxsize = ~static_cast<size_t>(0)) const;
75
78 result<std::u16string> peek_u16string_at(size_t offset, size_t length) const;
79
80
81 virtual ok_error_t peek_data(std::vector<uint8_t>& container, uint64_t offset,
82 uint64_t size, uint64_t virtual_address = 0) {
83 if (size == 0) {
84 return ok();
85 }
86 // Even though offset + size < ... => offset < ...
87 // the addition could overflow so it's worth checking both
88 const bool read_ok = offset <= this->size() &&
89 (offset + size) <= this->size()
90 /* Check for an overflow */
91 && (static_cast<int64_t>(offset) >= 0 &&
92 static_cast<int64_t>(size) >= 0) &&
93 (static_cast<int64_t>(offset + size) >= 0);
94 if (!read_ok) {
96 }
97 container.resize(size);
98 if (peek_in(container.data(), offset, size, virtual_address)) {
99 return ok();
100 }
102 }
103
104 virtual ok_error_t read_data(std::vector<uint8_t>& container, uint64_t size) {
105 if (!peek_data(container, pos(), size)) {
107 }
108
110 return ok();
111 }
112
113 ok_error_t read_data(std::vector<uint8_t>& container) {
114 const size_t size = this->size() - this->pos();
115 return read_data(container, size);
116 }
117
118 template<class T>
119 ok_error_t read_objects(std::vector<T>& container, uint64_t count) {
120 if (count == 0) {
121 return ok();
122 }
123 const size_t size = count * sizeof(T);
124 auto ret = peek_objects(container, count);
125 if (!ret) {
127 }
129 return ok();
130 }
131
132 template<class T>
133 ok_error_t peek_objects(std::vector<T>& container, uint64_t count) {
134 return peek_objects_at(pos(), container, count);
135 }
136
137 template<class T>
138 ok_error_t peek_objects_at(uint64_t offset, std::vector<T>& container,
139 uint64_t count) {
140 if (count == 0) {
141 return ok();
142 }
143 const auto current_p = pos();
144 setpos(offset);
145
146 const size_t size = count * sizeof(T);
147
148 if (!can_read(offset, size)) {
149 setpos(current_p);
151 }
152
153 container.resize(count);
154
155 if (!peek_in(container.data(), pos(), size)) {
156 setpos(current_p);
158 }
159
160 setpos(current_p);
161 return ok();
162 }
163
164 void setpos(size_t pos) const {
165 pos_ = pos;
166 }
167
168 const BinaryStream& increment_pos(size_t value) const LIEF_LIFETIMEBOUND {
169 pos_ += value;
170 return *this;
171 }
172
173 void decrement_pos(size_t value) const {
174 if (pos_ > value) {
175 pos_ -= value;
176 } else {
177 pos_ = 0;
178 }
179 }
180
181 size_t pos() const {
182 return pos_;
183 }
184
185 bool is_valid() const {
186 return pos_ < size();
187 }
188
189 operator bool() const {
190 return is_valid();
191 }
192
193 template<class T>
194 const T* read_array(size_t size) const;
195
196 template<class T, size_t N>
197 ok_error_t peek_array(std::array<T, N>& dst) const {
198 if /*constexpr*/ (N == 0) {
199 return ok();
200 }
201 // Even though offset + size < ... => offset < ...
202 // the addition could overflow so it's worth checking both
203 const bool read_ok =
204 pos_ <= size() &&
205 (pos_ + N) <= size()
206 /* Check for an overflow */
207 && (static_cast<int64_t>(pos_) >= 0 && static_cast<int64_t>(N) >= 0) &&
208 (static_cast<int64_t>(pos_ + N) >= 0);
209
210 if (!read_ok) {
212 }
213 if (peek_in(dst.data(), pos_, N)) {
214 return ok();
215 }
217 }
218
219 template<class T, size_t N>
220 ok_error_t read_array(std::array<T, N>& dst) const {
221 if (!peek_array<T, N>(dst)) {
223 }
224
225 increment_pos(N);
226 return ok();
227 }
228
229 template<class T>
230 result<T> peek() const;
231
232 template<class T>
233 result<T> peek(size_t offset) const;
234
235 template<class T>
236 const T* peek_array(size_t size) const;
237
238 template<class T>
239 const T* peek_array(size_t offset, size_t size) const;
240
241 template<class T>
242 result<T> read() const;
243
244 template<typename T>
245 bool can_read() const;
246
247 template<typename T>
248 bool can_read(size_t offset) const;
249
250 bool can_read(int64_t offset, int64_t size) const {
251 return offset < (int64_t)this->size() &&
252 (offset + size) < (int64_t)this->size();
253 }
254
255 size_t align(size_t align_on) const;
256
257 void set_endian_swap(bool swap) {
258 endian_swap_ = swap;
259 }
260
261 template<class T>
262 static bool is_all_zero(const T& buffer) {
263 const auto* ptr = reinterpret_cast<const uint8_t* const>(&buffer);
264 return std::all_of(ptr, ptr + sizeof(T), [](uint8_t x) { return x == 0; });
265 }
266
267 bool should_swap() const {
268 return endian_swap_;
269 }
270
271 virtual const uint8_t* p() const {
272 return nullptr;
273 }
274
275 virtual uint8_t* start() {
276 return const_cast<uint8_t*>(static_cast<const BinaryStream*>(this)->start());
277 }
278
279 virtual uint8_t* p() {
280 return const_cast<uint8_t*>(static_cast<const BinaryStream*>(this)->p());
281 }
282
283 virtual uint8_t* end() {
284 return const_cast<uint8_t*>(static_cast<const BinaryStream*>(this)->end());
285 }
286
287 virtual const uint8_t* start() const {
288 return nullptr;
289 }
290
291 virtual const uint8_t* end() const {
292 return nullptr;
293 }
294
295 virtual result<const void*> read_at(uint64_t offset, uint64_t size,
296 uint64_t virtual_address = 0) const = 0;
297 virtual ok_error_t peek_in(void* dst, uint64_t offset, uint64_t size,
298 uint64_t virtual_address = 0) const {
299 if (auto raw = read_at(offset, size, virtual_address)) {
300 if (dst == nullptr) {
302 }
303
304 const void* ptr = *raw;
305
306 if (ptr == nullptr) {
308 }
309
310 memcpy(dst, ptr, size);
311 return ok();
312 }
314 }
315
316 template<class T>
317 const T* cast() const {
318 static_assert(std::is_base_of<BinaryStream, T>::value,
319 "Require BinaryStream inheritance");
320 if (T::classof(*this)) {
321 return static_cast<const T*>(this);
322 }
323 return nullptr;
324 }
325
326 template<class T>
327 T* cast() {
328 return const_cast<T*>(static_cast<const BinaryStream*>(this)->cast<T>());
329 }
330
331 protected:
332 BinaryStream() = default;
333
334 mutable size_t pos_ = 0;
335 bool endian_swap_ = false;
336 STREAM_TYPE stype_ = STREAM_TYPE::UNKNOWN;
337};
338
340 public:
341 ScopedStream(const ScopedStream&) = delete;
343
346
347 explicit ScopedStream(BinaryStream& stream, uint64_t pos) :
348 pos_{stream.pos()},
349 stream_{stream} {
350 stream_.setpos(pos);
351 }
352
353 explicit ScopedStream(BinaryStream& stream) :
354 pos_{stream.pos()},
355 stream_{stream} {}
356
358 stream_.setpos(pos_);
359 }
360
362 return &stream_;
363 }
364
366 return stream_;
367 }
368
369 const BinaryStream& operator*() const {
370 return stream_;
371 }
372
373 private:
374 uint64_t pos_ = 0;
375 BinaryStream& stream_;
376};
377
379 public:
382
385
386 explicit ToggleEndianness(BinaryStream& stream, bool value) :
387 endian_swap_(stream.should_swap()),
388 stream_{stream} {
389 stream.set_endian_swap(value);
390 }
391
392 explicit ToggleEndianness(BinaryStream& stream) :
393 endian_swap_(stream.should_swap()),
394 stream_{stream} {
395 stream.set_endian_swap(!stream_.should_swap());
396 }
397
399 stream_.set_endian_swap(endian_swap_);
400 }
401
403 return &stream_;
404 }
405
407 return stream_;
408 }
409
410 const BinaryStream& operator*() const {
411 return stream_;
412 }
413
414 private:
415 bool endian_swap_ = false;
416 BinaryStream& stream_;
417};
418
419
420template<class T>
422 result<T> tmp = this->peek<T>();
423 if (!tmp) {
424 return tmp;
425 }
426 this->increment_pos(sizeof(T));
427 return tmp;
428}
429
430template<class T>
432 const auto current_p = pos();
433 T ret{};
434 if (auto res = peek_in(&ret, pos(), sizeof(T))) {
435 setpos(current_p);
436 if (endian_swap_) {
437 swap_endian(&ret);
438 }
439 return ret;
440 }
441
442 setpos(current_p);
444}
445
446template<class T>
447result<T> BinaryStream::peek(size_t offset) const {
448 const size_t saved_offset = this->pos();
449 this->setpos(offset);
450 result<T> r = this->peek<T>();
451 this->setpos(saved_offset);
452 return r;
453}
454
455
456template<class T>
457const T* BinaryStream::peek_array(size_t size) const {
458 result<const void*> raw = this->read_at(this->pos(), sizeof(T) * size);
459 if (!raw) {
460 return nullptr;
461 }
462 return reinterpret_cast<const T*>(raw.value());
463}
464
465template<class T>
466const T* BinaryStream::peek_array(size_t offset, size_t size) const {
467 const size_t saved_offset = this->pos();
468 this->setpos(offset);
469 const T* r = this->peek_array<T>(size);
470 this->setpos(saved_offset);
471 return r;
472}
473
474
475template<typename T>
477 // Even though pos_ + sizeof(T) < ... => pos_ < ...
478 // the addition could overflow so it's worth checking both
479 return pos_ < size() && (pos_ + sizeof(T)) < size();
480}
481
482
483template<typename T>
484bool BinaryStream::can_read(size_t offset) const {
485 // Even though offset + sizeof(T) < ... => offset < ...
486 // the addition could overflow so it's worth checking both
487 return offset < size() && (offset + sizeof(T)) < size();
488}
489
490
491template<class T>
492const T* BinaryStream::read_array(size_t size) const {
493 const T* tmp = this->peek_array<T>(size);
494 this->increment_pos(sizeof(T) * size);
495 return tmp;
496}
497
498}
499#endif
Definition ASN1Reader.hpp:32
Class that is used to a read stream of data from different sources.
Definition BinaryStream.hpp:33
size_t align(size_t align_on) const
ok_error_t peek_array(std::array< T, N > &dst) const
Definition BinaryStream.hpp:197
result< std::string > read_mutf8(size_t maxsize=~static_cast< size_t >(0)) const
STREAM_TYPE
Definition BinaryStream.hpp:37
@ MEMORY
Definition BinaryStream.hpp:40
result< T > peek() const
Definition BinaryStream.hpp:431
result< int64_t > read_dwarf_encoded(uint8_t encoding) const
virtual uint8_t * p()
Definition BinaryStream.hpp:279
result< std::string > peek_string(size_t maxsize=~static_cast< size_t >(0)) const
virtual const uint8_t * p() const
Definition BinaryStream.hpp:271
const T * read_array(size_t size) const
Definition BinaryStream.hpp:492
virtual uint64_t size() const =0
friend class ASN1Reader
Definition BinaryStream.hpp:35
result< std::u16string > peek_u16string_at(size_t offset, size_t length) const
bool can_read() const
Definition BinaryStream.hpp:476
ok_error_t read_objects(std::vector< T > &container, uint64_t count)
Definition BinaryStream.hpp:119
ok_error_t peek_objects_at(uint64_t offset, std::vector< T > &container, uint64_t count)
Definition BinaryStream.hpp:138
result< std::u16string > peek_u16string(size_t length) const
const T * cast() const
Definition BinaryStream.hpp:317
result< std::u16string > read_u16string() const
void set_endian_swap(bool swap)
Definition BinaryStream.hpp:257
bool is_memory_stream() const
Definition BinaryStream.hpp:56
const BinaryStream & increment_pos(size_t value) const
Definition BinaryStream.hpp:168
result< std::u16string > peek_u16string() const
bool should_swap() const
Definition BinaryStream.hpp:267
result< std::string > peek_string_at(size_t offset, size_t maxsize=~static_cast< size_t >(0)) const
ok_error_t peek_objects(std::vector< T > &container, uint64_t count)
Definition BinaryStream.hpp:133
size_t pos() const
Definition BinaryStream.hpp:181
virtual uint8_t * start()
Definition BinaryStream.hpp:275
result< uint64_t > read_uleb128(size_t *size=nullptr) const
virtual result< const void * > read_at(uint64_t offset, uint64_t size, uint64_t virtual_address=0) const =0
void setpos(size_t pos) const
Definition BinaryStream.hpp:164
bool is_valid() const
Definition BinaryStream.hpp:185
virtual const uint8_t * end() const
Definition BinaryStream.hpp:291
static bool is_all_zero(const T &buffer)
Definition BinaryStream.hpp:262
result< std::string > read_string(size_t maxsize=~static_cast< size_t >(0)) const
T * cast()
Definition BinaryStream.hpp:327
virtual ok_error_t peek_in(void *dst, uint64_t offset, uint64_t size, uint64_t virtual_address=0) const
Definition BinaryStream.hpp:297
result< std::u16string > read_u16string(size_t length) const
virtual ok_error_t peek_data(std::vector< uint8_t > &container, uint64_t offset, uint64_t size, uint64_t virtual_address=0)
Definition BinaryStream.hpp:81
void decrement_pos(size_t value) const
Definition BinaryStream.hpp:173
result< T > read() const
Definition BinaryStream.hpp:421
BinaryStream(STREAM_TYPE type)
Definition BinaryStream.hpp:47
virtual const uint8_t * start() const
Definition BinaryStream.hpp:287
result< uint64_t > read_sleb128(size_t *size=nullptr) const
ok_error_t read_array(std::array< T, N > &dst) const
Definition BinaryStream.hpp:220
bool can_read(int64_t offset, int64_t size) const
Definition BinaryStream.hpp:250
ok_error_t read_data(std::vector< uint8_t > &container)
Definition BinaryStream.hpp:113
virtual ~BinaryStream()=default
STREAM_TYPE type() const
Definition BinaryStream.hpp:52
virtual ok_error_t read_data(std::vector< uint8_t > &container, uint64_t size)
Definition BinaryStream.hpp:104
virtual uint8_t * end()
Definition BinaryStream.hpp:283
const BinaryStream & operator*() const
Definition BinaryStream.hpp:369
ScopedStream(BinaryStream &stream, uint64_t pos)
Definition BinaryStream.hpp:347
ScopedStream(BinaryStream &stream)
Definition BinaryStream.hpp:353
ScopedStream(const ScopedStream &)=delete
ScopedStream & operator=(const ScopedStream &)=delete
ScopedStream & operator=(ScopedStream &&)=delete
~ScopedStream()
Definition BinaryStream.hpp:357
BinaryStream & operator*()
Definition BinaryStream.hpp:365
ScopedStream(ScopedStream &&)=delete
BinaryStream * operator->()
Definition BinaryStream.hpp:361
ToggleEndianness(BinaryStream &stream, bool value)
Definition BinaryStream.hpp:386
ToggleEndianness(ToggleEndianness &&)=delete
ToggleEndianness & operator=(const ToggleEndianness &)=delete
ToggleEndianness & operator=(ToggleEndianness &&)=delete
BinaryStream & operator*()
Definition BinaryStream.hpp:406
BinaryStream * operator->()
Definition BinaryStream.hpp:402
ToggleEndianness(BinaryStream &stream)
Definition BinaryStream.hpp:392
~ToggleEndianness()
Definition BinaryStream.hpp:398
const BinaryStream & operator*() const
Definition BinaryStream.hpp:410
ToggleEndianness(const ToggleEndianness &)=delete
Opaque structure that is used by LIEF to avoid writing result<void> f(...). Instead,...
Definition errors.hpp:117
Wrapper that contains an Object (T) or an error.
Definition errors.hpp:77
#define LIEF_LIFETIMEBOUND
Definition compiler_attributes.hpp:72
@ read_error
Definition errors.hpp:25
tl::unexpected< lief_errors > make_error_code(lief_errors e)
Create a standard error code from lief_errors.
Definition errors.hpp:53
LIEF namespace.
Definition Abstract/Binary.hpp:40
ok_t ok()
Return success for function with return type ok_error_t.
Definition errors.hpp:101
void swap_endian(T *)
Definition endianness_support.hpp:117
#define LIEF_API
Definition visibility.h:45