标 题: 【原创】外挂普及教程
作 者: garyzjq
时 间: 2010-3-10 20:59
链 接: http://bbs.pediy.com/showthread.php?t=108616
最近看到几篇关于问外挂制作的帖子,那我就来介绍一种常用的简单的外挂注入方式吧,仅作为普及教程(觉得在看雪发表这种没技术含量的文章比较上不了台面。。。)
这里只介绍自己怎么做外挂,就不介绍怎么穿过NP或其他游戏保护或者怎么做商用外挂(一是我没做过,二是做了估计也立马被这里的牛们当CrackMe了。。。=。=)
好了,我不废话了
做调用CALL的外挂主要是要找基址找偏移找CALL地址,这些我就不介绍了,可以去看看“啊冲视频笔记”之类的外挂教学视频,很容易就掌握了,由于“未经同意,请不要转载”这句话,我就不在这里公布下载地址了。。。
找到这些地址之后就是怎么在外挂中找到游戏及显示数据(比如HP、MP)和利用CALL
下面来介绍显示数据和利用CALL
一、疯狂CreateRemoteThread(VC++6环境)
这种办法,不需要建立DLL,对于没做过DLL的初学者来说就比较简单,但效率没后一种高
1.首先,你要获得这个游戏进程的句柄
//找窗体
iHWnd=::FindWindow(aClassName,aWindowName);
//获得进程PID
::GetWindowThreadProcessId(iHWnd,&iPID);
//用PROCESS_ALL_ACCESS属性打开这个进程
iProcessHandle=::OpenProcess(PROCESS_ALL_ACCESS,false,iPID);
有了这个iProcessHandle你就能用ReadProcessMemory和WriteProcessMemory来读和写游戏进程里的数据了
那么显示的问题就解决了
::ReadProcessMemory(iProcessHandle,(LPCVOID)(aAddr+aBuffer),(LPVOID)aBuf,aSize,&readbyte);
::WriteProcessMemory(iProcessHandle,(LPVOID)(aAddr+aBuffer),(LPVOID)aBuf,aSize,&writebyte);
要读一个DWORD或者一个指针地址可以如下
DWORD temp;
::ReadProcessMemory(iProcessHandle,(LPCVOID)(aAddr+aBuffer),(LPVOID)&temp,4,&readbyte);
这里aAddr是基址,aBuffer是偏移,aBuf是读/写入数据缓存的地方,aSize是读/写入数据大小
一般读HP MP之类的就能这么读
2.之后就是调用CALL,前提是已经找到了正确的call地址。调用CALL其实就是在你代码里写入类似 mov eax,xxxxxxxx CALL eax(前面可能有一些push传参数)的汇编代码,然后把这段代码写入游戏的进程里,然后再创建一个远程线程在游戏进程里运行这段代码,它就调用那个CALL实现某些功能了
//在目标进程里申请要注入代码的空间
iCallBase=::VirtualAllocEx(iProcessHandle,NULL,MAX_CALL_SIZE,MEM_COMMIT,PAGE_EXECUTE_READWRITE);
iArgBase=::VirtualAllocEx(iProcessHandle,NULL,MAX_ARG_SIZE,MEM_COMMIT,PAGE_READWRITE);
//写入函数代码
if(!::WriteProcessMemory(iProcessHandle,iCallBase,aAddFunc,MAX_CALL_SIZE,&numofbyte))return 0;
if(!numofbyte)return 0;
if(aAddArg&&aArgSize>0)
{
numofbyte=0;
if(!iArgBase)return 0;
//写入参数代码(可以没有参数)
if(!::WriteProcessMemory(iProcessHandle,iArgBase,aAddArg,aArgSize,&numofbyte))return 0;
if(numofbyte!=aArgSize)return 0;
}
//关键!远程创建线程,这样游戏就开始创建一个线程运行你这段代码了
iRemoteThreadHandel=::CreateRemoteThread(iProcessHandle,NULL,0,(LPTHREAD_START_ROUTINE)iCallBase,iArgBase,0,NULL);
if(!iRemoteThreadHandel)return 0;
::WaitForSingleObject(iRemoteThreadHandel,INFINITE);
::CloseHandle(iRemoteThreadHandel);
iRemoteThreadHandel=NULL;
注意:
a.用push传参数是c里面一般函数调用的做法,所以你要确保游戏是用c语言写的
b.由于某些原因(网上有说,这里不重复),你要远程CALL的话一定不能用debug编译外挂程序,一定要用release,而且嵌入的代码要尽可能简单,不然有很多原因会让你远程失败
剩下的事情就很简单了,在外挂里设个SetTimer(),把检查HP然后吃药,检查有没有打怪,选怪打怪等等的功能放进去组合一下就好了
这个方法不用做DLL,一个exe就能搞定,但是有个小问题,你如果装360安全卫士然后让外挂运行一小时的话会发现他已经记录了几万个远程创建线程事件。。。因此称之为“疯狂CreateRemoteThread”
二、DLL注入(VC++6 & DELPHI 7)
有个前提,游戏不反对你注入=。=(或者参考论坛里的一篇介绍利用输入法注入的文章也行)
DLL注入就要你先写一个DLL,里面就是外挂程序,然后把这个DLL注入到目标游戏,启动它,让它不断检查HP打怪状态等等然后CALL,这些基本和上一种方法一致
这里主要来说一下怎么注入一个始终伴随游戏存在的DLL,并且能在游戏里呼出挂的窗口进行一些设置
1.呼出窗口
大家都知道,一般呼出就是在DLL里用钩子挂钩KEYBOARD
iHook:=SetWindowsHookEx(WH_KEYBOARD, MyHookProc , HInstance, aThreadId);
//键盘挂钩反馈函数
function MyHookProc(iCode:Integer;wParam:WPARAM;lParam:LPARAM):LRESULT;stdcall;
var
wKey:word;
begin
wKey:=word(wParam);
if (HIWORD(lParam) and KF_UP = 0) and (HC_ACTION = iCode) then
//按F2显示外挂呼出窗口
if (wKey = VK_F2) then
begin
frmMain.Show;
end;
//按F3隐藏外挂呼出窗口
if (wKey = VK_F3) then
begin
frmMain.Hide;
end;
//按F4结束外挂线程
if(wKey = VK_F4) then
begin
iIsExit:=1;
end;
Result:=CallNextHookEx(iHook,iCode,wParam,lParam);
end;
2.DLL的制作
这里我用到了DELPHI 7,因为要做一个可以呼出窗口的DLL嘛,VC++在DLL里放个窗体显示貌似有点麻烦,所以这里我选用DELPHI,懂VC++的不要BS我啊。。。
DLL要完成的是如何创建一个伴随游戏主线程而存在的子线程
在DLL的入口:
CreateThread(nil,0,@ZStartThread,nil,0,id);
procedure ZStartThread;stdcall;
begin
//挂钩键盘
if not ZHookKeyboard(ZGetMainThreadId(GetCurrentProcessId())) then
begin
messagebox(0,'hook error',0,0);
exit;
end;
//建一个FORM,是已经编辑好的外挂呼出窗口
frmMain:=TfrmMain.Create(nil);
//这里创建了一个循环的线程,直到iIsExit=1
while iIsExit=0 do
begin
//主动让出CPU
sleep(1);
application.ProcessMessages;
end;
//卸载钩子
if not ZUnhookKeyboard() then
begin
messagebox(0,'unhook error',0,0);
exit;
end;
frmMain.FreeOnRelease;
DLLProc(DLL_PROCESS_DETACH);
end;
基本上DLL框架就这点内容,具体怎么显示HP怎么CALL就要根据游戏实际情况了
3.DLL注入进游戏
那么怎么注入进游戏呢?其实有两种方式,一种是挂键盘钩子,然后在游戏中按呼出外挂键,这时如果游戏进程内没有该DLL,则系统会帮你加载进去,完成了注入(附件ZCommonWG2);还有一种是利用CreateRemoteThread,让游戏主动LoadLibrary你的外挂DLL(附件ZCommonWG)
这里用后一种方式实现(VC++)
int RemoteLoadDll(HWND aHwnd)
{
DWORD dwId;
HANDLE hProcess;
char sz[MAX_PATH];
HANDLE hThread;
DWORD dwModule;
void *pData;
::GetWindowThreadProcessId(aHwnd,&dwId);
hProcess=::OpenProcess(PROCESS_ALL_ACCESS,FALSE,dwId);
if(!hProcess)return 0;
ZFile::ZGetCurFileName(sz,"ZKeyHook.dll");
pData=::VirtualAllocEx(hProcess,0,sizeof(sz)+1,MEM_COMMIT,PAGE_READWRITE);
if(!pData)return 0;
if(!::WriteProcessMemory(hProcess,pData,sz,sizeof(sz)+1,0)) return 0;
hThread=::CreateRemoteThread(hProcess,NULL,0,(LPTHREAD_START_ROUTINE)::GetProcAddress(::LoadLibrary("kernel32.dll"),"LoadLibraryA"),pData,0,0);
if(hThread==NULL)return 0;
::WaitForSingleObject(hThread,INFINITE);
::GetExitCodeThread(hThread,&dwModule);
::CloseHandle(hThread);
::VirtualFreeEx(hProcess,pData,sizeof(sz),MEM_RELEASE);
hThread=::CreateRemoteThread(hProcess,NULL,0,(LPTHREAD_START_ROUTINE)::GetProcAddress(::LoadLibrary("kernel32.dll"),"FreeLibrary"),&dwModule,0,0);
if(hThread==NULL)return 0;
::WaitForSingleObject(hThread,INFINITE);
::CloseHandle(hThread);
::CloseHandle(hProcess);
return 1;
}
CreateRemoteThread的使用在第一种方式里介绍过了,这里就不重复了
要注意的是,在2中说在DLL入口用CreateThread建立一个无限循环的线程,而不是在DLL入口直接无限循环,否则会导致远程注入的程序因等待LoadLibrary结束而挂起
总结:这里只是大致介绍了DLL的一种注入方式和做这类外挂的一个大体框架,具体外挂的代码还要自己实践来实现,比如找CALL就要用到OD,所以新人们OD技术一定要过关啊~
提示,示例代码中凡是函数前有Z字母的基本就是用户定义的函数(我的坏习惯~嘿嘿)
这是偶滴看雪处女座,如果有不当之处请大家见谅,如果有错误或不足之处请大家指出啊~
下面两个源码(在文中已经分别介绍过他们的实现原理了):
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课
上传的附件: