蜘蛛纸牌也来辅助一下
记得曾在论坛里看到过有人把扫雷改成秒破版的,当时下载下来,挺有意思,想着自己有时间也改个玩玩。这下好了,到了外蒙,除了不缺时间,什么都缺,于是动手改了这个蜘蛛纸牌的游戏在反编译游戏之前,我先玩了几局,感觉上不是很有意思 :<
首先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取窗体大小即可) 膜拜。。。 看不太懂,膜拜 先顶了 再慢慢的看 现在连这个都出辅助了..汗.....没什么好玩的了.. :lol今天天气真晴朗,处处百花香~ 看不太懂,膜拜 看不懂。。慢慢学
页:
[1]