就是跟 Jass运行时有关了,就叫它 JassEnv 吧。不停地追溯这个 JassEnv 的值,最后来到
了这里:
int __fastcall sub_6F44C150(int a1, int a2, int a3, int a4, int a5, int a6)
{
int v6;
int v7;
int result;
v7 = a2;
v6 = sub_6F44BDF0(a1);
if ( a5 && *(_DWORD *)(v6 + 32) )
result = sub_6F460810();
else
result = sub_6F4603C0(v6, v7, a3, a4, a6, 0); // 追溯 caller一直到这个函
数,v6 就是 JassEnv
return result;
}
可以很明显的看出来,JassEnv 就是 6F44BDF0 这个函数的返回值。跟进后发现这个函数只
是根据 TLS中记录的一个结构中根据仅有的一个参数取出一个值。实地设断后发现他的返回
值有两个,参数也只有1、
2 两种情况。考虑到 war3 在运行时有两套 Jass 环境,一套是主环境,另一套是 AI 环境,
所以认为参数是用来表示取得哪一套环境的 JassEnv,测试后发现,参数是 1 时,返回主
JassEnv。这个函数命名为PGetCurrentJassEnv。
然后分析 I2S 函数:
int __cdecl jassnative_I2S(int a1)
{
char v2; // 这里应该是数组的,F5没有看出来
unsigned int v3;
v3 = (unsigned int)&v2 ^ dword_6FAC4160;
Storm_578(&v2, 0x100u, "%d", a1);
return sub_6F3BB560((int)&v2);
}
其中 Storm_578是 Storm.dll 中的函数,这个基本上跟 wsprintf 没区别了。发现转换后的
string 传给了 6F3BB560做参数。实地调试发现这个函数返回的正是stringid。这个函数命
名为 PSaveString。
至此,我们已经掌握了在native 中传递 string做参数的方法了。
DotA 6.71b 逆向分析
魔兽争霸的地图实际上是一种叫做 mpq 格式的压缩包,跟目录下的 war3.mpq 等文件是同一
种格式。网上已经有各种操作 mpq 格式的工具。这里推荐一下 HkeW3mModifier,很好很强
大。打开地图后全部解压,将解压后的units目录放在魔兽争霸目录下,打开地图编辑器,
就可以看到DotA 的单位和物品了。

用 JassCraft(推荐的Jass 脚本编辑器)打开解压出来的 war3map.j, 整理代码后在12053
行左右找到如下代码:
function W41 takes nothing returns boolean
local trigger t = GetTriggeringTrigger()
local location W51
local real HQI
if GetTriggerEvalCount(t) == 600 or S00 then
……
set FL0 = FL0 + 1
set N0 = CreateUnitAtLoc(Player(12), 'n00L', W51, bj_UNIT_FACING)
call SetUnitAcquireRange(N0, 150)
call VP1(N0)
……
return false
endfunction
也就是 Roshan 是通过 CreateUnitAtLoc 函数创建的,IDA 分析后发现,CreateUnitAtLoc
函数也是在内部调用 CreateUnit 函数,所以只要 Hook CreateUnit 就可以了。至于为什么
要这样处理……实际上这是个历史遗留问题,本来作者还想实现对方插眼提示的,不过后来
发现插眼的时候创建单位并不是使用CreateUnit函数,但是这样Hook的做法还是保留了下
来:)
接下来,在12246 行附近找到了如下函数:
function WK1 takes nothing returns boolean
……
if FM == false then
if WL1 and WM1 then
set WN1 = GetRandomInt(1, 5)
if WN1 == 1 then
call CreateItem('I007', x, y)
elseif WN1 == 2 then
……
endif
endif |