Skip to content

Fix examples/shellcode_run.py: handle illegal-instruction exceptions and missing Windows DLLs#1630

Open
retrocpugeek wants to merge 4 commits into
qilingframework:devfrom
retrocpugeek:fix/examples
Open

Fix examples/shellcode_run.py: handle illegal-instruction exceptions and missing Windows DLLs#1630
retrocpugeek wants to merge 4 commits into
qilingframework:devfrom
retrocpugeek:fix/examples

Conversation

@retrocpugeek

Copy link
Copy Markdown

Fixes #1629

examples/shellcode_run.py currently crashes on its first stage and never runs to completion. This PR fixes the underlying issues and gets the example running end-to-end.

What's in here

  1. Hook undefined/reserved-instruction exceptions on ARM/ARM64 Linux — these archs only hooked the svc syscall trap (exception 2), so any other CPU exception reached the unhandled-interrupt dispatcher and raised QlErrorCoreHook. Since execve was patched to return -1 for paths outside the rootfs, rootfs-less shellcode now runs past execve into its trailing data, which decodes as an undefined instruction (exception 1). A real kernel delivers SIGILL and terminates the process; we now emulate that by stopping cleanly. The magic syscall-trap number is also replaced with the named EXCP.SWI constant.

  2. Same fix for MIPS Linux — MIPS only hooked the syscall exception; the run-off there decodes as a reserved instruction (exception 20). Added a small EXCP enum to mips_const so the syscall/reserved-instruction codes are named rather than magic numbers, and routed it through the same handler.

  3. Skip the Windows stages gracefully when system DLLs are absent — those stages need genuine Windows DLLs collected via examples/scripts/dllscollector.bat, which are not shipped. Without them the loader failed to map ntdll.dll and the example died with an unhandled UC_ERR_READ_UNMAPPED traceback. The example now probes for ntdll.dll and skips with a message pointing at the collector script.

  4. Add a MIPS32 big-endian shellcode testtest_shellcode.py only covered little-endian MIPS; added a byte-swapped big-endian counterpart run with endian=QL_ENDIAN.EB.

Verification

  • examples/shellcode_run.py runs to completion (all five Linux stages emulate; Windows stages skip cleanly, or run when DLLs are provisioned).
  • tests/test_shellcode.py: 7/7 pass.

Checklist

Which kind of PR do you create?

  • This PR only contains minor fixes.
  • This PR contains major feature update.
  • This PR introduces a new function/api for Qiling Framework.

Coding convention?

  • The new code conforms to Qiling Framework naming convention.
  • The imports are arranged properly.
  • Essential comments are added.
  • The reference of the new code is pointed out.

Extra tests?

  • I have added enough tests for this PR.

Changelog?

  • This PR doesn't need to update Changelog.

Target branch?

  • The target branch is dev branch.

One last thing

🤖 Generated with Claude Code

retrocpugeek and others added 4 commits June 21, 2026 19:45
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) <noreply@anthropic.com>
Same class of crash as the ARM/ARM64 fix: MIPS Linux only hooked the
syscall exception, so any other CPU exception reached the unhandled-interrupt
dispatcher and raised QlErrorCoreHook.

In examples/shellcode_run.py the rootfs-less MIPS shellcode runs past its
denied execve into the trailing "/bin/sh" string, which decodes as a
reserved instruction (exception 20) -- delivered as SIGILL on a real kernel.

Hook the reserved-instruction exception and route it through the existing
hook_cpu_exception handler. Add a small EXCP enum to mips_const so the
syscall and reserved-instruction codes are named rather than magic numbers.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The Windows shellcode stages need genuine Windows system DLLs, which are not
redistributable and so are not shipped with Qiling; they must be collected
from a licensed Windows host via examples/scripts/dllscollector.bat. Without
them the loader failed to map ntdll.dll and the example crashed with an
unhandled UC_ERR_READ_UNMAPPED traceback.

Probe for ntdll.dll under each Windows rootfs and skip those stages with a
message pointing at the collector script, so the example runs to completion
on a stock checkout.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
test_shellcode.py only exercised little-endian MIPS. Add a big-endian
counterpart (MIPS32EB_LIN) so MIPS BE emulation is covered too.

The shellcode is the byte-swapped form of MIPS32EL_LIN (instruction words
reversed, '/bin/sh' string left intact) and is run with endian=QL_ENDIAN.EB,
reusing the existing graceful_execve EXIT hook to end emulation cleanly.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant