• 3768阅读
  • 7回复

昭姐每天一练- 一步一步教你写DOTA外挂 [复制链接]

上一主题 下一主题
离线小昭
 

只看楼主 倒序阅读 使用道具 楼主  发表于: 2013-06-08

好久木有研究DOTA了,整理篇小菜文章。
首先,我们要提升外挂本身程序权限,使其能够有权限修改war3游戏的内存。这个c++可以使用如下代码
[cpp] view plaincopy
void EnableDebugPriv()//提升程序自身权限  
{  
        HANDLE hToken;  
        LUID sedebugnameValue;  
        TOKEN_PRIVILEGES tkp;  
        if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) return;  
        if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME,&sedebugnameValue))  
        {  
                CloseHandle(hToken);  
                return;  
        }  
        tkp.PrivilegeCount = 1;  
        tkp.Privileges[0].Luid = sedebugnameValue;  
        tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;  
        if (!AdjustTokenPrivileges(hToken, FALSE, &tkp, sizeof tkp, NULL, NULL)) CloseHandle(hToken);  
}  

其次,有了权限以后,我们要找到war3.exe进程ID。并打开进程以供编辑修改内存,到达作弊目的。
获得进程ID:下面这个函数方法就是返回进程的,直接写进程名称,如:GetPIDForProcess(“war3.exe”),还可以用FindWindow的方法,反正能找到进程ID就可以了。
[cpp] view plaincopy
//HWND hwar3=::FindWindow(NULL,TEXT("Warcraft III"));  
//DWORD PID, TID;  
//TID = ::GetWindowThreadProcessId (hwar3, &PID);  
DWORD GetPIDForProcess(char* process)//获取进程ID  
{  
        BOOL                    working;  
        PROCESSENTRY32          lppe= {0};  
        DWORD                   targetPid=0;  
        HANDLE hSnapshot=CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS ,0);  
        if (hSnapshot)  
        {  
                lppe.dwSize=sizeof(lppe);  
                working=Process32First(hSnapshot,&lppe);  
                while (working)  
                {  
                        if(strcmp((const char *)lppe.szExeFile,process)==0)  
                        {  
                                targetPid=lppe.th32ProcessID;  
                                break;  
                        }working=Process32Next(hSnapshot,&lppe);  
                }  
        }  
        CloseHandle( hSnapshot );  
        return targetPid;  
}  

注意:有的名称为War3.exe或war3.exe,用toolhelp32方式需要比较进程名字,这时是会区分大小写的。FindWindow则不用,窗口标题都是固定的Warcraft III。
进程ID已经找到,现在是不是直接打开修改内存作弊呢?不,还早呢。我们修改内存也不能乱来,你得先找到Game.dll判断游戏版本,对应修改,要不会把魔兽搞火了,突然跳出来,那你就崩溃了,后悔都来不及。有木有,有木有?开图导致游戏崩溃的老实交代一下。

下面的方法获取game.dll的基址和路径。GetDLLBase(“game.dll”,PID)直接返回的就是game.dll的基址,这个后面是需要用到的。定义一个全局变量TCHAR  LastDLLPath[260],LastDLLPath返回的就是game.dll路径,。
[cpp] view plaincopy
DWORD GetDLLBase(char* DllName, DWORD tPid)  
{  
        HANDLE snapMod;  
        MODULEENTRY32 me32;  
        if (tPid == 0) return 0;  
        snapMod = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, tPid);  
        me32.dwSize = sizeof(MODULEENTRY32);  
        if (Module32First(snapMod, &me32))  
        {  
                do  
                {  
                        if (strcmp(DllName,(const char *)me32.szModule) == 0)  
                        {  
                                strcpy(LastDLLPath ,me32.szExePath);//game.dll路径  
                                CloseHandle(snapMod);  
                                return (DWORD) me32.modBaseAddr;  
                        }  
                }while(Module32Next(snapMod,&me32));  
        }  
        else  
        {  
                 Powers=true;  
        }  
        CloseHandle(snapMod);  
        return 0;  
}  
还有就是使用native api ZwQueryVirtualMemory来获取gamedll的基址,这个有些麻烦不过还算是稍微底层些
[cpp] view plaincopy
typedef enum _MEMORY_INFORMATION_CLASS  
{  
    MemoryBasicInformation,  
    MemoryWorkingSetList,  
    MemorySectionName,  
    MemoryBasicVlmInformation  
} MEMORY_INFORMATION_CLASS;  
  
