[原創]曾經寫的一個遍歷尋仙游戲服務器列表的文章(基礎)
Posted on 2009-09-24 01:45 besterChen 閱讀(2312) 評論(2) 編輯 收藏 引用 所屬分類: 外掛/游戲分析聲明:本文是好久以前發在吾愛破解論壇上的,由于它有一定的紀念意義,故將它轉發到這里留念之用……
游戲的服務器列表更新了,而存服務器信息的XML在游戲的更新服務器上加密保存的,沒能力分析它的解密算法,就想寫個程序,從內存里讀出來,工作量就是那么多,不如索性做成教程,分享給像我一樣剛剛入門的朋友,說不定就又能混一篇精華,哈哈……
其實這個的道理跟找游戲中遍歷怪物啊,地面物品啊,都是都差不多的,而且,這個的數據不會想怪一下死了消失了,也不會像在游戲里,你中斷的時間太長游戲會掉線,而且這個的負責程度相當于一個普通沒有任何保護的游戲,比較簡單,但是,在這里提醒大家,如果要做外掛的話,大家自己搗鼓搗鼓,學習一下就可以了,不要靠這個來賺錢,不安全的,尤其是TX的游戲,所以請大家自重!
本文適合還沒有入門的新朋友上手用的,所有,各位大牛就可以飄過不看它了!
不多廢話,進入主題,我現在要找服務器列表,當然就要先從內存里搜一個服務器的名字(別的信息不知道,只能搜名字了!)
像上圖,就搜葫蘆山了,結果如下:
在OD里的數據區中看一下這幾個地址,確定,第一個地址也就是:
來到這里:
現在我們需要知道的是,EAX的內容是從哪里來的,所以向上看:
0040CA3B |> \8BC2 |mov eax, edx ; 1
是EDX給的EAX,再向上:
0040CA17 |. 8B83 18010000 |mov eax, dword ptr [ebx+118]
0040CA1D |. 8B
0040CA20 |. 8D
0040CA23 |. 8D57 04 |lea edx, dword ptr [edi+4] ; 1
0040CA26 |. 85D2 |test edx, edx
0040CA28 |. C746
0040CA
0040CA35 |. 75 04 |jnz short 0040CA3B
這個流程應該是比較簡單的,我就從頭分析一下:
跟進ESI看下:
繼續:
到這里可以知道,大區的地址指針+偏移的形式應該在:[
看下EAX的內容:
01325CA0 00 00 00 00 64 77 94
01325CB0 98 83
01325CC0 06 00 00 00 B0 4E 31 01
01325CD
01325CE0 01 00 00 00 40 05
01325CF0 78 01
這個就是服務器大區的大概的結構了,里面各個數據的含義還不是很清楚,不過不用著急,通過代碼,我們都會弄明白的!
繼續回到代碼中分析:
看一下ECX的內容吧
0132D048 0D F0 AD BA 0D F0 AD BA 0D F0 AD BA 0D F0 AD BA .瓠?瓠?瓠?瓠
0132D058 0D F0 AD BA 0D F0 AD BA 0D F0 AD BA 0D F0 AD BA .瓠?瓠?瓠?瓠
0132D068 0D F0 AD BA 0D F0 AD BA 0D F0 AD BA 0D F0 AD BA .瓠?瓠?瓠?瓠
0132D078 0D F0 AD BA 0D F0 AD BA 0D F0 AD BA 0D F0 AD BA .瓠?瓠?瓠?瓠
都是同一個數字,暫且不多考慮,估計接下來應該是遍歷大區的信息啊,想下,我們在寫程序的時候,循環需要什么來著,對,就是循環多少次啊,接下來的代碼就是計算有多少個大區,以控制循環多少次了!
好了,到這里,EAX里存的就是有多少個大區了
繼續看
下面開始循環,遍歷各個大區,我們取大區的信息也主要就依靠這個循環了~~~
根據這個循環,我們可以很輕松的寫出遍歷所有大區的代碼了,不過不用太著急,在這里標記一下,因為,這個大區的結構中很多的數據成員,我們都不清楚,現在只知道第一個成員是大區的ID,第二個是名字,而且結構的大小要湊夠0x1D4,其他的都還未知,繼續分析,或許等下就清晰了,嘿嘿!
繼續看代碼:
哈哈,結構越來越清晰,思路越來越明確,大家猜下,接下來是要干什么了啊~~~~
不如我們跟進去看看,服務器結構是什么樣子,哈哈
不多介紹了,繼續看下面的代碼:
0040CA01 |.
0040CA05 |.
0040CA0B |. 3BAB
0040CA11 |.
這里用到了這個0x
繼續看代碼:
0040CA17 |. 8B83 18010000 |mov eax, dword ptr [ebx+118] ; 取出第一個服務器結構的首地址
0040CA1D |. 8B
0040CA20 |. 8D
0040CA23 |. 8D57 04 |lea edx, dword ptr [edi+4] ; 還記得EDI里存的是什么吧,哈哈,偏移+4就是取服務器名字了~
0040CA26 |. 85D2 |test edx, edx
0040CA28 |. C746
0040CA
0040CA35 |. 75 04 |jnz short 0040CA3B
0040CA37 |.
0040CA39 |. EB 14 |jmp short 0040CA
0040CA3B |> 8BC2 |mov eax, edx ; 1
0040CA3D |. 8D48 01 |lea ecx, dword ptr [eax+1]
0040CA40 |.
0040CA44 |>
0040CA46 |. 40 ||inc eax ;眼熟不?哈哈
0040CA47 |.
0040CA49 |.^ 75 F9 |\jnz short 0040CA44
好了,分析就基本上到此結束了,沒有必要再繼續分析了,接下來就是分析一下,我們需要什么東西,然后就是怎么把我們分析得到的東西轉換成代碼!
先想想我們現在需要的東西:各個大區的序號,還有大區的名字,服務器的名字,服務器的IP和端口號,需要的數據就這么多了,接下來看看我們現在分析到了什么數據~
先看下服務器的結構中,我們已經知道了哪些數據:
引用:
1. 每個服務器信息結構的大小是0x108
2. 直接引用服務器的數據,如下:
我們需要的數據很少,只需要名字和IP及端口就OK了,所以直接看數據段,來肉眼來定一下就可以了
這樣,我們定義的結構體,如下:
// 游戲服務器的數據結構定義
typedef struct _GAME_SERVERLIST_INFO
{
DWORD dwServerNo; // 服務器的序號 0
char szName[8]; // 名字offset 4
DWORD dwUnknow1[30]; // 未知offset C
char sServerIpPort[20]; // IP和端口 offset
DWORD dwUnknow2[28]; // 結尾的數據 offset 1CC 用來補齊0x108的結構大小
} GAME_SERVERLIST_INFO, *PGAME_SERVERLIST_INFO;
引用:
1. 現在只知道第一個成員是大區的ID,第二個是名字,而且結構的大小要湊夠0x1D4
2. 看到了吧,大區數構中偏移第
3. 大區數構中偏移第1CC的成員是個最大值
好了,現在我們來定義大區的結構體,如下:
// 游戲大區的數據結構定義
typedef struct _GAME_AREA_INFO
{
DWORD dwTheAreaNo; // 大區的序號 0
char szName[8]; // 名字offset 4
DWORD dwUnknow1[111]; // 未知offset C 這些數據對我們不重要,直接掠過
_GAME_SERVERLIST_INFO *pServerListOfFirst; // 首個服務器 offset
PDWORD pTheEndData; // 結尾的數據 offset 1CC
PDWORD pTheEndData2; // 結尾的數據 1D0 用來補齊0x1D4的結構大小
} GAME_AREA_INFO, *PGAME_AREA_INFO;
到現在,數據已經整理好了,應該可以寫代碼了,在寫代碼以前呢,我們整理總結一下我們收集到的數據:
引用:
1. 這個程序的基址是:
2. 大區的地址指針+偏移的形式應該在:[
3. 計算大區數量的算法如下:
[
4. 計算每個區服務器數量的算法如下:
用GAME_AREA_INFO:: pTheEndData 減去GAME_AREA_INFO::pServerListOfFirst 然后再如下計算:
/************************************************************************/
/* 函數名:GetAreaInfo
/* 參 數:無
/* 返回值:返回一個指向大區結構的指針
/* 功 能:獲取服務器列表中大區的結構體指針
/* 作 者:bester @
/************************************************************************/
__declspec(naked) PGAME_AREA_INFO WINAPI GetAreaInfo()
{
__asm
{
mov eax, dword ptr [XXUPDATE_BASE_ADDR+0xDC]
test eax, eax
je NULL_POINT
mov eax, dword ptr [eax]
test eax, eax
je NULL_POINT
mov eax, dword ptr [eax+0x4]
NULL_POINT:
retn;
}
}
/************************************************************************/
/* 函數名:GetAreaNum
/* 參 數:無
/* 返回值:返回游戲更新列表中大區的個數
/* 功 能:獲取游戲更新列表中大區的個數
/* 作 者:bester @ 2/17/2009
/************************************************************************/
__declspec(naked) int WINAPI GetAreaNum()
{
_asm
{
mov eax, dword ptr [XXUPDATE_BASE_ADDR+0xDC]
test eax, eax
je NULL_POINT
mov eax, dword ptr [eax]
test eax, eax
je NULL_POINT
mov eax, dword ptr [eax+0x4]
test eax, eax
je NULL_POINT
mov ecx, dword ptr [XXUPDATE_BASE_ADDR+0xDC]
test ecx, ecx
je NULL_POINT
mov ecx, dword ptr [ecx]
test ecx, ecx
je NULL_POINT
mov ecx, dword ptr [eax+0x8]
test ecx, ecx
je NULL_POINT
sub ecx,eax
mov eax, 0x
imul ecx
add edx, ecx
sar edx, 0x8
mov eax, edx
shr eax, 0x
add eax, edx
NULL_POINT:
retn;
}
}
/************************************************************************/
/* 函數名:GetServerInfo
/* 參 數:無
/* 返回值:返回一個指向服務器結構體的指針
/* 功 能:獲取服務器列表中服務器的結構體指針
/* 作 者:bester @ 2/17/2009
/************************************************************************/
PGAME_SERVERLIST_INFO CMyForm::GetServerInfo(PGAME_AREA_INFO pAreaInfo)
{
return pAreaInfo->pServerListOfFirst;
}
/************************************************************************/
/* 函數名:GetServerNum
/* 參 數:指定的大區結構體指針
/* 返回值:返回指定的大區中服務器的數量
/* 功 能:獲取指定的大區中服務器的個數
/* 作 者:bester @ 2/17/2009
/************************************************************************/
int WINAPI GetServerNum(PGAME_AREA_INFO pAreaInfo)
{
DWORD TheServerNum = 0;
TheServerNum = (DWORD)pAreaInfo->pTheEndData - (DWORD)pAreaInfo->pServerListOfFirst;
_asm
{
mov ECX,TheServerNum
mov eax, 0x3E
imul ecx ; 3E
sar edx, 0x6 ; 取其積的高32位值右移6位
mov eax, edx
shr eax, 0x
add eax, edx ; OD顯示是4,即有4個服務器,循環次數為4次
mov TheServerNum,eax
}
return TheServerNum;
}
/************************************************************************/
/* 函數名:GetTheMaxServerNum
/* 參 數:無
/* 返回值:返回游戲指定的每個區最多可擁有服務器的數量
/* 功 能:如果取到的服務器個數大于等于這個數就說明出錯了
/* 作 者:bester @ 2/17/2009
/************************************************************************/
__declspec(naked) int WINAPI GetTheMaxServerNum()
{
__asm
{
mov eax, dword ptr [XXUPDATE_BASE_ADDR+0x
test eax, eax
je NULL_POINT
mov eax, dword ptr [eax]
NULL_POINT:
retn;
}
}
/************************************************************************/
/* 函數名:OnBtnRefurbish
/* 參 數:無
/* 返回值:無
/* 功 能:獲取游戲更新程序的服務器列表中的數據并更新到程序的列表視圖中
/* 作 者:bester @ 2/17/2009
/************************************************************************/
void CMyForm::OnBtnRefurbish()
{
CString szTemp = "";
int AreaNum = GetAreaNum();
szTemp.Format("獲取到大區的數量是0x%08X",AreaNum);
MessageBox(szTemp);
try
{
PGAME_SERVERLIST_INFO pServerListInfo = NULL;
PGAME_AREA_INFO pAreaInfo = GetAreaInfo();
if (pAreaInfo != NULL)
{
for (int x=0; x<=AreaNum; x++,pAreaInfo++)
{
pServerListInfo = pAreaInfo->pServerListOfFirst;
if (pServerListInfo != NULL)
{
for (int y=0; y<=GetServerNum(pAreaInfo); y++, pServerListInfo++)
{
//序號
szTemp.Format(_T("%.2d"), y+1);
m_ServerList.InsertItem(y,szTemp);
//大區的名字
m_ServerList.SetItemText(y,1,pServerListInfo->szName);
//服務器的名字
m_ServerList.SetItemText(y,2,pServerListInfo->szName);
//IP和Port
m_ServerList.SetItemText(y,3,pServerListInfo->sServerIpPort);
}
}
}
}
UpdateData();
delete pServerListInfo;
delete pAreaInfo;
}catch(...)
{
::AfxMessageBox("遍歷服務器列表時,出現異常……");
}
}
最后看下效果吧:
總的來說,還是不錯的,是嗎?
哈哈,本人才疏學淺,也只能講點這些簡單的東西了,僅希望能以此拋磚引玉,引來大牛的分享……