最新消息:用博客记录点滴生活。与您共同关注互联网,分享互联网。

侠盗猎车手罪恶都市死机以及鼠标不能动完美解决方法

初衷

gta-vc-cpu-2VC这款游戏有很多BUG,比如在多核心处理器的计算机上很容易死机,全屏的话基本上就得重启,而且有时候鼠标不能动,必须来回按ESC/Resume才能解决(有时候RP不好就直接黑屏了)。这都是多核CPU造成的问题。解决方法倒是也容易,打开任务管理器,找到gta-vc.exe进程,右键“关系设置”,然后只勾选一个CPU(通常是CPU0),点确定即可。
可是每回进游戏都得再切出来一次调试,实在是很麻烦,下面我就讲讲如何修改gta-vc.exe,让它启动时自动设置为在CPU0上运行。
本文对读者要求较高,仅作抛砖引玉之用,看不懂过程的或者懒得看的可以直接略,后面有修改好的gta-vc.exe下载地址

修改过程

1.分析任务管理器的内部实现

首先要分析taskmgr.exe,也就是系统的任务管理器,看看他是怎么实现为进程指定CPU运行的,既然要操作进程,那么它内部肯定需要用OpenProcess函数打开进程,然后用返回的句柄操作,那么我们就用OD调试之,给OpenProcess下断(注意要暂停刷新,否则都会一直断在OpenProcess里面)下好断之后,如上操作一遍,勾选CPU0,然后点确定,这时就断在了OpenProcess里,返回程序领空,来看看关键代码。

