Skip to content

Commit

Permalink
Support reading and writing signed values through existing accessor m…
Browse files Browse the repository at this point in the history
…ethods
  • Loading branch information
elicn committed Jul 2, 2024
1 parent 1c90e4a commit 055d474
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 30 deletions.
38 changes: 23 additions & 15 deletions qiling/os/freebsd/syscall.py
Original file line number Diff line number Diff line change
@@ -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_ = [
Expand Down Expand Up @@ -59,37 +61,43 @@ 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":
out_vecs = [CTL_HW, HW_PAGESIZE]
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

24 changes: 18 additions & 6 deletions qiling/os/memory.py
Original file line number Diff line number Diff line change
Expand Up @@ -313,26 +313,32 @@ 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
"""

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}")
Expand All @@ -349,25 +355,31 @@ 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.
Args:
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}")
Expand Down
25 changes: 17 additions & 8 deletions qiling/os/posix/syscall/resource.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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
Expand All @@ -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
Expand Down
2 changes: 1 addition & 1 deletion qiling/os/posix/syscall/socket.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 055d474

Please sign in to comment.