PC微信hook学习笔记(三)—— 获取登录二维码

本篇笔记学习教程:获取微信二维码基址,手动实现hook
本篇笔记参考博客:PC微信逆向:使用HOOK拦截二维码

1. 二维码图片基址

1.1 PNG文件在内存中的表现形式

微信的登录二维码是png格式,png图片在OD中能看到,就是png图片在内存中的表现形式。PNG文件头格式解析,这篇文章就介绍了png图片在内存中的数据表现。
在OD中,如果能看到某个数据块的前三行是这种样子的话,就说明这块内存存储了一个png图片。
在这里插入图片描述

1.2 用CE查找变量所在地址

  1. 打开微信,打开CE
  2. 在CE中,查询【未知的初始值】,此时,因为还没有获取登录二维码,所以存储二维码的变量尚未初始化。
    在这里插入图片描述
  3. (1)微信点击【切换账号】,CE选择【变动的数值】,点击【再次扫描】。(2)手机扫一扫【登录二维码】,不要点登录,CE点击【再次扫描】
    在这里插入图片描述
  4. 重复第3步,每次都滚动到最底下查看绿色基址。当绿色地址只剩下几个的时候,添加到下方窗口里,观察这几个地址的变化。
    在这里插入图片描述

1.3 用OD在地址附近打断点

  1. 打开OD,附加进程,输入命令:【dd 776DC348】。注意,这个地址,是CE中刚才加入观察的数值长度为5个的,如15990
  2. 选中地址【776DC348】右击,添加断点,添加断点后微信点击【切换账号】。这一步有可能造成OD卡死,多试几次,或换一个地址试试,直到出现下图中的二维码还未刷新出来的场景(就算出现这个空白,地址也可能是错的,参见第6步)。

在这里插入图片描述
3. 在右下角窗口中,查找类似“返回到WeChatWi.0x地址 来自 WeChatWi.0x地址”的条目,选中右击【反汇编窗口中跟随】,并在左上角窗口中,选中在语句【lea ecx,dword ptr ss:[ebp+0xC]】接着的【call】语句,按F2加断点。找差不多三四个就可以了。
在这里插入图片描述
4. 【删除内存断点】,并点击小三角运行程序。让这次的二维码扫描掉,再来显示一次二维码,使得OD进入函数断点。
5. 进入某个断点后,输入命令:dc [ecx]查询数据。能出现下图所示数据的话,恭喜你找到了二维码图片。
6. 若是OD卡死,从第5步再来几次;若是没找到图片,或者输入dc [ecx]命令后是一串别的乱码,说明一开始用来打断点的地址不对,那么,不要着急,从第1步再来几次。(淡定如君子其实慌如疯狗…特喵的我找了近2小时!)
在这里插入图片描述

1.4 打印图片

OD打印数据的命令:dm 数据的地址,数据的长度,"文件名称"

dm 04D7DD80,EA2,"w_qrcode.png"
ret
  1. 将上面的命令,修改成自己的地址,保存到txt文件中。
  2. 在OD左上角的窗口中任意位置右击→选择【运行脚本】→选择刚才保存的命令文本。
  3. 保存的图片在微信的安装目录里。
    在这里插入图片描述
  4. 验证图片的话,就删掉刚才保存的图片,重新显示出一张二维码,扫描OD保存的那张,观察微信是否有变化。
    在这里插入图片描述

1.5 计算二维码图片基址

二维码图片基址:(call语句的地址)53C3ACE7 - WeChatWin.dll = 1F ACE7‬
WeChatWin.dll 的地址,可以在OD中点击【e-显示模块窗口】快速查找。
在这里插入图片描述

2. 二维码内容基址

2.1 获取二维码图片中包含的文本内容

首先,关于二维码生成原理,大家可看下这位大佬的博客:二维码原理详解
在这里插入图片描述
识别二维码,就是将二进制编码的图片,按编码规则解码出来并解析所包含的内容(可能是字符串、网址等)。

怎么找出微信登录二维码中所包含的内容呢?

  1. 打开微信→切换账号→截图二维码→保存为图片
  2. 打开草料-二维码解码器→上传刚才的二维码截图,可得到解码结果:http://weixin.qq.com/x/4_QBLO7V2dAAUmd28I54
  3. 很显然,http://weixin.qq.com/是一个服务器地址。在浏览器里访问这个地址,首页显示pc版微信的下载;后面的x/4_QBLO7V2dAAUmd28I54,应该是接口和参数,只不过做了一些处理。

每一次点击切换账号,二维码都会变化,那么,上面的4_QBLO7V2dAAUmd28I54也是每次都不一样,我们需要找到的,就是存储这个变量的基址。
在这里插入图片描述

2.2 找到存放二维码内容的基址

  1. 打开CE→打开微信进程→搜索二维码解析出的字符串,如4_QBLO7V2dAAUmd28I54;需要注意的是,微信的二维码隔一段时间就会刷新,所以要以最新的二维码截图解析。
  2. 等待几秒钟,刚才扫描出的地址列表会变化,这时,切换到微信窗口,等到新的二维码刷新出来,会看到CE里有一个地址里的当前值,与搜索的字符串长度相等;此时,将刷新出来的二维码内容解析出来,正好与CE里变化的字符串相同。那么先记住这个地址,如下图的072FBFB0
    在这里插入图片描述
  3. 打开OD→附加微信→在右下角命令框中输入:dd 072FBFB0
  4. 选中072FBFB0并右击→断点→内存写入,并点击小三角运行。
  5. 点击OD操作栏的【t】→在窗口内右击→“Resume All Threads”→点击【c】,切换回主窗口
    在这里插入图片描述
    6.等到OD页面出现【暂停】时,说明断点起效,二维码刷新了!点击右下角第一行,则左下角的地址栏会显示对应地址信息。
    这里有几个地址信息:(1)WeChatWi.796CA008 (2)796CA010
    在这里插入图片描述
  6. 用CE验证下刚才找到的地址:(1)手动添加地址→WeChatWin.dll→添加后右击,以十六进制显示。(2)手动添加地址→796CA010→添加后右击,以十六进制显示(3)用计算器计算基址偏移量:796CA010 - WeChatWin.dll地址 = 13AA010‬
    在这里插入图片描述
    这样就得到存放二维码内容的基址: WeChatWin.dll + 0x13AA010‬

3. 编写程序显示二维码

首先,找到需要hook的call,在OD左上角窗口中右击,选择【转到】→【表达式】,输入二维码图片基址偏移量: 1FACE7
在这里插入图片描述

3.1 hook思路

在这里插入图片描述
在call语句前面,有个E8 747F2A00E8是call的机器码,747F2A00是函数地址相对于当前地址的偏移量。

  1. 第一步——截住信鸽
    首先修改内存,让call语句执行的程序整个变成执行我们自己的函数。将原来的E8 747F2A00,替换成E9 新的地址E9是jmp的机器码;新的地址 = 要跳转的地址 - hook的地址 - 5;减5是因为E9 新的地址占5个字节,要跳转的地址就是自定义函数的地址。
  2. 第二步——copy书信
    需要用一个变量pic,存储ecx里的png图片数据,并显示到自己的窗口中;
  3. 第三步——让信鸽按原来的方向继续飞
    jmp命令跳回到原来call语句的下一句,让程序继续执行。下一句的地址偏移为:78D9ACEC - WeChatWin.dll = 1FACEC

3.2 代码

  1. 打开VS2017→新建【动态链接库DLL】
  2. 在【资源文件】下右击→添加→资源→Dialog,并建立如下图的布局,对布局元素修改ID以便引用。
    在这里插入图片描述
  3. 新建hook.cpphook.c
  4. 选中项目,右击,选择【重新生成】。

hook.cpp

#include "pch.h"
#include "resource.h" 
#include "hook.h"
#include "stdio.h"
#include "atlimage.h"

// 一条命令的字节长度
#define HOOK_LEN 5
// 原来的call编码
BYTE oldCallCode[HOOK_LEN] = { 0 };

// 弹框句柄
HWND wDlg = 0;
// 保存二维码并显示
void showImg(DWORD qrcodeEcx) {
	// 获取图片长度
	DWORD picLen = qrcodeEcx + 0x4;
	// 图片数据 
	char picData[0xFFF] = { 0 };
	size_t copyLen = (size_t)*((LPVOID *)picLen);
	memcpy(picData, *((LPVOID *)qrcodeEcx), copyLen);  // 拷贝图片

	//将文件写到本地
	HANDLE hFile = CreateFileA("E:\\wechat_qrcode.png", GENERIC_ALL, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
	if (hFile == NULL)
	{
		MessageBox(NULL, L"创建图片文件失败", L"错误", 0);
		return;
	}

	DWORD dwRead = 0;
	if (WriteFile(hFile, picData, copyLen, &dwRead, NULL) == 0)
	{
		MessageBox(NULL, L"写入图片文件失败", L"错误", 0);
		return;
	}
	CloseHandle(hFile);
	

	// 读取图片,显示到弹框中
	CImage cimg;
	CRect crect;
	HWND picHw = GetDlgItem(wDlg, QRCODE);
	GetClientRect(picHw, &crect);
	cimg.Load(L"E:\\wechat_qrcode.png");
	cimg.Draw(GetDC(picHw), crect);
}


// 需要返回地址
DWORD returnAddr = 0;
// 声明备份的8个寄存器
DWORD pEax = 0;
DWORD pEcx = 0;
DWORD pEdx = 0;
DWORD pEbx = 0;
DWORD pEbp = 0;
DWORD pEsp = 0;
DWORD pEsi = 0;
DWORD pEdi = 0;
// 声明裸函数,这样编译器就不会做其他多余的操作,不会修改堆栈
void __declspec(naked) showPic() {

	// 备份寄存器 
	__asm{
		mov pEax, eax
		mov pEcx, ecx
		mov pEdx, edx
		mov pEbx, ebx
		mov pEbp, ebp
		mov pEsp, esp
		mov pEsi, esi
		mov pEdi, edi
	}
	// 图片数据在ecx里
	showImg(pEcx);

	// 返回地址 1FACEC
	returnAddr = (DWORD)GetModuleHandle(L"WeChatWin.dll") + 0x1FACEC;
	// 恢复寄存器,并跳回到原来的执行语句地址
	__asm {
		mov eax,pEax
		mov ecx,pEcx
		mov edx,pEdx
		mov ebx,pEbx
		mov ebp,pEbp
		mov esp,pEsp
		mov esi,pEsi
		mov edi,pEdi
		jmp returnAddr
	}
}

// 开始hook
void startHook(DWORD imgAddr,LPVOID funAddr, HWND hwndDlg) {

	// ----------------------- 1.获取基址 ----------------------
	// 模块基址
	DWORD weChatWinAddr = (DWORD)GetModuleHandle(L"WeChatWin.dll");
	// 二维码图片地址
	DWORD imgHook = weChatWinAddr + imgAddr;

	// ----------------------- 2.组装E9数据 ----------------------
	// 二进制byte
	BYTE jmpCode[HOOK_LEN] = { 0 };
	// 头部E9
	jmpCode[0] = 0xE9;
	// 计算公式:要跳转的地址 - hook的地址 - 5
	// 要跳转的地址就是自己函数的地址
	*(DWORD *)&jmpCode[1] = (DWORD)funAddr - imgHook - HOOK_LEN;

	// ----------------------- 3.备份原来的call地址,用于卸载 ----------------------
	// 获取进程句柄
	HANDLE wechatH = OpenProcess(PROCESS_ALL_ACCESS, NULL, GetCurrentProcessId());
	if (ReadProcessMemory(wechatH, (LPCVOID)imgHook, oldCallCode, HOOK_LEN, NULL) == 0) {
		MessageBox(NULL, L"读取内存失败", L"错误", 0);
		return;
	}

	// ----------------------- 4.写入E9数据 ----------------------
	if (WriteProcessMemory(wechatH, (LPVOID)imgHook, jmpCode, HOOK_LEN, NULL) == 0) {
		MessageBox(NULL, L"内存写入失败", L"错误", 0);
		return;
	}

	// 赋值弹框句柄,用于显示图片
	wDlg = hwndDlg;

	// 二维码链接
	char qrcodeUrl[0x300] = { 0 };
	DWORD qrcodeUrlAddr = weChatWinAddr + 0x13AA010;
	// 读取指针里的数据  
	sprintf_s(qrcodeUrl, "%s%s", "http://weixin.qq.com/x/", *((DWORD *)qrcodeUrlAddr));
	MessageBoxA(NULL, qrcodeUrl, "二维码链接", NULL);
}

// 卸载hook
void unHook(DWORD imgAddr) {
	// 模块基址
	DWORD weChatWinAddr = (DWORD)GetModuleHandle(L"WeChatWin.dll");
	// 二维码图片地址
	DWORD imgHook = weChatWinAddr + imgAddr;
	// 写入备份
	HANDLE wechatH = OpenProcess(PROCESS_ALL_ACCESS, NULL, GetCurrentProcessId());
	if (WriteProcessMemory(wechatH, (LPVOID)imgHook, oldCallCode, HOOK_LEN, NULL) == 0) {
		MessageBox(NULL, L"卸载hook失败", L"错误", 0);
		return;
	}
}

hook.h

#pragma once
#include "pch.h"
void startHook(DWORD imgAddr, LPVOID funAddr, HWND hwndDlg);
void showPic();
void unHook(DWORD imgAddr);

dllmain.cpp

// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "pch.h"
#include "hook.h"
#include "windows.h"
#include "resource.h" 

INT_PTR CALLBACK DialogProc(
	HWND hwndDlg,
	UINT uMsg,
	WPARAM wParam,
	LPARAM lParam
);

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
		DialogBox(hModule, MAKEINTRESOURCE(MAIN), NULL, &DialogProc);
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}

