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