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#include <utility>
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
71 result<uint64_t> read_uleb128(size_t* size = nullptr) const;
72 result<uint64_t> read_sleb128(size_t* size = nullptr) const;
73
74 result<int64_t> read_dwarf_encoded(uint8_t encoding) const;
75
76 result<std::string> read_string(size_t maxsize = ~static_cast<size_t>(0)) const;
77 result<std::string> peek_string(size_t maxsize = ~static_cast<size_t>(0)) const;
79 peek_string_at(size_t offset,
80 size_t maxsize = ~static_cast<size_t>(0)) const;
81
84
85 result<std::string> read_mutf8(size_t maxsize = ~static_cast<size_t>(0)) const;
86
89 result<std::u16string> peek_u16string_at(size_t offset, size_t length) const;
90
91
92 virtual ok_error_t peek_data(std::vector<uint8_t>& container, uint64_t offset,
93 uint64_t size, uint64_t virtual_address = 0) {
94 if (size == 0) {
95 return ok();
96 }
97 // Even though offset + size < ... => offset < ...
98 // the addition could overflow so it's worth checking both
99 const bool read_ok = offset <= this->size() &&
100 (offset + size) <= this->size()
101 /* Check for an overflow */
102 && (static_cast<int64_t>(offset) >= 0 &&
103 static_cast<int64_t>(size) >= 0) &&
104 (static_cast<int64_t>(offset + size) >= 0);
105 if (!read_ok) {
107 }
108
109 if (container.size() < size) {
110 std::vector<uint8_t> buffer(size);
111 const ok_error_t res = peek_in(buffer.data(), offset, size, virtual_address);
112 container = std::move(buffer);
113 return res;
114 }
115
116 return peek_in(container.data(), offset, size, virtual_address);
117 }
118
119 virtual ok_error_t read_data(std::vector<uint8_t>& container, uint64_t size) {
120 if (!peek_data(container, pos(), size)) {
122 }
123
125 return ok();
126 }
127
128 ok_error_t read_data(std::vector<uint8_t>& container) {
129 const size_t size = this->size() - this->pos();
130 return read_data(container, size);
131 }
132
133 template<class T>
134 ok_error_t read_objects(std::vector<T>& container, uint64_t count) {
135 if (count == 0) {
136 return ok();
137 }
138 const size_t size = count * sizeof(T);
139 auto ret = peek_objects(container, count);
140 if (!ret) {
142 }
144 return ok();
145 }
146
147 template<class T>
148 ok_error_t peek_objects(std::vector<T>& container, uint64_t count) {
149 return peek_objects_at(pos(), container, count);
150 }
151
152 template<class T>
153 ok_error_t peek_objects_at(uint64_t offset, std::vector<T>& container,
154 uint64_t count) {
155 if (count == 0) {
156 return ok();
157 }
158 const auto current_p = pos();
159 setpos(offset);
160
161 const size_t size = count * sizeof(T);
162
163 if (!can_read(offset, size)) {
164 setpos(current_p);
166 }
167
169
170 if (container.size() < count) {
171 std::vector<T> buffer(count);
172 res = peek_in(buffer.data(), pos(), size);
173 container = std::move(buffer);
174 } else {
175 res = peek_in(container.data(), pos(), size);
176 }
177
178 setpos(current_p);
179 return res;
180 }
181
182 void setpos(size_t pos) const {
183 pos_ = pos;
184 }
185
186 const BinaryStream& increment_pos(size_t value) const LIEF_LIFETIMEBOUND {
187 pos_ += value;
188 return *this;
189 }
190
191 void decrement_pos(size_t value) const {
192 if (pos_ > value) {
193 pos_ -= value;
194 } else {
195 pos_ = 0;
196 }
197 }
198
199 size_t pos() const {
200 return pos_;
201 }
202
203 bool is_valid() const {
204 return pos_ < size();
205 }
206
207 operator bool() const {
208 return is_valid();
209 }
210
211 template<class T>
212 const T* read_array(size_t size) const;
213
214 template<class T, size_t N>
215 ok_error_t peek_array(std::array<T, N>& dst) const {
216 if /*constexpr*/ (N == 0) {
217 return ok();
218 }
219 // Even though offset + size < ... => offset < ...
220 // the addition could overflow so it's worth checking both
221 const bool read_ok =
222 pos_ <= size() &&
223 (pos_ + N) <= size()
224 /* Check for an overflow */
225 && (static_cast<int64_t>(pos_) >= 0 && static_cast<int64_t>(N) >= 0) &&
226 (static_cast<int64_t>(pos_ + N) >= 0);
227
228 if (!read_ok) {
230 }
231 if (peek_in(dst.data(), pos_, N)) {
232 return ok();
233 }
235 }
236
237 template<class T, size_t N>
238 ok_error_t read_array(std::array<T, N>& dst) const {
239 if (!peek_array<T, N>(dst)) {
241 }
242
243 increment_pos(N);
244 return ok();
245 }
246
247 template<class T>
248 result<T> peek() const;
249
250 template<class T>
251 result<T> peek(size_t offset) const;
252
253 template<class T>
254 const T* peek_array(size_t size) const;
255
256 template<class T>
257 const T* peek_array(size_t offset, size_t size) const;
258
259 template<class T>
260 result<T> read() const;
261
262 template<typename T>
263 bool can_read() const;
264
265 template<typename T>
266 bool can_read(size_t offset) const;
267
268 bool can_read(int64_t offset, int64_t size) const {
269 return offset < (int64_t)this->size() &&
270 size < ((int64_t)this->size() - offset);
271 }
272
273 size_t align(size_t align_on) const;
274
275 void set_endian_swap(bool swap) {
276 endian_swap_ = swap;
277 }
278
279 template<class T>
280 static bool is_all_zero(const T& buffer) {
281 const auto* ptr = reinterpret_cast<const uint8_t* const>(&buffer);
282 return std::all_of(ptr, ptr + sizeof(T), [](uint8_t x) { return x == 0; });
283 }
284
285 bool should_swap() const {
286 return endian_swap_;
287 }
288
289 virtual const uint8_t* p() const {
290 return nullptr;
291 }
292
293 virtual uint8_t* start() {
294 return const_cast<uint8_t*>(static_cast<const BinaryStream*>(this)->start());
295 }
296
297 virtual uint8_t* p() {
298 return const_cast<uint8_t*>(static_cast<const BinaryStream*>(this)->p());
299 }
300
301 virtual uint8_t* end() {
302 return const_cast<uint8_t*>(static_cast<const BinaryStream*>(this)->end());
303 }
304
305 virtual const uint8_t* start() const {
306 return nullptr;
307 }
308
309 virtual const uint8_t* end() const {
310 return nullptr;
311 }
312
313 virtual result<const void*> read_at(uint64_t offset, uint64_t size,
314 uint64_t virtual_address = 0) const = 0;
315 virtual ok_error_t peek_in(void* dst, uint64_t offset, uint64_t size,
316 uint64_t virtual_address = 0) const {
317 if (dst == nullptr) {
319 }
320 if (auto raw = read_at(offset, size, virtual_address)) {
321 const void* ptr = *raw;
322
323 if (ptr == nullptr) {
325 }
326
327 memcpy(dst, ptr, size);
328 return ok();
329 }
331 }
332
333 template<class T>
334 const T* cast() const {
335 static_assert(std::is_base_of<BinaryStream, T>::value,
336 "Require BinaryStream inheritance");
337 if (T::classof(*this)) {
338 return static_cast<const T*>(this);
339 }
340 return nullptr;
341 }
342
343 template<class T>
344 T* cast() {
345 return const_cast<T*>(static_cast<const BinaryStream*>(this)->cast<T>());
346 }
347
348 virtual bool bind_binary(Binary& /*bin*/) {
349 return false;
350 }
351
352 protected:
353 BinaryStream() = default;
354
355 mutable size_t pos_ = 0;
356 bool endian_swap_ = false;
357 STREAM_TYPE stype_ = STREAM_TYPE::UNKNOWN;
358};
359
361 public:
362 ScopedStream(const ScopedStream&) = delete;
364
367
368 explicit ScopedStream(BinaryStream& stream, uint64_t pos) :
369 pos_{stream.pos()},
370 stream_{stream} {
371 stream_.setpos(pos);
372 }
373
374 explicit ScopedStream(BinaryStream& stream) :
375 pos_{stream.pos()},
376 stream_{stream} {}
377
379 stream_.setpos(pos_);
380 }
381
383 return &stream_;
384 }
385
387 return stream_;
388 }
389
390 const BinaryStream& operator*() const {
391 return stream_;
392 }
393
394 private:
395 uint64_t pos_ = 0;
396 BinaryStream& stream_;
397};
398
400 public:
403
406
407 explicit ToggleEndianness(BinaryStream& stream, bool value) :
408 endian_swap_(stream.should_swap()),
409 stream_{stream} {
410 stream.set_endian_swap(value);
411 }
412
413 explicit ToggleEndianness(BinaryStream& stream) :
414 endian_swap_(stream.should_swap()),
415 stream_{stream} {
416 stream.set_endian_swap(!stream_.should_swap());
417 }
418
420 stream_.set_endian_swap(endian_swap_);
421 }
422
424 return &stream_;
425 }
426
428 return stream_;
429 }
430
431 const BinaryStream& operator*() const {
432 return stream_;
433 }
434
435 private:
436 bool endian_swap_ = false;
437 BinaryStream& stream_;
438};
439
440
441template<class T>
443 result<T> tmp = this->peek<T>();
444 if (!tmp) {
445 return tmp;
446 }
447 this->increment_pos(sizeof(T));
448 return tmp;
449}
450
451template<class T>
453 const auto current_p = pos();
454 T ret{};
455 if (auto res = peek_in(&ret, pos(), sizeof(T))) {
456 setpos(current_p);
457 if (endian_swap_) {
458 swap_endian(&ret);
459 }
460 return ret;
461 }
462
463 setpos(current_p);
465}
466
467template<class T>
468result<T> BinaryStream::peek(size_t offset) const {
469 const size_t saved_offset = this->pos();
470 this->setpos(offset);
471 result<T> r = this->peek<T>();
472 this->setpos(saved_offset);
473 return r;
474}
475
476
477template<class T>
478const T* BinaryStream::peek_array(size_t size) const {
479 result<const void*> raw = this->read_at(this->pos(), sizeof(T) * size);
480 if (!raw) {
481 return nullptr;
482 }
483 return reinterpret_cast<const T*>(raw.value());
484}
485
486template<class T>
487const T* BinaryStream::peek_array(size_t offset, size_t size) const {
488 const size_t saved_offset = this->pos();
489 this->setpos(offset);
490 const T* r = this->peek_array<T>(size);
491 this->setpos(saved_offset);
492 return r;
493}
494
495
496template<typename T>
498 // Even though pos_ + sizeof(T) < ... => pos_ < ...
499 // the addition could overflow so it's worth checking both
500 return pos_ < size() && (pos_ + sizeof(T)) < size();
501}
502
503
504template<typename T>
505bool BinaryStream::can_read(size_t offset) const {
506 // Even though offset + sizeof(T) < ... => offset < ...
507 // the addition could overflow so it's worth checking both
508 return offset < size() && (offset + sizeof(T)) < size();
509}
510
511
512template<class T>
513const T* BinaryStream::read_array(size_t size) const {
514 const T* tmp = this->peek_array<T>(size);
515 this->increment_pos(sizeof(T) * size);
516 return tmp;
517}
518
519}
520#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:215
result< std::string > read_mutf8(size_t maxsize=~static_cast< size_t >(0)) const
STREAM_TYPE
Definition BinaryStream.hpp:39
@ MEMORY
Definition BinaryStream.hpp:42
@ DUMP
Definition BinaryStream.hpp:45
result< T > peek() const
Definition BinaryStream.hpp:452
result< int64_t > read_dwarf_encoded(uint8_t encoding) const
virtual uint8_t * p()
Definition BinaryStream.hpp:297
result< std::string > peek_string(size_t maxsize=~static_cast< size_t >(0)) const
virtual bool bind_binary(Binary &)
Definition BinaryStream.hpp:348
virtual const uint8_t * p() const
Definition BinaryStream.hpp:289
const T * read_array(size_t size) const
Definition BinaryStream.hpp:513
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:497
ok_error_t read_objects(std::vector< T > &container, uint64_t count)
Definition BinaryStream.hpp:134
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:153
result< std::u16string > peek_u16string(size_t length) const
const T * cast() const
Definition BinaryStream.hpp:334
result< std::u16string > read_u16string() const
bool is_memory_view() const
Definition BinaryStream.hpp:67
void set_endian_swap(bool swap)
Definition BinaryStream.hpp:275
bool is_memory_stream() const
Definition BinaryStream.hpp:59
const BinaryStream & increment_pos(size_t value) const
Definition BinaryStream.hpp:186
result< std::u16string > peek_u16string() const
bool should_swap() const
Definition BinaryStream.hpp:285
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:148
size_t pos() const
Definition BinaryStream.hpp:199
virtual uint8_t * start()
Definition BinaryStream.hpp:293
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:182
bool is_valid() const
Definition BinaryStream.hpp:203
virtual const uint8_t * end() const
Definition BinaryStream.hpp:309
static bool is_all_zero(const T &buffer)
Definition BinaryStream.hpp:280
result< std::string > read_string(size_t maxsize=~static_cast< size_t >(0)) const
T * cast()
Definition BinaryStream.hpp:344
virtual ok_error_t peek_in(void *dst, uint64_t offset, uint64_t size, uint64_t virtual_address=0) const
Definition BinaryStream.hpp:315
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:92
void decrement_pos(size_t value) const
Definition BinaryStream.hpp:191
result< T > read() const
Definition BinaryStream.hpp:442
BinaryStream(STREAM_TYPE type)
Definition BinaryStream.hpp:50
virtual const uint8_t * start() const
Definition BinaryStream.hpp:305
result< uint64_t > read_sleb128(size_t *size=nullptr) const
ok_error_t read_array(std::array< T, N > &dst) const
Definition BinaryStream.hpp:238
bool can_read(int64_t offset, int64_t size) const
Definition BinaryStream.hpp:268
ok_error_t read_data(std::vector< uint8_t > &container)
Definition BinaryStream.hpp:128
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:119
virtual uint8_t * end()
Definition BinaryStream.hpp:301
Generic interface representing a binary executable.
Definition Abstract/Binary.hpp:60
const BinaryStream & operator*() const
Definition BinaryStream.hpp:390
ScopedStream(BinaryStream &stream, uint64_t pos)
Definition BinaryStream.hpp:368
ScopedStream(BinaryStream &stream)
Definition BinaryStream.hpp:374
ScopedStream(const ScopedStream &)=delete
ScopedStream & operator=(const ScopedStream &)=delete
ScopedStream & operator=(ScopedStream &&)=delete
~ScopedStream()
Definition BinaryStream.hpp:378
BinaryStream & operator*()
Definition BinaryStream.hpp:386
ScopedStream(ScopedStream &&)=delete
BinaryStream * operator->()
Definition BinaryStream.hpp:382
ToggleEndianness(BinaryStream &stream, bool value)
Definition BinaryStream.hpp:407
ToggleEndianness(ToggleEndianness &&)=delete
ToggleEndianness & operator=(const ToggleEndianness &)=delete
ToggleEndianness & operator=(ToggleEndianness &&)=delete
BinaryStream & operator*()
Definition BinaryStream.hpp:427
BinaryStream * operator->()
Definition BinaryStream.hpp:423
ToggleEndianness(BinaryStream &stream)
Definition BinaryStream.hpp:413
~ToggleEndianness()
Definition BinaryStream.hpp:419
const BinaryStream & operator*() const
Definition BinaryStream.hpp:431
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:41
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:118
#define LIEF_API
Definition visibility.h:45