2014年09月05日 ⁄ 综合 ⁄ 共 8724字 ⁄ 字号    ⁄ 评论关闭

(一)外挂一般都能在游戏的界面中按一个热键(比如F12,HOME等),就可以呼出外挂的窗口,然后在里面进行外挂的功能设置,这个外挂的窗口是怎么弄出来的呢?

要想在游戏里显示出窗口,那么我们要显示的这个窗口就要和游戏本身“混”在一起,也就是说我们的外挂窗口要“混入”游戏的内部,让游戏不排斥外挂窗口,把外挂窗口当做“自己人”,这样我们的外挂才能去“影响”游戏本身的运行。行话把这个叫“注入”。

那怎么“注入”呢?
Windows操作系统有个API函数SetWindowsHookEx,该函数的可以在系统上安装一个“钩子(HOOK)”。也就是把我们自己编写的一个回调函数设置为系统“钩子”。“钩子(HOOK)”有什么用呢?系统发送给各种程序窗口的消息,都要先经过“钩子”先处理之后再送到它本来要去的窗口。而在“钩子”处理来的消息的时候,Windows操作系统就已经自动把“钩子”“钩”在了消息即将到达的目的程序窗口上了,此时“钩子”就已经“混入”了目的窗口的内部了

==========================================
以下shaker注明:
这个教程存在一个漏洞,以使一些对DLL编程不是很了解的人不能顺利的完成编译。
BUG如下:原文中的S3DHOOK.DEF文件中的内容如下

; S3DHook.def : Declares the module parameters for the DLL.

LIBRARY      "S3DHook"
DESCRIPTION  "S3DHook Windows Dynamic Link Library"

EXPORTS
    ; Explicit exports can go here
使得生成的DLL没有任何输出函数,在编译EXE工程出现错误,要解决这个问题只要改变原来的S3DHook.def文件的内容如下:
; S3DHook.def : Declares the module parameters for the DLL.

LIBRARY      "S3DHook"
DESCRIPTION  "S3DHook Windows Dynamic Link Library"

EXPORTS
    ; Explicit exports can go here
InstallHook
UninstallHook
如此,问题便得到解决!

把有关外挂功能的代码和“钩子”函数一起放到同一个DLL中,那么我们的外挂也就一同被注入到游戏里面去了

在“我的文档”中建立一个文件夹名字叫“神迹外挂”然后打带VC6,建立新工程
VC6神迹外挂的DIY-LMLPHP
点OK,选择
Regular DLL using shared mfc DLL

在S3DHook.h头文件中加入

#ifndef S3DHOOKAPI
#define S3DHOOKAPI extern "C" __declspec(dllimport)
#endif

在S3DHook.cpp中

#include "S3DHook.h"
这一句之前加入
#define S3DHOOKAPI extern "C" __declspec(dllexport)

也就是这个样子成了这个
#define S3DHOOKAPI extern "C" __declspec(dllexport)
#include "S3DHook.h"
在S3DHook.cpp中加入全局共享数据

#pragma comment(linker,"section:Shared,rws")
#pragma data_seg("Shared")
HHOOK g_hhook;
#pragma data_seg()
在S3DHook.cpp加入钩子回调函数

LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
{
BOOL bKeyUp = lParam & (1 << 31);
if (bKeyUp && wParam == VK_F12 && nCode == HC_ACTION) {
AfxMessageBox("ok");
}
return ::CallNextHookEx(g_hhook, nCode, wParam ,lParam);
}
在文件前面加入函数的原形以便后面引用
LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam);

在S3DHook.H里加入“导出(export)”的钩子安装卸载函数原形

S3DHOOKAPI BOOL WINAPI InstallHook();
S3DHOOKAPI BOOL WINAPI UninstallHook();

在S3DHook.CPP里加入钩子安装卸载函数的实现

S3DHOOKAPI BOOL WINAPI InstallHook()
{
if (g_hhook == NULL) {
g_hhook = ::SetWindowsHookEx(WH_KEYBOARD, (HOOKPROC)KeyboardProc, theApp.m_hInstance, 0);
if (g_hhook != NULL)
return TRUE;
}
return FALSE;
}

S3DHOOKAPI BOOL WINAPI UninstallHook()
{
return ::UnhookWindowsHookEx(g_hhook);
}

好了,现在我们建立的这个DLL具有基本的键盘钩子的功能,编译生成S3DHook.dll
下面建立一个EXE来调用这个DLL
VC6神迹外挂的DIY-LMLPHP
这个是对话框型的工程

在MainDlg.cpp中加入对DLL的调用

插入头文件包含
#include "../s3dhook/s3dhook.h"

更改工程设置

Project->settings->link->Object/library modules:
输入../s3dhook/debug/s3dhook.lib

在对话框的OnInitDialog中加入InstallHook();安装键盘钩子
在OnClose中加入UninstallHook();关闭程序时卸载键盘钩子
编译这个对话框EXE

把这两个工程生成的S3DHook.dll和Main.exe放到同一个文件夹中,运行
这个是DLLsf_20046314251.rar 
这个是EXEsf_200463142551.rar

下面我们在S3DHook这个DLL工程中添加一个从CDialog派生的CS3DHookDlg类

把DIALOG ID改为IDD_S3DHOOK_DIALOG
VC6神迹外挂的DIY-LMLPHP
添加的对话框类的操作如下
主菜单->Insert->New Form

为了方便,把CS3DHookDlg的源程序文件名字分别改为
s3dhook.h
s3dhook.cpp

操作:点上图中的Chang
VC6神迹外挂的DIY-LMLPHP
把刚才自动生成的对话框的Caption改为"外挂呼出窗口"

下面定义一个全局窗口指针来保存我们要生成的这个窗口的指针,以便后面对"外挂呼出窗口"进行控制,把它和全局变量
CS3DHookApp theApp;
写在一起,也就是这个样子
CS3DHookApp theApp;
CS3DHookDlg *pCWndWGMain;
下面对钩子回调函数进行改造,以便使我们的"外挂呼出窗口"能够在按F12时呼出
如下:
LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
{
//按F12弹起时呼出外挂
BOOL bKeyUp = lParam & (1 << 31);
if (bKeyUp && wParam == VK_F12 && nCode == HC_ACTION) {
if (pCWndWGMain == NULL) 
{
//更改当前有效模块状态到DLL中
//以便正确的读取对话框的资源
AFX_MANAGE_STATE(AfxGetStaticModuleState());
//找到当前的有效激活窗口
CWnd *pCWnd = CWnd::GetForegroundWindow();
//生成CS3DHookDlg类的对象实例
//此处应该生成一个非模态对话框
pCWndWGMain = new CS3DHookDlg();
pCWndWGMain->Create(IDD_S3DHOOK_DIALOG, pCWnd);
}
else 
{
//根据当前呼出窗口的状态来显示或隐藏呼出窗口
pCWndWGMain->ShowWindow(pCWndWGMain->IsWindowVisible() ? SW_HIDE : SW_SHOW);
}
}
return ::CallNextHookEx(g_hhook, nCode, wParam ,lParam);
}
有关上面的这一句
在Regular MFC DLL中使用资源时非常重要
AFX_MANAGE_STATE(AfxGetStaticModuleState());
更多说明请参看
这里
当关掉外挂的主程序时,还要做点善后工作
重载CS3DHookApp类的ExitInstance函数,在其中删除对话框
int CS3DHookApp::ExitInstance() 
{
// TODO: Add your specialized code here and/or call the base class
delete pCWndWGMain;
return CWinApp::ExitInstance();
}
重新编译生成S3DHOOK.dll并和Main.exe放到一起,运行它试试看

运行后,随便打开一个其他的什么窗口,按F12,看到什么了?

哈哈,我们的"外挂呼出窗口"呼出来了,真是千呼万唤始出来啊

我在记事本中试了试:

