天天看点

dll导出中函数中使用CString类型的参数引发的ESP出错

今天下午花了很久去调试这个bug,因为自己一个dll函数参数的疏忽,导致浪费很多的时间去调试。

这个参数直接使用了CString类型,后面会造成两次析构而引发ESP出错,直接崩掉...

dll导出中函数中使用CString类型的参数引发的ESP出错

回来的时候,抽空分析了一下,大致过程是这样的,先写个简单的伪代码来调试吧:

Dll源码:

//

// MyDll.h

#include <afx.h>

#ifndef MY_API

#define MY_API _declspec(dllimport)

#endif

CString g_strTitle = "";

MY_API void MySetTitle(CString strTitle);

MY_API CString MyGetTitle();

//

// MyDll.cpp

#include "MyDll.h"

void MySetTitle(CString strTitle)

{

g_strTitle = strTitle;

}

CString MyGetTitle()

{

return g_strTitle;

}

主工程为对话框,添加两个按钮,消息响应如下:

void CTestDlg::OnBtnDefault()

{

// TODO: Add your control notification handler code here

CString str = "Hello World !";

}

void CTestDlg::OnBtnTest()

{

// TODO: Add your control notification handler code here

CString str = "Hello World !";

MySetTitle(str);

str = MyGetTitle();

}

点击测试,再关闭队话框,即出现常见的对话框(如果在调试时,将会出现上图结果):

Microsoft Visual C++ Debug Library:

Debug Error:

Program: ...

Module:

file: i386/chkesp.c

Line: xx

The value of ESP was not properly saved across a function call. This is usually a result of calling a function declared with one calling convention with a function pointer declared with a different calling convention.

(Press Retry to Debug the Application)

于是载入IDA,查看两个函数反汇编代码:

; Attributes: bp-based frame

; protected: void __thiscall CTestDlg::OnBtnDefault(void)

?OnBtnDefault@CTestDlg@@IAEXXZ proc near

var_48= byte ptr -48h

var_8= byte ptr -8

var_4= dword ptr -4

push ebp

mov ebp, esp

sub esp, 48h

push ebx

push esi

push edi

push ecx

lea edi, [ebp+var_48]

mov ecx, 12h

mov eax, 0CCCCCCCCh

rep stosd

pop ecx

mov [ebp+var_4], ecx

push offset aHelloWorld ; "Hello World !"

lea ecx, [ebp+var_8]

call ??0CString@@QAE@PBD@Z ; CString::CString(char const *) ; 构造函数

lea ecx, [ebp+var_8]

call ??1CString@@QAE@XZ ; CString::~CString(void) ; 调用析构函数,这里是正常的

pop edi

pop esi

pop ebx

add esp, 48h

cmp ebp, esp

call __chkesp

mov esp, ebp

pop ebp

retn

?OnBtnDefault@CTestDlg@@IAEXXZ endp

; Attributes: bp-based frame

; protected: void __thiscall CTestDlg::OnBtnTest(void)

?OnBtnTest@CTestDlg@@IAEXXZ proc near

var_68= byte ptr -68h

var_28= dword ptr -28h

var_24= dword ptr -24h

var_20= dword ptr -20h

var_1C= byte ptr -1Ch

var_18= dword ptr -18h

var_14= byte ptr -14h

var_10= dword ptr -10h

var_C= dword ptr -0Ch

var_4= dword ptr -4

push ebp

mov ebp, esp

push 0FFFFFFFFh

push offset __ehhandler$?OnBtnTest@CTestDlg@@IAEXXZ

mov eax, large fs:0

push eax

mov large fs:0, esp

sub esp, 5Ch

push ebx

push esi

push edi

push ecx

lea edi, [ebp+var_68]

mov ecx, 17h

mov eax, 0CCCCCCCCh

rep stosd

pop ecx

mov [ebp+var_10], ecx

push offset aHelloWorld ; "Hello World !"

lea ecx, [ebp+var_14]

call ??0CString@@QAE@PBD@Z ; CString::CString(char const *)

mov [ebp+var_4], 0

push ecx

mov ecx, esp

mov [ebp+var_18], esp

lea eax, [ebp+var_14]

push eax

call ??0CString@@QAE@ABV0@@Z ; CString::CString(CString const &) ; 构造函数

mov [ebp+var_20], eax

call ds:__imp_?MySetTitle@@YAXVCString@@@Z ; MySetTitle(CString)

add esp, 4

mov esi, esp

lea ecx, [ebp+var_1C]

push ecx

call ds:__imp_?MyGetTitle@@YA?AVCString@@XZ ; MyGetTitle(void)

add esp, 4

cmp esi, esp

call __chkesp

mov [ebp+var_24], eax

mov edx, [ebp+var_24]

mov [ebp+var_28], edx

mov byte ptr [ebp+var_4], 1

mov eax, [ebp+var_28]

push eax

lea ecx, [ebp+var_14]

call ??4CString@@QAEABV0@ABV0@@Z ; CString::operator=(CString const &); 这里拷贝构造函数,所以才引起两次析构造

mov byte ptr [ebp+var_4], 0

lea ecx, [ebp+var_1C]

call ??1CString@@QAE@XZ ; CString::~CString(void) ; 第一次调用析构函数

mov [ebp+var_4], 0FFFFFFFFh

lea ecx, [ebp+var_14]

call ??1CString@@QAE@XZ ; CString::~CString(void) ; 第二次调用析构函数

mov ecx, [ebp+var_C]

mov large fs:0, ecx

pop edi

pop esi

pop ebx

add esp, 68h

cmp ebp, esp

call __chkesp

mov esp, ebp

pop ebp

retn

?OnBtnTest@CTestDlg@@IAEXXZ endp  

于是,这个bug就立刻展现眼前,原来是CString拷贝构造函数引起两次析构函数,而造成ESP出错!于是改这个bug也很简单了,在参数前加个&即可。感谢法师,加一个&符号即可修正之.....

突然,才发现MS SDK的MFC类库函数也很少用到这个类型的参数,只是有很少的一部分也只是加上&引用使用。

所以,建议大家在Dll中尽量不要直接使用CString作为参数,可以直接使用LPCSTR替代吧!

配套源码:http://download.csdn.net/source/2372247

另外,送一个相关的超级传送门...

点击此处!!!

继续阅读