diff --git a/qiling/os/posix/filestruct.py b/qiling/os/posix/filestruct.py index f20fd38fd..0a1ef2a75 100644 --- a/qiling/os/posix/filestruct.py +++ b/qiling/os/posix/filestruct.py @@ -59,10 +59,8 @@ def fcntl(self, cmd, arg): pass def ioctl(self, cmd, arg): - try: - return fcntl.ioctl(self.__fd, cmd, arg) - except Exception: - pass + # might throw an OSError + return fcntl.ioctl(self.__fd, cmd, arg) def dup(self) -> 'ql_socket': new_s = self.__socket.dup() diff --git a/qiling/os/posix/posix.py b/qiling/os/posix/posix.py index 6f5041c9e..b67e409fe 100644 --- a/qiling/os/posix/posix.py +++ b/qiling/os/posix/posix.py @@ -99,12 +99,15 @@ def __init__(self, ql: Qiling): self.ql = ql self.sigaction_act = [0] * 256 - self.uid = self.euid = self.profile.getint("KERNEL","uid") - self.gid = self.egid = self.profile.getint("KERNEL","gid") - - self.pid = self.profile.getint("KERNEL", "pid") - self.ipv6 = self.profile.getboolean("NETWORK", "ipv6") - self.bindtolocalhost = self.profile.getboolean("NETWORK", "bindtolocalhost") + conf = self.profile['KERNEL'] + self.uid = self.euid = conf.getint('uid') + self.gid = self.egid = conf.getint('gid') + self.pid = conf.getint('pid') + + conf = self.profile['NETWORK'] + self.ipv6 = conf.getboolean('ipv6') + self.bindtolocalhost = conf.getboolean('bindtolocalhost') + self.ifrname_ovr = conf.get('ifrname_override') self.posix_syscall_hooks = { QL_INTERCEPT.CALL : {}, diff --git a/qiling/os/posix/syscall/ioctl.py b/qiling/os/posix/syscall/ioctl.py index 8ae78c5eb..4b1b988da 100644 --- a/qiling/os/posix/syscall/ioctl.py +++ b/qiling/os/posix/syscall/ioctl.py @@ -79,12 +79,24 @@ def ioctl(_fd: int, _cmd: int, _arg: int): return None if isinstance(ql.os.fd[fd], ql_socket) and cmd in (SIOCGIFADDR, SIOCGIFNETMASK): + data = ql.mem.read(arg, 64) + + ifr_name_override = ql.os.ifrname_ovr + + if ifr_name_override is not None: + # make sure the interface name does not exceed 16 characters. + # pad it with null bytes if shorter + ifr_name_override = ifr_name_override[:16].ljust(16, '\x00') + + data[0:16] = ifr_name_override.encode() + try: - data = ql.os.fd[fd].ioctl(cmd, bytes(ql.mem.read(arg, 64))) - ql.mem.write(arg, data) - except: + data = ql.os.fd[fd].ioctl(cmd, bytes(data)) + except OSError as ex: + ql.log.debug(f'the underlying ioctl raised an exception: {ex.strerror}') regreturn = -1 else: + ql.mem.write(arg, data) regreturn = 0 else: diff --git a/qiling/os/posix/syscall/socket.py b/qiling/os/posix/syscall/socket.py index 523be9807..db362b52c 100644 --- a/qiling/os/posix/syscall/socket.py +++ b/qiling/os/posix/syscall/socket.py @@ -8,6 +8,7 @@ import sys import socket import struct +from typing import Tuple from unicorn.unicorn import UcError @@ -67,15 +68,19 @@ def ql_bin_to_ip(ip): return ipaddress.ip_address(ip).compressed -def ql_unix_socket_path(ql: Qiling, sun_path: bytearray) -> str: +def ql_unix_socket_path(ql: Qiling, sun_path: bytearray) -> Tuple[str, str]: if sun_path[0] == 0: # Abstract Unix namespace # TODO: isolate from host namespace # TODO: Windows ql.log.warning(f'Beware! Usage of hosts abstract socket namespace {bytes(sun_path)}') - return sun_path.decode() - sun_path = sun_path.split(b'\0')[0].decode() - return ql.os.path.transform_to_real_path(sun_path) + + return (sun_path.decode(), '') + + vpath = sun_path.split(b'\0', maxsplit=1)[0].decode() + hpath = ql.os.path.virtual_to_host_path(vpath) + + return (hpath, vpath) def ql_syscall_socket(ql: Qiling, socket_domain, socket_type, socket_protocol): @@ -120,39 +125,44 @@ def ql_syscall_socket(ql: Qiling, socket_domain, socket_type, socket_protocol): return idx -def ql_syscall_connect(ql: Qiling, connect_sockfd, connect_addr, connect_addrlen): +def ql_syscall_connect(ql: Qiling, sockfd: int, addr: int, addrlen: int): AF_UNIX = 1 AF_INET = 2 - sock_addr = ql.mem.read(connect_addr, connect_addrlen) - family = ql.unpack16(sock_addr[ : 2]) - s = ql.os.fd[connect_sockfd] - ip = b'' - sun_path = b'' - port = 0 + + sock_addr = ql.mem.read(addr, addrlen) + family = ql.unpack16(sock_addr[:2]) + + sock = ql.os.fd[sockfd] + assert isinstance(sock, ql_socket) + + dest = None + + if sock.family != family: + return -1 + + if sock.family == AF_UNIX: + hpath, vpath = ql_unix_socket_path(ql, sock_addr[2:]) + + ql.log.debug(f'connecting to "{vpath}"') + dest = hpath + + elif sock.family == AF_INET: + port, host = struct.unpack(">HI", sock_addr[2:8]) + ip = ql_bin_to_ip(host) + + ql.log.debug(f'connecting to {ip}:{port}') + dest = (ip, port) + + else: + return -1 + try: - if s.family == family: - if s.family == AF_UNIX: - sun_path = ql_unix_socket_path(ql, sock_addr[2:]) - s.connect(sun_path) - regreturn = 0 - elif s.family == AF_INET: - port, host = struct.unpack(">HI", sock_addr[2:8]) - ip = ql_bin_to_ip(host) - s.connect((ip, port)) - regreturn = 0 - else: - regreturn = -1 - else: - regreturn = -1 + sock.connect(dest) except: regreturn = -1 - - if s.family == AF_UNIX: - ql.log.debug("connect(%s) = %d" % (sun_path, regreturn)) - elif s.family == AF_INET: - ql.log.debug("connect(%s, %d) = %d" % (ip, port, regreturn)) else: - ql.log.debug("connect() = %d" % (regreturn)) + regreturn = 0 + return regreturn @@ -314,9 +324,9 @@ def ql_syscall_bind(ql: Qiling, bind_fd, bind_addr, bind_addrlen): port = port + 8000 if sin_family == 1: - path = ql_unix_socket_path(ql, data[2:]) - ql.log.info(path) - ql.os.fd[bind_fd].bind(path) + hpath, vpath = ql_unix_socket_path(ql, data[2:]) + ql.log.debug(f'binding socket to "{vpath}"') + ql.os.fd[bind_fd].bind(hpath) # need a proper fix, for now ipv4 comes first elif sin_family == 2 and ql.os.bindtolocalhost == True: @@ -338,7 +348,7 @@ def ql_syscall_bind(ql: Qiling, bind_fd, bind_addr, bind_addrlen): regreturn = 0 if sin_family == 1: - ql.log.debug("bind(%d, %s, %d) = %d" % (bind_fd, path, bind_addrlen, regreturn)) + ql.log.debug("bind(%d, %s, %d) = %d" % (bind_fd, vpath, bind_addrlen, regreturn)) else: ql.log.debug("bind(%d,%s:%d,%d) = %d" % (bind_fd, host, port, bind_addrlen,regreturn)) ql.log.debug("syscall bind host: %s and port: %i sin_family: %i" % (ql_bin_to_ip(host), port, sin_family)) @@ -619,10 +629,10 @@ def ql_syscall_sendto(ql: Qiling, sockfd: int, sendto_buf, sendto_len, sendto_fl ql.log.debug("sendto() len is " + str(sendto_len)) if sin_family == 1: - path = ql_unix_socket_path(ql, data[2:]) + hpath, vpath = ql_unix_socket_path(ql, data[2:]) - ql.log.debug("sendto() path is " + str(path)) - regreturn = sock.sendto(bytes(tmp_buf), sendto_flags, path) + ql.log.debug("sendto() path is " + str(vpath)) + regreturn = sock.sendto(bytes(tmp_buf), sendto_flags, hpath) else: ql.log.debug("sendto() addr is %s:%d" % (host, port)) regreturn = sock.sendto(bytes(tmp_buf), sendto_flags, (host, port)) diff --git a/qiling/profiles/linux.ql b/qiling/profiles/linux.ql index a0a628ad3..2bfe4a096 100644 --- a/qiling/profiles/linux.ql +++ b/qiling/profiles/linux.ql @@ -32,6 +32,11 @@ current_path = / [NETWORK] +# override the ifr_name field in ifreq structures to match the hosts network interface name. +# that fixes certain socket ioctl errors where the requested interface name does not match the +# one on the host. comment out to avoid override +ifrname_override = eth0 + # To use IPv6 or not, to avoid binary double bind. ipv6 and ipv4 bind the same port at the same time bindtolocalhost = True # Bind to localhost