Unpacker Tail Transitions
- often (not always) found at the end of unpacking code
- Usually comes in one of the following forms:
- jump immediate (jmp 0401234)
- Jumps generally take 1 byte operand, while transitions from unpackers to application code require a larger operand (e.g. 4 bytes)
- push / ret
- A push followed by a return is fishy as the pushed value becomes the return address
- The unpacker may have a constant or set a register that it jumps to
- pusha / popa
- Not a transition technique, but usually used for restoring registers to entry-point state
- Hardware Breakpoint on one of the saved registers may halt debugger just before transition to OEP
- View ESP in dump, select one of the 4 byte aligned values, and set bp (f2)
When a tail jump is unidentifiable, attempt to locate OEP by a section hop
- Generally code resides in single section of the PE file.
- Hops between sections are unusual and occur often in the transition from unpacker stubs to application code
- OllyDump automates the search for such a hop and attempts to break when found
Beware of self-modifying code
- How Software Breakpoints work
- Debugger stores a copy of the byte at the breakpoint address
- This byte is replaced with 0xCC
- When the address is reached, the debugger swaps in the original byte
- How self-modifying code works
- A byte or block is read from memory
- Optional transformations are applied
- The byte is written (original location or elsewhere)
- Self-modify reads may read wrong (0xCC) byte
- Self-modify writes may overwrite breakpoints
- Use Hardware breakpoints when possible (4 in 32-bit)
Breaking on common events
- Debuggers often allow breaking on Library Load/Unload
- Packers often end with a series of LoadLibrary / GetProcAddress calls
- Could set a breakpoint on common start-up functions
- GetCommandLineA, GetVersion
- Catch is they have to be loaded and available for breakpoints