聲明:本文是好久以前發在吾愛破解論壇上的,由于它有一定的紀念意義,故將它轉發到這里留念之用……
游戲的服務器列表更新了,而存服務器信息的XML在游戲的更新服務器上加密保存的,沒能力分析它的解密算法,就想寫個程序,從內存里讀出來,工作量就是那么多,不如索性做成教程,分享給像我一樣剛剛入門的朋友,說不定就又能混一篇精華,哈哈……
其實這個的道理跟找游戲中遍歷怪物啊,地面物品啊,都是都差不多的,而且,這個的數據不會想怪一下死了消失了,也不會像在游戲里,你中斷的時間太長游戲會掉線,而且這個的負責程度相當于一個普通沒有任何保護的游戲,比較簡單,但是,在這里提醒大家,如果要做外掛的話,大家自己搗鼓搗鼓,學習一下就可以了,不要靠這個來賺錢,不安全的,尤其是TX的游戲,所以請大家自重!
本文適合還沒有入門的新朋友上手用的,所有,各位大牛就可以飄過不看它了!
不多廢話,進入主題,我現在要找服務器列表,當然就要先從內存里搜一個服務器的名字(別的信息不知道,只能搜名字了!)

像上圖,就搜葫蘆山了,結果如下:

在OD里的數據區中看一下這幾個地址,確定,第一個地址也就是:01329F6C 就是我們像要的地址了,至于為什么,你看一下就可以知道了(因為第一個在一個結構體數組里,其他的都是獨立的,所以第一個是!)。
好了,在OD里,來到01329F6C這個地址,在第一個DWORD上下內存訪問斷點:

來到這里:

現在我們需要知道的是,EAX的內容是從哪里來的,所以向上看:
0040CA3B |> \8BC2 |mov eax, edx ; 1
是EDX給的EAX,再向上:
0040CA17 |. 8B83 18010000 |mov eax, dword ptr [ebx+118]
0040CA1D |. 8B34A8 |mov esi, dword ptr [eax+ebp*4]
0040CA20 |. 8D04A8 |lea eax, dword ptr [eax+ebp*4]
0040CA23 |. 8D57 04 |lea edx, dword ptr [edi+4] ; 1
0040CA26 |. 85D2 |test edx, edx
0040CA28 |. C746 3C 01000> |mov dword ptr [esi+3C], 1
0040CA2F |. 8D9E 8C000000 |lea ebx, dword ptr [esi+8C]
0040CA35 |. 75 04 |jnz short 0040CA3B
這個流程應該是比較簡單的,我就從頭分析一下:
0040C8C0 /$ 6A FF push -1
0040C8C2 |. 68 08184600 push 00461808 ; 咐|g; SE 處理程序安裝
0040C8C7 |. 64:A1 0000000>mov eax, dword ptr fs:[0]
0040C8CD |. 50 push eax
0040C8CE |. 64:8925 00000>mov dword ptr fs:[0], esp
0040C8D5 |. 83EC 24 sub esp, 24
0040C8D8 |. 53 push ebx
0040C8D9 |. 8BD9 mov ebx, ecx
0040C8DB |. 8B83 78010000 mov eax, dword ptr [ebx+178] ; 取出返回值,如果是-1就不做處理!
0040C8E1 |. 83F8 FF cmp eax, -1
0040C8E4 |. 895C24 08 mov dword ptr [esp+8], ebx
0040C8E8 |. 0F84 89000000 je 0040C977
0040C8EE |. 55 push ebp
0040C8EF |. 57 push edi
0040C8F0 |. 8D4C24 2C lea ecx, dword ptr [esp+2C]
0040C8F4 |. 51 push ecx
0040C8F5 |. 8D5424 2C lea edx, dword ptr [esp+2C]
0040C8F9 |. 52 push edx
0040C8FA |. 50 push eax
0040C8FB |. 8D8B 58010000 lea ecx, dword ptr [ebx+158]
0040C901 |. E8 0ACAFFFF call 00409310
0040C906 |. 33ED xor ebp, ebp
0040C908 |. 3BC5 cmp eax, ebp ; 如果函數返回值是0,就走人~~~
0040C90A |. 74 69 je short 0040C975
0040C90C |. 8B78 04 mov edi, dword ptr [eax+4]
0040C90F |. 3BFD cmp edi, ebp
0040C911 |. 74 62 je short 0040C975
0040C913 |. 56 push esi ; ESI和EBX的值都是004812A8
0040C914 |. 8BB3 DC000000 mov esi, dword ptr [ebx+DC]
跟進ESI看下:
011A21E8 0D F0 AD BA 70 5C 32 01 58 7D 32 01 2C 7F 32 01 .瓠簆\2X}2,2
011A21F8 0D F0 AD BA 88 31 32 01 A8 31 32 01 A8 31 32 01 .瓠簣12?2?2
011A2208 0D F0 AD BA 28 01 30 01 42 01 30 01 42 01 30 01 .瓠?0B0B0
011A2218 0D F0 AD BA 28 23 1A 01 00 00 00 00 0D F0 AD BA .瓠?#.....瓠
011A2228 80 23 1A 01 00 00 00 00 00 F0 AD BA 0D F0 AD BA €#.....瓠?瓠
繼續:
0040C91A |. 8B46 04 mov eax, dword ptr [esi+4] ; 取出了各個大區的序號·~~
0040C91D |. 3BC5 cmp eax, ebp
到這里可以知道,大區的地址指針+偏移的形式應該在:[004812A8+DC]+4]
看下EAX的內容:
01325C70 01 00 00 00 B9 E3 B6 AB C7 F8 00 00 58 F8 17 04 ...廣東區..X?
01325C80 00 00 00 00 58 F8 17 04 EA 76 94 7C 00 00 1A 01 ....X? 陃攟..
01325C90 64 77 94 7C D0 31 30 01 00 00 1A 01 D8 31 30 01 dw攟?0..?0
01325CA0 00 00 00 00 64 77 94 7C 50 32 30 01 00 00 1A 01 ....dw攟P20..
01325CB0 98 83 1A 01 C0 30 30 01 00 00 1A 01 00 00 00 00 槂?0......
01325CC0 06 00 00 00 B0 4E 31 01 0F 00 00 00 A8 4E 31 01 ...癗1...∟1
01325CD0 A8 4E 31 01 30 18 00 00 78 01 1A 01 28 03 1A 01 ∟10..x(
01325CE0 01 00 00 00 40 05 1A 01 38 00 00 00 48 32 30 01 ...@8...H20
01325CF0 78 01 1A 01 30 2E 38 32 30 34 2E 30 36 00 00 00 x0.8204.06...
這個就是服務器大區的大概的結構了,里面各個數據的含義還不是很清楚,不過不用著急,通過代碼,我們都會弄明白的!
繼續回到代碼中分析:
0040C91F |. C74424 1C FFF> mov dword ptr [esp+1C], -1
0040C927 |. 75 04 jnz short 0040C92D
0040C929 |. 33C0 xor eax, eax
0040C92B |. EB 18 jmp short 0040C945
0040C92D |> 8B4E 08 mov ecx, dword ptr [esi+8]
看一下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 .瓠?瓠?瓠?瓠
都是同一個數字,暫且不多考慮,估計接下來應該是遍歷大區的信息啊,想下,我們在寫程序的時候,循環需要什么來著,對,就是循環多少次啊,接下來的代碼就是計算有多少個大區,以控制循環多少次了!
0040C930 |. 2BC8 sub ecx, eax
0040C932 |. B8 8DC0088C mov eax, 8C08C08D
0040C937 |. F7E9 imul ecx
0040C939 |. 03D1 add edx, ecx
0040C93B |. C1FA 08 sar edx, 8
0040C93E |. 8BC2 mov eax, edx
0040C940 |. C1E8 1F shr eax, 1F
0040C943 |. 03C2 add eax, edx ;這里就是大區的數量了OD顯示的是0x12,即有0x12個大區
好了,到這里,EAX里存的就是有多少個大區了
繼續看
0040C945 |> 33C9 xor ecx, ecx
0040C947 |. 3BC5 cmp eax, ebp
0040C949 |. 76 29 jbe short 0040C974
0040C94B |. 8B97 84000000 mov edx, dword ptr [edi+84] ; 取出我們選擇的大區的序號
0040C951 |. 8B76 04 mov esi, dword ptr [esi+4] ; 取出大區的首個元素:序號
下面開始循環,遍歷各個大區,我們取大區的信息也主要就依靠這個循環了~~~
0040C954 |. 33FF xor edi, edi
0040C956 |. 897424 24 mov dword ptr [esp+24], esi
0040C95A |. 897C24 10 mov dword ptr [esp+10], edi
0040C95E |. 8BFF mov edi, edi
0040C960 |> 391437 / cmp dword ptr [edi+esi], edx ; 循環比較,看看取那個區下的服務器
0040C963 |. 74 26 |je short 0040C98B ; 只要是我們選的區,拿就跳走,不是就繼續循環
0040C965 |. 41 | inc ecx
0040C966 |. 81C7 D4010000 | add edi, 1D4 ; 在內存里,每個大區信息的結構體大小為0x1D4
0040C96C |. 3BC8 | cmp ecx, eax
0040C96E |.^ 72 F0 \ jb short 0040C960
0040C970 |. 897C24 10 mov dword ptr [esp+10], edi
根據這個循環,我們可以很輕松的寫出遍歷所有大區的代碼了,不過不用太著急,在這里標記一下,因為,這個大區的結構中很多的數據成員,我們都不清楚,現在只知道第一個成員是大區的ID,第二個是名字,而且結構的大小要湊夠0x1D4,其他的都還未知,繼續分析,或許等下就清晰了,嘿嘿!
繼續看代碼:
0040C98B |> \8B8437 C80100> mov eax, dword ptr [edi+esi+1C8] ; 看到了吧,大區數構中偏移第1C8的成員是個最小值
0040C992 |. 3BC5 cmp eax, ebp
0040C994 |. 897C24 10 mov dword ptr [esp+10], edi
0040C998 |. 75 06 jnz short 0040C9A0
0040C99A |. 896C24 20 mov dword ptr [esp+20], ebp
0040C99E |. EB 1E jmp short 0040C9BE
0040C9A0 |> 8B8C37 CC0100> mov ecx, dword ptr [edi+esi+1CC] ; 大區數構中偏移第1CC的成員是個最大值
0040C9A7 |. 2BC8 sub ecx, eax ; 同算大區的數量一樣,計算服務器的數量
0040C9A9 |. B8 E1830F3E mov eax, 3E0F83E1
0040C9AE |. F7E9 imul ecx ; 3E0F83E1*(最大值-最小值)
0040C9B0 |. C1FA 06 sar edx, 6 ; 取其積的高32位值右移6位
0040C9B3 |. 8BC2 mov eax, edx
0040C9B5 |. C1E8 1F shr eax, 1F ; 再移動0x1F位
0040C9B8 |. 03C2 add eax, edx ; 取他們的和OD顯示是4,即4個服務器,循環4次
0040C9BA |. 894424 20 mov dword ptr [esp+20], eax ; 把結果保存一下
哈哈,結構越來越清晰,思路越來越明確,大家猜下,接下來是要干什么了啊~~~~
0040C9BE |> \8B83 1C010000 mov eax, dword ptr [ebx+11C] ; 還不清楚它的作用,OD顯示是6,先不管它
0040C9C4 |. 33D2 xor edx, edx
0040C9C6 |. 85C0 test eax, eax
0040C9C8 |. 894424 18 mov dword ptr [esp+18], eax
0040C9CC |. 0F8E 95010000 jle 0040CB67
0040C9D2 |. 33C9 xor ecx, ecx
0040C9D4 |. EB 0A jmp short 0040C9E0 ; 下面的代碼應該很眼熟吧,典型的For循環
0040C9D6 |> 8B4C24 2C /mov ecx, dword ptr [esp+2C]
0040C9DA |. 8B5424 28 |mov edx, dword ptr [esp+28]
0040C9DE |. 8BFF |mov edi, edi
0040C9E0 |> 3B5424 20 cmp edx, dword ptr [esp+20] ; 將計數器EDX跟服務器的數量進行比較,看看循環是否結束
0040C9E4 |. 0F83 42010000 |jnb 0040CB2C
0040C9EA |. 8B8437 C80100>|mov eax, dword ptr [edi+esi+1C8] ; 取出第一個服務器的信息結構的首地址
不如我們跟進去看看,服務器結構是什么樣子,哈哈
0132A5B8 0A 00 00 00 BA F9 C2 AB C9 BD 00 40 A0 F9 17 04 ....葫蘆山.@狔
0132A5C8 94 FA 17 04 00 00 00 00 00 E9 92 7C 40 00 93 01 旡 .....閽|@.?
0132A5D8 FF FF 01 00 00 00 1A 01 64 77 94 7C 98 FA 17 04 ...dw攟橔
0132A5E8 A7 C6 98 7C 00 00 1A 01 00 00 00 00 30 2E 31 01 榺......0.1
0132A5F8 00 00 1A 01 38 2E 31 01 94 FA 17 04 F8 FA 17 04 ..8.1旡
0132A608 00 E9 92 7C 00 00 00 00 00 00 1A 01 00 00 00 00 .閽|..........
0132A618 08 FB 17 04 B0 D9 98 7C 08 06 1A 01 94 D9 98 7C ? 百榺斮榺
0132A628 00 00 1A 01 38 2E 31 01 60 00 00 40 30 5B 27 01 ..8.1`..@0['
0132A638 F8 D6 2C 01 31 31 39 2E 31 34 37 2E 31 36 2E 31 ,119.147.16.1
0132A648 33 38 3A 33 31 30 30 00 00 00 00 00 00 00 00 00 38:3100.........
不多介紹了,繼續看下面的代碼:
0040C9F1 |. 8D3C01 |lea edi, dword ptr [ecx+eax] ; ECX是結構偏移的計數器,大家都應該能看明白的
0040C9F4 |. 42 |inc edx ; 循環計數器了,不用我講的·~~
0040C9F5 |. 81C1 08010000 |add ecx, 108 ; 每個服務器信息結構的大小是0x108,指到數組的下個成員
0040C9FB |. 85ED |test ebp, ebp
0040C9FD |. 895424 28 |mov dword ptr [esp+28], edx ; 保存服務器的序號信息
0040CA01 |. 894C24 2C |mov dword ptr [esp+2C], ecx ; 保存服務器信息結構的偏移信息
0040CA05 |. 0F8C A9020000 |jl 0040CCB4
0040CA0B |. 3BAB 1C010000 |cmp ebp, dword ptr [ebx+11C] ; 看來這個0x11C很關鍵哦,
0040CA11 |. 0F8D 9D020000 |jge 0040CCB4 ; 大于等于就跳走了~~~
這里用到了這個0x11C這個變量,根據上面代碼的最后兩行,如果服務器數量大于等于6就跳走,估計是個用來防止異常的,有興趣的朋友可以跟一下,這里就不多說了!
繼續看代碼:
0040CA17 |. 8B83 18010000 |mov eax, dword ptr [ebx+118] ; 取出第一個服務器結構的首地址
0040CA1D |. 8B34A8 |mov esi, dword ptr [eax+ebp*4]
0040CA20 |. 8D04A8 |lea eax, dword ptr [eax+ebp*4] ; dword ptr [eax+ebp*4]不會被看糊涂吧,取結構體成員的基本方法!
0040CA23 |. 8D57 04 |lea edx, dword ptr [edi+4] ; 還記得EDI里存的是什么吧,哈哈,偏移+4就是取服務器名字了~
0040CA26 |. 85D2 |test edx, edx
0040CA28 |. C746 3C 01000>|mov dword ptr [esi+3C], 1
0040CA2F |. 8D9E 8C000000 |lea ebx, dword ptr [esi+8C]
0040CA35 |. 75 04 |jnz short 0040CA3B
0040CA37 |. 33C0 |xor eax, eax
0040CA39 |. EB 14 |jmp short 0040CA4F
0040CA3B |> 8BC2 |mov eax, edx ; 1
0040CA3D |. 8D48 01 |lea ecx, dword ptr [eax+1]
0040CA40 |. 894C24 30 |mov dword ptr [esp+30], ecx
0040CA44 |> 8A08 |/mov cl, byte ptr [eax] ; 斷在了這里,也就是說,這里取了服務器的列表信息!
0040CA46 |. 40 ||inc eax ;眼熟不?哈哈
0040CA47 |. 84C9 ||test cl, cl
0040CA49 |.^ 75 F9 |\jnz short 0040CA44
好了,分析就基本上到此結束了,沒有必要再繼續分析了,接下來就是分析一下,我們需要什么東西,然后就是怎么把我們分析得到的東西轉換成代碼!
先想想我們現在需要的東西:各個大區的序號,還有大區的名字,服務器的名字,服務器的IP和端口號,需要的數據就這么多了,接下來看看我們現在分析到了什么數據~
先看下服務器的結構中,我們已經知道了哪些數據:
引用:
1. 每個服務器信息結構的大小是0x108
2. 直接引用服務器的數據,如下:
0132A5B8 0A 00 00 00 BA F9 C2 AB C9 BD 00 40 A0 F9 17 04 ....葫蘆山.@狔
0132A5C8 94 FA 17 04 00 00 00 00 00 E9 92 7C 40 00 93 01 旡 .....閽|@.?
0132A5D8 FF FF 01 00 00 00 1A 01 64 77 94 7C 98 FA 17 04 ...dw攟橔
0132A5E8 A7 C6 98 7C 00 00 1A 01 00 00 00 00 30 2E 31 01 榺......0.1
0132A5F8 00 00 1A 01 38 2E 31 01 94 FA 17 04 F8 FA 17 04 ..8.1旡
0132A608 00 E9 92 7C 00 00 00 00 00 00 1A 01 00 00 00 00 .閽|..........
0132A618 08 FB 17 04 B0 D9 98 7C 08 06 1A 01 94 D9 98 7C ? 百榺斮榺
0132A628 00 00 1A 01 38 2E 31 01 60 00 00 40 30 5B 27 01 ..8.1`..@0['
0132A638 F8 D6 2C 01 31 31 39 2E 31 34 37 2E 31 36 2E 31 ,119.147.16.1
0132A648 33 38 3A 33 31 30 30 00 00 00 00 00 00 00 00 00 38:3100.........
我們需要的數據很少,只需要名字和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 1C8
DWORD dwUnknow2[28]; // 結尾的數據 offset 1CC 用來補齊0x108的結構大小
} GAME_SERVERLIST_INFO, *PGAME_SERVERLIST_INFO;
再看大區的數據結構中,我們知道的數據:
引用:
1. 現在只知道第一個成員是大區的ID,第二個是名字,而且結構的大小要湊夠0x1D4
2. 看到了吧,大區數構中偏移第1C8的成員是個最小值
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 1C8
PDWORD pTheEndData; // 結尾的數據 offset 1CC
PDWORD pTheEndData2; // 結尾的數據 1D0 用來補齊0x1D4的結構大小
} GAME_AREA_INFO, *PGAME_AREA_INFO;
到現在,數據已經整理好了,應該可以寫代碼了,在寫代碼以前呢,我們整理總結一下我們收集到的數據:
引用:
1. 這個程序的基址是:004812A8
2. 大區的地址指針+偏移的形式應該在:[004812A8+DC]+4]
3. 計算大區數量的算法如下:
[004812A8]+DC]+8] 減去 [004812A8]+DC]+4] 然后在:
0040C932 |. B8 8DC0088C mov eax, 8C08C08D
0040C937 |. F7E9 imul ecx
0040C939 |. 03D1 add edx, ecx
0040C93B |. C1FA 08 sar edx, 8
0040C93E |. 8BC2 mov eax, edx
0040C940 |. C1E8 1F shr eax, 1F
0040C943 |. 03C2 add eax, edx
4. 計算每個區服務器數量的算法如下:
用GAME_AREA_INFO:: pTheEndData 減去GAME_AREA_INFO::pServerListOfFirst 然后再如下計算:
0040C9A9 |. B8 E1830F3E mov eax, 3E0F83E1
0040C9AE |. F7E9 imul ecx ; 3E0F83E1*(最大值-最小值)
0040C9B0 |. C1FA 06 sar edx, 6 ; 取其積的高32位值右移6位
0040C9B3 |. 8BC2 mov eax, edx
0040C9B5 |. C1E8 1F shr eax, 1F ; 再移動0x1F位
0040C9B8 |. 03C2 add eax, edx ; OD顯示是4,即有4個服務器,循環次數為4次
0040C9BA |. 894424 20 mov dword ptr [esp+20], eax ; 把結果保存一下
5. 基址:004812A8 加上0x11C 中的值是最大的服務器數量,若算出來的服務器數量大于等于這個數,就是出錯了,應該加容錯處理!
好了,現在我們正式的開始寫程序,程序可以寫成DLL的方式,也可以寫成EXE的方式,由于DLL的調試不是很方便,我就只貼一下DLL方式的代碼,至于EXE方式的,大家可以看附件的程序,不多說了,大家看代碼就好:
/************************************************************************/
/* 函數名:GetAreaInfo
/* 參 數:無
/* 返回值:返回一個指向大區結構的指針
/* 功 能:獲取服務器列表中大區的結構體指針
/* 作 者:bester @ 2/17/2009
/************************************************************************/
__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, 0x8C08C08D
imul ecx
add edx, ecx
sar edx, 0x8
mov eax, edx
shr eax, 0x1F
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, 0x3E0F83E1
imul ecx ; 3E0F83E1*(最大值-最小值)
sar edx, 0x6 ; 取其積的高32位值右移6位
mov eax, edx
shr eax, 0x1F ; 再移動0x1F位
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+0x11C]
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("遍歷服務器列表時,出現異常……");
}
}
最后看下效果吧:

總的來說,還是不錯的,是嗎?
哈哈,本人才疏學淺,也只能講點這些簡單的東西了,僅希望能以此拋磚引玉,引來大牛的分享……