|
- 阅读本文前,请先阅读
- JAPI基本原理及应用(未完待续.......)
- [url]http://www.islga.org/bbs/read.php?tid=12371[/url]
复制代码
rahxephon为japi揭开了冰山一角,苦等一年却没有下文,只好自己摸索,略有心得,不敢独享。
一、搜索BindNative函数
上文说到只要将某一处的CALL Game.6F69DC30改为我们自己的函数就能给jass添加新函数,但是还有一个问题,怎么知道这个CALL Game.6F69DC30在哪呢?不同版本的game.dll的CALL Game.6F69DC30的位置不一样,绑定Native函数的地址也不总是6F69DC30,用固定地址只能在特定的war3版本中使用。
用上次的方法在OD里随便搜一个jass函数,可以看到下面的数据:
- 6F3CF20F    B9 F0DC3A6F     mov     ecx, 6F3ADCF0
- 6F3CF214    E8 07680700     call    6F445A20
- 6F3CF219    68 D826926F     push    6F9226D8        ; ASCII "()V"
- 6F3CF21E    BA E443936F     mov     edx, 6F9343E4   ; ASCII "DoNotSavplay"
- 6F3CF223    B9 10DD3A6F     mov     ecx, 6F3ADD10
- 6F3CF228    E8 F3670700     call    6F445A20
- 6F3CF22D    68 E489936F     push    6F9389E4        ; ASCII "(II)I"
- 6F3CF232    BA D443936F     mov     edx, 6F9343D4   ; ASCII "GetRandomInt"
- 6F3CF237    B9 20DD3A6F     mov     ecx, 6F3ADD20
- 6F3CF23C    E8 DF670700     call    6F445A20
- 6F3CF241    68 D892936F     push    6F9392D8        ; ASCII "(RR)R"
- 6F3CF246    BA C443936F     mov     edx, 6F9343C4   ; ASCII "GetRandomReal"
- 6F3CF24B    B9 60DD3A6F     mov     ecx, 6F3ADD60
- 6F3CF250    E8 CB670700     call    6F445A20
复制代码
仔细想下OD是怎么知道绑定Native函数的地址在这里的?对,是通过CJ函数的函数名,我们也可以偷偷学下OD的办法。
mov     edx, 6F9343C4   ; ASCII "GetRandomReal"
是把"GetRandomReal"字符串的地址6F9343C4压栈,来到6F9343C4地址可以看到
这里其实就是保存"GetRandomReal"的asccii码,好了,现在已经可以动手写个搜索函数了。总结下步骤
1、搜索game.dll里的"GetRandomReal"字符串,取得其首地址(当然也可以是其他函数)
2、搜索这个地址出现的位置
3、第二步的值加0x09就是call BindNativeFunc的地址了。
当然搜索"GetRandomReal"只需在.rdata段(122)和.data段(120)里搜,搜索地址只需在.text段里搜,熟悉PE文件结构的同学都应该知道。
好了这次真的可以写属于我们自己的japi.dll了。
二、数据转换
当你兴致勃勃地动手写第一个属于自己的jass函数时,你会郁闷地发现,jass虚拟机传进来的的参数,无论handle,string,integer还是real,全部一律是int。所以有必要把jass内部的数据转换函数找到,这样才能真正读到jass的变量里的内容。那么就从string开始吧。先找个用string作为输入参数的jass函数来看下,比如S2I,这次改用IDA,用老方法来到S2I的函数这里:
- .text:6F3AD4F0 jassS2I         proc near               ; DATA XREF: sub_6F3CBA60+14Fo
- .text:6F3AD4F0
- .text:6F3AD4F0 arg_0           = dword ptr  4
- .text:6F3AD4F0
- .text:6F3AD4F0                 mov     ecx, [esp+arg_0]
- .text:6F3AD4F4                 call    sub_6F4B4990
- .text:6F3AD4F4
- .text:6F3AD4F9                 test    eax, eax
- .text:6F3AD4FB                 jz      short loc_6F3AD50C
- .text:6F3AD4FB
- .text:6F3AD4FD                 cmp     byte ptr [eax], 0
- .text:6F3AD500                 jz      short loc_6F3AD50C
- .text:6F3AD500
- .text:6F3AD502                 mov     [esp+arg_0], eax
- .text:6F3AD506                 jmp     ds:atoi
- .text:6F3AD506
- .text:6F3AD50C ; ---------------------------------------------------------------------------
- .text:6F3AD50C
- .text:6F3AD50C loc_6F3AD50C:                           ; CODE XREF: jassS2I+Bj
- .text:6F3AD50C                                         ; jassS2I+10j
- .text:6F3AD50C                 xor     eax, eax
- .text:6F3AD50E                 retn
复制代码
这个函数很短,过程也很容易看得懂,先调用sub_6F4B4990取得一个地址,然后用atoi函数转换成int,显然sub_6F4B4990就是我们想要的字符串转换函数。依然可以用上面说过的搜索法,得到这个地址,以便适用于所有版本的war3。
很简单吧,其他的数据类型也是用类似的方法,不再细述。
三、操纵全局变量
这个稍微复杂些。静态分析已经看不出什么端倪了,我们来调试下war3,找到war3读写全局变量的方法。
先准备一个测试地图,这里只贴关键部分。
[codes=jass]
function act takes nothing returns nothing
    set udg_actboy168 = GetRandomInt(1000,2000)
endfunction
function test takes nothing returns nothing
    local timer t = CreateTimer()
    call TimerStart(t, 1.0, TRUE, function act)
endfunction
[/codes]
然后在GetRandomInt函数的出口处下一个断点(自然是用OD了),然后单步跟踪吧,仔细观察寄存器值的变化,这里需要一些耐心,在6F44F4B7附近(我的是122),"udg_actboy168"这个字符串终于出现了,想必读取全局变量地址的函书就在附近。单步跟踪附近的几个call,获得全局变量地址的函数就在这里- .text:6F44F4C0                 mov     ecx, [esi+2858h]
- .text:6F44F4C6                 push    ebp
- .text:6F44F4C7                 call    sub_6F43D4F0
复制代码 在数据窗口里查看从这个函数返回的值的位置,地址+20处,正是上一次全局变量的值(因为这里还没改),觉得不放心也可以多做几次来验证。
这个函数有两个输入参数,一个是ebp,就是变量名,另一个是[esi+2858h],想必就是一个记录全局变量的表的地址。esi想必就是jass虚拟机类的地址。
沿着此处一路向上跟踪esi的值是怎么来的,可以在6F43BF73处发现,这里call    sub_6F43BC10后就得到了这个值,一直传到后面去。sub_6F43BC10实际就是调用了TlsGetValue来得到,不管怎么样,我们可以通过直接调用sub_6F43BC10来得到这个值。不过sub_6F43BC10还有个输入参数,用OD在这里下断发现这个参数永远是2。
好了,获取全局变量地址的方法我们已经找到了
1、用sub_6F43BC10得到jass虚拟机类
2、用sub_6F43D4F0得到全局变量的地址
3、地址+20就是全局变量的值
等等,还有一个问题没有解决,那就是sub_6F43BC10函数和sub_6F43D4F0函数的地址,为了通用性,还必须通过搜索game.dll的方法来得到。
对于sub_6F43BC10,向上查找可以找到一个"config"字符串,利用它来定位吧;对于sub_6F43D4F0,我的方法是,自己写一个,照抄代码即可。
四、调用CJ函数
这个...还记得怎么搜索CJ函数地址吧。
五、后记
至此,我们编写jass内部函数已经和在jass里面写函数没什么区别了。希望此文能给你一些启发。 |
|