12558网页游戏私服论坛

 找回密码
 立即注册
游戏开服表 申请开服
游戏名称 游戏描述 开服状态 游戏福利 运营商 游戏链接
攻城掠地-仿官 全新玩法,觉醒武将,觉醒技能 每周新区 经典复古版本,长久稳定 进入游戏
巅峰新版攻 攻城掠地公益服 攻城掠地SF 新兵种、新武将(兵种) 进入游戏
攻城掠地公 散人玩家的天堂 新开 进入游戏
改版攻城掠 上线即可国战PK 稳定新区 全新改版,功能强大 进入游戏
少年江山 高福利高爆率 刚开一秒 江湖水落潜蛟龙 进入游戏
太古封魔录 开服送10亿钻石 福利多多 不用充钱也可升级 进入游戏
神魔之道 签到送元宝 稳定开新区 送豪华签到奖励 进入游戏
神奇三国 统帅三军,招揽名将 免费玩新区 激情国战,征战四方 进入游戏
龙符 三日豪礼领到爽 天天开新区 助你征战无双 进入游戏
王者之师 免费领豪华奖励 免费玩新区 6元送6888元宝 进入游戏
查看: 917|回复: 0

深入游戏变速底层原理以及内核变速的实现

[复制链接]

516

主题

516

帖子

1042

积分

实习版主

Rank: 7Rank: 7Rank: 7

积分
1042
发表于 2019-8-19 11:07:12 | 显示全部楼层 |阅读模式
深入游戏变速底层原理以及内核变速的实现

前言

当你接触了一款叫做“变速齿轮”的软件,你可以玩上一整个星期。因为它实在是太酷炫了,无论网页(Flash、HTML5)、还是小软件,又或者是单机游戏,他总能给你惊喜,唯独网游不能!那么我们今天就来探究一下变速原理,并且我们试试看,能不能写出一个更牛逼的变速软件。
我们本章节内容大致如下图:




变速原理探究

变速齿轮(GearNT 0.46) 原理

我们拿个经典的变速软件开刀,这就是“变速齿轮”了,感觉变速的始祖就是他了。记得之前读过一篇文章,关于兄弟变速的,内容大致是说兄弟变速的创造历史,作者也是个很早很牛逼的程序辕。他写的变速器是受到了“变速齿轮”的启发,并且用了多个方面重新实现了一款变速软件,可以通过内核实现变速。所以,为了起到同样的这种启发作用,我们就拿“变速齿轮”这款软件做为学习的样本吧。
首先打开一个32位的记事本,作者的系统是 Win7x64 的,但是一代经典还是年事已高,只支持32位软件,所以吧用个32位的记事本研究下咯。然后在“变速齿轮”里对记事本调一下速度,随便调,我就不截图了。使用软件PCHunter,扫描记事本中的应用层钩子,我们可以看到如下图:




看到他被挂钩了8个API相关个 inline hook,其中两个API钩子能够实现“继承”,简单的说就是打开一个登录器使用了“变速齿轮”加速,然后登录器打开游戏时,游戏也会被加速。然后剩下的6个API都是关键,分别是
GetTickCountQueryPerformanceCounterGetMessageTimeSetTimertimeGetTimetimeSetEvent游戏常用的一般都是高精度计时,比如 Timer 啊什么的并不常用于游戏,因为精度一般。接下来就让我们深入了解“高精度计时API”
高精度计时API

GetTickCountGetTickCount64QueryPerformanceCountertimeGetTime顾名思义,就是精度很高,时间不会产生偏差,我们可以写个例子代码(使用 MinGW 编译)如下





代码由for(;;){}来产生无限循环,Sleep(1000)来产生1秒的延迟。然后分别由3个高精度计时API来计算时间差,并吧计算结果显示出来。由图上可以看出,QueryPerformanceCounter和timeGetTime的计算精度还是可以的,GetTickCount的精度略差。我们暂且不管精度怎么样,接来下操起家伙(IDA)就是干。
逆向与深入计时API

QueryPerformanceCounter 原理详解

我们就先对QueryPerformanceCounter进行逆向吧,毕竟所有高精度计时API中,精度最最高的就是他。研究他比较有“说服力”(假的,纯粹是因为很想知道这个API的工作原理以及为毛精度如此高。我们先试用 OllyICE 跟一下发现QueryPerformanceCounter的原型是ntdll.RtlQueryPerformanceCounter,我们就使用IDA对ntdll进行逆向。





由于算法还是比较复杂的,而且运算比较多,用的汇编指令不是特别常见,所以我们还是直接F5吧



我们得到他的返回值的算法应该就是这样
(LARGE_INTEGER)((MEMORY[0x7FFE03B8] + __rdtsc()) >> (MEMORY[0x7FFE02ED] >> 2))大致原理就是(*a+rdtsc)>>(*b>>2),a、b是两个内存指针,我们尝试下
使用CE,打开程序的7FFE03B8地址,可以看到这个数字是在跳动(变化)的,我们要改的话不怎么好改,再打开7FFE02ED地址,可以看到一个固定的值不会变化。那我们尝试改一下这个地址里的值,然后发现改不了。然后又试了下写进程序里用WriteProcessMemory修改也改不动,再尝试使用VirtualProtectEx修改内存属性PAGE_EXECUTE_READWRITE,完了改写数值能成功,但是会导致GetTickCount和timeGetTime计时异常。哎呀,难道说…… 这个地址,有蹊跷!




深入 0x7FFE0000 SharedUserData

我们百度0x7FFE0000这个地址,得到了一个叫做 SharedUserData 的东东,是个内核的映射,Ring3下是只读的,所以如上,我们并改不了他的数据(确切的说改了,数据就不会同步,会导致大量的API无法工作)。
这个结构在应用层和内核的地址如下表
内核起始地址内核结束地址用户起始地址用户结束地址32 系统0xFFDF00000xFFDF0FFF0x7FFE00000x7FFE0FFF64 系统0xFFFFF780 000000000xFFFFF780 00000FFF0x7FFE00000x7FFE0FFF然后我们使用 LiveKD / WinDbg 对 KUSER_SHARED_DATA 的结构进行列出,在我的电脑(Win7x64 7601)上,他的结构如下
WDFLDR!KUSER_SHARED_DATA   +0x000 TickCountLowDeprecated : Uint4B   +0x004 TickCountMultiplier : Uint4B   +0x008 InterruptTime    : _KSYSTEM_TIME   +0x014 SystemTime       : _KSYSTEM_TIME   +0x020 TimeZoneBias     : _KSYSTEM_TIME   +0x02c ImageNumberLow   : Uint2B   +0x02e ImageNumberHigh  : Uint2B   +0x030 NtSystemRoot     : [260] Wchar   +0x238 MaxStackTraceDepth : Uint4B   +0x23c CryptoExponent   : Uint4B   +0x240 TimeZoneId       : Uint4B   +0x244 LargePageMinimum : Uint4B   +0x248 AitSamplingValue : Uint4B   +0x24c AppCompatFlag    : Uint4B   +0x250 RNGSeedVersion   : Uint8B   +0x258 GlobalValidationRunlevel : Uint4B   +0x25c TimeZoneBiasStamp : Int4B   +0x260 Reserved2        : Uint4B   +0x264 NtProductType    : _NT_PRODUCT_TYPE   +0x268 ProductTypeIsValid : UChar   +0x269 Reserved0        : [1] UChar   +0x26a NativeProcessorArchitecture : Uint2B   +0x26c NtMajorVersion   : Uint4B   +0x270 NtMinorVersion   : Uint4B   +0x274 ProcessorFeatures : [64] UChar   +0x2b4 Reserved1        : Uint4B   +0x2b8 Reserved3        : Uint4B   +0x2bc TimeSlip         : Uint4B   +0x2c0 AlternativeArchitecture : _ALTERNATIVE_ARCHITECTURE_TYPE   +0x2c4 AltArchitecturePad : [1] Uint4B   +0x2c8 SystemExpirationDate : _LARGE_INTEGER   +0x2d0 SuiteMask        : Uint4B   +0x2d4 KdDebuggerEnabled : UChar   +0x2d5 MitigationPolicies : UChar   +0x2d5 NXSupportPolicy  : Pos 0, 2 Bits   +0x2d5 SEHValidationPolicy : Pos 2, 2 Bits   +0x2d5 CurDirDevicesSkippedForDlls : Pos 4, 2 Bits   +0x2d5 Reserved         : Pos 6, 2 Bits   +0x2d6 Reserved6        : [2] UChar   +0x2d8 ActiveConsoleId  : Uint4B   +0x2dc DismountCount    : Uint4B   +0x2e0 ComPlusPackage   : Uint4B   +0x2e4 LastSystemRITEventTickCount : Uint4B   +0x2e8 NumberOfPhysicalPages : Uint4B   +0x2ec SafeBootMode     : UChar   +0x2ed Reserved12       : [3] UChar   +0x2f0 SharedDataFlags  : Uint4B   +0x2f0 DbgErrorPortPresent : Pos 0, 1 Bit   +0x2f0 DbgElevationEnabled : Pos 1, 1 Bit   +0x2f0 DbgVirtEnabled   : Pos 2, 1 Bit   +0x2f0 DbgInstallerDetectEnabled : Pos 3, 1 Bit   +0x2f0 DbgLkgEnabled    : Pos 4, 1 Bit   +0x2f0 DbgDynProcessorEnabled : Pos 5, 1 Bit   +0x2f0 DbgConsoleBrokerEnabled : Pos 6, 1 Bit   +0x2f0 DbgSecureBootEnabled : Pos 7, 1 Bit   +0x2f0 SpareBits        : Pos 8, 24 Bits   +0x2f4 DataFlagsPad     : [1] Uint4B   +0x2f8 TestRetInstruction : Uint8B   +0x300 QpcFrequency     : Int8B   +0x308 SystemCallPad    : [3] Uint8B   +0x320 TickCount        : _KSYSTEM_TIME   +0x320 TickCountQuad    : Uint8B   +0x320 ReservedTickCountOverlay : [3] Uint4B   +0x32c TickCountPad     : [1] Uint4B   +0x330 Cookie           : Uint4B   +0x334 CookiePad        : [1] Uint4B   +0x338 ConsoleSessionForegroundProcessId : Int8B   +0x340 TimeUpdateSequence : Uint8B   +0x348 BaselineSystemTimeQpc : Uint8B   +0x350 BaselineInterruptTimeQpc : Uint8B   +0x358 QpcSystemTimeIncrement : Uint8B   +0x360 QpcInterruptTimeIncrement : Uint8B   +0x368 QpcSystemTimeIncrement32 : Uint4B   +0x36c QpcInterruptTimeIncrement32 : Uint4B   +0x370 QpcSystemTimeIncrementShift : UChar   +0x371 QpcInterruptTimeIncrementShift : UChar   +0x372 Reserved8        : [14] UChar   +0x380 UserModeGlobalLogger : [16] Uint2B   +0x3a0 ImageFileExecutionOptions : Uint4B   +0x3a4 LangGenerationCount : Uint4B   +0x3a8 Reserved4        : Uint8B   +0x3b0 InterruptTimeBias : Uint8B   +0x3b8 TscQpcBias       : Uint8B   +0x3c0 ActiveProcessorCount : Uint4B   +0x3c4 ActiveGroupCount : UChar   +0x3c5 Reserved9        : UChar   +0x3c6 TscQpcData       : Uint2B   +0x3c6 TscQpcEnabled    : UChar   +0x3c7 TscQpcShift      : UChar   +0x3c8 TimeZoneBiasEffectiveStart : _LARGE_INTEGER   +0x3d0 TimeZoneBiasEffectiveEnd : _LARGE_INTEGER   +0x3d8 XState           : _XSTATE_CONFIGURATION然后顺个便,把其他几个API也给逆了,对照上表,我们得到详细的注解。
QueryPerformanceCounter(LARGE_INTEGER)((MEMORY[0x7FFE03B8] + __rdtsc()) >> (MEMORY[0x7FFE02ED] >> 2));   +0x3b8 TscQpcBias       : Uint8B   +0x2ed Reserved12       : [3] UCharGetTickCountMEMORY[0x7FFE0004] * (MEMORY[0x7FFE0324] > 24)   +0x004 TickCountMultiplier : Uint4B   +0x320 TickCount        : _KSYSTEM_TIMEGetTickCount64(MEMORY[0x7FFE0324] * (unsigned __int64)MEMORY[0x7FFE0004] > 24)timeGetTime@0x41B00000dword_41B28FE0 + (unsigned __int64)((MEMORY[0x7FFE0008] - qword_41B28FD8) / 10000)   +0x008 InterruptTime    : _KSYSTEM_TIME这边还有个 _KSYSTEM_TIME 结构,这个结构是3个DWORD32。
ntdll!_KSYSTEM_TIME   +0x000 LowPart          : Uint4B   +0x004 High1Time        : Int4B   +0x008 High2Time        : Int4B有了他,我们能够事倍功半,呸!事半功倍。接下来我们就要进内核玩Ring0了,想想就有些小激动。
深入与验证

使用CE、MinGW(G++)验证

我们先来用 MinGW 写一个小程序,程序原理如下图



程序根据这个原理,来推出新值下的时间,和老的时间比值。这个值也就是之后能否实现加速的关键,如果无法反推出这个值,那么就无法实现加速。我们开始写代码吧





程序完美运行!鼓掌,那我们就确定了这个值,能被用于加速,里面的数学原理我也不懂,但从结果表明,应该和4(1

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
楼主热帖
回复

使用道具 举报

*滑块验证:
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|Archiver|手机版|小黑屋|12558网页游戏私服论坛 |网站地图

GMT+8, 2024-5-8 01:16 , Processed in 0.093750 second(s), 31 queries .

Powered by Discuz! X3.4

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表