VC6神迹外挂的DIY-LMLPHP
Main.exe和s3dhook.dll在这里: sf_200463194336.rar
要让他只在指定的程序窗口中呼出,每次都进神迹的游戏客户端试验很麻烦
我们就在记事本中试验,让他只能在记事本中呼出,最好还要能像真正的外挂那样
对挂入的程序做点手脚,
说做就做,开工!!!
继续对钩子回调函数进行改造
如下:
LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
{
//按F12弹起时呼出外挂
BOOL bKeyUp = lParam & (1 << 31);
if (bKeyUp && wParam == VK_F12 && nCode == HC_ACTION) {
if (pCWndWGMain == NULL) 
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
CWnd *pCWnd = CWnd::GetForegroundWindow();
//当前窗口是否为记事本窗口
char buf[MAX_PATH];
::GetClassName(pCWnd->GetSafeHwnd(), buf, MAX_PATH);
if (lstrcmpi(buf, "notepad") == 0) {
pCWndWGMain = new CS3DHookDlg();
//创建"外挂呼出窗口"时把记事本窗口作为他的父窗口
pCWndWGMain->Create(IDD_S3DHOOK_DIALOG, pCWnd);
pCWndWGMain->ShowWindow(SW_SHOW);
}
}
else 
{
//根据当前呼出窗口的状态来显示或隐藏呼出窗口
pCWndWGMain->ShowWindow(pCWndWGMain->IsWindowVisible() ? SW_HIDE : SW_SHOW);
}
}
return ::CallNextHookEx(g_hhook, nCode, wParam ,lParam);
}

重新编译并运行,我们的"外挂呼出窗口"可以多个记事本中呼出
如图,并还没有发现会造成程序崩溃,有发现的,请告诉我!后面会附上

VC6神迹外挂的DIY-LMLPHP
这里是重新编译的Main.exe和s3dhook.dll

本主题包含附件: sf_200463202334.rar

