LIEF: Library to Instrument Executable Formats Version 0.15.1
Loading...
Searching...
No Matches
BinaryStream.hpp
Go to the documentation of this file.
1/* Copyright 2017 - 2024 R. Thomas
2 * Copyright 2017 - 2024 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 <memory>
22#include <cstring>
23#include <string>
24#include <type_traits>
25#include <algorithm>
26
28#include "LIEF/errors.hpp"
29
30namespace LIEF {
31class ASN1Reader;
32
35 public:
36 friend class ASN1Reader;
37
38 enum class STREAM_TYPE {
39 UNKNOWN = 0,
40 VECTOR,
41 MEMORY,
42 SPAN,
43 FILE,
44
46 };
47
49 stype_(type)
50 {}
51 virtual ~BinaryStream() = default;
52 virtual uint64_t size() const = 0;
53
54 STREAM_TYPE type() const {
55 return stype_;
56 }
57
60
61 result<int64_t> read_dwarf_encoded(uint8_t encoding) const;
62
63 result<std::string> read_string(size_t maxsize = ~static_cast<size_t>(0)) const;
64 result<std::string> peek_string(size_t maxsize = ~static_cast<size_t>(0)) const;
65 result<std::string> peek_string_at(size_t offset, size_t maxsize = ~static_cast<size_t>(0)) const;
66
69
70 result<std::string> read_mutf8(size_t maxsize = ~static_cast<size_t>(0)) const;
71
74 result<std::u16string> peek_u16string_at(size_t offset, size_t length) const;
75
76
77 virtual ok_error_t peek_data(std::vector<uint8_t>& container,
78 uint64_t offset, uint64_t size)
79 {
80
81 if (size == 0) {
82 return ok();
83 }
84 // Even though offset + size < ... => offset < ...
85 // the addition could overflow so it's worth checking both
86 const bool read_ok = offset <= this->size() && (offset + size) <= this->size()
87 /* Check for an overflow */
88 && (static_cast<int64_t>(offset) >= 0 && static_cast<int64_t>(size) >= 0)
89 && (static_cast<int64_t>(offset + size) >= 0);
90 if (!read_ok) {
92 }
93 container.resize(size);
94 if (peek_in(container.data(), offset, size)) {
95 return ok();
96 }
98 }
99
100 virtual ok_error_t read_data(std::vector<uint8_t>& container, uint64_t size) {
101 if (!peek_data(container, pos(), size)) {
103 }
104
106 return ok();
107 }
108
109 void setpos(size_t pos) const {
110 pos_ = pos;
111 }
112
113 void increment_pos(size_t value) const {
114 pos_ += value;
115 }
116
117 void decrement_pos(size_t value) const {
118 if (pos_ > value) {
119 pos_ -= value;
120 } else {
121 pos_ = 0;
122 }
123 }
124
125 size_t pos() const {
126 return pos_;
127 }
128
129 operator bool() const {
130 return pos_ < size();
131 }
132
133 template<class T>
134 const T* read_array(size_t size) const;
135
136 template<class T>
137 result<T> peek() const;
138
139 template<class T>
140 result<T> peek(size_t offset) const;
141
142 template<class T>
143 const T* peek_array(size_t size) const;
144
145 template<class T>
146 const T* peek_array(size_t offset, size_t size) const;
147
148 template<class T>
149 result<T> read() const;
150
151 template<typename T>
152 bool can_read() const;
153
154 template<typename T>
155 bool can_read(size_t offset) const;
156
157 size_t align(size_t align_on) const;
158
159 /* Functions that are endianness aware */
160 template<class T>
161 typename std::enable_if<std::is_integral<T>::value, result<T>>::type
162 peek_conv() const;
163
164 template<class T>
165 typename std::enable_if<!std::is_integral<T>::value, result<T>>::type
166 peek_conv() const;
167
168 template<class T>
169 result<T> peek_conv(size_t offset) const;
170
171 template<class T>
172 result<T> read_conv() const;
173
174 /* Read an array of values and adjust endianness as needed */
175 template<typename T>
176 std::unique_ptr<T[]> read_conv_array(size_t size) const;
177
178 template<typename T>
179 std::unique_ptr<T[]> peek_conv_array(size_t offset, size_t size) const;
180
181 template<typename T>
182 static T swap_endian(T u);
183
184 void set_endian_swap(bool swap) {
185 endian_swap_ = swap;
186 }
187
188 template<class T>
189 static bool is_all_zero(const T& buffer) {
190 const auto* ptr = reinterpret_cast<const uint8_t *const>(&buffer);
191 return std::all_of(ptr, ptr + sizeof(T),
192 [] (uint8_t x) { return x == 0; });
193 }
194
195 bool should_swap() const {
196 return endian_swap_;
197 }
198
199 virtual const uint8_t* p() const {
200 return nullptr;
201 }
202
203 virtual uint8_t* start() {
204 return const_cast<uint8_t*>(static_cast<const BinaryStream*>(this)->start());
205 }
206
207 virtual uint8_t* p() {
208 return const_cast<uint8_t*>(static_cast<const BinaryStream*>(this)->p());
209 }
210
211 virtual uint8_t* end() {
212 return const_cast<uint8_t*>(static_cast<const BinaryStream*>(this)->end());
213 }
214
215 virtual const uint8_t* start() const {
216 return nullptr;
217 }
218
219 virtual const uint8_t* end() const {
220 return nullptr;
221 }
222
223 protected:
224 BinaryStream() = default;
225 virtual result<const void*> read_at(uint64_t offset, uint64_t size) const = 0;
226 virtual ok_error_t peek_in(void* dst, uint64_t offset, uint64_t size) const {
227 if (auto raw = read_at(offset, size)) {
228 if (dst == nullptr) {
230 }
231
232 const void* ptr = *raw;
233
234 if (ptr == nullptr) {
236 }
237
238 memcpy(dst, ptr, size);
239 return ok();
240 }
242 }
243 mutable size_t pos_ = 0;
244 bool endian_swap_ = false;
246};
247
249 public:
250 ScopedStream(const ScopedStream&) = delete;
252
255
256 explicit ScopedStream(BinaryStream& stream, uint64_t pos) :
257 pos_{stream.pos()},
258 stream_{stream}
259 {
260 stream_.setpos(pos);
261 }
262
263 explicit ScopedStream(BinaryStream& stream) :
264 pos_{stream.pos()},
265 stream_{stream}
266 {}
267
269 stream_.setpos(pos_);
270 }
271
273 return &stream_;
274 }
275
277 return stream_;
278 }
279
280 const BinaryStream& operator*() const {
281 return stream_;
282 }
283
284 private:
285 uint64_t pos_ = 0;
286 BinaryStream& stream_;
287};
288
289
290template<class T>
292 result<T> tmp = this->peek<T>();
293 if (!tmp) {
294 return tmp;
295 }
296 this->increment_pos(sizeof(T));
297 return tmp;
298}
299
300template<class T>
302 const auto current_p = pos();
303 T ret{};
304 if (auto res = peek_in(&ret, pos(), sizeof(T))) {
305 setpos(current_p);
306 return ret;
307 }
308
309 setpos(current_p);
311}
312
313template<class T>
314result<T> BinaryStream::peek(size_t offset) const {
315 const size_t saved_offset = this->pos();
316 this->setpos(offset);
317 result<T> r = this->peek<T>();
318 this->setpos(saved_offset);
319 return r;
320}
321
322
323template<class T>
324const T* BinaryStream::peek_array(size_t size) const {
325 result<const void*> raw = this->read_at(this->pos(), sizeof(T) * size);
326 if (!raw) {
327 return nullptr;
328 }
329 return reinterpret_cast<const T*>(raw.value());
330}
331
332template<class T>
333const T* BinaryStream::peek_array(size_t offset, size_t size) const {
334 const size_t saved_offset = this->pos();
335 this->setpos(offset);
336 const T* r = this->peek_array<T>(size);
337 this->setpos(saved_offset);
338 return r;
339}
340
341
342template<typename T>
344 // Even though pos_ + sizeof(T) < ... => pos_ < ...
345 // the addition could overflow so it's worth checking both
346 return pos_ < size() && (pos_ + sizeof(T)) < size();
347}
348
349
350template<typename T>
351bool BinaryStream::can_read(size_t offset) const {
352 // Even though offset + sizeof(T) < ... => offset < ...
353 // the addition could overflow so it's worth checking both
354 return offset < size() && (offset + sizeof(T)) < size();
355}
356
357
358template<class T>
359const T* BinaryStream::read_array(size_t size) const {
360 const T* tmp = this->peek_array<T>(size);
361 this->increment_pos(sizeof(T) * size);
362 return tmp;
363}
364
365
366template<class T>
368 result<T> tmp = this->peek_conv<T>();
369 if (!tmp) {
370 return tmp;
371 }
372 this->increment_pos(sizeof(T));
373 return tmp;
374}
375
376template<class T>
377typename std::enable_if<std::is_integral<T>::value, result<T>>::type
379 T ret;
380 if (auto res = peek_in(&ret, pos(), sizeof(T))) {
381 return endian_swap_ ? swap_endian<T>(ret) : ret;
382 }
384}
385
386template<class T>
387typename std::enable_if<!std::is_integral<T>::value, result<T>>::type
389 T ret;
390 if (auto res = peek_in(&ret, pos(), sizeof(T))) {
391 if (endian_swap_) {
393 }
394 return ret;
395 }
397}
398
399
400template<class T>
401result<T> BinaryStream::peek_conv(size_t offset) const {
402 const size_t saved_offset = this->pos();
403 this->setpos(offset);
404 result<T> r = this->peek_conv<T>();
405 this->setpos(saved_offset);
406 return r;
407}
408
409
410template<typename T>
411std::unique_ptr<T[]> BinaryStream::read_conv_array(size_t size) const {
412 const T *t = this->read_array<T>(size);
413
414 if (t == nullptr) {
415 return nullptr;
416 }
417
418 std::unique_ptr<T[]> uptr(new T[size]);
419
420 for (size_t i = 0; i < size; i++) {
421 uptr[i] = t[i];
422 if (this->endian_swap_) {
424 } /* else no conversion, just provide the copied data */
425 }
426 return uptr;
427}
428
429
430template<typename T>
431std::unique_ptr<T[]> BinaryStream::peek_conv_array(size_t offset, size_t size) const {
432 const T *t = this->peek_array<T>(offset, size);
433
434 if (t == nullptr) {
435 return nullptr;
436 }
437
438 std::unique_ptr<T[]> uptr(new T[size]);
439
440 for (size_t i = 0; i < size; i++) {
441 uptr[i] = t[i];
442 if (this->endian_swap_) {
444 } /* else no conversion, just provide the copied data */
445 }
446 return uptr;
447}
448}
449#endif
Definition ASN1Reader.hpp:32
Class that is used to a read stream of data from different sources.
Definition BinaryStream.hpp:34
size_t align(size_t align_on) const
void increment_pos(size_t value) const
Definition BinaryStream.hpp:113
result< std::string > read_mutf8(size_t maxsize=~static_cast< size_t >(0)) const
STREAM_TYPE
Definition BinaryStream.hpp:38
result< uint64_t > read_sleb128() const
virtual ok_error_t peek_data(std::vector< uint8_t > &container, uint64_t offset, uint64_t size)
Definition BinaryStream.hpp:77
result< T > peek() const
Definition BinaryStream.hpp:301
result< uint64_t > read_uleb128() const
result< int64_t > read_dwarf_encoded(uint8_t encoding) const
static T swap_endian(T u)
virtual uint8_t * p()
Definition BinaryStream.hpp:207
result< std::string > peek_string(size_t maxsize=~static_cast< size_t >(0)) const
virtual const uint8_t * p() const
Definition BinaryStream.hpp:199
const T * read_array(size_t size) const
Definition BinaryStream.hpp:359
virtual uint64_t size() const =0
result< std::u16string > peek_u16string_at(size_t offset, size_t length) const
bool can_read() const
Definition BinaryStream.hpp:343
result< std::u16string > peek_u16string(size_t length) const
std::unique_ptr< T[]> peek_conv_array(size_t offset, size_t size) const
Definition BinaryStream.hpp:431
result< std::u16string > read_u16string() const
void set_endian_swap(bool swap)
Definition BinaryStream.hpp:184
std::enable_if< std::is_integral< T >::value, result< T > >::type peek_conv() const
Definition BinaryStream.hpp:378
result< std::u16string > peek_u16string() const
bool should_swap() const
Definition BinaryStream.hpp:195
result< std::string > peek_string_at(size_t offset, size_t maxsize=~static_cast< size_t >(0)) const
std::unique_ptr< T[]> read_conv_array(size_t size) const
Definition BinaryStream.hpp:411
size_t pos() const
Definition BinaryStream.hpp:125
virtual uint8_t * start()
Definition BinaryStream.hpp:203
result< T > read_conv() const
Definition BinaryStream.hpp:367
const T * peek_array(size_t size) const
Definition BinaryStream.hpp:324
void setpos(size_t pos) const
Definition BinaryStream.hpp:109
virtual const uint8_t * end() const
Definition BinaryStream.hpp:219
static bool is_all_zero(const T &buffer)
Definition BinaryStream.hpp:189
result< std::string > read_string(size_t maxsize=~static_cast< size_t >(0)) const
result< std::u16string > read_u16string(size_t length) const
void decrement_pos(size_t value) const
Definition BinaryStream.hpp:117
result< T > read() const
Definition BinaryStream.hpp:291
BinaryStream(STREAM_TYPE type)
Definition BinaryStream.hpp:48
virtual const uint8_t * start() const
Definition BinaryStream.hpp:215
virtual ~BinaryStream()=default
STREAM_TYPE type() const
Definition BinaryStream.hpp:54
virtual ok_error_t read_data(std::vector< uint8_t > &container, uint64_t size)
Definition BinaryStream.hpp:100
virtual uint8_t * end()
Definition BinaryStream.hpp:211
Definition BinaryStream.hpp:248
const BinaryStream & operator*() const
Definition BinaryStream.hpp:280
ScopedStream(BinaryStream &stream, uint64_t pos)
Definition BinaryStream.hpp:256
ScopedStream(BinaryStream &stream)
Definition BinaryStream.hpp:263
ScopedStream(const ScopedStream &)=delete
ScopedStream & operator=(const ScopedStream &)=delete
ScopedStream & operator=(ScopedStream &&)=delete
~ScopedStream()
Definition BinaryStream.hpp:268
BinaryStream & operator*()
Definition BinaryStream.hpp:276
ScopedStream(ScopedStream &&)=delete
BinaryStream * operator->()
Definition BinaryStream.hpp:272
tl::unexpected< lief_errors > make_error_code(lief_errors e)
Create an standard error code from lief_errors.
Definition errors.hpp:51
void swap_endian(T *)
LIEF namespace.
Definition Abstract/Binary.hpp:32
result< ok_t > ok_error_t
Opaque structure that is used by LIEF to avoid writing result<void> f(...). Instead,...
Definition errors.hpp:108
ok_t ok()
Return success for function with return type ok_error_t.
Definition errors.hpp:92
tl::expected< T, lief_errors > result
Wrapper that contains an Object (T) or an error.
Definition errors.hpp:74