其实我是想研究war3.exe的自我保护方式的,但是直接从游戏下手又找不到下手的地方,于是就来分析外挂,外挂里面一定有关于游戏的保护方式的信息。
这里使用的游戏版本是1.20e
=============Wc3piderror120ae.exe的分析
Wc3piderror120ae.exe是一个补丁,如果使用MapHack时出现pid错误时使用这个程序对game.dll进行补丁,即可以消除该错误。
修改 的主过程:
00404D6B . FF15 90104000 call dword ptr [<&MSVBVM60.__vbaFil>; MSVBVM60.__vbaFileOpen
00404D71 . 8B7E 34 mov edi, dword ptr [esi+34]
00404D74 . 83FF 06 cmp edi, 6
00404D77 . 72 06 jb short 00404D7F
00404D79 . FF15 54104000 call dword ptr [<&MSVBVM60.__vbaGen>; MSVBVM60.__vbaGenerateBoundsError
00404D7F > 6A 01 push 1
00404D81 . 8B46 48 mov eax, dword ptr [esi+48]
00404D84 . 8B0CB8 mov ecx, dword ptr [eax+edi*4]
00404D87 . 51 push ecx ; 跟踪时会发现这里的偏移是995E
00404D88 . FF15 88104000 call dword ptr [<&MSVBVM60.__vbaFil>; MSVBVM60.__vbaFileSeek
00404D8E . 395D 0C cmp dword ptr [ebp+C], ebx
00404D91 . 8B7E 34 mov edi, dword ptr [esi+34]
00404D94 . 75 15 jnz short 00404DAB
00404D96 . 83FF 06 cmp edi, 6
00404D99 . 72 06 jb short 00404DA1
00404D9B . FF15 54104000 call dword ptr [<&MSVBVM60.__vbaGen>; MSVBVM60.__vbaGenerateBoundsError
00404DA1 > 6A 01 push 1
00404DA3 . 8B56 64 mov edx, dword ptr [esi+64]
00404DA6 . 03D7 add edx, edi
00404DA8 . 52 push edx
00404DA9 . EB 16 jmp short 00404DC1
00404DAB > 83FF 06 cmp edi, 6
00404DAE . 72 06 jb short 00404DB6
00404DB0 . FF15 54104000 call dword ptr [<&MSVBVM60.__vbaGen>; MSVBVM60.__vbaGenerateBoundsError
00404DB6 > 6A 01 push 1
00404DB8 . 8B86 80000000 mov eax, dword ptr [esi+80]
00404DBE . 03C7 add eax, edi
00404DC0 . 50 push eax
00404DC1 > 6A 01 push 1
00404DC3 . FF15 0C104000 call dword ptr [<&MSVBVM60.__vbaPut>; MSVBVM60.__vbaPut3
00404DC9 . 8B4D DC mov ecx, dword ptr [ebp-24]
00404DCC . FF15 58104000 call dword ptr [<&MSVBVM60.__vbaI2I>; MSVBVM60.__vbaI2I4
00404DD2 . 50 push eax
00404DD3 . FF15 4C104000 call dword ptr [<&MSVBVM60.__vbaFil>; MSVBVM60.__vbaFileClose
这段代码的任务是把文件中偏移0x995E处的数据修改为0xEB
恢复 的主过程
00404D6B . FF15 90104000 call dword ptr [<&MSVBVM60.__vbaFil>; MSVBVM60.__vbaFileOpen
00404D71 . 8B7E 34 mov edi, dword ptr [esi+34]
00404D74 . 83FF 06 cmp edi, 6
00404D77 . 72 06 jb short 00404D7F
00404D79 . FF15 54104000 call dword ptr [<&MSVBVM60.__vbaGen>; MSVBVM60.__vbaGenerateBoundsError
00404D7F > 6A 01 push 1
00404D81 . 8B46 48 mov eax, dword ptr [esi+48]
00404D84 . 8B0CB8 mov ecx, dword ptr [eax+edi*4]
00404D87 . 51 push ecx ; 跟踪时会发现这里的偏移是995E
00404D88 . FF15 88104000 call dword ptr [<&MSVBVM60.__vbaFil>; MSVBVM60.__vbaFileSeek
00404D8E . 395D 0C cmp dword ptr [ebp+C], ebx
00404D91 . 8B7E 34 mov edi, dword ptr [esi+34]
00404D94 . 75 15 jnz short 00404DAB
00404D96 . 83FF 06 cmp edi, 6
00404D99 . 72 06 jb short 00404DA1
00404D9B . FF15 54104000 call dword ptr [<&MSVBVM60.__vbaGen>; MSVBVM60.__vbaGenerateBoundsError
00404DA1 > 6A 01 push 1
00404DA3 . 8B56 64 mov edx, dword ptr [esi+64]
00404DA6 . 03D7 add edx, edi
00404DA8 . 52 push edx
00404DA9 . EB 16 jmp short 00404DC1
00404DAB > 83FF 06 cmp edi, 6
00404DAE . 72 06 jb short 00404DB6
00404DB0 . FF15 54104000 call dword ptr [<&MSVBVM60.__vbaGen>; MSVBVM60.__vbaGenerateBoundsError
00404DB6 > 6A 01 push 1
00404DB8 . 8B86 80000000 mov eax, dword ptr [esi+80]
00404DBE . 03C7 add eax, edi
00404DC0 . 50 push eax
00404DC1 > 6A 01 push 1
00404DC3 . FF15 0C104000 call dword ptr [<&MSVBVM60.__vbaPut>; MSVBVM60.__vbaPut3
00404DC9 . 8B4D DC mov ecx, dword ptr [ebp-24]
00404DCC . FF15 58104000 call dword ptr [<&MSVBVM60.__vbaI2I>; MSVBVM60.__vbaI2I4
00404DD2 . 50 push eax
00404DD3 . FF15 4C104000 call dword ptr [<&MSVBVM60.__vbaFil>; MSVBVM60.__vbaFileClose
这段代码的任务是把文件中偏移0x995E处的数据修改为0x74
到Game.DLL中来找找被修改的代码是什么样子的,被修改的数据位于:
6F009880 /$ 55 push ebp
6F009881 |. 8BEC mov ebp, esp
6F009883 |. 81EC 20020000 sub esp, 220
6F009889 |. 53 push ebx
6F00988A |. 33DB xor ebx, ebx
6F00988C |. 56 push esi
6F00988D |. 57 push edi
6F00988E |. 885D F8 mov byte ptr [ebp-8], bl
6F009891 |. 885D F9 mov byte ptr [ebp-7], bl
6F009894 |. 885D FA mov byte ptr [ebp-6], bl
6F009897 |. 885D FB mov byte ptr [ebp-5], bl
6F00989A |. 885D FC mov byte ptr [ebp-4], bl
6F00989D |. C645 FD 01 mov byte ptr [ebp-3], 1
6F0098A1 |. FF15 9050706F call dword ptr [<&KERNEL32.GetCurrent>; [GetCurrentProcess
6F0098A7 |. 68 6C7A7E6F push 6F7E7A6C ; /FileName = "advapi32.dll"
6F0098AC |. 8945 E0 mov [local.8], eax ; |
6F0098AF |. 895D F4 mov [local.3], ebx ; |
6F0098B2 |. 33FF xor edi, edi ; |
6F0098B4 |. FF15 7850706F call dword ptr [<&KERNEL32.LoadLibrar>; \LoadLibraryA
6F0098BA |. 8BF0 mov esi, eax
6F0098BC |. 3BF3 cmp esi, ebx
6F0098BE |. 0F84 C3000000 je 6F009987
6F0098C4 |. 68 507A7E6F push 6F7E7A50 ; ASCII "AllocateAndInitializeSid"
6F0098C9 |. 8BD6 mov edx, esi
6F0098CB |. 8D4D E8 lea ecx, [local.6]
6F0098CE |. E8 CD000000 call 6F0099A0
6F0098D3 |. 85C0 test eax, eax
6F0098D5 |. 0F84 A5000000 je 6F009980
6F0098DB |. 68 407A7E6F push 6F7E7A40 ; ASCII "InitializeAcl"
6F0098E0 |. 8BD6 mov edx, esi
6F0098E2 |. 8D4D E4 lea ecx, [local.7]
6F0098E5 |. E8 B6000000 call 6F0099A0
6F0098EA |. 85C0 test eax, eax
6F0098EC |. 0F84 8E000000 je 6F009980
6F0098F2 |. 68 2C7A7E6F push 6F7E7A2C ; ASCII "AddAccessDeniedAce"
6F0098F7 |. 8BD6 mov edx, esi
6F0098F9 |. 8D4D F0 lea ecx, [local.4]
6F0098FC |. E8 9F000000 call 6F0099A0
6F009901 |. 85C0 test eax, eax
6F009903 |. 74 7B je short 6F009980
6F009905 |. 68 1C7A7E6F push 6F7E7A1C ; ASCII "SetSecurityInfo"
6F00990A |. 8BD6 mov edx, esi
6F00990C |. 8D4D EC lea ecx, [local.5]
6F00990F |. E8 8C000000 call 6F0099A0
6F009914 |. 85C0 test eax, eax
6F009916 |. 74 68 je short 6F009980
6F009918 |. 8D45 F4 lea eax, [local.3]
6F00991B |. 50 push eax
6F00991C |. 53 push ebx
6F00991D |. 53 push ebx
6F00991E |. 53 push ebx
6F00991F |. 53 push ebx
6F009920 |. 53 push ebx
6F009921 |. 53 push ebx
6F009922 |. 53 push ebx
6F009923 |. 53 push ebx
6F009924 |. 6A 01 push 1
6F009926 |. 8D4D F8 lea ecx, [local.2]
6F009929 |. 51 push ecx
6F00992A |. FF55 E8 call [local.6]
6F00992D |. 85C0 test eax, eax
6F00992F |. 74 4F je short 6F009980
6F009931 |. 6A 02 push 2
6F009933 |. 68 00020000 push 200
6F009938 |. 8D95 E0FDFFFF lea edx, [local.136]
6F00993E |. 52 push edx
6F00993F |. FF55 E4 call [local.7]
6F009942 |. 85C0 test eax, eax
6F009944 |. 74 3A je short 6F009980
6F009946 |. 8B45 F4 mov eax, [local.3]
6F009949 |. 50 push eax
6F00994A |. 68 FEFF1FF0 push F01FFFFE
6F00994F |. 6A 02 push 2
6F009951 |. 8D8D E0FDFFFF lea ecx, [local.136]
6F009957 |. 51 push ecx
6F009958 |. FF55 F0 call [local.4]
6F00995B |. 85C0 test eax, eax
6F00995D |. 74 21 je short 6F009980
6F00995F |. 8B45 E0 mov eax, [local.8]
6F009962 |. 53 push ebx
6F009963 |. 8D95 E0FDFFFF lea edx, [local.136]
6F009969 |. 52 push edx
6F00996A |. 53 push ebx
6F00996B |. 53 push ebx
6F00996C |. 68 04000080 push 80000004
6F009971 |. 6A 06 push 6
6F009973 |. 50 push eax
6F009974 |. FF55 EC call [local.5]
6F009977 |. 85C0 test eax, eax
6F009979 |. 75 05 jnz short 6F009980
6F00997B |. BF 01000000 mov edi, 1
6F009980 |> 56 push esi ; /hLibModule
6F009981 |. FF15 7450706F call dword ptr [<&KERNEL32.FreeLibrar>; \FreeLibrary
6F009987 |> 8B45 F4 mov eax, [local.3]
6F00998A |. 3BC3 cmp eax, ebx
6F00998C |. 74 07 je short 6F009995
6F00998E |. 50 push eax ; /pSID
6F00998F |. FF15 0050706F call dword ptr [<&ADVAPI32.FreeSid>] ; \FreeSid
6F009995 |> 8BC7 mov eax, edi
6F009997 |. 5F pop edi
6F009998 |. 5E pop esi
6F009999 |. 5B pop ebx
6F00999A |. 8BE5 mov esp, ebp
6F00999C |. 5D pop ebp
6F00999D \. C3 retn
这段代码等价于:
BOOL getAddr(DWORD *pProc,HMODULE hModule, LPCSTR lpProcName)
{
*pProc = GetProcAddress(hModule, lpProcName);
if (nProc)
return 1;
else
return 0;
}
BOOL fun()
{
SID_IDENTIFIER_AUTHORITY IdentifierAuthority;
SID sid = 0;
DWORD nRet = 0;
ACL acl;
HANDLE hCurrentProcess = GetCurrentProcess();
DWORD hLibModule = LoadLibraryA("advapi32.dll");
LPVOID AllocateAndInitializeSid, InitializeAcl, AddAccessDeniedAce, SetSecurityInfo;
IdentifierAuthority.value[0] = 1;
IdentifierAuthority.value[1] = 0;
IdentifierAuthority.value[2] = 0;
IdentifierAuthority.value[3] = 0;
IdentifierAuthority.value[4] = 0;
IdentifierAuthority.value[5] = 0;
if (temp)
{
if (getAddr(&AllocateAndInitializeSid, hLibModule, "AllocateAndInitializeSid") &&
getAddr(&InitializeAcl, hLibModule, "InitializeAcl") &&
getAddr(&AddAccessDeniedAce, hLibModule, "AddAccessDeniedAce") &&
getAddr(&SetSecurityInfo, hLibModule, "SetSecurityInfo"))
{
if (AllocateAndInitializeSid(&IdentifierAuthority, 1, 0, 0, 0, 0, 0, 0, 0, 0, &sid) &&
InitializeAcl(&acl, 0x200, 2) &&
AddAccessDeniedAce(&acl, 2, 0xF01FFFFE, &sid) &&
SetSecurityInfo(hCurrentProcess, 6, 0x80000004, NULL, NULL, &acl, NULL))
{
nRet = 1;
}
}
FreeLibrary(hLibModule);
}
if (local3)
{
FreeSid(sid);
}
return nRet;
}
在这里就可以看出来程序把0x995E处修改为EB的目的是跳过对SetSecurityInfo的调用,如此用OpenProcess打开游戏进程就不需要很高的权限了。
=============mh120e5.exe的分析
程序全名为MapHack,可以去掉游戏中的战争迷雾、可以看隐形单位、可以看对方英雄技能....
程序在初始化时执行了等价于以下代码的过程:
BOOL WINAPI dbgPrivilegeSet(PCHAR psName)
{
HANDLE hToken;
TOKEN_PRIVILEGES tp;
BOOL fOK = FALSE;
if (psName && OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken))
{
if (LookupPrivilegeValue(NULL, psName, &(tp.Privileges->Luid)))
{
tp.Privileges->Attributes = SE_PRIVILEGE_ENABLED;
tp.PrivilegeCount = 1;
fOK = AdjustTokenPrivileges(hToken, FALSE, &tp, 0, NULL, NULL);
}
}
return fOK;
}
init()
{
....
dbgPrivilegeSet("SeDebugPrivilege");//提权
....
}
函数dbgPrivilegeSet作用是改变当前进程的特权。MapHack调用这个过程改变自己的特权以获得足够的权限来打开游戏进程。
MapHack实现其功能的代码在00414D90和00415D60两处的函数里,函数00414D90里根据游戏版本初始化了补丁数据,函数00415D60将补丁数据写入游戏进程。
分析00414D90来获取补丁数据是不切实际的,可以换个角度考虑-补丁数据是要调用WriteProcessMemory来写入目标程序的,而这个函数的参数里包含了补丁数据和补丁数组,我们只需要截获所有对这个函数的调用并记录参数即可。
用以下脚本实现收集补丁数据的功能:
=============Script Begin================
var addr
var byte
eob lableb
lableb:
mov addr, esp
add addr, 8
mov addr, [addr]
mov byte, esp
add byte, 0c
mov byte, [byte]
mov byte, [byte]
and byte, FF
add byte, ";"
add addr, ", (LPVOID)(&var), 1, NULL);"
wrta "F:\mh120e5\out.txt", "var = 0x"+byte
wrta "F:\mh120e5\out.txt", "WriteProcessMemory(g_hProcess, (LPVOID)0x"+addr
run
=============Script End==================
我们可以用获取到的补丁数据来做一个自己的MapHack了
=============anti-maphack ^_^
想屏蔽掉maphack的功能只要每隔一段时间就把游戏数据恢复一下即可。使用之前获取补丁数据的方法同样可以获取恢复数据。
恢复数据:
while (1)
{
if (检测到关键代码被修改)
将恢复数据写回游戏内存,覆盖掉补丁数据
Sleep(xxxx);
}
但是这个方法也并不是什么好方法,如果MH用更高的频率往游戏里写补丁数据的话就没辙了。(代码见anti-mh_1文件夹)
其实anti-maphack中心思想就是不让他把关键数据补丁掉,如果我们把关键数据移位了呢?那么他补丁的数据就会写到错误的地方,导致程序崩溃,这也算达到了我们的目的了。
我这里使用的方法是将一处MapHack要补丁代码拷贝到别的地方,然后从原来的地方跳转过去执行。如此,当MapHack补丁的时候就会把跳转语句覆盖掉,如此就会导致程序执行出错,也算是达到目的了。
这里选择6F4069F7处来做个演示,在6F704BAB有一段足够长的空闲内存
从6F4069F7跳转到6F704BAB的指令:E9 AF E1 2F 00
原始指令:66 8B 50 3C 66 89 55 E4
从6F704BB3跳转到6F4069FF的指令:E9 47 1E D0 FF
(详细代码见anti-mh_2文件夹)
开启MapHack功能前运行anti-mh.exe,执行完毕后进入游戏一切正常,然后开启MapHack功能,游戏崩溃,试验成功:)
当然这个演示还是很简单的,如果MapHack想绕过这个防御只需要读6F4069F7处的代码,然后根据该处的代码定位到关键数据所在地。
不过我们可以设计一套算法来确定关键代码要移动到哪里去,然后设计一个locate函数来专门负责定位到这些代码,关键代码原来的地方就改成对locate的调用,然后加强对locate函数的保护,即可大大增加MapHack的制作难度
[培训]内核驱动高级班,冲击BAT一流互联网大厂工
作,每周日13:00-18:00直播授课