mirror of
https://github.com/qilingframework/qiling.git
synced 2026-10-12 02:21:40 +00:00
Handle ARM/ARM64 Linux undefined-instruction exception
ARM and ARM64 Linux only hooked the svc syscall trap (exception 2), so any other CPU exception fell through to the unhandled-interrupt dispatcher and raised QlErrorCoreHook, crashing the framework. This surfaces in examples/shellcode_run.py: since execve was patched to return -1 for paths outside the rootfs, rootfs-less shellcode no longer stops at execve and instead runs into its trailing data. On ARM64 that data decodes as an undefined instruction (exception 1), which a real kernel would deliver as SIGILL and terminate the process. Hook EXCP.UDEF and emulate that termination by stopping cleanly, and replace the magic syscall-trap number with the named EXCP.SWI constant. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
19d33b65ff
commit
9fce1f83d5
@@ -12,6 +12,7 @@ from qiling import Qiling
|
||||
from qiling.arch.x86_const import GS_SEGMENT_ADDR, GS_SEGMENT_SIZE
|
||||
from qiling.arch.x86_utils import GDTManager, SegmentManager86, SegmentManager64
|
||||
from qiling.arch import arm_utils
|
||||
from qiling.arch.cortex_m_const import EXCP
|
||||
from qiling.cc import QlCC, intel, arm, mips, riscv, ppc
|
||||
from qiling.const import QL_ARCH, QL_OS
|
||||
from qiling.os.fcall import QlFunctionCall
|
||||
@@ -56,7 +57,8 @@ class QlOsLinux(QlOsPosix):
|
||||
# ARM
|
||||
if self.ql.arch.type == QL_ARCH.ARM:
|
||||
self.ql.arch.enable_vfp()
|
||||
self.ql.hook_intno(self.hook_syscall, 2)
|
||||
self.ql.hook_intno(self.hook_syscall, EXCP.SWI)
|
||||
self.ql.hook_intno(self.hook_cpu_exception, EXCP.UDEF)
|
||||
self.thread_class = thread.QlLinuxARMThread
|
||||
arm_utils.init_linux_traps(self.ql, {
|
||||
'memory_barrier': 0xffff0fa0,
|
||||
@@ -72,7 +74,8 @@ class QlOsLinux(QlOsPosix):
|
||||
# ARM64
|
||||
elif self.ql.arch.type == QL_ARCH.ARM64:
|
||||
self.ql.arch.enable_vfp()
|
||||
self.ql.hook_intno(self.hook_syscall, 2)
|
||||
self.ql.hook_intno(self.hook_syscall, EXCP.SWI)
|
||||
self.ql.hook_intno(self.hook_cpu_exception, EXCP.UDEF)
|
||||
self.thread_class = thread.QlLinuxARM64Thread
|
||||
|
||||
# X86
|
||||
@@ -137,6 +140,23 @@ class QlOsLinux(QlOsPosix):
|
||||
def hook_syscall(self, ql, intno = None):
|
||||
return self.load_syscall()
|
||||
|
||||
def hook_cpu_exception(self, ql, intno = None):
|
||||
# A cpu exception the kernel would turn into a fatal signal that
|
||||
# terminates the process (e.g. SIGILL on an undefined instruction).
|
||||
# Emulate that termination by stopping cleanly instead of letting the
|
||||
# unhandled-interrupt dispatcher raise QlErrorCoreHook. This commonly
|
||||
# happens with shellcode that falls through into trailing data once a
|
||||
# terminal syscall (e.g. a denied execve) returns instead of replacing
|
||||
# the image.
|
||||
signame = {
|
||||
EXCP.UDEF: 'SIGILL',
|
||||
}.get(intno, f'exception {intno:#x}')
|
||||
|
||||
pc = ql.arch.regs.arch_pc
|
||||
|
||||
ql.log.debug(f'CPU raised {signame} at {pc:#x}; terminating emulated process')
|
||||
ql.stop()
|
||||
|
||||
def register_function_after_load(self, function):
|
||||
if function not in self.function_after_load_list:
|
||||
self.function_after_load_list.append(function)
|
||||
|
||||
Reference in New Issue
Block a user