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
27 changes: 23 additions & 4 deletions examples/shellcode_run.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,23 @@
# Cross Platform and Multi Architecture Advanced Binary Emulation Framework
#

import os
import sys

sys.path.append("..")
from qiling import Qiling
from qiling.const import QL_ARCH, QL_OS, QL_VERBOSE


def windows_rootfs_ready(rootfs: str) -> bool:
# The Windows examples need genuine Windows system DLLs, which cannot be
# redistributed and are therefore not shipped with Qiling. They must be
# collected from a licensed Windows host using the helper script at
# examples/scripts/dllscollector.bat. Probe for ntdll.dll so we can skip
# these stages with a helpful message instead of crashing on an unmapped
# read when the DLLs are absent.
return os.path.isfile(os.path.join(rootfs, 'Windows', 'System32', 'ntdll.dll'))

X86_LIN = bytes.fromhex('31c050682f2f7368682f62696e89e3505389e1b00bcd80')
X8664_LIN = bytes.fromhex('31c048bbd19d9691d08c97ff48f7db53545f995257545eb03b0f05')

Expand Down Expand Up @@ -87,12 +98,20 @@
ql.run()

print("\nWindows x86 Shellcode")
ql = Qiling(code=X86_WIN, archtype=QL_ARCH.X86, ostype=QL_OS.WINDOWS, rootfs=r'rootfs/x86_windows')
ql.run()
if windows_rootfs_ready(r'rootfs/x86_windows'):
ql = Qiling(code=X86_WIN, archtype=QL_ARCH.X86, ostype=QL_OS.WINDOWS, rootfs=r'rootfs/x86_windows')
ql.run()
else:
print(" [skipped] Windows system DLLs not found under rootfs/x86_windows/Windows/System32.")
print(" Collect them from a licensed Windows host with examples/scripts/dllscollector.bat.")

print("\nWindows x86-64 Shellcode")
ql = Qiling(code=X8664_WIN, archtype=QL_ARCH.X8664, ostype=QL_OS.WINDOWS, rootfs=r'rootfs/x8664_windows')
ql.run()
if windows_rootfs_ready(r'rootfs/x8664_windows'):
ql = Qiling(code=X8664_WIN, archtype=QL_ARCH.X8664, ostype=QL_OS.WINDOWS, rootfs=r'rootfs/x8664_windows')
ql.run()
else:
print(" [skipped] Windows system DLLs not found under rootfs/x8664_windows/Windows/System32.")
print(" Collect them from a licensed Windows host with examples/scripts/dllscollector.bat.")

# FIXME: freebsd sockets are currently broken.
#
Expand Down
9 changes: 9 additions & 0 deletions qiling/arch/mips_const.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,17 @@
# Cross Platform and Multi Architecture Advanced Binary Emulation Framework
#

from enum import IntEnum

from unicorn.mips_const import *


class EXCP(IntEnum):
# subset of QEMU's MIPS exception codes, as reported to unicorn interrupt hooks
SYSCALL = 17 # system call
BREAK = 18 # breakpoint
RI = 20 # reserved (illegal) instruction

reg_map = {
"r0": UC_MIPS_REG_0,
"r1": UC_MIPS_REG_1,
Expand Down
29 changes: 26 additions & 3 deletions qiling/os/linux/linux.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
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.arch.mips_const import EXCP as MIPS_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
Expand Down Expand Up @@ -56,7 +58,8 @@ def load(self):
# 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,
Expand All @@ -66,13 +69,15 @@ def load(self):

# MIPS32
elif self.ql.arch.type == QL_ARCH.MIPS:
self.ql.hook_intno(self.hook_syscall, 17)
self.ql.hook_intno(self.hook_syscall, MIPS_EXCP.SYSCALL)
self.ql.hook_intno(self.hook_cpu_exception, MIPS_EXCP.RI)
self.thread_class = thread.QlLinuxMIPS32Thread

# 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
Expand Down Expand Up @@ -137,6 +142,24 @@ def setup_procfs(self):
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', # ARM / ARM64 undefined instruction
MIPS_EXCP.RI: 'SIGILL', # MIPS reserved (illegal) instruction
}.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)
Expand Down
16 changes: 15 additions & 1 deletion tests/test_shellcode.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
sys.path.append("..")

from qiling import Qiling
from qiling.const import QL_ARCH, QL_OS, QL_INTERCEPT, QL_VERBOSE
from qiling.const import QL_ARCH, QL_OS, QL_ENDIAN, QL_INTERCEPT, QL_VERBOSE


# test = bytes.fromhex('cccc')
Expand All @@ -22,6 +22,13 @@
2f7368
''')

# big-endian counterpart of MIPS32EL_LIN: the instruction words are byte-swapped
# while the trailing '/bin/sh' string is left as-is
MIPS32EB_LIN = bytes.fromhex('''
2806ffff04d0ffff2805ffff27e410012484f00f24020fab0101010c2f62696e
2f7368
''')

X86_WIN = bytes.fromhex('''
fce8820000006089e531c0648b50308b520c8b52148b72280fb74a2631ffac3c
617c022c20c1cf0d01c7e2f252578b52108b4a3c8b4c1178e34801d1518b5920
Expand Down Expand Up @@ -105,6 +112,13 @@ def test_linux_mips32(self):
ql.os.set_syscall('execve', graceful_execve, QL_INTERCEPT.EXIT)
ql.run()

def test_linux_mips32eb(self):
print("Linux MIPS 32bit EB Shellcode")
ql = Qiling(code=MIPS32EB_LIN, archtype=QL_ARCH.MIPS, ostype=QL_OS.LINUX, endian=QL_ENDIAN.EB, verbose=QL_VERBOSE.OFF)

ql.os.set_syscall('execve', graceful_execve, QL_INTERCEPT.EXIT)
ql.run()

# This shellcode needs to be changed to something non-blocking
def test_linux_arm(self):
print("Linux ARM 32bit Shellcode")
Expand Down