Git Product home page Git Product logo

yaxpeax-x86's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

yaxpeax-x86's Issues

Fails to build with yaxpeax-arch dep issue

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:

  1. git clone from github
  2. cargo build
  3. cargo can't find yaxpeax-arch in the relative path.

Not sure if this is by design or a documentation bug but I figured I'd open an issue.

RegSpec is annoying to match on

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.

FFI examples directory is missing

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)

Exposing the relations between register names

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.

Incorrect "invalid operand" decoding errors

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

Decode `endbr{32,64}`

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

Overflow in multiply when formatting instruction

(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

Checking if opcode is a conditional jump

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.

Index out of bounds in `regspec_label`, accessible in user code through `RegSpec::mask` and `Display`

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).

DisplayStyle::C can't print "jmp rax"

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:

fn write_jmp_operand<T: fmt::Write, Y: YaxColors>(op: Operand, colors: &Y, out: &mut T) -> fmt::Result {
match op {
Operand::ImmediateI8(rel) => {
if rel >= 0 {
write!(out, "$+{}", colors.number(signed_i32_hex(rel as i32)))
} else {
write!(out, "${}", colors.number(signed_i32_hex(rel as i32)))
}
}
Operand::ImmediateI32(rel) => {
if rel >= 0 {
write!(out, "$+{}", colors.number(signed_i32_hex(rel)))
} else {
write!(out, "${}", colors.number(signed_i32_hex(rel)))
}
}
_ => { unreachable!() }
}
}

Add ability to query whether a memory operand is read or write

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!

`Prefixes::cs` vs `Prefixes::set_cs` is probably a very weird typo

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.

Panic when parsing fuzzed instruction (multiply with overflow)

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.

Reexport yaxpeax-arch

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.

replace unsafe hint function calls with panic-in-debug versions

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.

Commonize structs and enums

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?

What A 3.0 Might Look Like

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
    • years ago i'd had the mistaken impression that you could only run 16-bit code in real mode, 32-bit in protected mode, 64-bit in long mode. the actual hardware-allowed behavior is more subtle and controlled by a few different bits. these really should just be x86_64/x86_32/x86_16 or... something, talking about the bit width of the instruction set.
  • multiple-register operands should be structure-style with named fields
    • 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.0
  • instruction encoding
    • doesn't necessarily need a breaking change, may happen before or after 2.0, but by 2.0 i'd like to have an idea of if there need to be changes here or in yaxpeax-arch..
  • address the open issues re. register names and RegSpec relationships
    • again probably won't need breaking changes. i just want to address these wrinkles before worrying about larger breaking changes.

Add ability to query direction of an operand.

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!

Another multiply with overflow panic

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

Matrix/IRC/github discussions/etc support rooms

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.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.