Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
538 changes: 233 additions & 305 deletions Cargo.lock

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@ crossterm = "0.29.0"
directories-next = "2.0.0"
encoding_rs = "0.8.35"
evalexpr = "13.1.*"
goblin = "0.10.5"
hex = "0.4.3"
memchr = "2.8.0"
mmap-io = "0.9.4"
mmap-io = "1.0.0"
rand = "0.10.1"
ratatui = "0.30.0"
regex = "1.11.2"
Expand Down
68 changes: 59 additions & 9 deletions src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,16 @@ use std::{
};

use arboard::Clipboard;
use goblin::Object;
use goblin::error;
use mmap_io::{MemoryMappedFile, MmapMode};
use ratatui::{Frame, layout::Rect, widgets::ListState};

use crate::{
config::*,
editor::*,
global::calculator::Calculator,
header::header_view::{Elf, HeaderView, Pe},
hex::{hex_view::HexView, strings::FoundString},
input_history::InputHistory,
reader::Reader,
Expand All @@ -38,7 +41,7 @@ impl FileInfo {
/// which also takes care of unloading it if memory constrained.
pub fn get_buffer(&mut self) -> &[u8] {
if let Some(mmap) = self.mmap.as_mut() {
return mmap.as_slice(0, self.size as u64).unwrap();
return mmap.as_slice_bytes(0, self.size as u64).unwrap();
}

&[]
Expand Down Expand Up @@ -68,7 +71,9 @@ pub struct App {
pub dialog_renderer: Option<fn(&mut App, &mut Frame)>,
pub editor_view: AppView,
pub file_info: FileInfo,
pub header_view: HeaderView,
pub hex_view: HexView,
pub last_error: Dz6Error,
pub list_state: ListState,
pub log_scroll_offset: (u16, u16),
pub logs: Vec<String>,
Expand All @@ -79,7 +84,6 @@ pub struct App {
pub string_regex: String,
pub strings: Vec<FoundString>,
pub text_view: TextView,
pub last_error: Dz6Error,
}

impl App {
Expand Down Expand Up @@ -107,6 +111,10 @@ impl App {
dialog_2nd_renderer: None,
editor_view: AppView::Hex,
file_info: FileInfo::default(),
header_view: HeaderView {
// elf_header_table_state: TableState::new().with_selected_cell(Some((0, 1))),
..Default::default()
},
hex_view: HexView {
editing_hex: true,
highlights: HashSet::with_capacity(8),
Expand Down Expand Up @@ -134,14 +142,55 @@ impl App {
}

/// this function tries to identify a file type; this is a boilerplate implementation.
fn id_file(&mut self) {
fn id_file(&mut self) -> error::Result<()> {
let buffer = self.file_info.get_buffer();
self.file_info.r#type = match buffer[0] {
0x7f => "ELF",
0xca | 0xcf => "Mach-O",
0x4d => "PE",

self.file_info.r#type = match Object::parse(&buffer)? {
Object::COFF(_coff) => "COFF",
Object::Elf(elf) => {
self.header_view.elf = Some(Elf {
header: elf.header.clone(),
phdrs: elf.program_headers.clone(),
sections: elf.section_headers.clone(),
symtab: elf.syms.to_vec(),
strtab: elf
.syms
.iter()
.map(|s| s.st_name)
.filter_map(|idx| elf.strtab.get_at(idx).map(|name| (idx, name.to_owned())))
.collect(),
});
"ELF"
}
Object::Mach(_mach) => "Mach-O",
Object::PE(pe) => {
let mut imports = Vec::new();
for imp in pe.imports {
imports.push(crate::header::header_view::PEImport {
dll: imp.dll.to_string(),
name: imp.name.to_string(),
offset: imp.offset,
ordinal: imp.ordinal,
rva: imp.rva,
_size: imp.size,
});
}

self.header_view.pe = Some(Pe {
dos_header: pe.header.dos_header.clone(),
coff_header: pe.header.coff_header.clone(),
optional_header: pe.header.optional_header.clone(),
sections: pe.sections,
imports,
});

"PE"
}
Object::TE(_magic) => "TE",
_ => "",
}
};

Ok(())
}

/// load a file
Expand Down Expand Up @@ -182,7 +231,7 @@ impl App {
self.file_info.size = meta.len() as usize;

if self.file_info.size > 0 {
self.id_file();
_ = self.id_file();
}

self.log(format!(
Expand All @@ -199,6 +248,7 @@ impl App {
if self.config.database {
let _ = self.load_database();
}

Ok(())
}

Expand Down
20 changes: 20 additions & 0 deletions src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,26 @@ pub fn parse_command(app: &mut App, cmdline: &str) {
app.config.search_wrap = false;
app.dialog_renderer = None;
}
// view
"view" => {
if let Some(val) = value {
match val.as_str() {
"text" => {
app.editor_view = crate::editor::AppView::Text;
app.dialog_renderer = None;
}
"hex" => {
app.editor_view = crate::editor::AppView::Hex;
app.dialog_renderer = None;
}
"header" => {
app.editor_view = crate::editor::AppView::Header;
app.dialog_renderer = None;
}
_ => {}
}
}
}
_ => {
app.dialog_renderer = None;
}
Expand Down
29 changes: 28 additions & 1 deletion src/draw.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use ratatui::{Frame, prelude::*, widgets::Paragraph};
use crate::{
app::App,
editor::AppView,
global,
global, header,
hex::{self, comment},
ruler, text,
};
Expand Down Expand Up @@ -72,6 +72,8 @@ pub fn draw(frame: &mut Frame, app: &mut App) {
// Draw status bar at the bottom
global::status_bar::status_bar_draw(app, frame, vertical_layout[1]);

app.command_area = vertical_layout[2];

let horizontal_layout = Layout::default()
.direction(Direction::Horizontal)
.constraints(vec![Constraint::Percentage(100)])
Expand All @@ -80,6 +82,31 @@ pub fn draw(frame: &mut Frame, app: &mut App) {
text::draw::text_contents_draw(app, frame, horizontal_layout[0]);
app.text_view.area_height = horizontal_layout[0].height;
}
AppView::Header => {
let constraints = vec![
Constraint::Percentage(100), // middle area (text content)
Constraint::Length(1), // status bar
Constraint::Length(1), // command bar
];

let vertical_layout = Layout::default()
.direction(Direction::Vertical)
.constraints(constraints)
.split(frame.area());

// Draw status bar at the bottom
global::status_bar::status_bar_draw(app, frame, vertical_layout[1]);

app.command_area = vertical_layout[2];

let horizontal_layout = Layout::default()
.direction(Direction::Horizontal)
.constraints(vec![Constraint::Percentage(100)])
.split(vertical_layout[0]);

header::draw::header_contents_draw(app, frame, horizontal_layout[0]);
app.text_view.area_height = horizontal_layout[0].height;
}
}

// The right event handler function is set by the keypress
Expand Down
4 changes: 3 additions & 1 deletion src/editor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@
pub enum AppView {
Text,
Hex,
Header,
}

impl AppView {
pub fn next(&mut self) {
match self {
AppView::Text => *self = AppView::Hex,
AppView::Hex => *self = AppView::Text,
AppView::Text => *self = AppView::Header,
AppView::Header => *self = AppView::Hex,
}
}
}
Expand Down
3 changes: 2 additions & 1 deletion src/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ use ratatui::crossterm::event::{self, Event, KeyCode, KeyEvent, KeyEventKind};
use std::io::Result;

use crate::app::App;
use crate::commands;
use crate::editor::{AppView, UIState};
use crate::global;
use crate::hex;
use crate::text;
use crate::{commands, header};

pub fn handle_dialog_error_events(app: &mut App, key: KeyEvent) -> Result<bool> {
match key.code {
Expand All @@ -29,6 +29,7 @@ pub fn handle_events(app: &mut App) -> Result<bool> {
match app.editor_view {
AppView::Hex => hex::events::hex_mode_events(app, key)?,
AppView::Text => text::events::text_mode_events(app, key)?,
AppView::Header => header::events::header_view_events(app, key)?,
}
}
UIState::DialogHelp => handle_dialog_error_events(app, key)?,
Expand Down
4 changes: 3 additions & 1 deletion src/global/events.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
use ratatui::crossterm::event::{KeyCode, KeyEvent, KeyModifiers};

use crate::{app::App, commands, editor::UIState, global};
use crate::{app::App, beep, commands, editor::UIState, global};

use std::io::Result;

pub fn handle_global_events(app: &mut App, key: KeyEvent) -> Result<bool> {
match key.code {
// beep in NORMAL mode
KeyCode::Esc => beep!(),
// switch views
KeyCode::Enter => app.switch_editor_view(),
// log window
Expand Down
6 changes: 6 additions & 0 deletions src/global/goto.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::app::App;
use crate::editor::AppView;

impl App {
/// The goto() function handles moving page position and smooth transition between pages
Expand Down Expand Up @@ -64,6 +65,11 @@ impl App {
// Update offset
self.hex_view.offset = offset;

// if goto is called from Header view
if self.editor_view == AppView::Header {
self.editor_view = AppView::Hex;
}

App::log(self, format!("goto: {:x}", offset));
}
}
11 changes: 11 additions & 0 deletions src/header/draw.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
use ratatui::{Frame, layout::Rect};

use crate::{app::App, header::formats};

pub fn header_contents_draw(app: &mut App, frame: &mut Frame, area: Rect) {
if app.file_info.r#type == "PE" {
let _ = formats::pe::draw::pe_draw(app, frame, area);
} else if app.file_info.r#type == "ELF" {
let _ = formats::elf::draw::elf_draw(app, frame, area);
}
}
13 changes: 13 additions & 0 deletions src/header/events.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
use std::io::Result;

use ratatui::crossterm::event::KeyEvent;

use crate::{app::App, header::formats};

pub fn header_view_events(app: &mut App, key: KeyEvent) -> Result<bool> {
match app.file_info.r#type {
"ELF" => formats::elf::events::view_header_elf_events(app, key),
"PE" => formats::pe::events::view_header_pe_events(app, key),
_ => Ok(false),
}
}
Loading
Loading