typedef long (NTAPI * PF_ZwQueryVirtualMemory)  
(         IN HANDLE ProcessHandle,  
IN PVOID BaseAddress,  
IN MEMORY_INFORMATION_CLASS MemoryInformationClass,  
OUT PVOID MemoryInformation,  
IN ULONG MemoryInformationLength,  
OUT PULONG ReturnLength OPTIONAL  
);  
typedef struct _UNICODE_STRING  
{  
    USHORT Length;  
    USHORT MaximumLength;  
    PWSTR Buffer;  
} UNICODE_STRING, *PUNICODE_STRING;  
DWORD GetGameDLLAddr(HANDLE hWar3Handle,WCHAR * ModuleName)  
{  
    DWORD startAddr;  
    BYTE buffer[MAX_PATH*2+4];  
    MEMORY_BASIC_INFORMATION memBI;  
    PUNICODE_STRING secName;    
    PF_ZwQueryVirtualMemory ZwQueryVirtualMemory;  
  
    startAddr = 0x00000000;  
    ZwQueryVirtualMemory = (PF_ZwQueryVirtualMemory)GetProcAddress(GetModuleHandleA("ntdll"),"ZwQueryVirtualMemory");  
    do{  
        if(ZwQueryVirtualMemory(hWar3Handle,(PVOID)startAddr,MemoryBasicInformation,&memBI,sizeof(memBI),0 ) >= 0 &&  
            (memBI.Type == MEM_IMAGE))  
        {  
            if( ZwQueryVirtualMemory(hWar3Handle,(PVOID)startAddr,MemorySectionName,buffer,sizeof(buffer),0 ) >= 0 )  
            {  
                secName = (PUNICODE_STRING)buffer;  
                if(wcsicmp(ModuleName, wcsrchr(secName->Buffer,'\\')+1) == 0)  
                {  
                    return startAddr;  
                }  
            }  
            // 递增基址,开始下一轮查询!  
        }  
        startAddr += 0x10000;  
    }  
    while( startAddr < 0x80000000 );  
    return 0;  
};  

这里也需要注意的是game.dll的大小写或者名称,如有的平台为game124.dll。然后用下面两个方法获得版本。定义全局变量WC3VER  g_War3Ver,enum WC3VER{_UN,_120E,_124B,_124E,_125B,_126B}。
[cpp] view plaincopy
void GetWar3Ver()  
{  
        TCHAR FileVer[64];  
        ODV(TEXT("%s"),LastDLLPath);  
        GetFileVer(LastDLLPath,FileVer,64);  
        ODV(TEXT("%s"),FileVer);  
        if(lstrcmpi(FileVer,TEXT("1, 20, 4, 6074")) ==0)  
        {  
                g_War3Ver=_120E;  
        }  
        else if(lstrcmpi(FileVer,TEXT("1, 24, 1, 6374")) ==0)  
        {  
                g_War3Ver=_124B;  
        }  
        else if(lstrcmpi(FileVer,TEXT("1, 24, 4, 6387")) ==0)  
        {  
                g_War3Ver=_124E;  
        }  
        else if(lstrcmpi(FileVer,TEXT("1, 25, 1, 6397")) ==0)  
        {  
                g_War3Ver=_125B;  
        }  
        else if(lstrcmpi(FileVer,TEXT("1, 26, 0, 6401")) ==0)  
        {  
                g_War3Ver=_126B;  
        }  
        else  
        {  
                g_War3Ver=_UN;  
        }  
}  
DWORD  GetFileVer(__in LPTSTR FileName, __out LPTSTR lpVersion, __in DWORD nSize)  
{  
        TCHAR  SubBlock[64];  
        DWORD  InfoSize;  
        InfoSize = GetFileVersionInfoSize(FileName,NULL);        if(InfoSize==0) return 0;  
        TCHAR *InfoBuf = new TCHAR[InfoSize];    
        GetFileVersionInfo(FileName,0,InfoSize,InfoBuf);  
        unsigned int  cbTranslate = 0;  
        struct LANGANDCODEPAGE  
        {  
                WORD wLanguage;  
                WORD wCodePage;  
        }  
        *lpTranslate;  
        VerQueryValue(InfoBuf, TEXT("\\VarFileInfo\\Translation"),  
                (LPVOID*)&lpTranslate,&cbTranslate);  
        // Read the file description for each language and code page.  
        wsprintf( SubBlock,    
                TEXT("\\StringFileInfo\\%04x%04x\\FileVersion"),  
                lpTranslate[0].wLanguage,  
                lpTranslate[0].wCodePage);  
        void *lpBuffer=NULL;  
        unsigned int dwBytes=0;  
        VerQueryValue(InfoBuf, SubBlock, &lpBuffer, &dwBytes);    
        lstrcpyn(lpVersion,(LPTSTR)lpBuffer,nSize);  
        delete[] InfoBuf;  
        return dwBytes;  
}  

