开发外挂的一些原理

看本文前必须先会 ASM VC OD FPE HOOK
游戏修改
我一直用的是 FPE2000 ,不习惯用其他的修改器,因为有个功能其他的修改器没有,而且这个功能相当重
要。
怎样搜索就不讲了,主要讲分析。先看血和魔法,大家会发现一般的游戏血和魔法都在相临的位置,为什
么哪?这个原理很简单,因为编程人员的习惯问题。写游戏时会定义一个基本的结构,这个结构包含人物
的一些属性,例如:
struct _CHAR_ATTR
{
  char Name[30]
  DWORD HP,MAXHP;
  DWORD MP,MAXMP;
  DWORD Exp;
}
这样一来程序运行后分配内存是按结构分配的,所以直接分配一个结构的大小并不考虑结构中的变量,那
么结构中的变量地址当然是相临的。所以一般查到 HP 的时候就可以查到 1 个人物的一些基本属性了。以经
验来讲,人物的动作也在这个结构附近,这里就要介绍只有 FPE 有的重要功能了。动作一般是一些数字表
示,当人物做一个砍怪的动作时,会分 N 个细节的动作,一般这些动作都是连续的数字,好比 0 是站立准备
砍怪, 1 是举起武器, 2 是往下挥武器, 3 是砍到怪, 4 是收起武器,然后就是循环这几个动作,那我们怎样
找这个地址哪,首先找到 HP 地址,然后在 FPE 上点 EDIT 页面,这时 FPE 显示的数据是点击 edit 时的数据,再
转回游戏,不要动也不要掉血,不然就不准了,再转回 FPE ,按下 F5 (这个就是我说的重要功能),有没
有看到一些地址的数据已经变了,没错这些数据变动的数据就是人物的骨骼动画数据,如果没有发现变动
也不要紧,按 PAGE UP 或者 PAGE DOWN ,看看附近的内存页是否有数据在变动,接下来就是分析这些动画数
据了,我们再回游戏,然后让你的人物跑起来,要跑的比较远不然还没等转到 FPE 就停了,跑起来后我们
转回 FPE ,然后一直按着 F5 ,看数据的变化,主要是寻找 * 循环 * 变动的数据,可能刚开始找这些不会太明
白,不过没关系,凭知觉,看哪个象就在哪个地址锁定,锁定完后再去游戏跑跑看,如果人物跑的动作不
对的话那就证明你成功了,接下来用同样的方法找打怪的动作,找到后就可以做光人物加速而游戏速度不
变的功能了:)  什么?到现在你不还不知道怎么做人物加速?那我再费点手力,还看上面 0 1 2 3 4
动作,我们可以直接去掉 0 1 2 两个动作,在 FPE 里锁定值是 3 ,判断是 =0 ,就是当做 0 动作时直接跳到 3
动作。这样就直接是砍怪和收武器的动作了作了,如果还嫌慢那就把 4 也去掉, FPE 里同一地址锁定值是 0
,判断是 3 ,这样光剩下砍的动作了,做完后回游戏看看,发现你的任务砍怪时一直在抽筋:)
FPE
里还有个重要的搜索就是 '?' ,用这个可以搜索条状数值,如果不会的话可以哪一些单机游戏做实验。
窗口化
2D
游戏窗口化
主要是修改窗口类型。
//
窗口类型和窗口扩展类型
LONG style,exstyle
//
得到窗口类型
style= GetWindowLong(
窗口句柄 ,GWL_STYLE);
//
加上标题栏
style=style | WS_CAPTION ;
//
这里就是把游戏设置成窗口模式了
SetWindowLong(
窗口句柄 ,GWL_STYLE,style);// 修改窗体的 exstyle 属性
//
对于扩展类型可以改也可以不改,看自己喜好了。
exstyle=GetWindowLong(
窗口句柄 ,GWL_EXSTYLE);
exstyle=exstyle | WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;
//
设置窗口的扩展类型
SetWindowLong(
窗口句柄 ,GWL_EXSTYLE,exstyle);
//
这里是设置窗口的大小,并且设置置顶的属性
SetWindowPos(
窗口句柄 ,HWND_NOTOPMOST,0,0,800,600,SWP_SHOWWINDOW);
//
显示窗口
ShowWindow(
窗口句柄 ,SW_SHOWNORMAL);
//
接下来是修改窗口的消息了,一般游戏会在改变窗口模式消息检测是否是全屏模式
//
这个变量用来保存游戏窗口原先的消息处理函数
WNDPROC OldMsgProc; 
//
得到原来的消息函数
OldMsgProc=(WNDPROC)GetWindowLong(
窗口句柄 ,GWL_WNDPROC);
//
接下来我们就替换自己的窗口消息函数
if(SetWindowLong(
窗口句柄 ,GWL_WNDPROC,(long)MsgProc)==0)
  return false;
