V5多开器逆向
序言:多开器中,V5是比较优选的程序,可麻烦的是:它会自动以秒为单位生成大量log文件,在个人使用情况下根本不需要。- 目标:绕过(清除)程序自动以秒单位生成记录日志的功能
- 工具:IDA Freeware 8.4, ProcessMonitor, WinHEX
- 思路:
1. 检查有无相关配置文件的设定开关,如LogSetting=On(日志记录开关)改成Off、又或者LogLevel=3(日志输出等级)改成0;
2. 变动主程序除外的其他文件,比如移除或替换某些.dll文件,测试是否有作用;
**如以上无效,再考虑从程序逆向的角度修改.dll文件,实现绕过目的**
3. 定位程序入口函数断点:通常程序会调用Windows API,实现例如获取窗口句柄、当前时间、读取/写入/创建文件等功能;
4. 解析入口函数上下文涵义,接下来使用工具修改相应代码。
### 步骤一:
一切先从简单的入手,我先查看V5.exe同路径下找到config.ini文件,通常这种配置文件包含程序运行时所要运行的各种参数,其内容为:
```bash
Title=V5 程序多开器 0.1 Beta
LastPath=D:\霸王\Alefclient.exe
url=www.prayaya.com
Query=0
```
注意到这个配置文件里并没有Log相关的设置参数。尝试添加Log=Off、LogSetting=Off、LogLevel=0,重启V5程序后无效。
好,我继续尝试,把v5_Log.dll改成v5_Log.dllbak,启动V5.exe后有弹窗报错:
从弹窗信息上判断V5.exe会调用到v5_Log.dll中的一些关联库函数,截至这一步说明通过“简单修改、替换配置文件”是行不通了。
那接下来,只有从逆向角度来拆解程序运行的本质了,再找机会给它来个外科手术。
### 步骤二:
到了这一步,我也是卡壳好久应该从哪里入手,一直在想问题:程序在PC上运行的底层逻辑是什么?
Windows程序运行的底层逻辑可概括为:**系统将EXE从硬盘加载到内存,为其分配独立虚拟地址空间,创建进程与主线程,通过消息循环驱动事件,调用系统API完成硬件交互,由CPU分时调度执行指令**。
1. **加载启动**:双击程序后,系统加载器把EXE及依赖的DLL(Dynamic Link Library)动态链接文件读入内存,分配专属虚拟地址空间。
2. **事件驱动**:程序以`WinMain`为入口,建立消息循环,不断抓取用户操作、系统事件生成的消息,分发给对应窗口函数处理。(看到这里感觉和C语言那个int main{}有点对应上了)
3. **资源调度**:内核接管硬件交互,通过CPU分时切换线程,让多个程序看似同时运行,依托虚拟内存机制隔离各进程的内存空间避免互相干扰。
**结合我们的主线任务,第一个关键点来了:创建日志功能的函数调用存在于哪个文件呢?**
V5.exe同路径下有3个.dll文件:v5_hook.dll、v5_Log.dll、v5_Process_Manager.dll
先根据文件名预测一下它们的功能和包含日志创建功能入口函数的可能性:
| 模块名称 | 功能推测 | 包含日志创建功能的可能性分析 |
| :------------------------- | :----------------------------------------------------------- | ------------------------------------------------------------ |
| **v5_hook.dll** | **钩子/注入模块**。通常用于拦截API调用、监控程序行为或修改执行流程。 | **较低**。这类模块的核心任务是“拦截”和“转发”,其产生的监控数据(可视为日志来源)通常需要**传递给其他模块进行处理和持久化存储**。它自身更专注于底层技术实现,而非上层的数据格式组织和文件写入。 |
| **v5_Process_Manager.dll** | **进程管理模块**。可能负责进程的创建、枚举、终止、权限管理或注入等。 | **中等**。进程管理操作(如“启动了某进程”、“终止失败”)本身就是需要记录的重要**事件**。因此,该模块**极有可能调用日志接口来记录这些事件**,但它本身可能不直接实现文件写入,而是调用统一的日志服务(如 `v5_Log.dll`)。 |
| **v5_Log.dll** | **日志模块**。根据名称,这是**最可能**包含日志格式处理、文件操作(打开/写入/关闭)、日志级别管理和滚动归档等核心功能的模块。 | **最高**。专业日志模块的设计通常是独立的,以便被其他所有模块调用。主程序和其他DLL会将日志信息**发送给**它,由它统一负责实际的磁盘写入。 |
好,启动IDA对v5_Log.dll进行静态分析,通常函数调用处会有一些特征,比如备注或者其他上下文反汇编代码。
打开v5_Log.dll文件,使用菜单栏中->->,这里是查看v5_Log.dll导出的所有函数,很遗憾只有一个主入口地址为10004C99的DllEntryPoint函数,此函数的内容是:
```assembly
; Attributes: library function
; BOOL __stdcall DLLMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID IpvReserved)
_DllMain@12 proc near
hinstDLL= dword ptr 4
fdwReason= dword ptr 8
lpvReserved=dword ptr 0ch
mov eax, 1
retn 0ch
_DllMain@12 endp
```
拆解涵义为:
| 代码行 | 含义 |
| ----------------------------- | ------------------------------------------------------------ |
| `_DllMain@12 proc near` | 函数名为 `DllMain`,`@12` 表示使用 `__stdcall` 调用约定,3个参数共 12 字节(3×4),由被调用者清理栈 |
| `hinstDLL = dword ptr 4` | 第一个参数:DLL 实例句柄 |
| `fdwReason = dword ptr 8` | 第二个参数:调用原因(加载/卸载/线程创建等) |
| `lpvReserved = dword ptr 0Ch` | 第三个参数:保留参数 |
| `mov eax, 1` | 将返回值设为 1(即 `TRUE`),告诉系统“加载成功” |
| `retn 0Ch` | 返回调用者,同时将栈上 12 字节的参数弹出 |
实质上,DllMain函数什么都没有做,只是返回一个成功结果。说明日志的初始化、文件创建、写入等操作都不发生在DLL加载阶段,而是在后续被V5.exe调用某个导出函数时才触发。
一般而言,只看到一个 `DllEntryPoint` 是不正常的——如果 DLL 真的只导出了入口点而没有其他函数,那么主程序根本无法通过正常方式调用它(除非使用 COM 或其他特殊机制),而我之前测试“删除 DLL 后导致程序崩溃”的事实证明主程序**确实依赖这个 DLL 提供的某些函数**。
在IDA中查询v5_Log.dll中的Segments信息,看到汇编语言把程序段分为以下几段:
- `.text`:代码段,范围`0x10001000 ~ 0x10010000`,属性R(可读)X(可执行),符合存放程序代码的特征,长度接近60KB,已经被IDA识别出多个函数,说明代码段是正常的
- `.rdata`:只读数据段,`0x10010000 ~ 0x100101B0`,存放常量、导入表这类只读数据
- `.idata`:导入表段,`0x100101B0 ~ 0x10014000`,用来存放导入的外部函数信息
- `.data`:可读写数据段,`0x10014000 ~ 0x10017000`,存放全局变量
回到刚才的Exports,只有`DllEntryPoint`一个导出,没有自定义的导出函数,这是非常典型的**注入型DLL**的特征:这类DLL不需要被其他程序显式调用导出函数,只需要被加载进进程,在`DllMain`(也就是这里的DllEntryPoint)里完成初始化、执行自己的逻辑(***截至目前,判断这个v5_Log.dll,应该是某个程序的日志钩子/记录类的注入模块***)。
接下来的思路:在IDA中前往View->Open subviews->Imports可以看到这个.dll调用了哪些系统的Win32API
1. 先看**导入表(Imports窗口)**,看看它导入了哪些Win32 API,能提前猜出它大概做什么:比如如果导入了`WriteFile`/`CreateFile`就是写日志,导入了`SetWindowsHookEx`就是做消息钩子,导入了`VirtualProtect`这类就大概率是做逆向修改、内存操作。
2. 然后去`DllEntryPoint`(入口点)看逻辑:可以在导出表双击`DllEntryPoint`就能跳转到它的汇编代码,这是DLL被加载后第一个执行的代码,所有核心逻辑都是从这里展开的。
在这里,双击**WriteFile**(疑似写入文件的函数代码段部分,因为日志在增长的本质就是程序不断写入文件信息)后跳转到IDA View-A:
在 IDA 中看到的 `.idata` 段是专门用于存放程序导入的外部函数地址的区域,通常被称为导入表或 IAT(Import Address Table)。该段集中了所有程序引用的系统 API 或第三方库函数的地址,在 PE 文件加载时,操作系统会查找并填充这些函数在内存中的真实地址,供程序调用。这里看到的 WriteFile 条目 `extrn WriteFile:dword` 即声明了一个外部导入函数 WriteFile,其后跟着的函数原型说明该程序需要调用 Windows API 的 WriteFile 来执行文件、管道、串口或套接字的数据写入操作。这个条目所在的位置本质上是一个指针槽位,等待加载器填入来自 kernel32.dll 的 WriteFile 实际地址。
注意到,上面红框定位的.text代码段部分,有个注释:StartAddress+[某个偏移地址]。我定位开始地址StartAddress的位置,以查看函数在程序启动的时候做了哪些事情,调用了哪些API,使用了哪些参数等:(在IDA View界面按G触发地址搜索窗口,输入StartAddress后点击OK)
下图是第一次打开IDA加载dll后的图形,完整名字是控制流程图(CFG),这是IDA反汇编文件后自动得到的。
我通过Jump to address,输入"StartAddress"后,IDA自动定位到某个函数代码段(红色箭头部分)
通过分析,得出以下结论:这一代码段中,
1. 函数入口与栈帧建立
- 指令 `mov ebp, esp` 和 `push ebp` 是典型的函数开头序列,用于**建立栈帧**。这标志着这是一个函数的起始位置。
- `ebp`(基址指针)在此过程中被设置为指向当前栈帧的底部,为后续局部变量访问和栈操作提供基础。
2. 作为结构化异常处理(SEH)的一部分
- 注释 `__unwind` 和 `SEH` 明确指出,此函数与**结构化异常处理(Structured Exception Handling)** 机制相关。
- 在Windows程序中,SEH用于处理运行时异常(如访问违规、除零错误)。`__unwind` 代码通常出现在**异常处理函数**或与**异常展开(Exception Unwinding)** 过程中,负责在异常发生时清理栈状态、释放资源等。
- 地址 `10001610` 很可能就是此SEH处理函数或相关例程的起始地址。
**综合判断:** 跳转到的这个代码段,是一个**SEH异常处理相关函数的入口点**。其开头指令负责初始化函数的执行环境(栈帧),后续代码(未在参考文本中完整显示)预计会包含异常过滤、处理逻辑或栈清理代码。
到这里,似乎又陷入了僵局,现在是能够检索到”一切开始的地方StartAddress,但这并不是我要找的函数入口地址。复盘一下:程序启动后,随着代码的执行,它会调用很多W32API,我能够在IDA Imports中看得见,那么有什么途径,能够类似BurpSuite抓Web数据包一样在日志文件写入时我能够定位是哪个代码段在执行呢?
在此引入第二个关键点:Process Monitor(简称ProcMon),它是一款微软Sysinternals套件中的高级系统监视工具,可以通过它来捕获每个操作的完整线程堆栈。简单地讲就是:我一开V5.exe,就能看得见是哪个文件,哪一个偏移地址在“作祟”。
由于V5多开器运行后,\V5\Log\路径下会自动生成1.log、2.log...,所以在ProcMon中设置过滤器:
Operation is WriteFile,就能够看到是哪些代码段在写入,上图中每秒都在增加条目,我随便双击一条事件属性,并切换到选项卡,这里注意最后一条:**v5_Log.dll + 0x1a2b**
这是一条关键信息,代表着某个时间点,v5_Log.dll+的偏移地址在往1.log中写入日志信息
杀回IDA,直接定位.text代码段的0x1a2b偏移地址,看到是test eax, eax
`test eax, eax` 是逆向分析中的返回值检查指令,它通过对 eax 寄存器自身进行按位与运算来设置标志位,但不会改变 eax 的值。该指令的经典用途是判断某个函数调用或操作的返回结果是否为零,以便后续条件跳转(如 je、jne)决定程序分支。在日志写入相关的流程中,若该指令紧跟在某个函数调用之后,通常意味着程序正在检查前一步操作的返回值(例如日志写入是否成功、文件句柄是否有效等),此时 `eax` 中存放的往往是函数返回的状态码或错误号。OK,到这里,我相信标红部分就是我要找的修改点了。
第三个关键点接踵而至:那我怎么修改.dll呢?
这里使用WinHEX,它是一款十六进制编辑器,能够以二进制方式直接打开.dll数据,然后定位到偏移地址后修改值。(后头我用IDA Pro,x32dbg也同样能够修改,原理相同)
直接找到对应偏移地址值,这里原始值是85,那怎么修改呢?
`85`是x86汇编`test`指令的操作码,`test eax,eax`通常是用来判断结果是否为0的条件判断,`WriteFile`调用前一般会做功能开关判断:如果判断结果为真,则执行写入日志的`WriteFile`调用。 当前偏移0x1a2b的`85`对应`test`指令,此处判断通过后就会触发日志写入。
**修改原理:跳过写入逻辑**:把这个判断改成直接不满足条件,让永远不走到`WriteFile`写入流程,最简单的修改是覆盖为`33`,对应修改方法:
- 将偏移`0x1a2b`的字节`85`改为`33`,下一个相邻字节(偏移0x1a2c)修改为`C0`,整体构成`xor eax, eax`指令,修改后`eax`会被清零,接下来的条件判断会结果为0,直接跳过写入日志的分支,不会执行WriteFile。
调整后,重启V5,发现1.log里面始终会空,逆向成功!
## 复盘
由于是第一次使用IDA进行逆向分析,对其内部功能和片段不太熟悉,在定位.text代码段上花了很长时间。其实这次逆向的核心思路就是**利用ProcMon监控抓到不停写入日志信息的偏移地址,再使用WinHEX找到它,再修改函数调用代码段的xor eax, eax指令清空eax寄存器导致条件判断结果为0让程序永远不会执行日志写入**。
在研究的路上,还有以下两点能够进一步研究的:
1. 既然核心是修改程序的某个偏移地址值,Cheat Engine原则上也能够通过lua来修改相应值;
2. V5多开器不仅仅调用了WriteFile,还有CreateFile,我尝试过将CreateFile下方的判定代码段修改掉,但是这样会导致程序崩溃,这一点暂时没有想到合适的方案。 这个分析很实用,v5作为小型化快速很好用 本帖最后由 chenpi0813 于 2026-8-1 11:27 编辑
anlexN 发表于 2026-8-1 10:00
Have you tried to use V5 to open multiple wechat? and your wechat account is banned?
Due to the V5 multiplexer only supporting opening 32-bit applications, WeChat cannot be opened using V5 multi window.
But you can use Windows batch processing scripts, the source code is as follows:
@echo off
start "" "C:\Wechat\WeChat.exe"
start "" "C:\Wechat\WeChat.exe"
..
..
exit
After double clicking to execute, multiple WeChat login windows will pop up on your screen. 很好 很强大 历害了,点赞。 非常好用感谢 Process Monitor的下载地址是:https://learn.microsoft.com/en-us/sysinternals/downloads/procmon
最近还在研究API Monitor,觉着对逆向研究还是很有帮助的,非常高兴和各位一起研究学习,加油 我一个不懂逆向的竟然把这篇文章看完了。可能因为用过V5,知识不够只能手动删除日志文件。楼主真厉害,加油。 ekeen21 发表于 2026-7-26 14:17
我一个不懂逆向的竟然把这篇文章看完了。可能因为用过V5,知识不够只能手动删除日志文件。楼主真厉害,加油 ...
我也是第一次逆向,一起学习~:lol ekeen21 发表于 2026-7-26 14:17
我一个不懂逆向的竟然把这篇文章看完了。可能因为用过V5,知识不够只能手动删除日志文件。楼主真厉害,加油 ...
是的,之前只能自己做个.bat文件,每次启动时删除某个路径下所有.log和.dmp文件,属于“忍它很久了”;www 厉害了,已学习!