☢ Guerrilla (or Monkey) Patching in Rust for (unsafe) fun and profit.

Overview

Guerrilla

Crates.io Travis (.com) AppVeyor License

Guerrilla (or Monkey) Patching in Rust for (unsafe) fun and profit.

Provides aribtrary monkey patching in Rust. Please do not use this crate for anything outside of testing. It will not end well.

Can patch (almost) any function in Rust (free, associated, instance, generic, etc.). Can not patch anything from std.

Usage

extern crate guerrilla;

#[inline(never)]
fn say_hi(name: &str) {
    println!("hello, {}", name);
}

fn main() {
    // Variadic generics would be wondeful so we could have a [guerrilla::patch]
    let _guard = guerrilla::patch1(say_hi, |name| {
        // So sneaky... like a sneaky sneaky snek
        println!("bye, {}", name);
    });

    // [...]
    // Thousands of lines codes further in the project
    // [...]

    say_hi("Steve");

    // Once the guard is dropped the patch reverts the function to its original
    // behavior.
    drop(_guard);

    say_hi("Bob");
}

License

Licensed under either of

at your option.

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

Credits

Inspired (and derived) from monkey-patching-in-go.

Comments
  • collateral damage

    collateral damage

    Patching functions smaller than JMP_SIZE overwrites whatever happens to follow them. On x86_64, this affects the the_ultimate_question function in mod tests; patching it causes the next function defined, default, to crash.

    bug 
    opened by kazcw 7
  • Fix short jump patches

    Fix short jump patches

    Previously 5 was always subtracted from the function address difference. This is wrong for short jumps (we only need to subtract 2) and causes all kinds of shenanigans (mostly segfaults, see the added test).

    opened by ftilde 3
  • README.md false statement

    README.md false statement

    "Can not patch anything from std."

    #![feature(print_internals)]
    
    fn main() {
        std::mem::forget(guerrilla::patch1(std::io::_print, |_fmt_args|{
            panic!()
        }));
        println!("");
        return;
    }
    

    thread 'main' panicked at 'explicit panic', src/main.rs:X:Y

    opened by Hezuikn 1
  • Can't run a minimal example

    Can't run a minimal example

    So I've tried to use the lib on my machine, using minimal example from your test suite, namely

    src/main.rs

    fn main() {
        println!("Hello, world!");
    }
    
    #[cfg(test)]
    #[inline(never)]
    fn the_ultimate_question() -> u32 {
        42
    }
    
    #[cfg(test)]
    mod tests {
        use super::*;
    
        #[test]
        fn test_patch() {
            assert_eq!(the_ultimate_question(), 42);
    
            {
                let _guard = guerrilla::patch0(the_ultimate_question, || 24);
    
                assert_eq!(the_ultimate_question(), 24);
            }
    
            assert_eq!(the_ultimate_question(), 42);
        }
    }
    

    test result

    I figured I'll show full trace as this is some low-level stuff

    ~/DEV/gtest master*
    ❯ RUST_BACKTRACE=full cargo test
        Finished test [unoptimized + debuginfo] target(s) in 0.01s
         Running unittests src/main.rs (target/debug/deps/gtest-3465a4636c308b16)
    
    running 1 test
    test tests::test_patch ... FAILED
    
    failures:
    
    ---- tests::test_patch stdout ----
    thread 'tests::test_patch' panicked at 'assertion failed: `(left == right)`
      left: `-1`,
     right: `0`', /Users/retard/.cargo/registry/src/github.com-1ecc6299db9ec823/guerrilla-0.1.4/src/lib.rs:48:5
    stack backtrace:
       0:        0x105f75dd4 - std::backtrace_rs::backtrace::libunwind::trace::h0934ce8016da62ea
                                   at /rustc/a55dd71d5fb0ec5a6a3a9e8c27b2127ba491ce52/library/std/src/../../backtrace/src/backtrace/libunwind.rs:93:5
       1:        0x105f75dd4 - std::backtrace_rs::backtrace::trace_unsynchronized::h59c3dee870344ec2
                                   at /rustc/a55dd71d5fb0ec5a6a3a9e8c27b2127ba491ce52/library/std/src/../../backtrace/src/backtrace/mod.rs:66:5
       2:        0x105f75dd4 - std::sys_common::backtrace::_print_fmt::h498b4033e357b134
                                   at /rustc/a55dd71d5fb0ec5a6a3a9e8c27b2127ba491ce52/library/std/src/sys_common/backtrace.rs:66:5
       3:        0x105f75dd4 - <std::sys_common::backtrace::_print::DisplayBacktrace as core::fmt::Display>::fmt::h7d80e14dd19335c9
                                   at /rustc/a55dd71d5fb0ec5a6a3a9e8c27b2127ba491ce52/library/std/src/sys_common/backtrace.rs:45:22
       4:        0x105f919ab - core::fmt::write::h1709d0255080e28a
                                   at /rustc/a55dd71d5fb0ec5a6a3a9e8c27b2127ba491ce52/library/core/src/fmt/mod.rs:1198:17
       5:        0x105f72878 - std::io::Write::write_fmt::hc017b04bb9c972fa
                                   at /rustc/a55dd71d5fb0ec5a6a3a9e8c27b2127ba491ce52/library/std/src/io/mod.rs:1672:15
       6:        0x105f775ad - std::sys_common::backtrace::_print::h6f0d53db9de204c5
                                   at /rustc/a55dd71d5fb0ec5a6a3a9e8c27b2127ba491ce52/library/std/src/sys_common/backtrace.rs:48:5
       7:        0x105f775ad - std::sys_common::backtrace::print::h33928b4e9b284b1e
                                   at /rustc/a55dd71d5fb0ec5a6a3a9e8c27b2127ba491ce52/library/std/src/sys_common/backtrace.rs:35:9
       8:        0x105f775ad - std::panicking::default_hook::{{closure}}::had4d1cd22a173020
                                   at /rustc/a55dd71d5fb0ec5a6a3a9e8c27b2127ba491ce52/library/std/src/panicking.rs:295:22
       9:        0x105f7728d - std::panicking::default_hook::h42aa124509888735
                                   at /rustc/a55dd71d5fb0ec5a6a3a9e8c27b2127ba491ce52/library/std/src/panicking.rs:311:9
      10:        0x105f77ae8 - std::panicking::rust_panic_with_hook::h2b231e816574a23a
                                   at /rustc/a55dd71d5fb0ec5a6a3a9e8c27b2127ba491ce52/library/std/src/panicking.rs:698:17
      11:        0x105f77a23 - std::panicking::begin_panic_handler::{{closure}}::h9da8d88b7a4c9d5e
                                   at /rustc/a55dd71d5fb0ec5a6a3a9e8c27b2127ba491ce52/library/std/src/panicking.rs:588:13
      12:        0x105f76257 - std::sys_common::backtrace::__rust_end_short_backtrace::h10dbf1377dfaf877
                                   at /rustc/a55dd71d5fb0ec5a6a3a9e8c27b2127ba491ce52/library/std/src/sys_common/backtrace.rs:138:18
      13:        0x105f776fa - rust_begin_unwind
                                   at /rustc/a55dd71d5fb0ec5a6a3a9e8c27b2127ba491ce52/library/std/src/panicking.rs:584:5
      14:        0x105f9c2f3 - core::panicking::panic_fmt::hde1544b10dc8b4d3
                                   at /rustc/a55dd71d5fb0ec5a6a3a9e8c27b2127ba491ce52/library/core/src/panicking.rs:142:14
      15:        0x105f90427 - core::panicking::assert_failed_inner::h153877a946e95f35
                                   at /rustc/a55dd71d5fb0ec5a6a3a9e8c27b2127ba491ce52/library/core/src/panicking.rs:224:17
      16:        0x105f980b4 - core::panicking::assert_failed::h0c952f10eaf20388
                                   at /rustc/a55dd71d5fb0ec5a6a3a9e8c27b2127ba491ce52/library/core/src/panicking.rs:181:5
      17:        0x105f153f8 - guerrilla::copy_to_protected_address::he7516577c438eb1d
                                   at /Users/retard/.cargo/registry/src/github.com-1ecc6299db9ec823/guerrilla-0.1.4/src/lib.rs:48:5
      18:        0x105f13d8a - guerrilla::patch0::ha6c84ce43be61c5c
                                   at /Users/retard/.cargo/registry/src/github.com-1ecc6299db9ec823/guerrilla-0.1.4/src/lib.rs:234:17
      19:        0x105f14acc - gtest::tests::test_patch::h510fbd743d2c030b
                                   at /Users/retard/DEV/gtest/src/main.rs:20:26
      20:        0x105f14931 - gtest::tests::test_patch::{{closure}}::h2b0a69cc6c8c809c
                                   at /Users/retard/DEV/gtest/src/main.rs:16:5
      21:        0x105f14dc1 - core::ops::function::FnOnce::call_once::h39bb4500dd86e6a3
                                   at /rustc/a55dd71d5fb0ec5a6a3a9e8c27b2127ba491ce52/library/core/src/ops/function.rs:248:5
      22:        0x105f4b166 - core::ops::function::FnOnce::call_once::hd3a34913c2b3782a
                                   at /rustc/a55dd71d5fb0ec5a6a3a9e8c27b2127ba491ce52/library/core/src/ops/function.rs:248:5
      23:        0x105f4b166 - test::__rust_begin_short_backtrace::h4585f9c5640ec448
                                   at /rustc/a55dd71d5fb0ec5a6a3a9e8c27b2127ba491ce52/library/test/src/lib.rs:572:5
      24:        0x105f4a145 - <alloc::boxed::Box<F,A> as core::ops::function::FnOnce<Args>>::call_once::h845f99ab50caecd6
                                   at /rustc/a55dd71d5fb0ec5a6a3a9e8c27b2127ba491ce52/library/alloc/src/boxed.rs:1935:9
      25:        0x105f4a145 - <core::panic::unwind_safe::AssertUnwindSafe<F> as core::ops::function::FnOnce<()>>::call_once::h65a0795f52888231
                                   at /rustc/a55dd71d5fb0ec5a6a3a9e8c27b2127ba491ce52/library/core/src/panic/unwind_safe.rs:271:9
      26:        0x105f4a145 - std::panicking::try::do_call::h7231d107ab6a7741
                                   at /rustc/a55dd71d5fb0ec5a6a3a9e8c27b2127ba491ce52/library/std/src/panicking.rs:492:40
      27:        0x105f4a145 - std::panicking::try::hfb07ae8d672d4139
                                   at /rustc/a55dd71d5fb0ec5a6a3a9e8c27b2127ba491ce52/library/std/src/panicking.rs:456:19
      28:        0x105f4a145 - std::panic::catch_unwind::hc098328b3bc35ceb
                                   at /rustc/a55dd71d5fb0ec5a6a3a9e8c27b2127ba491ce52/library/std/src/panic.rs:137:14
      29:        0x105f4a145 - test::run_test_in_process::hc47b236ea8085ef3
                                   at /rustc/a55dd71d5fb0ec5a6a3a9e8c27b2127ba491ce52/library/test/src/lib.rs:595:18
      30:        0x105f4a145 - test::run_test::run_test_inner::{{closure}}::h6f1cb5c72f6693fa
                                   at /rustc/a55dd71d5fb0ec5a6a3a9e8c27b2127ba491ce52/library/test/src/lib.rs:489:39
      31:        0x105f157eb - test::run_test::run_test_inner::{{closure}}::hf20ba670a38e248e
                                   at /rustc/a55dd71d5fb0ec5a6a3a9e8c27b2127ba491ce52/library/test/src/lib.rs:516:37
      32:        0x105f157eb - std::sys_common::backtrace::__rust_begin_short_backtrace::h59cf3b5757eeaaa1
                                   at /rustc/a55dd71d5fb0ec5a6a3a9e8c27b2127ba491ce52/library/std/src/sys_common/backtrace.rs:122:18
      33:        0x105f1b9ac - std::thread::Builder::spawn_unchecked_::{{closure}}::{{closure}}::h5edd03a29d1385f8
                                   at /rustc/a55dd71d5fb0ec5a6a3a9e8c27b2127ba491ce52/library/std/src/thread/mod.rs:505:17
      34:        0x105f1b9ac - <core::panic::unwind_safe::AssertUnwindSafe<F> as core::ops::function::FnOnce<()>>::call_once::h2985eae67b18a09c
                                   at /rustc/a55dd71d5fb0ec5a6a3a9e8c27b2127ba491ce52/library/core/src/panic/unwind_safe.rs:271:9
      35:        0x105f1b9ac - std::panicking::try::do_call::ha5aa2ce6f0450132
                                   at /rustc/a55dd71d5fb0ec5a6a3a9e8c27b2127ba491ce52/library/std/src/panicking.rs:492:40
      36:        0x105f1b9ac - std::panicking::try::hd07d6a5dc8988e62
                                   at /rustc/a55dd71d5fb0ec5a6a3a9e8c27b2127ba491ce52/library/std/src/panicking.rs:456:19
      37:        0x105f1b9ac - std::panic::catch_unwind::h9c8fcaf2d1139ea8
                                   at /rustc/a55dd71d5fb0ec5a6a3a9e8c27b2127ba491ce52/library/std/src/panic.rs:137:14
      38:        0x105f1b9ac - std::thread::Builder::spawn_unchecked_::{{closure}}::h4476f28265ee914a
                                   at /rustc/a55dd71d5fb0ec5a6a3a9e8c27b2127ba491ce52/library/std/src/thread/mod.rs:504:30
      39:        0x105f1b9ac - core::ops::function::FnOnce::call_once{{vtable.shim}}::hce479c2981b1129e
                                   at /rustc/a55dd71d5fb0ec5a6a3a9e8c27b2127ba491ce52/library/core/src/ops/function.rs:248:5
      40:        0x105f7bf87 - <alloc::boxed::Box<F,A> as core::ops::function::FnOnce<Args>>::call_once::hb8ffae40895b6568
                                   at /rustc/a55dd71d5fb0ec5a6a3a9e8c27b2127ba491ce52/library/alloc/src/boxed.rs:1935:9
      41:        0x105f7bf87 - <alloc::boxed::Box<F,A> as core::ops::function::FnOnce<Args>>::call_once::h2e872f74d95473f3
                                   at /rustc/a55dd71d5fb0ec5a6a3a9e8c27b2127ba491ce52/library/alloc/src/boxed.rs:1935:9
      42:        0x105f7bf87 - std::sys::unix::thread::Thread::new::thread_start::h76e6c1c658a39a87
                                   at /rustc/a55dd71d5fb0ec5a6a3a9e8c27b2127ba491ce52/library/std/src/sys/unix/thread.rs:108:17
      43:     0x7fff6df90109 - __pthread_start
    
    
    failures:
        tests::test_patch
    
    test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.01s
    
    error: test failed, to rerun pass '--bin gtest'
    

    my platform

    ~/DEV/gtest master*
    ❯ rustc -vV
    rustc 1.64.0 (a55dd71d5 2022-09-19)
    binary: rustc
    commit-hash: a55dd71d5fb0ec5a6a3a9e8c27b2127ba491ce52
    commit-date: 2022-09-19
    host: x86_64-apple-darwin
    release: 1.64.0
    LLVM version: 14.0.6
    
    ~/DEV/gtest master*
    ❯ cargo -vV
    cargo 1.64.0 (387270bc7 2022-09-16)
    release: 1.64.0
    commit-hash: 387270bc7f446d17869c7f208207c73231d6a252
    commit-date: 2022-09-16
    host: x86_64-apple-darwin
    libgit2: 1.4.2 (sys:0.14.2 vendored)
    libcurl: 7.64.1 (sys:0.4.55+curl-7.83.1 system ssl:(SecureTransport) LibreSSL/2.8.3)
    os: Mac OS 10.15.7 [64-bit]
    

    help pl0x

    opened by mike-code 0
  • One patch! macro to rule them all

    One patch! macro to rule them all

    Currently there's the patch* functions which accept from 0 to 9 arguments, which is a limitation when it comes to a larger amount, which is surely non-idiomatic and not practical but can sometimes happen in FFI scenarios. Aside of that, the numbering just looks ugly. A patch! macro (likely implemented as a tiny separate crate decoupled from the patch guard and then reexported in the main crate because that's how proc-macro crates work) is a much cleaner way, I think.

    enhancement help wanted 
    opened by kotauskas 1
  • Thread safety?

    Thread safety?

    Hello, first of all, I am really amazed by this crate! But I am not able to understand, what is really going on under the hood to achieve this functionality. Therefore my first idea was to use your crate for mocking purposes in tests. Sadly, your implementation is not thread safe, reproducible example below:

    #[cfg(test)]
    extern crate guerrilla;
    
    fn function_int() -> i32 { 1 }
    
    fn function_str() -> &'static str { "original" }
    
    #[cfg(test)]
    mod tests {
        use super::*;
    
        #[test]
        fn test_int_default() {
            assert_eq!(function_int(), 1);
        }
    
        #[test]
        fn test_int_mock_1() {
            guerrilla::patch0(function_int, || 5);
            assert_eq!(function_int(), 5);
        }
    
        #[test]
        fn test_int_mock_2() {
            guerrilla::patch0(function_int, || 2);
            assert_eq!(function_int(), 2);
        }
    
        #[test]
        fn test_str_default() {
            assert_eq!(function_str(), "original");
        }
    
        #[test]
        fn test_str_mock_1() {
            guerrilla::patch0(function_str, || "mock_1");
            assert_eq!(function_str(), "mock_1");
        }
    
        #[test]
        fn test_str_mock_2() {
            guerrilla::patch0(function_str, || "mock_2");
            assert_eq!(function_str(), "mock_2");
        }
    }
    

    (Execute this a couple times to see random failures and random successful runs. Runs perfectly fine with cargo test -- --test-threads 1.)

    Is it possible, to achieve thread safety? And would it be something you would like to implement? I would really appreciate it :)

    Thank you!

    enhancement 
    opened by Gerschtli 1
  • Code following very small functions can trampled by jumps

    Code following very small functions can trampled by jumps

    This is very much a duplicate of #3, but it seems like you had problems consistently reproducing the problem in tests. I put together a proof of concept which (at least on my machines) consistently shows the described problem here.

    Unfortunately, I'm not sure how to turn this into a unit test as it relies on turning on size optimization (opt-level = "s") and requires the functions to be defined in another crate in order to avoid inlining. If you can think of a way to turn this into a test or just want to use the code for debugging purposes, feel free to do so!

    opened by ftilde 3
  • when guards are dropped out of order the original function is never restored

    when guards are dropped out of order the original function is never restored

    This is inherent to the API, as there's no reasonable interpretation of what version to "restore" in such a case; so it might just be one for the "no monkey business" section of the manual

    opened by kazcw 2