哈哈,解决了一个小问题,爽!!!!!!!!!!!!!
下面让我们的外挂对记事本做点小动作吧!
做点什么呢?
就让他在记事本的窗口中画个圆,怎么样?
来试试看了
为CS3DHookDlg添加WM_INITDIALOG的消息处理器
并在其中添加一个定时器,同时窗口关闭时要销毁定时器
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
设置
BOOL CS3DHookDlg::OnInitDialog() 
{
CDialog::OnInitDialog();

// TODO: Add extra initialization here
SetTimer(1000,100,0);
return TRUE;  // return TRUE unless you set the focus to a control
              // EXCEPTION: OCX Property Pages should return FALSE
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~~销毁,先要添加WM_CLOSE消息的处理器
void CS3DHookDlg::OnClose() 
{
// TODO: Add your message handler code here and/or call default
KillTimer(1000);
CDialog::OnClose();
}

响应CS3DHookDlg的WM_TIMER消息
代码如下:
void CS3DHookDlg::OnTimer(UINT nIDEvent) 
{
// TODO: Add your message handler code here and/or call default
//得到父窗口也就是记事本的指针
CWnd *pCWnd = GetParent();
//得到记事本的窗口设备上下文指针
CDC *pDC = pCWnd->GetWindowDC();
//画圆
pDC->Ellipse(100,100,200,200);
CDialog::OnTimer(nIDEvent);
}

效果如图:
VC6神迹外挂的DIY-LMLPHP
下面是重新编译过的程序

本主题包含附件: sf_200463212057.rar

在记事本中的实验非常成功,爽!!!!!!!!
下面让我们的程序只在神迹中呼出
主要是需要改造键盘钩子回调程序
只需要把
lstrcmpi(buf, "notepad")
改为
lstrcmpi(buf, "SG Engine")
就可以了
"SG Engine"是神迹客户端的窗口的类名
重新编译运行,即可在神迹中呼出了,并且也在其中的窗口上画了个圆,
但是画面在闪烁,具体解决方法还没有找到
估计是因为:游戏使用DirectX作图,而我们这里是用GDI作图
先不管它了,留在以后再解决了
暂时我们还不需要在游戏里作图
,把我们程序中刚才有关作图的部分都删除掉,
1.OnInitDialog中的
2.OnClose中的
3.OnTimer删掉

利用madCHook进行API挂接:如:WSASend,WSARecv等

到这里去下载
安装刚才下载的madCollection.exe,安装后注意到在
C:/Program Files/madCollection/madCodeHook/Dll
中有3个文件需要引入到我们的S3DHook.dll工程中去
如下:
madCHook - dynamic.h
madCHook - dynamic - microsoft.lib
madCHook.dll
为了便于使用,把他们的名字改一下,改为:
madCHook.h
madCHook.lib
madCHook.dll
把.h和.lib放到S3DHook工程所在的文件夹中
在S3DHook.cpp中包含madCHook.h头文件,加入
#include "madCHook.h"
在Project->Settings->Link->Object/library modules中
加入madCHook.lib
下面就可以在我们的DLL中使用madCHook对API进行挂接了
注意:编译后要把Main.exe,S3DHook.dll和madCHook.dll放在一起才能运行

下面,正式开始加入代码对WSASend进行挂接
首先在S3DHook.cpp中加入
#include "Winsock2.h"
然后加入对原始API函数的指针及自定义API HOOK函数的原形的声明:
int (WINAPI *oWSASend)(
SOCKET,
LPWSABUF,
DWORD,
LPDWORD,
DWORD,
LPWSAOVERLAPPED,
LPWSAOVERLAPPED_COMPLETION_ROUTINE
);
int cWSASend(
SOCKET s,
LPWSABUF lpBuffers,
DWORD dwBufferCount,
LPDWORD lpNumberOfBytesSent,
DWORD dwFlags,
LPWSAOVERLAPPED lpOverlapped,
LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
);
这个一定要和原始的API函数WSASend的形参一致才行,可以参看MSDN
为了把截获的数据显示出来,用资源编辑器在S3DHook中的外挂呼出窗口中加入一个ListBox,将其ID改为IDC_LIST_SEND,将其SORT属性去掉,
然后加入自定义API钩子的实现
int cWSASend(SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount,
 LPDWORD lpNumberOfBytesSent, DWORD dwFlags,
 LPWSAOVERLAPPED lpOverlapped,
 LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine)
{
char buf[1024];
lstrcpyn(buf,lpBuffers->buf,lpBuffers->len);
CListBox *pListBox = (CListBox *)pCWndWGMain->GetDlgItem(IDC_LIST_SEND);
pListBox->AddString(buf);
                //API钩子返回之前,对原始的API进行调用,
return oWSASend(s, lpBuffers, dwBufferCount, lpNumberOfBytesSent, dwFlags, lpOverlapped, lpCompletionRoutine);
}
下面我们利用madCHook安装WSASend这个API的钩子
成功后,游戏每次调用WSASend这个API的时候,都会去先执行我们写的
cWSASend,在这里,我们就可以截取到游戏发往服务器的数据封包,然后用游戏调用到cWSASend时使用的参数去调用我们刚才写的指向原始WSASend的函数指针oWSASend去调用原始的API将数据发到服务器,这样才能保证游戏能继续正常运行,否则游戏很有可能会掉线

在CS3DHookDlg::OnInitDialog() 加入这一句来安装API钩子
HookAPI("Ws2_32.dll", "WSASend", cWSASend, (PVOID *) &oWSASend);
参数说明:
1.要挂接的API所在的DLL
2.要挂接的API
3.自定义的函数,用来替换要挂接的API:cWSASend//游戏每次调用Ws2_32.dll中的WSASend,就会先进入我们自定义的cWSASend
4.指向原始API函数WSASend的指针//自定义的cWSASend截取封包并进行必要处理后,通过这个指针去调用原始的WSASend@Ws2_32.dll,以保证游戏的正常运行
BOOL CS3DHookDlg::OnInitDialog() 
{
CDialog::OnInitDialog();

// TODO: Add extra initialization here
HookAPI("Ws2_32.dll", "WSASend", cWSASend, (PVOID *) &oWSASend);
return TRUE;  // return TRUE unless you set the focus to a control
              // EXCEPTION: OCX Property Pages should return FALSE
}
编译之后,把Main.exe S3DHook.dll madCHook.dll放在一起,看看效果
运行Main.exe,启动进入神迹,按F12呼出
VC6神迹外挂的DIY-LMLPHP
令人激动的时刻啊!看到没有?截获的数据封包已经在ListBox控件中显示出来了!!!

其实这才是刚刚开始,要想有强大的功能,后面还要做许多工作

VC6神迹外挂的DIY-LMLPHP

04-28 05:59