程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> 教你編寫DOTA外掛

教你編寫DOTA外掛

編輯:C++入門知識

好久木有研究DOTA了,整理篇小菜文章。
首先,我們要提升外掛本身程序權限,使其能夠有權限修改war3游戲的內存。這個c++可以使用如下代碼
 
 
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就可以了。
 
//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路徑,。
 
 
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的基址,這個有些麻煩不過還算是稍微底層些
 
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}。
 
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然後根據對應的版本來修改內存以實現我們想要的東東了。
 
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編碼。

摘自 2012見證奇跡的時刻到了

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved