An open API service indexing awesome lists of open source software.

https://github.com/handy-sun/rcheat

Get/modify variable's value in another Linux running process
https://github.com/handy-sun/rcheat

cli elf gdb process ptrace

Last synced: 2 months ago
JSON representation

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

Awesome Lists containing this project

README

          

# **rcheat**

**English | [简体中文](./README.zh-CN.md)**

[![build-test](https://github.com/handy-sun/rcheat/actions/workflows/build-test.yml/badge.svg)](https://github.com/handy-sun/rcheat/actions/workflows/build-test.yml)
![latest_release](https://img.shields.io/github/v/tag/handy-sun/rcheat?label=release)
[![Crates.io](https://img.shields.io/crates/v/rcheat.svg)](https://crates.io/crates/rcheat)
![Linux](https://img.shields.io/badge/-Linux-grey?logo=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](#Installation)
* 1.1. [Via cargo](#Viacargo)
* 1.2. [Build src](#Buildsrc)
* 1.2.1. [Dependencies](#Dependencies)
* 1.2.2. [Building](#Building)
* 2. [Simple Example](#SimpleExample)
* 3. [Lua Scripting](#lua-scripting)
* 4. [Todo](#Todo)

## 1. Installation

### 1.1. Via cargo

Some ways to install cargo

- can be obtained using [rustup](https://rust-lang.github.io/rustup/)(Recommond)
- use Linux package management(e.g. apt, yum, dnf, pacman)
- download a offline tarball from [forge.rust-lang.org](https://forge.rust-lang.org/infra/archive-stable-version-installers.html)

In order to install, just run the following command

```sh
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

- [cargo](https://github.com/rust-lang/cargo/) >= 1.74
- [rustc](https://www.rust-lang.org/) >= 1.74

Suggest using the latest version

#### 1.2.2. Building

```shell
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](https://rsproxy.cn)).


## 2. Simple Example

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

```c
#include

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:
```sh
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!**

```sh
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`

```sh
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`:

```sh
sudo rcheat -n onlyc -k structure -f lua
```

3. rcheat loads `core.lua` (built-in), then loads all `.lua` files from the script directory
4. Matches the variable name against `Structure.match_table` to find the alias
5. Calls `Structure:new_(bytes)` to parse the raw bytes into a table
6. 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_(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](https://www.lua.org/manual/5.4/manual.html#6.4.2) 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` (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`:

```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
- [x] use lib like `table` to format matrix table data
- [x] use `lua` to customized output
- [x] search pid by process name (like linux command: `pidof/pgrep`)
- [x] regex replace String.contain
- [x] if match more than 1 entry name, ask for which one to select
- [x] demangle symbols