chore: split builtins in multiple files
Probably also other stuff but I left this uncommitted on my PC for too long and now I don't remember what I did (:
This commit is contained in:
parent
536612f356
commit
888b3279a2
11 changed files with 409 additions and 384 deletions
|
@ -2,7 +2,7 @@ use mlua::Lua;
|
||||||
use tokio::{sync::{mpsc, broadcast}, net::{TcpListener, TcpStream}, io::{AsyncWriteExt, AsyncReadExt}};
|
use tokio::{sync::{mpsc, broadcast}, net::{TcpListener, TcpStream}, io::{AsyncWriteExt, AsyncReadExt}};
|
||||||
use tracing::{debug, error, warn};
|
use tracing::{debug, error, warn};
|
||||||
|
|
||||||
use crate::runtime::{register_builtin_fn, VERSIONTEXT, repl::LuaRepl};
|
use crate::{repl::{LuaRepl, VERSIONTEXT}, tools::register_builtin_fn};
|
||||||
|
|
||||||
pub struct ControlChannel {
|
pub struct ControlChannel {
|
||||||
addr: String,
|
addr: String,
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
use mlua::{UserData, Error};
|
use mlua::{UserData, Error};
|
||||||
use tokio::sync::broadcast;
|
use tokio::sync::broadcast;
|
||||||
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Console (broadcast::Sender<String>);
|
pub struct Console (broadcast::Sender<String>);
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
mod runtime;
|
|
||||||
mod channel;
|
mod channel;
|
||||||
mod helpers;
|
mod helpers;
|
||||||
|
mod console;
|
||||||
|
mod repl;
|
||||||
|
mod tools;
|
||||||
|
|
||||||
use channel::ControlChannel;
|
use channel::ControlChannel;
|
||||||
use tracing::{error, debug};
|
use tracing::error;
|
||||||
|
|
||||||
#[ctor::ctor]
|
#[ctor::ctor]
|
||||||
fn contructor() {
|
fn contructor() {
|
||||||
|
@ -12,7 +14,6 @@ fn contructor() {
|
||||||
.with_max_level(tracing::Level::DEBUG)
|
.with_max_level(tracing::Level::DEBUG)
|
||||||
.with_writer(std::io::stderr)
|
.with_writer(std::io::stderr)
|
||||||
.init();
|
.init();
|
||||||
debug!("infected process");
|
|
||||||
tokio::runtime::Builder::new_current_thread()
|
tokio::runtime::Builder::new_current_thread()
|
||||||
.enable_all()
|
.enable_all()
|
||||||
.build()?
|
.build()?
|
||||||
|
|
|
@ -9,6 +9,8 @@ const FF : char = '\u{C}'; // line feed, <C-L>, ^L
|
||||||
const CMD: char = '\u{1B}'; // ANSI escape char
|
const CMD: char = '\u{1B}'; // ANSI escape char
|
||||||
const CR : char = '\u{A}'; // newline, \n, 10
|
const CR : char = '\u{A}'; // newline, \n, 10
|
||||||
|
|
||||||
|
pub const VERSIONTEXT : &str = "LuaJit 5.2 via rlua";
|
||||||
|
|
||||||
enum CmdStep {
|
enum CmdStep {
|
||||||
Nope,
|
Nope,
|
||||||
One,
|
One,
|
|
@ -1,345 +0,0 @@
|
||||||
use std::{ffi::{c_void, c_int}, num::NonZeroUsize, sync::atomic::{AtomicBool, Ordering}};
|
|
||||||
|
|
||||||
use iced_x86::{Decoder, DecoderOptions, IntelFormatter, Instruction, Formatter};
|
|
||||||
use mlua::{Lua, Error, Variadic, Value, ToLua, Table};
|
|
||||||
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 super::{console::Console, HELPTEXT, GLOBAL_CONSOLE};
|
|
||||||
|
|
||||||
const SIGSEGV_HOOK : AtomicBool = AtomicBool::new(false);
|
|
||||||
|
|
||||||
pub fn lua_help(lua: &Lua, _args: ()) -> Result<(), Error> {
|
|
||||||
let console : Console = lua.globals().get(GLOBAL_CONSOLE)?;
|
|
||||||
console.send(HELPTEXT.into())?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn lua_log(lua: &Lua, values: Variadic<Value>) -> Result<usize, Error> {
|
|
||||||
let mut out = String::new();
|
|
||||||
let console : Console = lua.globals().get(GLOBAL_CONSOLE)?;
|
|
||||||
for value in values {
|
|
||||||
out.push_str(&pretty_lua(value));
|
|
||||||
out.push(' ');
|
|
||||||
}
|
|
||||||
out.push('\n');
|
|
||||||
let size = out.len();
|
|
||||||
console.send(out)?;
|
|
||||||
Ok(size)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn lua_hexdump(lua: &Lua, (bytes, ret): (Vec<u8>, Option<bool>)) -> Result<Value, Error> {
|
|
||||||
if ret.unwrap_or(false) {
|
|
||||||
return Ok(pretty_hex::simple_hex(&bytes).to_lua(lua)?);
|
|
||||||
}
|
|
||||||
let console : Console = lua.globals().get(GLOBAL_CONSOLE)?;
|
|
||||||
console.send(pretty_hex::pretty_hex(&bytes) + "\n")?;
|
|
||||||
Ok(Value::Nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn padding(size: i32) -> String {
|
|
||||||
if size <= 0 {
|
|
||||||
"".into()
|
|
||||||
} else {
|
|
||||||
(0..size as usize).map(|_| " ").collect::<String>()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn lua_decomp(lua: &Lua, (bytes, ret): (Vec<u8>, Option<bool>)) -> Result<Value, Error> {
|
|
||||||
let ret_value = ret.unwrap_or(false);
|
|
||||||
let bitness = 8 * std::mem::size_of::<usize>() as u32;
|
|
||||||
let mut decoder = Decoder::with_ip(bitness, bytes.as_slice(), 0, DecoderOptions::NONE);
|
|
||||||
let mut formatter = IntelFormatter::new();
|
|
||||||
let mut instr_buffer = String::new();
|
|
||||||
let mut raw_buffer = String::new();
|
|
||||||
let mut instruction = Instruction::default();
|
|
||||||
let mut output = String::new();
|
|
||||||
let mut retval = vec![];
|
|
||||||
let mut count = 0;
|
|
||||||
while decoder.can_decode() {
|
|
||||||
decoder.decode_out(&mut instruction);
|
|
||||||
instr_buffer.clear();
|
|
||||||
formatter.format(&instruction, &mut instr_buffer);
|
|
||||||
if ret_value {
|
|
||||||
retval.push(instr_buffer.clone());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
raw_buffer.clear();
|
|
||||||
let start_index = instruction.ip() as usize;
|
|
||||||
let instrs_bytes = &bytes[start_index..start_index+instruction.len()];
|
|
||||||
for b in instrs_bytes {
|
|
||||||
raw_buffer.push_str(&format!("{:02x} ", b));
|
|
||||||
}
|
|
||||||
let padding = padding(30 - raw_buffer.len() as i32);
|
|
||||||
output.push_str(&format!("{:08X}: {}{}{}\n", instruction.ip(), raw_buffer, padding, instr_buffer));
|
|
||||||
count += 1;
|
|
||||||
}
|
|
||||||
if ret_value {
|
|
||||||
Ok(retval.to_lua(lua)?)
|
|
||||||
} else {
|
|
||||||
let console : Console = lua.globals().get(GLOBAL_CONSOLE)?;
|
|
||||||
console.send(output)?;
|
|
||||||
Ok(count.to_lua(lua)?)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn lua_hex(l: &Lua, (value, prefix): (Value, Option<bool>)) -> Result<String, Error> {
|
|
||||||
let pre = if prefix.unwrap_or(true) { "0x" } else { "" };
|
|
||||||
match value {
|
|
||||||
Value::Nil => Ok(format!("{}00", pre)),
|
|
||||||
Value::Boolean(b) => Ok(format!("{}{:02X}", pre, b as i32)),
|
|
||||||
Value::Integer(n) => Ok(format!("{}{:02X}", pre, n)),
|
|
||||||
Value::String(s) => Ok(
|
|
||||||
s.as_bytes()
|
|
||||||
.iter()
|
|
||||||
.map(|x| format!("{:02X}", x))
|
|
||||||
.fold(pre.into(), |acc, x| acc + x.as_str())
|
|
||||||
),
|
|
||||||
Value::Table(t) => Ok(
|
|
||||||
t.sequence_values::<Value>().into_iter()
|
|
||||||
.filter_map(|x| if let Ok(v) = x { Some(v) } else { None })
|
|
||||||
.map(|x| lua_hex(l, (x, Some(false))).unwrap_or("??".into())) // recursive! try stopping me
|
|
||||||
.fold(pre.into(), |acc, x| acc + x.as_str())
|
|
||||||
),
|
|
||||||
Value::Number(_) => Err(Error::RuntimeError("float has no hex value".into())),
|
|
||||||
Value::Function(_) => Err(Error::RuntimeError("function has no hex value".into())),
|
|
||||||
Value::Thread(_) => Err(Error::RuntimeError("thread has no hex value".into())),
|
|
||||||
Value::LightUserData(_) => Err(Error::RuntimeError("LightUserData has no hex value".into())),
|
|
||||||
Value::UserData(_) => Err(Error::RuntimeError("UserData has no hex value".into())),
|
|
||||||
Value::Error(_) => Err(Error::RuntimeError("Error has no hex value".into())),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// could just use .to_ne_bytes() but lot of trailing zeros
|
|
||||||
fn i64_to_significant_bytes(n: i64) -> Vec<u8> {
|
|
||||||
let mut out = vec![];
|
|
||||||
for i in 0..8 {
|
|
||||||
let val = (n >> (i*8)) as u8;
|
|
||||||
let res = n >> ((i+1) * 8);
|
|
||||||
if val == 0 && res == 0 { break; }
|
|
||||||
out.push(val);
|
|
||||||
}
|
|
||||||
out
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn lua_bytes(l: &Lua, value: Value) -> Result<Vec<u8>, Error> {
|
|
||||||
match value {
|
|
||||||
Value::Nil => Ok(vec![]),
|
|
||||||
Value::Boolean(b) => Ok(if b { vec![1] } else { vec![0] }),
|
|
||||||
Value::Integer(n) => Ok(i64_to_significant_bytes(n)),
|
|
||||||
Value::String(s) => Ok(s.as_bytes().to_vec()),
|
|
||||||
Value::Table(t) => Ok(
|
|
||||||
t.sequence_values::<Value>().into_iter()
|
|
||||||
.filter_map(|x| if let Ok(v) = x { Some(v) } else { None })
|
|
||||||
.map(|x| lua_bytes(l, x).unwrap_or(vec![]))
|
|
||||||
.fold(vec![], |mut acc, mut x| { acc.append(&mut x); acc })
|
|
||||||
),
|
|
||||||
Value::Number(_) => Err(Error::RuntimeError("cannot display float bytes value".into())),
|
|
||||||
Value::Function(_) => Err(Error::RuntimeError("cannot display function bytes value".into())),
|
|
||||||
Value::Thread(_) => Err(Error::RuntimeError("cannot display thread bytes value".into())),
|
|
||||||
Value::LightUserData(_) => Err(Error::RuntimeError("cannot display LightUserData bytes value".into())),
|
|
||||||
Value::UserData(_) => Err(Error::RuntimeError("cannot display UserData bytes value".into())),
|
|
||||||
Value::Error(_) => Err(Error::RuntimeError("cannot display Error bytes value".into())),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn lua_read(_: &Lua, (addr, size): (usize, usize)) -> Result<Vec<u8>, Error> {
|
|
||||||
if size == 0 {
|
|
||||||
return Ok("".into());
|
|
||||||
}
|
|
||||||
let ptr = addr as *const u8;
|
|
||||||
let slice = unsafe { std::slice::from_raw_parts(ptr, size) };
|
|
||||||
Ok(slice.to_vec())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn lua_write(_: &Lua, (addr, data): (usize, Vec<u8>)) -> Result<usize, Error> {
|
|
||||||
for (i, byte) in data.iter().enumerate() {
|
|
||||||
let off = (addr + i) as *mut u8;
|
|
||||||
unsafe { *off = *byte } ;
|
|
||||||
}
|
|
||||||
Ok(data.len())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn lua_find(
|
|
||||||
_: &Lua, (start, size, pattern, first): (usize, usize, Vec<u8>, Option<bool>)
|
|
||||||
) -> Result<Vec<usize>, Error> {
|
|
||||||
let window = pattern.len();
|
|
||||||
let first_only = first.unwrap_or(false);
|
|
||||||
let mut matches = vec![];
|
|
||||||
|
|
||||||
for i in 0..(size-window) {
|
|
||||||
let slice = unsafe { std::slice::from_raw_parts((start + i) as *const u8, window) };
|
|
||||||
if slice == pattern {
|
|
||||||
matches.push(start + i);
|
|
||||||
if first_only { break; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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> {
|
|
||||||
let maps = proc_maps()
|
|
||||||
.map_err(|e| Error::RuntimeError(
|
|
||||||
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 = String::new();
|
|
||||||
let mut count = 0;
|
|
||||||
for map in maps {
|
|
||||||
count += 1;
|
|
||||||
out.push_str(
|
|
||||||
format!(
|
|
||||||
" * [{}] 0x{:08X}..0x{:08X} +{:08x} ({}b) \t {:?} {}\n",
|
|
||||||
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() },
|
|
||||||
).as_str()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
let console : Console = lua.globals().get(GLOBAL_CONSOLE)?;
|
|
||||||
console.send(out)?;
|
|
||||||
Ok(Value::Integer(count))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 = String::new();
|
|
||||||
let mut count = 0;
|
|
||||||
for task in maps {
|
|
||||||
match thread_status(task) {
|
|
||||||
Ok(s) => {
|
|
||||||
count += 1;
|
|
||||||
out.push_str(
|
|
||||||
format!(" * [{}] {} {} | {} fd)\n", 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(GLOBAL_CONSOLE)?;
|
|
||||||
console.send(out)?;
|
|
||||||
Ok(Value::Integer(count))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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)) } {
|
|
||||||
Ok(()) => Ok(()),
|
|
||||||
Err(e) => Err(Error::RuntimeError(format!("could not run mprotect ({}): {}", e, e.desc()))),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn lua_mmap(_: &Lua, (addr, length, prot, flags, fd, offset): (Option<usize>, usize, Option<i32>, Option<i32>, Option<i32>, Option<i64>)) -> Result<usize, Error> {
|
|
||||||
if length <= 0 {
|
|
||||||
return Ok(0); // TODO make this an Err
|
|
||||||
}
|
|
||||||
match unsafe { mmap(
|
|
||||||
if let Some(a) = addr { NonZeroUsize::new(a) } else { None },
|
|
||||||
NonZeroUsize::new(length).unwrap(), // safe because we manually checked lenght to be > 0
|
|
||||||
if let Some(p) = prot { ProtFlags::from_bits_truncate(p) } else { ProtFlags::PROT_READ | ProtFlags::PROT_WRITE },
|
|
||||||
if let Some(f) = flags { MapFlags::from_bits_truncate(f) } else { MapFlags::MAP_PRIVATE | MapFlags::MAP_ANON },
|
|
||||||
fd.unwrap_or(-1),
|
|
||||||
offset.unwrap_or(0),
|
|
||||||
) } {
|
|
||||||
Ok(x) => Ok(x as usize),
|
|
||||||
Err(e) => Err(Error::RuntimeError(format!("could not run mmap ({}): {}", e, e.desc()))),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn lua_munmap(_: &Lua, (addr, len): (usize, usize)) -> Result<(), Error> {
|
|
||||||
match unsafe { munmap(addr as *mut c_void, len) } {
|
|
||||||
Ok(()) => Ok(()),
|
|
||||||
Err(e) => Err(Error::RuntimeError(format!("could not run munmap ({}): {}", e, e.desc()))),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extern fn handle_sigsegv(_signal: c_int) {
|
|
||||||
eprintln!("Segmentation fault (ignored)");
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn lua_catch_sigsev(_: &Lua, mode: Option<bool>) -> Result<bool, Error> {
|
|
||||||
match mode {
|
|
||||||
Some(m) => match m {
|
|
||||||
true => {
|
|
||||||
let handler = SigHandler::Handler(handle_sigsegv);
|
|
||||||
match unsafe { nix::sys::signal::signal(SIGSEGV, handler) } {
|
|
||||||
Ok(_h) => {
|
|
||||||
SIGSEGV_HOOK.store(true, Ordering::Relaxed);
|
|
||||||
Ok(true)
|
|
||||||
},
|
|
||||||
Err(e) => Err(Error::RuntimeError(format!("could not set sig handler ({}): {}", e, e.desc()))),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
false => {
|
|
||||||
match unsafe { nix::sys::signal::signal(SIGSEGV, SigHandler::SigDfl) } {
|
|
||||||
Ok(_h) => {
|
|
||||||
SIGSEGV_HOOK.store(false, Ordering::Relaxed);
|
|
||||||
Ok(false)
|
|
||||||
},
|
|
||||||
Err(e) => Err(Error::RuntimeError(format!("could not reset sig handler ({}): {}", e, e.desc()))),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
None => Ok(SIGSEGV_HOOK.load(Ordering::Relaxed)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn lua_exit(_: &Lua, code: Option<i32>) -> Result<(), Error> {
|
|
||||||
#[allow(unreachable_code)]
|
|
||||||
Ok(std::process::exit(code.unwrap_or(0)))
|
|
||||||
}
|
|
38
src/tools/dumb.rs
Normal file
38
src/tools/dumb.rs
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
use std::{sync::atomic::{AtomicBool, Ordering}, ffi::c_int};
|
||||||
|
|
||||||
|
use mlua::{Lua, Error};
|
||||||
|
use nix::sys::signal::{SigHandler, Signal::SIGSEGV};
|
||||||
|
|
||||||
|
const SIGSEGV_HOOK : AtomicBool = AtomicBool::new(false);
|
||||||
|
|
||||||
|
extern fn handle_sigsegv(_signal: c_int) {
|
||||||
|
eprintln!("Segmentation fault (ignored)");
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn lua_catch_sigsev(_: &Lua, mode: Option<bool>) -> Result<bool, Error> {
|
||||||
|
match mode {
|
||||||
|
Some(m) => match m {
|
||||||
|
true => {
|
||||||
|
let handler = SigHandler::Handler(handle_sigsegv);
|
||||||
|
match unsafe { nix::sys::signal::signal(SIGSEGV, handler) } {
|
||||||
|
Ok(_h) => {
|
||||||
|
SIGSEGV_HOOK.store(true, Ordering::Relaxed);
|
||||||
|
Ok(true)
|
||||||
|
},
|
||||||
|
Err(e) => Err(Error::RuntimeError(format!("could not set sig handler ({}): {}", e, e.desc()))),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
false => {
|
||||||
|
match unsafe { nix::sys::signal::signal(SIGSEGV, SigHandler::SigDfl) } {
|
||||||
|
Ok(_h) => {
|
||||||
|
SIGSEGV_HOOK.store(false, Ordering::Relaxed);
|
||||||
|
Ok(false)
|
||||||
|
},
|
||||||
|
Err(e) => Err(Error::RuntimeError(format!("could not reset sig handler ({}): {}", e, e.desc()))),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
None => Ok(SIGSEGV_HOOK.load(Ordering::Relaxed)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
166
src/tools/format.rs
Normal file
166
src/tools/format.rs
Normal file
|
@ -0,0 +1,166 @@
|
||||||
|
use iced_x86::{Decoder, DecoderOptions, IntelFormatter, Instruction, Formatter};
|
||||||
|
use mlua::{Lua, Error, Variadic, Value, ToLua};
|
||||||
|
|
||||||
|
use crate::{helpers::pretty_lua, console::Console};
|
||||||
|
|
||||||
|
pub const GLOBAL_CONSOLE : &str = "GLOBAL_CONSOLE";
|
||||||
|
|
||||||
|
pub const HELPTEXT : &str = "?> This is a complete lua repl
|
||||||
|
?> Make scripts or just evaluate expressions
|
||||||
|
?> print() will go to original process stdout, use log()
|
||||||
|
?> to send to this console instead
|
||||||
|
?> Each connection will spawn a fresh repl, but only one
|
||||||
|
?> concurrent connection is allowed
|
||||||
|
?> Some ad-hoc functions to work with affected process
|
||||||
|
?> are already available in this repl globals:
|
||||||
|
> log([arg...]) print to console rather than stdout
|
||||||
|
> hexdump(bytes, [ret]) print hexdump of given {bytes} to console
|
||||||
|
> exit([code]) immediately terminate process
|
||||||
|
> mmap([a], l, [p], [f], [d], [o]) execute mmap syscall
|
||||||
|
> munmap(ptr, len) unmap {len} bytes at {ptr}
|
||||||
|
> mprotect(ptr, len, prot) set {prot} flags from {ptr} to {ptr+len}
|
||||||
|
> 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}
|
||||||
|
> write(addr, bytes) write given {bytes} at {addr}
|
||||||
|
> find(ptr, len, match, [first]) search from {ptr} to {ptr+len} for {match} and return addrs
|
||||||
|
> x(number, [prefix]) show hex representation of given {number}
|
||||||
|
> b(string) return array of bytes from given {string}
|
||||||
|
> sigsegv([set]) get or set SIGSEGV handler state
|
||||||
|
> help() print these messages
|
||||||
|
";
|
||||||
|
|
||||||
|
|
||||||
|
pub fn lua_help(lua: &Lua, _args: ()) -> Result<(), Error> {
|
||||||
|
let console : Console = lua.globals().get(GLOBAL_CONSOLE)?;
|
||||||
|
console.send(HELPTEXT.into())?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn lua_log(lua: &Lua, values: Variadic<Value>) -> Result<usize, Error> {
|
||||||
|
let mut out = String::new();
|
||||||
|
let console : Console = lua.globals().get(GLOBAL_CONSOLE)?;
|
||||||
|
for value in values {
|
||||||
|
out.push_str(&pretty_lua(value));
|
||||||
|
out.push(' ');
|
||||||
|
}
|
||||||
|
out.push('\n');
|
||||||
|
let size = out.len();
|
||||||
|
console.send(out)?;
|
||||||
|
Ok(size)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn lua_hexdump(lua: &Lua, (bytes, ret): (Vec<u8>, Option<bool>)) -> Result<Value, Error> {
|
||||||
|
if ret.unwrap_or(false) {
|
||||||
|
return Ok(pretty_hex::simple_hex(&bytes).to_lua(lua)?);
|
||||||
|
}
|
||||||
|
let console : Console = lua.globals().get(GLOBAL_CONSOLE)?;
|
||||||
|
console.send(pretty_hex::pretty_hex(&bytes) + "\n")?;
|
||||||
|
Ok(Value::Nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn padding(size: i32) -> String {
|
||||||
|
if size <= 0 {
|
||||||
|
"".into()
|
||||||
|
} else {
|
||||||
|
(0..size as usize).map(|_| " ").collect::<String>()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn lua_decomp(lua: &Lua, (bytes, ret): (Vec<u8>, Option<bool>)) -> Result<Value, Error> {
|
||||||
|
let ret_value = ret.unwrap_or(false);
|
||||||
|
let bitness = 8 * std::mem::size_of::<usize>() as u32;
|
||||||
|
let mut decoder = Decoder::with_ip(bitness, bytes.as_slice(), 0, DecoderOptions::NONE);
|
||||||
|
let mut formatter = IntelFormatter::new();
|
||||||
|
let mut instr_buffer = String::new();
|
||||||
|
let mut raw_buffer = String::new();
|
||||||
|
let mut instruction = Instruction::default();
|
||||||
|
let mut output = String::new();
|
||||||
|
let mut retval = vec![];
|
||||||
|
let mut count = 0;
|
||||||
|
while decoder.can_decode() {
|
||||||
|
decoder.decode_out(&mut instruction);
|
||||||
|
instr_buffer.clear();
|
||||||
|
formatter.format(&instruction, &mut instr_buffer);
|
||||||
|
if ret_value {
|
||||||
|
retval.push(instr_buffer.clone());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
raw_buffer.clear();
|
||||||
|
let start_index = instruction.ip() as usize;
|
||||||
|
let instrs_bytes = &bytes[start_index..start_index+instruction.len()];
|
||||||
|
for b in instrs_bytes {
|
||||||
|
raw_buffer.push_str(&format!("{:02x} ", b));
|
||||||
|
}
|
||||||
|
let padding = padding(30 - raw_buffer.len() as i32);
|
||||||
|
output.push_str(&format!("{:08X}: {}{}{}\n", instruction.ip(), raw_buffer, padding, instr_buffer));
|
||||||
|
count += 1;
|
||||||
|
}
|
||||||
|
if ret_value {
|
||||||
|
Ok(retval.to_lua(lua)?)
|
||||||
|
} else {
|
||||||
|
let console : Console = lua.globals().get(GLOBAL_CONSOLE)?;
|
||||||
|
console.send(output)?;
|
||||||
|
Ok(count.to_lua(lua)?)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn lua_hex(l: &Lua, (value, prefix): (Value, Option<bool>)) -> Result<String, Error> {
|
||||||
|
let pre = if prefix.unwrap_or(true) { "0x" } else { "" };
|
||||||
|
match value {
|
||||||
|
Value::Nil => Ok(format!("{}00", pre)),
|
||||||
|
Value::Boolean(b) => Ok(format!("{}{:02X}", pre, b as i32)),
|
||||||
|
Value::Integer(n) => Ok(format!("{}{:02X}", pre, n)),
|
||||||
|
Value::String(s) => Ok(
|
||||||
|
s.as_bytes()
|
||||||
|
.iter()
|
||||||
|
.map(|x| format!("{:02X}", x))
|
||||||
|
.fold(pre.into(), |acc, x| acc + x.as_str())
|
||||||
|
),
|
||||||
|
Value::Table(t) => Ok(
|
||||||
|
t.sequence_values::<Value>().into_iter()
|
||||||
|
.filter_map(|x| if let Ok(v) = x { Some(v) } else { None })
|
||||||
|
.map(|x| lua_hex(l, (x, Some(false))).unwrap_or("??".into())) // recursive! try stopping me
|
||||||
|
.fold(pre.into(), |acc, x| acc + x.as_str())
|
||||||
|
),
|
||||||
|
Value::Number(_) => Err(Error::RuntimeError("float has no hex value".into())),
|
||||||
|
Value::Function(_) => Err(Error::RuntimeError("function has no hex value".into())),
|
||||||
|
Value::Thread(_) => Err(Error::RuntimeError("thread has no hex value".into())),
|
||||||
|
Value::LightUserData(_) => Err(Error::RuntimeError("LightUserData has no hex value".into())),
|
||||||
|
Value::UserData(_) => Err(Error::RuntimeError("UserData has no hex value".into())),
|
||||||
|
Value::Error(_) => Err(Error::RuntimeError("Error has no hex value".into())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// could just use .to_ne_bytes() but lot of trailing zeros
|
||||||
|
fn i64_to_significant_bytes(n: i64) -> Vec<u8> {
|
||||||
|
let mut out = vec![];
|
||||||
|
for i in 0..8 {
|
||||||
|
let val = (n >> (i*8)) as u8;
|
||||||
|
let res = n >> ((i+1) * 8);
|
||||||
|
if val == 0 && res == 0 { break; }
|
||||||
|
out.push(val);
|
||||||
|
}
|
||||||
|
out
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn lua_bytes(l: &Lua, value: Value) -> Result<Vec<u8>, Error> {
|
||||||
|
match value {
|
||||||
|
Value::Nil => Ok(vec![]),
|
||||||
|
Value::Boolean(b) => Ok(if b { vec![1] } else { vec![0] }),
|
||||||
|
Value::Integer(n) => Ok(i64_to_significant_bytes(n)),
|
||||||
|
Value::String(s) => Ok(s.as_bytes().to_vec()),
|
||||||
|
Value::Table(t) => Ok(
|
||||||
|
t.sequence_values::<Value>().into_iter()
|
||||||
|
.filter_map(|x| if let Ok(v) = x { Some(v) } else { None })
|
||||||
|
.map(|x| lua_bytes(l, x).unwrap_or(vec![]))
|
||||||
|
.fold(vec![], |mut acc, mut x| { acc.append(&mut x); acc })
|
||||||
|
),
|
||||||
|
Value::Number(_) => Err(Error::RuntimeError("cannot display float bytes value".into())),
|
||||||
|
Value::Function(_) => Err(Error::RuntimeError("cannot display function bytes value".into())),
|
||||||
|
Value::Thread(_) => Err(Error::RuntimeError("cannot display thread bytes value".into())),
|
||||||
|
Value::LightUserData(_) => Err(Error::RuntimeError("cannot display LightUserData bytes value".into())),
|
||||||
|
Value::UserData(_) => Err(Error::RuntimeError("cannot display UserData bytes value".into())),
|
||||||
|
Value::Error(_) => Err(Error::RuntimeError("cannot display Error bytes value".into())),
|
||||||
|
}
|
||||||
|
}
|
36
src/tools/memory.rs
Normal file
36
src/tools/memory.rs
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
use mlua::{Lua, Error};
|
||||||
|
|
||||||
|
pub fn lua_read(_: &Lua, (addr, size): (usize, usize)) -> Result<Vec<u8>, Error> {
|
||||||
|
if size == 0 {
|
||||||
|
return Ok("".into());
|
||||||
|
}
|
||||||
|
let ptr = addr as *const u8;
|
||||||
|
let slice = unsafe { std::slice::from_raw_parts(ptr, size) };
|
||||||
|
Ok(slice.to_vec())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn lua_write(_: &Lua, (addr, data): (usize, Vec<u8>)) -> Result<usize, Error> {
|
||||||
|
for (i, byte) in data.iter().enumerate() {
|
||||||
|
let off = (addr + i) as *mut u8;
|
||||||
|
unsafe { *off = *byte } ;
|
||||||
|
}
|
||||||
|
Ok(data.len())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn lua_find(
|
||||||
|
_: &Lua, (start, size, pattern, first): (usize, usize, Vec<u8>, Option<bool>)
|
||||||
|
) -> Result<Vec<usize>, Error> {
|
||||||
|
let window = pattern.len();
|
||||||
|
let first_only = first.unwrap_or(false);
|
||||||
|
let mut matches = vec![];
|
||||||
|
|
||||||
|
for i in 0..(size-window) {
|
||||||
|
let slice = unsafe { std::slice::from_raw_parts((start + i) as *const u8, window) };
|
||||||
|
if slice == pattern {
|
||||||
|
matches.push(start + i);
|
||||||
|
if first_only { break; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(matches)
|
||||||
|
}
|
|
@ -1,16 +1,23 @@
|
||||||
pub mod builtins;
|
|
||||||
pub mod console;
|
|
||||||
pub mod repl;
|
|
||||||
|
|
||||||
use mlua::{Lua, Error};
|
use mlua::{Lua, Error};
|
||||||
use nix::sys::mman::{ProtFlags, MapFlags};
|
use nix::sys::mman::{ProtFlags, MapFlags};
|
||||||
|
|
||||||
use tokio::sync::broadcast;
|
use tokio::sync::broadcast;
|
||||||
|
|
||||||
use crate::runtime::console::Console;
|
use crate::console::Console;
|
||||||
use crate::runtime::builtins::*;
|
|
||||||
|
|
||||||
pub const GLOBAL_CONSOLE : &str = "GLOBAL_CONSOLE";
|
use self::format::GLOBAL_CONSOLE;
|
||||||
|
|
||||||
|
pub mod format;
|
||||||
|
pub mod memory;
|
||||||
|
pub mod syscall;
|
||||||
|
pub mod proc;
|
||||||
|
|
||||||
|
pub mod dumb;
|
||||||
|
|
||||||
|
use self::dumb::*;
|
||||||
|
use self::format::*;
|
||||||
|
use self::memory::*;
|
||||||
|
use self::proc::*;
|
||||||
|
use self::syscall::*;
|
||||||
|
|
||||||
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(GLOBAL_CONSOLE, Console::from(console))?; // TODO passing it this way makes clones
|
lua.globals().set(GLOBAL_CONSOLE, Console::from(console))?; // TODO passing it this way makes clones
|
||||||
|
@ -43,29 +50,3 @@ pub fn register_builtin_fn(lua: &Lua, console: broadcast::Sender<String>) -> Res
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const VERSIONTEXT : &str = "LuaJit 5.2 via rlua";
|
|
||||||
pub const HELPTEXT : &str = "?> This is a complete lua repl
|
|
||||||
?> Make scripts or just evaluate expressions
|
|
||||||
?> print() will go to original process stdout, use log()
|
|
||||||
?> to send to this console instead
|
|
||||||
?> Each connection will spawn a fresh repl, but only one
|
|
||||||
?> concurrent connection is allowed
|
|
||||||
?> Some ad-hoc functions to work with affected process
|
|
||||||
?> are already available in this repl globals:
|
|
||||||
> log([arg...]) print to console rather than stdout
|
|
||||||
> hexdump(bytes, [ret]) print hexdump of given {bytes} to console
|
|
||||||
> exit([code]) immediately terminate process
|
|
||||||
> mmap([a], l, [p], [f], [d], [o]) execute mmap syscall
|
|
||||||
> munmap(ptr, len) unmap {len} bytes at {ptr}
|
|
||||||
> mprotect(ptr, len, prot) set {prot} flags from {ptr} to {ptr+len}
|
|
||||||
> 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}
|
|
||||||
> write(addr, bytes) write given {bytes} at {addr}
|
|
||||||
> find(ptr, len, match, [first]) search from {ptr} to {ptr+len} for {match} and return addrs
|
|
||||||
> x(number, [prefix]) show hex representation of given {number}
|
|
||||||
> b(string) return array of bytes from given {string}
|
|
||||||
> sigsegv([set]) get or set SIGSEGV handler state
|
|
||||||
> help() print these messages
|
|
||||||
";
|
|
104
src/tools/proc.rs
Normal file
104
src/tools/proc.rs
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
use mlua::{Lua, Error, Table, Value, ToLua};
|
||||||
|
use procfs::{process::{Status, MemoryMap, Process, MemoryMaps, Task, TasksIter}, ProcResult, ProcError};
|
||||||
|
use tracing::warn;
|
||||||
|
|
||||||
|
use crate::console::Console;
|
||||||
|
|
||||||
|
use super::format::GLOBAL_CONSOLE;
|
||||||
|
|
||||||
|
|
||||||
|
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> {
|
||||||
|
let maps = proc_maps()
|
||||||
|
.map_err(|e| Error::RuntimeError(
|
||||||
|
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 = String::new();
|
||||||
|
let mut count = 0;
|
||||||
|
for map in maps {
|
||||||
|
count += 1;
|
||||||
|
out.push_str(
|
||||||
|
format!(
|
||||||
|
" * [{}] 0x{:08X}..0x{:08X} +{:08x} ({}b) \t {:?} {}\n",
|
||||||
|
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() },
|
||||||
|
).as_str()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
let console : Console = lua.globals().get(GLOBAL_CONSOLE)?;
|
||||||
|
console.send(out)?;
|
||||||
|
Ok(Value::Integer(count))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 = String::new();
|
||||||
|
let mut count = 0;
|
||||||
|
for task in maps {
|
||||||
|
match thread_status(task) {
|
||||||
|
Ok(s) => {
|
||||||
|
count += 1;
|
||||||
|
out.push_str(
|
||||||
|
format!(" * [{}] {} {} | {} fd)\n", 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(GLOBAL_CONSOLE)?;
|
||||||
|
console.send(out)?;
|
||||||
|
Ok(Value::Integer(count))
|
||||||
|
}
|
||||||
|
}
|
43
src/tools/syscall.rs
Normal file
43
src/tools/syscall.rs
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
use std::{ffi::c_void, num::NonZeroUsize};
|
||||||
|
|
||||||
|
use mlua::{Lua, Error};
|
||||||
|
use nix::sys::mman::{mprotect, ProtFlags, mmap, MapFlags, munmap};
|
||||||
|
|
||||||
|
use cordy_macro::lua_fn;
|
||||||
|
|
||||||
|
#[lua_fn]
|
||||||
|
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)) } {
|
||||||
|
Ok(()) => Ok(()),
|
||||||
|
Err(e) => Err(Error::RuntimeError(format!("could not run mprotect ({}): {}", e, e.desc()))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn lua_mmap(_: &Lua, (addr, length, prot, flags, fd, offset): (Option<usize>, usize, Option<i32>, Option<i32>, Option<i32>, Option<i64>)) -> Result<usize, Error> {
|
||||||
|
if length <= 0 {
|
||||||
|
return Ok(0); // TODO make this an Err
|
||||||
|
}
|
||||||
|
match unsafe { mmap(
|
||||||
|
if let Some(a) = addr { NonZeroUsize::new(a) } else { None },
|
||||||
|
NonZeroUsize::new(length).unwrap(), // safe because we manually checked lenght to be > 0
|
||||||
|
if let Some(p) = prot { ProtFlags::from_bits_truncate(p) } else { ProtFlags::PROT_READ | ProtFlags::PROT_WRITE },
|
||||||
|
if let Some(f) = flags { MapFlags::from_bits_truncate(f) } else { MapFlags::MAP_PRIVATE | MapFlags::MAP_ANON },
|
||||||
|
fd.unwrap_or(-1),
|
||||||
|
offset.unwrap_or(0),
|
||||||
|
) } {
|
||||||
|
Ok(x) => Ok(x as usize),
|
||||||
|
Err(e) => Err(Error::RuntimeError(format!("could not run mmap ({}): {}", e, e.desc()))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn lua_munmap(_: &Lua, (addr, len): (usize, usize)) -> Result<(), Error> {
|
||||||
|
match unsafe { munmap(addr as *mut c_void, len) } {
|
||||||
|
Ok(()) => Ok(()),
|
||||||
|
Err(e) => Err(Error::RuntimeError(format!("could not run munmap ({}): {}", e, e.desc()))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn lua_exit(_: &Lua, code: Option<i32>) -> Result<(), Error> {
|
||||||
|
#[allow(unreachable_code)]
|
||||||
|
Ok(std::process::exit(code.unwrap_or(0)))
|
||||||
|
}
|
Loading…
Reference in a new issue