current commit
260929.11 · deps: update lief to 1.0.0
/* Copyright 2017 - 2026 R. Thomas
 * Copyright 2017 - 2026 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.
 */
#include "LIEF/BinaryStream/BinaryStream.hpp"
#include "LIEF/DWARF/enums.hpp"
#include "third-party/utfcpp.hpp"

#include <mbedtls/x509.h>
#include <mbedtls/x509_crt.h>

#include <algorithm>

namespace LIEF {


result<int64_t> BinaryStream::read_dwarf_encoded(uint8_t encoding) const {
  const auto encodevalue = static_cast<LIEF::dwarf::EH_ENCODING>(encoding & 0x0F);

  switch (encodevalue) {
    case LIEF::dwarf::EH_ENCODING::ULEB128:
    {
      return read_uleb128();
    }

    case LIEF::dwarf::EH_ENCODING::SDATA2:
    case LIEF::dwarf::EH_ENCODING::UDATA2:
    {
      return read<int16_t>();
    }

    case LIEF::dwarf::EH_ENCODING::SDATA4:
    case LIEF::dwarf::EH_ENCODING::UDATA4:
    {
      return read<int32_t>();
    }

    case LIEF::dwarf::EH_ENCODING::SDATA8:
    case LIEF::dwarf::EH_ENCODING::UDATA8:
    {
      return read<int64_t>();
    }

    case LIEF::dwarf::EH_ENCODING::SLEB128:
    {
      return read_sleb128();
    }

    default:
    {
      return 0;
    }
  }
}

// A 64-bit integer is encoded on at most 10 LEB128 bytes
static constexpr unsigned LEB128_MAX_BYTES = 10;

result<uint64_t> BinaryStream::read_uleb128(size_t* size) const {
  uint64_t value = 0;
  unsigned shift = 0;
  unsigned count = 0;
  result<uint8_t> byte_read = 0;

  const uint64_t opos = pos();

  do {
    if (count == LEB128_MAX_BYTES) {
      return make_error_code(lief_errors::corrupted);
    }

    byte_read = read<uint8_t>();
    if (!byte_read) {
      return make_error_code(lief_errors::read_error);
    }
    ++count;

    const uint64_t slice = *byte_read & 0x7f;

    if (shift < 64u) {
      if ((slice << shift >> shift) != slice) {
        return make_error_code(lief_errors::corrupted);
      }
      value |= slice << shift;
      shift += 7;
    } else if (slice != 0) {
      return make_error_code(lief_errors::corrupted);
    }
  } while (*byte_read >= 128);

  if (size != nullptr) {
    *size = pos() - opos;
  }

  return value;
}

result<uint64_t> BinaryStream::read_sleb128(size_t* size) const {
  int64_t value = 0;
  unsigned shift = 0;
  unsigned count = 0;
  result<uint8_t> byte_read = 0;
  const uint64_t opos = pos();

  do {
    if (count == LEB128_MAX_BYTES) {
      return make_error_code(lief_errors::corrupted);
    }

    byte_read = read<uint8_t>();
    if (!byte_read) {
      return make_error_code(lief_errors::read_error);
    }
    ++count;

    const uint64_t slice = *byte_read & 0x7f;

    if (shift < 64u) {
      value |= (int64_t)(slice << shift);
      shift += 7;
    } else if (slice != ((value < 0) ? 0x7fu : 0x00u)) {
      return make_error_code(lief_errors::corrupted);
    }
  } while (*byte_read >= 128);

  if (shift < 64u && (*byte_read & 0x40) != 0) {
    value |= (int64_t)-1llu << shift;
  }

  if (size != nullptr) {
    *size = pos() - opos;
  }

  return (uint64_t)value;
}

result<std::string> BinaryStream::read_string(size_t maxsize) const {
  result<std::string> str = peek_string(maxsize);
  if (!str) {
    return str;
  }
  increment_pos(str->size() + 1); // +1 for'\0'
  return str;
}

result<std::string> BinaryStream::peek_string(size_t maxsize) const {
  std::string str_result;
  str_result.reserve(10);
  result<char> c = '\0';
  size_t off = pos();

  if (!can_read<char>()) {
    return str_result;
  }

  size_t count = 0;
  do {
    c = peek<char>(off);
    if (!c) {
      return make_error_code(c.error());
    }
    off += sizeof(char);
    str_result.push_back(*c);
    ++count;
  } while (count < maxsize && c && *c != '\0' && off < size());
  str_result.back() = '\0';
  return str_result.c_str();
}

result<std::string> BinaryStream::peek_string_at(size_t offset,
                                                 size_t maxsize) const {
  size_t saved_offset = pos();
  setpos(offset);
  result<std::string> tmp = peek_string(maxsize);
  setpos(saved_offset);
  return tmp;
}

result<std::u16string> BinaryStream::read_u16string() const {
  result<std::u16string> str = peek_u16string();
  if (!str) {
    return str;
  }
  increment_pos((str->size() + 1) * sizeof(uint16_t)); // +1 for'\0'
  return str.value();
}

result<std::u16string> BinaryStream::peek_u16string() const {
  std::u16string u16_str;
  u16_str.reserve(10);
  result<char16_t> c = char16_t{0};
  size_t off = pos();

  if (!can_read<char16_t>()) {
    return u16_str;
  }

  do {
    c = peek<char16_t>(off);
    if (!c) {
      return make_error_code(c.error());
    }
    off += sizeof(char16_t);
    u16_str.push_back(*c);
  } while (c && *c != 0 && off < size());
  u16_str.back() = '\0';
  return u16_str.c_str();
}


result<std::u16string> BinaryStream::read_u16string(size_t length) const {
  result<std::u16string> str = peek_u16string(length);
  increment_pos(length * sizeof(uint16_t)); // +1 for'\0'
  return str;
}

result<std::u16string> BinaryStream::peek_u16string(size_t length) const {
  if (length == static_cast<size_t>(SIZE_MAX)) {
    return peek_u16string();
  }

  std::vector<char16_t> raw_u16str;
  raw_u16str.resize(length, 0);
  if (peek_in(raw_u16str.data(), pos(), length * sizeof(char16_t))) {
    if (endian_swap_) {
      for (char16_t& x : raw_u16str) {
        LIEF::swap_endian(&x);
      }
    }
    return std::u16string{raw_u16str.begin(), raw_u16str.end()};
  }
  return make_error_code(lief_errors::read_error);
}

result<std::u16string> BinaryStream::peek_u16string_at(size_t offset,
                                                       size_t length) const {
  size_t saved_offset = pos();
  setpos(offset);
  result<std::u16string> tmp = peek_u16string(length);
  setpos(saved_offset);
  return tmp;
}


size_t BinaryStream::align(size_t align_on) const {
  if (align_on == 0) {
    return 0;
  }
  const size_t r = pos() % align_on;
  if (r == 0) {
    return 0;
  }
  size_t padding = align_on - r;
  increment_pos(padding);
  return padding;
}


result<std::string> BinaryStream::read_mutf8(size_t maxsize) const {
  std::u32string u32str;

  for (size_t i = 0; i < maxsize; ++i) {
    result<uint8_t> res_a = read<char>();
    if (!res_a) {
      return make_error_code(res_a.error());
    }
    uint8_t a = *res_a;

    if (static_cast<uint8_t>(a) < 0x80) {
      if (a == 0) {
        break;
      }
      u32str.push_back(a);
    } else if ((a & 0xe0) == 0xc0) {

      result<uint8_t> res_b = read<int8_t>();
      if (!res_b) {
        return make_error_code(res_b.error());
      }
      uint8_t b = *res_b & 0xFF;

      if ((b & 0xC0) != 0x80) {
        break;
      }
      u32str.push_back(static_cast<char32_t>((((a & 0x1F) << 6) | (b & 0x3F))));
    } else if ((a & 0xf0) == 0xe0) {
      result<uint8_t> res_b = read<uint8_t>();
      result<uint8_t> res_c = read<uint8_t>();
      if (!res_b) {
        return make_error_code(res_b.error());
      }
      if (!res_c) {
        return make_error_code(res_c.error());
      }
      uint8_t b = *res_b;
      uint8_t c = *res_c;

      if (((b & 0xC0) != 0x80) || ((c & 0xC0) != 0x80)) {
        break;
      }
      u32str.push_back(static_cast<char32_t>(((a & 0x1F) << 12) |
                                             ((b & 0x3F) << 6) | (c & 0x3F)));
    } else {
      break;
    }
  }

  std::string u8str;

  std::replace_if(
      u32str.begin(), u32str.end(),
      [](const char32_t c) { return !utf8::internal::is_code_point_valid(c); }, '.'
  );

  utf8::unchecked::utf32to8(u32str.begin(), u32str.end(),
                            std::back_inserter(u8str));
  return u8str;
}

}