Skip to content

handy-sun/rcheat

Repository files navigation

rcheat

English | 简体中文
build-test latest_release Crates.io Linux

Get/modify simple variable's value in another Linux running process

Please note that this project is only for learning and research purposes, and the author is not responsible for any legal consequences caused by the use of this project.


Table of Contents

1. Installation

1.1. Via cargo

Some ways to install cargo
  • can be obtained using rustup(Recommond)
  • use Linux package management(e.g. apt, yum, dnf, pacman)
  • download a offline tarball from forge.rust-lang.org

In order to install, just run the following command

cargo install --force rcheat

This will install cargo-make in your ~/.cargo/bin. Make sure to add ~/.cargo/bin directory to your PATH variable. You will have a executable available: rcheat

1.2. Build src

1.2.1. Dependencies

Suggest using the latest version

1.2.2. Building

git clone https://github.com/handy-sun/rcheat.git
cd rcheat
cargo build

You will have a executable available: ./target/debug/rcheat

Tips: If download speed from crates.io is too slow. use a mirror to speed up(e.g. use rsproxy).

2. Simple Example

for example, a C source file onlyc.c with some global variables:

#include <unistd.h>

const char sc_sig_arr[][6] = { " ", "HUP", "INT", "QUIT", "ILL", "TRAP", "IOT", "BUS", "FPE", "KILL" };
const char techs[] = "\x02str.wa : ? !\ndaw\r21";
struct DemoStru {
    int int32;
    short uint16;
};
struct DemoStru structure;

int main() {
    structure.int32 = 0x7ffe8092;
    structure.uint16 = 0x321b;
    while (1) {
        sleep(30);
    }
    return 0;
}

Then compile and run it:

gcc onlyc.c -o onlyc && ./onlyc

Get pid of onlyc(e.g. use command: pidof) and use rcheat with -p option: NOTE: This program must be run with root privileges!

pidof onlyc
# output: 13725
sudo rcheat -p 13725

Then will get the output about all global variables about this program

...
Matched count: 3
Index: var_name                                 | var_size(B)
    0: sc_sig_arr                               |      60
    1: structure                                |       8
    2: techs                                    |      21
Please input index to choose the var(default is 0):

Input 2 and Enter, you will see the byte value and ascii content of this variable (control char that unvisible show as .)

0x0000: 0273 7472 2e77 6120 3a20 3f20 210a 6461 ┃ .str.wa : ? !.da
0x0010: 770d 3231 00                            ┃ w.21.

You also can specify the total name or partly keyword of the variable with option -k

sudo rcheat -p 13725 -k sig_arr
...

0x0000: 2000 0000 0000 4855 5000 0000 494e 5400 ┃  .....HUP...INT.
0x0010: 0000 5155 4954 0000 494c 4c00 0000 5452 ┃ ..QUIT..ILL...TR
0x0020: 4150 0000 494f 5400 0000 4255 5300 0000 ┃ AP..IOT...BUS...
0x0030: 4650 4500 0000 4b49 4c4c 0000           ┃ FPE...KILL..

After version 0.1.3, option -n/--name can query pid by process name

sudo rcheat -n onlyc -k sig_arr

3. Lua Scripting

Since version 0.2.0, rcheat supports using Lua scripts to define custom binary struct parsing and formatted table output. Use the -f lua option to enable it.

How It Works

  1. Place Lua script files in /etc/rcheat/lua/
  2. Run rcheat with -f lua:
sudo rcheat -n onlyc -k structure -f lua
  1. rcheat loads core.lua (built-in), then loads all .lua files from the script directory
  2. Matches the variable name against Structure.match_table to find the alias
  3. Calls Structure:new_<alias>(bytes) to parse the raw bytes into a table
  4. Outputs a formatted table

Writing a Lua Script

Every script must define a global Structure table with:

  • match_table — maps variable name patterns (Lua string.find) to aliases
  • new_<alias>(bytes) — constructor that parses raw bytes and returns an instance

Column definition format:

Field Description Examples
name Column header name 'id', 'health'
size Number of bytes 1, 2, 4, 8
fmt string.unpack format 'i' signed, 'I' unsigned, 'f' float, 's' string, 'c' char, nil auto signed int

When fmt is 'i', 'I', 's', or 'c', the size is appended automatically (e.g. i4, I1). When fmt is nil, it defaults to i<size> (signed integer). For 'f', the size is determined by the format itself (4 bytes for f, 8 for d).

Example

/etc/rcheat/lua/example.lua:

Structure = {}
Structure.__index = Structure

-- Match variable names containing 'pcmStateList' to alias 'psl'
Structure.match_table = {
    ['pcmStateList'] = 'psl',
}

-- Constructor: parse bytes into a table with columns {id, stared, act}
function Structure:new_psl(bytes)
    self.psl_col = {
        { name = 'id',     size = 4, fmt = 'i' },  -- signed 32-bit int
        { name = 'stared', size = 1, fmt = 'I' },  -- unsigned 8-bit int
        { name = 'act',    size = 4, fmt = 'f' },  -- 32-bit float
    }

    return setmetatable({ psl = SetupTableData(bytes, self.psl_col) }, Structure)
end

Output (rcheat will format it as an aligned table):

╭─────┬────┬────────╮
│ (i) │ id │ stared │   act │
├─────┼────┼────────┤───────┤
│   0 │  1 │      0 │  3.50 │
│   1 │  2 │      1 │  7.25 │
╰─────┴────┴────────┴───────╯

Built-in Functions (core.lua)

SetupTableData(bytes, tab_list) — Iterates over raw bytes according to the column definitions and returns a two-dimensional table. Each row is an array of { name, size, data } entries. The function loops over the byte array, slicing it by each column's size and unpacking with string.unpack using the specified fmt.

4. Todo

The development plan of the project and the functions to be implemented

  • parse .debug* section
  • use log crate such as log/env_logger etc.
  • write data to tracee process' memory
  • use config.toml to reduce some inputs
  • use lib like table to format matrix table data
  • use lua to customized output
  • search pid by process name (like linux command: pidof/pgrep)
  • regex replace String.contain
  • if match more than 1 entry name, ask for which one to select
  • demangle symbols

About

Get/modify variable's value in another Linux running process

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors