DllMain操作不当造成死锁
2016-05-20 16:42
423 查看
背景是这样的,做驻留模块的时候用到了dll劫持,需要在黑DLL(A)里面的dllmain里面调用驻留DLL(B)中的一些功能,但是在实际操作过程中却遇到了问题,现象是B中的功能没有被执行,连打印的DBG信息都没了(其实也就是死锁了),尝试过在dllmain中直接调用函数加载B,也尝试了在dllmain中创建线程加载B,都会出现一些问题,实在不想继续对dllmain云里雾里,于是决定去搞清楚。
首先整个流程是这样的:
exe加载A,A加载B。
1. 如果A中在Dll_process_attach中使用loadlibrary的方式加载B
A代码:
B代码:
EXE代码:
执行之后得出整个流程:
EXE开始加载——DLL_PROCESS_ATTACH(A中)——DLL_PROCESS_ATTACH(B中)——B加载完成(代码返回A中提示)——A加载完成(代码返回EXE)——DLL_PROCESS_DETACH(B中)——DLL_PROCESS_DETACH(A中)——结束。
用LoadLibrary加载时是以进程attach的方式加载,不会造成死锁。
2. A中DLL_PROCESS_ATTACH以创建线程的方式加载B
A代码:
B和EXE代码保持不变,此时弹出A的DLL_PROCESS_ATTACH消息框之后程序就没有反应了。程序死锁。死锁是由于CreateThread后面的WaitForSingleObject导致的,创建线程的方式加载另一个DLL时正常的顺序是:
EXE开始加载——DLL_PROCESS_ATTACH(A中)——创建线程完成(仅仅是完成创建,线程并没有返回)(A)——DLL_THREAD_ATTACH(A)——DLL_PROCESS_ATTACH(B)——A/B加载同时完成——DLL_THREAD_DETACH(B)——DLL_THREAD_DETACH(A)——DLL_PROCESS_DETACH(B)——DLL_PROCESS_DETACH(A)——结束
如果加了WaitForSingleObject,A会等待线程返回才能继续执行,但是此时需要先完成A.dll的DLL_THREAD_ATTACH才轮到B中DLL_PROCESS_ATTACH,线程在等待执行A的DLL_THREAD_ATTACH,A在等待线程的返回,因此造成死锁。
3. 总结
为了避免dllmain中产生死锁,首先要搞清楚case走的流程,其次应慎重使用等待类函数,诸如等待事件返回,等待线程返回的,避免互相等待。
首先整个流程是这样的:
exe加载A,A加载B。
1. 如果A中在Dll_process_attach中使用loadlibrary的方式加载B
A代码:
// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "stdafx.h"
WCHAR wzTarDll[MAX_PATH];
HANDLE hThread = 0;
VOID WINAPI ThreadLoadDll( void)
{
if ( LoadLibrary(wzTarDll))
{
MessageBoxW( NULL, L"thread load DllLockB done", L"DllLockA", MB_OK);
}
else
{
MessageBoxW( NULL, L"thread load DllLockB failed", L"DllLockA", MB_OK);
}
}
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
MessageBoxW( NULL, L"DLL_PROCESS_ATTACH", L"DllLockA", MB_OK);
//TODO
GetCurrentDirectoryW( MAX_PATH, wzTarDll);
wcscat( wzTarDll, L"\\DllLockB.dll");
if ( LoadLibrary(wzTarDll))
{
MessageBoxW( NULL, L"load DllLockB.dll done", L"DllLockA", MB_OK);
}
else
{
MessageBoxW( NULL, L"Load DllLockB.dll failed", L"DllLockA", MB_OK);
}break;
case DLL_THREAD_ATTACH:
MessageBoxW( NULL, L"DLL_THREAD_ATTACH", L"DllLockA", MB_OK);
break;
case DLL_THREAD_DETACH:
MessageBoxW( NULL, L"DLL_THREAD_DETACH", L"DllLockA", MB_OK);
break;
case DLL_PROCESS_DETACH:
MessageBoxW( NULL, L"DLL_PROCESS_DETACH", L"DllLockA", MB_OK);
break;
}
return TRUE;
}
B代码:
// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "stdafx.h"
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
MessageBoxW( NULL, L"DLL_PROCESS_ATTACH", L"DllLockB", MB_OK);
break;
case DLL_THREAD_ATTACH:
MessageBoxW( NULL, L"DLL_THREAD_ATTACH", L"DllLockB", MB_OK);
break;
case DLL_THREAD_DETACH:
MessageBoxW( NULL, L"DLL_THREAD_DETACH", L"DllLockB", MB_OK);
break;
case DLL_PROCESS_DETACH:
MessageBoxW( NULL, L"DLL_PROCESS_DETACH", L"DllLockB", MB_OK);
break;
}
return TRUE;
}
EXE代码:
#include <Windows.h>
BOOL APIENTRY WinMain( __in HINSTANCE hInstance, __in_opt HINSTANCE hPrevInstance, __in_opt LPSTR lpCmdLine, __in int nShowCmd )
{
WCHAR wzTarDLL[MAX_PATH];
GetCurrentDirectoryW( MAX_PATH, wzTarDLL);
wcscat( wzTarDLL, L"\\DllLockA.dll");
if ( LoadLibraryW(wzTarDLL))
{
MessageBoxW( NULL, L"load DllLockA.dll done", L"DllLockEXE", MB_OK);
return TRUE;
}
return FALSE;
}
执行之后得出整个流程:
EXE开始加载——DLL_PROCESS_ATTACH(A中)——DLL_PROCESS_ATTACH(B中)——B加载完成(代码返回A中提示)——A加载完成(代码返回EXE)——DLL_PROCESS_DETACH(B中)——DLL_PROCESS_DETACH(A中)——结束。
用LoadLibrary加载时是以进程attach的方式加载,不会造成死锁。
2. A中DLL_PROCESS_ATTACH以创建线程的方式加载B
A代码:
// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "stdafx.h"
WCHAR wzTarDll[MAX_PATH];
HANDLE hThread = 0;
VOID WINAPI ThreadLoadDll( void)
{
if ( LoadLibrary(wzTarDll))
{
MessageBoxW( NULL, L"thread load DllLockB done", L"DllLockA", MB_OK);
}
else
{
MessageBoxW( NULL, L"thread load DllLockB failed", L"DllLockA", MB_OK);
}
}
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
MessageBoxW( NULL, L"DLL_PROCESS_ATTACH", L"DllLockA", MB_OK);
//TODO
GetCurrentDirectoryW( MAX_PATH, wzTarDll);
wcscat( wzTarDll, L"\\DllLockB.dll");
hThread = CreateThread( NULL, 0, (LPTHREAD_START_ROUTINE)ThreadLoadDll, NULL, NULL, NULL);
WaitForSingleObject( hThread, INFINITE);
if (hThread)
{
MessageBoxW( NULL, L"create thread done", L"DllLockA", MB_OK);
}
else
{
WCHAR wzError[512];
wsprintfW( wzError, L"create thread failed , error code = %d", GetLastError());
MessageBoxW( NULL, wzError, L"DllLockA", MB_OK);
}
break;
case DLL_THREAD_ATTACH:
MessageBoxW( NULL, L"DLL_THREAD_ATTACH", L"DllLockA", MB_OK);
break;
case DLL_THREAD_DETACH:
MessageBoxW( NULL, L"DLL_THREAD_DETACH", L"DllLockA", MB_OK);
break;
case DLL_PROCESS_DETACH:
MessageBoxW( NULL, L"DLL_PROCESS_DETACH", L"DllLockA", MB_OK);
break;
}
return TRUE;
}
B和EXE代码保持不变,此时弹出A的DLL_PROCESS_ATTACH消息框之后程序就没有反应了。程序死锁。死锁是由于CreateThread后面的WaitForSingleObject导致的,创建线程的方式加载另一个DLL时正常的顺序是:
EXE开始加载——DLL_PROCESS_ATTACH(A中)——创建线程完成(仅仅是完成创建,线程并没有返回)(A)——DLL_THREAD_ATTACH(A)——DLL_PROCESS_ATTACH(B)——A/B加载同时完成——DLL_THREAD_DETACH(B)——DLL_THREAD_DETACH(A)——DLL_PROCESS_DETACH(B)——DLL_PROCESS_DETACH(A)——结束
如果加了WaitForSingleObject,A会等待线程返回才能继续执行,但是此时需要先完成A.dll的DLL_THREAD_ATTACH才轮到B中DLL_PROCESS_ATTACH,线程在等待执行A的DLL_THREAD_ATTACH,A在等待线程的返回,因此造成死锁。
3. 总结
为了避免dllmain中产生死锁,首先要搞清楚case走的流程,其次应慎重使用等待类函数,诸如等待事件返回,等待线程返回的,避免互相等待。
相关文章推荐
- Linux C函数参考手册(PDF版)
- C# partial关键字说明
- C#线程间不能调用剪切板的解决方法
- Lua教程(十七):C API简介
- 简单谈谈lua和c的交互
- C#线程同步的三类情景分析
- C#获取进程或线程相关信息的方法
- 简单对比C#程序中的单线程与多线程设计
- C#停止线程的方法
- C#子线程更新UI控件的方法实例总结
- C#中的委托数据类型简介
- C#线程队列用法实例分析
- C#编写的艺术字类实例代码
- C#实现打造气泡屏幕保护效果
- 举例讲解C#编程中委托的实例化使用
- 使用C#代码获取存储过程返回值
- C++中explict关键字用法
- C/C++数据对齐详细解析
- 利用C语言来求最大连续子序列乘积的方法