论坛帮助 |
社区圈子 |
日历事件 |
2009-11-20, 21:47 | #1 | |||
|
||||
高级会员
等级: 六袋长老
|
在反编译游戏之前,我先玩了几局,感觉上不是很有意思 :< 首先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 010027和谐 |. 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 010028和谐 |. 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取窗体大小即可)
__________________
花有重开日 人无往少年 排版设计之家QQ群 44671929 方正畅流大印流程群 50016142 方正书版、飞腾、CD、PS、ID、AI、PDF等排版设计制作输出,专门解决各种疑难杂症↑↑ 我们不生产艺术,我们只是艺术的搬运工…… |
|||