天天看點

一種簡單、安全的Dota全圖新思路 作者:LC 【轉】

我自己的話:看了大神的文章,受益匪淺,自己根據源碼也修改了部分,很好用,也一直在用~謝謝~

一種簡單、安全的Dota全圖新思路

作者:LC

            我是一個非計算機相關專業的工科生.半年前,我構思了一種簡單,安全的Dota全圖新思路,經過一周的努力,把它寫成了程式.應各位網友要求,在此開源,供學術交流. 

       遊戲是一種封裝體,開發者希望玩家看到的是遊戲畫面,音樂等等,希望得到的是玩家對遊戲角色的操作指令.這就是這個封裝體與玩家互動的Ouput和Intput.然而程式的運作必然會借助硬體,遊戲程式的運作,包括了各種資料接收,處理,運算,發送.遊戲資料并不是全部傳送的伺服器運算然後傳回,而是在本地完成大多數運算,運算過程肯定要在記憶體上完成.玩家看到的遊戲畫面,其實是各種遊戲資料在空間上的有序展示.無論是1602液晶LCD顯示器,還是計算機顯示屏,都存在一個映射關系: f(memroy)=Display.當然,我沒有什麼遊戲開發經曆,這隻是我的一些比較感性的想法.

       所謂全圖,就是知曉地圖作戰資訊.以往常見的思路,是去除戰争迷霧.我認為去除戰争迷霧可能涉及到修改記憶體位址,容易被對戰平台發現,是以沒有采用這種思路.我的思路是讀取記憶體位址.讀出對方英雄的(x,y)坐标,經過坐标變換後,顯示到小地圖上.

       問題一:如何通過讀取記憶體獲得遊戲資料?因為程式運作必然借助硬體,它們之間存在資料互動,是以封裝不可能是絕對完美的,隻需用程式遠端讀取遊戲程式所在的記憶體位址便可達到目的.

       問題二:如何把讀到的遊戲資料顯示在小地圖上?這個很簡單,測量算出資料(x,y)和屏顯(X,Y)的變換關系,讀到(x,y)坐标後,變換,用一個循環函數将屏顯函數挂起,便可在螢幕上不斷顯示最新的坐标點.

       問題三:如何獲得英雄坐标對應記憶體位址?我把這個放在最後說是因為這個問題是最難的,最核心的.我花了7天寫這個小程式,其中有4-5天是在解決這個問題.我有挺多思路,做了很多實驗:

          1.首先我采集了4組資料(用CE和War3trainer配合測位址資料),想看看在一把Dota中,10個英雄的X坐标之間有沒有線性的關系.我把這10個十六進制數排序,相減,發現間隔值有很多是一樣的,但不規律.是以,它們之間沒關系.我們無法通過測出一個X坐标位址,推算出其他.

          2.基址+偏移量法.好像和我們C語言課老師講的多級指針有關系,這種方法真是廣為流傳.其實我現在也不太會用CE測基址偏移量,不是不會用,是不太會用.是以我基本上也沒算出坐标X位址的基址和偏移量,當然也不能怪我.         

          3.複查法.連搞了三天還沒有一點進展,難免會用這種極端的手段,算是走了個彎路吧.所謂複查法,就是不斷篩選,把War3遊戲記憶體位址周遊一遍,存下符合條件的X#,完事再篩...故稱複查法. 從10W篩到2W,就篩不下去了,放棄.

        痛苦的實驗了無數遍,我決定還得從基址+偏移量入手,因為我認為我的測量手法不對,是以測不出基、偏.是以我不停的實驗,各種選英雄什麼的.真是無數次實驗...終于有一次,我偶爾-repick了一次,結果還真測出基址來了.于是我趕忙把實驗步驟記錄下來,照做了幾遍,果然測出了基址(在CE中展現為綠色數字).但是,第二天睡醒了起來做,就再也做不出來了,即使按照步驟來.完全想不出為什麼,隻是繼續不停的實驗,不停的實驗,做的多了,觀察的多了,突然就發現了問題:那個基址,記錄的是移動的機關的X#.關鍵詞:移動.

得到記憶體配置設定關系如下:

AddY=AddX+0x4;         AddY是儲存Y坐标值的變量所對應的記憶體位址;AddX同理. 這個關系是一個前輩測算出來的.

AddX=X#+0x78;       X#就是上一段話最後說的X#,一定要牢記.就是X#這個位址,正偏0x78個機關,便是AddX;我測的.

ReadProcessMemory(hProc,LPVOID(gameBase+reinterpret_cast<char*>(0xACE5B0)),&fx1,4,NULL);

上面這行代碼,是整個程式的靈魂,也就是我實驗了無數遍發現的遊戲記憶體位址配置設定規則:

  如果Game.dll的記憶體首注入點為gameBase,那麼這個關鍵基址是gameBase+0xACE5B0.一般情況下,gameBase=0x6F000000,對戰平台為了反作弊,改變了Game.dll的記憶體首注入點,使記憶體ZB器失效,除此之外,再無高明之政策,而這個關鍵基址0x6FACE5B0,它儲存的值,是即時在移動的機關的X#值.據我粗略的觀察,理論上,在 [t0,T]時間段内,ReadValue(6FACE5B0,t0)=X#,設這個機關坐标值(x,y),如果有dx/dt!=0或dy/dt!=0,那麼ReadValue(6FACE5B0,t)=X#.粗略感覺是這樣的.實際上,ReadValue(6FACE5B0,t)的值瞬息萬變,隻要t-->無窮,那它涵蓋了整個遊戲中所有正在移動的機關的X值,是以我們隻需定義一個數組,将符合條件的X#存起來,存夠了數,便把他們轉換,顯示,就達到目的了.

  我之前一直在說,符合條件的X#.實際上是說符合條件的(x,y)組合.那何為符合條件?我提幾條:

     1.浮點型資料類型

     2.x,y∈(0,500)

     3.(AddX,AddY)∉{(AddX,AddY)|Had saved ever before}

        第一條為啥就不說了.第二條,如果你好好做過實驗,你會發現dota地圖是個面積500x500的圖,這個500是個長度當量,不同的電腦,這個當量是一樣的,就是500.而顯示函數的 分辨率就随電腦分辨率而變,是以寫顯示函數的時候要刻意讀系統系統資料庫獲得分辨率資訊,這是後話了.第三條,意思就是已經有了的坐标位址,就不要再存了,避免重複而漏掉其他.

          X#值瞬息萬變,就要求有一個強力的判斷函數,來過濾出合格的X#,存好.這個函數直接影響程式效率,這也是我開源的一個原因:想請大家來完善它.

        雖然我說了,ReadValue(6FACE5B0,t)=X#,但也不盡如此.6FACE5B0的确是一個很優秀的基址,但也是含有雜質的.有些雜質位址不可思議的通過了判斷函數,被存了起來,很困擾.還有個問題是這個程式在别的電腦上可能運作不成功,有可能是權限問題..是以編譯好的exe請用管理者身份運作.這個程式雖小,還不是一兩句話能說清的,自己看代碼吧.打字累了.

我的聯系方式是:[email protected] 歡迎來信讨論.

本來想按照這個思路開發LOL挂,沒成功.缺乏工具,而且網遊試驗起來很卡,不靈活.

特别鳴謝: TC天馳,linger2012liu

TC天馳 很多人都認識他....大神,War3Trainer是他的作品..而且一直在更新,真是很好的實驗工具.感謝!

linger2012liu 我的程式的思路起源于他,基本上是以他的程式為基礎改的,...其實改動很多的..感謝!

程式作者:LC 轉載注明作者及出處http://blog.csdn.net/lc_dotaallmap/article/details/8578817

程式C源代碼:

#include <stdio.h>

#include <stdlib.h>

#include <windows.h>

#include <cstdlib>

#include <iostream>

#include <Tlhelp32.h>

void EnableDebugPriv();

void Getpid();

void Printcurrent();

int system(const char *string);

void Getaddress();

void Getgameinfo();

int Judgesize(float,float);

void Welcome();

double getscreenRX();

DWORD GetPIDForProcess(char* process);

DWORD GetDLLBase(char* DllName, DWORD tPid);

