蜘蛛纸牌分析

今晚一口气看完了M的BLOG,鸭梨山大啊,看来我还要好好努力。。先转一篇

一大早起来我家的小猫又出问题了。。哎这也用了好多年了。。现在觉得三天两头给我坏一次。。Link灯一直不亮。。打电话给电信局的。人家说他在休假不管这事。。这尼玛坑爹的。大过年的让我上不了网啊。。看会书。。但是上不了网查资料也查不了。。。老爸也很郁闷。。上不了网他就打不了斗地主了。。在卧室里一个人玩蜘蛛纸牌呢。但是技术不够。。让我过去帮他一起看。。正好来分析一下。。帮老爸做个外挂。。。
OD加载之。对RegisterClass下断,一共中断四次后程序就运行了。。看参数WNDCLASS的lpfnWndProc字段。。只有一次在当前模块内。。。看栈里的数据

0006FB08   010071DD  /CALL 到 RegisterClassW 来自 spider.010071D7
0006FB0C   0006FB1C  \pWndClass = 0006FB1C


 

这里0006FB1C就是WNDCLASS结构。lpfnWndProc位于第二个四字。

0006FB1C  00002003
0006FB20  010070AB  spider.010070AB


 可以确定这里就是主窗体过程了。。。。

在游戏刚开始的时候会弹出一个对话框让你选择难度。这里再对DialogBoxParam下断。中断后栈里的数据如下

0006F744   0100671E  /CALL 到 DialogBoxParamW 来自 spider.01006718
0006F748   01000000  |hInst = 01000000
0006F74C   00000077  |pTemplate = 77
0006F750   000704E6  |hOwner = 000704E6 ('蜘蛛',class='蜘蛛')
0006F754   01007B06  |DlgProc = spider.01007B06
0006F758   01012008  \lParam = spider.01012008


 

很显然对话框窗体过程为01007B06。在这里对WM_COMMAND消息下断。按“确定”。看代码

01007B39  |> \56            push    esi                                              ;  Case 1 of switch 01007B1C
01007B3A  |.  8B35 68120001 mov     esi, dword ptr [<&USER32.IsDlgButtonChecked>]    ;  USER32.IsDlgButtonChecked
01007B40  |.  68 F0030000   push    3F0                                              ; /ButtonID = 3F0 (1008.)
01007B45  |.  FF75 08       push    dword ptr [ebp+8]                                ; |hWnd
01007B48  |.  FFD6          call    esi                                              ; \IsDlgButtonChecked
01007B4A  |.  85C0          test    eax, eax
01007B4C  |.  74 10         je      short 01007B5E
01007B4E  |.  A1 44300101   mov     eax, dword ptr [1013044]
01007B53  |.  8B40 04       mov     eax, dword ptr [eax+4]
01007B56  |.  C700 01000000 mov     dword ptr [eax], 1
01007B5C  |.  EB 24         jmp     short 01007B82
01007B5E  |>  68 F1030000   push    3F1
01007B63  |.  FF75 08       push    dword ptr [ebp+8]
01007B66  |.  FFD6          call    esi
01007B68  |.  85C0          test    eax, eax
01007B6A  |.  A1 44300101   mov     eax, dword ptr [1013044]
01007B6F  |.  8B40 04       mov     eax, dword ptr [eax+4]
01007B72  |.  74 08         je      short 01007B7C
01007B74  |.  C700 02000000 mov     dword ptr [eax], 2
01007B7A  |.  EB 06         jmp     short 01007B82
01007B7C  |>  C700 04000000 mov     dword ptr [eax], 4
01007B82  |>  6A 01         push    1                                                ; /Result = 1
01007B84  |.  FF75 08       push    dword ptr [ebp+8]                                ; |hWnd
01007B87  |.  FF15 98110001 call    dword ptr [<&USER32.EndDialog>]                  ; \EndDialog


 

这里调用IsDlgButtonChecked函数来获取单选按钮的选择情况来设置游戏难度。注意这几句

01007B4E  |.  A1 44300101   mov     eax, dword ptr [1013044]
01007B53  |.  8B40 04       mov     eax, dword ptr [eax+4]
01007B56  |.  C700 01000000 mov     dword ptr [eax], 1
。。。
01007B74  |.  C700 02000000 mov     dword ptr [eax], 2
。。。
01007B7C  |>  C700 04000000 mov     dword ptr [eax], 4


 

把结果储存在[[[1013044] + 4]]中。1为初级。2中级。4高级。。。。这里储存的地址为004C0728。。对这里设置硬件访问断点。。。中断在了这里

01007502  |> \8338 01       |||cmp     dword ptr [eax], 1


 

看这附近的代码

