• Stars
    star
    151
  • Rank 246,057 (Top 5 %)
  • Language
    Rust
  • License
    MIT License
  • Created almost 8 years ago
  • Updated 3 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Scroll - making scrolling through buffers fun since 2016

Actions crates.io version

Scroll - cast some magic

         _______________
    ()==(              (@==()
         '______________'|
           |             |
           |   แผ€ฯฮตฯ„ฮฎ     |
         __)_____________|
    ()==(               (@==()
         '--------------'

Documentation

https://docs.rs/scroll

Usage

Add to your Cargo.toml

[dependencies]
scroll = "0.11"

Overview

Scroll implements several traits for read/writing generic containers (byte buffers are currently implemented by default). Most familiar will likely be the Pread trait, which at its basic takes an immutable reference to self, an immutable offset to read at, (and a parsing context, more on that later), and then returns the deserialized value.

Because self is immutable, all reads can be performed in parallel and hence are trivially parallelizable.

A simple example demonstrates its flexibility:

use scroll::{ctx, Pread, LE};

fn main() -> Result<(), scroll::Error> {
    let bytes: [u8; 4] = [0xde, 0xad, 0xbe, 0xef];

    // reads a u32 out of `b` with the endianness of the host machine, at offset 0, turbofish-style
    let number: u32 = bytes.pread::<u32>(0)?;
    // ...or a byte, with type ascription on the binding.
    let byte: u8 = bytes.pread(0)?;

    //If the type is known another way by the compiler, say reading into a struct field, we can omit the turbofish, and type ascription altogether!

    // If we want, we can explicitly add a endianness to read with by calling `pread_with`.
    // The following reads a u32 out of `b` with Big Endian byte order, at offset 0
    let be_number: u32 = bytes.pread_with(0, scroll::BE)?;
    // or a u16 - specify the type either on the variable or with the beloved turbofish
    let be_number2 = bytes.pread_with::<u16>(2, scroll::BE)?;

    // Scroll has core friendly errors (no allocation). This will have the type `scroll::Error::BadOffset` because it tried to read beyond the bound
    let byte: scroll::Result<i64> = bytes.pread(0);

    // Scroll is extensible: as long as the type implements `TryWithCtx`, then you can read your type out of the byte array!

    // We can parse out custom datatypes, or types with lifetimes
    // if they implement the conversion trait `TryFromCtx`; here we parse a C-style \0 delimited &str (safely)
    let hello: &[u8] = b"hello_world\0more words";
    let hello_world: &str = hello.pread(0)?;
    assert_eq!("hello_world", hello_world);

    // ... and this parses the string if its space separated!
    use scroll::ctx::*;
    let spaces: &[u8] = b"hello world some junk";
    let world: &str = spaces.pread_with(6, StrCtx::Delimiter(SPACE))?;
    assert_eq!("world", world);
    Ok(())
}

Deriving Pread and Pwrite

Scroll implements a custom derive that can provide Pread and Pwrite implementations for your structs.

use scroll::{Pread, Pwrite, BE};

#[derive(Pread, Pwrite)]
struct Data {
    one: u32,
    two: u16,
    three: u8,
}

fn main() -> Result<(), scroll::Error> {
    let bytes: [u8; 7] = [0xde, 0xad, 0xbe, 0xef, 0xfa, 0xce, 0xff];
    // Read a single `Data` at offset zero in big-endian byte order.
    let data: Data = bytes.pread_with(0, BE)?;
    assert_eq!(data.one, 0xdeadbeef);
    assert_eq!(data.two, 0xface);
    assert_eq!(data.three, 0xff);

    // Write it back to a buffer
    let mut out: [u8; 7] = [0; 7];
    out.pwrite_with(data, 0, BE)?;
    assert_eq!(bytes, out);
    Ok(())
}

This feature is not enabled by default, you must enable the derive feature in Cargo.toml to use it:

[dependencies]
scroll = { version = "0.10", features = ["derive"] }

std::io API

Scroll can also read/write simple types from a std::io::Read or std::io::Write implementor. The built-in numeric types are taken care of for you. If you want to read a custom type, you need to implement the FromCtx (how to parse) and SizeWith (how big the parsed thing will be) traits. You must compile with default features. For example:

use std::io::Cursor;
use scroll::IOread;

fn main() -> Result<(), scroll::Error> {
    let bytes_ = [0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0xef,0xbe,0x00,0x00,];
    let mut bytes = Cursor::new(bytes_);

    // this will bump the cursor's Seek
    let foo = bytes.ioread::<usize>()?;
    // ..ditto
    let bar = bytes.ioread::<u32>()?;
    Ok(())
}

Similarly, we can write to anything that implements std::io::Write quite naturally:

use scroll::{IOwrite, LE, BE};
use std::io::{Write, Cursor};

fn main() -> Result<(), scroll::Error> {
    let mut bytes = [0x0u8; 10];
    let mut cursor = Cursor::new(&mut bytes[..]);
    cursor.write_all(b"hello")?;
    cursor.iowrite_with(0xdeadbeef as u32, BE)?;
    assert_eq!(cursor.into_inner(), [0x68, 0x65, 0x6c, 0x6c, 0x6f, 0xde, 0xad, 0xbe, 0xef, 0x0]);
    Ok(())
}

Advanced Uses

Scroll is designed to be highly configurable - it allows you to implement various context (Ctx) sensitive traits, which then grants the implementor automatic uses of the Pread and/or Pwrite traits.

For example, suppose we have a datatype and we want to specify how to parse or serialize this datatype out of some arbitrary byte buffer. In order to do this, we need to provide a TryFromCtx impl for our datatype.

In particular, if we do this for the [u8] target, using the convention (usize, YourCtx), you will automatically get access to calling pread_with::<YourDatatype> on arrays of bytes.

use scroll::{ctx, Pread, BE, Endian};

struct Data<'a> {
  name: &'a str,
  id: u32,
}

// note the lifetime specified here
impl<'a> ctx::TryFromCtx<'a, Endian> for Data<'a> {
  type Error = scroll::Error;
  // and the lifetime annotation on `&'a [u8]` here
  fn try_from_ctx (src: &'a [u8], endian: Endian)
    -> Result<(Self, usize), Self::Error> {
    let offset = &mut 0;
    let name = src.gread::<&str>(offset)?;
    let id = src.gread_with(offset, endian)?;
    Ok((Data { name: name, id: id }, *offset))
  }
}

fn main() -> Result<(), scroll::Error> {
    let bytes = b"UserName\x00\x01\x02\x03\x04";
    let data = bytes.pread_with::<Data>(0, BE)?;
    assert_eq!(data.id, 0x01020304);
    assert_eq!(data.name.to_string(), "UserName".to_string());
    Ok(())
}

Please see the official documentation, or a simple example for more.

Contributing

Any ideas, thoughts, or contributions are welcome!

More Repositories

1

bingrep

like ~~grep~~ UBER, but for binaries
Rust
1,704
star
2

goblin

An impish, cross-platform binary parsing crate, written in Rust
Rust
1,167
star
3

faerie

Magical ELF and Mach-o object file writer backend
Rust
263
star
4

dryad

An almost-parallel, semi-functioning, dynamic linker experiment, written in Rust
Rust
162
star
5

rdr

Rdr is a cross-platform binary analysis and reverse engineering library, utilizing a unique symbol map for global analysis.
OCaml
83
star
6

raml

OCaml runtime and FFI bindings directly in Rust
Rust
78
star
7

elf2json

Converts an ELF binary to a JSON representation
OCaml
32
star
8

bin2json

Converts ELF, mach-o, or PE binaries to a JSON representation
OCaml
29
star
9

cargo-sym

Prints various binary symbols in your crate
Rust
13
star
10

silicon-disassembler

A high-performance, asynchronous disassembler which uses capstone.js as the backend and Web Workers for non-blocking requests
HTML
11
star
11

compilers-vm

A sandmark VM, in OCaml. Very fast.
OCaml
8
star
12

lazy_transducer

Lazy, parallel, indexable, generic data iterators
Rust
7
star
13

apli

Haskell APL Interpreter - ver 0.1
Haskell
6
star
14

stackup

Statically modify a program binary's runtime stack size on Mac OS X --- without recompiling.
Swift
6
star
15

algos

Algebraic arithmetization of boolean algebra and its implementation.
OCaml
6
star
16

silicon-hex-table

An efficient web-component hex table for all your online hexadecimal needs.
HTML
4
star
17

cloaked-robot

compilers final project --- Data Flow
Haskell
3
star
18

silicon-instructions

Display assembly instructions in a list-like format, using highlight.js
CSS
3
star
19

paper-trek

Boldly going where no paper element has gone before!
JavaScript
2
star
20

derp-archer

Project 2: Grammar Analysis and Parsing, CS454/554
Haskell
2
star
21

dotfiles

dotfiles and other configuration files
Haskell
2
star
22

metagoblin

WIP: Provides abstracted meta information about a binary
Rust
1
star
23

element-party

The splash page for http://element-party.xyz --- for those times when you just need to party.
HTML
1
star
24

scroll_derive

Macros 1.1 implementing #[derive(Pread, Pwrite)]
Rust
1
star
25

m4b.github.io

HTML
1
star
26

chat

Simple OCaml chat program
OCaml
1
star
27

ms-present

Master's Examination slides
TeX
1
star
28

silicon-symbols

Displays a list of symbols
HTML
1
star
29

drag-drop-behaviors

HTML
1
star
30

topo-tango

NM Game Jam Topological Tango
Lua
1
star
31

silicon-upload

A simple binary upload widget, to deliver all those bits to the server of your choice
HTML
1
star
32

get-polymer-imports

Simple python script to get all the polymer imports from a bower_components directory
Python
1
star
33

avant-lexeme

haskell compilers project
Haskell
1
star
34

foiled

Scala Implementation of FOIL
Scala
1
star
35

silicon-image

Displays a rasterized binary image with `silicon-image-behavior`, to update binary offsets via mouse movement, and etc.
HTML
1
star