聚星亭

吾笨笨且懒散兮 急须改之而奋进
posts - 74, comments - 166, trackbacks - 0, articles - 0
  C++博客 :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理

       声明:本文是好久以前发在吾爱破解论坛上的,由于它有一定的纪念意义,故将它转发到这里留念之用……

       游戏的服务器列表更新了,而存服务器信息的XML在游戏的更新服务器上加密保存的,没能力分析它的解密算法,就想写个程序,从内存里读出来,工作量就是那么多,不如索性做成教程,分享给像我一样刚刚入门的朋友,说不定就又能混一篇精华,哈哈…… 

       其实这个的道理跟找游戏中遍历怪物啊,地面物品啊,都是都差不多的,而且,这个的数据不会想怪一下死了消失了,也不会像在游戏里,你中断的时间太长游戏会掉线,而且这个的负责程度相当于一个普通没有任何保护的游戏,比较简单,但是,在这里提醒大家,如果要做外挂的话,大家自己捣鼓捣鼓,学习一下就可以了,不要靠这个来赚钱,不安全的,尤其是TX的游戏,所以请大家自重! 

本文适合还没有入门的新朋友上手用的,所有,各位大牛就可以飘过不看它了!
   
   
不多废话,进入主题,我现在要找服务器列表,当然就要先从内存里搜一个服务器的名字(别的信息不知道,只能搜名字了!)

像上图,就搜葫芦山了,结果如下:

OD里的数据区中看一下这几个地址,确定,第一个地址也就是:01329F6C 就是我们像要的地址了,至于为什么,你看一下就可以知道了(因为第一个在一个结构体数组里,其他的都是独立的,所以第一个是!)。

 好了,在OD里,来到01329F6C这个地址,在第一个DWORD上下内存访问断点:

来到这里:

现在我们需要知道的是,EAX的内容是从哪里来的,所以向上看:

0040CA3B  |> \8BC2          |mov     eax, edx                        ;  1

EDX给的EAX,再向上:

0040CA17  |.  8B83 18010000   |mov     eaxdword ptr [ebx+118]
0040CA1D  |.  8B34A8            |mov     esidword ptr [eax+ebp*4]
0040CA20  |.  8D04A8            |lea     eaxdword ptr [eax+ebp*4]
0040CA23  |.  8D57 04            |lea     edxdword ptr [edi+4]          ;  1
0040CA26  |.  85D2                |test    edxedx
0040CA28  |.  C746 3C 01000>  |mov     dword ptr [esi+3C], 1
0040CA2F  |.  8D9E 8C000000  |lea     ebxdword 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     eaxdword 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     ebxecx
0040C8DB  |.  8B83 78010000 mov     eaxdword 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     ecxdword ptr [esp+2C]
0040C8F4  |.  51            push    ecx
0040C8F5  |.  8D5424 2C     lea     edxdword ptr [esp+2C]
0040C8F9  |.  52            push    edx
0040C8FA  |.  50            push    eax
0040C8FB  |.  8D8B 58010000 lea     ecxdword ptr [ebx+158]
0040C901  |.  E8 0ACAFFFF   call    00409310
0040C906  |.  33ED          xor     ebpebp
0040C908  |.  3BC5          cmp     eaxebp                         ;  
如果函数返回值是0,就走人~~~
0040C90A  |.  74 69         je      short 0040C975
0040C90C  |.  8B78 04       mov     edidword ptr [eax+4]
0040C90F  |.  3BFD          cmp     ediebp
0040C911  |.  74 62         je      short 0040C975
0040C913  |.  56            push    esi                              ;  ESI
EBX的值都是004812A8
0040C914  |.  8BB3 DC000000 mov     esidword 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     eaxdword ptr [esi+4]           ;  取出了各个大区的序号·~~
0040C91D  |.  3BC5          cmp     eaxebp

到这里可以知道,大区的地址指针+偏移的形式应该在:[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  ....dwP20..

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            eaxeax
0040C92B  |.  EB 18                         jmp          short 0040C945
0040C92D  |>  8B4E 08                   mov          ecxdword 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     ecxeax
0040C932  |.  B8 8DC0088C                    mov     eax8C08C08D
0040C937  |.  F7E9                                  imul    ecx
0040C939  |.  03D1                                  add     edxecx
0040C93B  |.  C1FA 08                             sar     edx, 8
0040C93E  |.  8BC2                                 mov     eaxedx
0040C940  |.  C1E8 1F                            shr     eax1F
0040C943  |.  03C2                                  add     eaxedx              ;
这里就是大区的数量了OD显示的是0x12,即有0x12个大区

好了,到这里,EAX里存的就是有多少个大区了

继续看

0040C945  |>  33C9                                 xor     ecxecx
0040C947  |.  3BC5                                   cmp     eaxebp
0040C949  |.  76 29                                   jbe     short 0040C974
0040C94B  |.  8B97 84000000                  mov     edxdword ptr [edi+84]           ;  
取出我们选择的大区的序号
0040C951  |.  8B76 04                             mov     esidword ptr [esi+4]            ;  
取出大区的首个元素:序号

 

下面开始循环,遍历各个大区,我们取大区的信息也主要就依靠这个循环了~~~

0040C954  |.  33FF                          xor     ediedi
0040C956  |.  897424 24                 mov     dword ptr [esp+24], esi
0040C95A  |.  897C24 10               mov     dword ptr [esp+10], edi
0040C95E  |.  8BFF                        mov     ediedi
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     ecxeax
0040C96E  |.^ 72 F0         \             jb      short 0040C960
0040C970  |.  897C24 10                 mov     dword ptr [esp+10], edi

根据这个循环,我们可以很轻松的写出遍历所有大区的代码了,不过不用太着急,在这里标记一下,因为,这个大区的结构中很多的数据成员,我们都不清楚,现在只知道第一个成员是大区的ID,第二个是名字而且结构的大小要凑够0x1D4,其他的都还未知,继续分析,或许等下就清晰了,嘿嘿!

      

继续看代码:

0040C98B  |> \8B8437 C80100>   mov     eaxdword ptr [edi+esi+1C8]       看到了吧,大区数构中偏移第1C8的成员是个最小值
0040C992  |.  3BC5                         cmp     eaxebp
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     ecxdword ptr [edi+esi+1CC]      ;  
大区数构中偏移第1CC的成员是个最大值
0040C9A7  |.  2BC8                       sub     ecxeax                          ;  
同算大区的数量一样,计算服务器的数量
0040C9A9  |.  B8 E1830F3E           mov     eax, 3E0F83E1
0040C9AE  |.  F7E9                         imul    ecx                               ;  3E0F83E1*(
最大值-最小值)
0040C9B0  |.  C1FA 06                    sar     edx, 6                            ;  
取其积的高32位值右移6
0040C9B3  |.  8BC2                         mov     eaxedx
0040C9B5  |.  C1E8 1F                   shr     eax1F                           ;  
再移动0x1F
0040C9B8  |.  03C2                         add     eaxedx                          ;  
取他们的和OD显示是4,即4个服务器,循环4
0040C9BA  |.  894424 20                mov     dword ptr [esp+20], eax           ;  
把结果保存一下

哈哈,结构越来越清晰,思路越来越明确,大家猜下,接下来是要干什么了啊~~~~

 

0040C9BE  |> \8B83 1C010000 mov     eaxdword ptr [ebx+11C]          还不清楚它的作用,OD显示是6,先不管它
0040C9C4  |.  33D2                  xor     edxedx
0040C9C6  |.  85C0          test    eaxeax
0040C9C8  |.  894424 18     mov     dword ptr [esp+18], eax
0040C9CC  |.  0F8E 95010000 jle     0040CB67
0040C9D2  |.  33C9          xor     ecxecx
0040C9D4  |.  EB 0A         jmp     short 0040C9E0                    ;  
下面的代码应该很眼熟吧,典型的For循环
0040C9D6  |>  8B4C24 2C     /mov     ecxdword ptr [esp+2C]
0040C9DA  |.  8B5424 28     |mov     edxdword ptr [esp+28]
0040C9DE  |.  8BFF          |mov     ediedi
0040C9E0  |>  3B5424 20      cmp     edxdword ptr [esp+20]          ;  
将计数器EDX跟服务器的数量进行比较,看看循环是否结束
0040C9E4  |.  0F83 42010000 |jnb     0040CB2C
0040C9EA  |.  8B8437 C80100>|mov     eaxdword 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     edidword ptr [ecx+eax]         ;  ECX是结构偏移的计数器,大家都应该能看明白的
0040C9F4  |.  42                              |inc     edx                              ;  
循环计数器了,不用我讲的·~~
0040C9F5  |.  81C1 08010000        |add     ecx, 108                        
每个服务器信息结构的大小是0x108,指到数组的下个成员
0040C9FB  |.  85ED                       |test    ebpebp
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     ebpdword ptr [ebx+11C]         ;  
看来这个0x11C很关键哦,
0040CA11  |.  0F8D 9D020000      |jge     0040CCB4                         ;  
大于等于就跳走了~~~

 

这里用到了这个0x11C这个变量,根据上面代码的最后两行,如果服务器数量大于等于6就跳走,估计是个用来防止异常的,有兴趣的朋友可以跟一下,这里就不多说了!

继续看代码:

0040CA17  |.  8B83 18010000 |mov     eaxdword ptr [ebx+118]         ;  取出第一个服务器结构的首地址
0040CA1D  |.  8B34A8        |mov     esidword ptr [eax+ebp*4]
0040CA20  |.  8D04A8        |lea     eaxdword ptr [eax+ebp*4]       ;  dword ptr [eax+ebp*4]
不会被看糊涂吧,取结构体成员的基本方法!
0040CA23  |.  8D57 04       |lea     edxdword ptr [edi+4]            
还记得EDI里存的是什么吧,哈哈,偏移+4就是取服务器名字了~
0040CA26  |.  85D2          |test    edxedx
0040CA28  |.  C746 3C 01000>|mov     dword ptr [esi+3C], 1
0040CA2F  |.  8D9E 8C000000 |lea     ebxdword ptr [esi+8C]
0040CA35  |.  75 04         |jnz     short 0040CA3B
0040CA37  |.  33C0          |xor     eaxeax
0040CA39  |.  EB 14         |jmp     short 0040CA4F
0040CA3B  |>  8BC2          |mov     eaxedx                         ;  1
0040CA3D  |.  8D48 01       |lea     ecxdword ptr [eax+1]
0040CA40  |.  894C24 30     |mov     dword ptr [esp+30], ecx
0040CA44  |>  8A08          |/mov     clbyte ptr [eax]              ;  
断在了这里,也就是说,这里取了服务器的列表信息!
0040CA46  |.  40            ||inc     eax                              
眼熟不?哈哈
0040CA47  |.  84C9          ||test    clcl
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     eax8C08C08D
0040C937  |.  F7E9                                  imul    ecx
0040C939  |.  03D1                                  add     edxecx
0040C93B  |.  C1FA 08                             sar     edx, 8
0040C93E  |.  8BC2                                 mov     eaxedx
0040C940  |.  C1E8 1F                             shr     eax1F
0040C943  |.  03C2                                  add     eaxedx

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     eaxedx
0040C9B5  |.  C1E8 1F        shr     eax1F                           ;  
再移动0x1F
0040C9B8  |.  03C2            add     eaxedx          ;  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        eaxdword ptr [XXUPDATE_BASE_ADDR+0xDC]
            test    eaxeax
            je        NULL_POINT
            mov        eaxdword ptr [eax]
            test    eaxeax
            je        NULL_POINT
            mov        eaxdword ptr [eax+0x4]
NULL_POINT:
        retn;
    }
}

 

