Exception Mechanism Exploration
Exception implementation mechanism
When the program is running, it often encounters various errors. In order to maintain the robustness of the program and the system when encountering errors, the exception handling mechanism came into being.
To put it simply, the exception handling mechanism is that when an exception occurs, the system throws an exception first, and the program checks first to see if it has a function that can handle this exception. Give the system, that is, the default exception handling, to randomly terminate the process
SEH , Structure Exception Handler, exception handling structure , is a data structure used by Windows in exception handling. SEH contains a SEH linked list pointer and the handle of the exception handling function. Multiple SEHs in the stack form a one-way linked list from the top of the stack to the bottom of the stack through the linked list pointer. The SEH at the top of the linked list passes through TEB (Thread Environment Block) Pointer ID at byte offset 0. Let's experiment with several different exception handler registration methods
Add SEH using C language
It is very simple to add an exception handler in C language, __try
just wrap the code to be checked with and __except
write the exception handling code in it, like this:
__try{
// 监控的代码
}
__except ( expression ){
// 异常处理代码
}
The following is a code example of registering an exception handler using C language
#include <stdio.h>
#include <windows.h>
int main(){
int a =0, b =1, c =0;
__try
{
c = b / a;//触发除零异常
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
MessageBox(NULL, TEXT("integer divide by zero."),
//弹窗提示除0异常
TEXT("ERROR"), MB_OK);
//scanf("%d",&a);;
a =1; //修正除0错误
c = b / a;
}
printf("b / a = %d\n", c);
printf("sucess\n");
system("pause");
return 0;
}
The pop-up window is shown in the figure
After an exception is thrown, the exception is automatically handled to solve the problem of removing 0
Register SEH with inline assembly block
The following is an example code for registering SEH using assembly embedded
#include <stdio.h>
#include <windows.h>
char pw[10] = "SEH";
char input[1024] = {
0};
//异常处理函数
EXCEPTION_DISPOSITION
__cdecl
_except_handler( struct _EXCEPTION_RECORD *ExceptionRecord,
void * EstablisherFrame,
struct _CONTEXT *ContextRecord,
void * DispatcherContext )
{
//异常处理函数逻辑
if(strcmp(input, pw) == 0){
printf("ok, you are here. Congratulation!\n");
system("pause");
exit(0);//完成口令匹配后结束进程——否则因为没有对异常代码进行处理,所以该进程会一直在触发异常和异常处理之间循环。
}
else{
printf("ok, you are here. But your pw is wrong!\n");
}
//返回,告知os继续执行异常代码
return ExceptionContinueExecution;
}
int main(){
int a = 1, c = 0;
int res;
printf("please input password : ");
scanf("%s", input);
DWORD handler = (DWORD)_except_handler;
//注册异常处理函数
__asm{
push handler;
push FS:[0];
mov FS:[0], ESP;
}
//设计的除零异常
res = a / c;
//如果不处理异常则会运行至此
printf("What a pity, you found a wrong way.\n");
system("pause");
return -1;
}
We pull out the code block that compiles and registers SEH
DWORD handler = (DWORD)_except_handler;
//注册异常处理函数
__asm{
push handler;
push FS:[0];
mov FS:[0], ESP;
}
Combined with the complete code above, it can be seen that the function of this assembly is to push handler
this DWORD
data type onto the stack, that is, to push the address of the exception handling function we wrote above onto the stack first, and then push the header of the SEH linked list into stack, and then FS:[0]
modify to the address of the current ESP, FS:[0]
the position pointed to by this pointer is TEB(线程环境块)
the first address of , which is equivalent to inserting a program-defined exception handling function in the SEH linked list
In fact, if we VS2010
compile this code with , the inline SEH registration assembly code block will fail to register, and we will echo no matter what we enterWhat a pity, you found a wrong way.
This is because the platform tool set VS2010
used is v100 by default, even if a historical version is selected forward, it is also v90, and the compiler version we need is VC6.0
, let’s go to VC++ 6.0
compile the same code
Run the compiled program, just enter a wrong777777
This is because there is no interruption after entering the error branch in the exception handling function, so it keeps looping, and we enter the correct passwordSEH
The SetUnhandledExceptionFilter function sets a custom exception handler
In order to make the program logic more complex, confusing, and harder to debug than ordinary anti-debugging, we can use SetUnhandledExceptionFilter()
the function to set a custom exception handler. The function of this function is to set the default exception handler to a custom exception handler, and It will only be called in the non-debugging state, the example code is posted below
#include <stdio.h>
#include <windows.h>
char pw[10] = "SEH";
char input[1024] = {
0};
EXCEPTION_DISPOSITION
__cdecl
_except_handler( struct _EXCEPTION_RECORD *ExceptionRecord,
void * EstablisherFrame,
struct _CONTEXT *ContextRecord,
void * DispatcherContext )
{
if(strcmp(input, pw) == 0){
printf("ok, you are here. Congratulation!\n");
}
else{
printf("ok, you are here. But your pw is wrong!\n");
}
system("pause");
exit(0);
return ExceptionContinueExecution;
}
int main(){
int a = 1, c = 0;
int res;
printf("please input password : ");
scanf("%s", input);
SetUnhandledExceptionFilter((LPTOP_LEVEL_EXCEPTION_FILTER )_except_handler);
res = a / c;
printf("What a pity, you found a wrong way.\n");
system("pause");
return -1;
}
Also use to VC++ 6.0
compile and enter the wrong password 777777
to display an error
Enter the correct passwordSEH
Reverse SEH instance
Above we have reproduced several exception handling process implementation mechanisms. Now we stand on the opposite side of the program. If we don’t know the correct password of this program, how can we reverse the password?
We first use the second example to compile the program and try to reverse it. We first drag the program into IDA
You will find that the default main function does not contain functions in exception handling, and there is no control flow that jumps to other code blocks in the main function
Let's try to debug it x32dbg
dynamically , and run until a division by 0 exception occurs, and the program terminates
At this time, look at the SEH chain of a program
Since the SEH linked list is added from the header, the first SEH address here is the exception handling function customized by the program. Let’s go in and have a look directly, and we will find that this section is the exception handling process customized by the program. Among them, strcmp
the function is the password we are looking forSEH
Next, let's continue to try to reverse the program that uses SetUnhandledExceptionFilter
the function anti-debugging
We directly use to x32dbg
start dynamic debugging, and it also runs until an exception occurs when dividing by 0
Also enter the SEH chain perspective, but this time we found that in addition to the default exception handling SEH, the SEH registered by the program itself does not appear in the SEH chain, why?
As we mentioned earlier, SetUnhandledExceptionFilter
the function will only be called in the non-debugging state, and we use x32dbg
to debug, the program runs in debug mode, and naturally it will not be transferred to SetUnhandledExceptionFilter
the exception handling function registered with the function
So how to solve this problem, we can use the release version to run the program directly, then manually add a int 3
breakpoint then use dynamic debugging software ollydbg
or x32dbg
attach it to the program. At this time, we can check the real error when an exception is triggered. The SEH chain is up
But to be honest, this question does not use this trick, we directly search for the string with the wrong password x32dbg
inok, you are here. But your pw is wrong!
The positions corresponding to these strings are directly searched
We go directly to this hidden process and still find the correct password
Since all string searches are used, can IDA also perform static search passwords?
The answer is yes, we look at the string and enter this exception handling process
Cross-reference to find the corresponding assembly location
Since the password is stored in plain text when this program is designed, we can easily find it in the memory.
As we all know, SEH is stored in the stack, that is to say, as long as the structure is clever, we can design a piece of data to just submerge the call address of the SEH exception function, and let the address point to ShellCode. When an exception occurs, the program will call the original exception handling function of ShellCode. This is why SEH is so important