- Instead of writing INT3 Breakpoints and catching the BREAKPOINT Exception, I'm setting the memory to PAGE_NOACCESS and catching the ACCESS_VIOLATION Exception.
- Instead of writing the address of my handler to the PEB directly, I'm using a VEH (Vectored Exception Handler), which is being added using the AddVectoredExceptionHandler() WinAPI Function.
This method of hooking works by modifying the protection of a byte of memory at the location of our hook to PAGE_NOACCESS. When this memory is executed, our VEH catches an ACCESS_VIOLATION Exception. It then checks the instruction pointer to see if the violation occurred at the location of our hook. If this ACCESS_VIOLATION is not our hook, we return CONTINUE_SEARCH to allow other handlers to work with the exception. However, if it is our hook, we do 3 things:
- Execute our hook's callback
- Restore the memory access to its original state
- Set the trap flag, which will trigger a single step
We then return CONTINUE_EXECUTION. This time, the hooked code executes it's first byte flawlessly and then triggers a SINGLE_STEP Exception. This single step is a result of setting the trap flag, and it allows us to set the hooked memory back to PAGE_NOACCESS so our hook will execute again the next time the hooked memory is accessed.
Since our exception handler's ContextRecord contains a pointer to the top of the stack, we are also able to access function arguments and find return addresses. Because of this, we can block the execution of a function or change the input, just like in a normal hook. To block execution, we can place a JMP [ReturnAddressFromStack] in our hook handler. While this jumps right back into execution and skips steps 2 and 3 of our exception handling, it is jumping back to the calling function and not the hooked code. The NOACCESS remains in place for next time.
While this rendition of SEH Hooking isn't flawless, it does illustrate the concept and get the job done. In most cases, if you plan on using SEH Hooking, I would recommend using INT3 Breakpoints, though. This method, however, is a good way to bypass modification detection. While programs may checksum their code, they don't usually verify the memory protection of every page in the program.
In the example code, I hook the MessageBoxA() WinAPI Function. Inside my handler, I call printf() to print the caption and text to the console. I then block execution of MessageBoxA().