dll反复加载卸载过程中遇见的诡异问题

      这两天遇见了个诡异的问题,扩展进程反复的加载和卸载一个插件的dll的时候,扩展会在启动的时候崩溃。

  通过windbg查看崩溃栈:

0:024> kb
ChildEBP RetAddr Args to Child
WARNING: Frame IP not in any known module. Following frames may be wrong.
02c6f7d4 77d18734 0033115c 00000024 00000000 0x38931f0
02c6f800 77d18816 038931f0 0033115c 00000024 USER32!InternalCallWinProc+0x28//InternalCallWinProc是user32.dll回调我们注册的窗口函数的地方,其实就是这里的窗口函数是上次注册的,dll的地址空间变化后就内存访问异常了。

02c6f868 77d28ea0 00000000 038931f0 0033115c USER32!UserCallWinProcCheckWow+0x150
02c6f8bc 77d2d08a 008fca00 00000024 00000000 USER32!DispatchClientMessage+0xa3
02c6f8e4 7c92e473 02c6f8f4 0000003c 008fca00 USER32!__fnINOUTLPPOINT5+0x27
02c6f92c 77d2e389 77d2e34f 00000000 02c6fe54 ntdll!KiUserCallbackDispatcher+0x13
02c6fdd0 77d2e442 00000000 02c6fe54 00000000 USER32!NtUserCreateWindowEx+0xc
02c6fe7c 77d2d0d6 00000000 02b5f1d8 00000000 USER32!_CreateWindowEx+0x1ed
02c6feb8 02ae3e4b 00000000 02b5f1d8 00000000 USER32!CreateWindowExW+0x33
02c6ff78 78132848 0296f0a8 96fe7b94 a9c821de boxbro!kernel_thread+0xcb [e:\workspace\disco\box\src\kernel\kernel_data.h @ 403]
02c6ffb0 781328c8 7c80b729 00c1d3d8 a9c821de MSVCR80!_callthreadstart+0x1b [f:\sp\vctools\crt_bld\self_x86\crt\src\thread.c @ 293]
02c6ffb4 7c80b729 00c1d3d8 a9c821de 00c1550c MSVCR80!_threadstart+0x5a [f:\sp\vctools\crt_bld\self_x86\crt\src\thread.c @ 275]
02c6ffec 00000000 7813286e 00c1d3d8 00000000 kernel32!BaseThreadStart+0x37

 

发现上面的栈崩溃在CreateWindowExW里面,最后出问题的地方是在user32.dll的InternalCallWinProc方法里面

从表面看是说:非法方位内存,看下面红色异常部分的说明,疑惑就在这里,怎么会调用函数异常呢。

(10f4.17e4): Access violation - code c0000005 (first chance)//内存访问异常
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=7ff92000 ebx=00000000 ecx=00000000 edx=00000004 esi=038931f0 edi=02c6f83c
eip=038931f0 esp=02c6f7d8 ebp=02c6f800 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010202
038931f0 ?? ???//这里是发生异常的地方,这个地址的内存是乱的,不是程序代码。其实他的上下都是乱的。

 

我一眼就看出来是回调我们的方法发生异常,但是什么情况会出现这个问题呢?

经过一晚上的分析,也木有思路,一直任务是不知道什么时候释放了dll倒是访问非法,因为要访问的程序地址中都是NULL:

eip:38931f0

No prior disassembly possible
038931f0 ?? ???
038931f1 ?? ???
038931f2 ?? ???
038931f3 ?? ???
038931f4 ?? ???
038931f5 ?? ???
038931f6 ?? ???
038931f7 ?? ???

你妈,全是空的东西,其实真实情况还有可能是乱七八糟的东西。晕各种晕~~~~;怎么代码段访问就非法了呢?还有什么情况会发生dll代码访问非法?我们服务的线程也都退出了,释放也很干净。。。。。

最后发现个诡异的情况,加载dll的时候有时候的地址空间不一样,eip寄存器指向的函数代码是非法的内存地址,就崩溃了。

到此基本明了了,原来是dll在free了之后,引用计数等于0的时候系统会自动卸载掉。当我们再次load的时候,就会出现映射到进程空间的地址变化的情况,然后我们之前通过RegisterClass来注册的窗口类中的消息处理函数地址还是原来模块的就发生了调用原来模块映射的函数地址,就发生访问非法的问题了。

WNDCLASS wc;
if (0 == GetClassInfo(GetModuleHandle(0),kernel_class_name,&wc))
{
WNDCLASS wc = {0};
wc.lpfnWndProc = kernel_thread_wnd_proc;
wc.hInstance = GetModuleHandle(0);
wc.lpszClassName = kernel_class_name;

if (0==RegisterClass(&wc))
return;
}

 

其实因为是通过attach到进程的方式调试的,在cmd窗口可以看到dll加载的情况,可以根据上次加载的地址来计算出发生访问异常的函数的代码,具体计算过程是:

进程第一次load dll的地址空间:   ModLoad: 03830000 0391c000   

进程第二次load dll的地址空间: ModLoad: 02a80000 02b6c000 

指令寄存器指向的内存地址:eip=038931f0

 

因为第二次load的dll空间变化后,eip指向的还是第一次load的时候的代码地址,

于是,我们要算出第一次的地址需要先算出代码相对模块首地址的便宜

offset = 0x038931f0-0x03830000

之后加上新的模块首地址:

address = offset  + 0x02a80000 

 

address = 0x038931f0-0x03830000+0x02a80000 

 

最后我们在windbg中执行:ln 0x038931f0-0x03830000+0x02a80000 得到如下结果:

 

0:024> ln 0x038931f0-0x03830000+0x02a80000
e:\workspace\disco\box\src\kernel\kernel_data.h(211)//错误代码所在文件
(02ae31f0) boxbro!kernel_thread_wnd_proc | (02ae3d80) boxbro!kernel_thread//这里显示函数地址 对应dll 函数名 对应的类或结构体的地址名称
Exact matches:
boxbro!kernel_thread_wnd_proc (struct HWND__ *, unsigned int, unsigned int, long)//这里是明确的代码行

 

ps:在调试的时候,lm命令会打印出加载的模块,之外还会打印出类似下面的内容:

Unloaded modules:
21100000 21186000 sqlite3.dll
01200000 0123c000 CloudMsgApps.dll
037f0000 0382c000 libcurl.dll
036a0000 036d1000 ssleay32.dll
036e0000 037e2000 LIBEAY32.dll
71a40000 71a4b000 WSOCK32.dll
02ca0000 02d83000 nimbus.dll
02c60000 02c9c000 ShowApp.dll
02270000 02353000 nimbus.dll
。。。

我猜测可能是在进程空间反复加载卸载dll,会互相占用空间,有冲突的会打印出来。也可能我理解错了希望有人指正。

 

 

 

 

 

 

 

 

 

     

转载于:https://www.cnblogs.com/terminator-studio/archive/2012/04/12/2444465.html

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值