diff --git a/qiling/os/freebsd/syscall.py b/qiling/os/freebsd/syscall.py index 77691936a..bb1c563bd 100644 --- a/qiling/os/freebsd/syscall.py +++ b/qiling/os/freebsd/syscall.py @@ -1,16 +1,18 @@ #!/usr/bin/env python3 -# +# # Cross Platform and Multi Architecture Advanced Binary Emulation Framework # +import ctypes +from datetime import datetime +from math import floor +from qiling import Qiling from qiling.arch.x86_const import * from qiling.os.freebsd.const import * +from qiling.os.posix.const import ENOENT from qiling.os.posix.syscall.unistd import ql_syscall_getcwd from qiling.os.posix.syscall.stat import ql_syscall_newfstatat -from datetime import datetime -from math import floor -import ctypes class timespec(ctypes.Structure): _fields_ = [ @@ -59,20 +61,21 @@ def ql_syscall___getcwd(ql, path_buff, path_buffsize, *args, **kw): def ql_syscall_fstatat(ql, newfstatat_dirfd, newfstatat_path, newfstatat_buf_ptr, newfstatat_flag, *args, **kw): return ql_syscall_newfstatat(ql, newfstatat_dirfd, newfstatat_path, newfstatat_buf_ptr, newfstatat_flag, *args, **kw) -def ql_syscall___sysctl(ql, name, namelen, old, oldlenp, new_arg, newlen): +def ql_syscall___sysctl(ql: Qiling, name: int, namelen: int, old: int, oldlenp: int, new_arg: int, newlen: int): ql.log.debug("__sysctl(name: 0x%x, namelen: 0x%x, old: 0x%x, oldlenp: 0x%x, new: 0x%x, newlen: 0x%x)" % ( name, namelen, old, oldlenp, new_arg, newlen )) - vecs = [] - for i in range(namelen): - vecs.append(ql.unpack32s(ql.mem.read(name + i*4, 4))) + + vecs = [ql.mem.read_ptr(name + i * 4, 4, signed=True) for i in range(namelen)] + ql.log.debug(f"__sysctl vectors: {vecs}") if vecs[0] == CTL_SYSCTL: if vecs[1] == CTL_SYSCTL_NAME2OID: # Write oid to old and oldlenp - sysctl_name = ql.mem.string(new_arg) + sysctl_name = ql.os.utils.read_cstring(new_arg) out_vecs = [] out_len = 0 + # TODO: Implement oid<-->name as many as possible from FreeBSD source. # Search SYSCTL_ADD_NODE etc. if sysctl_name == "hw.pagesizes": @@ -80,16 +83,21 @@ def ql_syscall___sysctl(ql, name, namelen, old, oldlenp, new_arg, newlen): out_len = 2 else: ql.log.warning("Unknown oid name!") + for i, v in enumerate(out_vecs): - ql.mem.write(old + 4*i, ql.pack32s(v)) - ql.mem.write(oldlenp, ql.pack32s(out_len)) - return 2 # ENOENT + ql.mem.write_ptr(old + i * 4, v, 32, signed=True) + + ql.mem.write_ptr(oldlenp, out_len, 32, signed=True) + + return -ENOENT + if vecs[0] == CTL_KERN: if vecs[1] == KERN_OSRELDATE: - if old == 0 or oldlenp == 0: + if old == 0 or oldlenp == 0: return -1 + # Ignore oldlenp check. - ql.mem.write(old, ql.pack32s(FREEBSD_OSRELDATE)) + ql.mem.write_ptr(old, FREEBSD_OSRELDATE, 32, signed=True) + return 0 return 0 - \ No newline at end of file diff --git a/qiling/os/memory.py b/qiling/os/memory.py index b5be6d91d..ec643c0e4 100644 --- a/qiling/os/memory.py +++ b/qiling/os/memory.py @@ -313,13 +313,14 @@ def read(self, addr: int, size: int) -> bytearray: return self.ql.uc.mem_read(addr, size) - def read_ptr(self, addr: int, size: int = 0) -> int: + def read_ptr(self, addr: int, size: int = 0, *, signed = False) -> int: """Read an integer value from a memory address. Bytes read will be unpacked using emulated architecture properties. Args: addr: memory address to read size: pointer size (in bytes): either 1, 2, 4, 8, or 0 for arch native size + signed: interpret value as a signed integer (default: False) Returns: integer value stored at the specified memory address """ @@ -327,12 +328,17 @@ def read_ptr(self, addr: int, size: int = 0) -> int: if not size: size = self.ql.arch.pointersize - __unpack = { + __unpack = ({ + 1: self.ql.unpack8s, + 2: self.ql.unpack16s, + 4: self.ql.unpack32s, + 8: self.ql.unpack64s + } if signed else { 1: self.ql.unpack8, 2: self.ql.unpack16, 4: self.ql.unpack32, 8: self.ql.unpack64 - }.get(size) + }).get(size) if __unpack is None: raise QlErrorStructConversion(f"Unsupported pointer size: {size}") @@ -349,7 +355,7 @@ def write(self, addr: int, data: bytes) -> None: self.ql.uc.mem_write(addr, data) - def write_ptr(self, addr: int, value: int, size: int = 0) -> None: + def write_ptr(self, addr: int, value: int, size: int = 0, *, signed = False) -> None: """Write an integer value to a memory address. Bytes written will be packed using emulated architecture properties. @@ -357,17 +363,23 @@ def write_ptr(self, addr: int, value: int, size: int = 0) -> None: addr: target memory address value: integer value to write size: pointer size (in bytes): either 1, 2, 4, 8, or 0 for arch native size + signed: interpret value as a signed integer (default: False) """ if not size: size = self.ql.arch.pointersize - __pack = { + __pack = ({ + 1: self.ql.pack8s, + 2: self.ql.pack16s, + 4: self.ql.pack32s, + 8: self.ql.pack64s + } if signed else { 1: self.ql.pack8, 2: self.ql.pack16, 4: self.ql.pack32, 8: self.ql.pack64 - }.get(size) + }).get(size) if __pack is None: raise QlErrorStructConversion(f"Unsupported pointer size: {size}") diff --git a/qiling/os/posix/syscall/resource.py b/qiling/os/posix/syscall/resource.py index 6d26cdbaf..f0152c043 100644 --- a/qiling/os/posix/syscall/resource.py +++ b/qiling/os/posix/syscall/resource.py @@ -20,16 +20,17 @@ def setrlimit(self, resource, rlim): def __getrlimit_common(ql: Qiling, res: int, rlim: int) -> int: RLIMIT_STACK = 3 + if res == RLIMIT_STACK: - if ql.arch.bits == 64: - stack_size = int(ql.os.profile.get("OS64", "stack_size"), 16) - elif ql.arch.bits == 32: - stack_size = int(ql.os.profile.get("OS32", "stack_size"), 16) + stack_size = ql.os.profile.getint(f"OS{ql.arch.bits}", "stack_size") rlimit = (stack_size, -1) + else: rlimit = resource.getrlimit(res) - - ql.mem.write(rlim, ql.packs(rlimit[0]) + ql.packs(rlimit[1])) + + # FIXME: not sure whether these should be pointersize values or always 32 bits + ql.mem.write_ptr(rlim + 0 * 4, rlimit[0], 4, signed=True) + ql.mem.write_ptr(rlim + 1 * 4, rlimit[1], 4, signed=True) return 0 @@ -41,7 +42,11 @@ def ql_syscall_getrlimit(ql: Qiling, res: int, rlim: int): def ql_syscall_setrlimit(ql: Qiling, res: int, rlim: int): # maybe we can nop the setrlimit - tmp_rlim = (ql.unpack32s(ql.mem.read(rlim, 4)), ql.unpack32s(ql.mem.read(rlim + 4, 4))) + tmp_rlim = ( + ql.mem.read_ptr(rlim + 0 * 4, 4, signed=True), + ql.mem.read_ptr(rlim + 1 * 4, 4, signed=True) + ) + resource.setrlimit(res, tmp_rlim) return 0 @@ -51,7 +56,11 @@ def ql_syscall_prlimit64(ql: Qiling, pid: int, res: int, new_limit: int, old_lim if pid == 0 and new_limit == 0: try: rlim = resource.getrlimit(res) - ql.mem.write(old_limit, ql.packs(rlim[0]) + ql.packs(rlim[1])) + + # FIXME: not sure whether these should be pointersize values or always 32 bits + ql.mem.write_ptr(old_limit + 0 * 4, rlim[0], 4, signed=True) + ql.mem.write_ptr(old_limit + 1 * 4, rlim[1], 4, signed=True) + return 0 except: return -1 diff --git a/qiling/os/posix/syscall/socket.py b/qiling/os/posix/syscall/socket.py index 74a16c20e..12b579ca7 100644 --- a/qiling/os/posix/syscall/socket.py +++ b/qiling/os/posix/syscall/socket.py @@ -325,7 +325,7 @@ def ql_syscall_getsockopt(ql: Qiling, sockfd: int, level: int, optname: int, opt ql.log.debug(f'Converted emulated socket option {vsock_opt} to host socket option {hsock_opt}') - optlen = min(ql.unpack32s(ql.mem.read(optlen_addr, 4)), 1024) + optlen = min(ql.mem.read_ptr(optlen_addr, 4, signed=True), 1024) if optlen < 0: return -EINVAL