Is it possible to edit static variables in a binary executable (in Linux)?

I'm trying to wrote a program which interact with kwin_wayland, use libc to read its memory.

Currently, the offset is calculated while the program is compiled, thus when kwin_wayland updated, the offset changes, and the program will use the outdated offset.

I'm not willing to compile the program each time kwin is updated, thus I want to write some update function that update the offset (stored in variables like pub static OFFSET: usize = 0xdeadbeef;)

I have the following 2 questions:

  1. Since the pub static OFFSET: usize = 0xdeadbeef; seems unchanged, will rustc inline it directly just like we are defining something like pub const OFFSET: usize = 0xdeadbeef;?
  2. Is there a way to modify their value stored in binary executables?

The values that init the statics are usually in a separate section of the executable, so they could be edited. If it's an external symbol you should be able to find it via symbol tables.

However, you can make it AtomicUsize, and you will be able to edit it safely at run time.

I'm not sure about immutable values on the Rust side, but if the static is in C code (extern C block), then Rust will definitely assume it's mutable and re-read it when needed.

4 Likes

I don't think rustc provides guarantees in that regard. It seems purely the responsibility of the optimizer. That said, caching a value loaded from an immutable static looks like a possible optimization. For that reason I'd wrap it in UnsafeCell (to block immutability assumptions), and since it's a global static it should probably be an AtomicUsize, even if you don't plan to modify it at runtime. Perhaps also leak a ptr::addr_of_mut!(OFFSET) via hint::black_box, so that the optimizer doesn't try to do any global analysis of mutability if you're producing an executable rather than a library.

Solution:

#[used] // prevent compiler from optimize it out.
#[unsafe(link_section = ".kwin.mouse.loc.pos")]
pub(crate) static mut POS_OFFSET: usize = 0; // modify .kwin.mouse.loc.pos later.
#[used] // although usually WORKSPACE_OFFSET.addr() == POS_OFFSET.addr() + size_of::<usize>(), but here, creating another section could be better.
#[unsafe(link_section = ".kwin.mouse.loc.kwin")]
pub(crate) static mut WORKSPACE_OFFSET: usize = 0; // modify .kwin.mouse.loc.workspace later.

Then the sections could be modified by offset readed by readelf -WCS.

Plus, since I'm operating pointers, using static mut POS_OFFSET should be fine. Since it is mutable indeed, and I could check whether it is modified.

2 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.