English | 简体中文
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.
-
- 1.1. Via cargo
- 1.2. Build src
- 1.2.1. Dependencies
- 1.2.2. Building
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 rcheatThis 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
Suggest using the latest version
git clone https://github.com/handy-sun/rcheat.git
cd rcheat
cargo buildYou 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).
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 && ./onlycGet 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 13725Then 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
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.
- Place Lua script files in
/etc/rcheat/lua/ - Run
rcheatwith-f lua:
sudo rcheat -n onlyc -k structure -f lua- rcheat loads
core.lua(built-in), then loads all.luafiles from the script directory - Matches the variable name against
Structure.match_tableto find the alias - Calls
Structure:new_<alias>(bytes)to parse the raw bytes into a table - Outputs a formatted table
Every script must define a global Structure table with:
match_table— maps variable name patterns (Lua string.find) to aliasesnew_<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).
/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)
endOutput (rcheat will format it as an aligned table):
╭─────┬────┬────────╮
│ (i) │ id │ stared │ act │
├─────┼────┼────────┤───────┤
│ 0 │ 1 │ 0 │ 3.50 │
│ 1 │ 2 │ 1 │ 7.25 │
╰─────┴────┴────────┴───────╯
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.
The development plan of the project and the functions to be implemented
- parse
.debug*section - use log crate such as
log/env_loggeretc. - write data to tracee process' memory
- use config.toml to reduce some inputs
- use lib like
tableto format matrix table data - use
luato 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