若干個游戲輔助的分析手記(一)


  一年一度的重裝系統“大工程”又浩浩盪盪的開工了。

  整理去年一年的工具及資料,今天起陸續放一點以前給客戶做的游戲輔助的手記。(一年多了,客戶應該不會介意吧)

  廢話不多說,今天是第一篇。

  《龍翔密傳》分析手記

  

選怪

突破口:

  CE搜索變化值,不停選怪取消怪,定位到如下代碼:

00413b5e - 89 be b0 00 00 00 - mov [esi+000000b0],edi

  稍微回溯一下

00435BDC   /E9 4B090000     jmp     0043652C
00435BE1   |8B0D 08536300   mov     ecx, dword ptr [635308]
00435BE7   |8B11            mov     edx, dword ptr [ecx]
00435BE9   |8B46 0C         mov     eax, dword ptr [esi+C]
00435BEC   |8B52 34         mov     edx, dword ptr [edx+34]
00435BEF   |6A 00           push    0                                ; 固定參數
00435BF1   |50              push    eax                              ; 怪物ID
00435BF2   |FFD2            call    edx                              ; 選怪CALL edx = 0048ac00

  該CALL就是選怪CALL。

該CALL 可以直接調用實現選怪
_asm
{
	push 0    // 固定參數
	push id    // 怪物ID
	call 0x0048ac00
}

 

怪物列表
突破口:
  根據選怪的反匯編代碼,跟進00435BF2這個CALL發現了另一個CALL:

00413AA9  |.  53            push    ebx                              ;  怪物ID
00413AAA  |.  FFD2          call    edx                              ;  根據怪物ID,取對象基址
再跟進此CALL發現另一個CALL:
00412CA5  |.  50            push    eax                              ;  怪物ID指針
00412CA6  |.  8D4C24 14     lea     ecx, dword ptr [esp+14]
00412CAA  |.  51            push    ecx                              ;  上層CALL的返回地址
00412CAB  |.  E8 30260000   call    004152E0                         ;  獲得怪物列表
//////
00412CAB  |.  E8 30260000   call    004152E0                         ;  獲得怪物列表

  這個CALL很關鍵了,是遍歷怪物列表的關鍵,以下是該CALL的分析:

004152E0  /$  55            push    ebp
004152E1  |.  8BEC          mov     ebp, esp
004152E3  |.  8B4F 18       mov     ecx, dword ptr [edi+18]          ;  超級像怪物列表(ecx = [01c398b8 + 18] = 01c45390)
004152E6  |.  8B41 04       mov     eax, dword ptr [ecx+4]           ;  eax = [[[635210]+90+18]+4] = 20904618
004152E9  |.  83EC 10       sub     esp, 10
004152EC  |.  8078 15 00    cmp     byte ptr [eax+15], 0
004152F0  |.  53            push    ebx
004152F1  |.  56            push    esi
004152F2  |.  8BF1          mov     esi, ecx
004152F4  |.  75 1E         jnz     short 00415314
004152F6  |.  8B4D 0C       mov     ecx, dword ptr [ebp+C]           ;  怪物ID
004152F9  |.  8B09          mov     ecx, dword ptr [ecx]
004152FB  |.  EB 03         jmp     short 00415300
004152FD  |   8D49 00       lea     ecx, dword ptr [ecx]
00415300  |>  3948 0C       /cmp     dword ptr [eax+C], ecx
00415303  |.  7D 05         |jge     short 0041530A
00415305  |.  8B40 08       |mov     eax, dword ptr [eax+8]
00415308  |.  EB 04         |jmp     short 0041530E
0041530A  |>  8BF0          |mov     esi, eax                        ;  esi 將作為本CALL 的返回值
0041530C  |.  8B00          |mov     eax, dword ptr [eax]
0041530E  |>  8078 15 00    |cmp     byte ptr [eax+15], 0
00415312  |.^ 74 EC         \je      short 00415300

  經過查看00415300 到00415312代碼所訪問的內存模型,判斷怪物列表為一顆二叉樹. 上面首先獲得eax,即怪物二叉樹的根節點. eax = [[[[635210]+90+18]+4] 然后從00415300 到00415312是重點,這里遍歷二叉樹找出對應ID的對象. 左樹是小([eax]),右樹是大([eax+8]).[eax+4]是父節點.[eax+c]是ID. [eax+15]有點特別,是一個bool類型,通過反匯編代碼可以看出,此變量為整個遍歷的結束條件. 所以,大膽推測[eax+15]標示了該節點的前一個節點是否是選中狀態.如果為選中狀態則為true.

  通過調用此CALL的上層CALL : 00412CEF  |> \8B47 10       mov     eax, dword ptr [edi+10]          ; 可以看出[eax+10]就是怪物對象地址 所以可以聲明一個數據結構如下:

typedef struct _NPC
{
	NPC* LeftSmall;
	NPC* Parent;
	NPC* RightBig;
	int id;
	DATA* pData;
}NPC;

  根據反匯編代碼004152EC到00415312寫出大致的模擬算法如下:

NPC* GetAddressByID(int nID)
{
	NPC* a = A.Head;
	NPC* temp = NULL;
	NPC* find = NULL;
	if(a->selected == false)
	{
		int id = nID;
		do
		{
			if(a->id < id)// 如果當前ID小於給定的ID,則往大的方向遍歷
			{
				temp = a->RigthBig;
			}
			else
			{
				find = a;
				a = a->LeftSmall;
			}
		}while(a->selecte != false);
	}
	return find;
}

  下面是根據分析資料實現遍歷怪物列表的算法:(前序遞歸)

CArray<DATA*,DATA*&> NPCArray;
void PreOrder(NPC* Tree)
{
    if(Tree->id != 0)
    {
        NPCArray.Add(Tree->pData);
        PreOrder(Tree->LeftSmall);
        PreOrder(Tree->RightBig);
    }
}

  注意用此根節點遍歷出來包括了怪物對象,還包括其他一些無用的對象,需要過濾.
  這里過濾的方式是物品的話,最大血量及當前血量都是0,可以以此作為判斷是否是物品的根據.肯定二叉樹葉子節點有一個變量是標明該對象的類型的,是背包還是怪物還是地上的物品等等.目前沒有分析了.暫時用最大血量跟當前血量做判斷.

 

打怪

突破口:

  OD在GameClient模塊搜索"skill"查找到Send_Skill... 順藤摸瓜,回溯幾層CALL后,發現一個switch分支,當為7的分支的時候就是打怪

00423317  |.  E8 14200000   call    00425330
0042331C  |.  5F            pop     edi
0042331D  |.  5E            pop     esi
0042331E  |.  8BE5          mov     esp, ebp
00423320  |.  5D            pop     ebp
00423321  |.  C3            retn
00423322  |>  56            push    esi                              ;  esi = 上層ECX (DWORD [esi] 恆定為5e6e68); Case 7 of switch 0042327E
00423323  |.  E8 D81C0000   call    00425000                         ;  **************真正的打怪CALL 距離不夠會自動靠近一步
00423328  |.  5F            pop     edi
00423329  |.  5E            pop     esi
0042332A  |.  8BE5          mov     esp, ebp
0042332C  |.  5D            pop     ebp
0042332D  |.  C3            retn

  直接CALL 00425000 就會對當前選定的怪進行攻擊.
  esi = 上層ECX。

  以下是上層代碼:

00418F0F  |.  8B8B CC010000 mov     ecx, dword ptr [ebx+1CC]         ;  ecx = [上層ecx + 1cc] 上層ECX = 1EB35128
00418F15  |.  85C9          test    ecx, ecx
00418F17  |.  74 06         je      short 00418F1F
00418F19  |.  8B01          mov     eax, dword ptr [ecx]
00418F1B  |.  8B10          mov     edx, dword ptr [eax]
00418F1D  |.  FFD2          call    edx                              ;  edx = 00423210,內含真正的打怪CALL

  

  該層的ECX又等於[該層上層ECX + 1cc],經過實測該層上層ECX恆定為1EB35128(此值非常眼熟,挺像怪物列表里的一個值,有待考證)

經過以上分析,打怪CALL的調用代碼如下:

_asm
{
	mov eax,[1EB35128+1CC]
	push eax
	call 0x00425000
}

  上面的ECX = 1EB35128,此值非恆定,經過跟蹤發現此為一個NEW出來的地址.
  尋找過程:
  發現DWORD [esi]恆定為56e6e8,直接搜索常數,發現有兩個引用的地方,一個是構造函數,一個是析構函數,用構造函數來突破.構造函數附近代碼如下:

0041FC91  |.  E8 08771300   call    <jmp.&MSVCR90.operator new>      ;  !!!!!!!!!!!!!這里NEW了一個對象,打怪CALL需要用到
0041FC96  |.  83C4 04       add     esp, 4
0041FC99      8945 F0       mov     dword ptr [ebp-10], eax
0041FC9C      C645 FC 01    mov     byte ptr [ebp-4], 1
0041FCA0      3BC7          cmp     eax, edi
0041FCA2  |.  74 09         je      short 0041FCAD
0041FCA4  |.  56            push    esi
0041FCA5  |.  50            push    eax
0041FCA6  |.  E8 D5330000   call    00423080

  OK,既然是NEW出來的,那就給它做個內存補丁:

  在下面找一空白處(關鍵是見縫插針,不能覆蓋原來的代碼)
  填寫如下代碼:

  

0041FD31      A3 0AFD4100   mov     dword ptr [41FD0A], eax
0041FD36      8945 F0       mov     dword ptr [ebp-10], eax
0041FD39      E9 15010000   jmp     0041FE53

0041FE53      C645 FC 01    mov     byte ptr [ebp-4], 1
0041FE57    ^ E9 44FEFFFF   jmp     0041FCA0

  修改完畢后,原來的代碼變成了這樣:

0041FC91  |.  E8 08771300   call    <jmp.&MSVCR90.operator new>      ;  !!!!!!!!!!!!!這里NEW了一個對象,打怪CALL需要用到
0041FC96  |.  83C4 04       add     esp, 4
0041FC99      E9 93000000   jmp     0041FD31
0041FC9E      90            nop
0041FC9F      90            nop
0041FCA0  |.  3BC7          cmp     eax, edi
0041FCA2  |.  74 09         je      short 0041FCAD
0041FCA4  |.  56            push    esi
0041FCA5  |.  50            push    eax
0041FCA6  |.  E8 D5330000   call    00423080                         ;  此CALL里eax填充56E6E8
0041FCAB  |.  EB 02         jmp     short 0041FCAF

  注意:因為新增的代碼是在.text區段,而該區段是不能寫的,所以在測試的時候用PE Explorer修改該區段為可寫.否則
004236D9      A3 D8364200   mov     dword ptr [4236D8], eax這句代碼將出錯.用代碼實現的時候注意修改內存屬性。

(更新)在第一個測試版本中發現該CALL因為會判斷怪的距離,不夠的話會自動靠近.經過實測,當此CALL尚未執行完畢(即正在自動靠近怪物的時候),又調用此CALL的時候會讓游戲崩潰,由此判定此CALL不是線程安全的函數,需要調用者確保線程安全.所以這個CALL太雞肋!繼續找個完美的CALL。。。

004235B5  |> \8B42 08       mov     eax, dword ptr [edx+8]           ;  Case 3 of switch 00423555
004235B8  |.  8B4A 0C       mov     ecx, dword ptr [edx+C]
004235BB  |.  8B52 10       mov     edx, dword ptr [edx+10]
004235BE  |.  52            push    edx                              ;  edx = 1eb怪物ID
004235BF  |.  51            push    ecx                              ;  1
004235C0  |.  50            push    eax                              ;  0
004235C1  |.  E8 9A120000   call    00424860                         ;  

 之前的CALL實際上是消息循環自動調用的,檢查某個標記存在則自動調用之前找到的打怪CALL.而這里這個CALL可以理解為正是設定標志的.設定后可以讓消息循環自動調用.
注意!!!!!!!!!!!調用此CALL,需要內部的ESI賦值

以下是上個CALL的ESI賦值

0048B85A  |. /74 54         je      short 0048B8B0
0048B85C  |. |8B0D 10526300 mov     ecx, dword ptr [635210]
0048B862  |. |8B51 50       mov     edx, dword ptr [ecx+50]
0048B865  |. |8B8A CC010000 mov     ecx, dword ptr [edx+1CC]         ;  為打怪CALL,提供的ESI
0048B86B  |. |C74424 50 020>mov     dword ptr [esp+50], 2
0048B873  |. |BA 03000000   mov     edx, 3
0048B878  |. |66:895424 08  mov     word ptr [esp+8], dx
0048B87D  |. |8B10          mov     edx, dword ptr [eax]

  打怪代碼實現:  

_asm
{
	mov ecx,[635210]
	mov edx,[ecx+50]
	mov esi,[edx+1cc]
	push 0x6d//怪物ID
	push 1
	push 0
	call 00424860
}

怪物對象:
+14 ID范圍的最大值
+48 怪物ID
+11C X坐標
+120 Y坐標
名字:[[+158]+8]+8+4
突破口:CE搜名字,找到對應的怪后,下內存訪問斷點
血量:
當前:[[[+158]+8]+80]
最大:[[[+158]+8]+80+8]

004191A2  |.  8B86 58010000 mov     eax, dword ptr [esi+158]         ;  讀名字的關鍵開始
004191A8  |.  8B40 08       mov     eax, dword ptr [eax+8]
004191AB  |.  83C0 08       add     eax, 8
004191AE  |.  8378 18 10    cmp     dword ptr [eax+18], 10
004191B2  |.  72 05         jb      short 004191B9
004191B4  |.  8B40 04       mov     eax, dword ptr [eax+4]
004191B7  |.  EB 03         jmp     short 004191BC
004191B9  |>  83C0 04       add     eax, 4
004191BC  |>  8D50 01       lea     edx, dword ptr [eax+1]
004191BF  |.  90            nop
004191C0  |>  8A08          /mov     cl, byte ptr [eax]
004191C2  |.  40            |inc     eax
004191C3  |.  84C9          |test    cl, cl
004191C5  |.^ 75 F9         \jnz     short 004191C0

  當前選中對象的分析:

00436848  |.  8B4424 10     mov     eax, dword ptr [esp+10]
0043684C  |.  8BF0          mov     esi, eax
0043684E  |.  75 0F         jnz     short 0043685F
00436850  |.  C780 E8000000>mov     dword ptr [eax+E8], 3
0043685A  |.  E9 8E010000   jmp     004369ED
0043685F  |>  C780 E8000000>mov     dword ptr [eax+E8], 2
00436869  |.  D903          fld     dword ptr [ebx]
0043686B  |.  D998 A0000000 fstp    dword ptr [eax+A0]               ;  當前選中的物品ID
00436871  |.  D943 08       fld     dword ptr [ebx+8]
00436874  |.  C780 A8000000>mov     dword ptr [eax+A8], 1
0043687E  |.  D998 A4000000 fstp    dword ptr [eax+A4]               ;  當前選中的怪ID eax = 01c36c80 恆定
00436884  |.  E9 64010000   jmp     004369ED
00436889  |>  8B17          mov     edx, dword ptr [edi]             ;  Case 3 of switch 004367E7
0043688B  |.  8B42 18       mov     eax, dword ptr [edx+18]
0043688E  |.  8BCF          mov     ecx, edi
00436890  |.  FFD0          call    eax

  此段代碼會不停在浮點寄存器中壓棧出棧,eax = 01c36c80正是當前所選對象的基址.
eax = 01c36c80恆定,為確保以后游戲升級后能順利查找到該地址,下面是eax的特征碼
首先eax = [esp] + 10
查看函數開頭,實際是ebx的壓棧,繼續回溯ebx
則得到下列代碼

0051FF10  |.  395D C0       cmp     dword ptr [ebp-40], ebx
0051FF13  |.  0F85 87020000 jnz     005201A0
0051FF19  |.  8B0D 34526300 mov     ecx, dword ptr [635234]
0051FF1F  |.  8B1D 1C526300 mov     ebx, dword ptr [63521C]          ;  當前所選對象基地址
0051FF25  |.  8B01          mov     eax, dword ptr [ecx]
0051FF27  |.  8B33          mov     esi, dword ptr [ebx]             ;  GameClie.005E7EFC
0051FF29  |.  8B50 40       mov     edx, dword ptr [eax+40]
0051FF2C  |.  83C6 4C       add     esi, 4C
0051FF2F  |.  FFD2          call    edx

  

如上所示:[63521C]即為eax的基地址

+A0 當前選中物品的ID

+A4 當前選中怪的ID

 

物品列表

突破口

 CE監控 改寫 地址 01c36c80 + A0的代碼:

0043686b - d9 98 a0 00 00 00 - fstp dword ptr [eax+000000a0]// 排除掉了
0042fbf5 - 89 47 08 - mov [edi+08],eax
0041ff3e - c7 40 08 00 00 00 00 - mov [eax+08],00000000// 排除掉了

 0042fbf5 - 89 47 08 - mov [edi+08],eax是關鍵

  一路回溯,發現遍歷物品列表的函數 跟 遍歷怪物的函數是一樣的.
注意用此根節點遍歷出來包括了怪物對象,還包括其他一些無用的對象,需要過濾.
這里過濾的方式是物品的話,最大血量及當前血量都是0,可以以此作為判斷是否是物品的根據.肯定二叉樹葉子節點有一個變量是標明該對象的類型的,是背包還是怪物還是地上的物品等等.目前沒有分析了.

 

技能

 

004235EC  |.  53            push    ebx                              ;  -1
004235ED  |.  8B5C24 18     mov     ebx, dword ptr [esp+18]
004235F1  |.  53            push    ebx                              ;  -1
004235F2  |.  8B5C24 18     mov     ebx, dword ptr [esp+18]
004235F6  |.  53            push    ebx                              ;  -1
004235F7  |.  57            push    edi                              ;  怪物ID
004235F8  |.  52            push    edx                              ;  -1
004235F9  |.  51            push    ecx                              ;  2
004235FA  |.  50            push    eax                              ;  0x16 技能代碼
004235FB  |.  56            push    esi                              ;  esi = 01c28BE8 恆定
004235FC  |.  E8 0F0B0000   call    00424110                         ;  這個就是技能CALL

 

  

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM