zoukankan      html  css  js  c++  java
  • 开发外挂的一些原理

    看本文前必须先会ASMVCODFPEHOOK
    游戏修改
    我一直用的是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,看数据的变化,主要是寻找*循环*变动的数据,可能刚开始找这些不会太明
    白,不过没关系,凭知觉,看哪个象就在哪个地址锁定,锁定完后再去游戏跑跑看,如果人物跑的动作不
    对的话那就证明你成功了,接下来用同样的方法找打怪的动作,找到后就可以做光人物加速而游戏速度不
    变的功能了:) 什么?到现在你不还不知道怎么做人物加速?那我再费点手力,还看上面01234
    动作,我们可以直接去掉012两个动作,在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 dxCreateDevice,在其中把D3DPRESENT_PARAMETERS.Windowed属性改为true即可。不过
    可能导致游戏不能正常运行或者画面位移、透明等,这些就要参考dxD3DPRESENT_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写屏。原理很简单,游戏是要通过BltBltFast转换页面的,我将字写到后台页面就可以了,好处是不
    必太麻烦找游戏输出函数,而且换个游戏也一样能用。而且还能贴个图片到游戏。坏处是如果dx版本不同
    就要修改代码了。建议用MSdetours,方便而且稳定。
    //
    输出文字到一个页面
    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 CreateWindowWbp CreateWindowExW,然后按F9运行,点你连接的服务器,这时会中断下来,然后看他启动的文件和参数,先在启动文件创建个快捷方式,然后把参数填到快捷方式里,以后直接运行这个快捷方式即可。
    od取实际的游戏执行文件,然后在命令行输入bp sendbp recvbp WSASend, bp WSARecv四个命令,再点od的调试-》参数,把刚才记录的参数写进去。按CTRL+F2重新取。按F9运行。随便输入个帐号和密码点进入,这时会中断到send或者WSASend,按CTRL+F9即可回到游戏领域,然后像上看,加密可能就在这上面。首先我们来判断这个函数的开始,如何判断函数入口那,就是看PUSH语句,od一般把一个函数用蓝线扩起来了,比较容易分清。看函数入口到调用sendWSASend前面有没有call语句,如果有我们需要跟进去,如果发现离函数入口很近而且没有call语句那直接按CTRL+F9再回上一个领域,照这样的方法就可以找到加密算法了。其实很简单。
    接下来是recvWSARecv,首先讲recv,中断这里后我们在堆栈窗口右键点buffer,然后选内存中显示。再按CTRL+F9,发现内存中有了接收的数据,然后在内存的第一个字节右键点读硬件中断,继续按F9运行吧,很快会中断到读取内存的地方,很简单这里有可能就是加密的地方,如果看不出来那么在内存第5个字节同样做读硬件内存中断,很快就能找到解密地方。
    WSARecv
    buffer不一样,他是个缓存指针,如果中断到这里我们一样在堆栈窗口右键点buffer,然后选内存中显示。这是个结构,前4个是buffer大小,后面的才是数据,按CTRL+F9,发现接收数据,我们直接在第5个地方下读硬件中断,以后就跟recv一样了。呵呵一切都不难,只要会汇编就ok
    找写屏函数的方法
    我用si来分析的,因为od我不知道怎么搜索内存。。。
    在游戏的交谈栏里写一句话,先不要发送,然后用sis命令搜索你要发的这句话比如话是我要搜索的内存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 ??????
    }
    一般我都做利用recvWSARecv函数返回失败后做这些功能。
    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了。
  • 相关阅读:
    LeetCode Single Number
    Leetcode Populating Next Right Pointers in Each Node
    LeetCode Permutations
    Leetcode Sum Root to Leaf Numbers
    LeetCode Candy
    LeetCode Sort List
    LeetCode Remove Duplicates from Sorted List II
    LeetCode Remove Duplicates from Sorted List
    spring MVC HandlerInterceptorAdapter
    yum
  • 原文地址:https://www.cnblogs.com/tyjsjl/p/2156133.html
Copyright © 2011-2022 走看看