diff options
| author | Rubens Brandao <git@rubens.io> | 2024-08-06 11:01:23 -0300 |
|---|---|---|
| committer | Mason Reed <mason@vector35.com> | 2024-08-27 17:19:25 -0400 |
| commit | 7e1027d904e66b0cadb277883cdfa7b13dcb209f (patch) | |
| tree | 4451fe8134e7518d43b1e04c1446f56d3bb2091e /view/bintxt/src/ihex.rs | |
| parent | feca74615ab3e10e7e256630946d830d07414d08 (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.rs | 237 |
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) + } +} |
