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