0x01 手动调试

工具:x64dbg

微信客户端版本:4.0.0.26

文件-附加,选择带窗口标题的 Weixin 主进程(通常可以看到包含多个子进程)

点击”符号”选项,在模块中搜索到 weixin.dll,双击进入

通过搜索字符串revokemsg或匹配特征57 53 48 83 EC 20 48 89 CE 80 3D D1 F7 DC 05 00 75 21 48 B8(不同版本特征码不同,可以通过字符串搜索并调试定位),定位到消息撤回调用逻辑处。

搜索到多个结果

打断点调试,定位到撤回消息处

jne weixin.7FFEE4A62F24修改为jmp weixin.7FFEE4A62F24,跳过下方撤回消息执行逻辑

JNE 和 JMP 都是汇编中控制程序执行流程的跳转指令,但这两有所不同:

JNE:不相等时跳转,通常通过 CMP 指令来比较两个寄存器或内存中的值是否相等,不相等时 ZF (零标志位)会被置为 0,此时会执行跳转到 JNE 指令后的地址。

JMP:无条件跳转,CPU 直接跳转到 JMP 指令后的地址。

右键复制选区字节,获得适用于当前版本的特征码

修改后,右键选择”补丁”-“修补文件”,保存为新的 weixin.dll 文件。

用 patch 后的替换掉原本的weixin.dll ,即可实现防撤回。

发送端撤回消息:

接收端:

0x02 工具化

使用 python 将上述操作工具化,大致思路:

找到正在运行的Weixin.exe进程 -> 筛选出父进程 -> 定位调用的动态库weixin.dll -> 匹配特征码并替换

使用psutilwin32process 等相关库即可方便的完成上述操作

查找主进程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
proc_list = []
for proc in psutil.process_iter(['pid', 'name']):
try:
pid = proc.info['pid']
name = proc.info['name']
if name != 'Weixin.exe':
continue
# 只保留有子进程的
try:
children = psutil.Process(pid).children()
if not children:
continue
except Exception:
continue
proc_list.append((pid, name))
except Exception:
continue
proc_list.sort(key=lambda x: (x[1].lower(), x[0]))

查找动态库:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
try:
h_process = win32api.OpenProcess(0x0410, False, pid)
modules = win32process.EnumProcessModules(h_process)
dll_path = ''
for m in modules:
mod_name = win32process.GetModuleFileNameEx(h_process, m)
if os.path.basename(mod_name).lower() == 'weixin.dll':
dll_path = mod_name
break
win32api.CloseHandle(h_process)
if not dll_path:
self.log_message('错误:未找到weixin.dll模块')
self.dll_path = ''
return
self.dll_path = dll_path
except Exception as e:
self.log_message(f'错误:枚举模块失败:{e}')
self.dll_path = ''
return
if not os.path.isfile(self.dll_path):
self.log_message('错误:未找到有效的weixin.dll文件')
return

# 读取DLL文件内容
with open(self.dll_path, 'rb') as f:
data = bytearray(f.read())

替换特征码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 查找特征码
pattern_pos = -1
matched_pattern = None

# 遍历所有特征码对
for pattern in self.patterns:
old_pattern = pattern['old']
for i in range(len(data) - len(old_pattern)):
if data[i:i+len(old_pattern)] == old_pattern:
pattern_pos = i
matched_pattern = pattern
break
if pattern_pos != -1:
break

if pattern_pos == -1:
self.log_message('不支持当前版本!错误:未找到匹配的特征码')
return

# 替换特征码
new_pattern = matched_pattern['new']

data[pattern_pos:pattern_pos+len(new_pattern)] = new_pattern

再搞个图形化界面,最终效果:

本文仅用于学习和研究目的,请遵守相关法律法规和软件使用协议,作者不对任何可能的损失负责。