Owner
Ryan Leckey
Ryan Leckey
radare2-based decompiler and symbol executor

Radeco A radare2 based binary analysis framework consisting from the Radeco client, in ./radeco/ directory, ./radeco-lib/ - library where whole high-l

radare org 349 Dec 28, 2022
An impish, cross-platform binary parsing crate, written in Rust

libgoblin Documentation https://docs.rs/goblin/ changelog Usage Goblin requires rustc 1.40.0. Add to your Cargo.toml [dependencies] goblin = "0.4" Fea

null 892 Dec 22, 2022
Rust bindings for the unicorn CPU emulator

unicorn-rs THIS PACKAGE IS DEPRECATED AND NO LONGER MAINTAINED. Rust bindings are now included with unicorn and will be maintained there from now on.

null 129 Oct 10, 2022
rd is a record/replay debugger written in rust

rd The Record & Debug Tool The Record & Debug Tool (rd) is a Rust language port of the rr-debugger/rr debugger. With rd you can record Linux program e

Sidharth Kshatriya 948 Dec 27, 2022
Binary Analysis Framework in Rust

Welcome to Falcon Falcon is a formal binary analysis framework in Rust. Expression-based IL with strong influences from RREIL and Binary Ninja's LLIL.

Falcon Binary Analysis Framework 489 Dec 18, 2022
☢ Guerrilla (or Monkey) Patching in Rust for (unsafe) fun and profit.

