标题:蜘蛛纸牌也来辅助一下
作者:cyane
时间:2009/11/18, 0:32
连接:http://bbs.pediy.com/showthread.php?t=101435

       由于工作需要,到了外蒙,到了驻地才发现这里没有网络,连为数不多的电视都是接收的卫星信号,我快崩溃了......
       在工作之余,除了领略广袤的大草原的,还时不时的感受着不知从哪个方向刮来的大风,屋外虽算不上冰天雪地,但也是寒冷无比啊,在百无聊赖之际,就有了这篇东西(没什么技术,只是一时兴趣,希望大家不要笑话)。
      记得曾在论坛里看到过有人把扫雷改成秒破版的,当时下载下来,挺有意思,想着自己有时间也改个玩玩。这下好了,到了外蒙,除了不缺时间,什么都缺,于是动手改了这个蜘蛛纸牌的游戏
       在反编译游戏之前,我先玩了几局,感觉上不是很有意思 :<
       首先OD载入,打开后,来看看它调用的API函数吧,有很多,其中BitBlt比较明显,我们首先从它入手吧。可能有人会问为什么要首先在这个函数上设断,玩了这个游戏后你会发现它有很多位图的移动过程,所以免不了要复制图片从一点到另一点,所以我觉得这个函数比较重要,如果这里实在没什么收获,可以再选择嘛,呵呵。
    BP BitBlt后
    F9运行程序来看看
01002799  |.  8B46 0C       mov     eax, dword ptr [esi+C]
0100279C  |.  2B46 04       sub     eax, dword ptr [esi+4]
0100279F  |.  8955 E8       mov     dword ptr [ebp-18], edx
010027A2  |.  8B55 10       mov     edx, dword ptr [ebp+10]
010027A5  |.  3BD0            cmp     edx, eax
010027A7  |.  8955 18       mov     dword ptr [ebp+18], edx
010027AA  |.  0F8D BF000000 jge     0100286F
010027B0  |>  8B55 14       /mov     edx, dword ptr [ebp+14]
010027B3  |.  8B5D 18       |mov     ebx, dword ptr [ebp+18]
010027B6  |.  3B5A 0C       |cmp     ebx, dword ptr [edx+C]
010027B9  |.  0F8D B0000000 |jge     0100286F
010027BF  |.  8B55 EC       |mov     edx, dword ptr [ebp-14]
010027C2  |.  8B5D 10       |mov     ebx, dword ptr [ebp+10]
010027C5  |.  03D3            |add     edx, ebx
010027C7  |.  3955 18       |cmp     dword ptr [ebp+18], edx
010027CA  |.  7D 10           |jge     short 010027DC
010027CC  |.  8B55 EC       |mov     edx, dword ptr [ebp-14]
010027CF  |.  6A 40           |push    40
010027D1  |.  58                |pop     eax
010027D2  |.  2BC2            |sub     eax, edx
010027D4  |.  8955 F8       |mov     dword ptr [ebp-8], edx
010027D7  |.  8945 F0       |mov     dword ptr [ebp-10], eax
010027DA  |.  EB 1B           |jmp     short 010027F7
010027DC  |>  8B55 E8      |mov     edx, dword ptr [ebp-18]
010027DF  |.  8365 F0 00   |and     dword ptr [ebp-10], 0
010027E3  |.  2BC2            |sub     eax, edx
010027E5  |.  2B45 10       |sub     eax, dword ptr [ebp+10]
010027E8  |.  8955 F8       |mov     dword ptr [ebp-8], edx
010027EB  |.  3945 18       |cmp     dword ptr [ebp+18], eax
010027EE  |.  74 07           |je      short 010027F7
010027F0  |.  C745 F8 40000>|mov     dword ptr [ebp-8], 40
010027F7  |>  8B5D 0C      |mov     ebx, dword ptr [ebp+C]
010027FA  |.  EB 5A            |jmp     short 01002856
010027FC  |>  8B45 14       |/mov     eax, dword ptr [ebp+14]
010027FF  |.  3B58 08        ||cmp     ebx, dword ptr [eax+8]
01002802  |.  7D 56           ||jge     short 0100285A
01002804  |.  8B55 0C       ||mov     edx, dword ptr [ebp+C]
01002807  |.  8D0417        ||lea     eax, dword ptr [edi+edx]
0100280A  |.  3BD8            ||cmp     ebx, eax
0100280C  |.  7D 0A           ||jge     short 01002818
0100280E  |.  6A 3F            ||push    3F
01002810  |.  58                 ||pop     eax
01002811  |.  897D FC        ||mov     dword ptr [ebp-4], edi
01002814  |.  2BC7             ||sub     eax, edi
01002816  |.  EB 17            ||jmp     short 0100282F
01002818  |>  8B45 E4       ||mov     eax, dword ptr [ebp-1C]
0100281B  |.  2BC8             ||sub     ecx, eax
0100281D  |.  2BCA             ||sub     ecx, edx
0100281F  |.  3BD9            ||cmp     ebx, ecx
01002821  |.  8945 FC       ||mov     dword ptr [ebp-4], eax
01002824  |.  74 07           ||je      short 0100282D
01002826  |.  C745 FC 3F000>||mov     dword ptr [ebp-4], 3F
0100282D  |>  33C0          ||xor     eax, eax
0100282F  |>  68 2000CC00   ||push    0CC0020                      ; /ROP = SRCCOPY
01002834  |.  FF75 F0       ||push    dword ptr [ebp-10]             ; |YSrc
01002837  |.  50               ||push    eax                                      ; |XSrc
01002838  |.  FF75 F4       ||push    dword ptr [ebp-C]               ; |hSrcDC
0100283B  |.  FF75 F8       ||push    dword ptr [ebp-8]               ; |Height
0100283E  |.  FF75 FC       ||push    dword ptr [ebp-4]               ; |Width
01002841  |.  FF75 18       ||push    dword ptr [ebp+18]             ; |YDest
01002844  |.  53                ||push    ebx                                      ; |XDest
01002845  |.  FF75 08       ||push    dword ptr [ebp+8]              ; |hDestDC
01002848  |.  FF15 7C100001 ||call    dword ptr [<&GDI32.BitBlt>]    ; \BitBlt
0100284E  |.  8B4E 08       ||mov     ecx, dword ptr [esi+8]
01002851  |.  035D FC       ||add     ebx, dword ptr [ebp-4]
01002854  |.  2B0E           ||sub     ecx, dword ptr [esi]
01002856  |>  3BD9           | cmp     ebx, ecx
01002858  |.^ 7C A2         |\jl      short 010027FC
0100285A  |>  8B45 F8       |mov     eax, dword ptr [ebp-8]
0100285D  |.  0145 18       |add     dword ptr [ebp+18], eax
01002860  |.  8B46 0C       |mov     eax, dword ptr [esi+C]
01002863  |.  2B46 04       |sub     eax, dword ptr [esi+4]
01002866  |.  3945 18       |cmp     dword ptr [ebp+18], eax
01002869  |.^ 0F8C 41FFFFFF \jl      010027B0
      首先是在这里被断下了,我们先来看看这个函数体,有没有什么信息,往上瞧瞧