HANDLE hProc;

DWORD gameBase;

float x1;

float y1;

char* fx1;

char* addupx[6];

char* addupy[6];

HDC hdc;

HWND hwnd;

int info[2]={0,0};

double ScreenRate;

//-------------------------------------------------------------------------//

//程式作者:LC 轉載注明作者及出處//

// http://blog.csdn.net/lc_dotaallmap/article/details/8578817//

void main()

{

SetConsoleTitle("Dota For 1.24b");

Welcome();

EnableDebugPriv();

Getpid();

Getgameinfo();

Getaddress();

Printcurrent();

}

void Welcome()

{

printf("-----------------------------------------------------------------------------\n");

printf("\n  Dota all map program is running!\n");

printf("\n  Version 2.5 For Win7 & XP,War3-1.24b\n");

printf("\n\t\tProgrammed By LC\n");

Sleep(1500);

system("cls");

printf("-----------------------------------------------------------------------------\n");

}

void Getgameinfo()

{

  printf("What your Enemy is?\n");

  printf("Please Enter 9(TZ) or 1(JW).\n");

  scanf("%d",&info[0]);

  printf("\nHow Much Enemies you want to know?\n");

  printf("Please Enter 1-6.\n");

  scanf("%d",&info[1]);

  ScreenRate=getscreenRX();

}

void Getaddress()

{

 int k=0,i=0;

 char* nowaddx;

 int iexist=0;int tt;

    printf("\nNow we get the gameinfo and start to collect addresses.\n");

 info[1]--;

 for(k=0;k<=0+info[1];)

 {   

  int iexist=0;

  ReadProcessMemory(hProc,LPVOID(gameBase+reinterpret_cast<char*>(0xACE5B0)),&fx1,4,NULL);

  ReadProcessMemory(hProc,LPVOID(fx1+0x78),&x1,4,NULL);

        ReadProcessMemory(hProc,LPVOID(fx1+0x78+0x4),&y1,4,NULL);

  nowaddx=fx1+0x78;

   if((int)fx1)

   { 

  for(tt=0;tt<=i;tt++) if(addupx[tt]==nowaddx) iexist++;

  if(!(Judgesize(x1,y1)+iexist)) {addupx[i]=nowaddx;addupy[i]=nowaddx+0x4;i++;}

   }

  if(i>k) {k=i;printf("\nOne more:%x",fx1);}

 }

 printf("\nNow we get %d unit addresses!\n",k);

}

void Printcurrent()

{

  printf("\nWell Done!");

  printf("\nNow you can see your enemies on the map!");

  char ch[] = "@";

  float X1,Y1;

  while(1)

  { 

   hdc = GetWindowDC(hwnd);

      SetTextColor(hdc,RGB(255,0,0));

      SetBkMode(hdc,NULL);

    if(addupx[0]!=0)

 {   

  ReadProcessMemory(hProc,LPVOID(addupx[0]),&x1,8,NULL);

        ReadProcessMemory(hProc,LPVOID(addupy[0]),&y1,8,NULL);

  X1=0.36*x1*ScreenRate+5;

        Y1=-0.36*y1+753;

        TextOut(hdc,X1,Y1,"1",strlen(ch));

 }

 if(addupx[1]!=0)

 { 

  ReadProcessMemory(hProc,LPVOID(addupx[1]),&x1,8,NULL);

        ReadProcessMemory(hProc,LPVOID(addupy[1]),&y1,8,NULL);

     X1=0.36*x1*ScreenRate+5;

        Y1=-0.36*y1+753;

        TextOut(hdc,X1,Y1,"2",strlen(ch));

 }

 if(addupx[2]!=0)

 { 

  ReadProcessMemory(hProc,LPVOID(addupx[2]),&x1,8,NULL);

        ReadProcessMemory(hProc,LPVOID(addupy[2]),&y1,8,NULL);

     X1=0.36*x1*ScreenRate+5;

        Y1=-0.36*y1+753;

        TextOut(hdc,X1,Y1,"3",strlen(ch));

 }

 if(addupx[3]!=0)

 { 

  ReadProcessMemory(hProc,LPVOID(addupx[3]),&x1,8,NULL);

        ReadProcessMemory(hProc,LPVOID(addupy[3]),&y1,8,NULL);

     X1=0.36*x1*ScreenRate+5;

        Y1=-0.36*y1+753;

  TextOut(hdc,X1,Y1,"4",strlen(ch));

 }

 if(addupx[4]!=0)

 { 

  ReadProcessMemory(hProc,LPVOID(addupx[4]),&x1,8,NULL);

        ReadProcessMemory(hProc,LPVOID(addupy[4]),&y1,8,NULL);   

     X1=0.36*x1*ScreenRate+5;

        Y1=-0.36*y1+753;

        TextOut(hdc,X1,Y1,"5",strlen(ch));

 }

 if((addupx[5]!=0)&&(info[1]!=0))

 { 

  ReadProcessMemory(hProc,LPVOID(addupx[5]),&x1,8,NULL);

        ReadProcessMemory(hProc,LPVOID(addupy[5]),&y1,8,NULL);

     X1=0.36*x1+5;

        Y1=-0.36*y1+753;

  TextOut(hdc,X1,Y1,"6",strlen(ch));

 }

   UpdateWindow(hwnd);

   ReleaseDC(hwnd,hdc);

  }

}

int Judgesize(float nowx,float nowy)

{   

 int Isfloat=((int)nowx!=nowx)&&((int)nowy!=nowy);

    int ifJW=((info[0]==1)&&((nowx+nowy)<=140)&&((nowx>20)&&(nowy>20)));

    int ifTZ=((info[0]==9)&&((nowx+nowy)>=830)&&((nowx<480)&&(nowy<480)));

 if((ifJW||ifTZ)&&Isfloat) return 0;

 else return 1;

}

double getscreenRX()

{

 DWORD ValueWidth=0;

 DWORD ValueHeight=0;

 HKEY hkResult;

 int ret=RegOpenKeyEx(HKEY_CURRENT_USER,"Software\\Blizzard Entertainment\\Warcraft III\\Video",0,KEY_ALL_ACCESS,&hkResult);

 DWORD VDlong=sizeof(ValueWidth);

 RegQueryValueEx(hkResult,"reswidth",0,NULL,(PBYTE)&ValueWidth,&VDlong); 

 RegQueryValueEx(hkResult,"resheight",0,NULL,(PBYTE)&ValueHeight,&VDlong);

 if( (ValueWidth>=500)&&(ValueWidth<=3500)&&(ValueHeight>=500)&&(ValueHeight<=3500) ) return ( (1.0*ValueWidth/ValueHeight)/(1.0*1024/768) );

 else return 1;

}

//-----------------------------------------------------------------------//

void Getpid()

{

   hwnd = FindWindowA(NULL,"Warcraft III");

   DWORD PID = 0;

   HWND hw = FindWindowA("Warcraft III", NULL);

   while(hw == NULL)

   {

          Sleep(500);   

          hw = FindWindowA("Warcraft III", NULL);

   }

      GetWindowThreadProcessId(hw,&PID);

      hProc = OpenProcess(PROCESS_ALL_ACCESS, false, PID);

      gameBase = GetDLLBase("Game.dll", PID);

   if(!gameBase) 

   {

    printf("\nWarning:Not Get Debug Privilege.");

    gameBase = 0x6F000000;

   }

   if(!hProc) 

   {

    printf("\nNot Get hProc.");

   }

}

void EnableDebugPriv()

{

  HANDLE hToken;

  LUID sedebugnameValue;

  TOKEN_PRIVILEGES tkp;

  if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))

    CloseHandle(hToken);

  if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &sedebugnameValue))

  {

    CloseHandle(hToken);

    system("PAUSE");

  }

  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 );

}

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,me32.szModule) == 0){

                CloseHandle(snapMod);

    return (DWORD) me32.modBaseAddr;

            }

        }while(Module32Next(snapMod,&me32));

    }

    CloseHandle(snapMod);

    return 0;

}

程式作者:LC 轉載注明作者及出處http://blog.csdn.net/lc_dotaallmap/article/details/8578817