.text:0100746E sub_100746E     proc near               ; CODE XREF: sub_100454D+BCp
.text:0100746E                                         ; sub_1005AFB+2Ap
.text:0100746E
.text:0100746E Buffer_length   = dword ptr -0Ch
.text:0100746E buffer          = dword ptr -8
.text:0100746E var_4           = dword ptr -4
.text:0100746E random          = dword ptr  8
.text:0100746E
.text:0100746E                 mov     edi, edi
.text:01007470                 push    ebp
.text:01007471                 mov     ebp, esp
.text:01007473                 sub     esp, 0Ch
.text:01007476                 push    esi
.text:01007477                 mov     esi, ecx
.text:01007479                 push    edi
.text:0100747A                 mov     edi, [esi+8]
.text:0100747D                 mov     eax, edi
.text:0100747F                 shl     eax, 2
.text:01007482                 push    eax             ; wint_t
.text:01007483                 mov     [ebp+Buffer_length], edi
.text:01007486                 call    AllocateHeap    ; 申请缓冲区
.text:0100748B                 test    eax, eax
.text:0100748D                 pop     ecx
.text:0100748E                 mov     [ebp+buffer], eax
.text:01007491                 jz      loc_100753A
.text:01007497                 push    [ebp+random]
.text:0100749A                 call    InitRandom      ; 初始化随机数种子。把随机数写到10110A0中
.text:0100749F                 test    edi, edi
.text:010074A1                 pop     ecx
.text:010074A2                 jle     short loc_10074AD
.text:010074A4                 mov     ecx, edi        ; 取缓冲区长度1A0
.text:010074A6                 mov     edi, [ebp+buffer] ; 取缓冲区地址
.text:010074A9                 xor     eax, eax        ; eax清零
.text:010074AB                 rep stosd               ; 这里初始化缓冲区
.text:010074AD
.text:010074AD loc_10074AD:                            ; CODE XREF: sub_100746E+34j
.text:010074AD                 and     [ebp+var_4], 0
.text:010074B1                 cmp     dword ptr [esi+4], 0
.text:010074B5                 jle     short loc_100752D
.text:010074B7
.text:010074B7 loc_10074B7:                            ; CODE XREF: sub_100746E+BDj
.text:010074B7                 xor     edi, edi
.text:010074B9
.text:010074B9 loc_10074B9:                            ; CODE XREF: sub_100746E+B2j
.text:010074B9                 and     [ebp+random], 0
.text:010074BD
.text:010074BD loc_10074BD:                            ; CODE XREF: sub_100746E+61j
.text:010074BD                                         ; sub_100746E+ACj
.text:010074BD                 call    _rand
.text:010074C2                 cdq
.text:010074C3                 idiv    [ebp+Buffer_length] ; rand() % 68
.text:010074C6                 mov     eax, [ebp+buffer] ; 取缓冲区
.text:010074C9                 lea     eax, [eax+edx*4] ; 使用随机数得到一个随机地址
.text:010074CC                 cmp     dword ptr [eax], 0 ; 该处为零?
.text:010074CF                 jnz     short loc_10074BD ; 为零则说明该位已经设置过。不为零则重新生成一个随机数
.text:010074D1                 mov     dword ptr [eax], 1 ; 置1说明该位已经设置过
.text:010074D7                 mov     ecx, [esi+0Ch]  ; 取储存棋盘的缓冲区
.text:010074DA                 lea     eax, [edx+edx*2]
.text:010074DD                 lea     eax, [ecx+eax*4] ; 这两句为ecx+edx*0C
.text:010074DD                                         ; 每一张牌由3个dword组成,第一个dword为花色,第二个为大小,第三个为是不翻开的状态
.text:010074E0                 mov     [eax], edi      ; edi为花色。这里设置花色
.text:010074E2                 mov     ecx, [esi]      ; 获取当前级别
.text:010074E4                 cmp     ecx, 1          ; 初级?
.text:010074E7                 jnz     short loc_10074F1
.text:010074E9                 mov     dword ptr [eax], 3 ; 初级则花色都为3。即黑桃
.text:010074EF                 jmp     short loc_1007509
.text:010074F1 ; ---------------------------------------------------------------------------
.text:010074F1
.text:010074F1 loc_10074F1:                            ; CODE XREF: sub_100746E+79j
.text:010074F1                 push    2
.text:010074F3                 pop     edx
.text:010074F4                 cmp     ecx, edx
.text:010074F6                 jnz     short loc_1007509
.text:010074F8                 test    edi, edi
.text:010074FA                 jnz     short loc_1007502
.text:010074FC                 mov     dword ptr [eax], 3
.text:01007502
.text:01007502 loc_1007502:                            ; CODE XREF: sub_100746E+8Cj
.text:01007502                 cmp     dword ptr [eax], 1
.text:01007505                 jnz     short loc_1007509
.text:01007507                 mov     [eax], edx
.text:01007509
.text:01007509 loc_1007509:                            ; CODE XREF: sub_100746E+81j
.text:01007509                                         ; sub_100746E+88j ...
.text:01007509                 mov     ecx, [ebp+random]
.text:0100750C                 and     dword ptr [eax+8], 0
.text:01007510                 inc     [ebp+random]    ; 大小加1,0到C代表A到K
.text:01007513                 cmp     [ebp+random], 0Ch ; 和K比较,大于K的时候推出该循环
.text:01007517                 mov     [eax+4], ecx
.text:0100751A                 jle     short loc_10074BD
.text:0100751C                 inc     edi             ; 下一种花色
.text:0100751D                 cmp     edi, 3          ; 0到3。四种花色
.text:01007520                 jle     short loc_10074B9
.text:01007522                 inc     [ebp+var_4]
.text:01007525                 mov     eax, [ebp+var_4]
.text:01007528                 cmp     eax, [esi+4]
.text:0100752B                 jl      short loc_10074B7
.text:0100752D
.text:0100752D loc_100752D:                            ; CODE XREF: sub_100746E+47j
.text:0100752D                 push    [ebp+buffer]
.text:01007530                 call    FreeHeap
.text:01007535                 and     dword ptr [esi+10h], 0
.text:01007539                 pop     ecx
.text:0100753A
.text:0100753A loc_100753A:                            ; CODE XREF: sub_100746E+23j
.text:0100753A                 pop     edi
.text:0100753B                 pop     esi
.text:0100753C                 leave
.text:0100753D                 retn    4
.text:0100753D sub_100746E     endp


 

这里的代码用来布局棋盘。这里这个过程有一个参数。即ebp+8。这个参数是通过调用GetSystemTimeAsFileTime函数获得的一个随机数。。。这里通过这个获取到的时间来布局棋盘。。。其它代码按注释也是很好理解的。。。逆过来如下

struct pai
{
  int huase;
  int daxiao;
  BOOL fankai;
};
..........
  pai Buffer[0x68];
  p = AllocateHeap(10xA0);
  ZeroMemory(p, 0x1A0);
  for (int j = 0;j <= 3;j++)
  {
    for (int i = 0;i <= 0x0C;i++)
    {
      int r;
      do
      {
        r = rand() % l;
      } while (Buffer[r].huase != 0);
      Buffer[r].huase = j;
      if (dengji == 1)
      {
        Buffer[r].huase = 3;
      }
      if (dengji == 2)
      {
        if ((j != 0)&&(j != 1))
        {
          Buffer[r].huase = 0;
        }
        else
        {
          Buffer[r].huase = 3;
        }
      }
      Buffer[r].daxiao = i;
      Buffer[r].fankai = FALSE;
    }
  }


 

这样只要读出这里的数据就可以知道对应的地方是什么牌了。。。也可以把这里的翻开状态都改为1.。。这样所有的牌就都处于可见状态了
时间紧。。随便写几句代码演示一下效果好了。

#include <Windows.h>

int _tmain(int argc, _TCHAR* argv[])
{
  HWND hwnd = FindWindow(NULL, TEXT("蜘蛛"));
  DWORD pid;
  GetWindowThreadProcessId(hwnd, &pid);
  HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
  DWORD p;
  ReadProcessMemory(hProcess, (LPCVOID)0x01013044, &p, sizeof(p), NULL);
  ReadProcessMemory(hProcess, (LPCVOID)(p + 4), &p, sizeof(p), NULL);
  ReadProcessMemory(hProcess, (LPCVOID)(p + 0x0C), &p, sizeof(p), NULL);
  //取缓冲区地址
  for (int i = 0;i < 44;i++)
  {
    int x = 1;
    WriteProcessMemory(hProcess, (LPVOID)(p + i * 0x0C + 8), &x, sizeof(x), NULL);
    //讲翻开状态置TRUE
    ReadProcessMemory(hProcess, (LPCVOID)(p + i * 0x0C), &x, sizeof(x), NULL);
    //取花色
    switch (x)
    {
    case 0:
      printf("梅");
      break;
    case 1:
      printf("方");
      break;
    case 2:
      printf("红");
      break;
    case 3:
      printf("黑");
    }
    ReadProcessMemory(hProcess, (LPCVOID)(p + i * 0x0C + 4), &x, sizeof(x), NULL);
    if (x == 0)
    {
      printf("A");
    }
    else if (x == 0x0A)
    {
      printf("J");
    }
    else if (x == 0x0B)
    {
      printf("Q");
    }
    else if (x == 0x0C)
    {
      printf("K");
    }
    else
    {
      printf("%d", x + 1);
    }
    printf("\t");
    if ((i + 1) % 10 == 0)
    {
      printf("\n");
    }
  }
  return 0;
}



 

 

你可能感兴趣的:(c,command,user,null,Random,buffer)