分析别人的Unity变速器插件

  • 2017-07-23
  • 1,148

垃圾WP转换插件吧docx转过来全乱了 凑合着看吧

这次拿来开刀的小白鼠只支持部分Unity 而我也只逆向出核心算法 其他的我不管啦
我懒得用mac我在win下忽悠下用vscode写一个就好啦
好了正式开始 首先把小老鼠丢IDA分析好戳到入口函数F5

这个加速器的加速类型有3种模式就是Unity模式、AppController模式和AppDeleagate模式

其实这三个模式并没有什么差别都是为了找到一个时间点让window初始化好又能干活的地方初始化自己的东西

随便戳进去一个

没啥看头就是初始化东西和读取PreferenceLoader配置的地方值得注意的只有箭头指的不明觉厉的函数

戳进去看看

一个好消息看到了MSHookFunction IOS越狱开发的C/C艹函数的HOOK基本都是依赖这货

我们戳进去dword_2BDDC查找引用看看

没啥看头把函数排序下逐个函数戳一下看看

很走运第二个函数就有料了

可以看到是HOOK了gettimeofday这个函数

MSHOOKFunction的函数IDA F5分析不出来我们去看ARM吧

看到了提示一个sub_7018的函数戳进去

就是gettimeofday的proc 核心算法都找到了
好了开始山寨代码了
先上百度找下gettimeofday原型

表头文件:#include <sys/time.h>

函数原型:int gettimeofday(struct timeval *tv,struct timezone *tz)

函数说明:把目前的时间按tv所指的结构返回,当时地区的信息则放到tz所指的结构中

struct timeval
{
long tv_sec; //秒
long tv_usec;//微妙
}
struct timezone
{
int tz_minuteswest;//和格林威治时间差了多少分钟
int tz_dsttime;    //日光节约时间的状态
}

一找啥都出来了好了我们可以开始山寨了

按照尿性

V8是返回值0为success

v6 是开关

剥离出来的有效代码只有

其中V7=a1 即 第一个参数

if (qword_2BE18)
{
v1 = 1000000LL * *(_DWORD *)v7 + *(_DWORD *)(v7 + 4); //V1=当前时间微秒级别 1 s = 10^3 ms = 10^6 us = 10^9 ns
v2 = v1 - qword_2BE18; //V2=获当前时间与上一次原来时间的间隔
qword_2BE18 = v1; //备份当前时间到 上一次原来时间
v3 = (unsigned int)v2 * (unsigned __int64)(unsigned int)dword_2BB2C; //V3=加倍后时间间隔
//V4为时间间隔的纳秒级 所以要有10^3单位
//V4低32位=V3*1000的低32位
LODWORD(v4) = 1000 * v3;
//V4高32位=1000*(v3的高32位)+1000*(V3的高位32位+V2*倍率的符号位+V2高位*倍率)
HIDWORD(v4) = (1000 * (unsigned __int64)(unsigned int)v3 >> 32) + 1000 * (HIDWORD(v3) + v2 * (dword_2BB2C >> 31) + HIDWORD(v2) * dword_2BB2C);

qword_2BE20 += v4 / 1000;

*(_DWORD *)v7 = qword_2BE20 / 1000000;
*(_DWORD *)(v7 + 4) = qword_2BE20 - 1000000 * *(_DWORD *)v7;
}
else
{

qword_2BE18 = 1000000LL * *(_DWORD *)v7 + *(_DWORD *)(v7 + 4);
qword_2BE20 = 1000000LL * *(_DWORD *)v7 + *(_DWORD *)(v7 + 4);

}

从逻辑上看

qword_2BE18保存的是上一次原始时间

qword_2BE20 保存的是上一次修改过后的时间

dword_2BB2C保存的是变速倍数

接下来就开始抄代码了

#define USec_Scale (1000000LL)
struct timeval
{
long tv_sec; //秒
long tv_usec; //微妙
}
struct timezone
{
int tz_minuteswest; //和格林威治时间差了多少分钟
int tz_dsttime; //日光节约时间的状态
}
uint64_t lastOrigUSec = 0, lastActualUsec = 0;
int32_t multiple = 1;
int proc_gettimeofday(struct timeval *tv, struct timezone *tz)
{
int ret = (*func_orig_gettimeofday)(tv, tz);
if (lastOrigUSec != 0)
{
int64_t curUSec = tv->tv_sec * USec_Scale + tv->tv_usec; //当前USec
int64 invl = curUSec - lastOrigUSec; //获当前时间与上一次原来时间的间隔
lastOrigUSec = curUSec;
uint64_t newInvl = invl * (uint64_t)(uint32_t)multiple; //加倍后时间间隔

int64_t nSec;
LODWORD(nSec) = newInvl * 1000;
HIDWORD(nSec) = (1000 * (uint64_t)(uint32_t)newInvl >> 32) + 1000 * (HIDWORD(newInvl) + invl * (multiple >> 31) + HIDWORD(invl) * multiple);

lastActualUsec += NSEC / 1000;

tv->tv_sec = lastActualUsec/USec_Scale;
tv->tv_usec= lastActualUsec - USec_Scale * tv->tv_sec ;
}
else
{
lastOrigUSec = lastActualUsec = tv->tv_sec * USec_Scale + tv->tv_usec; //微妙级别
}
return ret;
}

抄完之后差不多就这样了

  1. 以上代码均没运行过+类型提示啥我就用啥 要使用的请自行修改

总结一下该插件过程:

  1. Hook UnityAppController类下的application didFinishLaunchingWithOptions方法在proc里初始化各种东西和sethook
  2. MSHookFunction _gettimeofday函数达到修改时间
  3. 进入proc_gettimeofday后先调用原函数再修改返回值

提示:游戏的帧率控制一般都是 帧数/时间间隔 如果时间间隔变大游戏就会加速