Lunatik is a framework for scripting the Linux kernel with Lua.
It is composed by the Lua interpreter modified to run in the kernel;
a device driver (written in Lua=)) and a command line tool
to load and run scripts and manage runtime environments from the user space;
a C API to load and run scripts and manage runtime environments from the kernel;
and Lua APIs for binding kernel facilities to Lua scripts.
Here is an example of a character device driver written in Lua using Lunatik
to generate random ASCII printable characters:
— /lib/modules/lua/passwd.lua
—
— implements /dev/passwd for generate passwords
— usage: $ sudo lunatik run passwd
— $ head -c /dev/passwd
local device=require(“device”)
local linux =require(“linux”)
local function nop() end — do nothing
local s=linux.stat
local driver={name=”passwd”, open=nop, release=nop, mode=s.IRUGO}
function driver:read() — read(2) callback
— generate random ASCII printable characters
return string.char(linux.random(32, 126))
end
— creates a new character device
device.new(driver)
make
sudo make install
sudo lunatik # execute Lunatik REPL
Lunatik 3.4 Copyright (C) 2023-2024 ring-0 Ltda.
> return 42 — execute this line in the kernel
42
usage: lunatik [load|unload|reload|status|list] [run|spawn|stop ]
load: load Lunatik kernel modules
unload: unload Lunatik kernel modules
reload: reload Lunatik kernel modules
status: show which Lunatik kernel modules are currently loaded
list: show which runtime environments are currently running
run: create a new runtime environment to run the script /lib/modules/lua/.lua
spawn: create a new runtime environment and spawn a thread to run the script /lib/modules/lua/.lua
stop: stop the runtime environment created to run the script
default: start a REPL (Read–Eval–Print Loop)
Lunatik 3.4 is based on
Lua 5.4 adapted
to run in the kernel.
Lunatik does not support floating-point arithmetic,
thus it does not support __div nor __pow
metamethods
and the type number has only the subtype integer.
Lunatik does not support both io and
os libraries,
and the given identifiers from the following libraries:
debug.debug,
math.acos,
math.asin,
math.atan,
math.ceil,
math.cos,
math.deg,
math.exp,
math.floor,
math.fmod,
math.huge.
math.log,
math.modf,
math.pi,
math.rad,
math.random,
math.randomseed,
math.sin,
math.sqrt,
math.tan,
math.type,
package.cpath.
Lunatik modifies the following identifiers:
_VERSION: is defined as “Lua 5.4-kernel”.
collectgarbage(“count”): returns the total memory in use by Lua in bytes, instead of Kbytes.
package.path: is defined as “/lib/modules/lua/?.lua;/lib/modules/lua/?/init.lua”.
require: only supports built-in or already linked C modules, that is, Lunatik cannot load kernel modules dynamically.
Lunatik does not support
luaL_Stream,
luaL_execresult,
luaL_fileresult,
luaopen_io and
luaopen_os.
Lunatik modifies luaL_openlibs to remove luaopen_io and luaopen_os.
int lunatik_runtime(lunatik_object_t **pruntime, const char *script, bool sleep);
lunatik_runtime() creates a new runtime environment then loads and runs the script
/lib/modules/lua/.lua as the entry point for this environment.
It must only be called from process context.
The runtime environment is a Lunatik object that holds
a Lua state.
Lunatik objects are special
Lua userdata
which also hold
a lock type and
a reference counter.
If sleep is true, lunatik_runtime() will use a
mutex
for locking the runtime environment and the
GFP_KERNEL
flag for allocating new memory later on on
lunatik_run() calls.
Otherwise, it will use a spinlock and GFP_ATOMIC.
lunatik_runtime() opens the Lua standard libraries
present on Lunatik.
If successful, lunatik_runtime() sets the address pointed by pruntime and
Lua’s extra space
with a pointer for the new created runtime environment,
sets the reference counter to 1 and then returns 0.
Otherwise, it returns -ENOMEM, if insufficient memory is available;
or -EINVAL, if it fails to load or run the script.
— /lib/modules/lua/mydevice.lua
function myread(len, off)
return “42”
end
static lunatik_object_t *runtime;
static int __init mydevice_init(void)
{
return lunatik_runtime(&runtime, “mydevice”, true);
}
int lunatik_stop(lunatik_object_t *runtime);
lunatik_stop()
closes
the
Lua state
created for this runtime environment and decrements the
reference counter.
Once the reference counter is decremented to zero, the
lock type
and the memory allocated for the runtime environment are released.
If the runtime environment has been released, it returns 1;
otherwise, it returns 0.
void lunatik_run(lunatik_object_t *runtime, (*handler)(…), &ret, …);
lunatik_run() locks the runtime environment and calls the handler
passing the associated Lua state as the first argument followed by the variadic arguments.
If the Lua state has been closed, ret is set with -ENXIO;
otherwise, ret is set with the result of handler(L, …) call.
Then, it restores the Lua stack and unlocks the runtime environment.
It is defined as a macro.
static int l_read(lua_State *L, char *buf, size_t len, loff_t *off)
{
size_t llen;
const char *lbuf;
lua_getglobal(L, “myread”);
lua_pushinteger(L, len);
lua_pushinteger(L, *off);
if (lua_pcall(L, 2, 2, 0) !=LUA_OK) { /* calls myread(len, off) */
pr_err(“%sn”, lua_tostring(L, -1));
return -ECANCELED;
}
lbuf=lua_tolstring(L, -2, &llen);
llen=min(len, llen);
if (copy_to_user(buf, lbuf, llen) !=0)
return -EFAULT;
*off=(loff_t)luaL_optinteger(L, -1, *off + llen);
return (ssize_t)llen;
}
static ssize_t mydevice_read(struct file *f, char *buf, size_t len, loff_t *off)
{
ssize_t ret;
lunatik_object_t *runtime=(lunatik_object_t *)f->private_data;
lunatik_run(runtime, l_read, ret, buf, len, off);
return ret;
}
void lunatik_getobject(lunatik_object_t *object);
lunatik_getobject() increments the
reference counter
of this object (e.g., runtime environment).
int lunatik_putobject(lunatik_object_t *object);
lunatik_putobject() decrements the
reference counter
of this object (e.g., runtime environment).
If the object has been released, it returns 1;
otherwise, it returns 0.
lunatik_object_t *lunatik_toruntime(lua_State *L);
lunatik_toruntime() returns the runtime environment referenced by the L’s
extra space.
The lunatik library provides support to load and run scripts and manage runtime environments from Lua.
lunatik.runtime(script [, sleep])
lunatik.runtime() creates a new
runtime environment
then loads and runs the script
/lib/modules/lua/.lua as the entry point for this environment.
It returns a Lunatik object representing the runtime environment.
If sleep is true or omitted, it will use a mutex
and
GFP_KERNEL;
otherwise, it will use a spinlock and GFP_ATOMIC.
lunatik.runtime() opens the Lua standard libraries
present on Lunatik.
lunatik.stop(runtime), runtime:stop()
lunatik.stop()
stops
the runtime environment and clear its reference from the runtime object.
The device library provides support for writting
character device drivers
in Lua.
device.new() returns a new device object
and installs its driver in the system.
The driver must be defined as a table containing the following field:
name: string defining the device name; it is used for creating the device file (e.g., /dev/).
The driver table might optionally contain the following fields:
read: callback function to handle the
read operation
on the device file.
It receives the driver table as the first argument
followed by two integers,
the length to be read and the file offset.
It should return a string and, optionally, the updated offset.
If the length of the returned string is greater than the requested length,
the string will be corrected to that length.
If the updated offset is not returned, the offset will be updated with offset + length.
write: callback function to handle the
write operation
on the device file.
It receives the driver table as the first argument
followed by the string to be written and
an integer as the file offset.
It might return optionally the written length followed by the updated offset.
If the returned length is greater than the requested length,
the returned length will be corrected.
If the updated offset is not returned, the offset will be updated with offset + length.
open: callback function to handle the
open operation
on the device file.
It receives the driver table and it is expected to return nothing.
release: callback function to handle the
release operation
on the device file.
It receives the driver table and it is expected to return nothing.
mode: an integer specifying the device
file mode.
If an operation callback is not defined, the device returns -ENXIO to VFS on its access.
device.stop(dev), dev:stop()
device.stop() removes a device driver specified by the dev object from the system.
The linux library provides support for some Linux kernel facilities.
linux.random() mimics the behavior of
math.random,
but binding ‘s
get_random_u32()
and
get_random_u64()
APIs.
When called without arguments,
produces an integer with all bits (pseudo)random.
When called with two integers m and n,
linux.random() returns a pseudo-random integer with uniform distribution in the range [m, n].
The call math.random(n), for a positive n, is equivalent to math.random(1, n).
linux.stat is a table that exports
integer flags to Lua.
“IRWXUGO”: permission to read, write and execute for user, group and other.
“IRUGO”: permission only to read for user, group and other.
“IWUGO”: permission only to write for user, group and other.
“IXUGO”: permission only to execute for user, group and other.
linux.schedule([timeout [, state]])
linux.schedule() sets the current task state and makes the it sleep until timeout milliseconds have elapsed.
If timeout is omitted, it uses MAX_SCHEDULE_TIMEOUT.
If state is omitted, it uses task.INTERRUPTIBLE.
linux.task is a table that exports
task state
flags to Lua.
“RUNNING”: task is executing on a CPU or waiting to be executed.
“INTERRUPTIBLE”: task is waiting for a signal or a resource (sleeping).
“UNINTERRUPTIBLE”: behaves like “INTERRUPTIBLE” with the exception that signal will not wake up the task.
“KILLABLE”: behaves like “UNINTERRUPTIBLE” with the exception that fatal signals will wake up the task.
“IDLE”: behaves like “UNINTERRUPTIBLE” with the exception that it avoids the loadavg accounting.
linux.errno is a table that exports
flags to Lua.
“PERM”: Operation not permitted.
“NOENT”: No such file or directory.
“SRCH”: No such process.
“INTR”: Interrupted system call.
“IO”: I/O error.
“NXIO”:No such device or address.
“2BIG”:, Argument list too long.
“NOEXEC”: Exec format error.
“BADF”: Bad file number.
“CHILD”: No child processes.
“AGAIN”: Try again.
“NOMEM”: Out of memory.
“ACCES”: Permission denied.
“FAULT”: Bad address.
“NOTBLK”: Block device required.
“BUSY”: Device or resource busy.
“EXIST”: File exists.
“XDEV”: Cross-device link.
“NODEV”: No such device.
“NOTDIR”: Not a directory.
“ISDIR”: Is a directory.
“INVAL”: Invalid argument.
“NFILE”: File table overflow.
“MFILE”: Too many open files.
“NOTTY”: Not a typewriter.
“TXTBSY”: Text file busy.
“FBIG”: File too large.
“NOSPC”: No space left on device.
“SPIPE”: Illegal seek.
“ROFS”: Read-only file system.
“MLINK”: Too many links.
“PIPE”: Broken pipe.
“DOM”: Math argument out of domain of func.
“RANGE”: Math result not representable.
The notifier library provides support for the kernel
notifier chains.
notifier.keyboard(callback)
notifier.keyboard() returns a new keyboard notifier object and installs it in the system.
The callback function is called whenever a console keyboard event happens
(e.g., a key has been pressed or released).
This callback receives the following arguments:
event: the available events are defined by the
notifier.kbd table.
down: true, if the key is pressed; false, if it is released.
shift: true, if the shift key is held; false, otherwise.
key: keycode or keysym depending on event.
The callback function might return the values defined by the
notifier.notify table.
notifier.kbd is a table that exports
KBD
flags to Lua.
“KEYCODE”: keyboard keycode, called before any other.
“UNBOUND_KEYCODE”: keyboard keycode which is not bound to any other.
“UNICODE”: keyboard unicode.
“KEYSYM”: keyboard keysym.
“POST_KEYSYM”: called after keyboard keysym interpretation.
notifier.netdevice(callback)
notifier.netdevice() returns a new netdevice notifier object and installs it in the system.
The callback function is called whenever a console netdevice event happens
(e.g., a network interface has been connected or disconnected).
This callback receives the following arguments:
event: the available events are defined by the
notifier.netdev table.
name: the device name.
The callback function might return the values defined by the
notifier.notify table.
notifier.netdev is a table that exports
NETDEV
flags to Lua.
notifier.notify is a table that exports
NOTIFY
flags to Lua.
“DONE”: don’t care.
“OK”: suits me.
“BAD”: bad/veto action.
“STOP”: clean way to return from the notifier and stop further calls.
notifier.delete(notfr), notfr:delete()
notifier.delete() removes a notifier specified by the notfr object from the system.
The socket library provides support for the kernel
networking handling.
This library was inspired by
Chengzhi Tan’s
GSoC project.
socket.new(family, type, protocol)
socket.new() creates a new socket object.
This function receives the following arguments:
family: the available address families are defined by the
socket.af table.
sock: the available types are present on the
socket.sock table.
protocol: the available protocols are defined by the
socket.ipproto table.
socket.af is a table that exports
address families (AF)
to Lua.
“UNSPEC”: Unspecified.
“UNIX”: Unix domain sockets.
“LOCAL”: POSIX name for AF_UNIX.
“INET”: Internet IP Protocol.
“AX25”: Amateur Radio AX.25.
“IPX”: Novell IPX.
“APPLETALK”: AppleTalk DDP.
“NETROM”: Amateur Radio NET/ROM.
“BRIDGE”: Multiprotocol bridge.
“ATMPVC”: ATM PVCs.
“X25”: Reserved for X.25 project.
“INET6”: IP version 6.
“ROSE”: Amateur Radio X.25 PLP.
“DEC”: Reserved for DECnet project.
“NETBEUI”: Reserved for 802.2LLC project.
“SECURITY”: Security callback pseudo AF.
“KEY”: PF_KEY key management API.
“NETLINK”: Netlink.
“ROUTE”: Alias to emulate 4.4BSD.
“PACKET”: Packet family.
“ASH”: Ash.
“ECONET”: Acorn Econet.
“ATMSVC”: ATM SVCs.
“RDS”: RDS sockets.
“SNA”: Linux SNA Project (nutters!).
“IRDA”: IRDA sockets.
“PPPOX”: PPPoX sockets.
“WANPIPE”: Wanpipe API Sockets.
“LLC”: Linux LLC.
“IB”: Native InfiniBand address.
“MPLS”: MPLS.
“CAN”: Controller Area Network.
“TIPC”: TIPC sockets.
“BLUETOOTH”: Bluetooth sockets.
“IUCV”: IUCV sockets.
“RXRPC”: RxRPC sockets.
“ISDN”: mISDN sockets.
“PHONET”: Phonet sockets.
“IEEE802154”: IEEE802154 sockets.
“CAIF”: CAIF sockets.
“ALG”: Algorithm sockets.
“NFC”: NFC sockets.
“VSOCK”: vSockets.
“KCM”: Kernel Connection Multiplexor.
“QIPCRTR”: Qualcomm IPC Router.
“SMC”: reserve number for PF_SMC protocol family that reuses AF_INET address family.
“XDP”: XDP sockets.
“MCTP”: Management component transport protocol.
“MAX”: Maximum.
socket.sock is a table that exports socket
types (SOCK):
“STREAM”: stream (connection) socket.
“DGRAM”: datagram (conn.less) socket.
“RAW”: raw socket.
“RDM”: reliably-delivered message.
“SEQPACKET”: sequential packet socket.
“DCCP”: Datagram Congestion Control Protocol socket.
“PACKET”: linux specific way of getting packets at the dev level.
and flags (SOCK):
“CLOEXEC”: n/a.
“NONBLOCK”: n/a.
socket.ipproto is a table that exports
IP protocols (IPPROTO)
to Lua.
“IP”: Dummy protocol for TCP.
“ICMP”: Internet Control Message Protocol.
“IGMP”: Internet Group Management Protocol.
“IPIP”: IPIP tunnels (older KA9Q tunnels use 94).
“TCP”: Transmission Control Protocol.
“EGP”: Exterior Gateway Protocol.
“PUP”: PUP protocol.
“UDP”: User Datagram Protocol.
“IDP”: XNS IDP protocol.
“TP”: SO Transport Protocol Class 4.
“DCCP”: Datagram Congestion Control Protocol.
“IPV6”: IPv6-in-IPv4 tunnelling.
“RSVP”: RSVP Protocol.
“GRE”: Cisco GRE tunnels (rfc 1701,1702).
“ESP”: Encapsulation Security Payload protocol.
“AH”: Authentication Header protocol.
“MTP”: Multicast Transport Protocol.
“BEETPH”: IP option pseudo header for BEET.
“ENCAP”: Encapsulation Header.
“PIM”: Protocol Independent Multicast.
“COMP”: Compression Header Protocol.
“SCTP”: Stream Control Transport Protocol.
“UDPLITE”: UDP-Lite (RFC 3828).
“MPLS”: MPLS in IP (RFC 4023).
“ETHERNET”: Ethernet-within-IPv6 Encapsulation.
“RAW”: Raw IP packets.
“MPTCP”: Multipath TCP connection.
socket:close(sock), sock:close()
socket.close() removes sock object from the system.
socket.send(sock, message, [addr [, port]]), sock:send(message, [addr [, port]])
socket.send() sends a string message through the socket sock.
If the sock address family is af.INET, then it expects the following arguments:
addr: integer describing the destination IPv4 address.
port: integer describing the destination IPv4 port.
Otherwise:
addr: packed string describing the destination address.
socket.receive(sock, length, [flags [, from]]), sock:receive(length, [flags [, from]])
socket.receive() receives a string with up to length bytes through the socket sock.
The available message flags are defined by the
socket.msg table.
If from is true, it returns the received message followed by the peer’s address.
Otherwise, it returns only the received message.
socket.msg is a table that exports
message flags
to Lua.
“OOB”: n/a.
“PEEK”: n/a.
“DONTROUTE”: n/a.
“TRYHARD”: Synonym for “DONTROUTE” for DECnet.
“CTRUNC”: n/a.
“PROBE”: Do not send. Only probe path f.e. for MTU.
“TRUNC”: n/a.
“DONTWAIT”: Nonblocking io.
“EOR”: End of record.
“WAITALL”: Wait for a full request.
“FIN”: n/a.
“SYN”: n/a.
“CONFIRM”: Confirm path validity.
“RST”: n/a.
“ERRQUEUE”: Fetch message from error queue.
“NOSIGNAL”: Do not generate SIGPIPE.
“MORE”: Sender will send more.
“WAITFORONE”: recvmmsg(): block until 1+ packets avail.
“SENDPAGE_NOPOLICY”: sendpage() internal: do no apply policy.
“SENDPAGE_NOTLAST”: sendpage() internal: not the last page.
“BATCH”: sendmmsg(): more messages coming.
“EOF”: n/a.
“NO_SHARED_FRAGS”: sendpage() internal: page frags are not shared.
“SENDPAGE_DECRYPTED”: sendpage() internal: page may carry plain text and require encryption.
“ZEROCOPY”: Use user data in kernel path.
“FASTOPEN”: Send data in TCP SYN.
“CMSG_CLOEXEC”: Set close_on_exec for file descriptor received through SCM_RIGHTS.
socket.bind(sock, addr [, port]), sock:bind(addr [, port])
socket.bind() binds the socket sock to a given address.
If the sock address family is af.INET, then it expects the following arguments:
addr: integer describing host IPv4 address.
port: integer describing host IPv4 port.
Otherwise:
addr: packed string describing host address.
socket.listen(sock [, backlog]), sock:listen([backlog])
socket.listen() moves the socket sock to listening state.
backlog: pending connections queue size.
If omitted, it uses
SOMAXCONN
as default.
socket.accept(sock [, flags]), sock:accept([flags])
socket.accept() accepts a connection on socket sock.
It returns a new socket object.
The available flags are present on the
socket.sock table.
socket.connect(sock, addr [, port] [, flags]), sock:connect(addr [, port] [, flags])
socket.connect() connects the socket sock to the address addr.
If the sock address family is af.INET, then it expects the following arguments:
addr: integer describing the destination IPv4 address.
port: integer describing the destination IPv4 port.
Otherwise:
addr: packed string describing the destination address.
The available flags are present on the
socket.sock table.
For datagram sockets, addr is the address to which datagrams are sent
by default, and the only address from which datagrams are received.
For stream sockets, attempts to connect to addr.
socket.getsockname(sock), sock:getsockname()
socket.getsockname() get the address which the socket sock is bound.
If the sock address family is af.INET, then it returns the following:
addr: integer describing the bounded IPv4 address.
port: integer describing the bounded IPv4 port.
Otherwise:
addr: packed string describing the bounded address.
socket.getpeername(sock), sock:getpeername()
socket.getpeername() get the address which the socket sock is connected.
If the sock address family is af.INET, then it returns the following:
addr: integer describing the peer’s IPv4 address.
port: integer describing the peer’s IPv4 port.
Otherwise:
addr: packed string describing the peer’s address.
The socket.inet library provides support for high-level IPv4 sockets.
inet.tcp() creates a new socket using
af.INET address family,
sock.STREAM type
and
ipproto.TCP protocol.
It overrides socket methods to use addresses as numbers-and-dots notation
(e.g., “127.0.0.1”), instead of integers.
inet.udp() creates a new socket using
af.INET address family,
sock.DGRAM type
and
ipproto.UDP protocol.
It overrides socket methods to use addresses as numbers-and-dots notation
(e.g., “127.0.0.1”), instead of integers.
udp:receivefrom(length [, flags])
udp:receivefrom() is just an alias to sock:receive(length, flags, true).
The rcu library provides support for the kernel
Read-copy update (RCU)
synchronization mechanism.
This library was inspired by
Caio Messias’
GSoC project.
rcu.table() creates a new rcu.table object
which binds the kernel generic hash table.
This function receives as argument the number of buckets rounded up to the next power of 2.
The default size is 1024.
Key must be a string and value must be a Lunatik object or nil.
The thread library provides support for the
kernel thread primitives.
thread.run(runtime, name)
thread.run() creates a new thread object and wakes it up.
This function receives the following arguments:
runtime: the
runtime environment
for running a task in the created kernel thread.
The task must be specified by returning a function on the script loaded
in the runtime environment.
name: string representing the name for the thread (e.g., as shown on ps).
thread.shouldstop() returns true if
thread.stop()
was called; otherwise, it returns false.
thread.current() returns a thread object representing the current task.
thread.stop(thrd), thrd:stop()
thread.stop() sets
thread.shouldstop()
on the thread thrd to return true, wakes thrd, and waits for it to exit.
thread.task(thread), thrd:task()
thread.task() returns a table containing the task information of this thread
(e.g., “cpu”, “command”, “pid” and “tgid”).
The fib library provides support for the
kernel Forwarding Information Base.
fib.newrule(table, priority)
fib.newrule() binds the kernel
fib_nl_newrule
API;
it creates a new FIB rule that matches the specified routing table
with the specified priorioty.
This function is similar to the user-space command
ip rule add
provided by iproute2.
fib.delrule(table, priority)
fib.delrule() binds the kernel
fib_nl_delrule
API;
it removes a FIB rule that matches the specified routing table
with the specified priorioty.
This function is similar to the user-space command
ip rule del
provided by iproute2.
The data library provides support for binding the system memory to Lua.
data.new() creates a new data object which allocates size bytes.
data.getnumber(d, offset), d:getnumber(offset)
data.getnumber() extracts a lua_Integer
from the memory referenced by a data object and a byte offset,
starting from zero.
data.setnumber(d, offset, number), d:setnumber(offset, number)
data.setnumber() insert a lua_Integer
number into the memory referenced by a data object and a byte offset,
starting from zero.
data.getbyte(d, offset), d:getbyte(offset)
data.getbyte() extracts a byte
from the memory referenced by a data object and a byte offset,
starting from zero.
data.setbyte(d, offset, byte), d:setbyte(offset, byte)
data.setbyte() insert a byte
into the memory referenced by a data object and a byte offset,
starting from zero.
data.getstring(d, offset, length), d:getstring(offset, length)
data.getstring() extracts a string with length bytes
from the memory referenced by a data object and a byte offset,
starting from zero.
data.setstring(d, offset, s), d:setstring(offset, s)
data.setstring() insert the string s
into the memory referenced by a data object and a byte offset,
starting from zero.
The probe library provides support for
kernel probes.
probe.new(symbol|address, handlers)
probe.new() returns a new probe object for monitoring a kernel symbol (string) or address (light userdata)
and installs its handlers in the system.
The handler must be defined as a table containing the following field:
pre: function to be called before the probed instruction.
It receives the symbol or address,
followed by a closure that may be called to
show the CPU registers and stack
in the system log.
post: function to be called after the probed instruction.
It receives the symbol or address,
followed by a closure that may be called to
show the CPU registers and stack
in the system log.
probe.stop() removes the probe handlers from the system.
probe.enable(p, bool), p:enable(bool)
probe.enable() enables or disables the probe handlers, accordingly to bool.
The syscall library provides support for system call addresses and numbers.
syscall.address() returns the system call address (light userdata) referenced by the given number.
syscall.number() returns the system call number referenced by the given name.
The syscall.table library provides support for translating system call names to addresses (light userdata).
spyglass
is a kernel script that implements a keylogger inspired by the
spy kernel module.
This kernel script logs the keysym of the pressed keys in a device (/dev/spyglass).
If the keysym is a printable character, spyglass logs the keysym itself;
otherwise, it logs a mnemonic of the ASCII code, (e.g., stands for 127).
/dev/spyglass” # disable the key logging
sudo sh -c “echo ‘enable=true’> /dev/spyglass” # enable the key logging
sudo sh -c “echo ‘net=127.0.0.1:1337’> /dev/spyglass” # enable network support
nc -lu 127.0.0.1 1337 & # listen to UDP 127.0.0.1:1337
sudo tail -f /dev/spyglass # sends the key log through the network”>sudo make examples_install # installs examples
sudo lunatik run examples/spyglass # runs spyglass
sudo tail -f /dev/spyglass # prints the key log
sudo sh -c “echo ‘enable=false’> /dev/spyglass” # disable the key logging
sudo sh -c “echo ‘enable=true’> /dev/spyglass” # enable the key logging
sudo sh -c “echo ‘net=127.0.0.1:1337’> /dev/spyglass” # enable network support
nc -lu 127.0.0.1 1337 & # listen to UDP 127.0.0.1:1337
sudo tail -f /dev/spyglass # sends the key log through the network
keylocker
is a kernel script that implements
Konami Code
for locking and unlocking the console keyboard.
When the user types ↑ ↑ ↓ ↓ ← → ← → LCTRL LALT,
the keyboard will be locked; that is, the system will stop processing any key pressed
until the user types the same key sequence again.
sudo make examples_install # installs examples
sudo lunatik run examples/keylocker # runs keylocker
# locks keyboard
# unlocks keyboard
tap
is a kernel script that implements a sniffer using AF_PACKET socket.
It prints destination and source MAC addresses followed by Ethernet type and the frame size.
sudo make examples_install # installs examples
sudo lunatik run examples/tap # runs tap
cat /dev/tap
shared
is a kernel script that implements an in-memory key-value store using
rcu,
data,
socket and
thread.
sudo make examples_install # installs examples
sudo lunatik spawn examples/shared # spawns shared
nc 127.0.0.1 90 # connects to shared
foo=bar # assigns “bar” to foo
foo # retrieves foo
bar
^C # finishes the connection
echod
is an echo server implemented as kernel scripts.
sudo make examples_install # installs examples
sudo lunatik spawn examples/echod/daemon # runs echod
nc 127.0.0.1 1337
hello kernel!
hello kernel!
systrack
is a kernel script that implements a device driver to monitor system calls.
It prints the amount of times each system call
was called since the driver has been installed.
sudo make examples_install # installs examples
sudo lunatik run examples/systrack # runs systracker
cat /dev/systrack
writev: 0
close: 1927
write: 1085
openat: 2036
read: 4131
readv: 0
Scripting the Linux Routing Table with Lua
Lua no Núcleo (Portuguese)
Linux Network Scripting with Lua
Scriptables Operating Systems with Lua
>>> Read full article>>>
Copyright for syndicated content belongs to the linked Source : Hacker News – https://github.com/luainkernel/lunatik