From 1838929daf55d881e5c3e0036cc53d64f4a82b67 Mon Sep 17 00:00:00 2001 From: elicn Date: Sun, 20 Nov 2022 14:23:57 +0200 Subject: [PATCH 1/9] Add support for big-endian BaseStruct and allow selection --- qiling/os/struct.py | 48 +++++++++++++++++++++++++++++++++++++-------- 1 file changed, 40 insertions(+), 8 deletions(-) diff --git a/qiling/os/struct.py b/qiling/os/struct.py index 31ed7dc7f..ad693f199 100644 --- a/qiling/os/struct.py +++ b/qiling/os/struct.py @@ -1,17 +1,31 @@ from __future__ import annotations import ctypes +import functools +import sys from contextlib import contextmanager -from functools import lru_cache from typing import TYPE_CHECKING, Any, Iterator, Type, TypeVar, Optional +from qiling.const import QL_ENDIAN + if TYPE_CHECKING: from qiling.os.memory import QlMemoryManager -class BaseStruct(ctypes.LittleEndianStructure): +# the cache decorator is needed here not only for performance purposes, but also to make sure +# the *same* class type is returned every time rather than creating another one with the same +# name and properties. +# +# TODO: work around the missing functools.cache decorator on Python versions earlier than 3.9 +cache = functools.cache if sys.version_info >= (3, 9) else functools.lru_cache(maxsize=2) + + +class BaseStruct(ctypes.Structure): """An abstract class for C structures. + + Refrain from subclassing it directly as it does not take the emulated architecture + properties into account. Subclass `BaseStructEL` or `BaseStructEB` instead. """ T = TypeVar('T', bound='BaseStruct') @@ -199,23 +213,41 @@ def memberat(cls, offset: int) -> Optional[str]: return next((fname for fname, *_ in cls._fields_ if cls.offsetof(fname) == offset), None) -# TODO: replace the lru_cache decorator with functools.cache when moving to Python 3.9 -@lru_cache(maxsize=2) -def get_aligned_struct(archbits: int) -> Type[BaseStruct]: - """Provide an aligned version of BaseStruct based on the underlying +class BaseStructEL(BaseStruct, ctypes.LittleEndianStructure): + """Little Endian structure base class. + """ + pass + + +class BaseStructEB(BaseStruct, ctypes.BigEndianStructure): + """Big Endian structure base class. + """ + pass + + +@cache +def get_aligned_struct(archbits: int, endian: QL_ENDIAN = QL_ENDIAN.EL) -> Type[BaseStruct]: + """Provide an aligned version of BaseStruct based on the emulated architecture properties. Args: archbits: required alignment in bits """ - class AlignedStruct(BaseStruct): + Struct = { + QL_ENDIAN.EL: BaseStructEL, + QL_ENDIAN.EB: BaseStructEB + }[endian] + + class AlignedStruct(Struct): _pack_ = archbits // 8 return AlignedStruct + +@cache def get_aligned_union(archbits: int): - """Provide an aligned union class based on the underlying architecture + """Provide an aligned union class based on the emulated architecture properties. This class does not inherit the special BaseStruct methods. FIXME: ctypes.Union endianess cannot be set arbitrarily, rather it depends From fdecf17f096a939249f8143dc44d219044a9b275 Mon Sep 17 00:00:00 2001 From: elicn Date: Sun, 20 Nov 2022 14:26:22 +0200 Subject: [PATCH 2/9] Adjust existing Windows structures --- qiling/os/windows/structs.py | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/qiling/os/windows/structs.py b/qiling/os/windows/structs.py index 433330150..258637c34 100644 --- a/qiling/os/windows/structs.py +++ b/qiling/os/windows/structs.py @@ -167,7 +167,7 @@ class DRIVER_OBJECT(Struct): return DRIVER_OBJECT -class KSYSTEM_TIME(struct.BaseStruct): +class KSYSTEM_TIME(struct.BaseStructEL): _fields_ = ( ('LowPart', ctypes.c_uint32), ('High1Time', ctypes.c_int32), @@ -175,7 +175,7 @@ class KSYSTEM_TIME(struct.BaseStruct): ) -class _LARGE_INTEGER(struct.BaseStruct): +class _LARGE_INTEGER(struct.BaseStructEL): _fields_ = ( ('LowPart', ctypes.c_uint32), ('HighPart', ctypes.c_int32) @@ -192,7 +192,7 @@ class LARGE_INTEGER(ctypes.Union): # https://www.geoffchappell.com/studies/windows/km/ntoskrnl/structs/kuser_shared_data/index.htm # https://doxygen.reactos.org/d7/deb/xdk_2ketypes_8h_source.html#l01155 -class KUSER_SHARED_DATA(struct.BaseStruct): +class KUSER_SHARED_DATA(struct.BaseStructEL): _fields_ = ( ('TickCountLowDeprecated', ctypes.c_uint32), ('TickCountMultiplier', ctypes.c_uint32), @@ -991,21 +991,21 @@ class LdrDataTableEntry(Struct): return LdrDataTableEntry -class FILETIME(struct.BaseStruct): +class FILETIME(struct.BaseStructEL): _fields_ = ( ('dwLowDateTime', ctypes.c_uint32), ('dwHighDateTime', ctypes.c_int32) ) # https://docs.microsoft.com/en-us/windows/console/coord-str -class COORD(struct.BaseStruct): +class COORD(struct.BaseStructEL): _fields_ = ( ('X', ctypes.c_uint16), ('Y', ctypes.c_uint16) ) # https://docs.microsoft.com/en-us/windows/console/small-rect-str -class SMALL_RECT(struct.BaseStruct): +class SMALL_RECT(struct.BaseStructEL): _fields_ = ( ('Left', ctypes.c_uint16), ('Top', ctypes.c_uint16), @@ -1014,7 +1014,7 @@ class SMALL_RECT(struct.BaseStruct): ) # https://docs.microsoft.com/en-us/windows/console/console-screen-buffer-info-str -class CONSOLE_SCREEN_BUFFER_INFO(struct.BaseStruct): +class CONSOLE_SCREEN_BUFFER_INFO(struct.BaseStructEL): _fields_ = ( ('dwSize', COORD), ('dwCursorPosition', COORD), @@ -1182,7 +1182,7 @@ def make_sid(auth_count: int): # byte is actually used. # # see: https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-sid_identifier_authority - class SID_IDENTIFIER_AUTHORITY(ctypes.BigEndianStructure): + class SID_IDENTIFIER_AUTHORITY(struct.BaseStructEB): _pack_ = 1 _fields_ = ( ('Value', ctypes.c_uint32), @@ -1192,7 +1192,7 @@ class SID_IDENTIFIER_AUTHORITY(ctypes.BigEndianStructure): assert ctypes.sizeof(SID_IDENTIFIER_AUTHORITY) == 6 # https://geoffchappell.com/studies/windows/km/ntoskrnl/api/rtl/sertl/sid.htm - class SID(struct.BaseStruct): + class SID(struct.BaseStructEL): _fields_ = ( ('Revision', ctypes.c_uint8), ('SubAuthorityCount', ctypes.c_uint8), @@ -1206,12 +1206,6 @@ class SID(struct.BaseStruct): ('SubAuthority', ctypes.c_uint32 * auth_count) ) - # the need of a big-endian structure forces us to define a non-BaseStruct structure field - # which breaks the 'volatile_ref' mechanism. we here prevent the user from doing that - @classmethod - def volatile_ref(cls, *args): - raise NotImplementedError(f'{cls.__name__} is not capable of volatile references') - # let SID structures be comparable def __eq__(self, other): if not isinstance(other, SID): @@ -1263,7 +1257,7 @@ def isFree(self) -> bool: # ) -class Point(struct.BaseStruct): +class Point(struct.BaseStructEL): _fields_ = ( ('x', ctypes.c_int32), ('y', ctypes.c_int32) @@ -1400,7 +1394,7 @@ class SYSTEM_INFO(Struct): return SYSTEM_INFO -class SYSTEMTIME(struct.BaseStruct): +class SYSTEMTIME(struct.BaseStructEL): _fields_ = ( ('wYear', ctypes.c_uint16), ('wMonth', ctypes.c_uint16), From a55dc798a7e23005cbc530842948bb2feb61e50b Mon Sep 17 00:00:00 2001 From: elicn Date: Sun, 20 Nov 2022 14:30:49 +0200 Subject: [PATCH 3/9] Revisit const mapping --- qiling/os/posix/const_mapping.py | 211 ++++++++++++++----------------- qiling/os/qnx/message.py | 6 +- qiling/os/qnx/syscall.py | 4 +- 3 files changed, 99 insertions(+), 122 deletions(-) diff --git a/qiling/os/posix/const_mapping.py b/qiling/os/posix/const_mapping.py index 99fb31ca5..3c46b76e6 100644 --- a/qiling/os/posix/const_mapping.py +++ b/qiling/os/posix/const_mapping.py @@ -1,40 +1,42 @@ #!/usr/bin/env python3 -# +# # Cross Platform and Multi Architecture Advanced Binary Emulation Framework # -from typing import Mapping, MutableSequence +from typing import Mapping, TypeVar from qiling import Qiling from qiling.const import QL_ARCH, QL_OS from .const import * -def _invert_dict(d: Mapping) -> Mapping: - return { v:k for k, v in d.items()} +KT = TypeVar('KT') +VT = TypeVar('VT') + + +def __invert_dict(d: Mapping[KT, VT]) -> Mapping[VT, KT]: + return {v: k for k, v in d.items()} -def _constant_mapping(bits: int, d_map: Mapping[str, int], ret: MutableSequence[str] = None, single_mapping: bool = False) -> str: - b_map = _invert_dict(d_map) +def _constant_mapping(bits: int, consts_map: Mapping[str, int]) -> str: + return __invert_dict(consts_map)[bits] - if single_mapping: - return b_map[bits] - if ret is None: - ret = [] +def _flags_mapping(value: int, flags_map: Mapping[str, int]) -> str: + names = [] - for val, sym in b_map.items(): - if val & bits: - bits ^= val - ret.append(sym) + for name, flag in flags_map.items(): + if value & flag: + value ^= flag + names.append(name) - if bits: - ret.append(f'{bits:#x}') + if value: + names.append(f'{value:#x}') - return " | ".join(ret) + return ' | '.join(names) -def ql_open_flag_mapping(ql: Qiling, flags): +def ql_open_flag_mapping(ql: Qiling, flags: int) -> int: def flag_mapping(flags, mapping_name, mapping_from, mapping_to, host_os, virt_os): ret = 0 for n in mapping_name: @@ -77,14 +79,12 @@ def flag_mapping(flags, mapping_name, mapping_from, mapping_to, host_os, virt_os elif virt_os == QL_OS.QNX: f = qnx_arm64_open_flags - if host_os == QL_OS.LINUX: - t = linux_x86_open_flags - elif host_os == QL_OS.MACOS: - t = macos_x86_open_flags - elif host_os == QL_OS.FREEBSD: - t = freebsd_x86_open_flags - elif host_os == QL_OS.WINDOWS: - t = windows_x86_open_flags + t = { + QL_OS.LINUX: linux_x86_open_flags, + QL_OS.MACOS: macos_x86_open_flags, + QL_OS.FREEBSD: freebsd_x86_open_flags, + QL_OS.WINDOWS: windows_x86_open_flags + }.get(host_os, {}) if f == t: return flags @@ -108,106 +108,83 @@ def mmap_flag_mapping(flags): 'MAP_ANON' : 0x00080000, 'MAP_SYSRAM' : 0x01000000 } - return _constant_mapping(flags, mmap_flags) + return _flags_mapping(flags, mmap_flags) -def mmap_prot_mapping(prots): - if prots == 0x0: + +def mmap_prot_mapping(prots: int) -> str: + if prots == 0: return 'PROT_NONE' mmap_prots = { - 'PROT_READ' : 0x1, - 'PROT_WRITE': 0x2, - 'PROT_EXEC' : 0x4, + 'PROT_READ' : 0b001, + 'PROT_WRITE': 0b010, + 'PROT_EXEC' : 0b100, # not supported by unicorn 'PROT_GROWSDOWN' : 0x01000000, 'PROT_GROWSUP' : 0x02000000 } - return _constant_mapping(prots, mmap_prots) - - -def socket_type_mapping(t, archtype, ostype): - if ostype == QL_OS.MACOS: - socket_type_map = { - QL_ARCH.X8664: linux_x86_socket_types, - QL_ARCH.ARM64: linux_arm_socket_types, - }[archtype] - else: - socket_type_map = { - QL_ARCH.X86: linux_x86_socket_types, - QL_ARCH.X8664: linux_x86_socket_types, - QL_ARCH.ARM: linux_arm_socket_types, - QL_ARCH.ARM64: linux_arm_socket_types, - QL_ARCH.MIPS: linux_mips_socket_types, - }[archtype] + + return _flags_mapping(prots, mmap_prots) + + +def socket_type_mapping(t: int, archtype: QL_ARCH) -> str: + socket_type_map = { + QL_ARCH.X86: linux_x86_socket_types, + QL_ARCH.X8664: linux_x86_socket_types, + QL_ARCH.ARM: linux_arm_socket_types, + QL_ARCH.ARM64: linux_arm_socket_types, + QL_ARCH.MIPS: linux_mips_socket_types + }[archtype] # https://code.woboq.org/linux/linux/net/socket.c.html#1363 - t &= SOCK_TYPE_MASK - return _constant_mapping(t, socket_type_map, single_mapping=True) - -def socket_domain_mapping(p, archtype, ostype): - if ostype == QL_OS.MACOS: - socket_domain_map = { - QL_ARCH.X8664: macos_x86_socket_domain, - QL_ARCH.ARM64: linux_arm_socket_domain, - }[archtype] - else: - socket_domain_map = { - QL_ARCH.X86: linux_x86_socket_domain, - QL_ARCH.X8664: linux_x86_socket_domain, - QL_ARCH.ARM: linux_arm_socket_domain, - QL_ARCH.ARM64: linux_arm_socket_domain, - QL_ARCH.MIPS: linux_mips_socket_domain, - }[archtype] - return _constant_mapping(p, socket_domain_map, single_mapping=True) - -def socket_level_mapping(t, archtype, ostype): - if ostype == QL_OS.MACOS: - socket_level_map = { - QL_ARCH.X8664: linux_x86_socket_level, - QL_ARCH.ARM64: linux_arm_socket_level, - }[archtype] - else: - socket_level_map = { - QL_ARCH.X86: linux_x86_socket_level, - QL_ARCH.X8664: linux_x86_socket_level, - QL_ARCH.ARM: linux_arm_socket_level, - QL_ARCH.ARM64: linux_arm_socket_level, - QL_ARCH.MIPS: linux_mips_socket_level, - }[archtype] - return _constant_mapping(t, socket_level_map, single_mapping=True) - - -def socket_ip_option_mapping(t, archtype, ostype): - if ostype == QL_OS.MACOS: - socket_option_map = { - QL_ARCH.X8664: macos_socket_ip_options, - QL_ARCH.ARM64: macos_socket_ip_options, - }[archtype] - else: - socket_option_map = { - QL_ARCH.X86: linux_socket_ip_options, - QL_ARCH.X8664: linux_socket_ip_options, - QL_ARCH.ARM: linux_socket_ip_options, - QL_ARCH.ARM64: linux_socket_ip_options, - QL_ARCH.MIPS: linux_socket_ip_options, - }[archtype] - return _constant_mapping(t, socket_option_map, single_mapping=True) - - -def socket_option_mapping(t, archtype, ostype): - if ostype == QL_OS.MACOS: - socket_option_map = { - QL_ARCH.X8664: linux_x86_socket_options, - QL_ARCH.ARM64: linux_arm_socket_options, - }[archtype] - else: - socket_option_map = { - QL_ARCH.X86: linux_x86_socket_options, - QL_ARCH.X8664: linux_x86_socket_options, - QL_ARCH.ARM: linux_arm_socket_options, - QL_ARCH.ARM64: linux_arm_socket_options, - QL_ARCH.MIPS: linux_mips_socket_options, - }[archtype] - return _constant_mapping(t, socket_option_map, single_mapping=True) \ No newline at end of file + return _constant_mapping(t & SOCK_TYPE_MASK, socket_type_map) + + +def socket_domain_mapping(p: int, archtype: QL_ARCH, ostype: QL_OS) -> str: + socket_domain_map = { + QL_ARCH.X86: linux_x86_socket_domain, + QL_ARCH.X8664: macos_x86_socket_domain if ostype == QL_OS.MACOS else linux_x86_socket_domain, + QL_ARCH.ARM: linux_arm_socket_domain, + QL_ARCH.ARM64: linux_arm_socket_domain, + QL_ARCH.MIPS: linux_mips_socket_domain + }[archtype] + + return _constant_mapping(p, socket_domain_map) + + +def socket_level_mapping(t: int, archtype: QL_ARCH) -> str: + socket_level_map = { + QL_ARCH.X86: linux_x86_socket_level, + QL_ARCH.X8664: linux_x86_socket_level, + QL_ARCH.ARM: linux_arm_socket_level, + QL_ARCH.ARM64: linux_arm_socket_level, + QL_ARCH.MIPS: linux_mips_socket_level + }[archtype] + + return _constant_mapping(t, socket_level_map) + + +def socket_ip_option_mapping(t: int, archtype: QL_ARCH, ostype: QL_OS) -> str: + socket_option_map = { + QL_ARCH.X86: linux_socket_ip_options, + QL_ARCH.X8664: macos_socket_ip_options if ostype == QL_OS.MACOS else linux_socket_ip_options, + QL_ARCH.ARM: linux_socket_ip_options, + QL_ARCH.ARM64: macos_socket_ip_options if ostype == QL_OS.MACOS else linux_socket_ip_options, + QL_ARCH.MIPS: linux_mips_socket_ip_options + }[archtype] + + return _constant_mapping(t, socket_option_map) + + +def socket_option_mapping(t: int, archtype: QL_ARCH) -> str: + socket_option_map = { + QL_ARCH.X86: linux_x86_socket_options, + QL_ARCH.X8664: linux_x86_socket_options, + QL_ARCH.ARM: linux_arm_socket_options, + QL_ARCH.ARM64: linux_arm_socket_options, + QL_ARCH.MIPS: linux_mips_socket_options + }[archtype] + + return _constant_mapping(t, socket_option_map) diff --git a/qiling/os/qnx/message.py b/qiling/os/qnx/message.py index e4621fcf6..f4686628c 100644 --- a/qiling/os/qnx/message.py +++ b/qiling/os/qnx/message.py @@ -13,7 +13,7 @@ from qiling.os.qnx.const import IO_FLAG_MASK, PAGESIZE, S_IFMT from qiling.os.qnx.helpers import get_message_body, QnxConn from qiling.os.qnx.types import file_access, file_stats, file_types, file_open_flags, file_sharing_modes, io_connect_eflag, io_connect_ioflag, io_connect_subtypes, lseek_whence, mem_ctrl_subtypes, mmap_flags, pathconf_names, sysconf_conditions, sysconf_consts, sysconf_names, sysconf_subtypes -from qiling.os.posix.const_mapping import _constant_mapping, mmap_prot_mapping, ql_open_flag_mapping +from qiling.os.posix.const_mapping import _flags_mapping, mmap_prot_mapping from qiling.os.posix.syscall import ql_syscall_close, ql_syscall_fstat, ql_syscall_lseek, ql_syscall_mmap, ql_syscall_open, ql_syscall_read, ql_syscall_write # TODO: move this to qiling.os.qnx.const? @@ -57,7 +57,7 @@ def ql_qnx_msg_io_connect(ql:Qiling, coid, smsg, sparts, rmsg, rparts, *args, ** ioflag_hi = ioflag & (~IO_FLAG_MASK) real_mode = mode & (~S_IFMT) # ql.log.debug(f'msg_io_connect(subtype = {subtype}, file_type = {file_type}, ioflag = 0x{ioflag:x}, mode = 0x{mode:x}, sflag = 0x{sflag:x}, access = {access}, extra_len = {extra_len})') - ql.log.debug(f'msg_io_connect(subtype = {io_connect_subtypes[subtype]}, file_type = {file_types[file_type]}, ioflag = {_constant_mapping(ioflag_lo, io_connect_ioflag) + _constant_mapping(ioflag_hi, file_open_flags)}, mode = 0x{real_mode:x}, type = {_constant_mapping((mode & S_IFMT), file_stats)}, sflag = {file_sharing_modes[sflag]})') + ql.log.debug(f'msg_io_connect(subtype = {io_connect_subtypes[subtype]}, file_type = {file_types[file_type]}, ioflag = {_flags_mapping(ioflag_lo, io_connect_ioflag) + _flags_mapping(ioflag_hi, file_open_flags)}, mode = 0x{real_mode:x}, type = {_flags_mapping((mode & S_IFMT), file_stats)}, sflag = {file_sharing_modes[sflag]})') # convert _IO_FLAG_? to O_? flag and then to O_? flags of host system ioflag -= 1 #ioflag = ql_open_flag_mapping(ql, ioflag) @@ -211,7 +211,7 @@ def ql_qnx_msg_mem_map(ql:Qiling, coid, smsg, sparts, rmsg, rparts, *args, **kw) if fd > 0: fd = ql.os.connections[fd].fd - ql.log.debug(f'mem_map(addr = 0x{addr:x}, len = 0x{len:x}, prot = {mmap_prot_mapping(prot)}, flags = {_constant_mapping(flags, mmap_flags)}, fd = {fd}, preload = 0x{preload:x}, align = 0x{align:x}, offset = 0x{offset:x})') + ql.log.debug(f'mem_map(addr = 0x{addr:x}, len = 0x{len:x}, prot = {mmap_prot_mapping(prot)}, flags = {_flags_mapping(flags, mmap_flags)}, fd = {fd}, preload = 0x{preload:x}, align = 0x{align:x}, offset = 0x{offset:x})') # map memory ret = ql_syscall_mmap(ql, addr, len, prot, flags, fd, offset) # struct _mem_map_replay in services/system/public/sys/memmsg.h diff --git a/qiling/os/qnx/syscall.py b/qiling/os/qnx/syscall.py index 2d22c9685..70c238fbb 100644 --- a/qiling/os/qnx/syscall.py +++ b/qiling/os/qnx/syscall.py @@ -16,7 +16,7 @@ def time_ns(): from qiling import Qiling from qiling.utils import ql_get_module_function -from qiling.os.posix.const_mapping import _constant_mapping +from qiling.os.posix.const_mapping import _flags_mapping from qiling.os.qnx.helpers import get_message_body, QnxConn, ux32s from qiling.os.qnx.map_msgtype import map_msgtype from qiling.os.qnx.structs import * @@ -26,7 +26,7 @@ def time_ns(): def ql_syscall_channel_create(ql:Qiling, flags, *args, **kw): # TODO: Can we ignore the flags? - ql.log.debug(f'syscall_channel_create(flags = {_constant_mapping(flags, channel_create_flags)})') + ql.log.debug(f'syscall_channel_create(flags = {_flags_mapping(flags, channel_create_flags)})') # return new Channel Id regreturn = ql.os.channel_id ql.os.channel_id += 1 From b76b54cce35f4769ba0adf9188f9b643189da4b9 Mon Sep 17 00:00:00 2001 From: elicn Date: Sun, 20 Nov 2022 14:31:59 +0200 Subject: [PATCH 4/9] Revisit socketcall syscall and support more functions --- qiling/os/posix/syscall/net.py | 95 +++++++++++++++++++--------------- 1 file changed, 53 insertions(+), 42 deletions(-) diff --git a/qiling/os/posix/syscall/net.py b/qiling/os/posix/syscall/net.py index 01dd7c0e8..fe9dd89a4 100644 --- a/qiling/os/posix/syscall/net.py +++ b/qiling/os/posix/syscall/net.py @@ -3,57 +3,68 @@ # Cross Platform and Multi Architecture Advanced Binary Emulation Framework # -from typing import Callable, Mapping, Tuple +import inspect +from enum import IntEnum +from typing import Callable, Mapping from qiling import Qiling -from qiling.os.posix.syscall.socket import ql_syscall_socket, ql_syscall_connect, ql_syscall_recv, ql_syscall_send, ql_syscall_bind, ql_syscall_listen, ql_syscall_accept, ql_syscall_getsockname, ql_syscall_setsockopt, ql_syscall_recvfrom, ql_syscall_sendto +from qiling.os.posix.syscall.socket import * + + +class SOCKETCALL(IntEnum): + SYS_SOCKET = 1 + SYS_BIND = 2 + SYS_CONNECT = 3 + SYS_LISTEN = 4 + SYS_ACCEPT = 5 + SYS_GETSOCKNAME = 6 + SYS_GETPEERNAME = 7 + SYS_SOCKETPAIR = 8 + SYS_SEND = 9 + SYS_RECV = 10 + SYS_SENDTO = 11 + SYS_RECVFROM = 12 + SYS_SHUTDOWN = 13 + SYS_SETSOCKOPT = 14 + SYS_GETSOCKOPT = 15 + SYS_SENDMSG = 16 + SYS_RECVMSG = 17 + SYS_ACCEPT4 = 18 + SYS_RECVMMSG = 19 + SYS_SENDMMSG = 20 + def ql_syscall_socketcall(ql: Qiling, call: int, args: int): - SOCKETCALL_SYS_SOCKET = 1 - SOCKETCALL_SYS_BIND = 2 - SOCKETCALL_SYS_CONNECT = 3 - SOCKETCALL_SYS_LISTEN = 4 - SOCKETCALL_SYS_ACCEPT = 5 - SOCKETCALL_SYS_GETSOCKNAME = 6 - SOCKETCALL_SYS_GETPEERNAME = 7 - SOCKETCALL_SYS_SOCKETPAIR = 8 - SOCKETCALL_SYS_SEND = 9 - SOCKETCALL_SYS_RECV = 10 - SOCKETCALL_SYS_SENDTO = 11 - SOCKETCALL_SYS_RECVFROM = 12 - SOCKETCALL_SYS_SHUTDOWN = 13 - SOCKETCALL_SYS_SETSOCKOPT = 14 - SOCKETCALL_SYS_GETSOCKOPT = 15 - SOCKETCALL_SYS_SENDMSG = 16 - SOCKETCALL_SYS_RECVMSG = 17 - SOCKETCALL_SYS_ACCEPT4 = 18 - SOCKETCALL_SYS_RECVMMSG = 19 - SOCKETCALL_SYS_SENDMMSG = 20 - - # map call values to their corresponding handlers and number of arguments they - # should read from the specified base pointer - handlers: Mapping[int, Tuple[Callable, int]] = { - SOCKETCALL_SYS_SOCKET : (ql_syscall_socket, 3), - SOCKETCALL_SYS_CONNECT : (ql_syscall_connect, 3), - SOCKETCALL_SYS_SEND : (ql_syscall_send, 4), - SOCKETCALL_SYS_RECVFROM : (ql_syscall_recvfrom, 6), - SOCKETCALL_SYS_SENDTO : (ql_syscall_sendto, 6), - SOCKETCALL_SYS_RECV : (ql_syscall_recv, 4), - SOCKETCALL_SYS_BIND : (ql_syscall_bind, 3), - SOCKETCALL_SYS_LISTEN : (ql_syscall_listen, 2), - SOCKETCALL_SYS_ACCEPT : (ql_syscall_accept, 3), - SOCKETCALL_SYS_GETSOCKNAME : (ql_syscall_getsockname, 3), - SOCKETCALL_SYS_SETSOCKOPT : (ql_syscall_setsockopt, 5) + # map call values to their corresponding handlers + handlers: Mapping[SOCKETCALL, Callable] = { + SOCKETCALL.SYS_SOCKET: ql_syscall_socket, + SOCKETCALL.SYS_BIND: ql_syscall_bind, + SOCKETCALL.SYS_CONNECT: ql_syscall_connect, + SOCKETCALL.SYS_LISTEN: ql_syscall_listen, + SOCKETCALL.SYS_ACCEPT: ql_syscall_accept, + SOCKETCALL.SYS_GETSOCKNAME: ql_syscall_getsockname, + SOCKETCALL.SYS_GETPEERNAME: ql_syscall_getpeername, + SOCKETCALL.SYS_SEND: ql_syscall_send, + SOCKETCALL.SYS_RECV: ql_syscall_recv, + SOCKETCALL.SYS_SENDTO: ql_syscall_sendto, + SOCKETCALL.SYS_RECVFROM: ql_syscall_recvfrom, + SOCKETCALL.SYS_SHUTDOWN: ql_syscall_shutdown, + SOCKETCALL.SYS_SETSOCKOPT: ql_syscall_setsockopt, + SOCKETCALL.SYS_GETSOCKOPT: ql_syscall_getsockopt, + SOCKETCALL.SYS_RECVMSG: ql_syscall_recvmsg } if call not in handlers: - ql.log.debug(f'socketcall: call {call:d} not implemented') - ql.os.stop() - raise + call_name = next((m.name for m in SOCKETCALL if m.value == call), '') + + raise NotImplementedError(f'socketcall: call {call_name or call} not implemented') + + handler = handlers[call] - handler, nargs = handlers[call] + # determine number of arguments, excluding the first 'ql' arg + nargs = len(inspect.signature(handler).parameters) - 1 # read 'nargs' arguments from the specified base pointer 'args' - params = (ql.unpack(ql.mem.read(args + i * ql.arch.pointersize, ql.arch.pointersize)) for i in range(nargs)) + params = (ql.mem.read_ptr(args + i * ql.arch.pointersize) for i in range(nargs)) return handler(ql, *params) From 6cedcfa561088219aab3fca28b7b76f35e0284ff Mon Sep 17 00:00:00 2001 From: elicn Date: Sun, 20 Nov 2022 14:34:15 +0200 Subject: [PATCH 5/9] Refactor POSIX socket syscalls --- qiling/os/posix/const.py | 8 +- qiling/os/posix/structs.py | 115 ++++ qiling/os/posix/syscall/socket.py | 881 ++++++++++++++++++------------ 3 files changed, 640 insertions(+), 364 deletions(-) create mode 100644 qiling/os/posix/structs.py diff --git a/qiling/os/posix/const.py b/qiling/os/posix/const.py index 9414dd2db..7fdac639e 100644 --- a/qiling/os/posix/const.py +++ b/qiling/os/posix/const.py @@ -241,13 +241,15 @@ linux_arm_socket_options = { "SO_DEBUG" : 0x0001, "SO_REUSEADDR" : 0x0002, - "SO_KEEPALIVE" : 0x0009, + "SO_TYPE" : 0x0003, + "SO_ERROR" : 0x0004, "SO_DONTROUTE" : 0x0005, "SO_BROADCAST" : 0x0006, - "SO_LINGER" : 0x000d, - "SO_OOBINLINE" : 0x000a, "SO_SNDBUF" : 0x0007, "SO_RCVBUF" : 0x0008, + "SO_KEEPALIVE" : 0x0009, + "SO_OOBINLINE" : 0x000a, + "SO_LINGER" : 0x000d, "SO_REUSEPORT" : 0x000f, "SO_SNDLOWAT" : 0x0013, "SO_RCVLOWAT" : 0x0012, diff --git a/qiling/os/posix/structs.py b/qiling/os/posix/structs.py new file mode 100644 index 000000000..bf1d146cd --- /dev/null +++ b/qiling/os/posix/structs.py @@ -0,0 +1,115 @@ +#!/usr/bin/env python3 +# +# Cross Platform and Multi Architecture Advanced Binary Emulation Framework +# + +import ctypes + +from qiling.const import QL_ENDIAN +from qiling.os import struct + + +def make_sockaddr(archbits: int, endian: QL_ENDIAN): + Struct = struct.get_aligned_struct(archbits, endian) + + class sockaddr(Struct): + _pack_ = 1 + + _fields_ = ( + ('sa_family', ctypes.c_uint16), + ) + + return sockaddr + + +def make_sockaddr_un(archbits: int, endian: QL_ENDIAN, pathlen: int): + Struct = struct.get_aligned_struct(archbits, endian) + + class sockaddr_un(Struct): + _fields_ = ( + ('sun_family', ctypes.c_int16), + ('sun_path', ctypes.c_char * pathlen) + ) + + return sockaddr_un + + +def make_sockaddr_in(archbits: int, endian: QL_ENDIAN): + Struct = struct.get_aligned_struct(archbits, endian) + + class in_addr(Struct): + _fields_ = ( + ('s_addr', ctypes.c_uint32), + ) + + class sockaddr_in(Struct): + _fields_ = ( + ('sin_family', ctypes.c_int16), + ('sin_port', ctypes.c_uint16), + ('sin_addr', in_addr), + ('sin_zero', ctypes.c_byte * 8) + ) + + return sockaddr_in + + +def make_sockaddr_in6(archbits: int, endian: QL_ENDIAN): + Struct = struct.get_aligned_struct(archbits, endian) + + class in6_addr(Struct): + _fields_ = ( + ('s6_addr', ctypes.c_uint8 * 16), + ) + + class sockaddr_in6(Struct): + _fields_ = ( + ('sin6_family', ctypes.c_int16), + ('sin6_port', ctypes.c_uint16), + ('sin6_flowinfo', ctypes.c_uint32), + ('sin6_addr', in6_addr), + ('sin6_scope_id', ctypes.c_uint32) + ) + + return sockaddr_in6 + + +def make_msghdr(archbits: int, endian: QL_ENDIAN): + Struct = struct.get_aligned_struct(archbits, endian) + + class msghdr(Struct): + _fields_ = ( + ('msg_name', ctypes.c_uint64), + ('msg_namelen', ctypes.c_int32), + ('msg_iov', ctypes.c_uint64), + ('msg_iovlen', ctypes.c_int32), + ('msg_control', ctypes.c_uint64), + ('msg_controllen', ctypes.c_int32), + ('msg_flags', ctypes.c_int32) + ) + + return msghdr + + +def make_cmsghdr(archbits: int, endian: QL_ENDIAN): + Struct = struct.get_aligned_struct(archbits, endian) + + class cmsghdr(Struct): + _fields_ = ( + ('cmsg_len', ctypes.c_int32), + ('cmsg_level', ctypes.c_int32), + ('cmsg_type', ctypes.c_int32) + ) + + return cmsghdr + + +def make_iovec(archbits: int, endian: QL_ENDIAN): + Struct = struct.get_aligned_struct(archbits, endian) + + class iovec(Struct): + _fields_ = ( + ('iov_base', ctypes.c_uint64), + ('iov_len', ctypes.c_uint64) + ) + + return iovec diff --git a/qiling/os/posix/syscall/socket.py b/qiling/os/posix/syscall/socket.py index db362b52c..21f46b4ef 100644 --- a/qiling/os/posix/syscall/socket.py +++ b/qiling/os/posix/syscall/socket.py @@ -3,404 +3,492 @@ # Cross Platform and Multi Architecture Advanced Binary Emulation Framework # -import ctypes import ipaddress -import sys import socket -import struct -from typing import Tuple - -from unicorn.unicorn import UcError +from typing import Optional, Tuple from qiling import Qiling from qiling.const import QL_ARCH, QL_VERBOSE from qiling.os.posix.const_mapping import socket_type_mapping, socket_level_mapping, socket_domain_mapping, socket_ip_option_mapping, socket_option_mapping from qiling.os.posix.const import * from qiling.os.posix.filestruct import ql_socket +from qiling.os.posix.structs import * + + +AF_UNIX = 1 +AF_INET = 2 +AF_INET6 = 10 + + +def inet_aton(ipaddr: str) -> int: + # ipdata = bytes(int(a, 0) for a in ipaddr.split('.', 4)) + ipdata = ipaddress.IPv4Address(ipaddr).packed + + return int.from_bytes(ipdata, byteorder='big') + + +def inet6_aton(ipaddr: str) -> int: + ipdata = ipaddress.IPv6Address(ipaddr).packed + + return int.from_bytes(ipdata, byteorder='big') + + +def inet_htoa(ql: Qiling, addr: int) -> str: + abytes = ql.pack32(addr) + + return ipaddress.IPv4Address(abytes).compressed + + +def inet_ntoa(addr: int) -> str: + abytes = addr.to_bytes(length=4, byteorder='big') -class msghdr(ctypes.Structure): - _fields_ = [ - ('msg_name' , ctypes.c_uint64), - ('msg_namelen' , ctypes.c_int32 ), - ('msg_iov' , ctypes.c_uint64), - ('msg_iovlen' , ctypes.c_int32 ), - ('msg_control' , ctypes.c_uint64), - ('msg_controllen', ctypes.c_int32 ), - ('msg_flags' , ctypes.c_int32 ) - ] + return ipaddress.IPv4Address(abytes).compressed - _pack_ = 8 - @classmethod - def load(cls, ql: Qiling, addr: int): - data = ql.mem.read(addr, ctypes.sizeof(msghdr)) - return msghdr.from_buffer(data) +def inet6_htoa(ql: Qiling, addr: bytes) -> str: + # TODO: swap addr bytes order according to ql.arch.endian? -class cmsghdr(ctypes.Structure): - _fields_ = [ - ('cmsg_len' , ctypes.c_int32), - ('cmsg_level', ctypes.c_int32), - ('cmsg_type' , ctypes.c_int32), - ] + return ipaddress.IPv6Address(addr).compressed - _pack_ = 8 - @classmethod - def load(cls, ql: Qiling, addr: int): - data = ql.mem.read(addr, ctypes.sizeof(cmsghdr)) - return cmsghdr.from_buffer(data) +def inet6_ntoa(addr: bytes) -> str: + return ipaddress.IPv6Address(addr).compressed -class iovec(ctypes.Structure): - _fields_ = [ - ('iov_base', ctypes.c_uint64), - ('iov_len' , ctypes.c_uint64), - ] - _pack_ = 8 +def ntohs(ql: Qiling, nval: int) -> int: + ebdata = nval.to_bytes(length=2, byteorder='big') - @classmethod - def load(cls, ql: Qiling, addr: int): - data = ql.mem.read(addr, ctypes.sizeof(iovec)) - return iovec.from_buffer(data) + return ql.unpack16(ebdata) -def ql_bin_to_ip(ip): - return ipaddress.ip_address(ip).compressed +def htons(ql: Qiling, hval: int) -> int: + ndata = ql.pack16(hval) + + return int.from_bytes(ndata, byteorder='big') def ql_unix_socket_path(ql: Qiling, sun_path: bytearray) -> Tuple[str, str]: - if sun_path[0] == 0: - # Abstract Unix namespace + vpath, _, _ = sun_path.partition(b'\x00') + + # an abstract Unix namespace? + if not vpath: # 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(), '') - vpath = sun_path.split(b'\0', maxsplit=1)[0].decode() + vpath = ql.os.path.virtual_abspath(vpath.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): +def ql_syscall_socket(ql: Qiling, domain: int, socktype: int, protocol: int): idx = next((i for i in range(NR_OPEN) if ql.os.fd[i] is None), -1) if idx != -1: - emu_socket_value = socket_type + vsock_type = socktype # ql_socket.open should use host platform based socket_type. try: - emu_socket_type = socket_type_mapping(socket_type, ql.arch.type, ql.os.type) + vsock_type_name = socket_type_mapping(vsock_type, ql.arch.type) except KeyError: - ql.log.error(f'Cannot convert emu_socket_type {emu_socket_value} to host platform based socket_type') + ql.log.error(f'Could not convert emulated socket type {vsock_type} to a socket type name') raise try: - socket_type = getattr(socket, emu_socket_type) + hsock_type = getattr(socket, vsock_type_name) except AttributeError: - ql.log.error(f'Cannot convert emu_socket_type {emu_socket_type}:{emu_socket_value} to host platform based socket_type') + ql.log.error(f'Could not convert emulated socket type name {vsock_type_name} to host socket type') raise - ql.log.debug(f'Convert emu_socket_type {emu_socket_type}:{emu_socket_value} to host platform based socket_type {emu_socket_type}:{socket_type}') + ql.log.debug(f'Converted emulated socket type {vsock_type_name} to host socket type {hsock_type}') try: - sock = ql_socket.open(socket_domain, socket_type, socket_protocol) + sock = ql_socket.open(domain, hsock_type, protocol) # set REUSEADDR options under debug mode if ql.verbose >= QL_VERBOSE.DEBUG: sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - ql.os.fd[idx] = sock - # May raise error: Protocol not supported except OSError as e: - ql.log.debug(f'{e}: {socket_domain=}, {socket_type=}, {socket_protocol=}') + ql.log.debug(f'Error opening socket: {e}') idx = -1 + else: + ql.os.fd[idx] = sock - socket_type = socket_type_mapping(socket_type, ql.arch.type, ql.os.type) - socket_domain = socket_domain_mapping(socket_domain, ql.arch.type, ql.os.type) - ql.log.debug("socket(%s, %s, %s) = %d" % (socket_domain, socket_type, socket_protocol, idx)) + s_socktype = socket_type_mapping(socktype, ql.arch.type) + s_domain = socket_domain_mapping(domain, ql.arch.type, ql.os.type) + ql.log.debug("socket(%s, %s, %s) = %d" % (s_domain, s_socktype, protocol, idx)) return idx def ql_syscall_connect(ql: Qiling, sockfd: int, addr: int, addrlen: int): - AF_UNIX = 1 - AF_INET = 2 + if sockfd not in range(NR_OPEN): + return -1 + + sock: Optional[ql_socket] = ql.os.fd[sockfd] - sock_addr = ql.mem.read(addr, addrlen) - family = ql.unpack16(sock_addr[:2]) + if sock is None: + return -1 - sock = ql.os.fd[sockfd] - assert isinstance(sock, ql_socket) + data = ql.mem.read(addr, addrlen) + print(f"data = {data.hex(' ')}") + + abits = ql.arch.bits + endian = ql.arch.endian + + sockaddr = make_sockaddr(abits, endian) + sockaddr_obj = sockaddr.from_buffer(data) dest = None + regreturn = -1 - if sock.family != family: + if sock.family != sockaddr_obj.sa_family: return -1 if sock.family == AF_UNIX: - hpath, vpath = ql_unix_socket_path(ql, sock_addr[2:]) + hpath, vpath = ql_unix_socket_path(ql, data[2:]) - ql.log.debug(f'connecting to "{vpath}"') + # TODO: support connecting to fs_mapped unix sockets + 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) + sockaddr_in = make_sockaddr_in(abits, endian) + sockaddr_obj = sockaddr_in.from_buffer(data) - ql.log.debug(f'connecting to {ip}:{port}') - dest = (ip, port) + port = ntohs(ql, sockaddr_obj.sin_port) + host = inet_htoa(ql, sockaddr_obj.sin_addr.s_addr) - else: + ql.log.debug(f'Connecting to {host}:{port}') + dest = (host, port) + + elif sock.family == AF_INET6 and ql.os.ipv6: + sockaddr_in6 = make_sockaddr_in(abits, endian) + sockaddr_obj = sockaddr_in6.from_buffer(data) + + port = ntohs(ql, sockaddr_obj.sin_port) + host = inet6_htoa(ql, sockaddr_obj.sin6_addr.s6_addr) + + ql.log.debug(f'Conecting to {host}:{port}') + dest = (host, port) + + if dest is not None: + try: + sock.connect(dest) + except (ConnectionError, FileNotFoundError): + regreturn = -1 + else: + regreturn = 0 + + return regreturn + + +def ql_syscall_getsockopt(ql: Qiling, sockfd: int, level: int, optname: int, optval_addr: int, optlen_addr: int): + if sockfd not in range(NR_OPEN): return -1 + sock: Optional[ql_socket] = ql.os.fd[sockfd] + + if sock is None: + return -1 + + vsock_level = level + try: - sock.connect(dest) - except: - regreturn = -1 - else: - regreturn = 0 + vsock_level_name = socket_level_mapping(vsock_level, ql.arch.type) + except KeyError: + ql.log.error(f'Could not convert emulated socket level {vsock_level} to a socket level name') + raise - return regreturn + try: + hsock_level = getattr(socket, vsock_level_name) + except AttributeError: + ql.log.error(f'Could not convert emulated socket level name {vsock_level_name} to host socket level') + raise + + ql.log.debug(f'Converted emulated socket level {vsock_level_name} to host socket level {hsock_level}') + + vsock_opt = optname + + try: + # emu_opt_name is based on level + if vsock_level_name == 'IPPROTO_IP': + vsock_opt_name = socket_ip_option_mapping(vsock_opt, ql.arch.type, ql.os.type) + else: + vsock_opt_name = socket_option_mapping(vsock_opt, ql.arch.type) + + # Fix for mips + if ql.arch.type == QL_ARCH.MIPS: + if vsock_opt_name.endswith("_NEW") or vsock_opt_name.endswith("_OLD"): + vsock_opt_name = vsock_opt_name[:-4] + + except KeyError: + ql.log.error(f'Could not convert emulated socket option {vsock_opt} to socket option name') + raise + + try: + hsock_opt = getattr(socket, vsock_opt_name) + except AttributeError: + ql.log.error(f'Could not convert emulated socket option name {vsock_opt_name} to host socket option') + raise + + ql.log.debug(f'Converted emulated socket option {vsock_opt_name} to host socket option {hsock_opt}') + optlen = min(ql.unpack32s(ql.mem.read(optlen_addr, 4)), 1024) -def ql_syscall_getsockopt(ql: Qiling, sockfd, level, optname, optval_addr, optlen_addr): - if sockfd not in range(NR_OPEN) or ql.os.fd[sockfd] is None: - return -EBADF + if optlen < 0: + return -1 try: - optlen = min(ql.unpack32s(ql.mem.read(optlen_addr, 4)), 1024) + optval = sock.getsockopt(hsock_level, hsock_opt, optlen) + except (ConnectionError, OSError): + return -1 + + ql.mem.write(optval_addr, optval) + + return 0 + + +def ql_syscall_setsockopt(ql: Qiling, sockfd: int, level: int, optname: int, optval_addr: int, optlen: int): + if sockfd not in range(NR_OPEN): + return -1 - if optlen < 0: - return -EINVAL + sock: Optional[ql_socket] = ql.os.fd[sockfd] + + if sock is None: + return -1 + + if optval_addr == 0: + sock.setsockopt(level, optname, None, optlen) - emu_level = level + else: + vsock_level = level try: - emu_level_name = socket_level_mapping(emu_level, ql.arch.type, ql.os.type) + vsock_level_name = socket_level_mapping(vsock_level, ql.arch.type) except KeyError: - ql.log.error(f"Can't convert emu_level {emu_level} to host platform based level") + ql.log.error(f'Could not convert emulated socket level {vsock_level} to a socket level name') raise try: - level = getattr(socket, emu_level_name) + hsock_level = getattr(socket, vsock_level_name) except AttributeError: - ql.log.error(f"Can't convert emu_level {emu_level_name}:{emu_level} to host platform based emu_level") + ql.log.error(f'Could not convert emulated socket level name {vsock_level_name} to host socket level') raise - ql.log.debug(f"Convert emu_level {emu_level_name}:{emu_level} to host platform based level {emu_level_name}:{level}") + ql.log.debug(f'Converted emulated socket level {vsock_level_name} to host socket level {hsock_level}') - emu_opt = optname + vsock_opt = optname try: - emu_level_name = socket_level_mapping(emu_level, ql.arch.type, ql.os.type) - # emu_opt_name is based on level - if emu_level_name == "IPPROTO_IP": - emu_opt_name = socket_ip_option_mapping(emu_opt, ql.arch.type, ql.os.type) + if vsock_level_name == 'IPPROTO_IP': + vsock_opt_name = socket_ip_option_mapping(vsock_opt, ql.arch.type, ql.os.type) else: - emu_opt_name = socket_option_mapping(emu_opt, ql.arch.type, ql.os.type) + vsock_opt_name = socket_option_mapping(vsock_opt, ql.arch.type) # Fix for mips if ql.arch.type == QL_ARCH.MIPS: - if emu_opt_name.endswith("_NEW") or emu_opt_name.endswith("_OLD"): - emu_opt_name = emu_opt_name[:-4] + if vsock_opt_name.endswith("_NEW") or vsock_opt_name.endswith("_OLD"): + vsock_opt_name = vsock_opt_name[:-4] except KeyError: - ql.log.error(f"Can't convert emu_optname {emu_opt} to host platform based optname") + ql.log.error(f'Could not convert emulated socket option {vsock_opt} to socket option name') raise try: - optname = getattr(socket, emu_opt_name) + hsock_opt = getattr(socket, vsock_opt_name) except AttributeError: - ql.log.error(f"Can't convert emu_optname {emu_opt_name}:{emu_opt} to host platform based emu_optname") + ql.log.error(f'Could not convert emulated socket option name {vsock_opt_name} to host socket option') raise - ql.log.debug(f"Convert emu_optname {emu_opt_name}:{emu_opt} to host platform based optname {emu_opt_name}:{optname}") + ql.log.debug(f'Converted emulated socket option {vsock_opt_name} to host socket option {hsock_opt}') - optval = ql.os.fd[sockfd].getsockopt(level, optname, optlen) - ql.mem.write(optval_addr, optval) - except UcError: - return -EFAULT - - return 0 + optval = ql.mem.read(optval_addr, optlen) - -def ql_syscall_setsockopt(ql: Qiling, sockfd, level, optname, optval_addr, optlen): - if sockfd not in range(NR_OPEN) or ql.os.fd[sockfd] is None: - return -EBADF - - regreturn = 0 - if optval_addr == 0: - ql.os.fd[sockfd].setsockopt(level, optname, None, optlen) - else: try: - emu_level = level + sock.setsockopt(hsock_level, hsock_opt, optval) + except (ConnectionError, OSError): + return -1 - try: - emu_level_name = socket_level_mapping(emu_level, ql.arch.type, ql.os.type) - except KeyError: - ql.log.error(f"Can't convert emu_level {emu_level} to host platform based level") - raise + return 0 - try: - level = getattr(socket, emu_level_name) - except AttributeError: - ql.log.error(f"Can't convert emu_level {emu_level_name}:{emu_level} to host platform based emu_level") - raise - ql.log.debug(f"Convert emu_level {emu_level_name}:{emu_level} to host platform based level {emu_level_name}:{level}") +def ql_syscall_shutdown(ql: Qiling, sockfd: int, how: int): + if sockfd not in range(NR_OPEN): + return -1 - emu_opt = optname + sock: Optional[ql_socket] = ql.os.fd[sockfd] - try: - emu_level_name = socket_level_mapping(emu_level, ql.arch.type, ql.os.type) + if sock is None: + return -1 - # emu_opt_name is based on level - if emu_level_name == "IPPROTO_IP": - emu_opt_name = socket_ip_option_mapping(emu_opt, ql.arch.type, ql.os.type) - else: - emu_opt_name = socket_option_mapping(emu_opt, ql.arch.type, ql.os.type) + try: + sock.shutdown(how) + except ConnectionError: + return -1 - # Fix for mips - if ql.arch.type == QL_ARCH.MIPS: - if emu_opt_name.endswith("_NEW") or emu_opt_name.endswith("_OLD"): - emu_opt_name = emu_opt_name[:-4] + return 0 - except KeyError: - ql.log.error(f"Can't convert emu_optname {emu_opt} to host platform based optname") - raise - try: - optname = getattr(socket, emu_opt_name) - except AttributeError: - ql.log.error(f"Can't convert emu_optname {emu_opt_name}:{emu_opt} to host platform based emu_optname") - raise +def ql_syscall_bind(ql: Qiling, sockfd: int, addr: int, addrlen: int): + if sockfd not in range(NR_OPEN): + return -1 - ql.log.debug(f"Convert emu_optname {emu_opt_name}:{emu_opt} to host platform based optname {emu_opt_name}:{optname}") + sock: Optional[ql_socket] = ql.os.fd[sockfd] - optval = ql.mem.read(optval_addr, optlen) - ql.os.fd[sockfd].setsockopt(level, optname, optval, None) + if sock is None: + return -1 - except UcError: - regreturn = -EFAULT + data = ql.mem.read(addr, addrlen) + print(f"data = {data.hex(' ')}") - except: - regreturn = -1 + abits = ql.arch.bits + endian = ql.arch.endian - return regreturn + sockaddr = make_sockaddr(abits, endian) + sockaddr_obj = sockaddr.from_buffer(data) + sa_family = sockaddr_obj.sa_family -def ql_syscall_shutdown(ql: Qiling, fd: int, how: int): - regreturn = 0 + dest = None + regreturn = -1 - if fd in range(NR_OPEN): - sock = ql.os.fd[fd] + if sa_family == AF_UNIX: + hpath, vpath = ql_unix_socket_path(ql, data[2:]) - if sock is not None: - try: - sock.shutdown(how) - except: - regreturn = -1 + ql.log.debug(f'Binding socket to "{vpath}"') + dest = hpath - return regreturn + elif sa_family == AF_INET: + sockaddr = make_sockaddr_in(abits, endian) + sockaddr_obj = sockaddr.from_buffer(data) + port = ntohs(ql, sockaddr_obj.sin_port) + host = inet_ntoa(sockaddr_obj.sin_addr.s_addr) -def ql_syscall_bind(ql: Qiling, bind_fd, bind_addr, bind_addrlen): - regreturn = 0 + if ql.os.bindtolocalhost: + host = '127.0.0.1' - if ql.arch.type == QL_ARCH.X8664: - data = ql.mem.read(bind_addr, 8) - else: - data = ql.mem.read(bind_addr, bind_addrlen) + if not ql.os.root and port <= 1024: + port = port + 8000 - sin_family = ql.unpack16(data[:2]) or ql.os.fd[bind_fd].family - port, host = struct.unpack(">HI", data[2:8]) - host = ql_bin_to_ip(host) + ql.log.debug(f'Binding socket to {host}:{port}') + dest = (host, port) - if not ql.os.root and port <= 1024: - port = port + 8000 + elif sa_family == AF_INET6 and ql.os.ipv6: + sockaddr_in6 = make_sockaddr_in(abits, endian) + sockaddr_obj = sockaddr_in6.from_buffer(data) - if sin_family == 1: - hpath, vpath = ql_unix_socket_path(ql, data[2:]) - ql.log.debug(f'binding socket to "{vpath}"') - ql.os.fd[bind_fd].bind(hpath) + port = ntohs(ql, sockaddr_obj.sin_port) + host = inet6_ntoa(sockaddr_obj.sin6_addr.s6_addr) - # need a proper fix, for now ipv4 comes first - elif sin_family == 2 and ql.os.bindtolocalhost == True: - host = "127.0.0.1" - ql.os.fd[bind_fd].bind((host, port)) + if ql.os.bindtolocalhost: + host = '::1' - # IPv4 should comes first - elif ql.os.ipv6 == True and sin_family == 10 and ql.os.bindtolocalhost == True: - host = "::1" - ql.os.fd[bind_fd].bind((host, port)) + if not ql.os.root and port <= 1024: + port = port + 8000 - elif ql.os.bindtolocalhost == False: - ql.os.fd[bind_fd].bind((host, port)) + ql.log.debug(f'Binding socket to {host}:{port}') + dest = (host, port) - else: - regreturn = -1 - - if ql.code: - regreturn = 0 - - if sin_family == 1: - 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)) + if dest is not None: + try: + sock.bind(dest) + except (ConnectionError, FileNotFoundError): + regreturn = -1 + else: + regreturn = 0 return regreturn def ql_syscall_getsockname(ql: Qiling, sockfd: int, addr: int, addrlenptr: int): if 0 <= sockfd < NR_OPEN: - socket = ql.os.fd[sockfd] + sock: Optional[ql_socket] = ql.os.fd[sockfd] + + if isinstance(sock, ql_socket): + abits = ql.arch.bits + endian = ql.arch.endian + + host, port = sock.getsockname() + + if sock.family == AF_INET: + sockaddr_in = make_sockaddr_in(abits, endian) - if isinstance(socket, ql_socket): - host, port = socket.getpeername() + with sockaddr_in.ref(ql.mem, addr) as obj: + obj.sin_family = AF_INET + obj.sin_port = htons(ql, port) + obj.sin_addr.s_addr = inet_aton(str(host)) - data = struct.pack("H", port) - data += ipaddress.ip_address(host).packed + elif sock.family == AF_INET6 and ql.os.ipv6: + sockaddr_in6 = make_sockaddr_in6(abits, endian) - addrlen = ql.mem.read_ptr(addrlenptr) + with sockaddr_in6.ref(ql.mem, addr) as obj: + obj.sin6_family = AF_INET6 + obj.sin6_port = htons(ql, port) + obj.sin6_addr.s6_addr = inet6_aton(str(host)) + + # FIXME: obj data should be written according to the value pointed by addrlenptr + # + # addrlen = ql.mem.read_ptr(addrlenptr) + # ql.mem.write(addr, data[:addrlen]) - ql.mem.write(addr, data[:addrlen]) regreturn = 0 else: - regreturn = -EPERM + regreturn = -1 else: - regreturn = -EPERM + regreturn = -1 + + ql.log.debug("getsockname(%d, %#x, %#x) = %d" % (sockfd, addr, addrlenptr, regreturn)) - ql.log.debug("getsockname(%d, 0x%x, 0x%x) = %d" % (sockfd, addr, addrlenptr, regreturn)) return regreturn def ql_syscall_getpeername(ql: Qiling, sockfd: int, addr: int, addrlenptr: int): if 0 <= sockfd < NR_OPEN: - socket = ql.os.fd[sockfd] + sock: Optional[ql_socket] = ql.os.fd[sockfd] + + if isinstance(sock, ql_socket): + abits = ql.arch.bits + endian = ql.arch.endian + + host, port = sock.getpeername() + + if sock.family == AF_INET: + sockaddr_in = make_sockaddr_in(abits, endian) - if isinstance(socket, ql_socket): - host, port = socket.getpeername() + with sockaddr_in.ref(ql.mem, addr) as obj: + obj.sin_family = int(sock.family) + obj.sin_port = htons(ql, port) + obj.sin_addr.s_addr = inet_aton(str(host)) - data = struct.pack("H", port) - data += ipaddress.ip_address(host).packed + elif sock.family == AF_INET6 and ql.os.ipv6: + sockaddr_in6 = make_sockaddr_in6(abits, endian) - addrlen = ql.mem.read_ptr(addrlenptr) + with sockaddr_in6.ref(ql.mem, addr) as obj: + obj.sin6_family = int(sock.family) + obj.sin6_port = htons(ql, port) + obj.sin6_addr.s6_addr = inet6_aton(str(host)) + + # FIXME: obj data should be written according to the value pointed by addrlenptr + # + # addrlen = ql.mem.read_ptr(addrlenptr) + # ql.mem.write(addr, data[:addrlen]) - ql.mem.write(addr, data[:addrlen]) regreturn = 0 else: - regreturn = -EPERM + regreturn = -1 else: - regreturn = -EPERM + regreturn = -1 + + ql.log.debug("getpeername(%d, %#x, %#x) = %d" % (sockfd, addr, addrlenptr, regreturn)) - ql.log.debug("getpeername(%d, 0x%x, 0x%x) = %d" % (sockfd, addr, addrlenptr, regreturn)) return regreturn @@ -408,65 +496,82 @@ def ql_syscall_listen(ql: Qiling, sockfd: int, backlog: int): if sockfd not in range(NR_OPEN): return -1 - sock = ql.os.fd[sockfd] + sock: Optional[ql_socket] = ql.os.fd[sockfd] if sock is None: return -1 try: sock.listen(backlog) - except: - if ql.verbose >= QL_VERBOSE.DEBUG: - raise - + except ConnectionError: return -1 return 0 -def ql_syscall_accept(ql: Qiling, accept_sockfd, accept_addr, accept_addrlen): - def inet_addr(ip): - ret = b'' - tmp = ip.split('.') - if len(tmp) != 4: - return ret - for i in tmp[ : : -1]: - ret += bytes([int(i)]) - return ret +def ql_syscall_accept(ql: Qiling, sockfd: int, addr: int, addrlen: int): + if sockfd not in range(NR_OPEN): + return -1 + + sock: Optional[ql_socket] = ql.os.fd[sockfd] + + if sock is None: + return -1 + try: - conn, address = ql.os.fd[accept_sockfd].accept() + conn, address = sock.accept() + except ConnectionError: + return -1 - if conn is None: - return -1 + if (conn is None) or (address is None): + return -1 + + idx = next((i for i in range(NR_OPEN) if ql.os.fd[i] is None), -1) + + if idx == -1: + return -1 - idx = next((i for i in range(NR_OPEN) if ql.os.fd[i] is None), -1) + ql.os.fd[idx] = conn + + if addr and addrlen: + abits = ql.arch.bits + endian = ql.arch.endian + + if conn.family == AF_INET: + sockaddr_in = make_sockaddr_in(abits, endian) + host, port = address + + with sockaddr_in.ref(ql.mem, addr) as obj: + obj.sin_family = AF_INET + obj.sin_port = htons(ql, port) + obj.sin_addr.s_addr = inet_aton(str(host)) + + objlen = sockaddr_in.sizeof() + + elif conn.family == AF_INET6 and ql.os.ipv6: + sockaddr_in6 = make_sockaddr_in6(abits, endian) + host, port = address + + with sockaddr_in6.ref(ql.mem, addr) as obj: + obj.sin6_family = AF_INET6 + obj.sin6_port = htons(ql, port) + obj.sin6_addr.s6_addr = inet6_aton(str(host)) + + objlen = sockaddr_in6.sizeof() - if idx == -1: - regreturn = -1 else: - ql.os.fd[idx] = conn - regreturn = idx - - if ql.code == None and accept_addr !=0 and accept_addrlen != 0: - tmp_buf = ql.pack16(conn.family) - tmp_buf += ql.pack16(address[1]) - tmp_buf += inet_addr(address[0]) - tmp_buf += b'\x00' * 8 - ql.mem.write(accept_addr, tmp_buf) - ql.mem.write_ptr(accept_addrlen, 16, 4) - except: - if ql.verbose >= QL_VERBOSE.DEBUG: - raise - regreturn = -1 + objlen = 0 - return regreturn + ql.mem.write_ptr(addrlen, objlen, 4) + + return idx def ql_syscall_recv(ql: Qiling, sockfd: int, buf: int, length: int, flags: int): if sockfd not in range(NR_OPEN): return -1 - sock = ql.os.fd[sockfd] + sock: Optional[ql_socket] = ql.os.fd[sockfd] if sock is None: return -1 @@ -486,74 +591,79 @@ def ql_syscall_send(ql: Qiling, sockfd: int, buf: int, length: int, flags: int): if sockfd not in range(NR_OPEN): return -1 - sock = ql.os.fd[sockfd] + sock: Optional[ql_socket] = ql.os.fd[sockfd] if sock is None: return -1 + content = ql.mem.read(buf, length) + try: - content = bytes(ql.mem.read(buf, length)) regreturn = sock.send(content, flags) - except: + except IOError: regreturn = 0 - ql.log.info(sys.exc_info()[0]) - - if ql.verbose >= QL_VERBOSE.DEBUG: - raise return regreturn def ql_syscall_recvmsg(ql: Qiling, sockfd: int, msg_addr: int, flags: int): - regreturn = 0 - if sockfd not in range(NR_OPEN) and ql.os.fd[sockfd] is not None: - msg = msghdr.load(ql, msg_addr) + if sockfd not in range(NR_OPEN): + return -1 - try: - data, ancdata, mflags, addr = ql.os.fd[sockfd].recvmsg(msg.msg_namelen, msg.msg_controllen, flags) - - # TODO: handle the addr - - iovec_addr = msg.msg_iov - has_written = 0 - for i in range(msg.msg_iovlen): - vec = iovec.load(ql, iovec_addr) - size = min(vec.iov_len, len(data) - has_written) - ql.mem.write( - vec.iov_base, - data[has_written: has_written + size] - ) - iovec_addr += ctypes.sizeof(iovec) - - cmsg_addr = msg.msg_control - for cmsg_level, cmsg_type, cmsg_data in ancdata: - cmsg = cmsghdr.load(ql, cmsg_addr) - cmsg.cmsg_len = len(cmsg_data) - cmsg.cmsg_level = cmsg_level - cmsg.cmsg_type = cmsg_type - cmsg_data_addr = cmsg_addr + ctypes.sizeof(cmsghdr) - - ql.mem.write(cmsg_data_addr, cmsg_data) - ql.mem.write(cmsg_addr, bytes(cmsg)) - - cmsg_addr += cmsg.cmsg_len - - msg.msg_flags = mflags - ql.mem.write(msg_addr, bytes(msg)) - - regreturn = len(data) - except OSError as e: - regreturn = -e.errno - else: - regreturn = -EBADF + sock: Optional[ql_socket] = ql.os.fd[sockfd] + + if sock is None: + return -1 + + abits = ql.arch.bits + endian = ql.arch.endian + + msghdr = make_msghdr(abits, endian) + msg = msghdr.load_from(ql.mem, msg_addr) + + try: + # TODO: handle the addr + data, ancdata, mflags, addr = sock.recvmsg(msg.msg_namelen, msg.msg_controllen, flags) + except ConnectionError: + return -1 + + iovec = make_iovec(abits, endian) + iovec_addr = msg.msg_iov + written = 0 + + for _ in range(msg.msg_iovlen): + with iovec.ref(ql.mem, iovec_addr) as obj: + size = min(obj.iov_len, len(data) - written) + ql.mem.write(obj.iov_base, data[written:written + size]) + + written += size + iovec_addr += iovec.sizeof() + + cmsghdr = make_cmsghdr(abits, endian) + cmsg_addr = msg.msg_control + + for cmsg_level, cmsg_type, cmsg_data in ancdata: + with cmsghdr.ref(ql.mem, cmsg_addr) as obj: + obj.cmsg_len = len(cmsg_data) + obj.cmsg_level = cmsg_level + obj.cmsg_type = cmsg_type + + cmsg_addr += cmsghdr.sizeof() + + ql.mem.write(cmsg_addr, cmsg_data) + cmsg_addr += len(cmsg_data) + + msg.msg_flags = mflags + msg.save_to(ql.mem, msg_addr) + + return len(data) - return regreturn def ql_syscall_recvfrom(ql: Qiling, sockfd: int, buf: int, length: int, flags: int, addr: int, addrlen: int): if sockfd not in range(NR_OPEN): return -1 - sock = ql.os.fd[sockfd] + sock: Optional[ql_socket] = ql.os.fd[sockfd] if sock is None: return -1 @@ -564,39 +674,73 @@ def ql_syscall_recvfrom(ql: Qiling, sockfd: int, buf: int, length: int, flags: i if sock.socktype == SOCK_STREAM: return ql_syscall_recv(ql, sockfd, buf, length, flags) - tmp_buf, tmp_addr = sock.recvfrom(length, flags) + data_buf, address = sock.recvfrom(length, flags) - if tmp_buf: + if data_buf: ql.log.debug("recvfrom() CONTENT:") - ql.log.debug("%s" % tmp_buf) + ql.log.debug("%s" % data_buf) sin_family = int(sock.family) - sockaddr_out = struct.pack("H", tmp_addr[1]) - sockaddr_out += ipaddress.ip_address(tmp_addr[0]).packed - addrlen = ql.mem.read_ptr(addrlen) - sockaddr_out = sockaddr_out[:addrlen] + objlen = 0 - if addr: - ql.mem.write(addr, sockaddr_out) - ql.mem.write(buf, tmp_buf) + # FIXME: only write up to sockaddr_out bytes of obj content? + # + # addrlen = ql.mem.read_ptr(addrlen) + # sockaddr_out = sockaddr_out[:addrlen] - return len(tmp_buf) + ql.mem.write(buf, data_buf) + return len(data_buf) -def ql_syscall_sendto(ql: Qiling, sockfd: int, sendto_buf, sendto_len, sendto_flags, sendto_addr, sendto_addrlen): + +def ql_syscall_sendto(ql: Qiling, sockfd: int, buf: int, length: int, flags: int, addr: int, addrlen: int): if sockfd not in range(NR_OPEN): return -1 - sock = ql.os.fd[sockfd] + sock: Optional[ql_socket] = ql.os.fd[sockfd] if sock is None: return -1 @@ -605,42 +749,57 @@ def ql_syscall_sendto(ql: Qiling, sockfd: int, sendto_buf, sendto_len, sendto_fl # For x8664, sendto() is called finally when calling send() in TCP communications if sock.socktype == SOCK_STREAM: - return ql_syscall_send(ql, sockfd, sendto_buf, sendto_len, sendto_flags) + return ql_syscall_send(ql, sockfd, buf, length, flags) + + tmp_buf = ql.mem.read(buf, length) + + ql.log.debug("sendto() CONTENT:") + ql.log.debug("%s" % tmp_buf) + + data = ql.mem.read(addr, addrlen) + print(f"data = {data.hex(' ')}") + + abits = ql.arch.bits + endian = ql.arch.endian + sockaddr = make_sockaddr(abits, endian) + sockaddr_obj = sockaddr.from_buffer(data) + + sa_family = sockaddr_obj.sa_family + + dest = None regreturn = 0 - try: - ql.log.debug("debug sendto() start") - tmp_buf = ql.mem.read(sendto_buf, sendto_len) + if sa_family == AF_UNIX: + hpath, vpath = ql_unix_socket_path(ql, data[2:]) - if ql.arch.type== QL_ARCH.X8664: - data = ql.mem.read(sendto_addr, 8) - else: - data = ql.mem.read(sendto_addr, sendto_addrlen) + ql.log.debug(f'Sending {len(tmp_buf):d} bytes to {vpath}') + dest = hpath - sin_family, = struct.unpack("HI", data[2:8]) - host = ql_bin_to_ip(host) + elif sa_family == AF_INET: + sockaddr = make_sockaddr_in(abits, endian) + sockaddr_obj = sockaddr.from_buffer(data) - ql.log.debug("fd is " + str(sockfd)) - ql.log.debug("sendto() CONTENT:") - ql.log.debug("%s" % tmp_buf) - ql.log.debug("sendto() flag is " + str(sendto_flags)) - ql.log.debug("sendto() len is " + str(sendto_len)) + port = ntohs(ql, sockaddr_obj.sin_port) + host = inet_ntoa(sockaddr_obj.sin_addr.s_addr) - if sin_family == 1: - hpath, vpath = ql_unix_socket_path(ql, data[2:]) + ql.log.debug(f'Sending {len(tmp_buf):d} bytes to {host}:{port}') + dest = (host, port) - 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)) - ql.log.debug("debug sendto end") - except: - ql.log.debug(sys.exc_info()[0]) + elif sa_family == AF_INET6 and ql.os.ipv6: + sockaddr_in6 = make_sockaddr_in(abits, endian) + sockaddr_obj = sockaddr_in6.from_buffer(data) - if ql.verbose >= QL_VERBOSE.DEBUG: - raise + port = ntohs(ql, sockaddr_obj.sin_port) + host = inet6_ntoa(sockaddr_obj.sin6_addr.s6_addr) + + ql.log.debug(f'Sending to {host}:{port}') + dest = (host, port) + + if dest is not None: + try: + regreturn = sock.sendto(tmp_buf, flags, dest) + except (ConnectionError, FileNotFoundError): + regreturn = 0 return regreturn From 1516f520cd697e751e461cb33342ca45b65811e0 Mon Sep 17 00:00:00 2001 From: elicn Date: Sun, 20 Nov 2022 14:35:04 +0200 Subject: [PATCH 6/9] Fix munmap bugs --- qiling/os/posix/syscall/mman.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qiling/os/posix/syscall/mman.py b/qiling/os/posix/syscall/mman.py index 5e3eae0bf..08b965727 100755 --- a/qiling/os/posix/syscall/mman.py +++ b/qiling/os/posix/syscall/mman.py @@ -13,10 +13,10 @@ def ql_syscall_munmap(ql: Qiling, addr: int, length: int): # get all mapped fd with flag MAP_SHARED and we definitely dont want to wipe out share library - mapped_fd = [fd for fd in ql.os.fd if fd != 0 and isinstance(fd, ql_file) and fd._is_map_shared and not (fd.name.endswith(".so") or fd.name.endswith(".dylib"))] + mapped_fd = [fd for fd in ql.os.fd if isinstance(fd, ql_file) and fd._is_map_shared and not (fd.name.endswith(".so") or fd.name.endswith(".dylib"))] if mapped_fd: - all_mem_info = [_mem_info for _, _, _, _mem_info in ql.mem.map_info if _mem_info not in ("[mapped]", "[stack]", "[hook_mem]")] + all_mem_info = [_mem_info for _, _, _, _mem_info, _ in ql.mem.map_info if _mem_info not in ("[mapped]", "[stack]", "[hook_mem]")] for _fd in mapped_fd: if _fd.name in [each.split()[-1] for each in all_mem_info]: From 4180070c43cc0f6f723b8649ca93e1483e50a86c Mon Sep 17 00:00:00 2001 From: elicn Date: Sun, 20 Nov 2022 14:40:43 +0200 Subject: [PATCH 7/9] Opportunistic code cleanup and PEP8-friendly tweaks --- qiling/os/posix/syscall/mman.py | 20 ++++----- qiling/os/qnx/message.py | 58 +++++++++++++----------- qiling/os/qnx/syscall.py | 78 ++++++++++++++++++++------------- qiling/os/struct.py | 18 ++++---- qiling/os/windows/structs.py | 11 ++++- tests/test_elf_multithread.py | 49 ++++++++------------- 6 files changed, 124 insertions(+), 110 deletions(-) diff --git a/qiling/os/posix/syscall/mman.py b/qiling/os/posix/syscall/mman.py index 08b965727..4c9a4edcc 100755 --- a/qiling/os/posix/syscall/mman.py +++ b/qiling/os/posix/syscall/mman.py @@ -10,6 +10,7 @@ from qiling.os.filestruct import ql_file from qiling.os.posix.const_mapping import * + def ql_syscall_munmap(ql: Qiling, addr: int, length: int): # get all mapped fd with flag MAP_SHARED and we definitely dont want to wipe out share library @@ -26,7 +27,7 @@ def ql_syscall_munmap(ql: Qiling, addr: int, length: int): _fd.lseek(_fd._mapped_offset) _fd.write(_buff) - length = ((length + 0x1000 - 1) // 0x1000) * 0x1000 + length = ql.mem.align_up(length) ql.mem.unmap(addr, length) return 0 @@ -148,15 +149,12 @@ def syscall_mmap_impl(ql: Qiling, addr: int, mlen: int, prot: int, flags: int, f def ql_syscall_old_mmap(ql: Qiling, struct_mmap_args: int): # according to the linux kernel this is only for the ia32 compatibility - def __read_int(address: int) -> int: - return ql.unpack32(ql.mem.read(address, 4)) - - addr = __read_int(struct_mmap_args + 0 * 4) - length = __read_int(struct_mmap_args + 1 * 4) - prot = __read_int(struct_mmap_args + 2 * 4) - flags = __read_int(struct_mmap_args + 3 * 4) - fd = __read_int(struct_mmap_args + 4 * 4) - offset = __read_int(struct_mmap_args + 5 * 4) + addr = ql.mem.read_ptr(struct_mmap_args + 0 * 4, 4) + length = ql.mem.read_ptr(struct_mmap_args + 1 * 4, 4) + prot = ql.mem.read_ptr(struct_mmap_args + 2 * 4, 4) + flags = ql.mem.read_ptr(struct_mmap_args + 3 * 4, 4) + fd = ql.mem.read_ptr(struct_mmap_args + 4 * 4, 4) + offset = ql.mem.read_ptr(struct_mmap_args + 5 * 4, 4) return syscall_mmap_impl(ql, addr, length, prot, flags, fd, offset, 0) @@ -168,6 +166,7 @@ def ql_syscall_mmap(ql: Qiling, addr: int, length: int, prot: int, flags: int, f def ql_syscall_mmap2(ql: Qiling, addr: int, length: int, prot: int, flags: int, fd: int, pgoffset: int): return syscall_mmap_impl(ql, addr, length, prot, flags, fd, pgoffset, 2) + def ql_syscall_shmget(ql: Qiling, key: int, size: int, shmflg: int): if shmflg & IPC_CREAT: if shmflg & IPC_EXCL: @@ -181,6 +180,7 @@ def ql_syscall_shmget(ql: Qiling, key: int, size: int, shmflg: int): if key not in ql.os._shms: return ENOENT + def ql_syscall_shmat(ql: Qiling, shmid: int, shmaddr: int, shmflg: int): # shmid == key # dummy implementation diff --git a/qiling/os/qnx/message.py b/qiling/os/qnx/message.py index f4686628c..3c82f5390 100644 --- a/qiling/os/qnx/message.py +++ b/qiling/os/qnx/message.py @@ -1,17 +1,14 @@ #!/usr/bin/env python3 -# +# # Cross Platform and Multi Architecture Advanced Binary Emulation Framework # import os from struct import pack, unpack from ctypes import c_int32 -from binascii import hexlify from qiling import Qiling -from qiling.os.filestruct import ql_file -from qiling.os.mapper import QlFsMappedObject from qiling.os.qnx.const import IO_FLAG_MASK, PAGESIZE, S_IFMT -from qiling.os.qnx.helpers import get_message_body, QnxConn +from qiling.os.qnx.helpers import get_message_body from qiling.os.qnx.types import file_access, file_stats, file_types, file_open_flags, file_sharing_modes, io_connect_eflag, io_connect_ioflag, io_connect_subtypes, lseek_whence, mem_ctrl_subtypes, mmap_flags, pathconf_names, sysconf_conditions, sysconf_consts, sysconf_names, sysconf_subtypes from qiling.os.posix.const_mapping import _flags_mapping, mmap_prot_mapping from qiling.os.posix.syscall import ql_syscall_close, ql_syscall_fstat, ql_syscall_lseek, ql_syscall_mmap, ql_syscall_open, ql_syscall_read, ql_syscall_write @@ -19,14 +16,16 @@ # TODO: move this to qiling.os.qnx.const? _IO_COMBINE_FLAG = 0x8000 -def ql_qnx_msg_io_close(ql:Qiling, coid, smsg, sparts, rmsg, rparts, *args, **kw): + +def ql_qnx_msg_io_close(ql: Qiling, coid, smsg, sparts, rmsg, rparts, *args, **kw): fd = ql.os.connections[coid].fd ql.os.connections[coid].fd = None ql.log.debug(f'msg_io_close(coid = {coid} => fd = {fd})') return ql_syscall_close(ql, fd) + # lib/c/support/_connect_ctrl.c::_connect_io() -def ql_qnx_msg_io_connect(ql:Qiling, coid, smsg, sparts, rmsg, rparts, *args, **kw): +def ql_qnx_msg_io_connect(ql: Qiling, coid, smsg, sparts, rmsg, rparts, *args, **kw): # first iov_t iov_base = ql.unpack32(ql.mem.read(smsg, 4)) iov_len = ql.unpack32(ql.mem.read(smsg + 4, 4)) @@ -40,19 +39,19 @@ def ql_qnx_msg_io_connect(ql:Qiling, coid, smsg, sparts, rmsg, rparts, *args, ** real_path = ql.os.path.transform_to_real_path(path) # check parameters assert (type, reply_max, entry_max, key, handle, zero, eflag, extra_type) == (0x100, 0xa18, 0x10, 0, 0, 0, 0, 0), "io_connect message is wrong" - + if not subtype in io_connect_subtypes: raise NotImplementedError(f'msg_io_connect subtype {subtype} not implemented') - + if not file_type in file_types: raise NotImplementedError(f'msg_io_connect file_type {file_type} not implemented') - + if not sflag in file_sharing_modes: raise NotImplementedError(f'msg_io_connect sharing flag {sflag} not implemented') - + if access != 0 and not access in file_access: raise NotImplementedError(f'msg_io_connect access {access} not implemented') - + ioflag_lo = ioflag & IO_FLAG_MASK ioflag_hi = ioflag & (~IO_FLAG_MASK) real_mode = mode & (~S_IFMT) @@ -133,8 +132,9 @@ def ql_qnx_msg_io_connect(ql:Qiling, coid, smsg, sparts, rmsg, rparts, *args, ** ql.mem.write(iov_base, pack(" 0: raise NotImplementedError("mmap with IOV not implemented") - + assert (c_int32(sparts).value, c_int32(rparts).value) == (-56, -24), "input/output sizes are wrong" assert (type, zero, reserved1) == (0x040, 0, 0), "mem_map message is wrong" # map message fd to underlying fd - + if fd > 0: fd = ql.os.connections[fd].fd - + ql.log.debug(f'mem_map(addr = 0x{addr:x}, len = 0x{len:x}, prot = {mmap_prot_mapping(prot)}, flags = {_flags_mapping(flags, mmap_flags)}, fd = {fd}, preload = 0x{preload:x}, align = 0x{align:x}, offset = 0x{offset:x})') # map memory ret = ql_syscall_mmap(ql, addr, len, prot, flags, fd, offset) @@ -218,7 +223,8 @@ def ql_qnx_msg_mem_map(ql:Qiling, coid, smsg, sparts, rmsg, rparts, *args, **kw) ql.mem.write(rmsg, pack(" None: """Store structure contents to a specified memory address. @@ -43,7 +41,7 @@ def save_to(self, mem: QlMemoryManager, address: int) -> None: mem.write(address, data) @classmethod - def load_from(cls: Type[T], mem: QlMemoryManager, address: int) -> T: + def load_from(cls, mem: QlMemoryManager, address: int): """Construct and populate a structure from saved contents. Args: @@ -58,7 +56,7 @@ def load_from(cls: Type[T], mem: QlMemoryManager, address: int) -> T: return cls.from_buffer(data) @classmethod - def volatile_ref(cls: Type[T], mem: QlMemoryManager, address: int) -> T: + def volatile_ref(cls, mem: QlMemoryManager, address: int): """Refer to a memory location as a volatile structure variable. Args: @@ -77,7 +75,7 @@ def volatile_ref(cls: Type[T], mem: QlMemoryManager, address: int) -> T: ... if p.x > 10: # x value is read directly from memory ... p.x = 10 # x value is written directly to memory ... # y value in memory remains unchanged - >>> + >>> """ # map all structure field names to their types @@ -134,12 +132,11 @@ def __setattr__(self, name: str, value: Any) -> None: # set attribute value super().__setattr__(name, value) - return VolatileStructRef() @classmethod @contextmanager - def ref(cls: Type[T], mem: QlMemoryManager, address: int) -> Iterator[T]: + def ref(cls, mem: QlMemoryManager, address: int): """A structure context manager to facilitate updating structure contents. On context enter, a structure is created and populated from the specified memory @@ -263,14 +260,15 @@ class AlignedUnion(ctypes.Union): return AlignedUnion + def get_native_type(archbits: int) -> Type[ctypes._SimpleCData]: """Select a ctypes integer type whose size matches the emulated architecture native size. """ __type = { - 32 : ctypes.c_uint32, - 64 : ctypes.c_uint64 + 32: ctypes.c_uint32, + 64: ctypes.c_uint64 } return __type[archbits] diff --git a/qiling/os/windows/structs.py b/qiling/os/windows/structs.py index 258637c34..170f534bc 100644 --- a/qiling/os/windows/structs.py +++ b/qiling/os/windows/structs.py @@ -188,6 +188,7 @@ class LARGE_INTEGER(ctypes.Union): ('QuadPart', ctypes.c_int64) ) + # see: # https://www.geoffchappell.com/studies/windows/km/ntoskrnl/structs/kuser_shared_data/index.htm # https://doxygen.reactos.org/d7/deb/xdk_2ketypes_8h_source.html#l01155 @@ -997,6 +998,7 @@ class FILETIME(struct.BaseStructEL): ('dwHighDateTime', ctypes.c_int32) ) + # https://docs.microsoft.com/en-us/windows/console/coord-str class COORD(struct.BaseStructEL): _fields_ = ( @@ -1004,6 +1006,7 @@ class COORD(struct.BaseStructEL): ('Y', ctypes.c_uint16) ) + # https://docs.microsoft.com/en-us/windows/console/small-rect-str class SMALL_RECT(struct.BaseStructEL): _fields_ = ( @@ -1013,6 +1016,7 @@ class SMALL_RECT(struct.BaseStructEL): ('Bottom', ctypes.c_uint16) ) + # https://docs.microsoft.com/en-us/windows/console/console-screen-buffer-info-str class CONSOLE_SCREEN_BUFFER_INFO(struct.BaseStructEL): _fields_ = ( @@ -1023,6 +1027,7 @@ class CONSOLE_SCREEN_BUFFER_INFO(struct.BaseStructEL): ('dwMaximumWindowSize', COORD) ) + # https://docs.microsoft.com/en-us/windows/win32/api/winternl/nf-winternl-ntqueryinformationprocess def make_process_basic_info(archbits: int): native_type = struct.get_native_type(archbits) @@ -1040,6 +1045,7 @@ class PROCESS_BASIC_INFORMATION(Struct): return PROCESS_BASIC_INFORMATION + # https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-osversioninfoa def make_os_version_info(archbits: int, *, wide: bool): Struct = struct.get_aligned_struct(archbits) @@ -1237,7 +1243,7 @@ def isFree(self) -> bool: # _fields_ = ( # ('OriginalFirstThunk', ctypes.c_uint32), # ('TimeDateStamp', ctypes.c_uint32), -# ('ForwarderChain', ctypes.c_uint32), +# ('ForwarderChain', ctypes.c_uint32), # ('Name', ctypes.c_uint32), # ('FirstThunk', ctypes.c_uint32) # ) @@ -1330,13 +1336,14 @@ class sockaddr_in(ctypes.BigEndianStructure): return sockaddr_in + # https://docs.microsoft.com/en-us/windows/win32/winsock/sockaddr-2 def make_sockaddr_in6(): # https://docs.microsoft.com/en-us/windows/win32/api/in6addr/ns-in6addr-in6_addr class in6_addr(ctypes.BigEndianStructure): _fields_ = ( - ('Byte', ctypes.c_uint8 * 16) + ('Byte', ctypes.c_uint8 * 16), ) class sockaddr_in6(ctypes.BigEndianStructure): diff --git a/tests/test_elf_multithread.py b/tests/test_elf_multithread.py index 5b5a67ecc..2121deb46 100644 --- a/tests/test_elf_multithread.py +++ b/tests/test_elf_multithread.py @@ -15,7 +15,7 @@ class ELFTest(unittest.TestCase): @unittest.skipIf(platform.system() == "Darwin" and platform.machine() == "arm64", 'darwin host') def test_elf_linux_execve_x8664(self): - ql = Qiling(["../examples/rootfs/x8664_linux/bin/posix_syscall_execve"], "../examples/rootfs/x8664_linux", verbose=QL_VERBOSE.DEBUG) + ql = Qiling(["../examples/rootfs/x8664_linux/bin/posix_syscall_execve"], "../examples/rootfs/x8664_linux", verbose=QL_VERBOSE.DEBUG) ql.run() for key, value in ql.loader.env.items(): @@ -27,12 +27,8 @@ def test_elf_linux_execve_x8664(self): del QL_TEST del ql - def test_elf_linux_cloexec_x8664(self): - ql = Qiling(["../examples/rootfs/x8664_linux/bin/x8664_cloexec_test"], - "../examples/rootfs/x8664_linux", - verbose=QL_VERBOSE.DEBUG, - multithread=True) + ql = Qiling(["../examples/rootfs/x8664_linux/bin/x8664_cloexec_test"], "../examples/rootfs/x8664_linux", verbose=QL_VERBOSE.DEBUG, multithread=True) filename = 'output.txt' err = ql_file.open(filename, os.O_RDWR | os.O_CREAT, 0o777) @@ -51,7 +47,6 @@ def test_elf_linux_cloexec_x8664(self): del ql - def test_multithread_elf_linux_x86(self): def check_write(ql, write_fd, write_buf, write_count, *args, **kw): nonlocal buf_out @@ -70,7 +65,6 @@ def check_write(ql, write_fd, write_buf, write_count, *args, **kw): del ql - def test_multithread_elf_linux_arm64(self): def check_write(ql, write_fd, write_buf, write_count, *args, **kw): nonlocal buf_out @@ -89,7 +83,6 @@ def check_write(ql, write_fd, write_buf, write_count, *args, **kw): del ql - def test_multithread_elf_linux_x8664(self): def check_write(ql, write_fd, write_buf, write_count, *args, **kw): nonlocal buf_out @@ -108,7 +101,6 @@ def check_write(ql, write_fd, write_buf, write_count, *args, **kw): del ql - def test_multithread_elf_linux_mips32eb(self): def check_write(ql, write_fd, write_buf, write_count, *args, **kw): nonlocal buf_out @@ -119,7 +111,7 @@ def check_write(ql, write_fd, write_buf, write_count, *args, **kw): except: pass buf_out = None - ql = Qiling(["../examples/rootfs/mips32_linux/bin/mips32_multithreading"], "../examples/rootfs/mips32_linux", multithread=True, verbose=QL_VERBOSE.DEBUG) + ql = Qiling(["../examples/rootfs/mips32_linux/bin/mips32_multithreading"], "../examples/rootfs/mips32_linux", verbose=QL_VERBOSE.DEBUG, multithread=True) ql.os.set_syscall("write", check_write, QL_INTERCEPT.ENTER) ql.run() @@ -127,7 +119,6 @@ def check_write(ql, write_fd, write_buf, write_count, *args, **kw): del ql - def test_multithread_elf_linux_mips32el(self): def check_write(ql, write_fd, write_buf, write_count, *args, **kw): nonlocal buf_out @@ -146,7 +137,6 @@ def check_write(ql, write_fd, write_buf, write_count, *args, **kw): del ql - def test_multithread_elf_linux_arm(self): def check_write(ql, write_fd, write_buf, write_count, *args, **kw): nonlocal buf_out @@ -184,7 +174,6 @@ def check_write(ql, write_fd, write_buf, write_count, *args, **kw): # del ql - def test_tcp_elf_linux_x86(self): def check_write(ql, write_fd, write_buf, write_count, *args, **kw): try: @@ -194,7 +183,7 @@ def check_write(ql, write_fd, write_buf, write_count, *args, **kw): ql.buf_out = buf except: pass - ql = Qiling(["../examples/rootfs/x86_linux/bin/x86_tcp_test","20001"], "../examples/rootfs/x86_linux", multithread=True) + ql = Qiling(["../examples/rootfs/x86_linux/bin/x86_tcp_test", "20001"], "../examples/rootfs/x86_linux", multithread=True, verbose=QL_VERBOSE.DEBUG) ql.os.set_syscall("write", check_write, QL_INTERCEPT.ENTER) ql.run() @@ -202,7 +191,6 @@ def check_write(ql, write_fd, write_buf, write_count, *args, **kw): del ql - def test_tcp_elf_linux_x8664(self): def check_write(ql, write_fd, write_buf, write_count, *args, **kw): try: @@ -212,7 +200,7 @@ def check_write(ql, write_fd, write_buf, write_count, *args, **kw): ql.buf_out = buf except: pass - ql = Qiling(["../examples/rootfs/x8664_linux/bin/x8664_tcp_test","20002"], "../examples/rootfs/x8664_linux", multithread=True) + ql = Qiling(["../examples/rootfs/x8664_linux/bin/x8664_tcp_test", "20002"], "../examples/rootfs/x8664_linux", multithread=True, verbose=QL_VERBOSE.DEBUG) ql.os.set_syscall("write", check_write, QL_INTERCEPT.ENTER) ql.run() @@ -220,7 +208,6 @@ def check_write(ql, write_fd, write_buf, write_count, *args, **kw): del ql - def test_tcp_elf_linux_arm(self): def check_write(ql, write_fd, write_buf, write_count, *args, **kw): try: @@ -230,7 +217,7 @@ def check_write(ql, write_fd, write_buf, write_count, *args, **kw): ql.buf_out = buf except: pass - ql = Qiling(["../examples/rootfs/arm_linux/bin/arm_tcp_test","20003"], "../examples/rootfs/arm_linux", multithread=True) + ql = Qiling(["../examples/rootfs/arm_linux/bin/arm_tcp_test", "20003"], "../examples/rootfs/arm_linux", multithread=True, verbose=QL_VERBOSE.DEBUG) ql.os.set_syscall("write", check_write, QL_INTERCEPT.ENTER) ql.run() @@ -248,7 +235,7 @@ def check_write(ql, write_fd, write_buf, write_count, *args, **kw): ql.buf_out = buf except: pass - ql = Qiling(["../examples/rootfs/arm64_linux/bin/arm64_tcp_test","20004"], "../examples/rootfs/arm64_linux", multithread=True) + ql = Qiling(["../examples/rootfs/arm64_linux/bin/arm64_tcp_test", "20004"], "../examples/rootfs/arm64_linux", multithread=True, verbose=QL_VERBOSE.DEBUG) ql.os.set_syscall("write", check_write, QL_INTERCEPT.ENTER) ql.run() @@ -266,7 +253,7 @@ def check_write(ql, write_fd, write_buf, write_count, *args, **kw): ql.buf_out = buf except: pass - ql = Qiling(["../examples/rootfs/armeb_linux/bin/armeb_tcp_test","20003"], "../examples/rootfs/armeb_linux", multithread=True) + ql = Qiling(["../examples/rootfs/armeb_linux/bin/armeb_tcp_test", "20003"], "../examples/rootfs/armeb_linux", multithread=True, verbose=QL_VERBOSE.DEBUG) ql.os.set_syscall("write", check_write, QL_INTERCEPT.ENTER) ql.run() @@ -276,13 +263,13 @@ def check_write(ql, write_fd, write_buf, write_count, *args, **kw): def test_tcp_elf_linux_mips32eb(self): - ql = Qiling(["../examples/rootfs/mips32_linux/bin/mips32_tcp_test","20005"], "../examples/rootfs/mips32_linux", multithread=True) + ql = Qiling(["../examples/rootfs/mips32_linux/bin/mips32_tcp_test", "20005"], "../examples/rootfs/mips32_linux", multithread=True, verbose=QL_VERBOSE.DEBUG) ql.run() del ql def test_tcp_elf_linux_mips32el(self): - ql = Qiling(["../examples/rootfs/mips32el_linux/bin/mips32el_tcp_test","20005"], "../examples/rootfs/mips32el_linux", multithread=True) + ql = Qiling(["../examples/rootfs/mips32el_linux/bin/mips32el_tcp_test", "20005"], "../examples/rootfs/mips32el_linux", multithread=True, verbose=QL_VERBOSE.DEBUG) ql.run() del ql @@ -297,7 +284,7 @@ def check_write(ql, write_fd, write_buf, write_count, *args, **kw): except: pass - ql = Qiling(["../examples/rootfs/x86_linux/bin/x86_udp_test","20007"], "../examples/rootfs/x86_linux", multithread=True) + ql = Qiling(["../examples/rootfs/x86_linux/bin/x86_udp_test", "20007"], "../examples/rootfs/x86_linux", multithread=True, verbose=QL_VERBOSE.DEBUG) ql.os.set_syscall("write", check_write, QL_INTERCEPT.ENTER) ql.run() @@ -316,7 +303,7 @@ def check_write(ql, write_fd, write_buf, write_count, *args, **kw): except: pass - ql = Qiling(["../examples/rootfs/x8664_linux/bin/x8664_udp_test","20008"], "../examples/rootfs/x8664_linux", multithread=True) + ql = Qiling(["../examples/rootfs/x8664_linux/bin/x8664_udp_test", "20008"], "../examples/rootfs/x8664_linux", multithread=True, verbose=QL_VERBOSE.DEBUG) ql.os.set_syscall("write", check_write, QL_INTERCEPT.ENTER) ql.run() @@ -334,7 +321,7 @@ def check_write(ql, write_fd, write_buf, write_count, *args, **kw): except: pass - ql = Qiling(["../examples/rootfs/arm64_linux/bin/arm64_udp_test","20009"], "../examples/rootfs/arm64_linux", multithread=True) + ql = Qiling(["../examples/rootfs/arm64_linux/bin/arm64_udp_test", "20009"], "../examples/rootfs/arm64_linux", multithread=True, verbose=QL_VERBOSE.DEBUG) ql.os.set_syscall("write", check_write, QL_INTERCEPT.ENTER) ql.run() @@ -352,7 +339,7 @@ def check_write(ql, write_fd, write_buf, write_count, *args, **kw): except: pass - ql = Qiling(["../examples/rootfs/armeb_linux/bin/armeb_udp_test","20010"], "../examples/rootfs/armeb_linux", multithread=True) + ql = Qiling(["../examples/rootfs/armeb_linux/bin/armeb_udp_test", "20010"], "../examples/rootfs/armeb_linux", multithread=True, verbose=QL_VERBOSE.DEBUG) ql.os.set_syscall("write", check_write, QL_INTERCEPT.ENTER) ql.run() @@ -362,7 +349,7 @@ def check_write(ql, write_fd, write_buf, write_count, *args, **kw): def test_http_elf_linux_x8664(self): def picohttpd(): - ql = Qiling(["../examples/rootfs/x8664_linux/bin/picohttpd","12911"], "../examples/rootfs/x8664_linux", multithread=True, verbose=QL_VERBOSE.DEBUG) + ql = Qiling(["../examples/rootfs/x8664_linux/bin/picohttpd", "12911"], "../examples/rootfs/x8664_linux", multithread=True, verbose=QL_VERBOSE.DEBUG) ql.run() picohttpd_therad = threading.Thread(target=picohttpd, daemon=True) @@ -377,7 +364,7 @@ def picohttpd(): def test_http_elf_linux_arm(self): def picohttpd(): - ql = Qiling(["../examples/rootfs/arm_linux/bin/picohttpd","12912"], "../examples/rootfs/arm_linux", multithread=True, verbose=QL_VERBOSE.DEBUG) + ql = Qiling(["../examples/rootfs/arm_linux/bin/picohttpd", "12912"], "../examples/rootfs/arm_linux", multithread=True, verbose=QL_VERBOSE.DEBUG) ql.run() picohttpd_therad = threading.Thread(target=picohttpd, daemon=True) @@ -392,14 +379,14 @@ def picohttpd(): def test_http_elf_linux_armeb(self): def picohttpd(): - ql = Qiling(["../examples/rootfs/armeb_linux/bin/picohttpd","12913"], "../examples/rootfs/armeb_linux", multithread=True, verbose=QL_VERBOSE.DEBUG) + ql = Qiling(["../examples/rootfs/armeb_linux/bin/picohttpd", "12913"], "../examples/rootfs/armeb_linux", multithread=True, verbose=QL_VERBOSE.DEBUG) ql.run() picohttpd_thread = threading.Thread(target=picohttpd, daemon=True) picohttpd_thread.start() time.sleep(1) - + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: s.connect(("localhost", 12913)) s.sendall(b"GET / HTTP/1.1\r\nHost: 127.0.0.1:12913\r\nUser-Agent: curl/7.74.0\r\nAccept: */*\r\n\r\n") From c9447f9df4ba5346307c685d41dbb94efea16a95 Mon Sep 17 00:00:00 2001 From: elicn Date: Sun, 20 Nov 2022 18:26:35 +0200 Subject: [PATCH 8/9] Remove forgotten debuggin residuals --- qiling/os/posix/syscall/socket.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/qiling/os/posix/syscall/socket.py b/qiling/os/posix/syscall/socket.py index 21f46b4ef..1699a817a 100644 --- a/qiling/os/posix/syscall/socket.py +++ b/qiling/os/posix/syscall/socket.py @@ -136,7 +136,6 @@ def ql_syscall_connect(ql: Qiling, sockfd: int, addr: int, addrlen: int): return -1 data = ql.mem.read(addr, addrlen) - print(f"data = {data.hex(' ')}") abits = ql.arch.bits endian = ql.arch.endian @@ -346,7 +345,6 @@ def ql_syscall_bind(ql: Qiling, sockfd: int, addr: int, addrlen: int): return -1 data = ql.mem.read(addr, addrlen) - print(f"data = {data.hex(' ')}") abits = ql.arch.bits endian = ql.arch.endian @@ -757,7 +755,6 @@ def ql_syscall_sendto(ql: Qiling, sockfd: int, buf: int, length: int, flags: int ql.log.debug("%s" % tmp_buf) data = ql.mem.read(addr, addrlen) - print(f"data = {data.hex(' ')}") abits = ql.arch.bits endian = ql.arch.endian From 6b2cdf47a0b73ed80c318199d07d8e634c74595c Mon Sep 17 00:00:00 2001 From: elicn Date: Mon, 21 Nov 2022 10:55:03 +0200 Subject: [PATCH 9/9] Improve AF_UNIX sockets support --- qiling/os/posix/syscall/socket.py | 216 +++++++++++++++++++----------- 1 file changed, 141 insertions(+), 75 deletions(-) diff --git a/qiling/os/posix/syscall/socket.py b/qiling/os/posix/syscall/socket.py index 1699a817a..7f3c96e78 100644 --- a/qiling/os/posix/syscall/socket.py +++ b/qiling/os/posix/syscall/socket.py @@ -407,87 +407,133 @@ def ql_syscall_bind(ql: Qiling, sockfd: int, addr: int, addrlen: int): def ql_syscall_getsockname(ql: Qiling, sockfd: int, addr: int, addrlenptr: int): - if 0 <= sockfd < NR_OPEN: - sock: Optional[ql_socket] = ql.os.fd[sockfd] + if sockfd not in range(NR_OPEN): + return -1 - if isinstance(sock, ql_socket): - abits = ql.arch.bits - endian = ql.arch.endian + sock: Optional[ql_socket] = ql.os.fd[sockfd] - host, port = sock.getsockname() + if sock is None: + return -1 - if sock.family == AF_INET: - sockaddr_in = make_sockaddr_in(abits, endian) + addrlen = ql.mem.read_ptr(addrlenptr) if addrlenptr else 0 - with sockaddr_in.ref(ql.mem, addr) as obj: - obj.sin_family = AF_INET - obj.sin_port = htons(ql, port) - obj.sin_addr.s_addr = inet_aton(str(host)) + abits = ql.arch.bits + endian = ql.arch.endian - elif sock.family == AF_INET6 and ql.os.ipv6: - sockaddr_in6 = make_sockaddr_in6(abits, endian) + sockname = sock.getsockname() + obj = None - with sockaddr_in6.ref(ql.mem, addr) as obj: - obj.sin6_family = AF_INET6 - obj.sin6_port = htons(ql, port) - obj.sin6_addr.s6_addr = inet6_aton(str(host)) + if sock.family == AF_UNIX: + hpath = sockname + vpath = ql.os.path.host_to_virtual_path(hpath) - # FIXME: obj data should be written according to the value pointed by addrlenptr - # - # addrlen = ql.mem.read_ptr(addrlenptr) - # ql.mem.write(addr, data[:addrlen]) + if addrlen: + # addrlen indicates the total obj size allowed to be written. + # that already includes the family field (2) and the path null + # terminator (1) + vpath = vpath[:addrlen - 2 - 1] - regreturn = 0 - else: - regreturn = -1 - else: - regreturn = -1 + sockaddr_un = make_sockaddr_un(abits, endian, len(vpath) + 1) - ql.log.debug("getsockname(%d, %#x, %#x) = %d" % (sockfd, addr, addrlenptr, regreturn)) + obj = sockaddr_un() + obj.sun_family = AF_UNIX + obj.sun_path = vpath.encode() + b'\x00' - return regreturn + elif sock.family == AF_INET: + sockaddr_in = make_sockaddr_in(abits, endian) + host, port = sockname + + obj = sockaddr_in() + obj.sin_family = AF_INET + obj.sin_port = htons(ql, port) + obj.sin_addr.s_addr = inet_aton(str(host)) + + elif sock.family == AF_INET6 and ql.os.ipv6: + sockaddr_in6 = make_sockaddr_in6(abits, endian) + host, port = sockname + + obj = sockaddr_in6() + obj.sin6_family = AF_INET6 + obj.sin6_port = htons(ql, port) + obj.sin6_addr.s6_addr = inet6_aton(str(host)) + + if obj: + objsize = obj.sizeof() + + if objsize <= addrlen: + obj.save_to(ql.mem, addr) + + if addrlenptr: + ql.mem.write_ptr(addrlenptr, objsize) + + ql.log.debug("getsockname(%d, %#x, %#x) = %d" % (sockfd, addr, addrlenptr, 0)) + + return 0 def ql_syscall_getpeername(ql: Qiling, sockfd: int, addr: int, addrlenptr: int): - if 0 <= sockfd < NR_OPEN: - sock: Optional[ql_socket] = ql.os.fd[sockfd] + if sockfd not in range(NR_OPEN): + return -1 - if isinstance(sock, ql_socket): - abits = ql.arch.bits - endian = ql.arch.endian + sock: Optional[ql_socket] = ql.os.fd[sockfd] - host, port = sock.getpeername() + if sock is None: + return -1 - if sock.family == AF_INET: - sockaddr_in = make_sockaddr_in(abits, endian) + addrlen = ql.mem.read_ptr(addrlenptr) if addrlenptr else 0 - with sockaddr_in.ref(ql.mem, addr) as obj: - obj.sin_family = int(sock.family) - obj.sin_port = htons(ql, port) - obj.sin_addr.s_addr = inet_aton(str(host)) + abits = ql.arch.bits + endian = ql.arch.endian - elif sock.family == AF_INET6 and ql.os.ipv6: - sockaddr_in6 = make_sockaddr_in6(abits, endian) + peername = sock.getpeername() + obj = None - with sockaddr_in6.ref(ql.mem, addr) as obj: - obj.sin6_family = int(sock.family) - obj.sin6_port = htons(ql, port) - obj.sin6_addr.s6_addr = inet6_aton(str(host)) + if sock.family == AF_UNIX: + hpath = peername + vpath = ql.os.path.host_to_virtual_path(hpath) - # FIXME: obj data should be written according to the value pointed by addrlenptr - # - # addrlen = ql.mem.read_ptr(addrlenptr) - # ql.mem.write(addr, data[:addrlen]) + if addrlen: + # addrlen indicates the total obj size allowed to be written. + # that already includes the family field (2) and the path null + # terminator (1) + vpath = vpath[:addrlen - 2 - 1] - regreturn = 0 - else: - regreturn = -1 - else: - regreturn = -1 + sockaddr_un = make_sockaddr_un(abits, endian, len(vpath) + 1) - ql.log.debug("getpeername(%d, %#x, %#x) = %d" % (sockfd, addr, addrlenptr, regreturn)) + obj = sockaddr_un() + obj.sun_family = AF_UNIX + obj.sun_path = vpath.encode() + b'\x00' - return regreturn + elif sock.family == AF_INET: + sockaddr_in = make_sockaddr_in(abits, endian) + host, port = peername + + obj = sockaddr_in() + obj.sin_family = AF_INET + obj.sin_port = htons(ql, port) + obj.sin_addr.s_addr = inet_aton(str(host)) + + elif sock.family == AF_INET6 and ql.os.ipv6: + sockaddr_in6 = make_sockaddr_in6(abits, endian) + host, port = peername + + obj = sockaddr_in6() + obj.sin6_family = AF_INET6 + obj.sin6_port = htons(ql, port) + obj.sin6_addr.s6_addr = inet6_aton(str(host)) + + if obj: + objsize = obj.sizeof() + + if objsize <= addrlen: + obj.save_to(ql.mem, addr) + + if addrlenptr: + ql.mem.write_ptr(addrlenptr, objsize) + + ql.log.debug("getpeername(%d, %#x, %#x) = %d" % (sockfd, addr, addrlenptr, 0)) + + return 0 def ql_syscall_listen(ql: Qiling, sockfd: int, backlog: int): @@ -507,7 +553,7 @@ def ql_syscall_listen(ql: Qiling, sockfd: int, backlog: int): return 0 -def ql_syscall_accept(ql: Qiling, sockfd: int, addr: int, addrlen: int): +def ql_syscall_accept(ql: Qiling, sockfd: int, addr: int, addrlenptr: int): if sockfd not in range(NR_OPEN): return -1 @@ -531,36 +577,56 @@ def ql_syscall_accept(ql: Qiling, sockfd: int, addr: int, addrlen: int): ql.os.fd[idx] = conn - if addr and addrlen: + if addr: + addrlen = ql.mem.read_ptr(addrlenptr) if addrlenptr else 0 + abits = ql.arch.bits endian = ql.arch.endian - if conn.family == AF_INET: + obj = None + + if conn.family == AF_UNIX: + hpath = address + vpath = ql.os.path.host_to_virtual_path(hpath) + + if addrlen: + # addrlen indicates the total obj size allowed to be written. + # that already includes the family field (2) and the path null + # terminator (1) + vpath = vpath[:addrlen - 2 - 1] + + sockaddr_un = make_sockaddr_un(abits, endian, len(vpath) + 1) + + obj = sockaddr_un() + obj.sun_family = AF_UNIX + obj.sun_path = vpath.encode() + b'\x00' + + elif conn.family == AF_INET: sockaddr_in = make_sockaddr_in(abits, endian) host, port = address - with sockaddr_in.ref(ql.mem, addr) as obj: - obj.sin_family = AF_INET - obj.sin_port = htons(ql, port) - obj.sin_addr.s_addr = inet_aton(str(host)) - - objlen = sockaddr_in.sizeof() + obj = sockaddr_in() + obj.sin_family = AF_INET + obj.sin_port = htons(ql, port) + obj.sin_addr.s_addr = inet_aton(str(host)) elif conn.family == AF_INET6 and ql.os.ipv6: sockaddr_in6 = make_sockaddr_in6(abits, endian) host, port = address - with sockaddr_in6.ref(ql.mem, addr) as obj: - obj.sin6_family = AF_INET6 - obj.sin6_port = htons(ql, port) - obj.sin6_addr.s6_addr = inet6_aton(str(host)) + obj = sockaddr_in6() + obj.sin6_family = AF_INET6 + obj.sin6_port = htons(ql, port) + obj.sin6_addr.s6_addr = inet6_aton(str(host)) - objlen = sockaddr_in6.sizeof() + if obj: + objsize = obj.sizeof() - else: - objlen = 0 + if objsize <= addrlen: + obj.save_to(ql.mem, addr) - ql.mem.write_ptr(addrlen, objlen, 4) + if addrlenptr: + ql.mem.write_ptr(addrlenptr, objsize) return idx