获得版本后就可以OpenProcess然后根据对应的版本来修改内存以实现我们想要的东东了。
[cpp] view plaincopy
switch(g_War3Ver)  
{  
      case _120E:  
      //修改内存代码自己去找吧//大地图去除迷雾  
      PATCH(0x406B53,"\x90\x8B\x09");  
      PATCH(0x2A0930,"\xD2");  
      //野外显血          
      PATCH(0x166E5E,"\x90\x90\x90\x90\x90\x90\x90\x90");  
      PATCH(0x16FE0A,"\x33\xC0\x90\x90");  
      //视野外点选  
      PATCH(0x1BD5A7,"\x90\x90");  
      PATCH(0x1BD5BB,"\xEB");  
      //小地图显示单位  
      PATCH(0x1491A8, "\x00");  
break;  
      case _124B:  
      //小地图显示单位  
      PATCH(0x361EAB,"\x90\x90\x39\x5E\x10\x90\x90\xB8\x00\x00\x00\x00\xEB\x07");  
break;  
      case _124E:  
//至于作弊代码你们是直接写,还是写成一个方法调用,随你们自己。  
break;  
       case _UN:  
       default:  
break;  
  }  

PATCH,这是定义的一个宏,#define  PATCH(i,w)  WriteProcessMemory(hopen,(LPVOID)(g_dwGameAddr+i),w,sizeof(w)-1,0);实现向目标进程某个地址写入数据。
这个宏在这里使用WriteProcessMemory,如果你使用DLL注入的话就要用
#define PATCH(i,w) memcpy((LPVOID)(g_dwGameAddr+i),w,sizeof(w)-1)。
最后补充一下根据上面的DWORD GetDLLBase(char* DllName, DWORD tPid)和DWORD GetPIDForProcess(char* process)可以获得War3.exe进程加载的所有模块,如果单机启动,是加载本地的game.dll。如果在平台上启动游戏,你会发现加载的是平台自带的game.dll。可以修改下GetDLLBase函数打印下加载模块的路径自己看下。
这时我通过本机跟11加载时的截图


另外还要注意下使用tlhelp32库的函数时最好程序使用ansi编码。
离线莫名

只看该作者 沙发  发表于: 2013-06-25
很高深的样子,有些看不懂呀,求指教
莫名
离线ilovec

只看该作者 板凳  发表于: 2013-07-05
昭姐,是男的吧。
离线769286964

只看该作者 地板  发表于: 2013-10-29
好厉害!不过作弊码不会写!~~~(>_<)~~~
离线mystone

只看该作者 4楼 发表于: 2014-05-23
这样的代码看了多少次了,就是没有具体的视频教,光贴代码新人是完全看不懂搞不明白的,还有我看了下BR的MH源码我也看了一下,好多啊!!!还有待进一步的学习研究!!!
离线liuying5822

只看该作者 5楼 发表于: 2015-02-21
支持看下
离线liuying5822

只看该作者 6楼 发表于: 2015-02-21
支持下

只看该作者 7楼 发表于: 2015-03-12
我就是玩DOTA 的--
快速回复
限100 字节
如果您在写长篇帖子又不马上发表,建议存为草稿
 
上一个 下一个