import struct import ctypes from .kernel import Kernel, Uint, Int import logging logger = logging.getLogger(__name__) class StructNewUtsname(ctypes.LittleEndianStructure): """ // See: https://elixir.bootlin.com/linux/v2.6.35/source/include/linux/utsname.h#L24 struct new_utsname { char sysname[__NEW_UTS_LEN + 1]; char nodename[__NEW_UTS_LEN + 1]; char release[__NEW_UTS_LEN + 1]; char version[__NEW_UTS_LEN + 1]; char machine[__NEW_UTS_LEN + 1]; char domainname[__NEW_UTS_LEN + 1]; }; """ __NEW_UTS_LEN = 64 + 1 _pack_ = 1 _fields_ = [ ('sysname', ctypes.c_char * __NEW_UTS_LEN), ('nodename', ctypes.c_char * __NEW_UTS_LEN), ('release', ctypes.c_char * __NEW_UTS_LEN), ('version', ctypes.c_char * __NEW_UTS_LEN), ('machine', ctypes.c_char * __NEW_UTS_LEN), ('domainname', ctypes.c_char * __NEW_UTS_LEN), ] @Kernel.register(0x109) def sys_clock_gettime(kernel: Kernel, clk_id: Uint, tp_addr: Uint): """ int clock_gettime(clockid_t clk_id, struct timespec *tp); The functions clock_gettime() and clock_settime() retrieve and set the time of the specified clock clk_id. The res and tp arguments are timespec structures, as specified in <time.h>: struct timespec { time_t tv_sec; /* seconds */ long tv_nsec; /* nanoseconds */ }; """ struct_timespec = struct.Struct('<II') import time time_nanoseconds = int(time.time() * 1_000_000_000) # Python 3.6 compatibility sec, nsec = time_nanoseconds // 1_000_000_000, time_nanoseconds % 1_000_000_000 kernel.cpu.mem.set_bytes(tp_addr, struct_timespec.size, struct_timespec.pack(sec, nsec)) return 0 @Kernel.register(0x36) def sys_ioctl(kernel: Kernel, fd: Int, request: Uint, data_addr: Uint): """ Arguments: (int fd, unsigned long request, ...) """ # SOURCE: http://man7.org/linux/man-pages/man2/ioctl_list.2.html # < include / asm - i386 / termios.h > # # 0x00005401 TCGETS struct termios * # 0x00005402 TCSETS const struct termios * # 0x00005403 TCSETSW const struct termios * # 0x00005404 TCSETSF const struct termios * # 0x00005405 TCGETA struct termio * # 0x00005406 TCSETA const struct termio * # 0x00005407 TCSETAW const struct termio * # 0x00005408 TCSETAF const struct termio * # 0x00005409 TCSBRK int # 0x0000540A TCXONC int # 0x0000540B TCFLSH int # 0x0000540C TIOCEXCL void # 0x0000540D TIOCNXCL void # 0x0000540E TIOCSCTTY int # 0x0000540F TIOCGPGRP pid_t * # 0x00005410 TIOCSPGRP const pid_t * # 0x00005411 TIOCOUTQ int * # 0x00005412 TIOCSTI const char * # 0x00005413 TIOCGWINSZ struct winsize * # 0x00005414 TIOCSWINSZ const struct winsize * # 0x00005415 TIOCMGET int * # 0x00005416 TIOCMBIS const int * # 0x00005417 TIOCMBIC const int * # 0x00005418 TIOCMSET const int * # 0x00005419 TIOCGSOFTCAR int * # 0x0000541A TIOCSSOFTCAR const int * # 0x0000541B FIONREAD int * # 0x0000541B TIOCINQ int * # 0x0000541C TIOCLINUX const char * // MORE # 0x0000541D TIOCCONS void # 0x0000541E TIOCGSERIAL struct serial_struct * # 0x0000541F TIOCSSERIAL const struct serial_struct * # 0x00005420 TIOCPKT const int * # 0x00005421 FIONBIO const int * # 0x00005422 TIOCNOTTY void # 0x00005423 TIOCSETD const int * # 0x00005424 TIOCGETD int * # 0x00005425 TCSBRKP int # 0x00005426 TIOCTTYGSTRUCT struct tty_struct * # 0x00005450 FIONCLEX void # 0x00005451 FIOCLEX void # 0x00005452 FIOASYNC const int * # 0x00005453 TIOCSERCONFIG void # 0x00005454 TIOCSERGWILD int * # 0x00005455 TIOCSERSWILD const int * # 0x00005456 TIOCGLCKTRMIOS struct termios * # 0x00005457 TIOCSLCKTRMIOS const struct termios * # 0x00005458 TIOCSERGSTRUCT struct async_struct * # 0x00005459 TIOCSERGETLSR int * # 0x0000545A TIOCSERGETMULTI struct serial_multiport_struct * # 0x0000545B TIOCSERSETMULTI const struct serial_multiport_struct * import enum directions = enum.Flag('directions', '_IOC_NONE _IOC_READ _IOC_WRITE', start=0) # TAKEN FROM: https://elixir.bootlin.com/linux/v5.0.8/source/include/uapi/asm-generic/ioctl.h _IOC_NRBITS = 8 _IOC_TYPEBITS = 8 _IOC_SIZEBITS = 14 _IOC_DIRBITS = 2 _IOC_NRMASK = ((1 << _IOC_NRBITS)-1) _IOC_TYPEMASK = ((1 << _IOC_TYPEBITS)-1) _IOC_SIZEMASK = ((1 << _IOC_SIZEBITS)-1) _IOC_DIRMASK = ((1 << _IOC_DIRBITS)-1) _IOC_NRSHIFT = 0 _IOC_TYPESHIFT = _IOC_NRSHIFT + _IOC_NRBITS _IOC_SIZESHIFT = _IOC_TYPESHIFT + _IOC_TYPEBITS _IOC_DIRSHIFT = _IOC_SIZESHIFT + _IOC_SIZEBITS _IOC_NONE = 0 _IOC_WRITE = 1 _IOC_READ = 2 _IOC_DIR = lambda nr: (((nr) >> _IOC_DIRSHIFT) & _IOC_DIRMASK) _IOC_TYPE = lambda nr: (((nr) >> _IOC_TYPESHIFT) & _IOC_TYPEMASK) _IOC_NR = lambda nr: (((nr) >> _IOC_NRSHIFT) & _IOC_NRMASK) _IOC_SIZE = lambda nr: (((nr) >> _IOC_SIZESHIFT) & _IOC_SIZEMASK) IOC_IN = (_IOC_WRITE << _IOC_DIRSHIFT) IOC_OUT = (_IOC_READ << _IOC_DIRSHIFT) IOC_INOUT = ((_IOC_WRITE|_IOC_READ) << _IOC_DIRSHIFT) IOCSIZE_MASK = (_IOC_SIZEMASK << _IOC_SIZESHIFT) IOCSIZE_SHIFT = (_IOC_SIZESHIFT) request_type = bytes([_IOC_TYPE(request)]) request_number = _IOC_NR(request) request_direction = directions(_IOC_DIR(request)) request_size = _IOC_SIZE(request) logger.info(f'ioctl(fd={fd},request={request:09_x} (type={request_type}, number={request_number}, direction={request_direction}, size={request_size}))') if request_type == b'T': if request_number == 19 and request_direction == directions._IOC_NONE: try: kernel.cpu.descriptors[fd] except IndexError: return kernel.cpu.__return(-1) # TAKEN FROM: http://man7.org/linux/man-pages/man4/tty_ioctl.4.html # # struct winsize # { # unsigned short ws_row; # unsigned short ws_col; # unsigned short ws_xpixel; / *unused * / # unsigned short ws_ypixel; / *unused * / # }; struct_winsize = struct.Struct('<HHHH') kernel.cpu.mem.set_bytes(data_addr, struct_winsize.size, struct_winsize.pack(256, 256, 0, 0)) return 0 return -1 @Kernel.register(0x01) def sys_exit(kernel: Kernel, code: Int): # deallocate memory logger.info('sys_exit: deallocating memory...') oldbrk = kernel.cpu.mem.program_break kernel.sys_brk(kernel.cpu.code_segment_end) if oldbrk > kernel.cpu.mem.program_break: logger.info('sys_exit: %d bytes of memory deallocated', oldbrk - kernel.cpu.mem.program_break) else: logger.info('sys_exit: no memory to deallocate') logger.info('sys_exit: closing file descriptors...') closed = 0 for i, descr in enumerate(kernel.cpu.descriptors[3:], 3): if descr is not None: if not descr.closed: closed += 1 kernel.cpu.descriptors[i].close() kernel.cpu.descriptors[i] = None kernel.cpu.descriptors = list(filter(None, kernel.cpu.descriptors)) if closed > 0: logger.info('sys_exit: closed %d file descriptors', closed) else: logger.info('sys_exit: no file descriptors to be closed') code &= 0o0377 kernel.cpu.descriptors[2].write(f'[!] Process exited with code {code}\n') kernel.cpu.RETCODE = code kernel.cpu.running = False return code @Kernel.register(0xfc) def sys_exit_group(kernel: Kernel, code: Int): return sys_exit(kernel, code) @Kernel.register(0x7a) def sys_newuname(kernel: Kernel, buf_addr: Uint): """ int sys_newuname(struct new_utsname *buf); """ logger.debug(f'sys_newuname(struct new_utsname *buf=0x%08X)', buf_addr) uname = StructNewUtsname( sysname=b'PyVM_Linux', nodename=b'PyVM_Linux', release=b'3.14', version=b'3.14', machine=b'PyVM - Intel IA-32 on Python', domainname=b'PyVM_Linux.local' ) kernel.cpu.mem.set_bytes(buf_addr, ctypes.sizeof(StructNewUtsname), bytes(uname)) return 0 @Kernel.register(0xae) def sys_sigaction(kernel: Kernel, signum: Int, act_addr: Uint, oldact_addr: Uint): """ int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact); See: http://man7.org/linux/man-pages/man2/rt_sigaction.2.html """ return -1 @Kernel.register(0xaf) def sys_sigprocmask(kernel: Kernel, how: Int, set_addr: Uint, oset_addr: Uint, sigsetsize: Uint): """ int rt_sigprocmask(int how, const kernel_sigset_t *set, kernel_sigset_t *oldset, size_t sigsetsize); See: http://man7.org/linux/man-pages/man2/rt_sigprocmask.2.html """ return -1 @Kernel.register(0xba) def sys_sigaltstack(kernel: Kernel, ss_addr: Uint, old_ss_addr: Uint): """ int sigaltstack(const stack_t *ss, stack_t *old_ss); See: http://man7.org/linux/man-pages/man2/sigaltstack.2.html """ return -1