/************************************************************************/
/* 
函数名:GetAreaNum
/* 
  数:无
/* 
返回值:返回游戏更新列表中大区的个数
/* 
  能:获取游戏更新列表中大区的个数
/* 
  者:bester @ 2/17/2009
/************************************************************************/

__declspec(naked) int WINAPI GetAreaNum()
{
    _asm
    {
        mov        eaxdword ptr [XXUPDATE_BASE_ADDR+0xDC]
        test    eaxeax
        je        NULL_POINT
        mov        eaxdword ptr [eax]
        test    eaxeax
        je        NULL_POINT
        mov        eaxdword ptr [eax+0x4]
        test    eaxeax
        je        NULL_POINT
        mov        ecxdword ptr [XXUPDATE_BASE_ADDR+0xDC]
        test    ecxecx
        je        NULL_POINT
        mov        ecxdword ptr [ecx]
        test    ecxecx
        je        NULL_POINT
        mov        ecxdword ptr [eax+0x8]
        test    ecxecx
        je        NULL_POINT
        sub        ecx,eax
        mov     eax, 0x8C08C08D
        imul    ecx
        add     edxecx
        sar     edx, 0x8
        mov     eaxedx
        shr     eax, 0x1F
        add     eaxedx
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     eaxedx
        shr     eax, 0x1F         ;  
再移动0x1F
        add     eaxedx          ;  OD
显示是4,即有4个服务器,循环次数为4
        mov        TheServerNum,eax
    }
    return    TheServerNum;
}

/************************************************************************/
/* 
函数名:GetTheMaxServerNum
/* 
  数:无
/* 
返回值:返回游戏指定的每个区最多可拥有服务器的数量
/* 
  能:如果取到的服务器个数大于等于这个数就说明出错了
/* 
  者:bester @ 2/17/2009
/************************************************************************/

__declspec(naked) int WINAPI GetTheMaxServerNum()
{
    __asm
    {
        mov        eaxdword ptr [XXUPDATE_BASE_ADDR+0x11C]
        test    eaxeax
        je        NULL_POINT
        mov        eaxdword 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("
遍历服务器列表时,出现异常……");
    }
}

       最后看下效果吧:

总的来说,还是不错的,是吗?

哈哈,本人才疏学浅,也只能讲点这些简单的东西了,仅希望能以此抛砖引玉,引来大牛的分享……

Feedback

# re: [原创]曾经写的一个遍历寻仙游戏服务器列表的文章(基础)  回复  更多评论   

2009-10-29 10:56 by 得到
看不懂诶...

# re: [原创]曾经写的一个遍历寻仙游戏服务器列表的文章(基础)  回复  更多评论   

2010-05-10 10:09 by zpeng
非常好的资料 ,

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理