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