/* Copyright 2017 - 2022 R. Thomas * Copyright 2017 - 2022 Quarkslab * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef LIEF_BINARY_STREAM_H_ #define LIEF_BINARY_STREAM_H_ #include #include #include #include #include #include #include #include "LIEF/BinaryStream/Convert.hpp" #include "LIEF/errors.hpp" struct mbedtls_x509_crt; struct mbedtls_x509_time; namespace LIEF { //! Class that is used to a read stream of data from different sources class BinaryStream { public: enum class STREAM_TYPE { UNKNOWN = 0, VECTOR, MEMORY, SPAN, FILE, ELF_DATA_HANDLER, }; BinaryStream(); virtual ~BinaryStream(); virtual uint64_t size() const = 0; inline STREAM_TYPE type() const { return stype_; } result read_uleb128() const; result read_sleb128() const; result read_dwarf_encoded(uint8_t encoding) const; result read_string(size_t maxsize = ~static_cast(0)) const; result peek_string(size_t maxsize = ~static_cast(0)) const; result peek_string_at(size_t offset, size_t maxsize = ~static_cast(0)) const; result read_u16string() const; result peek_u16string() const; result read_mutf8(size_t maxsize = ~static_cast(0)) const; result read_u16string(size_t length) const; result peek_u16string(size_t length) const; result peek_u16string_at(size_t offset, size_t length) const; virtual inline ok_error_t peek_data(std::vector& container, uint64_t offset, uint64_t size) { if (size == 0) { return ok(); } // Even though offset + size < ... => offset < ... // the addition could overflow so it's worth checking both const bool read_ok = offset <= this->size() && (offset + size) <= this->size() /* Check for an overflow */ && (static_cast(offset) >= 0 && static_cast(size) >= 0) && (static_cast(offset + size) >= 0); if (!read_ok) { return make_error_code(lief_errors::read_error); } container.resize(size); if (peek_in(container.data(), offset, size)) { return ok(); } return make_error_code(lief_errors::read_error); } virtual inline ok_error_t read_data(std::vector& container, uint64_t size) { if (!peek_data(container, pos(), size)) { return make_error_code(lief_errors::read_error); } increment_pos(size); return ok(); } void setpos(size_t pos) const; void increment_pos(size_t value) const; void decrement_pos(size_t value) const; size_t pos() const; operator bool() const; template const T* read_array(size_t size) const; template result peek() const; template result peek(size_t offset) const; template const T* peek_array(size_t size) const; template const T* peek_array(size_t offset, size_t size) const; template result read() const; template bool can_read() const; template bool can_read(size_t offset) const; size_t align(size_t align_on) const; /* Functions that are endianness aware */ template typename std::enable_if::value, result>::type peek_conv() const; template typename std::enable_if::value, result>::type peek_conv() const; template result peek_conv(size_t offset) const; template result read_conv() const; /* Read an array of values and adjust endianness as needed */ template std::unique_ptr read_conv_array(size_t size) const; template std::unique_ptr peek_conv_array(size_t offset, size_t size) const; template static T swap_endian(T u); void set_endian_swap(bool swap); /* ASN.1 & X509 parsing functions */ virtual result asn1_read_tag(int tag); virtual result asn1_read_len(); virtual result asn1_read_alg(); virtual result asn1_read_oid(); virtual result asn1_read_int(); virtual result> asn1_read_bitstring(); virtual result> asn1_read_octet_string(); virtual result> asn1_read_cert(); virtual result x509_read_names(); virtual result> x509_read_serial(); virtual result> x509_read_time(); template static bool is_all_zero(const T& buffer) { const auto* ptr = reinterpret_cast(&buffer); return std::all_of(ptr, ptr + sizeof(T), [] (uint8_t x) { return x == 0; }); } inline bool should_swap() const { return endian_swap_; } protected: virtual result read_at(uint64_t offset, uint64_t size) const = 0; inline virtual ok_error_t peek_in(void* dst, uint64_t offset, uint64_t size) const { if (auto raw = read_at(offset, size)) { if (dst == nullptr) { return make_error_code(lief_errors::read_error); } const void* ptr = *raw; if (ptr == nullptr) { return make_error_code(lief_errors::read_error); } memcpy(dst, ptr, size); return ok(); } return make_error_code(lief_errors::read_error); } mutable size_t pos_ = 0; bool endian_swap_ = false; STREAM_TYPE stype_ = STREAM_TYPE::UNKNOWN; }; class ScopedStream { public: ScopedStream(const ScopedStream&) = delete; ScopedStream& operator=(const ScopedStream&) = delete; ScopedStream(ScopedStream&&) = delete; ScopedStream& operator=(ScopedStream&&) = delete; explicit ScopedStream(BinaryStream& stream, uint64_t pos) : pos_{stream.pos()}, stream_{stream} { stream_.setpos(pos); } explicit ScopedStream(BinaryStream& stream) : pos_{stream.pos()}, stream_{stream} {} inline ~ScopedStream() { stream_.setpos(pos_); } private: uint64_t pos_ = 0; BinaryStream& stream_; }; template result BinaryStream::read() const { result tmp = this->peek(); if (!tmp) { return tmp.error(); } this->increment_pos(sizeof(T)); return tmp; } template result BinaryStream::peek() const { const auto current_p = pos(); T ret{}; if (auto res = peek_in(&ret, pos(), sizeof(T))) { setpos(current_p); return ret; } setpos(current_p); return make_error_code(lief_errors::read_error); } template result BinaryStream::peek(size_t offset) const { size_t saved_offset = this->pos(); this->setpos(offset); result r = this->peek(); this->setpos(saved_offset); return r; } template const T* BinaryStream::peek_array(size_t size) const { result raw = this->read_at(this->pos(), sizeof(T) * size); if (!raw) { return nullptr; } return reinterpret_cast(raw.value()); } template const T* BinaryStream::peek_array(size_t offset, size_t size) const { size_t saved_offset = this->pos(); this->setpos(offset); const T* r = this->peek_array(size); this->setpos(saved_offset); return r; } template bool BinaryStream::can_read() const { // Even though pos_ + sizeof(T) < ... => pos_ < ... // the addition could overflow so it's worth checking both return pos_ < size() && (pos_ + sizeof(T)) < size(); } template bool BinaryStream::can_read(size_t offset) const { // Even though offset + sizeof(T) < ... => offset < ... // the addition could overflow so it's worth checking both return offset < size() && (offset + sizeof(T)) < size(); } template const T* BinaryStream::read_array(size_t size) const { const T* tmp = this->peek_array(size); this->increment_pos(sizeof(T) * size); return tmp; } template result BinaryStream::read_conv() const { result tmp = this->peek_conv(); if (!tmp) { return tmp.error(); } this->increment_pos(sizeof(T)); return tmp; } template typename std::enable_if::value, result>::type BinaryStream::peek_conv() const { T ret; if (auto res = peek_in(&ret, pos(), sizeof(T))) { if (endian_swap_) { return swap_endian(ret); } return ret; } return make_error_code(lief_errors::read_error); } template typename std::enable_if::value, result>::type BinaryStream::peek_conv() const { T ret; if (auto res = peek_in(&ret, pos(), sizeof(T))) { if (endian_swap_) { LIEF::Convert::swap_endian(&ret); } return ret; } return make_error_code(lief_errors::read_error); } template result BinaryStream::peek_conv(size_t offset) const { size_t saved_offset = this->pos(); this->setpos(offset); result r = this->peek_conv(); this->setpos(saved_offset); return r; } template std::unique_ptr BinaryStream::read_conv_array(size_t size) const { const T *t = this->read_array(size); if (t == nullptr) { return nullptr; } std::unique_ptr uptr(new T[size]); for (size_t i = 0; i < size; i++) { uptr[i] = t[i]; if (this->endian_swap_) { LIEF::Convert::swap_endian(& uptr[i]); } /* else no conversion, just provide the copied data */ } return uptr; } template std::unique_ptr BinaryStream::peek_conv_array(size_t offset, size_t size) const { const T *t = this->peek_array(offset, size); if (t == nullptr) { return nullptr; } std::unique_ptr uptr(new T[size]); for (size_t i = 0; i < size; i++) { uptr[i] = t[i]; if (this->endian_swap_) { LIEF::Convert::swap_endian(& uptr[i]); } /* else no conversion, just provide the copied data */ } return uptr; } } #endif