Summary
When implementing a custom debugger plugin with TTD (Time-Travel Debugging) support using DBG_FLAG_TTD, the ev_set_backwards(false) callback is sent before ev_resume, causing the backward flag to be reset before the debugger can use it.
IDA Version
IDA Pro 9.2+
Steps to Reproduce
- Create a custom debugger plugin with
DBG_FLAG_TTDflag - Implement
ev_set_backwardsandev_resumecallbacks - User initiates “Backward Continue” from Debug menu
- Observe callback sequence in debugger logs
Expected Behavior
When user clicks “Backward Continue”, the callback sequence should be:
ev_set_backwards(true) <- Signal backward direction
ev_set_resume_mode(...) <- Set step/continue mode
ev_resume <- Execute with backward flag still true
ev_set_backwards(false) <- Reset for next operation (AFTER resume)
Actual Behavior
The actual callback sequence is:
ev_set_backwards(true) <- Signal backward direction
ev_set_backwards(false) <- IMMEDIATE reset (BEFORE resume!)
ev_set_resume_mode(1) <- RESMOD_INTO (not RESMOD_BACKINTO)
ev_resume <- Flag is already false!
Root Cause Analysis
After analyzing windbg_user.dll (the built-in WinDbg debugger backend), we found that:
-
WinDBG works because it runs synchronously inside IDA’s kernel during
invoke_callbacks(). The flag check incontinue_after_eventhappens duringev_resumeprocessing, before the reset callback is dispatched. -
Plugin callbacks receive all callbacks sequentially through the notification system. The reset arrives before we process the resume.
Additional Issue: RESMOD_INTO vs RESMOD_BACKINTO
For “Backward Continue”, IDA sends ev_set_resume_mode(RESMOD_INTO) instead of RESMOD_BACKINTO. This means the plugin cannot rely on resume mode alone to determine direction for backward continue operations.
| Operation | Expected | Actual |
|---|---|---|
| Forward Step Into | RESMOD_INTO | RESMOD_INTO ✓ |
| Backward Step Into | RESMOD_BACKINTO | RESMOD_BACKINTO ✓ |
| Forward Continue | RESMOD_NONE | (step+continue) |
| Backward Continue | RESMOD_BACKINTO? | RESMOD_INTO ✗ |