Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ ARG BASE_IMAGE="${REGISTRY}/ubuntu:22.04"
ARG VPN_VERSION="1.0.25"
ARG BUSYBOX_VERSION="0.0.15"
ARG LINUX_VERSION="3.5.16-beta"
ARG IGLOO_DRIVER_VERSION="0.0.29"
ARG IGLOO_DRIVER_VERSION="v0.0.31-pre.7340f4300"
ARG LIBNVRAM_VERSION="0.0.23"
ARG CONSOLE_VERSION="1.0.7"
ARG GUESTHOPPER_VERSION="1.0.20"
Expand Down Expand Up @@ -600,4 +600,4 @@ RUN cd /igloo_static && \
done \
done
RUN date +%s%N > /igloo_static/container_timestamp.txt
RUN pip install py-spy
RUN pip install py-spy
46 changes: 38 additions & 8 deletions pyplugins/apis/syscalls.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

from penguin import plugins, Plugin
import json
from typing import Dict, List, Any, Callable, Optional, Iterator
from typing import Dict, List, Any, Callable, Optional, Iterator, Generator
from hyper.consts import value_filter_type as vft
from hyper.consts import igloo_hypercall_constants as iconsts
from hyper.consts import igloo_base_hypercalls as bconsts
Expand Down Expand Up @@ -334,7 +334,6 @@ def __init__(self) -> None:

# Track function -> hook_ptr and name -> hook_ptr for easier lookup
self._func_to_hook_ptr = {} # Maps functions to hook pointers
self._name_to_hook_ptr = {} # Maps function names to hook pointers

# Syscall type information - using dictionary for fast name-based
# lookups
Expand Down Expand Up @@ -366,6 +365,9 @@ def __init__(self) -> None:
self._pending_hooks = []
self._syscall_event = plugins.portal.wrap(self._syscall_event)

# Wrap the unregister function
self.unregister_syscall_hook = plugins.portal.wrap(self._do_unregister_syscall_hook)

