【破解作者】 王仁军
【作者邮箱】 xgzxwrj001@163.com
【使用工具】 olldbg vc++6.0
【破解平台】 WinXP SP2
【软件名称】 游戏“XX三国”之“sanguo.exe”
【软件简介】 较早的一款游戏,每次进入游戏都要光碟,没有则退出。
【破解声明】 只是为了个人使用方便,与新手交流一下而已,高手就不必看了。
--------------------------------------------------------------------------------
【破解内容】我的小弟们受易老头的影响,拿着“XX三国”光碟去网络教室,想对杀一番,那知网络教室里的机子是没有光驱的,无法玩,
就找到了我。我也是个新手,但是想到它是几年前的东西,能难到哪里去?就斗胆一试。
一、光碟校验的破解
对光碟进行校验,一般就是找上面的文件,而这些文件有可能是游戏运行所必需的,我们可以动态跟踪程序,看看它需要哪些文件,对于
必需的文件,复制到游戏文件夹里,然后把程序动态生成的文件绝对路径改为相对路径就可以了。另外,程序中必定有检测驱动器类型的函数
GetDriveType,我们就从这个GetDriveType开始:
用OD载入,程序停在入口处,Ctrl+A分析代码后,右键菜单:查找——>所有模块间的调用,会打开“找到的模块间的调用”窗口,在其中找到下面这行:
0048663B call dword ptr [<&KERNEL32.GetDriveTypeA>]
双击这行反汇编代码来到CPU窗口,找到了整个校验call:
00486610 /$ 81EC 10020000 sub esp, 210 ;校验call入口
00486616 |. 53 push ebx
00486617 |. 55 push ebp
00486618 |. 8B2D C0216100 mov ebp, dword ptr [<&KERNEL32.GetPrivat>; kernel32.GetPrivateProfileStringA
0048661E |. 56 push esi
0048661F |. 57 push edi
00486620 |. 8BD9 mov ebx, ecx
00486622 |. C64424 14 00 mov byte ptr [esp+14], 0
00486627 |. C64424 11 3A mov byte ptr [esp+11], 3A ;":"
0048662C |. C64424 12 00 mov byte ptr [esp+12], 0
00486631 |. C64424 10 41 mov byte ptr [esp+10], 41 ;'A'=0x41,表示A盘,把它直接改成C盘:0x43
00486636 |> 8D4424 10 /lea eax, dword ptr [esp+10]
0048663A |. 50 |push eax ; /RootPathName,盘符为参数
0048663B |. FF15 24226100 |call dword ptr [<&KERNEL32.GetDriveTypeA>; \GetDriveTypeA,取EAX中驱动器类型
00486641 |. 83F8 05 |cmp eax, 5 ;返回值与5比较,5为光盘,3为硬盘,直接改成3就成硬盘版的了^_^
现在右键菜单:复制到可执行文件——>所有修改,保存文件,覆盖原文件?谁怕谁呀!如果重新载入就不用再改了。在下一行F2设断,F9运行后断在下面,先用F7一路跟下去:
00486644 |. 0F85 0C020000 |jnz 00486856 ;前面的修改使跳转未发生,后面还会提到此行的修改问题
0048664A |. 8A4424 14 |mov al, byte ptr [esp+14] ;[esp+14]中为一标志??
0048664E |. 84C0 |test al, al ;al==0
00486650 |. 75 25 |jnz short 00486677 ;未跳
00486652 |. 8D7C24 10 |lea edi, dword ptr [esp+10] ;[esp+10]中为盘符C
00486656 |. 83C9 FF |or ecx, FFFFFFFF ;ECX=-1
00486659 |. 33C0 |xor eax, eax ;EAX=0
0048665B |. 8D5424 14 |lea edx, dword ptr [esp+14]
0048665F |. F2:AE |repne scas byte ptr es:[edi]
00486661 |. F7D1 |not ecx
00486663 |. 2BF9 |sub edi, ecx
00486665 |. 8BC1 |mov eax, ecx
00486667 |. 8BF7 |mov esi, edi
00486669 |. 8BFA |mov edi, edx
0048666B |. C1E9 02 |shr ecx, 2
0048666E |. F3:A5 |rep movs dword ptr es:[edi], dword ptr [esi]
00486670 |. 8BC8 |mov ecx, eax
00486672 |. 83E1 03 |and ecx, 3
00486675 |. F3:A4 |rep movs byte ptr es:[edi], byte ptr [esi]
00486677 |> 8D7C24 10 |lea edi, dword ptr [esp+10]
0048667B |. 83C9 FF |or ecx, FFFFFFFF
0048667E |. 33C0 |xor eax, eax
00486680 |. 8D5424 18 |lea edx, dword ptr [esp+18]
00486684 |. F2:AE |repne scas byte ptr es:[edi]
00486686 |. F7D1 |not ecx
00486688 |. 2BF9 |sub edi, ecx
0048668A |. 8BC1 |mov eax, ecx
0048668C |. 8BF7 |mov esi, edi
0048668E |. 8BFA |mov edi, edx
00486690 |. 8D5424 18 |lea edx, dword ptr [esp+18]
00486694 |. C1E9 02 |shr ecx, 2
00486697 |. F3:A5 |rep movs dword ptr es:[edi], dword ptr [esi]
00486699 |. 8BC8 |mov ecx, eax
0048669B |. 33C0 |xor eax, eax
0048669D |. 83E1 03 |and ecx, 3
004866A0 |. F3:A4 |rep movs byte ptr es:[edi], byte ptr [esi] ;以上把“C:”复制了两份
004866A2 |. BF 28F86300 |mov edi, 0063F828 ;ASCII "\autorun.inf"
请注意,目标出现,要autorun.inf做什么呢?继续跟吧F7.
004866A7 |. 83C9 FF |or ecx, FFFFFFFF
004866AA |. F2:AE |repne scas byte ptr es:[edi]
004866AC |. F7D1 |not ecx ;计算出了"\autorun.inf"的长度
004866AE |. 2BF9 |sub edi, ecx ;让EDI仍指向ASCII "\autorun.inf"
004866B0 |. 8BF7 |mov esi, edi
004866B2 |. 8BFA |mov edi, edx ;EDX中为"C:"
004866B4 |. 8BD1 |mov edx, ecx ;"\autorun.inf"的长度暂存在EDX中
004866B6 |. 83C9 FF |or ecx, FFFFFFFF
004866B9 |. F2:AE |repne scas byte ptr es:[edi]
004866BB |. 8BCA |mov ecx, edx ;"\autorun.inf"的长度恢复到ECX中
004866BD |. 4F |dec edi ;EDI指向保存"C:"的下一位置
004866BE |. C1E9 02 |shr ecx, 2
004866C1 |. F3:A5 |rep movs dword ptr es:[edi], dword ptr [esi] ;连接成"C:\autorun.inf"
004866C3 |. 8BCA |mov ecx, edx ;"\autorun.inf"的长度恢复到ECX中
004866C5 |. 8D4424 18 |lea eax, dword ptr [esp+18] ;[esp+18]指向"C:\autorun.inf"
004866C9 |. 83E1 03 |and ecx, 3
004866CC |. 50 |push eax ;文件名"C:\autorun.inf"入栈
004866CD |. F3:A4 |rep movs byte ptr es:[edi], byte ptr [esi]
004866CF |. 8D8C24 2001000>|lea ecx, dword ptr [esp+120]
004866D6 |. 68 04010000 |push 104 ; size 入栈
004866DB |. 51 |push ecx ; buf 入栈
004866DC |. 68 20F86300 |push 0063F820 ; ASCII "NODISK" 默认值 入栈
004866E1 |. 68 14F86300 |push 0063F814 ; ASCII "ObjectKey" 键名 入栈
004866E6 |. 68 10F86300 |push 0063F810 ; ASCII "UI" 段名 入栈
004866EB |. FFD5 |call ebp ; kernel32.GetPrivateProfileStringA 读入字串,F8过之
到此,结合下一句知:程序找到合适的磁盘(前面的修改使它为C:盘)后,读根目录下的文件"autorun.inf",如果文件存在格式也对则会得到字串"SOFTWARE\Object Software (Beijng) Co.,Ltd.",否则为"NODISK",无碟?!
004866ED |. BE E4F76300 |mov esi, 0063F7E4 ; ASCII "SOFTWARE\Object Software (Beijng) Co.,Ltd."
004866F2 |. 8D8424 1C01000>|lea eax, dword ptr [esp+11C] ;[esp+11C]为从文件读到的字串
004866F9 |> 8A10 |/mov dl, byte ptr [eax] ;真正的校验开始了^_^,第一个地方
004866FB |. 8ACA ||mov cl, dl ;字符送入cl保存,作用见下面
004866FD |. 3A16 ||cmp dl, byte ptr [esi] ;逐个比较
004866FF |. 75 1C ||jnz short 0048671D ;不等则完蛋,你说改不改它呢?
00486701 |. 84C9 ||test cl, cl ;相等后再检查是否比较完了
00486703 |. 74 14 ||je short 00486719 ;比较成功的话去进行下一项检验
00486705 |. 8A50 01 ||mov dl, byte ptr [eax+1] ;下一字符
00486708 |. 8ACA ||mov cl, dl ;字符送入cl保存,
0048670A |. 3A56 01 ||cmp dl, byte ptr [esi+1] ;比较下一字符
0048670D |. 75 0E ||jnz short 0048671D ;不等则完蛋
0048670F |. 83C0 02 ||add eax, 2 ;字串地址加2,指向下一WORD
00486712 |. 83C6 02 ||add esi, 2 ;字串地址加2,指向下一WORD
00486715 |. 84C9 ||test cl, cl ;再检查是否比较完了
00486717 |.^ 75 E0 |\jnz short 004866F9 ;没完接着比较
00486719 |> 33C0 |xor eax, eax ;第一项校验成功,让标志EAX=0
0048671B |. EB 05 |jmp short 00486722 ;去检测第二个地方
0048671D |> 1BC0 |sbb eax, eax ;检测失败了
0048671F |. 83D8 FF |sbb eax, -1
00486722 |> 85C0 |test eax, eax ;EAX当然不为0
00486724 |. 0F85 2C010000 |jnz 00486856 ;去检查下一驱动器吧
第一处检测成功就会进行下面第二处检测,大同小异,不多说了
0048672A |. 8D4424 18 |lea eax, dword ptr [esp+18] ;第二处检测开始了
0048672E |. 8D8C24 1C01000>|lea ecx, dword ptr [esp+11C]
00486735 |. 50 |push eax
00486736 |. 68 04010000 |push 104
0048673B |. 51 |push ecx
0048673C |. 68 20F86300 |push 0063F820 ; ASCII "NODISK"
00486741 |. 68 DCF76300 |push 0063F7DC ; ASCII "BtnNum"
00486746 |. 68 10F86300 |push 0063F810 ; ASCII "UI"
0048674B |. FFD5 |call ebp
0048674D |. BE D8F76300 |mov esi, 0063F7D8 ; ASCII "11"
00486752 |. 8D8424 1C01000>|lea eax, dword ptr [esp+11C]
00486759 |> 8A10 |/mov dl, byte ptr [eax]
0048675B |. 8ACA ||mov cl, dl
0048675D |. 3A16 ||cmp dl, byte ptr [esi]
0048675F |. 75 1C ||jnz short 0048677D
00486761 |. 84C9 ||test cl, cl
00486763 |. 74 14 ||je short 00486779
00486765 |. 8A50 01 ||mov dl, byte ptr [eax+1]
00486768 |. 8ACA ||mov cl, dl
0048676A |. 3A56 01 ||cmp dl, byte ptr [esi+1]
0048676D |. 75 0E ||jnz short 0048677D
0048676F |. 83C0 02 ||add eax, 2
00486772 |. 83C6 02 ||add esi, 2
00486775 |. 84C9 ||test cl, cl
00486777 |.^ 75 E0 |\jnz short 00486759
00486779 |> 33C0 |xor eax, eax ;第二处检测完毕而且成功来到这里,EAX=0
0048677B |. EB 05 |jmp short 00486782 ;去第三处检测点
0048677D |> 1BC0 |sbb eax, eax
0048677F |. 83D8 FF |sbb eax, -1
00486782 |> 85C0 |test eax, eax
00486784 |. 0F85 CC000000 |jnz 00486856 ;不成功则去检查下一驱动器吧
修改004866FF jnz short 0048671D 为
004866FF jmp short 00486779 就跳过前面两处检测,无"C:\autorun.inf"也可以了。
去下面看看,都干了些什么。
0048678A |. 83C9 FF |or ecx, FFFFFFFF ;第三处检测开始
0048678D |. 8D7C24 10 |lea edi, dword ptr [esp+10] ;还记得吗?里面是盘符"C:"
00486791 |. F2:AE |repne scas byte ptr es:[edi] ;找EAX中的值,此时应为0
00486793 |. F7D1 |not ecx
00486795 |. 2BF9 |sub edi, ecx
00486797 |. 8D5424 18 |lea edx, dword ptr [esp+18] ;堆栈中为 (ASCII "C:\autorun.inf")
0048679B |. 8BC1 |mov eax, ecx
0048679D |. 8BF7 |mov esi, edi
0048679F |. C1E9 02 |shr ecx, 2
004867A2 |. 8BFA |mov edi, edx
004867A4 |. 8D5424 18 |lea edx, dword ptr [esp+18]
004867A8 |. F3:A5 |rep movs dword ptr es:[edi], dword ptr [esi]
004867AA |. 8BC8 |mov ecx, eax
004867AC |. 33C0 |xor eax, eax
004867AE |. 83E1 03 |and ecx, 3
004867B1 |. 68 78F46300 |push 0063F478 ; ASCII "rb" 参数入栈
又要打开什么文件了,瞧这个"rb",要读入二进制文件???
004867B6 |. F3:A4 |rep movs byte ptr es:[edi], byte ptr [esi]
004867B8 |. 83C9 FF |or ecx, FFFFFFFF
004867BB |. BF D4F76300 |mov edi, 0063F7D4
004867C0 |. F2:AE |repne scas byte ptr es:[edi]
004867C2 |. F7D1 |not ecx
004867C4 |. 2BF9 |sub edi, ecx
004867C6 |. 8BF7 |mov esi, edi
004867C8 |. 8BFA |mov edi, edx
004867CA |. 8BD1 |mov edx, ecx
004867CC |. 83C9 FF |or ecx, FFFFFFFF
004867CF |. F2:AE |repne scas byte ptr es:[edi]
004867D1 |. 8BCA |mov ecx, edx
004867D3 |. 4F |dec edi
004867D4 |. C1E9 02 |shr ecx, 2
004867D7 |. F3:A5 |rep movs dword ptr es:[edi], dword ptr [esi]
004867D9 |. 8BCA |mov ecx, edx
004867DB |. A1 84358800 |mov eax, dword ptr [883584]
004867E0 |. 83E1 03 |and ecx, 3
004867E3 |. 8D5424 1C |lea edx, dword ptr [esp+1C]
004867E7 |. F3:A4 |rep movs byte ptr es:[edi], byte ptr [esi]
004867E9 |. 8B3C85 E015640>|mov edi, dword ptr [eax*4+6415E0] ;ASCII"CHINESEPRC"
004867F0 |. 83C9 FF |or ecx, FFFFFFFF
004867F3 |. 33C0 |xor eax, eax
004867F5 |. F2:AE |repne scas byte ptr es:[edi]
004867F7 |. F7D1 |not ecx ;求出"CHINESEPRC"的总长度为11
004867F9 |. 2BF9 |sub edi, ecx
004867FB |. 8BF7 |mov esi, edi
004867FD |. 8BFA |mov edi, edx
004867FF |. 8BD1 |mov edx, ecx
00486801 |. 83C9 FF |or ecx, FFFFFFFF
00486804 |. F2:AE |repne scas byte ptr es:[edi]
00486806 |. 8BCA |mov ecx, edx
00486808 |. 4F |dec edi
00486809 |. C1E9 02 |shr ecx, 2
0048680C |. F3:A5 |rep movs dword ptr es:[edi], dword ptr [esi]
0048680E |. 8BCA |mov ecx, edx
00486810 |. 8D5424 1C |lea edx, dword ptr [esp+1C]
00486814 |. 83E1 03 |and ecx, 3
00486817 |. F3:A4 |rep movs byte ptr es:[edi], byte ptr [esi]
00486819 |. BF C8F76300 |mov edi, 0063F7C8 ;ASCII "\readme.txt" 值得关注
0048681E |. 83C9 FF |or ecx, FFFFFFFF
00486821 |. F2:AE |repne scas byte ptr es:[edi]
00486823 |. F7D1 |not ecx
00486825 |. 2BF9 |sub edi, ecx
00486827 |. 8BF7 |mov esi, edi
00486829 |. 8BFA |mov edi, edx
0048682B |. 8BD1 |mov edx, ecx
0048682D |. 83C9 FF |or ecx, FFFFFFFF
00486830 |. F2:AE |repne scas byte ptr es:[edi]
00486832 |. 8BCA |mov ecx, edx
00486834 |. 4F |dec edi
00486835 |. C1E9 02 |shr ecx, 2
00486838 |. F3:A5 |rep movs dword ptr es:[edi], dword ptr [esi] ;"C:\CHINESEPRC\readme.txt"
程序的效率也太低了吧???上面一大段只是为了得到这么一个字串"C:\CHINESEPRC\readme.txt"。嗯!下一目标出现^_^
0048683A |. 8BCA |mov ecx, edx
0048683C |. 8D4424 1C |lea eax, dword ptr [esp+1C]
00486840 |. 83E1 03 |and ecx, 3
00486843 |. 50 |push eax ;文件名"C:\CHINESEPRC\readme.txt"入栈
00486844 |. F3:A4 |rep movs byte ptr es:[edi], byte ptr [esi]
00486846 |. E8 22901600 |call 005EF86D ;应该象是 FILE *fp=fopen(filename,"rb")这样的函数调用吧?!
"C:\CHINESEPRC\readme.txt"本应在光盘上(C:是改来的),但是游戏文件夹里已经有这个"\readme.txt"了,这就好办了,只要把绝对路径改为相对路径就搞定这第三处校验,道理不用我多说了吧?。由
00486819 mov edi, 0063F7C8 ;ASCII "\readme.txt" 这一行知:从0063F7C9开始就是"readme.txt",所以改法如下:
00486819 push 0063F7C9 ;ASCII "readme.txt"
0048681E jmp short 00486846 ;后面生成绝对路径的代码就免了吧^_^,直接跳走就成相对路径了。
至此已经解决了三处,还有没有呢?用F8向下跑:
0048684B |. 83C4 08 |add esp, 8
0048684E |. 85C0 |test eax, eax ;那么EAX就是文件指针了
00486850 |. 0F85 88000000 |jnz 004868DE ;打开文件是否成功,成功就去校验文件的真假??
00486856 |> 8A4424 10 |mov al, byte ptr [esp+10] ;准备检查下一个驱动器
0048685A |. FEC0 |inc al
0048685C |. 3C 5A |cmp al, 5A ;"Z"=0x5A
0048685E |. 884424 10 |mov byte ptr [esp+10], al
00486862 |.^ 0F8E CEFDFFFF \jle 00486636 ;AL如果小于Z,检查下一个驱动器
校验失败现在不属于我,我不管了。
00486868 |. 8B83 C40A0000 mov eax, dword ptr [ebx+AC4] ;开始校验失败后的处理工作
0048686E |. 85C0 test eax, eax
00486870 |. 0F85 05010000 jnz 0048697B
00486876 |. 8A4424 14 mov al, byte ptr [esp+14]
0048687A |. 84C0 test al, al
0048687C |. 0F84 D0000000 je 00486952
00486882 |. 8D7C24 14 lea edi, dword ptr [esp+14]
00486886 |. 83C9 FF or ecx, FFFFFFFF
00486889 |. 33C0 xor eax, eax
0048688B |. 8D93 C4090000 lea edx, dword ptr [ebx+9C4]
00486891 |. F2:AE repne scas byte ptr es:[edi]
00486893 |. F7D1 not ecx
00486895 |. 2BF9 sub edi, ecx
00486897 |. 8BC1 mov eax, ecx
00486899 |. 8BF7 mov esi, edi
0048689B |. 8BFA mov edi, edx
0048689D |. C1E9 02 shr ecx, 2
004868A0 |. F3:A5 rep movs dword ptr es:[edi], dword ptr [esi]
004868A2 |. 8BC8 mov ecx, eax
004868A4 |. 33C0 xor eax, eax
004868A6 |. 83E1 03 and ecx, 3
004868A9 |. F3:A4 rep movs byte ptr es:[edi], byte ptr [esi]
004868AB |. BF D4F76300 mov edi, 0063F7D4
004868B0 |. 83C9 FF or ecx, FFFFFFFF
004868B3 |. F2:AE repne scas byte ptr es:[edi]
004868B5 |. F7D1 not ecx
004868B7 |. 2BF9 sub edi, ecx
004868B9 |. 8BF7 mov esi, edi
004868BB |. 8BD9 mov ebx, ecx
004868BD |. 8BFA mov edi, edx
004868BF |. 83C9 FF or ecx, FFFFFFFF
004868C2 |. F2:AE repne scas byte ptr es:[edi]
004868C4 |. 8BCB mov ecx, ebx
004868C6 |. 4F dec edi
004868C7 |. C1E9 02 shr ecx, 2
004868CA |. F3:A5 rep movs dword ptr es:[edi], dword ptr [esi]
004868CC |. 8BCB mov ecx, ebx
004868CE |. 83E1 03 and ecx, 3
004868D1 |. F3:A4 rep movs byte ptr es:[edi], byte ptr [esi]
004868D3 |. 5F pop edi
004868D4 |. 5E pop esi
004868D5 |. 5D pop ebp
004868D6 |. 5B pop ebx
004868D7 |. 81C4 10020000 add esp, 210
004868DD |. C3 retn ;无光碟则失败,返回。
004868DE |> 50 push eax ;文件指针入栈
004868DF |. E8 A28D1600 call 005EF686 ;此call是干什么的已经不重要了,我有那个"readme.txt"文件了啊
004868E4 |. 8B83 C40A0000 mov eax, dword ptr [ebx+AC4] ;取出“校验是否为真”的标志??
004868EA |. 83C4 04 add esp, 4
004868ED |. 85C0 test eax, eax ;检查标志
004868EF |. 75 51 jnz short 00486942 ;跳走了,去光明大道^_^
只要你有"readme.txt"文件,内容不限,那怕它是空的,都会跳走,不会执行到下面的代码
004868F1 |. 8D7C24 10 lea edi, dword ptr [esp+10] ;好象在清除什么内容??
004868F5 |. 83C9 FF or ecx, FFFFFFFF
004868F8 |. 33C0 xor eax, eax
004868FA |. 8D93 C4090000 lea edx, dword ptr [ebx+9C4]
00486900 |. F2:AE repne scas byte ptr es:[edi]
00486902 |. F7D1 not ecx
00486904 |. 2BF9 sub edi, ecx
00486906 |. 8BC1 mov eax, ecx
00486908 |. 8BF7 mov esi, edi
0048690A |. 8BFA mov edi, edx
0048690C |. C1E9 02 shr ecx, 2
0048690F |. F3:A5 rep movs dword ptr es:[edi], dword ptr [esi]
00486911 |. 8BC8 mov ecx, eax
00486913 |. 33C0 xor eax, eax
00486915 |. 83E1 03 and ecx, 3
00486918 |. F3:A4 rep movs byte ptr es:[edi], byte ptr [esi]
0048691A |. BF D4F76300 mov edi, 0063F7D4
0048691F |. 83C9 FF or ecx, FFFFFFFF
00486922 |. F2:AE repne scas byte ptr es:[edi]
00486924 |. F7D1 not ecx
00486926 |. 2BF9 sub edi, ecx
00486928 |. 8BF7 mov esi, edi
0048692A |. 8BD9 mov ebx, ecx
0048692C |. 8BFA mov edi, edx
0048692E |. 83C9 FF or ecx, FFFFFFFF
00486931 |. F2:AE repne scas byte ptr es:[edi]
00486933 |. 8BCB mov ecx, ebx
00486935 |. 4F dec edi
00486936 |. C1E9 02 shr ecx, 2
00486939 |. F3:A5 rep movs dword ptr es:[edi], dword ptr [esi]
0048693B |. 8BCB mov ecx, ebx
0048693D |. 83E1 03 and ecx, 3
00486940 |. F3:A4 rep movs byte ptr es:[edi], byte ptr [esi]
00486942 |> 5F pop edi ;准备退出,通向光明的大门^_^,直接从前面跳到这里岂不快哉?
00486943 |. 5E pop esi
00486944 |. 5D pop ebp
00486945 |. B8 01000000 mov eax, 1 ;让EAX=1,返回结果为 true
0048694A |. 5B pop ebx
0048694B |. 81C4 10020000 add esp, 210
00486951 |. C3 retn ;找到光盘的退出点
00486952 |> 8DBB C4020000 lea edi, dword ptr [ebx+2C4]
00486958 |. 83C9 FF or ecx, FFFFFFFF
0048695B |. 33C0 xor eax, eax
0048695D |. 8D93 C4090000 lea edx, dword ptr [ebx+9C4]
00486963 |. F2:AE repne scas byte ptr es:[edi]
00486965 |. F7D1 not ecx
00486967 |. 2BF9 sub edi, ecx
00486969 |. 8BC1 mov eax, ecx
0048696B |. 8BF7 mov esi, edi
0048696D |. 8BFA mov edi, edx
0048696F |. C1E9 02 shr ecx, 2
00486972 |. F3:A5 rep movs dword ptr es:[edi], dword ptr [esi]
00486974 |. 8BC8 mov ecx, eax
00486976 |. 83E1 03 and ecx, 3
00486979 |. F3:A4 rep movs byte ptr es:[edi], byte ptr [esi]
0048697B |> 5F pop edi ;准备退出
0048697C |. 5E pop esi
0048697D |. 5D pop ebp
0048697E |. 33C0 xor eax, eax ;让EAX=0,返回结果为 false
00486980 |. 5B pop ebx
00486981 |. 81C4 10020000 add esp, 210
00486987 \. C3 retn ;未找到光盘的退出点
就这三处校验,涉及两个文件,而且不是游戏必需的文件,"readme.txt"文件为空也可以,所以后来我干脆把
00486644 jnz 00486856 一行改为
00486644 jmp 00486942 保存修改,删掉那个"readme.txt"也照样可以进入游戏了,除了没有背景音乐外,其他一切正常。
二、实现背景音乐功能和作弊键功能
其实,没有背景音乐是因为背景音乐是以CD音轨的形式记录在光碟上的。未放入光碟,当然就没有背景音乐了。这通常有两种办法可以解决:跟踪、修改程序中播放音乐的相关代码,我晕倒;还有就是自己写个游戏外挂。我请出了VC6,很快稿定了音乐问题,还加了个作弊键。
先用Nero中的插件把那段CD音轨录制成"sanguo.mp3"备用。然后自己编写一段代码循环播放这个mp3就可以了,但是还要考虑一个问题:音乐最好是在玩者正式进入游戏后再播放,如果人家正在看那段动画,你的音乐响起,岂不坏人胃口!怎么办呢?嗯,想起来了,有钱好办事嘛^_^:正式进入游戏时玩家有一定数量的金钱,那么,没有正式进入游戏时玩家有金钱数吗?这个数在什么内存地址保存着?这个地址是变化的还是固定的?
进入游戏,用金山游侠搜寻一下金钱,找到了保存金钱数的内存地址,多次退出、进入游戏,都搜寻到同样的一个地址。而且,未正式进入游戏时此地址内一直为0,正式进入后则不为0。太好了,这个地址是固定的。下面就看我的代码吧:
// XX三国.cpp : Defines the entry point for the application.
//
#include "stdafx.h"
#include<mmsystem.h>
#pragma comment(lib,"winmm.lib")
/////////////////////////////////////////////////////////////////////////////
DWORD WINAPI getinfo(DWORD item,DWORD count)
{ MCI_STATUS_PARMS mcistatusparms;
mcistatusparms.dwCallback=NULL;//(DWORD)GetSafeHwnd();
mcistatusparms.dwItem=item;
mcistatusparms.dwReturn=0;
mciSendCommand(count,MCI_STATUS,MCI_STATUS_ITEM,(DWORD)&mcistatusparms);
return mcistatusparms.dwReturn;
}
int APIENTRY WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShow)
{ PROCESS_INFORMATION PI={0};
DWORD Isrun=0,M=0;
MCI_OPEN_PARMS mciopenparms;
MCI_PLAY_PARMS mciplayparms;
STARTUPINFO SI={0};
DWORD buf=0,size=0,adrr=0x1017d42;//存放金钱的地址
SI.cb=sizeof(STARTUPINFO);
SI.dwFlags=STARTF_USESHOWWINDOW;
SI.wShowWindow=SW_SHOWNORMAL;
CreateProcess(NULL,"sanguo.exe",NULL,NULL,false,0,NULL,NULL,&SI,&PI);//启动游戏程序
Sleep(20000);//等等吧,启动游戏需要时间
VirtualAllocEx(PI.hProcess,(LPVOID)adrr,sizeof(DWORD), MEM_COMMIT, PAGE_READWRITE) ;
do
{ ::GetExitCodeProcess(PI.hProcess,&Isrun);
if(Isrun!=STILL_ACTIVE)//游戏还在运行吗?
return 0;
::ReadProcessMemory(PI.hProcess,(LPCVOID)adrr,&buf,sizeof(buf),&size);
Sleep(100);
}while(!buf);//金钱数为0?
mciopenparms.lpstrElementName="sanguo.mp3";
mciopenparms.lpstrDeviceType=NULL;
mciopenparms.dwCallback=NULL;
mciSendCommand(0,MCI_OPEN,MCI_DEVTYPE_WAVEFORM_AUDIO,(DWORD)(LPVOID)&mciopenparms);
mciplayparms.dwCallback= NULL;//(DWORD)GetSafeHwnd();
mciplayparms.dwFrom=0;
mciplayparms.dwTo=getinfo(MCI_STATUS_LENGTH,mciopenparms.wDeviceID);
do //每秒检查一次mp3播放进度和游戏活动情况
{ DWORD pos=getinfo(MCI_STATUS_POSITION,mciopenparms.wDeviceID);
if(::GetAsyncKeyState(VK_HOME)<0)//按键否?
{ buf=8888;
if(++M==3)//按键时间长度因数之一
::WriteProcessMemory(PI.hProcess,(LPVOID)adrr,&buf,sizeof(buf),&size);
}
else M=0;
if(pos==mciplayparms.dwTo||pos==0)//循环播放mp3
mciSendCommand(mciopenparms.wDeviceID,MCI_PLAY,MCI_TO|MCI_FROM,(DWORD)(LPVOID)&mciplayparms);
Sleep(1000);//减轻CPU的负担;按键时间长度因数之二
::GetExitCodeProcess(PI.hProcess,&Isrun);
}while(Isrun==STILL_ACTIVE);//若游戏已经退出则结束本程序
::CloseHandle(PI.hProcess);
return 0;
}
用VC6编译后生成"XX三国.exe",把它与"sanguo.exe"、"sanguo.mp3"放在同一文件夹内,运行"XX三国.exe",游戏被启动,正式进入游戏后音乐响起,按住编辑区的"Home"键4秒,你就发发发发了。至此,所有问题全部解决,在WinXP/SP2下调试通过,收工!