浩劫者12345 发表于 2025-3-22 01:55

去除QQ聊天窗口的平滑滚动动画

本帖最后由 浩劫者12345 于 2025-3-22 02:20 编辑

## 去除QQ聊天窗口的平滑滚动

QQ版本: PC经典版 9.7.23 (29394)
工具: Cheat Engine (扫内存工具), IDA (逆向工具)

QQ在大概四五年前加入的聊天窗口的平滑滚动, 即滚动鼠标滚轮时, 有一个滚动的过渡动画, 而不是一帧直接到位

但是这个动画有缺点: 因为动画帧是跑在主线程的, 当QQ特别卡的时候, 动画就会严重掉帧, 非常影响使用

本文研究如何去除滚动动画, 直接一帧到位

本文图片较多, 内容浅显, 新手应该也能看懂 (我也是逆向新手)

---

上半部分是聊天窗口的, 消息列表的在**下半部分 - [去除平滑滚动 (消息列表)](#去除平滑滚动-消息列表)**

使用 Cheat Engine 打开 QQ 进程, 打开一个聊天界面, **扫描选项选 `未知初始值`**, 进行扫描




然后将聊天页面向下滚动, 扫描选项选 `增大值`, 点下一步



重复多次, **向上滚动就筛选 `减小值`, 向下滚就筛选 `增大值`**, 直到出现两个看起来很像页面滚动高度的值为止
然后添加到地址列表



修改第一个值会发现, 这个值是滚动条的
修改第二个值没有生效, 再次滚动会被新值覆盖



我们研究第二个值, 对其**查找写入操作**
然后滚动聊天框, 找到一处代码 `AFCtrl.dll+9F643 mov ,eax`
将其添加到代码列表



点击 `显示反汇编` 打个断点玩玩看

可以发现, 打断点的情况下, 滚动一次鼠标滚轮, **第一个 tick 滚动了 `11 (0xB)` 像素, 第二个 tick 滚动到了 `90 (0x5A)` 像素**



这附近的汇编不好看懂, **我们直接上 IDA**
IDA 的地址和 CE 有点不一样, 我喜欢把二进制复制出来搜索






反编译出来可以发现是把 `a2` 赋值给了内存, 并且发现 `a2` 是该函数的入参



> 提示: 可以按 `Alt+M` 添加地址书签, 方便后续找回这个位置
> 按 `Ctrl+Shift+M` 查看地址书签
>
>
>

在函数头选择 `查找引用`, 直接点进去




发现`v3 = *a2`, `*a2` 是入参, 继续往上找



但是发现找不到直接调用的方法了



我们直接开启调试器**打断点**

选择 `附加到进程`, 选择 QQ 进程




等待一段时间加载完 symbols, 我们用地址簿找回刚刚的代码, 打断点
再次滚动聊天框, 触发断点后, 就能看**调用栈**了



往上找, 发现这里是靠指针调用的函数, 所以没法静态找出引用
这里 `a2` 又是入参, 继续往上找



终于发现 `v4` 似乎是在这个函数通过一系列计算得到的



这里看 `v4` 似乎是一堆复杂计算得到的
我反复打断点研究了好多次, 发现这个似乎是栈里面的 C++ 类, 里面有各种方法通过指针调用, 实在太烧脑了看不下去

打断点也看不出来这附近的变量跟最终滚动位置有关的
要么就是差几十个像素, 要么就是不知道为什么调用了一个函数就把值改写了




到这里重新思考一下破解思路

我们要做的是去掉平滑滚动, 也就是让它**一下子就滚动到最终位置**
那么应该有一个变量, **存放了最终滚动位置**

但是这个类是在栈里面的, 每次调用都变地址, 搜索起来极为麻烦

就在我对这一堆满天飞的指针头晕的时候, 我点开了调用栈的上一个函数...



再往上...
惊人的来了, 这个入参的 **`v14 (0xB)` 不就是当前 tick 滚动的像素数吗!**



经过多次打断点得出, **`v11 (0x5A)` 是滚动的总像素数**
下面的判断语句只有在滚动的**第一个 tick 会执行**, 用来计算第一个 tick 滚动的距离
`v12` 是上一次滚动的值
`v13` `v14` 分别是正负滚动像素数, 下面的判断语句用来判断是向上还是向下滚动



我们把那个**第一次才触发的语句强制跳过, 让它第一次就把所有像素滚完**



测试结果是非常成功的, 但是有个问题:
这个每次滚动的最终位置带有加速, 如果你鼠标滚动滚得很快, 那么聊天记录会飞得越来越快看不清

只要修改一下上面那句**获取总滚动像素的语句, 改成常量就可以了**

右键 `mov eax, `, 点汇编, 把 `` 改成常量 (比如 `100`)
按回车应用修改




现在每次鼠标滚动都是常量了

下面就能点 `编辑` - `修补程序` - `将补丁应用到...`, 输出修改后的文件了
直接放到 QQ 文件夹替换掉就能修改成功



**大功告成!**

> 另外, 按 `Ctrl+Alt+P` 可以查看已修改的字节
>
>


## 去除平滑滚动 (消息列表)

用上面一样的方法**找滚动高度值**, 找出来之后**查找写入**, 给 `GF.dll+105DFB - sub ,ecx` **打断点**



易发现**每个滚动 tick 都会触发一次断点**, 而且**每个 tick 滚动的像素数在寄存器 `ecx`**
(这里因为是对内存中的值进行减法, 所以往下滚动是减去负数, `FFFF FFE5` 转换为 uint32 是 `-27`)



这里不做汇编分析了, 直接上 IDA

发现是把 `v16` 赋给内存, `v16` 从 `a3` 来, `a3` 是入参




但是调用这个函数的地方太多了, 我们还是打断点



往上一翻就很明显了, 我们直接研究这个 `v4`



容易发现 `v4` 前面对 `*(_DWORD *)(this + 596)` 有一个**减法运算**
多打几次断点可以看出, **`v4` 是当前 tick 滚动的距离, `this + 596` 存放剩余要滚动的像素数**

那么我们的思路就是让滚动一步到位, 直接把**剩余要滚动的总像素数, 作为传参**



把 `cvttsd2si eax, qword ptr ` 改为 `mov eax, `
立即生效了

> 这里插播一个知识:
> cvttsd2si 是 `double` 转 `int`, `qword ptr` 是 64 位指针, 跟 `` 搭配使用
> 这里不能只修改地址, 否则类型就出错了, 一开始没留意发现修改之后无法滚动



如果你不追求细节, 那么修改到这里其实就可以了

但是修改完之后你再打断点会发现, 虽然滚动一步到位了但还是**重复滚动了好几个 tick, 只不过每次的值都一样**
要做完美, 就要把**多余的滚动 tick 去掉**

如果你从头开始打断点就会发现, 当滚动到最后一 tick 时, 这个 `if ( !*(_DWORD *)(this + 592) )` 会进入
可以判断这是个**计数器, 指定要滚动多少个 tick 的**



往下翻有一个地方是把 `this + 592` 清零的, 我们试试让它强制执行, 即**第一个 tick 滚动完就将计数器清零**



调整后打断点实测这个函数**只会触发一次**了

修补完这两个地方之后, 主面板的平滑滚动也没了

**至此完成!**

浩劫者12345 发表于 2025-3-22 02:04

## 忘记说结论了

### 去除消息列表平滑滚动
`GF.dll` (9.7.21.903)

查 找: `F2 0F 2C 86 58 02 00 00`
替换为: `8B 86 54 02 00 00 90 90`

查 找: `66 0F 2F D1 72 16`
替换为: `66 0F 2F D1 90 90`


### 去除聊天窗口消息平滑滚动
注: 原版滚动存在加速, 这里修改为每次滚动100像素

`AFCtrl.dll` (9.7.23.29394)

```
查 找: 8B 86 F4 00 00 00 3B F9 73 43
替换为: B8 64 00 00 00 90 3B F9 EB 43
         ↑
         修改此 64 (十六进制) 为你想要的每次滚动的像素值
```

sunshan 发表于 2025-3-22 06:47

现在用QQ比较少了,但这个功能还是比较实用,感谢大佬

jayjjlee 发表于 2025-3-22 08:00

技术贴必须支持

wow888 发表于 2025-3-22 08:45

非常有点技术,感谢分享

l686 发表于 2025-3-22 08:51

大佬太强了,很实用

maya123 发表于 2025-3-22 09:11

讲解非常的详细,厉害了

zhiuan 发表于 2025-3-22 09:30

学习了,感谢楼主

hrflni 发表于 2025-3-22 09:36

大佬太强了,必须支持!

反向人cxr 发表于 2025-3-22 09:39

看不懂 根本看不懂 ,天文书{:1_937:}
页: [1] 2 3 4 5 6 7 8 9 10
查看完整版本: 去除QQ聊天窗口的平滑滚动动画