初探Windows异常分发

看了xjun师傅关于反调试的教程后,自己进行调试了下异常分发流程,在此记录一下:

测试的代码,分别在VEHandler,SEHandler,TopLevelExceptionFilter这三个中打印信息。

#include<windows.h>
#include<stdio.h>

LONG NTAPI VEH(
	struct _EXCEPTION_POINTERS *ExceptionInfo
)
{
	if (ExceptionInfo->ExceptionRecord->ExceptionCode == 0xC0000005)
	{
		printf("this is veh handler\n");
	}
	return EXCEPTION_CONTINUE_SEARCH;
}

EXCEPTION_DISPOSITION
__cdecl SEhandle(struct _EXCEPTION_RECORD *ExceptionRecord,
	void * EstablisherFrame,
	struct _CONTEXT *ContextRecord,
	void * DispatcherContext)
{
	if (ExceptionRecord->ExceptionCode == 0xC0000005)
	{
		printf("this is seh handler\n");
	}
	return ExceptionContinueSearch;
}

LONG MyTopLevelExceptionFilter(
	__in struct _EXCEPTION_POINTERS *ExceptionInfo
)
{
	if (ExceptionInfo->ExceptionRecord->ExceptionCode == 0xC0000005)
	{
		printf("this is LastExceptionFilter\n");
	}
	return EXCEPTION_CONTINUE_EXECUTION;
}

void main()
{
	AddVectoredExceptionHandler(0,VEH);
	SetUnhandledExceptionFilter((LPTOP_LEVEL_EXCEPTION_FILTER)MyTopLevelExceptionFilter);
	__asm
	{
		push SEhandle
		push fs:[0]
		mov fs:[0],esp
		mov eax,0
		mov dword ptr [eax],1
		pop fs:[0]
		add esp,4
	}
	
	RemoveVectoredExceptionHandler(VEH);
}

首先用Windbg_x64对ntdll(x64)的KiUserExceptionDispatcher下断点:

再对32位dll的KiUserExceptionDispatcher下断点:

然后运行程序,发生异常,触发了断点:


这里的je判断当前是64位的异常还是32位程序的异常,这里是32位程序的异常,所以不跳,执行call wow64!Wow64PrepareForException,然后执行call    ntdll!RtlDispatchException将异常传递给32位处理:


这个时候我们在32位的ntdll下的断点已经段下来了,这里就换用x32dbg来调试,windbg这个UI不想吐槽。。虽然功能强大。


这里执行过来先将异常的两个参数ExceptionContext和ExceptionRecord 压栈,然后调用RtlDispatchException。

两个结构体的结构:

typedef struct _EXCEPTION_RECORD {
    DWORD    ExceptionCode;
    DWORD ExceptionFlags;
    struct _EXCEPTION_RECORD *ExceptionRecord;
    PVOID ExceptionAddress;
    DWORD NumberParameters;
    ULONG_PTR ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS];

    } EXCEPTION_RECORD;

Context结构太多了不放上来,就是一堆寄存器。

跟进RtlDispatchException:


首先看到压入了ExceptionContext和ExceptionRecord然后执行VEH,跟进RtlpCallVecotredHandlers看见先对VEHAddress进行Decode,然后调用VEH处理


执行完后就已经打印出来了“this is veh handler”。

之后返回之前的CALL RtlpCallVecotredHandlers后面:

判断是否已经处理好了异常:


把在这里Hook掉修改Context和返回的eax就能够返回你想要的EIP。

在继续跟RtlpCallVecotredHandlers后面:


这里获取Sehandler的地址,然后检查handler是否合法::


然后调用RtlpExecuteHandlerForException进行调用SEHandler,跟进去就能看到SEH的调用:


这个call就是调用SEH_handler,之后就返回判断是否已经处理:


这里也可以是一个Hook点进行修改EIP来跳转

没有处理的话,就一直循环SEH链,直到倒数第二个SEH进行内部调用call UnhandledExceptionFilter进行处理,如果还不能处理就停止运行:

跟进这个SEH:


这里才是真的调用UnhandledExceptionFilter:


跟进去发现调用了一次NtQueryInformationProcess查询是否有DebugPort,如果有调试器则把异常给调试器处理,否则则执行TopLevelExceptionHandle进行处理:


这里就是调用TopLevelExceptionHandle,然后再进行判断是否处理,再进行结束进程或者继续运行,如果这里修改context的EIP和返回的值,那么应该也可以实现转移EIP:

Right:,能够跳转

最后返回最开始层:在KiUserExceptionDispatcher中,调用完异常处理后如果处理成功就会调用后面的ZwContinue进行返回


总结一下:

64位系统下windows的异常首先是在ntdll(64位)!KiUserExceptionDispatcher中判断是64位异常还是32位异常

                     |                                                                                                                                  |

 是32位异常   |                                                                                                                64位异常    |

                     |                                                                                                                                   |


wow64!Wow64PrepareForException->                                                             (x64)ntdll!RtlDispatchException        

(x86)ntdll!RtlDispatchException将异常传递给32位处理:

                    |

                    |

                    |

            VEH链处理

                    |

                    |

                    |

            SEH链处理

                    |

                    |

                    |

倒数第二个SEH  CALL UnHandledExceptionFliter  (会调用NtQueryInformationProcess查询DebugPort),

然后调用TopLevelExceptionFilter()处理

                    |

                    |

                    |

         os最后的异常处理终止进程










猜你喜欢

转载自blog.csdn.net/a893574301/article/details/80507978
今日推荐