Skip to content
Open
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
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
- Header view: new view to inspect ELF and PE executables.
- Added a command to change the view (same as pressing `Tab`). Example: `:set view hex` (possible values are `text`, `hex`, and `header`). It can be used in `~/.dz6init` to set the default view when dz6 is loaded.
- Added support for comments / lines starting with `#` in `~/.dz6init`.
- Logging: dz6 now uses `tracing`, so the `RUST_LOG` environment variable controls what is logged. Example: `RUST_LOG=debug dz6 file.bin`. Messages are shown in the log window (`Alt+l`) and also written to `stderr` if you redirect it (`dz6 file.bin 2> dz6.log`).
- Log window: shows the time, level, and module of each message. It scrolls with `j`/`k`, `f`/`b`, `g`/`G`, and `h`/`l`, and `c` clears it.
- Errors that were silent before are logged now, like opening a file without write permission, a broken `.dz6` database file, an invalid regex in the string list, and a `:w` that fails.

## Breaking changes:
- `Tab` cycles through available views now (`Enter` no longer does that).
Expand Down
105 changes: 105 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ regex = "1.11.2"
serde = { version = "1.0.228", features = ["derive"] }
shell-words = "1.1.0"
toml = "1.0.1"
tracing = "0.1.44"
tracing-subscriber = { version = "0.3.23", features = ["env-filter"] }
tui-input = "0.15.0"

