Introduction to Software Vulnerability Analysis (5)

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, __tryjust wrap the code to be checked with and __exceptwrite 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

image-20220604213204502

After an exception is thrown, the exception is automatically handled to solve the problem of removing 0

image-20220604213244880

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 handlerthis DWORDdata 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 VS2010compile 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.

image-20220604234536126

This is because the platform tool set VS2010used 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.0compile the same code

image-20220604235131012

Run the compiled program, just enter a wrong777777

image-20220604235624804

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

image-20220604235838528

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.0compile and enter the wrong password 777777to display an error

image-20220605020410794

Enter the correct passwordSEH

image-20220605020455957

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

image-20220605045616462

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 x32dbgdynamically , and run until a division by 0 exception occurs, and the program terminates

image-20220605204931568

At this time, look at the SEH chain of a program

image-20220605205715882

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, strcmpthe function is the password we are looking forSEH

image-20220605210205338

Next, let's continue to try to reverse the program that uses SetUnhandledExceptionFilterthe function anti-debugging

We directly use to x32dbgstart dynamic debugging, and it also runs until an exception occurs when dividing by 0

image-20220605211007209

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?

image-20220605211549839

As we mentioned earlier, SetUnhandledExceptionFilterthe function will only be called in the non-debugging state, and we use x32dbgto debug, the program runs in debug mode, and naturally it will not be transferred to SetUnhandledExceptionFilterthe 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 3breakpoint then use dynamic debugging software ollydbgor x32dbgattach 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 x32dbginok, you are here. But your pw is wrong!

image-20220605213018903

The positions corresponding to these strings are directly searched

image-20220605213226250

We go directly to this hidden process and still find the correct password

image-20220605213303144

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

image-20220605213523432

Cross-reference to find the corresponding assembly location

image-20220605213807332

Since the password is stored in plain text when this program is designed, we can easily find it in the memory.

image-20220605213822084

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

Guess you like

Origin blog.csdn.net/SimoSimoSimo/article/details/125742484