idakit-sys 0.1.0

Raw FFI bindings to IDA Pro's idalib runtime and the idakit C++ facade
Documentation
// Hand-written Custom bodies for the generated bytes domain (namespace idakit_gen). Raw byte reads,
// typed scalar reads (throw when any covered byte is uninitialized), string-literal decode, item
// classification, and linear navigation over bytes.hpp. SDK symbols that share a generated name are
// qualified with :: to avoid recursing into this namespace. min_ea/max_ea are templated, not here.

#include <pro.h>
#include <ida.hpp>

#include <bytes.hpp> // get_bytes, get_byte/word/dword/qword, is_loaded, get_flags, get_item_head,
                     // get_item_end, next_head, prev_head, get_cmt, get_strlit_contents,
                     // get_max_strlit_length, GMB_READALL, ALOPT_IGNHEADS, STRCONV_REPLCHAR

#include <stdexcept>
#include <vector>

#include "gen_bytes.h"

namespace idakit_gen {

namespace {
// A typed read must fail cleanly on a partly-mapped range, so gate on every covered byte.
bool range_loaded(ea_t ea, size_t n) {
  for (size_t i = 0; i < n; i++)
    if (!is_loaded(ea + i))
      return false;
  return true;
}
} // namespace

rust::Vec<uint8_t> get_bytes(uint64_t ea, size_t size) {
  std::vector<uint8_t> buf(size);
  int64_t r = (int64_t)::get_bytes(buf.data(), (ssize_t)size, (ea_t)ea, GMB_READALL);
  if (r < 0)
    throw std::runtime_error("get_bytes failed: unmapped range");
  rust::Vec<uint8_t> out;
  out.reserve(size);
  for (size_t i = 0; i < size; i++)
    out.push_back(buf[i]);
  return out;
}

uint8_t get_u8(uint64_t ea) {
  if (!range_loaded((ea_t)ea, 1))
    throw std::runtime_error("get_u8: byte uninitialized");
  return (uint8_t)get_byte((ea_t)ea);
}

uint16_t get_u16(uint64_t ea) {
  if (!range_loaded((ea_t)ea, 2))
    throw std::runtime_error("get_u16: covered byte uninitialized");
  return (uint16_t)get_word((ea_t)ea);
}

uint32_t get_u32(uint64_t ea) {
  if (!range_loaded((ea_t)ea, 4))
    throw std::runtime_error("get_u32: covered byte uninitialized");
  return (uint32_t)get_dword((ea_t)ea);
}

uint64_t get_u64(uint64_t ea) {
  if (!range_loaded((ea_t)ea, 8))
    throw std::runtime_error("get_u64: covered byte uninitialized");
  return (uint64_t)get_qword((ea_t)ea);
}

rust::String get_strlit(uint64_t ea, int32_t strtype) {
  size_t len = get_max_strlit_length((ea_t)ea, strtype, ALOPT_IGNHEADS);
  if (len == 0)
    throw std::runtime_error("no string literal at address");
  qstring out;
  ssize_t r = get_strlit_contents(&out, (ea_t)ea, len, strtype, nullptr, STRCONV_REPLCHAR);
  if (r < 0)
    throw std::runtime_error("undecodable string literal");
  return rust::String(out.c_str(), out.length());
}

uint64_t get_flags(uint64_t ea) { return (uint64_t)::get_flags((ea_t)ea); }

uint64_t get_item_head(uint64_t ea) { return (uint64_t)::get_item_head((ea_t)ea); }

uint64_t get_item_end(uint64_t ea) { return (uint64_t)::get_item_end((ea_t)ea); }

uint64_t get_next_head(uint64_t ea, uint64_t maxea) {
  return (uint64_t)next_head((ea_t)ea, (ea_t)maxea);
}

uint64_t get_prev_head(uint64_t ea, uint64_t minea) {
  return (uint64_t)prev_head((ea_t)ea, (ea_t)minea);
}

rust::String get_cmt(uint64_t ea, bool rptble) {
  qstring out;
  if (::get_cmt(&out, (ea_t)ea, rptble) < 0)
    throw std::runtime_error("no comment at address");
  return rust::String(out.c_str(), out.length());
}

} // namespace idakit_gen