iximeow / yaxpeax-x86 Goto Github PK
View Code? Open in Web Editor NEWx86 decoders for the yaxpeax project
License: BSD Zero Clause License
x86 decoders for the yaxpeax project
License: BSD Zero Clause License
C:\Users\iakronqu\source>git clone https://github.com/iximeow/yaxpeax-x86.git
Cloning into 'yaxpeax-x86'...
...
C:\Users\iakronqu\source\yaxpeax-x86>cargo build
Updating crates.io index
error: failed to load source for a dependency on `yaxpeax-arch`
Caused by:
Unable to update C:\Users\iakronqu\yaxpeax-arch
Caused by:
failed to read `C:\Users\iakronqu\yaxpeax-arch\Cargo.toml`
Caused by:
The system cannot find the path specified. (os error 3)
Steps to repro:
Not sure if this is by design or a documentation bug but I figured I'd open an issue.
For my use case I need both the protected and real mode modules. To reduce duplicate code I need a way to convert the real and protected mode RegSpec
into some enum I can match on for further operations.
However since RegSpec cannot be matched I have to resort to messy code that that can lead to bugs because of missing variant statements:
impl TryFrom<real_mode::RegSpec> for RegisterIndex {
type Error = InvalidRegister;
fn try_from(value: real_mode::RegSpec) -> Result<Self, Self::Error> {
use real_mode::RegSpec;
Ok(if value == RegSpec::ax() {
RegisterIndex::AX
} else if value == RegSpec::bx() {
RegisterIndex::BX
} else if value == RegSpec::cx() {
RegisterIndex::CX
} else if value == RegSpec::dx() {
RegisterIndex::DX
} else if value == RegSpec::si() {
RegisterIndex::SI
} else if value == RegSpec::di() {
RegisterIndex::DI
} else if value == RegSpec::sp() {
RegisterIndex::SP
} else if value == RegSpec::bp() {
RegisterIndex::BP
} else if value == RegSpec::EIP {
RegisterIndex::IP
} else if value == RegSpec::eflags() {
RegisterIndex::FLAGS
} else { // Yes this may be incomplete, I am certain of that...
return Err(InvalidRegister::Real(value));
})
}
}
It would be nice if yaxpeax added a sort of RegEnum
that could be matched on the to make these types of statements less annoying to work with.
The readme says "see the examples directory for FFI usage of this library", but there isn't an examples directory in this repo.
(also hi)
I find myself writing some ugly code to answer questions like "which 64-bit GPR is ch
a part of? which bits?".
I would expect yaxpeax
to provide APIs on RegSpec
for this, though I understand if this is something you want to model in yaxpeax_arch
, with the API design questions this involves.
While single-stepping a simple x86-64 ELF binary on Linux, I ran into (probably) spurious decoding errors with the following byte vectors (presented here with their XED-decoded short string):
"invalid operand":
0fc7642440: xsavec ptr [esp+0x40]
660febc3: por xmm0, xmm3
660febc4: por xmm0, xmm4
660febd3: por xmm2, xmm3
660f74c1: pcmpeqb xmm0, xmm1
660ff8c8: psubb xmm1, xmm0
660ff8d0: psubb xmm2, xmm0
These all occurred in either ld-2.30.so
or libc-2.30.so
.
I also ran into this, which is probably expected:
"the decoder is incomplete":
c5f96ec6: vmovd xmm0, esi
I'm mentioning this last one anyway because it was in libc-2.30.so
. It may be worth prioritizing such instructions.
A more verbose script-generated comparison to XED can be found here, with much more detail.
I am using the Default::default()
-provided long_mode::InstDecoder
, which IIUC is giving me the best possible chance at decoding.
Versions:
yaxpeax-86: 0.0.6
rustc: 1.41.0
XED: v10.0-791-gb4109c0
The special CET nops for indirect branch termination don't yet seem to be known to yax. This is easy to work around, when it matters, but it would be nice to e.g. have the right Display
impls.
Expected:
$ yaxdis f30f1efa
0x00000000: f30f1efa : endbr64
$ yaxdis f30f1efb
0x00000000: f30f1efb : endbr32
Actual:
$ yaxdis f30f1efa
0x00000000: f30f1efa : nop edx
$ yaxdis f30f1efb
0x00000000: f30f1efb : nop ebx
Per the Intel manual, there is a valid Opcode in Compat/Legacy mode at 0x9A
.
This does not return a Opcode::CALL
in real mode or protected mode decoders:
Long mode appears to be correct.
(grr, the second time i hit enter and made an issue too early, sorry if you have email notifs on!)
fn main() {
let decoder = yaxpeax_x86::amd64::InstDecoder::default();
let mut s = String::new();
let i = decoder.decode_slice(&[243, 15, 30, 15]).expect("unreachable");
i.write_to(&mut s).expect("unreachable");
}
Gives a multiply with overflow with integer overflow checks on, gives a index out of bounds panic (thankfully not unchecked indexing) in release
Currently, Opcode::condition
provides some facilities for handling Jcc, SETcc and CMOVcc with common code for the condition codes themselves. However, you still need to list all the 16 conditional jumps in a match, or do ugly string processing hacks, to actually check whether an Opcode is a Jcc, or whether it is SETcc or CMOVcc.
fn main() {
let reg = yaxpeax_x86::protected_mode::RegSpec::mask(31);
dbg!(format!("{}", reg));
}
In miri:
error: Undefined Behavior: pointer arithmetic failed: alloc1285 has size 2944, so pointer to 3056 bytes starting at offset 0 is out-of-bounds
--> /home/jess/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ptr/const_ptr.rs:283:18
|
283 | unsafe { intrinsics::offset(self, count) }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pointer arithmetic failed: alloc1285 has size 2944, so pointer to 3056 bytes starting at offset 0 is out-of-bounds
|
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
= note: inside `std::ptr::const_ptr::<impl *const &str>::offset` at /home/jess/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ptr/const_ptr.rs:283:18
= note: inside `std::ptr::const_ptr::<impl *const &str>::add` at /home/jess/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ptr/const_ptr.rs:564:18
= note: inside `<usize as std::slice::SliceIndex<[&str]>>::get_unchecked` at /home/jess/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/slice/index.rs:177:18
= note: inside `core::slice::<impl [&str]>::get_unchecked::<usize>` at /home/jess/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/slice/mod.rs:385:20
= note: inside `yaxpeax_x86::protected_mode::display::regspec_label` at /home/jess/.cargo/registry/src/github.com-1ecc6299db9ec823/yaxpeax-x86-1.1.2/src/protected_mode/display.rs:126:14
= note: inside `yaxpeax_x86::protected_mode::display::<impl std::fmt::Display for yaxpeax_x86::protected_mode::RegSpec>::fmt` at /home/jess/.cargo/registry/src/github.com-1ecc6299db9ec823/yaxpeax-x86-1.1.2/src/protected_mode/display.rs:131:21
= note: inside `std::fmt::write` at /home/jess/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/fmt/mod.rs:1149:17
= note: inside `<std::string::String as std::fmt::Write>::write_fmt` at /home/jess/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/fmt/mod.rs:186:9
= note: inside `std::fmt::format` at /home/jess/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/alloc/src/fmt.rs:597:5
note: inside `main` at /home/jess/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/alloc/src/macros.rs:111:19
--> src/main.rs:3:10
|
3 | dbg!(format!("{}", reg));
| ^^^^^^^^^^^^^^^^^^
= note: inside `<fn() as std::ops::FnOnce<()>>::call_once - shim(fn())` at /home/jess/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ops/function.rs:227:5
= note: inside `std::sys_common::backtrace::__rust_begin_short_backtrace::<fn(), ()>` at /home/jess/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/sys_common/backtrace.rs:123:18
= note: inside closure at /home/jess/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/rt.rs:145:18
= note: inside `std::ops::function::impls::<impl std::ops::FnOnce<()> for &dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe>::call_once` at /home/jess/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ops/function.rs:259:13
= note: inside `std::panicking::r#try::do_call::<&dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe, i32>` at /home/jess/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/panicking.rs:406:40
= note: inside `std::panicking::r#try::<i32, &dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe>` at /home/jess/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/panicking.rs:370:19
= note: inside `std::panic::catch_unwind::<&dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe, i32>` at /home/jess/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/panic.rs:133:14
= note: inside closure at /home/jess/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/rt.rs:128:48
= note: inside `std::panicking::r#try::do_call::<[closure@std::rt::lang_start_internal::{closure#2}], isize>` at /home/jess/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/panicking.rs:406:40
= note: inside `std::panicking::r#try::<isize, [closure@std::rt::lang_start_internal::{closure#2}]>` at /home/jess/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/panicking.rs:370:19
= note: inside `std::panic::catch_unwind::<[closure@std::rt::lang_start_internal::{closure#2}], isize>` at /home/jess/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/panic.rs:133:14
= note: inside `std::rt::lang_start_internal` at /home/jess/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/rt.rs:128:20
= note: inside `std::rt::lang_start::<()>` at /home/jess/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/rt.rs:144:17
= note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info)
Outside of miri:
memory allocation of 94086415520320 bytes failed
zsh: abort (core dumped) cargo run -q
This presumably just interprets some arbitrary 16 byte chunk of memory near REG_NAMES
as a (pointer, length).
This wasn't a fuzzer bug, this was me just looking for dubious unsafe
and this was the first one I looked at. Is this function performance critical?
Honestly I'd be inclined to drop the get_unchecked
and just pay the cost of the bounds check. (This goes for all places that use get_unchecked to avoid a bounds check, not just here).
fn main() {
use yaxpeax_arch::Decoder;
let decoder = yaxpeax_x86::amd64::InstDecoder::default();
let inst = decoder.decode_slice(&[0xFF, 0xE0]).unwrap();
println!("{}", inst.display_with(yaxpeax_x86::amd64::DisplayStyle::C));
}
hits the unreachable!
in write_jmp_operand
:
yaxpeax-x86/src/long_mode/display.rs
Lines 3519 to 3537 in 1dc77c2
I recently wrote a few lints for clippy that should help with CI testing given the couple of "fix no_std" pull requests I've seen: rust-lang/rust-clippy#9103
Although I don't think these are going to be stable until Rust 1.64
We have an issue in Firefox on Mac OS X where we get EXC_BAD_ACCESS
for bad memory access instead of EXCEPTION_ACCESS_VIOLATION_READ
/EXCEPTION_ACCESS_VIOLATION_WRITE
.
We are currently using yaxpeax to disassemble the crashing instruction, and we are able to determine the crashing memory access even when it is misreported; however, we aren't able to determine the direction of the access.
It would be helpful for FF devs to be able to report the access direction on Mac. Is there any way to do this that I'm perhaps missing? Is it a feature that could possibly be added?
Thanks ixi!
In all three copies of the Prefixes
struct, cs
is a public copy of set_cs
, with the signature fn(&mut self)
, while all the other segment registers have functions fn(&self) -> bool
. I highly doubt that this is intentional, and if it is, it deserves a doc-comment.
The following code panics when run on yaxpeax-x86 1.1.1
fn main() {
let decoder = yaxpeax_x86::amd64::InstDecoder::default();
drop(decoder.decode_slice(&[98, 98, 101, 164, 0, 89, 167, 98, 101, 10]));
}
The panic:
thread 'main' panicked at 'attempt to multiply with overflow', /home/jess/.cargo/registry/src/github.com-1ecc6299db9ec823/yaxpeax-x86-1.1.1/src/long_mode/evex.rs:14:5
stack backtrace:
0: rust_begin_unwind
at /rustc/8f117a77d0880ed59afcc1a19c72ec5c1e44b97c/library/std/src/panicking.rs:498:5
1: core::panicking::panic_fmt
at /rustc/8f117a77d0880ed59afcc1a19c72ec5c1e44b97c/library/core/src/panicking.rs:107:14
2: core::panicking::panic
at /rustc/8f117a77d0880ed59afcc1a19c72ec5c1e44b97c/library/core/src/panicking.rs:48:5
3: yaxpeax_x86::long_mode::evex::apply_disp_scale
at /home/jess/.cargo/registry/src/github.com-1ecc6299db9ec823/yaxpeax-x86-1.1.1/src/long_mode/evex.rs:14:5
4: yaxpeax_x86::long_mode::evex::read_evex
at /home/jess/.cargo/registry/src/github.com-1ecc6299db9ec823/yaxpeax-x86-1.1.1/src/long_mode/../shared/evex.in:243:11
5: yaxpeax_x86::long_mode::read_with_annotations
at /home/jess/.cargo/registry/src/github.com-1ecc6299db9ec823/yaxpeax-x86-1.1.1/src/long_mode/mod.rs:7623:21
6: <yaxpeax_x86::long_mode::InstDecoder as yaxpeax_arch::Decoder<yaxpeax_x86::long_mode::Arch>>::decode
at /home/jess/.cargo/registry/src/github.com-1ecc6299db9ec823/yaxpeax-x86-1.1.1/src/long_mode/mod.rs:4155:9
7: yaxpeax_x86::long_mode::InstDecoder::decode_slice
at /home/jess/.cargo/registry/src/github.com-1ecc6299db9ec823/yaxpeax-x86-1.1.1/src/long_mode/mod.rs:2830:9
8: scratchhXLguhiJy::main
at ./main.rs:3:10
9: core::ops::function::FnOnce::call_once
at /rustc/8f117a77d0880ed59afcc1a19c72ec5c1e44b97c/library/core/src/ops/function.rs:227:5
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
Since Instruction::len()
requires importing a trait from yaxpeax-arch
and the arch crate is a required dependency it may make sense to reexport yaxpeax-arch
inside of this crate and the other architecture crates.
Reexports would involve adding a pub use yaxpeax_arch;
statement to the lib.rs
. Adding an as <whatever>
as suitable to make sense in the crate structure.
This would allow users to not have to explicitly declare yaxpeax-arch
in their Cargo.toml
's dependencies while still being able to use the arch types.
ja $+0x1e
good
jna $+-0x1e
bad
Sorry if this is on purpose.
My question is why register r12 and r13 have the register number of 8 and 9 here?
https://github.com/iximeow/yaxpeax-x86/blob/no-gods-no-/src/long_mode/mod.rs#L293
Shouldn't this part be
register!(D,
eax => 0, ecx => 1, edx => 2, ebx => 3,
esp => 4, ebp => 5, esi => 6, edi => 7,
r8d => 8, r9d => 9, r10d => 10, r11d => 11,
r12d => 12, r13d => 13, r14d => 14, r15d => 15
);
?
std::slice::get_unchecked
is always unchecked, but its use in yaxpeax-x86
is as an optimization, not for load-bearing correctness. it would be helpful to panic if the expectation is violated in debug builds, instead of veering off into unknown states. the same applies for uses of unreachable_unchecked()
in yaxpeax-x86
.
for both hints, it would be good to have a wrapper that panics in debug builds, but is just the hint for release builds. maybe a debug_assert
, maybe an cfg_if
between two impls, either should be fine. but yaxpeax-x86
is well at the point that these unsafe hints should be designed to be caught by tests or fuzzing if they become erroneous.
A number of structures and enums are re-defined per processor mode in yaxpeax-x86
, such as Opcode
, ConditionCode
, Instruction
, Prefixes
, and so on...
Could you commonize these enums and structures, not only to minimize duplication but to also allow code to be written against these common types in all three processor modes?
idle wishlist of things i'd like to break for a 2.0 version of the disassembler.. yaxpeax-x86
was one of the first crates i'd published and has some poor API choices as a result.
{long,protected,real}_mode
need better names
x86_64
/x86_32
/x86_16
or... something, talking about the bit width of the instruction set.Operand
docs speak for themselves. which RegSpec
is the base, or first register in the address? which is second? which RegSpec
is the mask register in RegIndexBaseScaleDispMasked
? this would be a little more reasonable as RegIndexBase { base: RegSpec, index: RegSpec }
and so on. this will change in 2.0yaxpeax-arch
..Hi there,
We are currently working on a feature for Mozilla's Crash Reporter and Minidump Processor. We are trying to detect "impossible crashes" that may be caused by malfunctioning hardware.
For example, if the CPU reports a crash due to an invalid write, but the crashing instruction doesn't write anything, that would most likely be due to the CPU malfunctioning.
The issue is that neither of the Rust disassemblers that we could use (yaxpeax-x86 or iced-x86) currently report the "direction" of the operands of an instruction.
For example, it would be great to know that in the instruction mov [rax], rbx
the first operand is a "WRITE" operand.
Currently, to do this, we would have to match off of every x86 Opcode that accesses memory ourselves to determine the nature of each opcode's access (see this PR for a partial example). This is not something we generally want to do, and so we are kind-of stuck with perhaps switching to a non-Rust disassembler like Capstone (which, to be clear, we don't want to do because it has its own headaches).
Here is the docs for Capstone's take on this feature. You can see the RegAccessType tells you if each operand is ReadOnly, WriteOnly, or ReadWrite (like in the case of an add [rax], rbx
instruction.
If there is any way we can offer some help, please let us know... It might actually be easier to fix this in a Rust disassembler rather than trying to switch over to Capstone ๐.
Thanks!
fn main() {
let decoder = yaxpeax_x86::amd64::InstDecoder::default();
drop(decoder.decode_slice(&[98, 242, 253, 15, 138, 98, 242, 253, 15, 138]));
}
thread 'main' panicked at 'attempt to multiply with overflow', /home/jess/.cargo/registry/src/github.com-1ecc6299db9ec823/yaxpeax-x86-1.1.2/src/long_mode/../shared/evex.in:241:11
The fuzzer I'm using is just
#![no_main]
use libfuzzer_sys::fuzz_target;
fuzz_target!(|data: &[u8]| {
let decoder = yaxpeax_x86::amd64::InstDecoder::default();
drop(decoder.decode_slice(data));
});
(the drop is to ignore the must_use on a Result).
Probably should add this to the repo and run it yourself. See https://github.com/rust-fuzz/cargo-fuzz
I personally dislike spamming github's issue reports with questions about library usage and am wondering if there are any plans to open a place where we can ask questions without spamming github issues.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.