• Delphi XP扫雷外挂制作.


    技术交流,DH讲解.

    本来之前就在写这个文章,还写了War3挂的教程,后来因为一些事就没有弄了.
    现在过年在家又没有事就继续把这个写完吧.哈哈.
    不要以为写个挂很容易,要想写个挂肯定要了解游戏呀.
    我们现在来了解下地雷是怎么产生的?
    其实只是我自己猜想的,毕竟这个游戏不是我写的...
    1 用户选择了多大的棋盘,多少地雷后
    棋盘应该是用一个二维数组来存储的,地雷数肯定是用一个全局变量来存储的.这点儿有异议没有?
    没有我就继续往下说了...
    2 生成地雷,肯定是随机的,那么一定会调用Rand函数咯.
    a.首先判断地雷数是否为0 ---是0->结束生成过程
          |
       不是0
          |
    b.Rand产生X,Rand产生Y                                      
          |                                                                
    c.判断二维数组中(X,Y)的值是否为有雷值---不是-->转到b
          |
         是
          |
    d.雷数减一---------->转到a
    整个流程看懂了吧.
    那么我们来实际分析呀,OD Time,从上面看我bp Rand然后进入步骤b,只要到了步骤c我们就能知道雷的内存区域了,是吧?
    我们遍历区域就知道哪些格子是雷了塞.
    下断点:image
    当断点断下来了后  我们可以看到堆栈窗口:
    image
    选中第一个,然后回车.返回:

    01003940  /$  FF15 B0110001 call    dword ptr [<&msvcrt.rand>]       ; [rand
    01003946  |.  99            cdq
    01003947  |.  F77C24 04     idiv    dword ptr [esp+4]
    0100394B  |.  8BC2          mov     eax, edx
    0100394D  \.  C2 0400       retn    4
    
    

    有点儿基础的朋友可以看出来,这里是它写了一个函数,先Rand一个数,然后整除另一个数,返回取余的结果.
    其中这2个数是什么,上面那图我们看见2个返回到,是吧?那么选中第二个回车呗.
    image
    下断,单步走:我们就会发现压入栈的是1E=29,那么应该是30个,我们看一下是行还是列.
    image
    也就是这个这个函数传入的参数是行列数,哈哈因为这个C++的,所以PUSH那一下就是在传参数.好的看具体参数:
    image
    现在可以看出来,rand函数的参数用的返回地址,$010036D2,而rand出来值再对行列数取余,也就是[esp+4]就是这个30,这样就保证,最后这个值肯定小于30咯.

    010036C7  |> /FF35 34530001 push    dword ptr [1005334]              ;  将列数压入栈,发现存列的内存地址了
    010036CD  |. |E8 6E020000   call    01003940                         ;  返回X
    010036D2  |. |FF35 38530001 push    dword ptr [1005338]              ;  将行压入栈,发现存行的内存地址
    010036D8  |. |8BF0          mov     esi, eax                         ;  esi就是X
    010036DA  |. |46            inc     esi                              ;  因为rand是0~n-1,所以这里就要+1
    010036DB  |. |E8 60020000   call    01003940                         ;  返回Y
    010036E0  |. |40            inc     eax
    010036E1  |. |8BC8          mov     ecx, eax                         ;  ecx就是Y
    010036E3  |. |C1E1 05       shl     ecx, 5                           ;  将Y*32,这样就转换成一维数组
    010036E6  |. |F68431 405300>test    byte ptr [ecx+esi+1005340], 80   ;  我们这里发现数组的基址了.$80就是有雷咯
    010036EE  |.^ 75 D7         jnz     short 010036C7
    010036F0  |. |C1E0 05       shl     eax, 5
    010036F3  |. |8D8430 405300>lea     eax, dword ptr [eax+esi+1005340]
    010036FA  |. |8008 80       or      byte ptr [eax], 80
    010036FD  |. |FF0D 30530001 dec     dword ptr [1005330]              ;  减少雷总数
    01003703  |.^\75 C2         jnz     short 010036C7
    
    

    我们知道上面这些,我们就来看看具体某一个雷,当我们插了小旗子后有什么变化没有:
    我们执行到010036E1这句时候发现:esi是$10也就是16,ecx是3,那么16-3是雷.我们让游戏运行起来.
    image
    将这个设置成有雷后,这个内存的值就是8F了.点上红旗后:
    image
    有雷且有红旗这个值就是$8E.那么要是没有雷又被点了红旗会是什么值呢?
    image
    哈哈,$0E吧.记住了吧.我们来整理下.
    1 有雷 $8F
    2 无雷 $0F
    3 红旗 $FE
    1和3进行或,2和3进行或.
    好,该Delphi了.

    procedure TForm3.Button1Click(Sender: TObject);
    var
      pid:Integer;
      h:Cardinal;
      x,y:Integer;
      readed:Cardinal;
      Count:Cardinal;
      I: Integer;
      j: Integer;
      buf:array of Byte;
    begin
      edt1.Text:='';
      edt2.Text:='';
      mmo1.Clear;
      //查找扫雷游戏的PID
      ProcessList.Snap;
      pid:=ProcessList.FindPID('winmine.exe');
      if pid = -1 then
        Exit;
      //打开进程
      h:=OpenProcess(PROCESS_ALL_ACCESS,False,pid);
      if h = 0 then
        Exit;
      try
        //取得游戏的行列数
        readed:=0;
        ReadProcessMemory(h,Pointer($1005334),@Count,4,readed);
        if readed>0 then
        begin
          edt1.Text:=IntToStr(Count);
          x:=Count;
        end;
        readed:=0;
        ReadProcessMemory(h,Pointer($1005338),@Count,4,readed);
        if readed>0 then
        begin
          edt2.Text:=IntToStr(Count);
          y:=Count;
        end;
        //获取雷数组内存区域的大小
        Count:=Y shl 5 + X;
        SetLength(buf,Count);
        //读取雷内存
         ReadProcessMemory(h,Pointer($1005340),buf,Count,readed);
        //循环判断
        for I := 1 to x  do
          for j := 1 to y  do
            if buf[32*J + I] = $8F then
            begin
              mmo1.Lines.Add(Format('雷:列:%D,行:%D',[i,j]));
              buf[32*J + I]:= $8E;
            end;
        //写回去,这样小红旗都标记出来了....
        WriteProcessMemory(h,Pointer($1005340),buf,Count,readed);
      finally
        CloseHandle(h);
      end;
    end;

    image 
    哈哈,读取出来了吧.这里只是显示.下一篇文章,我们去寻找点击的call,然后实现自动点击.嘿嘿,秒杀.

    我是DH,今天就讲这么多了.

  • 相关阅读:
    【习题 6-10 UVA
    【习题 6-9 UVA
    【习题 6-8 UVA
    【NOIP2016练习】T1 挖金矿(二分答案)
    O(n)求1-n的逆元
    【NOIP2016练习】T1 string (计数)
    【NOIP2016练习】T2 跑跑步 (数论)
    【NOIP2016练习】T3 tree (树形DP)
    【CF679B】Theseus and labyrinth(数学,贪心)
    【NOIP2016练习】T2 旅行(树形DP,换根)
  • 原文地址:https://www.cnblogs.com/huangjacky/p/1669643.html
Copyright © 2020-2023  润新知