过NP的方法
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
nProte ct Ga meGua rd介绍与原理
n Prote ct Ga meGua rd 是一款比较著名的防作弊软件,在玩家使用外挂(无论任何游戏的外挂,就算不是当前游戏的外挂也不可以)的时候会提示"检测到游戏被破解修改"并强行关闭游戏。
什么是nPro tect?
nPro tect是设计用于保护个人电脑终端不被病毒和黑客程序感染的新概念的基于网络的反黑客和反病毒的工具。
他帮助确保所有输入个人电脑终端的信息在网络上不落入黑客手中。
在最终用户在执行电子贸易时,可以通过将nP rotec t配置在那些提供电子商务、进口贸易,电子贸易的金融机构的网站上,来提高安全等级。
nPro tect怎样工作?
nProt ect是一种基于服务器端的解决方案并且当那些需要保护的任何网络应用被运行时而自动启动。
nP rotec t被载入内存,所以最终用户不需要安装任何应用程序,只要nPr otect启动,就开始拒绝黑客工具和病毒的入侵!
nProt ect如何工作?
用户登陆时n Prote ct自动启动。
浏览器确认和自动安装安全模块到用户的个人电脑。
扫描黑客工具和病毒
通知用户目前的安全状态
如果有黑客工具和病毒尝试删除
在被入侵时端驻留内存来锁定黑客工具直到电脑或者nP rotec t关闭。
nProt ect G ameGu ard 的主要功能介紹:
实时侦测并封锁修改游戏之黑客程序。
实时侦测并封锁各类型系统病毒。
实时侦测并封锁加速程序。
实时侦测并封锁自动鼠标(连点)程序。
封锁不当外挂程序。
封锁各种意图远程控制玩家个人计算机的动作。
限制意图侧录键盘鼠标动作的恶性程序。
限制可疑间谍程序,加强安全性。
使用此软件的网络游戏还有很多,比如:冒险岛国际服,信长野望onlin e,希
望o nline等等
再来谈谈新版本的冒险岛外挂问题:
目前在服务器上能屏蔽掉的一般都是高速战斗,即两次战斗的数据传输时间间隔少于游戏设定的最少时间(比如说最快的攻击是用小刀砍一下,假设这个时间是2秒,而某ip长时间以1秒传送一次攻击指令(既含有攻击指令的封包),可以强制让此ip断线,但并不能做为封号的证据)高速采集原理也差不多,还有瞬移等。
什么是封包?客户端和服务器之间往来的数据就是封包
有学过计算机的因该都知道计算机网络被OS I参考模型分为7层:
第1层:物理层;传输单位是比特流,既b it
第2层:数据链路层;传输单位是帧,既fram e
第3层:网络层;这一层传输的就是包了,既pac ket
第4层:传输层;传输单位是段,既segm ent
第5层:会话层;建立连接并保持连接畅通
第6层:表示层;将信息"表示"为一种格式,可以理解为就是"格式转换"
第7层:应用层。
对软件提供接口以使程序能使用网络服务
数据在网络中传输不是一整大段的传输的,而是分成小块传输的,由于比较分散,所以封包。
大家可以理解为"搬家的时候把东西打成包好搬运"。
目前的外挂都是内嵌于游戏中,对游戏所接收和发送的封包进行截取,修改,重构建等。
话说回来,nPr otect Game Guard的确很厉害,从冒险岛更新它又来到我的硬盘上以后,我的Zone Alarm防火墙就开始报警了,说它要监视你的进程,并且欲访问网络,唉,没办法,为了玩冒险,只好放行咯。
试图访问inte rnet
监视冒险岛进程
切出游戏后,你会发现右下角多了个这东西,翻译过来就是"nPr otect游戏监视程序版本624"
什么是Z oneAl arm?Z oneAl arm是世界著名的防火墙之一,它的强大只有用过才知道
顺便说一句,世界顶级防火墙是 Loo k n S top ,可惜在中文系统上使用时问题比较多,我就跟它88了(广告先止住,免得有人扔砖)
值此,我想大家觉得冒险岛纯净时代再次来临了吧?
不过很可惜,答案是否定的。
很多人说nPr otect Game Guard很厉害,但是我要告诉大家:中国人更厉害!!
破解n Prote ct:
n Prote ct Ga meGua rd在启动后使用Se tWind owsHo okEx(Injec t DLL)方式进入所有的进程,并且在
OpenP roces s()
R eadPr ocess Memor y()
W riteP roces sMemo ry()
PostM essag e()<。
。
等等函数的头部加入JM P XXX XXX的代码跳入监测程序进行监测,如发现对游戏进行操作便拦截该操作,所以以上函数均无法正常工作。
于是,就避免了外挂问题。
解决方案:
1.运行时将要使用的动态连接库(如:user3
2.dll kern el32.dll等)复制后改名,使用Lo adLib rary和GetPr ocAdd ress载入函数。
要使用Lo adLib rary和GetPr ocAdd ress载入函数,你需要有Vi sualStudi o .NE T,或者其中的Vis ual B asic和Visua l C++,当然,你还要会使用他们,而且你还要知道nProt ect G ameGu ard运行时到底调用了哪些动态连接库(这时候大家就八仙过海各
显神通吧,想尽一切办法,如果你搞错了的话。
。
后果自己负责
咯。
。
。
)
2.恢复JMP XXXX XX处的代码
需要很高的技术并有很大的危险性,而且不知道会不会再被改。
。
一但失败,后果自己负责咯。
。
3.如果nP rotec t Gam eGuar d非正常关闭(看大家本事了),JMP X XXXXX处的代码不会被恢复而监测程序代码却已经被卸载,这时候如果被H ook程序调用函
数。
。
后果自己负责咯。
。
反正失败后后果都好不到哪里去,除了电脑爆炸之外,大家就自己去想吧,所以,小心,小心,再小心!
跳过n Prote ct:
首先要清楚,nProt ect通过连接其更新服务器获得当前最新文件内容,然后与本地文件作比较,如发现服务器端的文件与本地的不一致,则从更新服务器重新下载文件更新本地的nPro tect文件。
如果n Prote ct更新成功,而新版nProt ect又拦截外挂,那么理所当然地nPro tect每更新一次外挂就失效一次了。
通过分析游戏客户端用于解析该游戏各程序与其对应远端连接的IP列表文件,找出nPro tect更新服务器的地址,并分析出nPr otect官方更新服务器上的目录文件结构。
目录文件结构一般为: "更新服务器的名称[ur l=fil e://\\Game Guard]\\Ga meGua rd[/u rl]"先自己构建一台模拟n Prote ct更新服务器,服务器上目录文件结构与官方的相同,更新下载文件内容使用旧版nPr otect的内容(旧的客户端先别忙着删除。
。
)
将真实nProt ect更新服务器的地址,解析到你构建的模拟nPro tect更新服务器的IP地址.
例: 127.0.0.1gg.much ina.c om
写入到 sys tem32\\dri vers\\etc的 hos t 文件中
这个h ost 文件为系统T CP/IP协议配置I P解析服务, 没有后缀名,可用记事本或U E32打开编辑。
通常一个网游的顺利运行,是要连接服务器端多个IP的("nPro tect服务","连接服务","数据服务","登陆服务","主服务"。
。
)
而这一系列的服务都是由一个游戏主程序的启动运行来完成(如"命运"的
"WYD Lauch er.ex e","奇迹"的"M ain.e xe","冒险岛"的"Mapl eStor y.exe")
由于host文件已被修改过,其中n Prote ct更新的连接IP被解析为指向自己模拟的更新服务器,而模拟服务器上的"更新文件"是旧版本的,所以nP rotec t不但不会被更新为新版,反而会版本倒退。
并且往后都不会再更新。
。
另一个方法,则是直接修改游戏主程序代码内容,就是暴力修改了。
由于nPr otect的功能是附在游戏主程序的开头独立执行的,所以,通过对游戏主程序进行破解处理后,将游戏主程序关于nPr otect的执行部分的代码打上无效化标记,就很自然地跳过了n Prote ct了。
补充一下,构建模拟nProt ect更新服务器可用本地机器完成(nPr otect更新
IP:127.0.0.1有的安全软件会屏蔽掉此个本地自连接IP);用VM虚拟机完成(VM虚
拟的多系统可各自设置不同IP);或者。
。
另外组一台机器来做也
可。
。
还有,脱壳的方法比较不实际,因为一个当前网游的完整客户端主程序的脱壳工作是很繁重的,须要多C PU服务器级的技能才能比较有效率的完成(据程序外壳加密方式而定, 双P4 1.8G/1GRAM的系统跑MU97d的ma in.ex e脱壳,半天左右
吧。
。
), 脱壳后也不是人人都有能力对程序作适当修改,高级C语言编程能力的要求是跑不掉的。
这个还是太难了点。
。
一、NP用户层监视原理
NP启动后通过Wri tePro cessM emory跟Crea teRem oteTh read向所有进程注入代码(除了系统进程smss.exe),代码通过n p自己的L oadLi brary向目标进程加载
npg gNT.d es。
np ggNT.des一旦加载就马上开始干"坏事",挂钩(HOOK)系统关键函数如Op enPro cess,ReadP roces sMemo ry,Wr itePr ocess Memor y,Pos tMess age等等。
挂钩方法是通过改写系统函数头,在函数开始JMP到npggN T.des中的替换函数。
用户调用相应的系统函数时,会首先进入到npgg NT.de s模块等待NP的检查,如果发现是想对其保护的游戏进行不轨操作的话,就进行拦截,否则就调用原来的系统函数,让用户继续。
下面是NP启动前use r32.d ll中的P ostMe ssage A的源代码(NP版本900,X P sp2)
8BF F MOV EDI,EDI
55 PUS H EBP
8BEC MOVEBP,E SP
56 PUSH ESI
57 PU SH ED I
8B7D 0CMOV E DI,DW ORD P TR SS:[EBP+C]
8BC7 M OV EA X,EDI
2D 45010000 SU B EAX,145
74 42 JE S HORTUSER32.77D1CBDA
83E8 48 S UB EA X,48
74 3D JE S HORTUSER32.77D1CBDA
2D A6000000 SU B EAX,0A6
0F84D4530200 J E USE R32.77D41F7C
8B45 10 MOVEAX,D WORDPTR S S:[EB P+10]
8B0D 8000D777MOV E CX,DW ORD P TR DS:[77D70080]
F641 0204 TE ST BY TE PT R DS:[ECX+2],4
0F8503540200 J NZ US ER32.77D41FBE
8D45 10 LEA EAX,DWORD PTRSS:[E BP+10]
50PUSHEAX
57 PUS H EDI
E8 F BFEFF FF CA LL US ER32.77D1C AC0
F F75 14 PUS H DWO RD PT R SS:[EBP+14]
F F75 10 PUS H DWO RD PT R SS:[EBP+10]
57 PUS H EDI
FF75 08 P USH D WORDPTR S S:[EB P+8]
E8 AC BFFFF F CAL L USE R32.77D18B80
5F POPEDI
5E POP ESI
5D PO P EBP
C2 1000 R ETN 10
而下面是NP启动后use r32.d ll中的P ostMe ssage A的源代码(NP版本900,X P sp2)E9A69AB8CD J MP np ggNT.458A6630
56 PUS H ESI
57 P USH E DI
8B7D 0C MOVEDI,D WORDPTR S S:[EB P+C]
8BC7MOV E AX,ED I
2D45010000 S UB EA X,145
74 42 JESHORT USER32.77D1CBD A
83E8 48SUB E AX,48
74 3D JESHORT USER32.77D1CBD A
2DA6000000 S UB EA X,0A6
0F84 D4530200JE US ER32.77D41F7C
8B45 10 MOV EAX,DWORD PTRSS:[E BP+10]
8B0D 8000D777 MOVECX,D WORDPTR D S:[77D70080]
F641 02 04 T EST B YTE P TR DS:[ECX+2],4
0F85 03540200JNZ U SER32.77D41FBE
8D4510 LE A EAX,DWOR D PTR SS:[EBP+10]
50 PUSH EAX
57 PU SH ED I
E8FBFEF FFF C ALL U SER32.77D1CAC0
FF7514 PU SH DW ORD P TR SS:[EBP+14]
FF7510 PU SH DW ORD P TR SS:[EBP+10]
57 PU SH ED I
FF75 08PUSHDWORD PTRSS:[E BP+8]
E8 A CBFFF FF CA LL US ER32.77D18B80
5F POP EDI
5E PO P ESI
5D P OP EB P
C21000RETN10
通过对比我们可以发现,N P把Pos tMess ageA函数头原来的8BFF558BEC五个字节改为了E9A69AB8CD,即将MOV E DI,ED I PUS H EBP
MOVEBP,E SP 三条指令改为了JMP n pggNT.458A6630。
所以用户一旦调用
Po stMes sageA的话,就会跳转到np ggNT.des中的458A6630中去。
二、用户层反NP监视方法
1,把被N P修改了的函数头改回去
上面知道NP是通过在关键系统函数头写了一个JM P来进行挂钩的,因此,在理
论上我们可以通过把函数头写回去来进行调用。
在实际操作的时候,这种方法并不理想。
因为n pggNT.des也挂钩了把函数头改写回去的所有函数,还有它的监视线程也会进行检校判断它挂钩了的函数是不是被修改回去。
因此实现起来很困难,随时都会死程序。
2,构建自己的系统函数(感谢JTR提供)
这种方法适用于代码比较简单的系统函数。
下面我们看看k eybd_event的函数源码
8BFF MOVEDI,E DI ;USER32.key bd_ev ent
55 PUS H EBP
8BEC MOVEBP,E SP
83EC 1C SUBESP,1C
8B4D 10MOV E CX,DW ORD P TR SS:[EBP+10]
8365F0 00 ANDDWORD PTRSS:[E BP-10],0
894D E C MOV DWOR D PTR SS:[EBP-14],EC X
66:0FB64D 08MOVZX CX,B YTE P TR SS:[EBP+8]
66:894D E8MOV W ORD P TR SS:[EBP-18],CX
66:0FB64D 0C MOVZ X CX,BYTEPTR S S:[EB P+C]
66:894D EA MOVWORDPTR S S:[EB P-16],CX
8B4D 14 MOV ECX,DWORD PTRSS:[E BP+14]
894D F4MOV D WORDPTR S S:[EB P-C],ECX
6A 1CPUSH1C
33C0 XO R EAX,EAX
8D4DE4 LE A ECX,DWOR D PTR SS:[EBP-1C]
40 INCEAX
51 PUS H ECX
50 P USH E AX
8945 E4 MOVDWORD PTRSS:[E BP-1C],EAX
E8 9B8DFC FF CA LL US ER32.SendI nput
C9 LE AVE
C2 1000 RET N 10
由上面我们看到key bd_ev ent进行了一些参数的处理最后还是调用了user32.dll中的Sen dInpu t函数。
而下面是Se ndInp ut的源代码
B8F6110000 M OV EA X,11F6
BA0003F E7F M OV ED X,7FF E0300
FF12 CALL DWOR D PTR DS:[EDX]; ntd ll.Ki FastS ystem Call
C2 0C00 RE TN 0C
Send Input代码比较简单吧?我们发现Sen dInpu t最终是调用了ntd ll.dl l中的
Ki FastS ystem Call函数,我们再跟下去,K iFast Syste mCall就是这个样子了
8B D4 MO V EDX,ESP
0F34SYSEN TER
最终就是进入了SYSE NTER。
通过上面的代码我们发现一个k eybd_event函数构建并不复杂因此我们完全可以把上面的代码COP Y到自己的程序,用来替代原来的keybd_even t。
NP启动后依然会拦截原来的那个,但已经没关系啦,因为我们不需要用原来那个ke ybd_e vent了。
这种方法适用于源代码比较简单的系统函数,复杂的话实现起来就比较麻烦了。
我是没有信心去重新构建一个PostM essag eA,因为其中涉及到N个jmp和Call,看起来头都大。
还有在VC6里嵌入汇编经常死VC(这种事太烦人了),我想不会是我用了盗版的原因吧?
3,进入ring0(感谢风景的驱动鼠标键盘模拟工具)
由上面可以看到,NP用户层的监视不过是修改了一下系统的函数头,进行挂钩监视。
因此,要反NP用户层监视的话,进入ri ng0的话很多问题就可以解决了。
比
如Wi nIO在驱动层进行键盘模拟,n pggNT.des是拦截不到的。
但是由于NP用了特征码技术,再加上Wi nIO名气太大了,所以WinI O在NP版本8××以后都不能用了。
但是如果熟悉驱动开发的话,自己写一个也不是很困难的事。
说了那么多看起来很"高深"的东西,现在说一些象我这样的菜鸟都能明白的东西,呵呵,因为这是菜鸟想出来的菜办法。
4,断线程
我们知道NP是通过Creat eRemo teThr ead在目标进程创建远程线程的,还有一点,很重要的一点就是:NP向目标进程调用了Creat eRemo teThr ead后就什么都不管了,也就是说,凭本事可以对除游戏外的所有进程npg gNT.d es模块进行任何"处置"。
这样我们可以用一个很简单的方法就是检查自己的线程,发现多余的话(没特别的事情就是NP远程创建的)就马上结束了它,这样NP就无法注入了。
但是由
于w indow s系统是多任务系统,而Crea teRem oteTh read的执行时间又极短,要在这么短的时间内发现并结束它的话是一件很困难的事。
一旦Crea teRem oteTh read执行完毕而我们的监视线程还没有起作用的话,后果就惨重了,npg gNT.d es马上把程序"搞死"。
因为我们一直试图关闭它的线程,而np ggNT.des又拦截了
Ter minat eThre ad,所以我们就只能不断地"重复重复再重复"去试图关闭npg gNT.d es 的监视线程。
如果我们很幸运地在其执行注入代码时就能断了它地线程地话,
npgg NT.de s就无法注入了。
这种方法在NP早期版本大概有百分之五十的成功率,现在能有百分之一的成功率都不错了。
5,断线程之线程陷阱
我知道"线程陷阱"这个词肯定不是我首创,但用"陷阱"这种方法来对付N P之前在网上是找不到的。
为什么要叫"线程陷阱"?因为这确确实实是一个陷阱,在
np ggNT.des肯定要经过的地方设置一个"陷阱",等它来到之后,掉进去自动就死掉了。
而搭建陷阱的方法简单得令你难以相信。
上面我们从npgg NT.de s的监视原理可以看到,npgg NT.de s要来挂钩(HOOK)我们
的系统函数,这种的方法我们也会,是不是?哪想想,这种挂钩方法需要用到哪些系统函数呢?打开进程OpenP roces s或Get Curre ntPro cess(因为npg gNT.d es已经进入了目标进程,所以没有必要再调用Open Proce ss,肯定是用后者)、找模块地址
GetM odelH andle、找函数地址GetP rocAd dress、改写函数头的内存属性
Virt ualQu ery&V irtua lProt ect、写内存Wri tePro cessM emory。
嘿嘿,在这些地方设置陷阱就八九不离十了,肯定是n pggNT.des干那坏勾当要经过的地方。
怎么设陷阱呢?选一个上面说的函数(我没有一一尝试),先自己挂钩(嘿嘿,NP会我们也会)。
等到有人调用的时候,先判断当前的的线程是不是我们程序的,不是的话,那就断了它吧(一个Ex itThr ead就可以了)。
大概就像下面这个样子
HANDL E WIN API M yGetC urren tProc ess(V OID)//替换掉原来的Get Curre ntPro cess{
DWO RD dw Threa dId=G etCur rentT hread Id();//得到当前线程ID
if(!IsMyT hread(dwTh readI d)){//不是我们要保护的线程
Exi tThre ad(0);//断了它吧
}
Unhoo kGetC urren tProc ess(); //是我们要保护的线程调用就恢复函数头
HAN DLE h Proce ss=Ge tCurr entPr ocess();//让它调用
Rehoo kGetC urren tProc ess();//重新挂钩
re turnhProc ess;//返回调用结果
}
这种方法去掉npg gNT.d es的监视是完全能够实现的,但是这个函数
IsMyT hread(dwTh readI d)非常关键,要考虑周全,不然断错线程的话,就"自杀"了。
6,更简单的陷阱
原理跟上面一样,但是我们将替换函数写成这个样子
HA NDLEWINAP I MyG etCur rentP roces s(VOI D)//替换掉原来的GetCu rrent Proce ss {
HMODL E hMo d=Get Model Handl e("np ggNT.des");
if(hMod!=NULL){
Fr eeLib rary(hMod); //直接Free掉它
}
Unhoo kGetC urren tProc ess(); //是我们要保护的线程调用就恢复函数头
HAN DLE h Proce ss=Ge tCurr entPr ocess();//让它调用
Rehoo kGetC urren tProc ess();//重新挂钩
re turnhProc ess;//返回调用结果
}
这种方法就万无一失了,不用担心会"自杀"。
三、总结
由上面可以看到在用户层上反NP监视是不是很简单的事?最简单有效的就是第六种方法,短短的几行代码就可以搞定了。
但是不要指望去掉了npgg NT.de s就可以为所欲为了,还有NP还在驱动层做了很多手脚,比如Wr itePr ocess Memor y在用户层用没问题,但是过不了NP的驱动检查,对游戏完全没效果。
要去掉NP的注入是很容易的事,但是去掉npg gNT.d es并不是说我们想对游戏怎么样都可以了,NP还挂钩了很多内核函数,所以很多关键系
统函数就算我们在用户层能用也对游戏没有什么效果。
如果我们想在不破解NP前提下读写游戏内存该怎么办呢,我想办法至少有两个一、用驱动
在驱动下读写游戏内存是没问题,但是由于我不懂驱动,所以也没什么可说。
二、进入游戏进程
在用户层,如果我们想在不破解N P的前提下读写游戏内存的话,大概就只能进入游戏进程了。
因为很简单,我们的程序无法对游戏使用OpenP roces s、
Rea dProc essMe moery及
Wri tePro cessM emory这些函数(就算是去掉了NP监视模块npg gNT.d es),而NP 又不可能限制游戏自身使用这些函数,所以只要我们能够进入游戏进程就能够读写游戏的内存。
怎么
进入游戏呢?下面介绍两种方法:
1,最简单的办法—全局消息钩子(WH_GE TMESS AGE)
看似很复杂的东西原来很简单就可以实现,大道至易啊。
使用消息钩子进入游戏进程无疑是最简单的一种方法,具体编程大概象这样:一个消息钩子的D LL,里面包含一个消
息回调函数(什么都不用做),读写内存过程,跟主程序通讯过程或操作界面过程,当然在DLL_PROCE SS_AT TACH要判断当前的进程是不是游戏的,是的话就做相应的处理;一个安
装全局消息钩子的主程序。
大概这样就可以了。
使用全局消息钩子的好处是简单易用,但是不足之处是要在游戏完全启动(NP当然也启动啦)后才能进入,如果想在NP启动前做一
些什么事的话是不可能的。
另外也简单介绍一下防全局钩子的办法,Wind ows是通过调用Lo adLib raryE xW来向目标进程注入钩子DLL的,所以只要我们在钩子安装前挂钩了这个函数,全局钩子就干扰不了
了。
2,更麻烦的办法—远程注入
知道远程注入方法和原理的人可能会说“有没有搞错,Op enPro cess、
Write Proce ssMem ory这些必备函数都不能用,怎么注入?”,当然啦,NP启动后是不能干这些事情,所
以我们要在NP启动前完成。
这样一来,时机就很重要了。
游戏启动的流程大概是这样:游戏Main->Gam eGuar d.des->Gam eMon.des(N P进程)。
这里的做法是这样:游戏Main->Gam eGuar d.des(暂停)->注入DL L
->Ga meGua rd.de s(继
续)->Ga meMon.des。
关键点就是让Game Guard.des暂停,有什么办法?我想到一个是全局消息钩子(还是少不了它啊)。
要实现大概需要做下面的工作:一个全局消息钩子DLL,里面只
要一个消息回调函数(什么都不用做),DL L_PRO CESS_ATTAC H下进行当前进程判断找Game Guard.des,找到的话就向主程序S endMe ssage;主程序,负责安装钩子,接收钩子DLL发来的
消息,接收到消息就开始查找游戏进程,向游戏进程注入内存操作DLL,返回
给S endMe ssage让Game Guard.des继续,卸载钩子(免得它继续钩来钩去);内存操作DLL,负责对游戏
内存进行操作。
具体编写如下(有省略):
////////////////////////////////////////////////Ga meHoo k.cpp////////////////////////////////////////////////////////// ////////
BOO L IsG ameGu ard();
//////////////////////////////////
LRE SULTCALLB ACK G etMsg Proc(int n Code,WPARA M wPa ram,L PARAM lPar am) {
r eturn (Cal lNext HookE x(m_h Hook,nCode,wPar am,lP aram));//什么都不需要做
}
///////////////////////////////////////
B OOL W INAPI DllM ain(H INSTA NCE h Inst,DWORD dwRe ason,LPVOI D lp)
{
swit ch(dw Reaso n){
cas e DLL_PROC ESS_A TTACH:
if(IsGa meGua rd())//判断当前进程是不是Game Guard.des
Se ndMes sage(m_hwn dRecv,WM_H OOK_I N_GAM EGUAR D,NUL L,NUL L);//向主窗体发送消息,S endMe ssage是等待接受窗体处理完毕才返回的,
bre ak; //所以进程就暂停在这里,我们有足够的时间去做事情
caseDLL_P ROCES S_DET ACH:
break;
}
retur n TRU E;
}
///////////////////////////////////
GAME HOOKA PI BO OL Se tGame Hook(BOOLfInst all,H WND h wnd)
{
...
}
////////////////////////////////////////
BOO L IsG ameGu ard()
{
TCHA R s zFile Name[256];
G etMod uleFi leNam e(NUL L,szF ileNa me,256);
if(strst r(szF ileNa me,"G ameGu ard.d es")!=NULL){//这样的判断严格来说是有问题的,但实际操作也够用了。
当然也可以进行更严格的判断,不过麻烦点
retur n TRU E;
}
retu rn FA LSE;
}
//////////////////////////////////////////////////////Main/////////////////////////////////////////////////////////////////// /////
vo id On GameG uard(WPARA M wPa ram,L PARAM lPar am)//处理消息钩子DLL发来的消息就是上面Se ndMes sage的那个
{
DWORD dwPr ocess Id=Fi ndGam eProc ess(m_strG ameNa me);//开始查找游戏进程 if(dwPr ocess Id==0){
Me ssage Box(m_hWnd,"没有找到游戏进程","查找游戏进程",MB_O K);
r eturn;
}
i f(!In jectD ll(dw Proce ssId)){//查找到就开始注入
Me ssage Box(m_hWnd,"向游戏进程注入失败",注入",MB_OK);
retur n;
}
}
/////////////////////////////////////////////////
DWO RD Fi ndGam eProc ess(L PCSTR szGa meNam e)//负责查找游戏进程
{
HA NDLEhSnap shot=Creat eTool help32Snap shot(TH32C S_SNA PPROC ESS,0);
if(h Snaps hot==INVAL ID_HA NDLE_VALUE)
ret urn 0;
PROCE SSENT RY32pe={s izeof(pe)};
DWORD dwPr ocess ID=0;
f or(BO OL
fO K=Pro cess32Firs t(hSn apsho t,&pe);fOK;fOK=Proce ss32N ext(h Snaps hot,&pe)){
if(l strcm pi(sz GameN ame,p e.szE xeFil e)==0){
dwPr ocess ID=pe.th32Proce ssID;
b reak;
}
}
Clos eHand le(hS napsh ot);
re turndwPro cessI D;
}
/////////////////////////////////////////////////
BOOLInjec tDll(DWORD dwPr ocess Id)//负责注入,参考自Je ffrey Rich ter《w indow s 核心编程》
{
CSt ringstrTe xt;
cha r* sz LibFi leRem ote=N ULL;
HA NDLE
hProc ess=O penPr ocess(PROC ESS_C REATE_THRE AD|PR OCESS_VM_O PERAT ION|PROCES S_VM_WRITE,FALS E,dwP roces sId);
i f(hPr ocess==NUL L){
// Set Recor d("Op en ga me pr ocess fail ed!");
retur n FAL SE;
}
int cch=lstrl en(sz Dll)+1;
intcb=cc h*siz eof(c har);
s zLibF ileRe mote=(char*)Vir tualA llocE x(hPr ocess,NULL,cb,M EM_CO MMIT,PAGE_ READW RITE);
if(sz LibFi leRem ote==NULL){
// SetRe cord("Allo c mem ory t o gam e pro cessfaile d!");
Clos eHand le(hP roces s);
r eturn FALS E;
}
if(!Write Proce ssMem ory(h Proce ss,(L PVOID)szLi bFile Remot e,(LP VOID)szDll,cb,N UL L)){
// SetRe cord("Writ e gam e pro cessmemor y fai led!");
Cl oseHa ndle(hProc ess);
retu rn FA LSE;
}
PT HREAD_STAR T_ROU TINEpfnTh readR tn=(P THREA D_STA RT_RO UTINE) Get ProcA ddres s(Get Modul eHand le(TE XT("k ernel32")),"Loa dLibr aryA");
if(p fnThr eadRt n==NU LL){
// Se tReco rd("A llocmemor y togameproce ss fa iled!");
C loseH andle(hPro cess);
ret urn F ALSE;
}
H ANDLE hThr ead=C reate Remot eThre ad(hP roces s,NUL L,0,p fnThr eadRt n,
sz LibFi leRem ote,0,NULL);
if(!hThre ad)
{
// Set Recor d("Cr eateremot e thr ead f ailed!");
Close Handl e(hPr ocess);
retu rn FA LSE;
}
if(hThre ad!=N ULL)
Close Handl e(hTh read);
Clo seHan dle(h Proce ss);
retur n TRU E;
}
///////////////////////////操作游戏内存的DLL就不贴了,大家根据不同的需要各显神通吧///////////////////////////////////////////////////
这种方法比一个全局消息钩子麻烦一点,但是优点是显然易见的:可以在NP启动前做事情,比如HO OK游戏函数或做游戏内存补丁。
下面进入N P进程还要用到这种方法。
三、进入NP进程
如果我们对NP有足够的了解,想对它内存补丁一下,来做一些事情,哪又怎样才可以进入NP的进程呢?嗯,我们知道游戏启动流程是这样的游戏Mai n-
>Ga meGua rd.de s-
>G ameMo n.des(NP进程),其中G ameGu ard.d es跟Ga meMon.des进程是游戏M ain 通过调用函数C reate Proce ssA来创建的,上面我们说到有办法在NP进程
(Ga meMon.des)启动前将我们的
DL L注入到游戏进程里,因此我们可以在Gam eMon.des启动前挂钩
(H OOK)C reate Proce ssA,游戏创建NP进程时让N P暂停,但是游戏本来创建NP进程时就是让它先暂停的,这步我们
可以省了。
下面是游戏启动NP(版本900)时传递的参数
App licat ionNa me:C:\惊天动地Cabal Onli ne\Ga meGua rd\Ga meMon.des
Co mmand Line:\x01\x58\x6d\xa e\x99\x55\x57\x5d\x49\xbe\xe4\xe1\x9b\x14\xe6\x88\x5 7\x68\x6d\x11\xb9\x36\x73\x38\x71\x1e\x88\x46\xa9\x97\xd4\x3a\x20\x90
\x62\xae\x15\xcd\x4b\xc d\x72\x82\xbd\x75\x0a\x54\xf0\xcc\x01\xa d
Creat ionFl ags:4
D irect ory:
其中的Comm andLi ne好长啊,它要传递的参数是:一个被保护进程的pi d,两
个E vent的Handl e,以及当前time GetTi me的毫秒数 (感谢JTR分享)。
Crea tionF lags:4 查查w inbas e.h头文件,发现#defin e CRE ATE_S USPEN DED
0x00000004,所以N P进程创建时就是暂停的
在我们替换的Cre atePr ocess A中,先让游戏创建N P进程(由于游戏创建时NP进程本来就是暂停的,所以不用担心N P的问题),让游戏进程暂停(S endMe ssage就可以了),然后再
向NP进程注入DLL,最后让游戏进程继续。
这样我们的DLL就进入NP进程了。
实现起来大概是这样子
B OOL
W INAPI
MyCr eateP roces sA(//替换原来的Creat eProc essA
LPCST R lpA pplic ation Name,
LPST R lpC omman dLine,
LPS ECURI TY_AT TRIBU TES l pProc essAt tribu tes,
LPSEC URITY_ATTR IBUTE S lpT hread Attri butes,
BOO L bIn herit Handl es,
D WORDdwCre ation Flags,
LPV OID l pEnvi ronme nt,
L PCSTR lpCu rrent Direc tory,
LPST ARTUP INFOA lpSt artup Info,
LPPR OCESS_INFO RMATI ON lp Proce ssInf ormat ion
)
{
Unho okCre atePr ocess A();
BO OL
fR et=Cr eateP roces sA(lp Appli catio nName,lpCo mmand Line,lpPro cessA ttrib utes,lpThr ead At tribu tes,b Inher itHan dles,dwCre ation Flags,
lpE nviro nment,lpCu rrent Direc tory,lpSta rtupI nfo,l pProc essIn forma tion);
Reh ookCr eateP roces sA();
Se ndMes sage(hwndR ecv,//负责注入的窗体句柄
WM_H OOK_N P_CRE ATE,//自定义消息
(WP ARAM)lpPro cessI nform ation->dwP roces sId,//把NP进程ID传给负责注入的主窗体
N ULL);
r eturn fRet;
}
四、注意问题
由于我们是在不破解NP的前提下对游戏内存进行操作,所以一不小心的话,很容易就死游戏。
NP保护了游戏进程的代码段,所以在NP启动后就不要再对其代码段进行修改,要
补丁或H OOK系统函数这些都要在NP启动前完成。
当然读写游戏的数据段是没问题的,因为游戏本身也不断进行这样的操作。