Guerrilla Guerrilla (or Monkey) Patching in Rust for (unsafe) fun and profit. Provides aribtrary monkey patching in Rust. Please do not use this crate

Ryan Leckey 97 Dec 16, 2022
Easy c̵̰͠r̵̛̠ö̴̪s̶̩̒s̵̭̀-t̶̲͝h̶̯̚r̵̺͐e̷̖̽ḁ̴̍d̶̖̔ ȓ̵͙ė̶͎ḟ̴͙e̸̖͛r̶̖͗ë̶̱́ṉ̵̒ĉ̷̥e̷͚̍ s̷̹͌h̷̲̉a̵̭͋r̷̫̊ḭ̵̊n̷̬͂g̵̦̃ f̶̻̊ơ̵̜ṟ̸̈́ R̵̞̋ù̵̺s̷̖̅ţ̸͗!̸̼͋

Rust S̵̓i̸̓n̵̉ I̴n̴f̶e̸r̵n̷a̴l mutability! Howdy, friendly Rust developer! Ever had a value get m̵̯̅ð̶͊v̴̮̾ê̴̼͘d away right under your nose just when

null 294 Dec 23, 2022
Breaking your Rust code for fun and profit

Breaking your Rust code for fun & profit this is an architecture-preview, not all components are there This is a mutation testing framework for Rust c

null 533 Dec 18, 2022
An i386 operation system written in pure rust for fun and no profit.

OrustS An i386 operation system written in pure rust (for fun and no profit). This operation system is under active developing. Checklist implement a

M4tsuri 10 Aug 12, 2022
Breaking your Rust code for fun and profit

Breaking your Rust code for fun & profit this is an architecture-preview, not all components are there This is a mutation testing framework for Rust c

null 542 Jan 4, 2023
Type-check non-existing `Phantom` code for Fun And Profit

Sometimes you may want to write Rust code that ought to be type-checked (e.g., borrow-checked) in the same fashion as real Rust code even though that code is never intended to be run / to affect or even reach code generation.

Daniel Henry-Mantilla 4 Jun 5, 2022
A Litecord compatible/inspired OSS implementation of Discord's backend for fun and profit.

A Litecord compatible/inspired OSS implementation of Discord's backend for fun and profit.

Evie Viau 3 May 9, 2022
(Ab)using technology for fun & profit.

(Ab)using technology for fun & profit. Code accompanying my blog

Sylvain Kerkour 357 Dec 30, 2022
Pua-lang - a dialect of The Monkey Programming Language

pua-lang PUA Programming Language written in Rust. What's pua-lang? pua-lang is a dialect of The Monkey Programming Language, intended to mirror the i

