慎用RtlCaptureContext

2010年6月30日 | 分类: C/C++ | 标签:

RtlCaptureContext可以用来获取调用者的线程环境结构CONTEXT,但有个很重要的前提,就是调用者必须是bp-based frame的函数,所谓的bp-based frame,就是指函数开头有push ebp/mov ebp, esp指令,如果调用者不是bp-based frame的,则RtlCaptureContext有可能产生0xC0000005异常而使程序退出,这取决于调用者的ebp值,看看RtlCaptureContext的实现就知道了:

@RtlCaptureContext:
	push    ebx
	mov     ebx, dword ptr [esp+8]
	mov     dword ptr [ebx+B0], eax
	mov     dword ptr [ebx+AC], ecx
	mov     dword ptr [ebx+A8], edx
	mov     eax, dword ptr [esp]
	mov     dword ptr [ebx+A4], eax
	mov     dword ptr [ebx+A0], esi
	mov     dword ptr [ebx+9C], edi
	jmp     short @L00000001
	mov     edi, edi
	push    ebx
	mov     ebx, dword ptr [esp+8]
	mov     dword ptr [ebx+B0], 0
	mov     dword ptr [ebx+AC], 0
	mov     dword ptr [ebx+A8], 0
	mov     dword ptr [ebx+A4], 0
	mov     dword ptr [ebx+A0], 0
	mov     dword ptr [ebx+9C], 0
 
@L00000001:
	mov     word ptr [ebx+BC], cs
	mov     word ptr [ebx+98], ds
	mov     word ptr [ebx+94], es
	mov     word ptr [ebx+90], fs
	mov     word ptr [ebx+8C], gs
	mov     word ptr [ebx+C8], ss
	pushfd
	pop     dword ptr [ebx+C0]
 
	; 注意下面这条指令,直接访问了调用者的ebp,
	; 如果调用者不是bp-based frame的,则ebp
	; 有可能指向无效的内存
	mov     eax, dword ptr [ebp+4]
	mov     dword ptr [ebx+B8], eax
	mov     eax, dword ptr [ebp]
	mov     dword ptr [ebx+B4], eax
	lea     eax, dword ptr [ebp+8]
	mov     dword ptr [ebx+C4], eax
	pop     ebx
	retn    4

VC++编译出来的程序,Debug版一般都是bp-based frame的,而Release版因为会优化代码,就不一定是bp-based frame,比如Release版经常会把函数编译成类似如下:

	; 函数开头没有push ebp/mov ebp, esp
	sub     esp, xxx
	; 省略...
	call RtlCaptureContext

这种情况很明显了,因为ebp的值不确定,所以call RtlCaptureContext极有可能崩溃。

MSDN里没看到有关于bp-based frame的说明,这是我测试google-breakpad时发现的,以后要注意了。

google-breakpad是Google的一个跨平台开源库,用来生成崩溃转储的,目前Chrome,Firefox等在使用此库)

  1. default
    2010年7月13日14:03

    不错。