INT_PTR CALLBACK DialogProc(
	HWND hwndDlg,
	UINT uMsg,
	WPARAM wParam,
	LPARAM lParam
)
{
	char urlText[0x100] = { 0 };
	switch (uMsg)
	{
	case WM_INITDIALOG:

		break;
	case WM_COMMAND:
		if (wParam == START_HOOK) {
			// 开始hook
			startHook(0x1FACE7, showPic, hwndDlg);
			SetDlgItemText(hwndDlg, HOOK_STATUS, L"已hook");
		}
		if (wParam == UNHOOK) {
			// 卸载hook
			unHook(0x1FACE7);
			SetDlgItemText(hwndDlg, HOOK_STATUS, L"已卸载hook");
		}	
		break;
	case WM_CLOSE:
		EndDialog(hwndDlg, 0);

		break;
	default:
		break;
	}

	return false;
}

OD注入

在OD左上角窗口中右击,选择【StrongOD】→【InjectDll】→【Remote Thead】 ,然后选择生成的dll。
选中注入的那一行,按回车,再按回车,可以看到代码的汇编编码,按回退键回到原来的页面。按F8单步执行。

代码的执行结果如下图,在hook成功时,call语句是调用的DLL1;在卸载hook成功时,call语句就恢复成原来的WeChatWi函数
但是,不知道为啥,我这边没有实现修改成E9的jmp命令。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 8
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
### 回答1: Python钩子微信是一种使用Python编程语言编写的程序,用于检测和捕获微信应用程序的输入和输出,并进行处理和操作。该程序可以用于自动发送和接收微信消息,拦截和修改微信消息,获取微信用户信息以及实现其他自定义功能。要实现Python钩子微信,需要了解微信应用程序的工作原理和API,以及Python的基本编程知识和技能。在编写程序时,需要使用第方库和工具,例如itchat、pymouse、pyHook等。通过这些工具,可以轻松地编写Python钩子微信程序,并实现自己想要的功能。但是需要注意,在编写程序时,应遵守微信的相关规定和条例,不得用于非法或未经授权的用途。除此之外,在编写钩子程序时,还应注意安全问题,避免损坏计算机系统或容易出现安全漏洞。总之,Python钩子微信是实现微信自定义功能的一种有效方法,但需要在充分了解微信API和Python编程基础的基础上进行。 ### 回答2: Python hook PC微信,也就是通过Python程序来对PC版的微信进行一些自定义的操作。首先需要了解的是,PC微信是一个Windows平台的桌面应用程序,而Python是一种高级编程语言,可以通过一些模块和库来实现对PC微信的控制和管理。 要实现钩取(hookPC微信,需要使用到相关的模块和包,比如pyHook、keyboard、ctypes等。其中,pyHook是一个Python钩子模块,它可以捕获各种Windows事件,如键盘鼠标事件等。而keyboard则是另一个Python模块,它也可以用于监测和记录键盘事件。最后,ctypes是一个Python模块,用于调用或操作Windows中的DLL、API以及C的库。 钩取PC微信主要可以实现以下几个方面: 1. 监测和记录键盘事件 通过使用pyHook、keyboard等模块,可以在Python程序中监测和记录PC微信中的各种键盘事件,比如输入字符、快捷键等。这可以方便地实现自动回复、自动切换聊天窗口等功能。 2. 控制和管理PC微信窗口 通过使用ctypes,在Python程序中可以调用Windows API函数,比如FindWindow、SetWindowPos等,来实现对PC微信窗口的控制和管理,比如改变窗口大小、位置,最小化窗口等。 3. 实现自动化脚本 通过以上两个方面的应用,可以实现对PC微信的自动化控制和管理。比如可以编写Python脚本,使其自动监测PC微信中的消息,自动回复、发送消息,或者进行一些其他操作等。 总之,Python hook PC微信可以为我们带来很多便利和效率,在实际应用中也有很多种可能,比如在聊天机器人、自动化聊天、消息推送等方面都有着广泛的应用。 ### 回答3: Python Hook PC微信,简单来说就是使用Python编写程序完成对PC微信软件的钩子(hook)功能。钩子是一种编程技术,可以监视计算机的特定活动,并在特定事件发生时触发自定义功能。 对于PC微信软件而言,可能存在一些无法实现的需求,或者需要对其界面进行改进等情况。这时,我们可以使用Python编写程序,来完成对微信软件的钩子,实现我们想要的效果。 具体来说,Python Hook PC微信可以通过以下步骤实现: 1. 导入Python的ctypes库,用于调用Windows的API函数。 2. 使用WinAPI获取微信的句柄(handle),并将其与Python中的句柄绑定。 3. 使用WinAPI获取微信窗口中的各种控件,包括菜单、列表、文本框、按钮等。 4. 对这些控件进行钩子操作,比如监控鼠标和键盘事件,或者改变控件的样式、位置、大小等。 5. 根据需要编写自己的功能,比如监听微信收到新消息的通知,自动回复消息,或者改变接收到的消息格式等。 需要注意的是,钩子的实现需要一定的编程技巧和经验,同时也具有一定的安全风险。在实际编写过程中,应当按照规范操作,避免对微信软件及其用户造成损害。此外,Python Hook PC微信也有一定的局限性,可能会受到微信版本升级、系统环境变化、安全策略影响等问题。因此,我们需要在编写程序前进行足够的研究和测试,以确保可靠性和稳定性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值