feat(emu): resume out of a parked syscall via stack unwind#14
Merged
Conversation
A slice is frequently captured with its thread blocked in a library/kernel call (a Sleep, a wait, a recv), so the captured PC sits in ntdll/kernel. Stepping forward from there hits a syscall the emulator can't service, so the captured state was hard to drive. Add `MSLEmulator.resume_from_syscall()`: scan the stack from SP for the nearest return address that points into the program image (executable, captured memory), then set PC/SP so emulation continues in the caller as if the call had returned. The program image is identified by its `.exe` module; `--image-range` overrides it. `--pop-bytes N` additionally discards stdcall argument bytes the callee's `ret N` would have cleaned up (e.g. 4 for Sleep). Supporting helpers: `find_caller_frame()`, `in_system_call()`, `main_image()`, `module_at()`, and a public `sp_name`. Exposed on the CLI as `memslicer-emu --resume-from-syscall/-R`. Adds engine + CLI tests (skip-walking system/junk frames, the SP fixup, pop-bytes, the no-caller case, and CLI output). README + CHANGELOG updated. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
aee0053 to
d3324b7
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
A slice is frequently captured with its thread blocked in a library/kernel call (a
Sleep, a wait, arecv), so the captured PC sits inntdll/kernel. Stepping forward from there immediately hits a syscall the emulator can't service — so the most common capture state was awkward to drive withmemslicer-emu.The fix is the standard manual trick, made first-class: don't emulate the kernel — read the caller's return address off the stack and continue there.
What
MSLEmulator.resume_from_syscall(image_range=None, max_depth=256, pop_bytes=0)— scans the stack from SP for the nearest return address that points into the program image (executable, captured memory), then sets PC/SP so emulation continues in the caller as if the call had returned. Returns theCallerFrameused (orNone)..exemodule (main_image());--image-range LO:HIoverrides it for odd cases / missing module lists.--pop-bytes Nadditionally discards the stdcall argument bytes the callee'sret Nwould have cleaned up (e.g.4forSleep) so the caller's stack is left balanced. Default0= a plainret.find_caller_frame(),in_system_call(),main_image(),module_at(), and a publicsp_name.memslicer-emu --resume-from-syscall / -R, composes with--steps/--until/--registers.The walker skips nested system frames (return addresses into
ntdll/kernel32) and stack junk, landing on the first slot that returns into the analyzed binary — exactly the instruction after thecallthat entered the library.Tests
tests/test_emu.py: a synthetic "parked in ntdll" slice (module list + system region + crafted stack) covering image/system identification, frame-walk skipping system+junk frames, the SP fixup,--pop-bytes, the no-caller case, and CLI output (incl.--image-range).Full suite: 1142 passed, 10 skipped locally (the only failures are 4
TestEncryptedMSLWritercases that need thecryptographydep, unmodified by this PR).ruffclean on the changed files.README (new Parked in a syscall subsection) and CHANGELOG updated.
🤖 Generated with Claude Code