flaneur 3.2k Jan 6, 2023
Honkers launcher for Linux with automatic patching and telemetry disabling

Honkers launcher for Linux with automatic patching and telemetry disabling

An Anime Team 8 Apr 23, 2023
The Honkers Railway launcher for Linux with automatic patching and telemetry disabling

The Honkers Railway launcher for Linux with automatic patching and telemetry disabling

An Anime Team 73 May 9, 2023
A command-line tool for patching shell scripts inspired by resholve

patsh A command-line tool for patching shell scripts inspired by resholve nix run github:nix-community/patsh -- -f script.sh Usage Usage: patsh [OPTIO

Nix community projects 23 Jan 7, 2023
Detects usage of unsafe Rust in a Rust crate and its dependencies.

cargo-geiger ☢️ A program that lists statistics related to the usage of unsafe Rust code in a Rust crate and all its dependencies. This cargo plugin w

Rust Secure Code Working Group 1.1k Dec 26, 2022
Detects usage of unsafe Rust in a Rust crate and its dependencies.

cargo-geiger ☢️ A program that lists statistics related to the usage of unsafe Rust code in a Rust crate and all its dependencies. This cargo plugin w

Rust Secure Code Working Group 1.1k Jan 8, 2023
Detects usage of unsafe Rust in a Rust crate and its dependencies.

cargo-geiger ☢️ Looking for maintainer: https://github.com/rust-secure-code/cargo-geiger/issues/210 A program that lists statistics related to the usa

Rust Secure Code Working Group 1.1k Jan 4, 2023