新书推介:《语义网技术体系》
作者:瞿裕忠,胡伟,程龚
   XML论坛     W3CHINA.ORG讨论区     计算机科学论坛     SOAChina论坛     Blog     开放翻译计划     新浪微博  
 
  • 首页
  • 登录
  • 注册
  • 软件下载
  • 资料下载
  • 核心成员
  • 帮助
  •   Add to Google

    >> 本版讨论高级C/C++编程、代码重构(Refactoring)、极限编程(XP)、泛型编程等话题
    [返回] 计算机科学论坛计算机技术与应用『 C/C++编程思想 』 → 游戏外挂的编写原理 查看新帖用户列表

      发表一个新主题  发表一个新投票  回复主题  (订阅本版) 您是本帖的第 8335 个阅读者浏览上一篇主题  刷新本主题   平板显示贴子 浏览下一篇主题
     * 贴子主题: 游戏外挂的编写原理 举报  打印  推荐  IE收藏夹 
       本主题类别:     
     卷积内核 帅哥哟,离线,有人找我吗?
      
      
      威望:8
      头衔:总统
      等级:博士二年级(版主)
      文章:3942
      积分:27590
      门派:XML.ORG.CN
      注册:2004/7/21

    姓名:(无权查看)
    城市:(无权查看)
    院校:(无权查看)
    给卷积内核发送一个短消息 把卷积内核加入好友 查看卷积内核的个人资料 搜索卷积内核在『 C/C++编程思想 』的所有贴子 访问卷积内核的主页 引用回复这个贴子 回复这个贴子 查看卷积内核的博客楼主
    发贴心情 

    (3)、注入外挂代码进入被挂游戏进程中

      完成了定位和修改程序中调用API函数代码后,我们就可以随意设计自定义的API函
    数的替代函数了。做完这一切

    后,还需要将这些代码注入到被外挂游戏程序进程内存空间中,不然游戏进程根本不会
    访问到替代函数代码。注入方

    法有很多,如利用全局钩子注入、利用注册表注入挡截User32库中的API函数、利用
    CreateRemoteThread注入(仅限于

    NT/2000)、利用BHO注入等。因为我们在动作模拟技术一节已经接触过全局钩子,我相
    信聪明的读者已经完全掌握了

    全局钩子的制作过程,所以我们在后面的实例中,将继续利用这个全局钩子。至于其它
    几种注入方法,如果感兴趣可

    参阅MSDN有关内容。

      有了以上理论基础,我们下面就开始制作一个挡截MessageBoxA和recv函数的实
    例,在开发游戏外挂程序 时,可

    以此实例为框架,加入相应的替代函数和处理代码即可。此实例的开发过程如下:

      (1) 打开前面创建的ActiveKey项目。

      (2) 在ActiveKey.h文件中加入HOOKAPI结构,此结构用来存储被挡截API函数名
    称、原API函数地址和替代函数地

    址。

       typedef struct tag_HOOKAPI
       {
       LPCSTR szFunc;//被HOOK的API函数名称。
       PROC pNewProc;//替代函数地址。
       PROC pOldProc;//原API函数地址。
       }HOOKAPI, *LPHOOKAPI;

      (3) 打开ActiveKey.cpp文件,首先加入一个函数,用于定位输入库在输入数据段
    中的IAT地址。代码如下:

       extern "C" __declspec(dllexport)PIMAGE_IMPORT_DESCRIPTOR
       LocationIAT(HMODULE hModule, LPCSTR szImportMod)
       //其中,hModule为进程模块句柄;szImportMod为输入库名称。
       {
       //检查是否为DOS程序,如是返回NULL,因DOS程序没有IAT。
       PIMAGE_DOS_HEADER pDOSHeader = (PIMAGE_DOS_HEADER) hModule;
       if(pDOSHeader->e_magic != IMAGE_DOS_SIGNATURE) return NULL;
        //检查是否为NT标志,否则返回NULL。
        PIMAGE_NT_HEADERS pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pDOSHeader+
    (DWORD)(pDOSHeader-

    >e_lfanew));
        if(pNTHeader->Signature != IMAGE_NT_SIGNATURE) return NULL;
        //没有IAT表则返回NULL。
        
    if(pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Vir
    tualAddress == 0)

    return NULL;
        //定位第一个IAT位置。
        PIMAGE_IMPORT_DESCRIPTOR pImportDesc =
    (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)pDOSHeader + (DWORD)

    (pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Virtu
    alAddress));
        //根据输入库名称循环检查所有的IAT,如匹配则返回该IAT地址,否则检测下
    一个IAT。
        while (pImportDesc->Name)
        {
         //获取该IAT描述的输入库名称。
       PSTR szCurrMod = (PSTR)((DWORD)pDOSHeader +
    (DWORD)(pImportDesc->Name));
       if (stricmp(szCurrMod, szImportMod) == 0) break;
       pImportDesc++;
        }
        if(pImportDesc->Name == NULL) return NULL;
       return pImportDesc;
       }

      再加入一个函数,用来定位被挡截API函数的IAT项并修改其内容为替代函数地址。
    代码如下:

       extern "C" __declspec(dllexport)
       HookAPIByName( HMODULE hModule, LPCSTR szImportMod, LPHOOKAPI
    pHookApi)
       //其中,hModule为进程模块句柄;szImportMod为输入库名称;pHookAPI为
    HOOKAPI结构指针。
       {
        //定位szImportMod输入库在输入数据段中的IAT地址。
        PIMAGE_IMPORT_DESCRIPTOR pImportDesc = LocationIAT(hModule,
    szImportMod);
      if (pImportDesc == NULL) return FALSE;
        //第一个Thunk地址。
        PIMAGE_THUNK_DATA pOrigThunk = (PIMAGE_THUNK_DATA)((DWORD)hModule +
    (DWORD)(pImportDesc-

    >OriginalFirstThunk));
       //第一个IAT项的Thunk地址。
        PIMAGE_THUNK_DATA pRealThunk = (PIMAGE_THUNK_DATA)((DWORD)hModule +
    (DWORD)(pImportDesc-

    >FirstThunk));
        //循环查找被截API函数的IAT项,并使用替代函数地址修改其值。
       while(pOrigThunk->u1.Function)
    {
     //检测此Thunk是否为IAT项。
    if((pOrigThunk->u1.Ordinal & IMAGE_ORDINAL_FLAG) != IMAGE_ORDINAL_FLAG)
    {
      //获取此IAT项所描述的函数名称。
     PIMAGE_IMPORT_BY_NAME pByName
    =(PIMAGE_IMPORT_BY_NAME)((DWORD)hModule+(DWORD)(pOrigThunk-

    >u1.AddressOfData));
     if(pByName->Name[0] == \0) return FALSE;
      //检测是否为挡截函数。
    if(strcmpi(pHookApi->szFunc, (char*)pByName->Name) == 0)
      {
           MEMORY_BASIC_INFORMATION mbi_thunk;
           //查询修改页的信息。
           VirtualQuery(pRealThunk, &mbi_thunk,
    sizeof(MEMORY_BASIC_INFORMATION));
    //改变修改页保护属性为PAGE_READWRITE。
           VirtualProtect(mbi_thunk.BaseAddress,mbi_thunk.RegionSize,
    PAGE_READWRITE,

    &mbi_thunk.Protect);
    //保存原来的API函数地址。
          if(pHookApi->pOldProc == NULL)
    pHookApi->pOldProc = (PROC)pRealThunk->u1.Function;
      //修改API函数IAT项内容为替代函数地址。
    pRealThunk->u1.Function = (PDWORD)pHookApi->pNewProc;
    //恢复修改页保护属性。
    DWORD dwOldProtect;
           VirtualProtect(mbi_thunk.BaseAddress, mbi_thunk.RegionSize,
    mbi_thunk.Protect,

    &dwOldProtect);
          }
    }
      pOrigThunk++;
      pRealThunk++;
    }
      SetLastError(ERROR_SUCCESS); //设置错误为ERROR_SUCCESS,表示成功。
      return TRUE;
       }

      (4) 定义替代函数,此实例中只给MessageBoxA和recv两个API进行挡截。代码如下

       static int WINAPI MessageBoxA1 (HWND hWnd , LPCTSTR lpText, LPCTSTR
    lpCaption, UINT uType)
       {
        //过滤掉原MessageBoxA的正文和标题内容,只显示如下内容。
    return MessageBox(hWnd, "Hook API OK!", "Hook API", uType);
       }
       static int WINAPI recv1(SOCKET s, char FAR *buf, int len, int flags )
       {
       //此处可以挡截游戏服务器发送来的网络数据包,可以加入分析和处理数据代
    码。
       return recv(s,buf,len,flags);
       }

      (5) 在KeyboardProc函数中加入激活挡截API代码,在if( wParam == 0X79 )语句
    中后面加入如下else if语句:

       ......
       //当激活F11键时,启动挡截API函数功能。
       else if( wParam == 0x7A )
       {
        HOOKAPI api[2];
    api[0].szFunc ="MessageBoxA";//设置被挡截函数的名称。
    api[0].pNewProc = (PROC)MessageBoxA1;//设置替代函数的地址。
    api[1].szFunc ="recv";//设置被挡截函数的名称。
    api[1].pNewProc = (PROC)recv1; //设置替代函数的地址。
    //设置挡截User32.dll库中的MessageBoxA函数。
    HookAPIByName(GetModuleHandle(NULL),"User32.dll",&api[0]);
    //设置挡截Wsock32.dll库中的recv函数。
    HookAPIByName(GetModuleHandle(NULL),"Wsock32.dll",&api[1]);
       }
       ......

    ----------------------------------------------
    事业是国家的,荣誉是单位的,成绩是领导的,工资是老婆的,财产是孩子的,错误是自己的。

    点击查看用户来源及管理<br>发贴IP:*.*.*.* 2008/12/12 10:06:00
     
     GoogleAdSense
      
      
      等级:大一新生
      文章:1
      积分:50
      门派:无门无派
      院校:未填写
      注册:2007-01-01
    给Google AdSense发送一个短消息 把Google AdSense加入好友 查看Google AdSense的个人资料 搜索Google AdSense在『 C/C++编程思想 』的所有贴子 访问Google AdSense的主页 引用回复这个贴子 回复这个贴子 查看Google AdSense的博客广告
    2024/6/16 15:53:37

    本主题贴数10,分页: [1]

     *树形目录 (最近20个回帖) 顶端 
    主题:  游戏外挂的编写原理(4100字) - 卷积内核,2008年12月12日
        回复:  哦版主好强啊我顶(20字) - 秋十三,2009年1月21日
        回复:  非常感谢~~说Lua是一门写游戏脚本很好的语言所以鄙人开了一论坛~~~新论坛开张啦~~..(212字) - liuyuanyang,2009年1月16日
        回复:  谢谢了,刚做出一个 哈哈~(24字) - gtict,2008年12月31日
        回复:  能不能上传几个实例....(22字) - gtict,2008年12月31日
        回复:  (6) 在ActiveKey.cpp中加入头文件声明 "#include "wsock32.h..(621字) - 卷积内核,2008年12月12日
        回复:  (3)、注入外挂代码进入被挂游戏进程中  完成了定位和修改程序中调用API函数代码后,我们..(6195字) - 卷积内核,2008年12月12日
        回复:  (5)、从“工程”菜单中选择“设置”,弹出Project Setting对话框,选择Link标..(6941字) - 卷积内核,2008年12月12日
        回复:  (5).将ActiveKey项目中的ActiveKey.h头文件加入到Simulate项目中,..(7829字) - 卷积内核,2008年12月12日
        回复:  四、动作模拟技术  我们在前面介绍过,几乎所有的游戏都有大量繁琐和无聊的攻击动作以增加..(8867字) - 卷积内核,2008年12月12日

    W3C Contributing Supporter! W 3 C h i n a ( since 2003 ) 旗 下 站 点
    苏ICP备05006046号《全国人大常委会关于维护互联网安全的决定》《计算机信息网络国际联网安全保护管理办法》
    50.781ms