清风原木 发表于 2009-11-18 03:05

蜘蛛纸牌也来辅助一下

记得曾在论坛里看到过有人把扫雷改成秒破版的,当时下载下来,挺有意思,想着自己有时间也改个玩玩。这下好了,到了外蒙,除了不缺时间,什么都缺,于是动手改了这个蜘蛛纸牌的游戏
       在反编译游戏之前,我先玩了几局,感觉上不是很有意思 :<
       首先OD载入,打开后,来看看它调用的API函数吧,有很多,其中BitBlt比较明显,我们首先从它入手吧。可能有人会问为什么要首先在这个函数上设断,玩了这个游戏后你会发现它有很多位图的移动过程,所以免不了要复制图片从一点到另一点,所以我觉得这个函数比较重要,如果这里实在没什么收获,可以再选择嘛,呵呵。
    BP BitBlt后
    F9运行程序来看看
01002799|.8B46 0C       mov   eax, dword ptr
0100279C|.2B46 04       sub   eax, dword ptr
0100279F|.8955 E8       mov   dword ptr , edx
010027A2|.8B55 10       mov   edx, dword ptr
010027A5|.3BD0            cmp   edx, eax
010027A7|.8955 18       mov   dword ptr , edx
010027AA|.0F8D BF000000 jge   0100286F
010027B0|>8B55 14       /mov   edx, dword ptr
010027B3|.8B5D 18       |mov   ebx, dword ptr
010027B6|.3B5A 0C       |cmp   ebx, dword ptr
010027B9|.0F8D B0000000 |jge   0100286F
010027BF|.8B55 EC       |mov   edx, dword ptr
010027C2|.8B5D 10       |mov   ebx, dword ptr
010027C5|.03D3            |add   edx, ebx
010027C7|.3955 18       |cmp   dword ptr , edx
010027CA|.7D 10         |jge   short 010027DC
010027CC|.8B55 EC       |mov   edx, dword ptr
010027CF|.6A 40         |push    40
010027D1|.58                |pop   eax
010027D2|.2BC2            |sub   eax, edx
010027D4|.8955 F8       |mov   dword ptr , edx
010027D7|.8945 F0       |mov   dword ptr , eax
010027DA|.EB 1B         |jmp   short 010027F7
010027DC|>8B55 E8      |mov   edx, dword ptr
010027DF|.8365 F0 00   |and   dword ptr , 0
010027E3|.2BC2            |sub   eax, edx
010027E5|.2B45 10       |sub   eax, dword ptr
010027E8|.8955 F8       |mov   dword ptr , edx
010027EB|.3945 18       |cmp   dword ptr , eax
010027EE|.74 07         |je      short 010027F7
010027F0|.C745 F8 40000>|mov   dword ptr , 40
010027F7|>8B5D 0C      |mov   ebx, dword ptr
010027FA|.EB 5A            |jmp   short 01002856
010027FC|>8B45 14       |/mov   eax, dword ptr
010027FF|.3B58 08      ||cmp   ebx, dword ptr
01002802|.7D 56         ||jge   short 0100285A
01002804|.8B55 0C       ||mov   edx, dword ptr
01002807|.8D0417      ||lea   eax, dword ptr
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 , edi
01002814|.2BC7             ||sub   eax, edi
01002816|.EB 17            ||jmp   short 0100282F
01002818|>8B45 E4       ||mov   eax, dword ptr
0100281B|.2BC8             ||sub   ecx, eax
0100281D|.2BCA             ||sub   ecx, edx
0100281F|.3BD9            ||cmp   ebx, ecx
01002821|.8945 FC       ||mov   dword ptr , eax
01002824|.74 07         ||je      short 0100282D
01002826|.C745 FC 3F000>||mov   dword ptr , 3F
0100282D|>33C0          ||xor   eax, eax
0100282F|>68 2000CC00   ||push    0CC0020                      ; /ROP = SRCCOPY
01002834|.FF75 F0       ||push    dword ptr              ; |YSrc
01002837|.50               ||push    eax                                    ; |XSrc
01002838|.FF75 F4       ||push    dword ptr                ; |hSrcDC
0100283B|.FF75 F8       ||push    dword ptr                ; |Height
0100283E|.FF75 FC       ||push    dword ptr                ; |Width
01002841|.FF75 18       ||push    dword ptr              ; |YDest
01002844|.53                ||push    ebx                                    ; |XDest
01002845|.FF75 08       ||push    dword ptr             ; |hDestDC
01002848|.FF15 7C100001 ||call    dword ptr [<&GDI32.BitBlt>]    ; \BitBlt
0100284E|.8B4E 08       ||mov   ecx, dword ptr
01002851|.035D FC       ||add   ebx, dword ptr
01002854|.2B0E         ||sub   ecx, dword ptr
01002856|>3BD9         | cmp   ebx, ecx
01002858|.^ 7C A2         |\jl      short 010027FC
0100285A|>8B45 F8       |mov   eax, dword ptr
0100285D|.0145 18       |add   dword ptr , eax
01002860|.8B46 0C       |mov   eax, dword ptr
01002863|.2B46 04       |sub   eax, dword ptr
01002866|.3945 18       |cmp   dword ptr , eax
01002869|.^ 0F8C 41FFFFFF \jl      010027B0
      首先是在这里被断下了,我们先来看看这个函数体,有没有什么信息,往上瞧瞧
0100271F|.53            push    ebx
01002720|.56            push    esi
01002721|.57            push    edi
01002722|.FF75 08       push    dword ptr                 ; /hDC
01002725|.FF15 74100001 call    dword ptr            /<CreateCompatibleDC>
0100272B|.68 BC120001   push    010012BC             ; /RsrcName = "FELT"
01002730|.FF35 00200101 push    dword ptr    ; |hInst = 01000000
01002736|.8945 F4       mov   dword ptr , eax         ; |
01002739|.FF15 80120001 call    dword ptr [<&USER32.LoadBitmapW>>;
0100273F|.50            push    eax                              ; /hObject
01002740|.FF75 F4       push    dword ptr                 ; |hDC
01002743|.8945 DC       mov   dword ptr , eax          ; |
01002746|.FF15 78100001 call    dword ptr [<&GDI32.SelectObject>>;
0100274C|.8B75 18       mov   esi, dword ptr
0100274F|.8B1E          mov   ebx, dword ptr
01002751|.8B4D 14       mov   ecx, dword ptr
       恩,有了新的发现,有个LoadBitmap函数,这是加载位图的函数,加载的位图名称已经显示出来了,就是那个"FELT",用RESHACKER打开程序看看,在位图资源处应该会看到这个名为"FELT"的图片,只是用我的RESHACKER没有打开,换eXeScope试试,可以看到这个图片就是程序的背景图片,还有52张扑克图片,扑克的点数图片咱就不说了,"CARDBACK"是蜘蛛的扑克背景图片,标识号106的是关于对话框的图片,标识号108的是放置扑克的图片,这里只要一一对照知道是哪些相互对应就可以了。
   可以看到"FELT"应该是程序的背景图片,这里可以大胆猜猜测一下,程序中当前循环的目的就是画出程序的背景,这里可以先放一下,把这里BitBlt的断点先取消,接着F9往下看看
010028A7|.8945 FC       mov   dword ptr , eax
010028AA|.8B45 14       mov   eax, dword ptr
010028AD|.83F8 01       cmp   eax, 1
010028B0|.56                push    esi
010028B1|.8B75 08       mov   esi, dword ptr
010028B4|.57                push    edi
010028B5|.8975 D0       mov   dword ptr , esi
//=esi
010028B8|.7C 1D         jl      short 010028D7
010028BA|.83F8 34       cmp   eax, 34
010028BD|.7F 18         jg      short 010028D7
//如果EAX<或EAX>0x34(也就是52)跳到10028D7
//如果上述不满足向下走
010028BF|.50                push    eax                              ; /<%d>
010028C0|.8D45 D4       lea   eax, dword ptr           ; |
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
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    ; |hInst = 01000000
010028F9|.FF15 80120001 call    dword ptr [<&USER32.LoadBitmapW>>;
//用LoadBitmap来读取EAX中指定的图片
010028FF|.56               push    esi                              ; /hDC
01002900|.8945 CC       mov   dword ptr , eax          ; |
01002903|.FF15 74100001 call    dword ptr [<&GDI32.CreateCompati>;
01002909|.FF75 CC       push    dword ptr                ; /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
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       ; |YDest
0100292B|.8945 C8       mov   dword ptr , eax ; |
0100292E|.53                push    ebx                     ; |XDest
0100292F|.FF75 D0       push    dword ptr       ; |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 , eax          ; |
01005CF7|.FFD6          call    esi             ; \CreateCompatibleDC
01005CF9|.68 C8120001   push    010012C8      ; /RsrcName = "CARDBACK"
//这里是CARDBACK后面有LoadBitmap函数,应该就是装载CARDBACK图片的
01005CFE|.FF35 00200101 push    dword ptr ; |hInst = 01000000
01005D04|.8945 B8       mov   dword ptr , eax          ; |
01005D07|.FF15 80120001 call    dword ptr [<&USER32.LoadBitmapW>>;
01005D0D|.50            push    eax                              ; /hObject
01005D0E|.FF75 B8       push    dword ptr                ; |hDC
01005D11|.8945 94       mov   dword ptr , eax          ; |
01005D14|.FF15 78100001 call    dword ptr [<&GDI32.SelectObject>>; \
.........................................................
01005F0E|> \8B45 C8       ||mov   eax, dword ptr
//EAX=
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 , ecx
01005F19|.68 2000CC00   ||push    0CC0020
01005F1E|.51            ||push    ecx
01005F1F|.51            ||push    ecx
01005F20|.FF75 B8       ||push    dword ptr
//不为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    ; |YDest
01005F2C|.FF75 D0       ||push    dword ptr    ; |XDest
01005F2F|.53            ||push    ebx                  ; |hDestDC
01005F30|.FF15 7C100001 ||call    dword ptr [<&GDI32.BitBlt>] ; \BitBlt
//分析知道程序根据和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       ; |
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
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 ; |hInst = 01000000
01005F77|.FF15 80120001 ||call    dword ptr [<&USER32.LoadBitmap>;
01005F7D|.50            ||push    eax                            ; /hObject
01005F7E|.FF75 A8       ||push    dword ptr              ; |hDC
01005F81|.8945 A4       ||mov   dword ptr , 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       ; |hSrcDC
01005F96|.8945 90       ||mov   dword ptr , eax ; |
01005F99|.6A 60         ||push    60                     ; |Height = 60 (96.)
01005F9B|.6A 47         ||push    47                     ; |Width = 47 (71.)
01005F9D|.FF75 CC       ||push    dword ptr       ; |YDest
01005FA0|.FF75 D0       ||push    dword ptr       ; |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 , eax
01005DF1|.0F9DC1      ||setge   cl
01005DF4|.894D A0       ||mov   dword ptr , ecx
01005DF7|.8B4D C4       ||mov   ecx, dword ptr
01005DFA|.E8 17CFFFFF   ||call    01002D16                  ;关键CALL
01005DFF|.8945 C8       ||mov   dword ptr , 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 , -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 //关键地址
01002D2C|.57            push    edi
01002D2D|.FF75 0C   push    dword ptr //扑克的行数
01002D30|.FF75 08    push    dword ptr //扑克的列数
01002D33|.E8 97510000    call    01007ECF    //关键函数1
01002D38|.8B4E 04    mov   ecx, dword ptr
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
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
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掉,可以看到那些图片都正面向上了
    最初在这里我自己改写了一下,把间距拉大了一些,好看了一些,见下图
后来分析出计算扑克点数的地址后,就没再用这种方法了,后面的方法更好用,呵呵
表示显示扑克的行数
表示显示扑克的列数
    接下来看一下 关键函数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       ①
//EAX=是扑克的列数
01007ED7|.8B0C81   mov   ecx, dword ptr    ②
//这里需要用到上个函数的关键地址了,这里指向另一个地址
//这里可以看成是一个数组在取其中的某个值
01007EDA|.85C9         test    ecx, ecx
01007EDC|.74 10      je      short 01007EEE
01007EDE|.FF75 0C   push    dword ptr //扑克的行数为内循环的次数
01007EE1|.E8 0CFFFFFF   call    01007DF2
01007EE6|.85C0          test    eax, eax
01007EE8|.74 04         je      short 01007EEE
01007EEA|.8B00          mov   eax, dword ptr     ③
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     ④
01007DF9|.33D2          xor   edx, edx
01007DFB|.3955 08      cmp   dword ptr , 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     ⑤
01007E07|.42            |inc   edx
01007E08|.3B55 08   |cmp   edx, dword ptr //注意这里是循环体
01007E0B|.^ 7C F3      \jl      short 01007E00
01007E0D|>5D            pop   ebp
01007E0E\.C2 0400    retn    4
      这里我们把几个关键的语句拿下来组在一起看吧
01007ED4|.8B45 08    mov   eax, dword ptr       ①
//EAX=扑克的列数
01007ED7|.8B0C81   mov   ecx, dword ptr    ②
//ECX=的偏移地址
01007DF7|.8B01         mov   eax, dword ptr     ④
//EAX=的偏移地址
01007E04|.8B40 08    mov   eax, dword ptr     ⑤
//EAX=EAX+8
01007EEA|.8B00         mov   eax, dword ptr     ③
//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
01007441|.8B49 0C   mov   ecx, dword ptr
01007444|.8D0440      lea   eax, dword ptr
01007447|.8B0481      mov   eax, dword ptr
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
0100745B|.8B49 0C   mov   ecx, dword ptr
0100745E|.8D0440      lea   eax, dword ptr
01007461|.8B4481 04mov   eax, dword ptr
01007465|.5D               pop   ebp
01007466\.C2 0400      retn    4
       这里的主要也是4句,分析同上即可,最后得到EAX的数值,退出关键函数3后,
01002D60|.8D4403 01   lea   eax, dword ptr
//EAX=EBX+EAX+1
      这里看一下EAX的10进制数值,呵呵,是不是感觉很熟悉,恩,这就是扑克显示的点数,和资源里设置的是一样的,可以从wsprintfw函数中观察到。
  至此,分析的应该差不多了,写个测试程序应该很简单的,于是我就写了一个测试,很奇怪的是OD调试纸牌程序的时候,测试程序好用,一旦,单独打开纸牌程序,测试程序就不好用了?恩,奇怪啊,后来发现是
01002D29|>8B4E 08       mov   ecx, dword ptr //关键地址
   这一句的问题,我取的ecx地址是动态改变的,而=是程序的固定地址,在我电脑上显示的是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      '
Dim ESI8 As Long      '
Dim ESIC As Long      '
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取窗体大小即可)

2666fff 发表于 2009-11-18 10:25

膜拜。。。

gtyre1 发表于 2009-11-18 10:27

看不太懂,膜拜

246 发表于 2009-11-18 10:30

先顶了 再慢慢的看

『棟』 发表于 2009-11-18 12:39

现在连这个都出辅助了..汗.....没什么好玩的了..

清风原木 发表于 2009-11-18 14:52

:lol今天天气真晴朗,处处百花香~

syy111 发表于 2009-11-24 11:20

看不太懂,膜拜

qq411855 发表于 2009-11-24 11:50

看不懂。。慢慢学
页: [1]
查看完整版本: 蜘蛛纸牌也来辅助一下