☢ 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
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
Kepler is a vulnerability database and lookup store and API currently utilising National Vulnerability Database and NPM Advisories as data sources

Kepler — Kepler is a vulnerability database and lookup store and API currently utilising National Vulnerability Database and NPM Advisories as data so

Exein.io 101 Nov 12, 2022
Steals browser passwords and cookies and sends to webhook.

Browser-Stealer Steals browser passwords and cookies and sends to webhook. Donating Educational Purposes Only This code is made so you can learn from

RadonCoding 3 Sep 27, 2021
Xori is an automation-ready disassembly and static analysis library for PE32, 32+ and shellcode

Xori - Custom disassembly framework Xori is an automation-ready disassembly and static analysis library that consumes shellcode or PE binaries and pro

ENDGAME 712 Nov 28, 2022
🕵️‍♀️ Find, locate, and query files for ops and security experts ⚡️⚡️⚡️

Recon Find, locate, and query files for ops and security experts Key Features • How To Use • Download • Contributing • License Key Features Query with

Rusty Ferris Club 11 Dec 16, 2022
An esoteric language/compiler written with Rust and Rust LLVM bindings

MeidoLang (メイドラング) A not so useful and esoteric language. The goal of this project was to contain some quirky or novel syntax in a stack-style program

null 0 Dec 24, 2021
Rust-verification-tools - RVT is a collection of tools/libraries to support both static and dynamic verification of Rust programs.

Rust verification tools This is a collection of tools/libraries to support both static and dynamic verification of Rust programs. We see static verifi

null 253 Dec 31, 2022
link is a command and control framework written in rust

link link is a command and control framework written in rust. Currently in alpha. Table of Contents Introduction Features Feedback Build Process Ackno

null 427 Dec 24, 2022
Rust library for building and running BPF/eBPF modules

RedBPF A Rust eBPF toolchain. Overview The redbpf project is a collection of tools and libraries to build eBPF programs using Rust. It includes: redbp

foniod 1.5k Jan 1, 2023
A rust program to bruteforce ZIP, PDF and some popular hashes.

Veldora A program to bruteforce zips, pdfs and some popular hashes. This is basically a rust version of bruttle, but a lot faster. Installation: git c

Aquib 30 Dec 28, 2022
OpenSK is an open-source implementation for security keys written in Rust that supports both FIDO U2F and FIDO2 standards.

OpenSK This repository contains a Rust implementation of a FIDO2 authenticator. We developed OpenSK as a Tock OS application. We intend to bring a ful

Google 2.4k Jan 7, 2023
Advanced Fuzzing Library - Slot your Fuzzer together in Rust! Scales across cores and machines. For Windows, Android, MacOS, Linux, no_std, ...

LibAFL, the fuzzer library. Advanced Fuzzing Library - Slot your own fuzzers together and extend their features using Rust. LibAFL is written and main

Advanced Fuzzing League ++ 1.2k Jan 6, 2023
Modular, structure-aware, and feedback-driven fuzzing engine for Rust functions

Fuzzcheck Fuzzcheck is a modular, structure-aware, and feedback-driven fuzzing engine for Rust functions. Given a function test: (T) -> bool, you can

Loïc Lecrenier 397 Jan 6, 2023
A fast Rust-based safe and thead-friendly grammar-based fuzz generator

Intro fzero is a grammar-based fuzzer that generates a Rust application inspired by the paper "Building Fast Fuzzers" by Rahul Gopinath and Andreas Ze

null 203 Nov 9, 2022
Mundane is a Rust cryptography library backed by BoringSSL that is difficult to misuse, ergonomic, and performant (in that order).

Mundane Mundane is a Rust cryptography library backed by BoringSSL that is difficult to misuse, ergonomic, and performant (in that order). Issues and

Google 1.1k Jan 3, 2023
A Rust program to control bias lighting on Linux and Windows.

displaylight_rs This Rust workspace is a rewrite of my DisplayLight project. It colors leds mounted behind the monitor with the colors shown on the di

Ivor Wanders 2 Sep 25, 2022
Linux anti-debugging and anti-analysis rust library

DebugOff Library Linux anti-analysis Rust library The goal of this library is to make both static and dynamic (debugging) analysis more difficult. The

null 65 Jan 7, 2023
Attempts to suspend all known AV/EDRs processes on Windows using syscalls and the undocumented NtSuspendProcess API. Made with <3 for pentesters. Written in Rust.

Ronflex Attempts to suspend all known AV/EDRs processes on Windows using syscalls and the undocumented NtSuspendProcess API. Made with <3 for penteste

null 5 Apr 17, 2023
A rust library for sharing and updating arbitrary slices between threads, optimized for wait-free reads

atomicslice A Rust library for thread-safe shared slices that are just about as fast as possible to read while also being writable. Overview Use Atomi

Tim Straubinger 5 Dec 6, 2023