feat: add threads cmd, improved ret values

now procmaps, threads and hexdump output is different when returned:
hexdump gives just the hex string divided in octets, while procmaps and
threads return a table of tables with proper fields
This commit is contained in:
əlemi 2023-04-06 04:19:34 +02:00
parent 2be585fc7b
commit 690cb379d7
Signed by: alemi
GPG key ID: A4895B84D311642C
3 changed files with 93 additions and 22 deletions

View file

@ -22,3 +22,4 @@ serde_json = "1.0.95"
nix = "0.26.2" nix = "0.26.2"
pretty-hex = "0.3" pretty-hex = "0.3"
signal-hook = "0.3.15" signal-hook = "0.3.15"
procfs = "0.15.1"

View file

@ -1,8 +1,9 @@
use std::{ffi::c_void, num::NonZeroUsize}; use std::{ffi::{c_void, c_int}, num::NonZeroUsize, sync::atomic::{AtomicBool, Ordering}};
use mlua::{Lua, Error, Variadic, Value, ToLua}; use mlua::{Lua, Error, Variadic, Value, ToLua, Table};
use pox::{proc_maps::get_process_maps, tricks::fmt_path};
use nix::sys::{mman::{mprotect, ProtFlags, mmap, MapFlags, munmap}, signal::{Signal::SIGSEGV, SigHandler}}; use nix::sys::{mman::{mprotect, ProtFlags, mmap, MapFlags, munmap}, signal::{Signal::SIGSEGV, SigHandler}};
use procfs::{process::{Process, MemoryMaps, TasksIter, Status, Task, MemoryMap}, ProcError, ProcResult};
use tracing::warn;
use crate::helpers::pretty_lua; use crate::helpers::pretty_lua;
@ -29,12 +30,11 @@ pub fn lua_log(lua: &Lua, values: Variadic<Value>) -> Result<usize, Error> {
} }
pub fn lua_hexdump(lua: &Lua, (bytes, ret): (Vec<u8>, Option<bool>)) -> Result<Value, Error> { pub fn lua_hexdump(lua: &Lua, (bytes, ret): (Vec<u8>, Option<bool>)) -> Result<Value, Error> {
let txt = pretty_hex::pretty_hex(&bytes) + "\n";
if ret.unwrap_or(false) { if ret.unwrap_or(false) {
return Ok(txt.to_lua(lua)?); return Ok(pretty_hex::simple_hex(&bytes).to_lua(lua)?);
} }
let console : Console = lua.globals().get("console")?; let console : Console = lua.globals().get("console")?;
console.send(txt)?; console.send(pretty_hex::pretty_hex(&bytes))?;
Ok(Value::Nil) Ok(Value::Nil)
} }
@ -133,28 +133,97 @@ pub fn lua_find(
Ok(matches) Ok(matches)
} }
fn proc_table(lua: &Lua, task: Status) -> Result<Table, Error> {
let table = lua.create_table()?;
table.set("pid", task.pid)?;
table.set("name", task.name)?;
table.set("state", task.state)?;
table.set("fdsize", task.fdsize)?;
Ok(table)
}
fn map_table(lua: &Lua, task: MemoryMap) -> Result<Table, Error> {
let table = lua.create_table()?;
table.set("perms", task.perms.as_str())?;
table.set("address", task.address.0)?;
table.set("offset", task.offset)?;
table.set("size", task.address.1 - task.address.0)?;
table.set("path", format!("{:?}", task.pathname))?;
Ok(table)
}
fn proc_maps() -> ProcResult<MemoryMaps> {
Ok(Process::myself()?.maps()?)
}
pub fn lua_procmaps(lua: &Lua, ret: Option<bool>) -> Result<Value, Error> { pub fn lua_procmaps(lua: &Lua, ret: Option<bool>) -> Result<Value, Error> {
let mut out = String::new(); let maps = proc_maps()
let maps = get_process_maps(std::process::id() as i32)
.map_err(|e| Error::RuntimeError( .map_err(|e| Error::RuntimeError(
format!("could not obtain process maps: {}", e) format!("could not obtain process maps: {}", e)
))?; ))?;
if ret.unwrap_or(false) {
let mut out = vec![];
for map in maps {
out.push(map_table(lua, map)?);
}
Ok(out.to_lua(lua)?)
} else {
let mut out = format!("=> /proc/{}/maps", std::process::id());
for map in maps { for map in maps {
out.push_str( out.push_str(
format!( format!(
"[{}] 0x{:08X}..0x{:08X} +{:08x} ({}b) \t {} {}\n", "\n[{}] 0x{:08X}..0x{:08X} +{:08x} ({}b) \t {:?} {}",
map.flags, map.start(), map.start() + map.size(), map.offset, map.size(), fmt_path(map.filename()), map.perms.as_str(), map.address.0, map.address.1, map.offset, map.address.1 - map.address.0, map.pathname,
if map.inode != 0 { format!("({})", map.inode) } else { "".into() }, if map.inode != 0 { format!("({})", map.inode) } else { "".into() },
).as_str() ).as_str()
); );
} }
if ret.unwrap_or(false) {
return Ok(out.to_lua(lua)?);
}
let console : Console = lua.globals().get("console")?; let console : Console = lua.globals().get("console")?;
console.send(out)?; console.send(out)?;
Ok(Value::Nil) Ok(Value::Nil)
} }
}
fn thread_maps() -> ProcResult<TasksIter> {
Ok(Process::myself()?.tasks()?)
}
fn thread_status(task: Result<Task, ProcError>) -> ProcResult<Status> {
Ok(task?.status()?)
}
pub fn lua_threads(lua: &Lua, ret: Option<bool>) -> Result<Value, Error> {
let maps = thread_maps()
.map_err(|e| Error::RuntimeError(
format!("could not obtain task maps: {}", e)
))?;
if ret.unwrap_or(false) {
let mut out = vec![];
for task in maps {
match thread_status(task) {
Ok(s) => out.push(proc_table(lua, s)?),
Err(e) => warn!("could not parse task metadata: {}", e),
}
}
Ok(out.to_lua(lua)?)
} else {
let mut out = format!("=> /proc/{}/tasks", std::process::id());
for task in maps {
match thread_status(task) {
Ok(s) => {
out.push_str(
format!("\n * [{}] {} {} | {} fd)", s.pid, s.state, s.name, s.fdsize).as_str()
);
},
Err(e) => warn!("could not parse task metadata: {}", e),
}
}
let console : Console = lua.globals().get("console")?;
console.send(out)?;
Ok(Value::Nil)
}
}
pub fn lua_mprotect(_: &Lua, (addr, size, prot): (usize, usize, i32)) -> Result<(), Error> { pub fn lua_mprotect(_: &Lua, (addr, size, prot): (usize, usize, i32)) -> Result<(), Error> {
match unsafe { mprotect(addr as *mut c_void, size, ProtFlags::from_bits_truncate(prot)) } { match unsafe { mprotect(addr as *mut c_void, size, ProtFlags::from_bits_truncate(prot)) } {

View file

@ -10,7 +10,6 @@ use tokio::sync::broadcast;
use crate::runtime::console::Console; use crate::runtime::console::Console;
use crate::runtime::builtins::*; use crate::runtime::builtins::*;
pub fn register_builtin_fn(lua: &Lua, console: broadcast::Sender<String>) -> Result<(), Error> { pub fn register_builtin_fn(lua: &Lua, console: broadcast::Sender<String>) -> Result<(), Error> {
lua.globals().set("console", Console::from(console))?; // TODO passing it this way makes clones lua.globals().set("console", Console::from(console))?; // TODO passing it this way makes clones
@ -28,6 +27,7 @@ pub fn register_builtin_fn(lua: &Lua, console: broadcast::Sender<String>) -> Res
lua.globals().set("write", lua.create_function(lua_write)?)?; lua.globals().set("write", lua.create_function(lua_write)?)?;
lua.globals().set("find", lua.create_function(lua_find)?)?; lua.globals().set("find", lua.create_function(lua_find)?)?;
lua.globals().set("procmaps", lua.create_function(lua_procmaps)?)?; lua.globals().set("procmaps", lua.create_function(lua_procmaps)?)?;
lua.globals().set("threads", lua.create_function(lua_threads)?)?;
lua.globals().set("exit", lua.create_function(lua_exit)?)?; lua.globals().set("exit", lua.create_function(lua_exit)?)?;
lua.globals().set("mmap", lua.create_function(lua_mmap)?)?; lua.globals().set("mmap", lua.create_function(lua_mmap)?)?;
lua.globals().set("munmap", lua.create_function(lua_munmap)?)?; lua.globals().set("munmap", lua.create_function(lua_munmap)?)?;
@ -56,6 +56,7 @@ pub const HELPTEXT : &str = "?> This is a complete lua repl
> munmap(ptr, len) unmap {len} bytes at {ptr} > munmap(ptr, len) unmap {len} bytes at {ptr}
> mprotect(ptr, len, prot) set {prot} flags from {ptr} to {ptr+len} > mprotect(ptr, len, prot) set {prot} flags from {ptr} to {ptr+len}
> procmaps([ret]) get process memory maps as string > procmaps([ret]) get process memory maps as string
> threads([ret]) get process threads list as string
> read(addr, size) read {size} raw bytes at {addr} > read(addr, size) read {size} raw bytes at {addr}
> write(addr, bytes) write given {bytes} at {addr} > write(addr, bytes) write given {bytes} at {addr}
> find(ptr, len, match, [first]) search from {ptr} to {ptr+len} for {match} and return addrs > find(ptr, len, match, [first]) search from {ptr} to {ptr+len} for {match} and return addrs