A little intro before the code. It is possible, working the way through a SEH, to jump to another location instead of using a standard JMP. The advantage of this is the (eventual) obfuscation of the jump (if done properly), and although I’ve seen plenty of references around the web, I just couldn’t find anything on this matter, especially in the way I wanted it.
What you are going to find about SEH around the web (including dirty tricks) are most likely:
- Standard way (no tricks at all, just standard behaviour)
- Jump & Clean (jump to the SEH, never get back)
- Clean jump based on SEH hack (only here, searched a lot, and never found)
About #1, I will let you seek through the vast ASM/C++ references, no need to talk about it here.
About #2, code snippet:
.code
assume fs:nothing
start:
push @@SEH_Handler ; Address of SEH
push FS:[0] ; Original SEH
mov FS:[0], ESP ; Install SEH
xor eax, eax
idiv eax ; Generate exception
int 3 ; Will never reach this
jmp @@Exit
@@SEH_Handler:
mov esp, [esp+8] ; Restore pre-jump stack
pop FS:[0] ; Reset Original Handler
add esp, 4 ; Remove SEH address from stack
[...] ; Code goes on as normal
This code works perfectly. But there’s a pretty grievous problem with it. That is, it assumes and requires that you are the author of both codes (pre and post jump) and that you can modify them. Which wasn’t my case.
Example: while writing a packer, or a loader, or whatever you may want to, you just can’t clean SEH handler and the stack after the jump. Because, since the code isn’t yours and can’t be recoded, it won’t do it. And that’s bad. Imagine it doesn’t have an exception handler, and at every unhandled exception it “returns” to your OEP. Nasty, huh? And that’s the least of the problems actually.
Nastier problem is having a “dirty” stack, and god only knows what else could happen. But there is a workaround, which is cleaning and resetting everything inside the SEH handler, with a bit of hacking and eventually some heuristic approach. At least mine had to be, I knew nothing about it.
.code
assume fs:nothing
start:
push @@SEH_Handler ; Address of SEH
push FS:[0] ; Original SEH
mov FS:[0], ESP ; Install SEH
xor eax, eax
idiv eax ; Generate exception
int 3 ; Will never reach this
jmp @@Exit
@@SEH_Handler:
mov eax, [esp+C] ; Pointer to SEH Data
add [eax+C4], 8 ; [1]
mov [eax+B8], @@JustGoOn ; [2]
mov eax, [esp+8]
mov eax, [eax] ; Pointer to original Stack
mov FS:[0], eax ; Reset Original Handler
xor eax, eax ; Set to 0 to "return"
ret
; [ ESP+C ]: SEH_Data
; [ SEH_Data + C4 ]: Original Stack Address (pre-jump)
; [ SEH_DATA + B8 ]: Return Point
Pointer to SEH data ([ESP+C]) points to a zone in the stack with lots of info, including hardware breakpoints and (between the other things) the exact stack address returned to you after the jump. And, of course, the address it will return to.
Adding 8 in point [1] makes the kernel return to the address with the stack pointer set to the original one (pre-jump) minus the two push instructions (first two instructions after the start:). Which also basically means, it will return from the SEH handler with the original stack pointer, just as if the code started from the return point.
The point [2] should be fairly readable and intuitive: hacks the stack to “hack” the return point. Another approach, in case you wanted to continue right from the next instruction, would have been to add the size of the operand to the address (in this case the size of IDIV EAX). Just to remind you, the default SEH way is to return to the very same instruction that caused the exception.
With this piece of code you achieve two things: the first and most important, you perfectly and seamlessly jump from one context to another, just as if there were two programs in the same executable; secondly you have the ability to easily obfuscate the jump, which in this case is pretty obvious, but that’s up to you, not me.