QQIntl在Win8、Server2008等系统上卡死故障的HACKFIX

一个月前开始使用Server 2008 R2作为工作站系统,发现QQIntl经常出现卡死,根本无法使用,从1.5到2.1版本问题均存在。似乎在Win8.1上也有同样的问题。

卡死是线程死锁导致的,锁死发生在NTDLL中加载DLL文件的系统加载锁上,且锁死发生后占有锁的线程已经结束(而不是等待另一个锁这种互相等待的死锁),给排查带来了很大的难度。锁死发生后OD、VS不能挂入调试,否则OD等也会卡死。OD亦不能在启动QQ时就挂入,因为QQ本身有防调试机制。只能用WinDbg挂入。也就是说,根本无从下手,只能HackFix。

经过N天的实验,来回更换了五六个方案,最终采用了以下方案的HackFix,至今运行基本稳定。

#include <windows.h>
#include <winnt.h>
#include <process.h>

HANDLE hHookHeap = NULL;

PVOID InstallHook(PVOID CodeAddr, LONG CodeLen, PVOID HookProc)    //ret: StubProc
{
    if (hHookHeap == NULL) hHookHeap = HeapCreate(HEAP_CREATE_ENABLE_EXECUTE, 0, 0);
    DWORD oldProtect;
    VirtualProtect(CodeAddr, CodeLen, PAGE_EXECUTE_READWRITE, &oldProtect);
    PVOID StubProc = HeapAlloc(hHookHeap, 0, CodeLen + 5);
    memcpy(StubProc, (PVOID)CodeAddr, CodeLen);
    *((PBYTE)StubProc + CodeLen) = '\xE9';
    *(PDWORD)((PBYTE)StubProc + CodeLen + 1) = (DWORD)CodeAddr + CodeLen - ((DWORD)StubProc + CodeLen + 5);
    *((PBYTE)CodeAddr) = '\xE9';
    *(PDWORD)((PBYTE)CodeAddr + 1) = (DWORD)HookProc - ((DWORD)CodeAddr + 5);
    VirtualProtect(CodeAddr, CodeLen, oldProtect, &oldProtect);
    return StubProc;
}

DWORD lpLoaderLock = 0x1020c0;
void* lpStub;

void __stdcall hook_RtlEnterCriticalSection(CRITICAL_SECTION* lpCriticalSection)
{
	if ((DWORD)lpCriticalSection == lpLoaderLock)
	{
		int count;
		for (count = 0; count < 10; ++count)
		{
			DWORD dwLockThread = (DWORD)lpCriticalSection->OwningThread;
			if (dwLockThread == 0 || dwLockThread == GetCurrentThreadId())
			{
				break;
			}
			Sleep(10);
		}
		if (count == 10)
		{
			InitializeCriticalSectionEx(lpCriticalSection, 0, 0x2000000);
		}
	}
	((void(__stdcall*)(CRITICAL_SECTION*))lpStub)(lpCriticalSection);
}

void do_hook()
{
	DWORD hNTDLL = (DWORD)GetModuleHandleA("ntdll");
	lpLoaderLock += hNTDLL;
	lpStub = InstallHook((void*)(hNTDLL + 0x222b0), 5, &hook_RtlEnterCriticalSection);
}


BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
					 )
{
	switch (ul_reason_for_call)
	{
	case DLL_PROCESS_ATTACH:
		if (GetModuleHandleA("QQ.exe") == NULL)
			return FALSE;
		do_hook();
	case DLL_THREAD_ATTACH:
	case DLL_THREAD_DETACH:
	case DLL_PROCESS_DETACH:
		break;
	}
	return TRUE;
}

代码中的两个地址常数一个是加载锁的RVA,一个是RtlEnterCriticalSection的地址。

注入采用的是修改了QQ目录下的一个DLL使之LoadLibrary这个DLL(我选的是zlib,大部分TX签名的DLL在QQ登陆时会有hash校验而不能修改)。

4 thoughts on “QQIntl在Win8、Server2008等系统上卡死故障的HACKFIX

  1. 幽幽

    求解答 :
    HackFix 是一款调试器?
    《大部分TX签名的DLL》,如果修改这些做劫持 QQ 会崩溃还是会给检测出来???

    Reply
    1. gmsj0001 Post author

      hackfix指解决不了bug之根本的fix。qq登陆时会hash大多数dll,不通过则不允许登录。

Leave a Reply

Your email address will not be published. Required fields are marked *

Using REAL email address will help you receive reply notifications.