0100BA26 /$ 55 PUSH EBP
0100BA27 |. 8BEC MOV EBP,ESP
0100BA29 |. 51 PUSH ECX
0100BA2A |. 53 PUSH EBX
0100BA2B |. 56 PUSH ESI
0100BA2C |. 57 PUSH EDI
0100BA2D |. FF75 08 PUSH DWORD PTR SS:[EBP+8] ; /ProcessId
0100BA30 |. 33DB XOR EBX,EBX ; |
0100BA32 |. 53 PUSH EBX ; |Inheritable => FALSE
0100BA33 |. 68 00060000 PUSH 600 ; |Access = SET_INFORMATION|QUERY_INFORMATION
0100BA38 |. 8BF1 MOV ESI,ECX ; |
0100BA3A |. FF15 14110001 CALL DWORD PTR DS:[<&kernel32.OpenProces>; \OpenProcess
0100BA40 |. 8BF8 MOV EDI,EAX
0100BA42 |. 85FF TEST EDI,EDI
0100BA44 |. 74 4E JE SHORT taskmgr.0100BA94
0100BA46 |. 8D45 FC LEA EAX,DWORD PTR SS:[EBP-4]
0100BA49 |. 50 PUSH EAX ; /pSystemAffinityMask
0100BA4A |. 8D45 08 LEA EAX,DWORD PTR SS:[EBP+8] ; |
0100BA4D |. 50 PUSH EAX ; |pProcessAffinityMask
0100BA4E |. 57 PUSH EDI ; |hProcess
0100BA4F |. FF15 08110001 CALL DWORD PTR DS:[<&kernel32.GetProcess>; \GetProcessAffinityMask
0100BA55 |. 85C0 TEST EAX,EAX
0100BA57 |. 74 30 JE SHORT taskmgr.0100BA89
0100BA59 |. 8D45 08 LEA EAX,DWORD PTR SS:[EBP+8]
0100BA5C |. 50 PUSH EAX ; /lParam
0100BA5D |. 68 5BB60001 PUSH taskmgr.0100B65B ; |DlgProc = taskmgr.0100B65B
0100BA62 |. FF76 04 PUSH DWORD PTR DS:[ESI+4] ; |hOwner
0100BA65 |. 6A 7C PUSH 7C ; |pTemplate = 7C
0100BA67 |. FF35 705D0101 PUSH DWORD PTR DS:[1015D70] ; |hInst = 01000000
0100BA6D |. FF15 FC130001 CALL DWORD PTR DS:[<&user32.DialogBoxPar>; \DialogBoxParamW
0100BA73 |. 83F8 01 CMP EAX,1
0100BA76 |. 75 0E JNZ SHORT taskmgr.0100BA86
0100BA78 |. FF75 08 PUSH DWORD PTR SS:[EBP+8]
0100BA7B |. 57 PUSH EDI
0100BA7C |. FF15 0C110001 CALL DWORD PTR DS:[<&kernel32.SetProcess>; kernel32.SetProcessAffinityMask
0100BA82 |. 85C0 TEST EAX,EAX
0100BA84 |. 74 03 JE SHORT taskmgr.0100BA89
0100BA86 |> 33DB XOR EBX,EBX
0100BA88 |. 43 INC EBX
0100BA89 |> 57 PUSH EDI ; /hObject
0100BA8A |. FF15 5C110001 CALL DWORD PTR DS:[<&kernel32.CloseHandl>; \CloseHandle
0100BA90 |. 85DB TEST EBX,EBX
0100BA92 |. 75 14 JNZ SHORT taskmgr.0100BAA8
0100BA94 |> FF15 68110001 CALL DWORD PTR DS:[<&kernel32.GetLastErr>; [GetLastError
0100BA9A |. 50 PUSH EAX ; /Arg3
0100BA9B |. 68 68750000 PUSH 7568 ; |Arg2 = 00007568
0100BAA0 |. FF76 04 PUSH DWORD PTR DS:[ESI+4] ; |Arg1
0100BAA3 |. E8 789AFFFF CALL taskmgr.01005520 ; \taskmgr.01005520
0100BAA8 |> 5F POP EDI
0100BAA9 |. 5E POP ESI
0100BAAA |. 8BC3 MOV EAX,EBX
0100BAAC |. 5B POP EBX
0100BAAD |. C9 LEAVE
0100BAAE \. C2 0400 RETN 4

程序逻辑很简单,先调用OpenProcess打开进程,取得句柄,然后GetProcessAffinityMask,如果失败则设置错误信息然后返回,成功的话调用DialogBoxParamW从资源创建对话框,(也就是“关系设置”窗口),并将当前的CPU信息显示上去,知道点下“确定”,DialogBoxParamW才返回。如果用户修改了原先的CPU关系设置,那么就会调用SetProcessAffinityMask设置当前进程;如果没有,则直接关闭进程句柄,函数返回。

由此我们可以知道,用GetProcessAffinityMask可以取得一个进程的CPU设置,用SetProcessAffinityMask则可以对其进行设置,我们要是自动设置gta-vc.exe在CPU0上运行的话,显然是要用到后者,上MSDN查查这个函数,上面把用法什么的写的很详细,详见:http://msdn.microsoft.com/en-us/library/ms686223%28VS.85%29.aspx

2.修改启动程序gta-vc.exe

gta-vc-cpu-3经过一系列搜索,得知只要如此调用:SetProcessAffinityMask(ProcessHandle, 1)即可把对应的进程设置为在CPU0上运行,下面就是要自己修改gta-vc.exe,让它自己执行这个函数。
先用eXeScope看一下gta-vc.exe的导入表,我找到了Direct3DCreate函数,由d3d8.dll导出,显然这个函数是跟DirectX相关的,应该是在初始化阶段被调用。下面用OD断一下,看看能不能利用这个函数。

下断,然后运行,马上就断了下来,然后回到gta-vc.exe领空,可见调用代码如下:

0065B845 > \8B8424 440400>MOV EAX,DWORD PTR SS:[ESP+444] ; 案例 0 --> 分支 0065B5DA
0065B84C . 55 PUSH EBP
0065B84D . 6A 78 PUSH 78
0065B84F . 8B08 MOV ECX,DWORD PTR DS:[EAX]
0065B851 . 890D A4977800 MOV DWORD PTR DS:[7897A4],ECX
0065B857 . E8 007D0000 CALL <JMP.&d3d8.Direct3DCreate8>
0065B85C . 33FF XOR EDI,EDI
0065B85E . A3 B4977800 MOV DWORD PTR DS:[7897B4],EAX
0065B863 . 3BC7 CMP EAX,EDI
0065B865 . 75 0C JNZ SHORT gta-vc-b.0065B873
0065B867 . 5D POP EBP
0065B868 . 5F POP EDI
0065B869 . 33C0 XOR EAX,EAX
0065B86B . 5E POP ESI
0065B86C . 81C4 30040000 ADD ESP,430
0065B872 . C3 RETN

看到了这句:

0065B857 . E8 007D0000 CALL <JMP.&d3d8.Direct3DCreate8>

看来可以修改这里,插入一小段代码调用SetProcessAffinityMask(ProcessHandle, 1),为了不影响程序原本的功能,我们往下看看,找一段空字节插入代码,然后用JMP修改程序流程就行了。往下拉了一段,果然在0065C853的地方找到一堆NOP指令,看起来够长,把调用SetProcessAffinityMask的代码加在这里就行了,然后把原本的CALL <JMP.&d3d8.Direct3DCreate8>修改成JMP跳转到我们自己加的这段代码处,执行完再JMP回去即可。
gta-vc-cpu-4
但是在此之前还有个问题需要处理,那就是得处理SetProcessAffinityMask的地址,gta-vc.exe原版并没有导入SetProcessAffinityMask函数,虽然我们也可以用OD直接改成“call SetProcessAffinityMask”,不过这样的话是用的是硬编码,可能不支持VISTA/7及以上系统,所以我们还得自己给gta-vc.exe导入这个函数。

用LordPE载入之,找到导入表,然后右键“添加导入表”,DLL是kernel32.dll,函数名即SetProcessAffinityMask,然后点“+”,记下ThunkRVA(我们后面调用要用到),确定即可。
我添加的ThunkRVA值为00614026,关掉LordPE,重新用OD载入,现在可以开始修改了。
按照上面的步骤再操作一遍,找到我们之前找好的那一段NOP(0065C853),在这里加入如下代码:

0065C853 push 1
0065C855 push -1
0065C857 call dword ptr [00A14026]

大家肯定觉得奇怪,00A14026是哪里来的?实际上这个就是我们加入的导入函数SetProcessAffinityMask的ThunkVA,前面我们知道ThunkRVA是00614026,我们在OD里用Ctrl-G呼出前往窗口,选择RVA,然后输入00614026,确定。之后就跳到了00A14026,OD就已经为我们把RVA转换成VA了,所以这个00A14026就是导入Thunk的真实地址。
调用完之后我们还得正常调用Direct3DCreate8,但是0065C853这段NOP不够用了,得先short jmp到后面的另一小段NOP:

0065C85D JMP SHORT 0065C863
……
0065C863 call 0066355C

都搞定了我们还得JMP回去,让程序正常执行:

0065C868 jmp 0065B85C

最后把原始的CALL <JMP.&d3d8.Direct3DCreate8>改成JMP 0065C853,让它执行到这里时跳转到0065C853,执行完我们自己的代码后再JMP回来。都改好了,OD主界面右键“复制到可执行文件-所有修改-全部复制”,然后再右键“保存文件”,保存成gta-vc.exe(最好提前备份原版文件)。

gta-vc-cpu-5

大功告成

至此便大功告成,运行游戏,再使用任务管理器看一看“关系设置”,会发现已经自动设置为在CPU0上运行,不用每回再手动改了,我的目的也就达到了。游戏功能没有任何影响。

改好的gta-vc.exe下载地址(把图标修改了):传送门

本文转至百度贴吧:http://tieba.baidu.com/p/1254350883

正文部分到此结束

声明:本文采用 BY-NC-SA 协议进行授权,转载还请注明:http://www.777s.me/gta-vc-cpu.html - 777's Blog