def _setup_syscall_handler(self, cpu: int) -> None:
"""
Handler for setting up syscall definitions.
Expand Down Expand Up @@ -473,11 +475,6 @@ def _syscall_interrupt_handler(self) -> bool:
# Track function to hook pointer mappings
self._func_to_hook_ptr[func] = hook_ptr

# Store by function name if available
func_name = getattr(func, "__name__", None)
if func_name:
self._name_to_hook_ptr[func_name] = hook_ptr

'''
On repeated calls to the same syscall in portal we produce new
syscall_event objects. However, it doesn't update the version of the object
Expand Down Expand Up @@ -699,7 +696,7 @@ def _syscall_event(self, cpu: int, is_enter: Optional[bool] = None) -> Any:
# 1. Get Event Object (No bytes serialization yet)
sce = self._get_syscall_event(cpu, arg)
hook_ptr = sce.hook.address
if hook_ptr not in self._hooks:
if hook_ptr not in self._hooks or self._hooks[hook_ptr] is None:
return

# 2. Unpack Hook Data including read_only flag
Expand Down Expand Up @@ -993,3 +990,36 @@ def decorator(func):
return func

return decorator

def _do_unregister_syscall_hook(self, func: Callable) -> None:
"""Internal implementation of unregister_syscall_hook."""

if func not in self._func_to_hook_ptr:
self.logger.error("Function not registered as a syscall hook")
return

hook_ptr = self._func_to_hook_ptr[func]

if hook_ptr not in self._hooks:
self.logger.error(f"Hook pointer 0x{hook_ptr:x} not found in internal hooks when attempting to disable")
return

try:
self.logger.info("yielding unregister_syscall_hook")
retval = yield PortalCmd("unregister_syscall_hook", hook_ptr)
self.logger.info("yielded unregister_syscall_hook")

self._hooks.pop(hook_ptr, None)
self._func_to_hook_ptr.pop(func, None)
self._hook_info.pop(hook_ptr, None)
self._hook_proto_cache.pop(hook_ptr, None)

if retval != 0:
self.logger.warning(f"Kernel returned non-zero ({retval}) for unregister_syscall_hook, but proceeding with cleanup")

except Exception as e:
self.logger.error(f"Error unregistering syscall hook 0x{hook_ptr:x} ({func}): {e}")
import traceback
self.logger.error(f"Traceback: {traceback.format_exc()}")

self.logger.info("_do_unregister_syscall_hook: END")
29 changes: 19 additions & 10 deletions pyplugins/testing/syscall_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,11 @@ def __init__(self):
self.ioctl_ret_num = 0
self.ioctl_ret2_num = 0
self.ioctl_ret3_num = 0
self.getpids = 0 # For unregister test
self.hook_unregistered = False
syscalls.syscall("on_sys_ioctl_enter", comm_filter="send_syscall",
arg_filters=[None, 0xabcd])(self.test_skip_retval)
self.getpid_hook = syscalls.syscall("on_sys_getpid_return")(self.getpid)

def test_skip_retval(self, regs, proto, syscall, fd, op, arg):
assert fd == 9, f"Expected fd 9, got {fd:#x}"
Expand Down Expand Up @@ -72,21 +75,20 @@ def ioctl_noret(self, regs, proto, syscall, fd, op, arg):
with open(join(self.outdir, "syscall_test.txt"), "a") as f:
f.write("Syscall ioctl_noret: failure\n")

@syscalls.syscall("on_sys_getpid_return")
def getpid(self, regs, proto, syscall, *args):
# NOTE: We've removed this check because it was causing issues
# It doesn't seem to indicate anything negative so we're skipping it
# proc = self.panda.plugins['osi'].get_current_process(cpu)
# if syscall.retval != proc.pid and syscall.retval != 0:
# self.logger.error(
# f"Syscall test failed: getpid returned {syscall.retval:#x}, expected {proc.pid:#x}")
# self.success_getpid = False
# self.report_getpid()
# return
if "send_syscall" in self.panda.get_process_name(self.panda.get_cpu()):
self.success_getpid = True
self.report_getpid()

if not self.hook_unregistered:
result = plugins.syscalls.unregister_syscall_hook(self.getpid)

if not result:
self.logger.error("Failed to unregister getpid hook!")

self.hook_unregistered = True
self.getpids += 1

@syscalls.syscall("on_sys_clone_enter")
def syscall_test(self, regs, proto, syscall, *args):
if self.reported_clone:
Expand Down Expand Up @@ -150,6 +152,13 @@ def report_getpid(self):
self.logger.info(f"Syscall getpid test: {result}")
f.write(f"Syscall getpid test: {result}\n")

def report_unregister(self):
with open(join(self.outdir, "syscall_test.txt"), "a") as f:
result = "passed" if self.getpids == 1 else "failed"
self.logger.info(f"Syscall unregister test: {result} (getpid hook called {self.getpids} times)")
f.write(f"Syscall unregister test: {result}\n")

def uninit(self):
self.report_clone()
self.report_getpid()
self.report_unregister()
9 changes: 7 additions & 2 deletions tests/unit_tests/test_target/patches/tests/syscall.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ plugins:
type: file_contains
file: syscall_test.txt
string: "Syscall ioctl_reg3: success 1"
syscall_hypercall+unregister:
type: file_contains
file: syscall_test.txt
string: "Syscall unregister test: passed"

static_files:
/tests/syscall.sh:
Expand All @@ -46,8 +50,8 @@ static_files:
/igloo/utils/send_syscall ioctl 0x13 0x0 0x1
/igloo/utils/send_syscall ioctl 0x13 0x1234 0xabce
/igloo/utils/send_syscall ioctl 0x13 0x1234 0xabcd
/igloo/utils/send_syscall ioctl 0x9 0xabcd

/igloo/utils/send_syscall ioctl 0x9 0xabcd

if [ $? -ne 43 ]; then
echo "Error: send_syscall retval enter failed"
Expand All @@ -60,6 +64,7 @@ static_files:
exit 1
fi
/igloo/utils/send_syscall getpid
/igloo/utils/send_syscall getpid # This one should not get through because we'll unregister
echo "Syscall test: passed"
exit 0
fi
Expand Down
Loading