//
窗口消息函数
LRESULT CALLBACK MsgProc(HWND hWnd,UINT msg,WPARAM wparam,LPARAM lparam)
{
//
消息过滤
switch (msg)
{
case WM_ACTIVATEAPP:
case WM_ACTIVATE:
case WM_KILLFOCUS:
case WM_SETFOCUS:
case WM_CLOSE:
return 0;
//
杀掉检测窗口模式的定时器
case WM_TIMER:
if(wparam==
检测窗口模式的定时器 ID)
KillTimer(hWnd,wparam); //
杀掉
break;
}
return CallWindowProc(OldProcMsg,hWnd,msg,wparam,lparam);
}
这样就完成窗口化了,还有一点需要注意的,就是如果游戏启用定时器检测窗口状态时我们必须把这些定
时器关掉,可以用 spy++ 检测游戏窗口用了哪几个定时器。然后记录下来定时器的 ID ,在窗口消息
3D 游戏窗口化
3D
游戏就比较简单了,主要是靠的是 DirectX 中的 CreateDevice 函数,当然每个 dx 版本创建都不一样,但
基本步骤都差不多。只要修改 D3DPRESENT_PARAMETERS 结构就可以实现了。
我们只要 hook dx CreateDevice ,在其中把 D3DPRESENT_PARAMETERS.Windowed 属性改为 true 即可。不过
可能导致游戏不能正常运行或者画面位移、透明等,这些就要参考 dx D3DPRESENT_PARAMETERS 结构另外
的参数了。可以参考任意一个 D3D 教程,里面都有详细的解释。
游戏加速
游戏加速是利用修改时间函数的返回值。
利用 hook 修改返回值应该是很简单的,我就不讲了,但是有些游戏当你利用 hook 修改后他却提示你修改函
数被修改而终止游戏。不过不要怕只要会汇编没什么能难倒的。
下面的函数就是修改了 GetTickCount() 函数的返回值用 vc 写的。下面是在 hook 初始化时做的工作。
DWORD dwIdOld1=0;
DWORD *p1=((DWORD*)GetTickCount)+3;
VirtualProtectEx(GetCurrentProcess(),(LPVOID)p1,2,PAGE_READWRITE,&dwIdOld1);
if(m_Speed==1)
{
__asm
{
push eax
mov eax,p1
mov [eax+1],0x17 //
1 倍速度
pop eax
}
}
else
{
__asm
{
push eax
mov eax,p1
mov [eax+1],0x16 //
2 倍速度
pop eax
}
}
VirtualProtectEx(GetCurrentProcess(),(LPVOID)p1,2,dwIdOld1,&dwIdOld1);
这个我就不多讲了因为想要源代码的可以找我。
写屏
大多数人是利用修改游戏函数写屏的,我的方法是 HOOK 
dx 写屏。原理很简单,游戏是要通过 Blt BltFast 转换页面的,我将字写到后台页面就可以了,好处是不
必太麻烦找游戏输出函数,而且换个游戏也一样能用。而且还能贴个图片到游戏。坏处是如果 dx 版本不同
就要修改代码了。建议用 MS detours ,方便而且稳定。
//
输出文字到一个页面
HRESULT DrawText(LPDIRECTDRAWSURFACE m_pdds,TCHAR* strText,DWORD dwOriginX,DWORD dwOriginY,
 COLORREF crBackground,COLORREF crForeground)
{
    HDC hDC = NULL;
    HRESULT hr;
HFONT hFont=NULL;
    if( m_pdds == NULL || strText == NULL )
        return E_INVALIDARG;
    // Make sure this surface is restored.
    if( FAILED( hr = m_pdds->Restore() ) )
        return hr;
    if( FAILED( hr = m_pdds->GetDC( &hDC ) ) )
        return hr;
    // Set the background and foreground color
    SetBkColor( hDC, crBackground );
    SetTextColor( hDC, crForeground );
    if( hFont )
        SelectObject( hDC, hFont );
    // Use GDI to draw the text on the surface
    TextOut( hDC, dwOriginX, dwOriginY, strText, strlen(strText) );
    if( FAILED( hr = m_pdds->ReleaseDC( hDC ) ) )
        return hr;
    return S_OK;
}
//
显示文本太简单了,就在 HOOK 的函数里写 1 句。
//
老版本的 BltFast  这个是从离屏页面 Copy 图片到后台页面的函数
//DefHookDApi 
是我自己写的快捷定义 hook 函数不用去管。 detours
DefHookDApi(BltFast,HRESULT,(DWORD x,DWORD y,LPDIRECTDRAWSURFACE lpdds, LPRECT lprc,DWORD 
n))
{
//
我们直接把东西 Copy 到离屏页面
DrawText(lpdds,"BltFast",0,0,RGB(0,0,0),RGB(255,255,0));
HRESULT ret=Real_BltFast(x,y,lpdds,lprc,n);

return ret;
}
//
老版本的 Blt  这个是从后台页面 Copy 主页面的函数
DefHookDApi(Blt,HRESULT,(GUID FAR *lpGUID,LPRECT lprc,LPDIRECTDRAWSURFACE lpdds,LPRECT 
lprc1,
   DWORD n, LPDDBLTFX n1))
{
//
我们直接把东西 Copy 到后台页面
DrawText(lpdds,"Blt",0,0,RGB(0,0,0),RGB(255,255,0));
HRESULT ret=Real_Blt(lpGUID,lprc,lpdds,lprc1,n,n1);
return ret;
}
分析封包
我是用 OD 直接解密的,不提倡用 wpe 看,看的累,而且看半天看不出来东西。
首先是确保游戏运行文件没有加壳。再看看启动后的游戏进程名是否跟你运行的 exe 名一样,如果不一样那么我们先来看他是如何启动的游戏进程,用 od 取你运行的 exe 文件,然后在命令里面输入 bp CreateWindowW bp CreateWindowExW, 然后按 F9 运行,点你连接的服务器,这时会中断下来,然后看他启动的文件和参数,先在启动文件创建个快捷方式,然后把参数填到快捷方式里,以后直接运行这个快捷方式即可。
od 取实际的游戏执行文件,然后在命令行输入 bp send bp recv bp WSASend, bp WSARecv 四个命令,再点 od 的调试 - 》参数,把刚才记录的参数写进去。按 CTRL+F2 重新取。按 F9 运行。随便输入个帐号和密码点进入,这时会中断到 send 或者 WSASend ,按 CTRL+F9 即可回到游戏领域,然后像上看,加密可能就在这上面。首先我们来判断这个函数的开始,如何判断函数入口那,就是看 PUSH 语句, od 一般把一个函数用蓝线扩起来了,比较容易分清。看函数入口到调用 send WSASend 前面有没有 call 语句,如果有我们需要跟进去,如果发现离函数入口很近而且没有 call 语句那直接按 CTRL+F9 再回上一个领域,照这样的方法就可以找到加密算法了。其实很简单。
接下来是 recv WSARecv ,首先讲 recv ,中断这里后我们在堆栈窗口右键点 buffer ,然后选内存中显示。再按 CTRL+F9 ,发现内存中有了接收的数据,然后在内存的第一个字节右键点读硬件中断,继续按 F9 运行吧,很快会中断到读取内存的地方,很简单这里有可能就是加密的地方,如果看不出来那么在内存第 5 个字节同样做读硬件内存中断,很快就能找到解密地方。
WSARecv
buffer 不一样,他是个缓存指针,如果中断到这里我们一样在堆栈窗口右键点 buffer ,然后选内存中显示。这是个结构,前 4 个是 buffer 大小,后面的才是数据,按 CTRL+F9 ,发现接收数据,我们直接在第 5 个地方下读硬件中断,以后就跟 recv 一样了。呵呵一切都不难,只要会汇编就 ok
找写屏函数的方法
我用 si 来分析的,因为 od 我不知道怎么搜索内存。。。
在游戏的交谈栏里写一句话,先不要发送,然后用 si s 命令搜索你要发的这句话比如话是 我要搜索的内存 s ds:00000000 l ffffffff ' 我要搜索的内存 ', 有可能会找到多个地址,这个跟 fpe 的搜索差不多,一个一个修改试试,用 “d  地址 命令可以查看找到的地址,然后光标移动到内存页面上面就可以修改了。找到正确的后就要下内存断点了 ,"bpm  地址  r" 命令就可以了,返回游戏把话发出去,这时就会中断到读这段内存的语句了,一般是 lea  汇编指令,意思是把这个内存地址副给积存器,然后 PUSH ,然后就是 call 了,至于如何分析 call 有几个参数可以看 win32asm 教程。有时候也可能复杂点,就是游戏把这个内存拷贝到另一个内存然后再输出,这样就是多了一个步骤而已,只要在拷贝到另一个内存的地址下个内存中断即可。找到函数如何利用哪?在你的 hook dll 中可以直接调用这个函数,例如这个写屏函数有 2 个参数, 1 个是 buf ,第 2 个是 buf 的长度,汇编语句是
mov ebx, ds[????]
push ebx
lea eax
ds[????]
push eax
call ??????
// vc 写个函数,不用怀疑就是那么简单
__declspec(naked) Out(char* buf,int len)
{
  mov ebx, len
  push ebx
  lea eax
buf
  push eax
  call ??????
}
一般我都做利用 recv WSARecv 函数返回失败后做这些功能。
DefHookApi(recv,int,(SOCKET s,char *buf,int len,int flags))
{
  int ret=Real_recv(s,buf,len,flags);
  if(ret<1&&strcmp(outbuf,""))
  {
    Out(outbuf,strlen(outbuf));
    ZeroMemory( outbuf, sizeof(outbuf) ); //
记得清空不然会一直发送
  }
}
这样就 ok 了。
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值