|
警告:此篇文章涉及的内容可能违反《星际争霸II》及《星际争霸II地图编辑器》最终用户协议,仅供技术研究,非法使用责任自负。
曾经jass有过自定义native的研究,叫做jAPI。星际2作为暴雪游戏,其结构和原理都与魔兽非常相似。当然,具体细节还是有不同的。
根据aeris的这篇帖子(http://bbs.islga.org/read-htm-tid-39991.html),以及后续研究,星际2的native注册是放置了一个结构体(位于.rdata),而后程序扫描注册,不同于jass的函数名和地址都写死在.code的做法。
用反汇编打开星际2程序(IDA或者带隐藏插件的OD,推荐allydbg),代码段搜索特征码
8D A4 24 00 00 00 00 57 8B C6 E8 ,代码位置-4 byte 即为native注册信息结构体的指针(实际为一句指令的一部分,little endian)
以注册信息结构体指针地址为准,
+0x1B byte 即为注册时判断native结束地址.
其余参考aeris的帖子就很容易懂了。
如果想要自定义一个新的native,有两种办法:
1. 修改这个结构体的内容
2. 修改代码,让它使用位于另一个地址的结构体
实际上1并不可行,因为结构体的大小已经被限定了。
使用方法2可以把整个native注册结构体复制出来,并且在尾端添加自己的native,而后修改代码令其读取修改后的结构体。
不过仅仅修改注册还是不够的,因为在游戏内执行脚本时,还有一道关卡。
每个Galaxy native在编译成字节码时都会得到一个序号,也就是从1到总native数量的序号。
同时,Galaxy脚本中的函数调用也会被翻译为调用某序号的函数,
因此执行的时候会额外判断该序号函数是否存在,如果超过已知总数——注意这个总数是写死在.code中的——则不予以执行。
所以,对代码进行修改也是必须的:
在之前提到的注册信息结构体指针地址的 - 0xFE 和 -0xEF分别是galaxy运行时所判断结构体地址(用以转换脚本调用到函数调用),和函数总数量。
下面是一段不保证能编译的简单代码,注册了GetLocalPlayer函数,返回本地玩家(呵呵。。。。。。你懂的)
- /*
- const int Offset_NativeBeginHook = ?
- // 8D A4 24 00 00 00 00 57 8B C6 E8 offset -0x4
- const int Offset_NativeEndHook = ?
- // BeginHook + 0x1B
- const int Offset_NativeTotalCount = ?
- // BeginHook - 0xFE
- const int Offset_NativeExecHook = ?
- // BeginHook - 0xEF
- const int Offset_LocalPlayer = ?
- // 55 8B EC 83 7D 08 00 74 11 83 3D ?? ?? ?? ?? 00 75 08 38 0D
- // Offset + 0x14
- */
- /* you need to include necessary libs here */
- DWORD *lpCurrentNativeInfo;
- int customNativeCount;
- DWORD *nativeBeginHook = (DWORD *)Offset_NativeBeginHook;
- DWORD *nativeEndHook = (DWORD *)Offset_NativeEndHook;
- int *nativeTotalCount = (int *)Offset_NativeTotalCount;
- DWORD *nativeExecHook = (DWORD *)Offset_NativeExecHook;
- DWORD oldNativeBeginHook;
- DWORD oldNativeEndHook;
- DWORD oldNativeExecHook;
- DWORD oldNativeTotalCount;
- void *lpSC2NativeInfo;
- void *lpnewNativeInfo;
- int nativeByteCount;
- void BindAllNatives ();
- BOOL DwPatch(DWORD *lpDwPatch, DWORD *lpOldVal, DWORD DwVal) {
- DWORD dwOldProtect;
- if(VirtualProtect(lpDwPatch, 4, PAGE_READWRITE, &dwOldProtect)){
- if (lpOldVal){
- *lpOldVal = *lpDwPatch;
- }
- *lpDwPatch = DwVal;
- return VirtualProtect(lpDwPatch, 4, dwOldProtect, &dwOldProtect);
- }
- return false;
- }
- void BindNative ( void *nativefunc, char *nativename, char returntype, char *paraminfo){
- *((void **)lpCurrentNativeInfo + 0x0) = nativefunc;
- *((char **)lpCurrentNativeInfo + 0x1) = nativename;
- *((byte *)lpCurrentNativeInfo + 0x8) = strlen(paraminfo);
- *((char *)lpCurrentNativeInfo+0x9) = returntype;
- strcpy(((char *)lpCurrentNativeInfo + 0xA), paraminfo);
- lpCurrentNativeInfo += ( 0x18 / 4 );
- customNativeCount ++;
- }
- void DoPatch(){
- DwPatch(nativeBeginHook, &oldNativeBeginHook, (DWORD)lpnewNativeInfo);
- DwPatch(nativeEndHook, &oldNativeEndHook, (DWORD)lpCurrentNativeInfo);
- }
- /* called by main function */
- void InitNatives () {
- lpSC2NativeInfo = (void *)*nativeBeginHook;//start loc
- lpnewNativeInfo = malloc(0x12000); //create enough room for ~3072 nativeinfos
- nativeByteCount = ( (int)*nativeEndHook - (int)*nativeBeginHook );
- customNativeCount = 0;
- if (lpnewNativeInfo) {
- ZeroMemory( lpnewNativeInfo, 0x12000);
- memcpy( lpnewNativeInfo, lpSC2NativeInfo, nativeByteCount);//copy all natives
- lpCurrentNativeInfo = (DWORD *)lpnewNativeInfo + nativeByteCount / 4;
- BindAllNatives();
- if (lpCurrentNativeInfo) {
- *(DWORD *)lpCurrentNativeInfo = 0xFFFFFFFF;
- DwPatch(nativeExecHook, &oldNativeExecHook, (DWORD)lpnewNativeInfo);
- DwPatch((DWORD *)nativeTotalCount, &oldNativeTotalCount, (DWORD)(customNativeCount + (nativeByteCount / 0x18)));
- DoPatch();
- }
- }
- int GetLocalPlayer(void) {
- return *(byte *) (Offset_LocalPlayer);
- }
- void BindAllNatives () {
- //custom natives
- BindNative(GetLocalPlayer,"GetLocalPlayer",'4',"-");
- }
复制代码
随后,你需要通过在地图中插入自定义natives.galaxy或者干脆在地图galaxy中声明该native:- native int GetLocalPlayer ();
复制代码
这样自定义native就与暴雪提供的native一样可以使用了。
如果想要让编辑器一同支持自定义native,则还需要对编辑器exe做同样的事情,在此就留给大家自己寻找了。 |
评分
-
查看全部评分
|