0100271F  |.  53            push    ebx
01002720  |.  56            push    esi
01002721  |.  57            push    edi
01002722  |.  FF75 08       push    dword ptr [ebp+8]                ; /hDC
01002725  |.  FF15 74100001 call    dword ptr            /<CreateCompatibleDC>
0100272B  |.  68 BC120001   push    010012BC             ; /RsrcName = "FELT"
01002730  |.  FF35 00200101 push    dword ptr [1012000]   ; |hInst = 01000000
01002736  |.  8945 F4       mov     dword ptr [ebp-C], eax           ; |
01002739  |.  FF15 80120001 call    dword ptr [<&USER32.LoadBitmapW>>; 
0100273F  |.  50            push    eax                              ; /hObject
01002740  |.  FF75 F4       push    dword ptr [ebp-C]                ; |hDC
01002743  |.  8945 DC       mov     dword ptr [ebp-24], eax          ; |
01002746  |.  FF15 78100001 call    dword ptr [<&GDI32.SelectObject>>; 
0100274C  |.  8B75 18       mov     esi, dword ptr [ebp+18]
0100274F  |.  8B1E          mov     ebx, dword ptr [esi]
01002751  |.  8B4D 14       mov     ecx, dword ptr [ebp+14]
       恩,有了新的发现,有个LoadBitmap函数,这是加载位图的函数,加载的位图名称已经显示出来了,就是那个"FELT",用RESHACKER打开程序看看,在位图资源处应该会看到这个名为"FELT"的图片,只是用我的RESHACKER没有打开,换eXeScope试试,可以看到这个图片就是程序的背景图片,还有52张扑克图片,扑克的点数图片咱就不说了,"CARDBACK"是蜘蛛的扑克背景图片,标识号106的是关于对话框的图片,标识号108的是放置扑克的图片,这里只要一一对照知道是哪些相互对应就可以了。


     可以看到"FELT"应该是程序的背景图片,这里可以大胆猜猜测一下,程序中当前循环的目的就是画出程序的背景,这里可以先放一下,把这里BitBlt的断点先取消,接着F9往下看看
010028A7  |.  8945 FC       mov     dword ptr [ebp-4], eax
010028AA  |.  8B45 14       mov     eax, dword ptr [ebp+14]
010028AD  |.  83F8 01       cmp     eax, 1
010028B0  |.  56                push    esi
010028B1  |.  8B75 08       mov     esi, dword ptr [ebp+8]
010028B4  |.  57                push    edi
010028B5  |.  8975 D0       mov     dword ptr [ebp-30], esi  
//[ebp-30]=esi
010028B8  |.  7C 1D           jl      short 010028D7  
010028BA  |.  83F8 34       cmp     eax, 34
010028BD  |.  7F 18           jg      short 010028D7
//如果EAX<[ebp-30]或EAX>0x34(也就是52)跳到10028D7
//如果上述不满足向下走
010028BF  |.  50                push    eax                              ; /<%d>
010028C0  |.  8D45 D4       lea     eax, dword ptr [ebp-2C]          ; |
010028C3  |.  68 DC120001   push    010012DC             ; |Format = "CARD%d"
010028C8  |.  50                push    eax                              ; |s
010028C9  |.  FF15 84120001 call    dword ptr [<&USER32.wsprintfW>]  ; \wsprintfW
//以Format函数格式输出,这里可以应该是 输出“CARD1”~“CARD52”
010028CF  |.  83C4 0C       add     esp, 0C
010028D2  |.  8D45 D4       lea     eax, dword ptr [ebp-2C]
010028D5  |.  EB 1B           jmp     short 010028F2
//上面不符合条件的跳转到这里
010028D7  |>  83F8 69      cmp     eax, 69
010028DA  |.  75 07           jnz     short 010028E3
010028DC  |.  68 BC120001   push    010012BC                  ;  UNICODE "FELT"
010028E1  |.  EB 10           jmp     short 010028F3
010028E3  |>  83F8 68      cmp     eax, 68
010028E6  |.  75 07           jnz     short 010028EF
010028E8  |.  68 C8120001   push    010012C8               ;  UNICODE "CARDBACK"
//可以看到会压入堆栈1个值,这里可以看到FELT 或 CARDBACK 其中之一
//从上面对于图片的分析知道,这是2幅图片的值
//如果不是这2幅图片则向下跳转到10028F3
010028ED  |.  EB 04          jmp     short 010028F3
010028EF  |>  0FB7C0       movzx   eax, ax
010028F2  |>  50              push    eax    
//把刚才得到的CARD? 或 FELT 或 CARDBACK压入堆栈
010028F3  |>  FF35 00200101 push    dword ptr [1012000]     ; |hInst = 01000000
010028F9  |.  FF15 80120001 call    dword ptr [<&USER32.LoadBitmapW>>; 
//用LoadBitmap来读取EAX中指定的图片
010028FF  |.  56                 push    esi                              ; /hDC
01002900  |.  8945 CC       mov     dword ptr [ebp-34], eax          ; |
01002903  |.  FF15 74100001 call    dword ptr [<&GDI32.CreateCompati>; 
01002909  |.  FF75 CC       push    dword ptr [ebp-34]               ; /hObject
0100290C  |.  8B3D 78100001 mov     edi, dword ptr [<&GDI32.SelectOb>; |
01002912  |.  8BF0            mov     esi, eax                         ; |
01002914  |.  56                push    esi                              ; |hDC
01002915  |.  FFD7            call    edi                         ;| SelectObject
01002917  |.  8B5D 0C       mov     ebx, dword ptr [ebp+C]
0100291A  |.  68 2000CC00   push    0CC0020                 ; /ROP = SRCCOPY
0100291F  |.  6A 00           push    0                       ; |YSrc = 0
01002921  |.  6A 00           push    0                       ; |XSrc = 0
01002923  |.  56                push    esi                     ; |hSrcDC
01002924  |.  6A 60           push    60                      ; |Height = 60 (96.)
01002926  |.  6A 47           push    47                      ; |Width = 47 (71.)
01002928  |.  FF75 10        push    dword ptr [ebp+10]      ; |YDest
0100292B  |.  8945 C8       mov     dword ptr [ebp-38], eax ; |
0100292E  |.  53                push    ebx                     ; |XDest
0100292F  |.  FF75 D0       push    dword ptr [ebp-30]      ; |hDestDC
01002932  |.  FF15 7C100001 call    dword ptr [<&GDI32.BitBlt>]; |
//拷贝指定的图片到某个位置
      恩,分析这么多差不多了,把这里的断点取消,F9再往下看,我们会发现程序已经出现窗口了,出现了可以供选择难易程度的对话框,我们先选择 "初级",然后点击"确定"看看


我们会发现程序又被断下了,由于下面的程序段比较长,我省略一部分
01005CEA  |.  8B35 74100001 mov     esi, dword ptr [<&GDI32.CreateCo>;  
01005CF0  |.  53            push    ebx                              ; /hDC
01005CF1  |.  FFD6          call    esi             ; \CreateCompatibleDC
01005CF3  |.  53            push    ebx                              ; /hDC
01005CF4  |.  8945 A8       mov     dword ptr [ebp-58], eax          ; |
01005CF7  |.  FFD6          call    esi             ; \CreateCompatibleDC
01005CF9  |.  68 C8120001   push    010012C8        ; /RsrcName = "CARDBACK"
//这里是CARDBACK后面有LoadBitmap函数,应该就是装载CARDBACK图片的
01005CFE  |.  FF35 00200101 push    dword ptr [1012000]  ; |hInst = 01000000
01005D04  |.  8945 B8       mov     dword ptr [ebp-48], eax          ; |
01005D07  |.  FF15 80120001 call    dword ptr [<&USER32.LoadBitmapW>>; 
01005D0D  |.  50            push    eax                              ; /hObject
01005D0E  |.  FF75 B8       push    dword ptr [ebp-48]               ; |hDC
01005D11  |.  8945 94       mov     dword ptr [ebp-6C], eax          ; |
01005D14  |.  FF15 78100001 call    dword ptr [<&GDI32.SelectObject>>; \
.........................................................
01005F0E  |> \8B45 C8       ||mov     eax, dword ptr [ebp-38]
//EAX=[EBP-38]
01005F11  |.  83F8 68       ||cmp     eax, 68
01005F14  |.  75 29         ||jnz     short 01005F3F
//EAX与0x68比较,还记得0x68和0x69吗?
//呵呵 就是那2幅图片啊,如果忘记了,看看我们前面分析的那个函数段吧
//如果不是CARDBACK图片就向下到1005f3f
01005F16  |.  394D A0       ||cmp     dword ptr [ebp-60], ecx
01005F19  |.  68 2000CC00   ||push    0CC0020
01005F1E  |.  51            ||push    ecx
01005F1F  |.  51            ||push    ecx
01005F20  |.  FF75 B8       ||push    dword ptr [ebp-48]
//[ebp-60]不为0,就PUSH 60
01005F23  |.  75 16         ||jnz     short 01005F3B
01005F25  |.  6A 09         ||push    9
01005F27  |>  6A 47         ||push    47                     ; |Width = 47 (71.)
01005F29  |.  FF75 CC       ||push    dword ptr [ebp-34]     ; |YDest
01005F2C  |.  FF75 D0       ||push    dword ptr [ebp-30]     ; |XDest
01005F2F  |.  53            ||push    ebx                    ; |hDestDC
01005F30  |.  FF15 7C100001 ||call    dword ptr [<&GDI32.BitBlt>] ; \BitBlt
//分析知道程序根据[EBP-60]和ECX的数值进行比较,从而确定绘制的图高0x60或者是9
//这里我们可以理解成绘制一幅位图或者是绘制一幅图的一部分
01005F36  |.  E9 84000000   ||jmp     01005FBF
01005F3B  |>  6A 60         ||push    60
01005F3D  |.^ EB E8         ||jmp     short 01005F27
//上面知道,如果不是CARDBACK图片就到这里了,再接着分析
01005F3F  |>  83F8 01       ||cmp     eax, 1
01005F42  |.  7C 1D         ||jl      short 01005F61
01005F44  |.  83F8 34       ||cmp     eax, 34
01005F47  |.  7F 18         ||jg      short 01005F61
//EAX如果小于1,或大于52,就跳到1005F61
//EAX如果是 1~52 时,这个值也是可以查到(eXeScope)
01005F49  |.  50            ||push    eax                            ; /<%d>
01005F4A  |.  8D45 D4       ||lea     eax, dword ptr [ebp-2C]        ; |
01005F4D  |.  68 DC120001   ||push    010012DC           ; |Format = "CARD%d"
01005F52  |.  50            ||push    eax                            ; |s
01005F53  |.  FF15 84120001 ||call    dword ptr [<&USER32.wsprintfW>>; \wsprintfW
//如果EAX属于1~52 之间就输入 CARD1~CARD52,和刚才分析过的程序段一样吧
01005F59  |.  83C4 0C       ||add     esp, 0C
01005F5C  |.  8D45 D4       ||lea     eax, dword ptr [ebp-2C]
01005F5F  |.  EB 0F         ||jmp     short 01005F70
01005F61  |>  83F8 69       ||cmp     eax, 69
01005F64  |.  75 07         ||jnz     short 01005F6D
01005F66  |.  68 BC120001   ||push    010012BC             ;  UNICODE "FELT"
//这里是EAX 和 0x69的比较,就是是否画出图FELT
01005F6B  |.  EB 04         ||jmp     short 01005F71
01005F6D  |>  0FB7C0        ||movzx   eax, ax
01005F70  |>  50            ||push    eax
01005F71  |>  FF35 00200101 ||push    dword ptr [1012000] ; |hInst = 01000000
01005F77  |.  FF15 80120001 ||call    dword ptr [<&USER32.LoadBitmap>; 
01005F7D  |.  50            ||push    eax                            ; /hObject
01005F7E  |.  FF75 A8       ||push    dword ptr [ebp-58]             ; |hDC
01005F81  |.  8945 A4       ||mov     dword ptr [ebp-5C], eax        ; |
01005F84  |.  FF15 78100001 ||call    dword ptr [<&GDI32.SelectObjec>; \
01005F8A  |.  68 2000CC00   ||push    0CC0020                 ; /ROP = SRCCOPY
01005F8F  |.  6A 00         ||push    0                       ; |YSrc = 0
01005F91  |.  6A 00         ||push    0                       ; |XSrc = 0
01005F93  |.  FF75 A8       ||push    dword ptr [ebp-58]      ; |hSrcDC
01005F96  |.  8945 90       ||mov     dword ptr [ebp-70], eax ; |
01005F99  |.  6A 60         ||push    60                     ; |Height = 60 (96.)
01005F9B  |.  6A 47         ||push    47                     ; |Width = 47 (71.)
01005F9D  |.  FF75 CC       ||push    dword ptr [ebp-34]      ; |YDest
01005FA0  |.  FF75 D0       ||push    dword ptr [ebp-30]      ; |XDest
01005FA3  |.  53            ||push    ebx                     ; |hDestDC
01005FA4  |.  FF15 7C100001 ||call    dword ptr [<&GDI32.BitBlt>]  ; \BitBlt
       根据这里的BitBlt中Height和Width 2个值我们可以分析出每张扑克牌的高度是0x60,宽度是0x47,这里我们可以截取一幅扑克图片的大小来比较一下,可以知道这个数值确实是单张扑克的尺寸,这里我们就可以猜测一下了,函数的功能就是绘制下边这幅图。


这里我们再次取消BitBlt的断点后,F9运行,可以发现程序已经运行,没有再被断下来了。
    呵呵,好了,初步分析可以告一段落了,这里我们首先来分析一下,最初的那个疑问,首个BitBlt是否是用来画出背景的呢?这里可以用个简单的方法,根据我们刚才的分析知道
push    010012BC  对应的是  "FELT"
push    010012C8  对应的是  "CARDBACK"
      这里我们可以把"FELT"替换成"CARDBACK"试一下,恩,效果是很明显的。
这样刚才的分析应该大体都成立,因为都用到了LoadBitmap来加载图片,而且我们可以分析具体显示的是哪幅位图,思路应该没什么问题。
  下面我们再来具体的分析一下,刚才所遇到的每个被断下位置处的函数体的作用。看能不能更快找到关键地方呢(下面我所说的函数体一词,指的是包含BitBlt函数的那个整体函数,即PUSH EBP, MOV EBP, ESP下的所有部分)。
    首先分析第一个函数,从具体功能来说,我们分析出它会绘制出程序的背景,试想一下,它和后面我们遇到的函数比较而言,其中不太可能有产生扑克点数函数,恩,可以先放一下。
再来分析一下遇到的第二个函数,可以看到wsprintfw函数将输出CARD1~CARD52间的某个值,也就是对应扑克牌中的具有具体花色和具体点数的牌,这可以从上面分析中得知(好像挺嗦:)这里我们试想一下,程序的工作流程应该是先初始化产生一组随机数,然后通过随机数来计算从而得到要输出的扑克,然后来才会在画面上绘制出具体的扑克。有了这个思想,我们来看一下wsprintfw函数距离函数体开始处的位置,可以发现距离是很近的,可以断定这里不可能有初始化的函数,只可能是初始化好后,程序运行到这里把位图拷贝到桌面才对。
     好的,我们再来看一下第三个函数吧,根据我们刚才的思路,这第三个参数是最有可能的,我们就先把注意力集中到这里吧。
     我们先在地址01005FA4 这里的BitBlt下断点,其他的断点都先删除,经过跟踪,我们发现一直都是在一个循环体内跑,而且只要想点开程序窗体,马上又被断下来了,仔细想想,BitBlt这个函数肯定是不断的被用来重画画面的,如果一直这样循环怎么行呢,恩,我们再想想有没有什么其他的方法呢?恩,有的,就是wsprintfw函数,可以想到只有当显示扑克点数的时候它才会运行到这里,所以这里应该不会总是处在循环状态,我们来测试一下,就在wsprintfw函数下断点,其他的断点删除,下面是堆栈显示的图片。


     很明显这张应该是CARD44,应该是 黑桃5。
然后我们F8单步来走,当函数跳回到开始处时,发现了一个特别的CALL,经验证这是个关键CALL
01005DEE  |.  3945 BC       ||cmp     dword ptr [ebp-44], eax
01005DF1  |.  0F9DC1        ||setge   cl
01005DF4  |.  894D A0       ||mov     dword ptr [ebp-60], ecx
01005DF7  |.  8B4D C4       ||mov     ecx, dword ptr [ebp-3C]
01005DFA  |.  E8 17CFFFFF   ||call    01002D16                  ;  关键CALL
01005DFF  |.  8945 C8       ||mov     dword ptr [ebp-38], eax
//那我们F7跟进,看看
01002D16  /$  8BFF        mov     edi, edi     GDI32.SetPixel
01002D18  |.  55            push    ebp
01002D19  |.  8BEC        mov     ebp, esp
01002D1B  |.  837D 0C FF   cmp     dword ptr [ebp+C], -1
01002D1F  |.  56            push    esi
01002D20  |.  8BF1        mov     esi, ecx
01002D22  |.  75 05       jnz     short 01002D29
01002D24  |.  6A 6C       push    6C
01002D26  |.  58            pop     eax
01002D27  |.  EB 42       jmp     short 01002D6B
01002D29  |>  8B4E 08       mov     ecx, dword ptr [esi+8]  //关键地址
01002D2C  |.  57            push    edi
01002D2D  |.  FF75 0C   push    dword ptr [ebp+C]  //扑克的行数
01002D30  |.  FF75 08    push    dword ptr [ebp+8]  //扑克的列数
01002D33  |.  E8 97510000    call    01007ECF    //关键函数1
01002D38  |.  8B4E 04    mov     ecx, dword ptr [esi+4]
01002D3B  |.  8BF8         mov     edi, eax    //edi=eax
01002D3D  |.  57            push    edi
01002D3E  |.  E8 DB460000   call    0100741E
01002D43  |.  85C0         test    eax, eax
01002D45      74 20        je      short 01002D67  //关键跳转
//这里如果NOP掉,程序上端的扑克都将正面向上
01002D47  |.  8B76 04        mov     esi, dword ptr [esi+4]
01002D4A  |.  53            push    ebx
01002D4B  |.  57            push    edi
01002D4C  |.  8BCE         mov     ecx, esi
01002D4E  |.  E8 E6460000   call    01007439    //关键函数2
01002D53  |.  8BD8         mov     ebx, eax
01002D55  |.  57            push    edi
01002D56  |.  6BDB 0D      imul    ebx, ebx, 0D
01002D59  |.  8BCE         mov     ecx, esi
01002D5B  |.  E8 F3460000   call    01007453    //关键函数3
01002D60  |.  8D4403 01     lea     eax, dword ptr [ebx+eax+1]
01002D64  |.  5B            pop     ebx
01002D65  |.  EB 03         jmp     short 01002D6A
01002D67  |>  6A 68         push    68
01002D69  |.  58            pop     eax
01002D6A  |>  5F            pop     edi
01002D6B  |>  5E            pop     esi
01002D6C  |.  5D            pop     ebp
01002D6D  \.  C2 0800       retn    8
      下面我们具体分析每个关键的作用
    01002D45地址处的跳转控制这程序上端的扑克是否处于正面显示状态,如果NOP掉,可以看到那些图片都正面向上了


    最初在这里我自己改写了一下,把间距拉大了一些,好看了一些,见下图


后来分析出计算扑克点数的地址后,就没再用这种方法了,后面的方法更好用,呵呵
[ESP+C]表示显示扑克的行数
[ESP+8]表示显示扑克的列数
    接下来看一下 关键函数1吧 F7跟进
01007ECF  /$  8BFF         mov     edi, edi     GDI32.SetPixel
01007ED1  |.  55             push    ebp
01007ED2  |.  8BEC         mov     ebp, esp
01007ED4  |.  8B45 08    mov     eax, dword ptr [ebp+8]      ①
//EAX=[EBP+8]是扑克的列数
01007ED7  |.  8B0C81     mov     ecx, dword ptr [ecx+eax*4]   ②
//这里需要用到上个函数的关键地址了,这里指向另一个地址
//这里可以看成是一个数组在取其中的某个值
01007EDA  |.  85C9         test    ecx, ecx
01007EDC  |.  74 10        je      short 01007EEE
01007EDE  |.  FF75 0C     push    dword ptr [ebp+C]//扑克的行数为内循环的次数
01007EE1  |.  E8 0CFFFFFF   call    01007DF2
01007EE6  |.  85C0          test    eax, eax
01007EE8  |.  74 04         je      short 01007EEE
01007EEA  |.  8B00          mov     eax, dword ptr [eax]    ③
01007EEC  |.  EB 03         jmp     short 01007EF1
01007EEE  |>  83C8 FF    or      eax, FFFFFFFF
01007EF1  |>  5D             pop     ebp
01007EF2  \.  C2 0800     retn    8
在地址01007EE1  还有个CALL 跟进
01007DF2  /$  8BFF          mov     edi, edi               ;  GDI32.SetPixel
01007DF4  |.  55              push    ebp
01007DF5  |.  8BEC          mov     ebp, esp
01007DF7  |.  8B01          mov     eax, dword ptr [ecx]    ④
01007DF9  |.  33D2          xor     edx, edx
01007DFB  |.  3955 08      cmp     dword ptr [ebp+8], edx
01007DFE  |.  7E 0D         jle     short 01007E0D
01007E00  |>  85C0         /test    eax, eax
01007E02  |.  74 09         |je      short 01007E0D
01007E04  |.  8B40 08     |mov     eax, dword ptr [eax+8]    ⑤
01007E07  |.  42              |inc     edx
01007E08  |.  3B55 08     |cmp     edx, dword ptr [ebp+8]  //注意这里是循环体
01007E0B  |.^ 7C F3        \jl      short 01007E00
01007E0D  |>  5D            pop     ebp
01007E0E  \.  C2 0400    retn    4
      这里我们把几个关键的语句拿下来组在一起看吧
01007ED4  |.  8B45 08    mov     eax, dword ptr [ebp+8]      ①
//EAX=扑克的列数
01007ED7  |.  8B0C81     mov     ecx, dword ptr [ecx+eax*4]   ②
//ECX=[ECX+EAX*4]的偏移地址
01007DF7  |.  8B01         mov     eax, dword ptr [ecx]    ④
//EAX=[ECX]的偏移地址
01007E04  |.  8B40 08    mov     eax, dword ptr [eax+8]    ⑤
//EAX=EAX+8
01007EEA  |.  8B00         mov     eax, dword ptr [eax]    ③
//EAX=[EAX]的偏移地址   
//别搞混了⑤ ③ 可不是连续的!
     这个关系很明确了吧,这就是这2个CALL的具体作用
     分析完 关键函数1 在往下看,不就是EDI=EAX了吗,这里先保存这个EDI的数值
     再来F7进入 关键函数2 吧
01007439  /$  8BFF         mov     edi, edi
0100743B  |.  55              push    ebp
0100743C  |.  8BEC          mov     ebp, esp
0100743E  |.  8B45 08     mov     eax, dword ptr [ebp+8]
01007441  |.  8B49 0C     mov     ecx, dword ptr [ecx+C]
01007444  |.  8D0440      lea     eax, dword ptr [eax+eax*2]
01007447  |.  8B0481      mov     eax, dword ptr [ecx+eax*4]
0100744A  |.  5D              pop     ebp
0100744B  \.  C2 0400     retn    4
      可以看到主要的就4句,意思和上面刚分析的差不多,这里主要是得到EAX的数值
出了关键函数2后,会发现EBX=EAX, EBX=EBX*0xD,保存EBX,下面会用到的。
接着进入关键函数3
01007453  /$  8BFF          mov     edi, edi
01007455  |.  55              push    ebp
01007456  |.  8BEC          mov     ebp, esp
01007458  |.  8B45 08     mov     eax, dword ptr [ebp+8]
0100745B  |.  8B49 0C     mov     ecx, dword ptr [ecx+C]
0100745E  |.  8D0440      lea     eax, dword ptr [eax+eax*2]
01007461  |.  8B4481 04  mov     eax, dword ptr [ecx+eax*4+4]
01007465  |.  5D               pop     ebp
01007466  \.  C2 0400      retn    4
       这里的主要也是4句,分析同上即可,最后得到EAX的数值,退出关键函数3后,
01002D60  |.  8D4403 01     lea     eax, dword ptr [ebx+eax+1]
//EAX=EBX+EAX+1  
      这里看一下EAX的10进制数值,呵呵,是不是感觉很熟悉,恩,这就是扑克显示的点数,和资源里设置的是一样的,可以从wsprintfw函数中观察到。
  至此,分析的应该差不多了,写个测试程序应该很简单的,于是我就写了一个测试,很奇怪的是OD调试纸牌程序的时候,测试程序好用,一旦,单独打开纸牌程序,测试程序就不好用了?恩,奇怪啊,后来发现是
01002D29  |>  8B4E 08       mov     ecx, dword ptr [esi+8]  //关键地址
     这一句的问题,我取的ecx地址是动态改变的,而[esi+8]=[01012010]是程序的固定地址,在我电脑上显示的是1012010这个数值,应该是全局变量,呵呵,改好地址后,测试程序成功了。
这里贴一下我用VB 写的测试程序吧。
Dim AppHandle As Long    '程序句柄  
Dim ProcessID As Long    '进程PID  
Dim proHandle As Long    '进程句柄  
Dim ReadData(10) As Long  '读取内存数据的数组  
Dim lAddress As Long      '暂存地址
Dim ESI4 As Long      '[ESI+4]
Dim ESI8 As Long      '[ESI+8]
Dim ESIC As Long      '[ESI+C]
Dim n As Integer    '行数变量
Dim i As Integer    '列数变量
Dim m As Integer    '临时变量
Dim I2 As Integer    '临时变量
Dim J As Integer    '临时变量
Dim B(109) As Long    '计算每张扑克用到的变量的地址

  List1.Clear
  AppHandle = FindWindow(vbNullString, "蜘蛛")
  If AppHandle = 0 Then
        Form1.Caption = "游戏辅助--游戏没有启动"
    Else
        Form1.Caption = "游戏辅助--连接游戏成功"
    GetWindowThreadProcessId WndHandle, ProcessID    '获取PID
    If ProcessID = 0 Then
            MsgBox "获取程序PID错误"
        Else
      proHandle = OpenProcess(PROCESS_ALL_ACCESS, False, ProcessID)  
'获取进程句柄
            ReadProcessMemory proHandle, ByVal &H101200C, ReadData(0), 4, 0&
ESI4 = ReadData(0)
            ReadProcessMemory proHandle, ByVal &H1012010, ReadData(0), 4, 0&
            ESI8 = ReadData(0)
            ReadProcessMemory proHandle, ByVal &H1012014, ReadData(0), 4, 0&
            ESIC = ReadData(0)
      '保存了关键的地址
      For n = 0 to 9              '行数
            List1.AddItem "第" & n + 1 & "列"
            For i = 1 To 11              '列数
      EAX = EBX = ECX = EDI = 0        '初始化
            If (J = 0) Then
                lAddress = ESI8 + n * 4   'EAX*4
                ReadProcessMemory proHandle, ByVal lAddress, ReadData(0), 4, 0&
                lAddress = ReadData(0)
                ReadProcessMemory proHandle, ByVal lAddress, ReadData(0), 4, 0&
                lAddress = ReadData(0)
                ReadProcessMemory proHandle, ByVal lAddress, ReadData(0), 4, 0&
                EDI = ReadData(0)      '保存EDI
            Else
                lAddress = ESI8 + n * 4   'EAX*4
                ReadProcessMemory proHandle, ByVal lAddress, ReadData(0), 4, 0&
                lAddress = ReadData(0)
                For I2 = 1 To J  '根据列数循环查找对应的地址
                    ReadProcessMemory proHandle, ByVal lAddress, ReadData(0), 4, 0&
                    lAddress = ReadData(0) + 8
                Next I2
                ReadProcessMemory proHandle, ByVal lAddress, ReadData(0), 4, 0&
                lAddress = ReadData(0)
                ReadProcessMemory proHandle, ByVal lAddress, ReadData(0), 4, 0&
                EDI = ReadData(0)      '保存EDI
            End If
            J = J + 1
            EAX = EDI
            lAddress = ESI4 + 12
            ReadProcessMemory proHandle, ByVal lAddress, ReadData(0), 4, 0&
            lAddress = ReadData(0) + (EAX + EAX * 2) * 4
            ReadProcessMemory proHandle, ByVal lAddress, ReadData(0), 4, 0&
            EBX = ReadData(0) * 13
            lAddress = ESI4 + 12
            ReadProcessMemory proHandle, ByVal lAddress, ReadData(0), 4, 0&
            lAddress = ReadData(0) + (EAX + EAX * 2) * 4 + 4
            'B(m) = lAddress  '定义的一个地址数组B(109)存放的所有地址  
            ReadProcessMemory proHandle, ByVal lAddress, ReadData(0), 4, 0&
            EAX = EBX + ReadData(0) + 1
            List1.AddItem 'CARD' & EAX    '输出为CARD?的字符
            m = m + 1
            Next i
            J = 0
            Next n
            CloseHandle (proHandle)
    End If
  End If
      以上是读取每张扑克的点数,程序很简陋,只是用来测试一下的。  程序中可以注意到我定义了B(109)的数组,用来存储地址,所以就可以来修改内存,这样就可以把每张牌修改成自己想要的点数
程序格式如下
WriteProcessMemory proHandle, ByVal B(0), 0, 4, 0&  '这里0表示A的意思
WriteProcessMemory proHandle, ByVal B(109), 1, 4, 0&  '1表示2的意思
其他的同理修改即可。
InvalidateRect AppHandle, rect, True  '用来刷新屏幕(rect取窗体大小即可)
      发个图给大家看一下吧,如下图


经过测试可以在初级中实现秒级破解,在中级和高级中效果一样,不过会涉及到扑克牌花色的影响,可以再改进的,这里我没有在继续了,思路都是差不多的,有兴趣的可以试试。