summaryrefslogtreecommitdiff
path: root/view/bintxt/src/ihex.rs
diff options
context:
space:
mode:
authorRubens Brandao <git@rubens.io>2024-08-06 11:01:23 -0300
committerMason Reed <mason@vector35.com>2024-08-27 17:19:25 -0400
commit7e1027d904e66b0cadb277883cdfa7b13dcb209f (patch)
tree4451fe8134e7518d43b1e04c1446f56d3bb2091e /view/bintxt/src/ihex.rs
parentfeca74615ab3e10e7e256630946d830d07414d08 (diff)
Implement IHEX, SREC and TI-TXT views
All bundled under the arch_bintxt/arch_bintext view plugin
Diffstat (limited to 'view/bintxt/src/ihex.rs')
-rw-r--r--view/bintxt/src/ihex.rs237
1 files changed, 237 insertions, 0 deletions
diff --git a/view/bintxt/src/ihex.rs b/view/bintxt/src/ihex.rs
new file mode 100644
index 00000000..5b210ff4
--- /dev/null
+++ b/view/bintxt/src/ihex.rs
@@ -0,0 +1,237 @@
+use binaryninja::binaryview::*;
+use binaryninja::custombinaryview::*;
+use binaryninja::rc::Ref;
+use binaryninja::segment::SegmentBuilder;
+use ihex::Record;
+
+use crate::segment_after_address;
+use crate::segment_from_address;
+use crate::sort_and_merge_segments;
+use crate::MergedSegment;
+use crate::UnmergedSegment;
+
+fn parse_ihex(string: &str) -> Result<(Vec<u8>, IHexViewData)> {
+ let mut reader = ihex::Reader::new(&string);
+ let mut unmerged_data: Vec<UnmergedSegment> = vec![];
+ let mut start = None;
+ let mut offset = None;
+ while let Some(record) = reader.next() {
+ let record = record.map_err(|e| {
+ log::error!("Unable to parse record: {e}");
+ ()
+ })?;
+ match record {
+ ihex::Record::EndOfFile => break,
+ ihex::Record::Data {
+ offset: data_offset,
+ value,
+ } => {
+ let address = match offset {
+ Some(IHexOffset::Segment(offset)) => {
+ u64::from(offset) * 16 + u64::from(data_offset)
+ }
+ Some(IHexOffset::Linear(offset)) => {
+ u64::from(offset) << 16 | u64::from(data_offset)
+ }
+ None => data_offset.into(),
+ };
+ // check if the block is just extending the previous one
+ match unmerged_data.last_mut() {
+ // check if the block is just extending the previous one, merge both
+ Some(last) if last.end() == address => last.data.extend(value),
+ // otherwise just add a new block
+ _ => unmerged_data.push(UnmergedSegment {
+ address,
+ data: value,
+ }),
+ }
+ }
+ ihex::Record::StartSegmentAddress { cs, ip } => {
+ if start.is_some() {
+ log::error!("Multiple Start Address defined");
+ return Err(());
+ }
+ start = Some(IHexStart::Segment { _cs: cs, ip });
+ }
+ ihex::Record::StartLinearAddress(offset) => {
+ if start.is_some() {
+ log::error!("Multiple Start Address defined");
+ return Err(());
+ }
+ start = Some(IHexStart::Linear(offset));
+ }
+ ihex::Record::ExtendedSegmentAddress(segment_offset) => {
+ offset = Some(IHexOffset::Segment(segment_offset))
+ }
+ ihex::Record::ExtendedLinearAddress(linear_offset) => {
+ offset = Some(IHexOffset::Linear(linear_offset))
+ }
+ }
+ }
+ // can't have other record after the EoF record
+ if reader.next().is_some() {
+ log::error!("Found record after EoF record");
+ return Err(());
+ }
+
+ let merged = sort_and_merge_segments(unmerged_data)?;
+ Ok((
+ merged.data,
+ IHexViewData {
+ segments: merged.segments,
+ start,
+ },
+ ))
+}
+
+pub struct IHexViewConstructor {
+ pub core: BinaryViewType,
+}
+
+impl AsRef<BinaryViewType> for IHexViewConstructor {
+ fn as_ref(&self) -> &BinaryViewType {
+ &self.core
+ }
+}
+
+impl CustomBinaryViewType for IHexViewConstructor {
+ fn create_custom_view<'builder>(
+ &self,
+ parent: &BinaryView,
+ builder: CustomViewBuilder<'builder, Self>,
+ ) -> Result<CustomView<'builder>> {
+ let bytes = parent.len();
+ let mut buf = vec![0; bytes];
+ let bytes_read = parent.read(&mut buf, 0);
+ if bytes_read != bytes {
+ log::error!("IHex file is too small");
+ return Err(());
+ }
+ let string = String::from_utf8(buf).map_err(|_| {
+ log::error!("File contains invalid UTF8 characters");
+ })?;
+
+ let (data, segments) = parse_ihex(&string)?;
+
+ let parent_bin = BinaryView::from_data(&parent.file(), &data)?;
+ builder.create::<IHexView>(&parent_bin, segments)
+ }
+}
+
+impl BinaryViewTypeBase for IHexViewConstructor {
+ fn is_valid_for(&self, data: &BinaryView) -> bool {
+ // TODO check filename and identify the type if necessary
+ //let filename = data.file().filename();
+ //let filename = std::path::Path::new(filename.as_str());
+ //let filetype = filename
+ // .extension()
+ // .map(|ext| match ext.to_string_lossy().as_ref() {
+ // // General-purpose:
+ // "hex" | "mcs" | "int" | "ihex" | "ihe" | "ihx" => true,
+ // // Platform-specific:
+ // "h80" | "h86" | "a43" | "a90" => Some(true),
+ // // Binary or Intel hex:
+ // "obj" | "obl" | "obh" | "rom" | "eep" => true,
+ // // TODO: Split, banked, or paged:
+ // //.hxl–.hxh,[8] .h00–.h15, .p00–.pff[9]
+ // _ => false,
+ // });
+
+ // The biggest possible record is data record with 255 bytes: 268 bytes
+ let mut first_bytes = [0u8; 384];
+ let read_bytes = data.read(&mut first_bytes, 0);
+ let data = String::from_utf8_lossy(&first_bytes[0..read_bytes]);
+ let Some(line) = data.lines().next() else {
+ return false;
+ };
+ Record::from_record_string(line).is_ok()
+ }
+
+ fn is_deprecated(&self) -> bool {
+ false
+ }
+}
+
+pub struct IHexView {
+ core: Ref<BinaryView>,
+ segments: Vec<MergedSegment>,
+ start: Option<IHexStart>,
+}
+
+pub struct IHexViewData {
+ segments: Vec<MergedSegment>,
+ start: Option<IHexStart>,
+}
+
+#[derive(Clone, Copy, Debug)]
+enum IHexStart {
+ Segment { _cs: u16, ip: u16 },
+ Linear(u32),
+}
+
+#[derive(Clone, Copy, Debug)]
+enum IHexOffset {
+ Segment(u16),
+ Linear(u16),
+}
+
+impl AsRef<BinaryView> for IHexView {
+ fn as_ref(&self) -> &BinaryView {
+ &self.core
+ }
+}
+
+unsafe impl CustomBinaryView for IHexView {
+ type Args = IHexViewData;
+
+ fn new(handle: &BinaryView, _args: &Self::Args) -> Result<Self> {
+ Ok(Self {
+ core: handle.to_owned(),
+ // NOTE dummy values, final values are added on init
+ start: None,
+ segments: vec![],
+ })
+ }
+
+ fn init(&mut self, IHexViewData { start, segments }: Self::Args) -> Result<()> {
+ self.start = start;
+ self.segments = segments;
+
+ for segment in self.segments.iter() {
+ self.add_segment(
+ SegmentBuilder::from(*segment)
+ .executable(true)
+ .readable(true)
+ .contains_data(true)
+ .contains_code(true),
+ );
+ }
+ Ok(())
+ }
+}
+
+impl BinaryViewBase for IHexView {
+ fn entry_point(&self) -> u64 {
+ match self.start {
+ Some(IHexStart::Linear(addr)) => addr.into(),
+ Some(IHexStart::Segment { _cs, ip }) => ip.into(),
+ None => self.start(),
+ }
+ }
+
+ fn default_endianness(&self) -> binaryninja::Endianness {
+ binaryninja::Endianness::LittleEndian
+ }
+
+ fn address_size(&self) -> usize {
+ 4
+ }
+
+ fn offset_valid(&self, offset: u64) -> bool {
+ segment_from_address(&self.segments, offset).is_some()
+ }
+
+ fn next_valid_offset_after(&self, offset: u64) -> u64 {
+ segment_after_address(&self.segments, offset)
+ }
+}