binaryninja/binary_view/
reader.rs

1// Copyright 2022-2025 Vector 35 Inc.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! A convenience class for reading binary data
16
17use binaryninjacore_sys::*;
18use std::fmt::Debug;
19
20use crate::binary_view::{BinaryView, BinaryViewBase};
21use crate::Endianness;
22
23use crate::rc::Ref;
24use std::io::{ErrorKind, Read, Seek, SeekFrom};
25
26pub struct BinaryReader {
27    view: Ref<BinaryView>,
28    handle: *mut BNBinaryReader,
29}
30
31impl BinaryReader {
32    pub fn new(view: &BinaryView) -> Self {
33        let handle = unsafe { BNCreateBinaryReader(view.handle) };
34        Self {
35            view: view.to_owned(),
36            handle,
37        }
38    }
39
40    pub fn new_with_opts(view: &BinaryView, options: &BinaryReaderOptions) -> Self {
41        let mut reader = Self::new(view);
42        if let Some(endianness) = options.endianness {
43            reader.set_endianness(endianness);
44        }
45        // Set the virtual base before we seek.
46        if let Some(virtual_base) = options.virtual_base {
47            reader.set_virtual_base(virtual_base);
48        }
49        if let Some(address) = options.address {
50            reader.seek_to_offset(address);
51        }
52        reader
53    }
54
55    pub fn endianness(&self) -> Endianness {
56        unsafe { BNGetBinaryReaderEndianness(self.handle) }
57    }
58
59    pub fn set_endianness(&mut self, endianness: Endianness) {
60        unsafe { BNSetBinaryReaderEndianness(self.handle, endianness) }
61    }
62
63    pub fn virtual_base(&self) -> u64 {
64        unsafe { BNGetBinaryReaderVirtualBase(self.handle) }
65    }
66
67    pub fn set_virtual_base(&mut self, virtual_base_addr: u64) {
68        unsafe { BNSetBinaryReaderVirtualBase(self.handle, virtual_base_addr) }
69    }
70
71    /// Prefer using [BinaryReader::seek] over this.
72    pub fn seek_to_offset(&mut self, offset: u64) {
73        unsafe { BNSeekBinaryReader(self.handle, offset) }
74    }
75
76    /// Prefer using [BinaryReader::seek] over this.
77    pub fn seek_to_relative_offset(&mut self, offset: i64) {
78        unsafe { BNSeekBinaryReaderRelative(self.handle, offset) }
79    }
80
81    pub fn offset(&self) -> u64 {
82        unsafe { BNGetReaderPosition(self.handle) }
83    }
84
85    /// Are we at the end of the file?
86    pub fn is_eof(&self) -> bool {
87        unsafe { BNIsEndOfFile(self.handle) }
88    }
89}
90
91impl Debug for BinaryReader {
92    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
93        f.debug_struct("BinaryReader")
94            .field("offset", &self.offset())
95            .field("virtual_base", &self.virtual_base())
96            .field("endianness", &self.endianness())
97            .finish()
98    }
99}
100
101impl Seek for BinaryReader {
102    /// Seek to the specified position.
103    fn seek(&mut self, pos: SeekFrom) -> std::io::Result<u64> {
104        match pos {
105            SeekFrom::Current(offset) => self.seek_to_relative_offset(offset),
106            SeekFrom::Start(offset) => self.seek_to_offset(offset),
107            SeekFrom::End(end_offset) => {
108                // We do NOT need to add the image base here as
109                // the reader (unlike the writer) can set the virtual base.
110                let offset =
111                    self.view
112                        .len()
113                        .checked_add_signed(end_offset)
114                        .ok_or(std::io::Error::new(
115                            ErrorKind::Other,
116                            "Seeking from end overflowed",
117                        ))?;
118                self.seek_to_offset(offset);
119            }
120        };
121
122        Ok(self.offset())
123    }
124}
125
126impl Read for BinaryReader {
127    fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
128        let len = buf.len();
129
130        let result = unsafe { BNReadData(self.handle, buf.as_mut_ptr() as *mut _, len) };
131
132        if !result {
133            Err(std::io::Error::new(ErrorKind::Other, "Read out of bounds"))
134        } else {
135            Ok(len)
136        }
137    }
138}
139
140impl Drop for BinaryReader {
141    fn drop(&mut self) {
142        unsafe { BNFreeBinaryReader(self.handle) }
143    }
144}
145
146unsafe impl Sync for BinaryReader {}
147unsafe impl Send for BinaryReader {}
148
149#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
150pub struct BinaryReaderOptions {
151    endianness: Option<Endianness>,
152    virtual_base: Option<u64>,
153    address: Option<u64>,
154}
155
156impl BinaryReaderOptions {
157    pub fn new() -> Self {
158        Self::default()
159    }
160
161    pub fn with_endianness(mut self, endian: Endianness) -> Self {
162        self.endianness = Some(endian);
163        self
164    }
165
166    pub fn with_virtual_base(mut self, virtual_base_addr: u64) -> Self {
167        self.virtual_base = Some(virtual_base_addr);
168        self
169    }
170
171    pub fn with_address(mut self, address: u64) -> Self {
172        self.address = Some(address);
173        self
174    }
175}