# The profile that 'dist' will build with
Expand Down
28 changes: 27 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ Once you load a file in **dz6**, you can use the commands below.
| Key | Action | Tips |
| ------- | ---------------- | -------------------------------------------------------------- |
| `Tab` | Switch views | Cycle through Hex, Text, and Headers. Backtab: Cycle backward. |
| `Alt+l` | Open log window | |
| `Alt+l` | Open log window | See [Logging](#logging) |
| `:` | Open command bar | See [Commands](#commands) |

#### Commands
Expand Down Expand Up @@ -256,6 +256,32 @@ The Header view is a new view (expected in v0.8.0) available for executable file
| `Space` | Change output numeric base | Output numbers in hexadecimal (default) or decimal |
| `Enter` | Follow a field value | For applicable fields (e.g., `AddressOfEntryPoint` in the PE Optional Header), follow their value in the Hex view. |

## Logging

**dz6** uses [tracing](https://docs.rs/tracing), so the `RUST_LOG` environment variable controls what is logged, as in any other Rust program:

RUST_LOG=debug dz6 file.bin
RUST_LOG=dz6::hex::search=trace dz6 file.bin

The levels are `error`, `warn`, `info`, `debug`, and `trace`. If you don't set `RUST_LOG`, dz6 logs at `info` level and the crates it uses at `warn`.

Messages are shown in the log window (`Alt+l`), which keeps the most recent ones. They also go to `stderr`, but since the interface takes over the terminal, writing there while dz6 is running would mess up the screen. So it only happens if you redirect `stderr` somewhere else:

dz6 file.bin 2> dz6.log

If you don't redirect it, the messages are printed after you quit dz6: warnings and errors only, or everything that matched your `RUST_LOG`, if you set one.

### Log window

| Key | Action | Tips |
| ----------------------- | -------------------- | --------------------------------------------- |
| `j` / `k` | Scroll one message | Down and Up arrow keys also work |
| `f` / `b` | Scroll one page | PgDown and PgUp also work |
| `g` / `G` | First / last message | Home and End also work |
| `h` / `l` | Scroll sideways | Left and Right arrow keys also work |
| `c` | Clear the messages | Only clears the window, not a redirected file |
| `q` | Close the window | `Esc` also works |

## FAQ

**1. I'm on a Mac. How am I supposed to use `Alt` key?!**
Expand Down
69 changes: 47 additions & 22 deletions src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,13 @@ use goblin::Object;
use goblin::error;
use mmap_io::{MemoryMappedFile, MmapMode};
use ratatui::{Frame, layout::Rect, widgets::ListState};
use tracing::{debug, error, info, instrument, warn};

use crate::{
config::*,
editor::*,
global::calculator::Calculator,
global::log::LogView,
header::header_view::{Elf, HeaderView, Pe},
hex::{hex_view::HexView, strings::FoundString},
input_history::InputHistory,
Expand Down Expand Up @@ -76,8 +78,7 @@ pub struct App {
pub hex_view: HexView,
pub last_error: Dz6Error,
pub list_state: ListState,
pub log_scroll_offset: (u16, u16),
pub logs: Vec<String>,
pub log_view: LogView,
pub reader: Reader,
pub running: bool,
pub screen: Rect,
Expand Down Expand Up @@ -123,8 +124,7 @@ impl App {
..Default::default()
},
list_state: ListState::default(),
log_scroll_offset: (0, 0),
logs: Vec::with_capacity(100),
log_view: LogView::default(),
reader: Reader::new(),
running: true,
screen: Rect::default(),
Expand Down Expand Up @@ -223,6 +223,11 @@ impl App {
}

/// load a file
#[instrument(
level = "debug",
skip_all,
fields(path = filepath, offset = initial_offset, read_only = read_only)
)]
pub fn load_file(
&mut self,
filepath: &str,
Expand All @@ -241,53 +246,73 @@ impl App {
let meta = path.metadata()?;

// We try to open file readwrite to use this later for saving
if !read_only && let Ok(file) = OpenOptions::new().read(true).write(true).open(path) {
self.file_info.file = Some(file);
} else {
if read_only {
self.file_info.is_read_only = true;
} else {
match OpenOptions::new().read(true).write(true).open(path) {
Ok(file) => self.file_info.file = Some(file),
Err(error) => {
warn!(%error, "cannot open for writing, continuing read-only");
self.file_info.is_read_only = true;
}
}
}

// We map it on memory readonly as changed to mapped memory also changes it on disk
if let Ok(mmap) = MemoryMappedFile::builder(path)
match MemoryMappedFile::builder(path)
.mode(MmapMode::ReadOnly)
.open()
{
self.file_info.mmap = Some(mmap);
} else {
return Err(std::io::Error::other("could not open file"));
Ok(mmap) => self.file_info.mmap = Some(mmap),
Err(error) => {
error!(%error, "cannot memory map the file");
return Err(std::io::Error::other("could not open file"));
}
}

self.file_info.size = meta.len() as usize;

if self.file_info.size > 0 {
_ = self.id_file();
match self.id_file() {
Ok(()) => debug!(kind = self.file_info.r#type, "file type identified"),
// most files are not executables, so this is normal
Err(error) => debug!(%error, "unknown file format"),
}
}

self.log(format!(
"filesize: {} (0x{:x})",
self.file_info.size, self.file_info.size
));
info!(
path = %self.file_info.path,
size = self.file_info.size,
read_only = self.file_info.is_read_only,
"file loaded"
);

if initial_offset != 0 {
self.goto(0);
}
self.goto(initial_offset);

// try to load a database for this file, but continue otherwise
if self.config.database {
let _ = self.load_database();
if self.config.database
&& let Err(error) = self.load_database()
{
debug!(%error, "no database loaded");
}

Ok(())
}

pub fn reload_file(&mut self) {
let fp = self.file_info.path.clone();
self.load_file(&fp, self.hex_view.offset, self.file_info.is_read_only)
.expect("could not reload the file");
let path = self.file_info.path.clone();

if let Err(error) = self.load_file(&path, self.hex_view.offset, self.file_info.is_read_only)
{
error!(path = %path, %error, "could not reload the file");
}
}

/// write what's cached to the actual file
#[instrument(name = "write", level = "debug", skip(self))]
pub fn write_to_file(&mut self) -> io::Result<()> {
if self.file_info.file.is_none() {
return Err(io::Error::other("file not open"));
Expand All @@ -311,7 +336,7 @@ impl App {
}
}

App::log(self, format!("{} bytes written to file", total_written));
info!(bytes = total_written, "wrote changes to file");
self.hex_view.changed_bytes.clear();
Ok(())
}
Expand Down
Loading