协议型外挂制作教程三

声明:
以下内容是我以前的blog中的,均是早期的内容,只是简单的搬到这里,留个纪念。关于信息安全方面,很早之前已经不再深入,因此,请勿联系。

协议型外挂制作教程三

By rix
上一节中我们说了外挂平台的搭建,我们做完了Hook.dll和wg.exe,但如何让wg.exe调用Hook.dll中的函数,可以自己查看CB的教程或者到网上查查。在这里我是在wg.exe的Hook按钮事件中添加了下面的语句来实现:

if(gamethreadid)

{

if(EnableHook(gamethreadid)==false)

ShowMessage("error");

}

你可以点击Hook按钮在游戏界面出来之后,然后在游戏界面中按F12键调出外挂的窗口。

昨天的尾巴完事之后,开始今天的教程。今天我想说说APIHOOK。虽然APIHOOK在大话游戏的外挂制作中不是必须的,但为了按照一般的制作流程顺序,就先将这部分加入到里面去了。

使用APIHOOK的原因也很简单,游戏肯定要调用某些系统函数,使用APIHOOK可以简单的查看一些关键的信息并进行修改(就这么简单的理由?是的,我们一向再用杀牛的刀宰鸡的。。。)。

Jeffrey Richter用了大量的篇幅来讲如何插入DLL和挂接API,如果你不知道Jeffrey Richter是谁的话,总该知道《Windows核心编程》的作者吧,如果不知道,我倒,系统抛出例外,你是外星人吧。我们的程序运行在用户层上,J。R提出了两种办法,一种是改写代码,我刚开始也试图用这种办法,后来发现这种办法确实存在的漏洞多多,和J。R说的一样。最后还是采用操作模块的输入节了。

在查看资料的过程中,我发现J。R的代码在中文Windows 2000上并不能运行(难道是外国人用的系统和中国的不一样?),后来只好J。R的思路,重新安排了一下函数,但大部分函数都一样的。为了方便,我没有在类中捕获LoadLibraryA、LoadLibraryW、LoadLibraryExA和LoadLibraryExW,也是因为我们的外挂程序运行的时候游戏的窗口已经出来了,该加载的一般都加载了。

下面是我的APIHOOK类的源代码,该源代码是根据J.R的思路重新整理他的源代码来的:

       /*HookAPI.h*/

#include "windows.h"

 

class CAPIHOOK

{

public:

CAPIHOOK(PSTR pszCalleeModName,PSTR pszFuncName,PROC pfnHook,HANDLE prochandle,HMODULE hmod);

~CAPIHOOK();

operator PROC(){return (m_pfnOrig);};

public:

static PVOID sm_pvMaxAppAddr;

 static CAPIHOOK* sm_pHead;

CAPIHOOK* m_pNext;

PCSTR m_pszCalleeModName;

PCSTR m_pszFuncName;

PROC m_pfnOrig;

PROC m_pfnHook;

BOOL m_fExcludeAPIHookMod;

HMODULE m_module;

HANDLE m_handle;

private:

pfnOrig,PROC pfnHook,BOOL fExcludeAPIHookMod);

 void WINAPI ReplaceIATEntryInOneMod(PCSTR pszCalleeModName,PROC pfnOrig,PROC pfnHook,HMODULE hmodcaller,HANDLE handle);

 void WINAPI FixupNewlyLoadedModule(HMODULE hmod,DWORD dwFlags);

FARPROC WINAPI GetProcAddress(HMODULE hmod,PCSTR pszProcName);

};

 

 

/*HookApi.cpp*/

 

 

#include "hookapi.h"

#include 

#include "imagehlp.h"

PVOID CAPIHOOK::sm_pvMaxAppAddr = NULL;

const BYTE cPushOpCode = 0x68;

CAPIHOOK *CAPIHOOK::sm_pHead = NULL;

 

 

CAPIHOOK::CAPIHOOK(PSTR pszCalleeModName, PSTR pszFuncName, PROC pfnHook,

  HANDLE prochandle, HMODULE hmod)

{

  m_handle = prochandle;

  if (sm_pvMaxAppAddr == NULL)

  {

    SYSTEM_INFO si;

    GetSystemInfo(&si);

    sm_pvMaxAppAddr = si.lpMaximumApplicationAddress;

  }

  m_pNext = sm_pHead;

  sm_pHead = this;

  m_pszCalleeModName = pszCalleeModName;

  m_pszFuncName = pszFuncName;

  m_pfnHook = pfnHook;

  m_pfnOrig = ::GetProcAddress(GetModuleHandleA(pszCalleeModName),

    m_pszFuncName);

  assert(m_pfnOrig != NULL);

  if (m_pfnOrig == NULL)

  {

return;

  }

  if (m_pfnOrig > sm_pvMaxAppAddr)

  {

    PBYTE pb = (PBYTE)m_pfnOrig;

    if (pb[0] == cPushOpCode)

    {

      PVOID pv = *(PVOID*) &pb[1];

      m_pfnOrig = (PROC)pv;

    }

  }

  m_module = GetModuleHandle(pszCalleeModName);

  ReplaceIATEntryInOneMod(m_pszCalleeModName, m_pfnOrig, m_pfnHook, m_module,

    prochandle);

}

 

CAPIHOOK::~CAPIHOOK()

{

  ReplaceIATEntryInOneMod(m_pszCalleeModName, m_pfnHook, m_pfnOrig, m_module,

    m_handle);

  CAPIHOOK *p = sm_pHead;

  if (p == this)

  {

    sm_pHead = p->m_pNext;

  }

  else

  {

    BOOL fFound = FALSE;

    for (; !fFound && (p->m_pNext != NULL); p = p->m_pNext)

    {

      if (p->m_pNext == this)

      {

        p->m_pNext = p->m_pNext->m_pNext;

        break;

      }

    }

    assert(fFound);

  }

}

 

void WINAPI CAPIHOOK::FixupNewlyLoadedModule(HMODULE hmod, DWORD dwFlags)

{

  if ((hmod != NULL) && ((dwFlags &LOAD_LIBRARY_AS_DATAFILE) == 0))

  {

    for (CAPIHOOK *p = sm_pHead; p != NULL; p = p->m_pNext)

    {

      ReplaceIATEntryInOneMod(p->m_pszCalleeModName, p->m_pfnOrig, p->m_pfnHook,

        hmod, m_handle);

    }

  }

}

 

FARPROC WINAPI CAPIHOOK::GetProcAddress(HMODULE hmod, PCSTR pszProcName)

{

  FARPROC pfn = ::GetProcAddress(hmod, pszProcName);

  CAPIHOOK *p = sm_pHead;

  for (; (pfn != NULL) && (p != NULL); p = p->m_pNext)

  {

    if (pfn == p->m_pfnOrig)

    {

      pfn = p->m_pfnHook;

      break;

    }

  }

  return (pfn);

}

 

void WINAPI CAPIHOOK::ReplaceIATEntryInOneMod(PCSTR pszCalleeModName, PROC

  pfnCurrent, PROC pfnHook, HMODULE hmodcaller, HANDLE handle)

{

  ULONG ulSize;

 

  PIMAGE_IMPORT_DESCRIPTOR pImportDesc = (PIMAGE_IMPORT_DESCRIPTOR)

    ImageDirectoryEntryToData(hmodcaller, TRUE, IMAGE_DIRECTORY_ENTRY_IMPORT,

    &ulSize);

  if (pImportDesc == NULL)

  {

   

    return ;

  }

 

  for (; pImportDesc->Name; pImportDesc++)

  {

    PSTR pszModName = (PSTR)((PBYTE)hmodcaller + pImportDesc->Name);

    if (lstrcmpiA(pszModName, pszCalleeModName) == 0)

    {

     

      break;

    }

  }

  if (pImportDesc->Name == 0)

  {

   

    return ;

  }

  PIMAGE_THUNK_DATA pThunk = (PIMAGE_THUNK_DATA)((PBYTE)hmodcaller +

    pImportDesc->FirstThunk);

  for (; pThunk->u1.Function; pThunk++)

  {

    PROC *ppfn = (PROC*) &pThunk->u1.Function;

    BOOL fFound = (*ppfn == pfnCurrent);

    if (!fFound && (*ppfn > sm_pvMaxAppAddr))

    {

      PBYTE pbInFunc = (PBYTE) *ppfn;

      if (pbInFunc[0] == cPushOpCode)

      {

        ppfn = (PROC*) &pbInFunc[1];

        fFound = (*ppfn == pfnCurrent);

      }

    }

    if (fFound)

    {

      HANDLE handle1 = OpenProcess(PROCESS_ALL_ACCESS, FALSE,

        GetCurrentProcessId());

      DWORD dwIdOld;

      VirtualProtectEx(handle1, ppfn, sizeof(pfnHook), PAGE_READWRITE, &dwIdOld)

        ;

 

      if (WriteProcessMemory(handle1, ppfn, &pfnHook, sizeof(pfnHook), NULL) ==

        false)

      {

       

        return ;

      }

      else

      {

        VirtualProtectEx(handle1, ppfn, sizeof(pfnHook), dwIdOld, &dwIdOld);

        return ;

      }

    }

  }

}

上面是APIHOOK的完整代码。下面是使用的例子(拦截WString2ID函数):

typedef unsigned long(__stdcall *WString2ID)(char const*);

unsigned long __stdcall myWString2ID(char const*);

CAPIHOOK *My_WString2ID;

My_WString2ID = new CAPIHOOK("windsoul.dll", "?WString2ID@@YGKPBD@Z",

          (PROC)myWString2ID, gamehandle, gameInstance);

自己的myWString2ID的实现:

unsigned long __stdcall myWString2ID(char const *a)

{

  // SendMessage(wghandle,WM_USER+1,(WPARAM)a,NULL);

   return (((WString2ID)My_WString2ID->m_pfnOrig)(a));

}

下面是用来拦截游戏的WndProc函数的,当时写的时候为了全面,至于如何去用,随便自己了,反正我没有用。

gamehWnd = GetActiveWindow();

        gamehandle =GetCurrentProcess();

        gameInstance = (HINSTANCE)GetWindowLong(gamehWnd, GWL_HINSTANCE);

gameproc = (WNDPROC)SetWindowLong(gamehWnd, GWL_WNDPROC, (LONG)

          MyMsgProc);

自己用来替换游戏的WndProc函数:

LRESULT APIENTRY MyMsgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM

  lParam)

{

/*在这里做自己想做的事情,剩下的让游戏的WndProc来处理*/

  return CallWindowProc(gameproc, hwnd, message, wParam, lParam);

}

这一节到这里就结束了,下一节开始游戏程序的研究。最好准备大话客户端9.16更新之前的最后一个版本,不使用最新的版本有下面的原因:

1、 如果对现在客户端作过多的透漏的话,将会发现做盗号类的程序比做外挂要简单,这不是我所希望看到的。

2、 新版本采用的加密办法(双精度浮点数加密)在讲解上非常的麻烦,不是一般人容易入门的,但解决的办法和9.16之前的版本一样,只是繁琐而以。

3、 脱壳后的程序有更多的需要人工识别的部分,这会造成不必要的麻烦,免得误导大家。

本文章只贴于www.csdn.net和www.gameres.com 的Blog上面,请勿将文章用于任何商业场合,如果因为本教程引起其他的后果的话,则与本人无关,本人只讲技术实现。如果要转贴的话,请注明出处,如果有疑问或者商议的话,请发E-Mail到zeze0556@sina.com或者QQ:23033206留言,MSN:zeze0556@msn.com。另外请勿给我信箱发垃圾邮件,在添加好友的时候一定要写好附言,我已经被莫名其妙的广告信件和流言蜚语吓得没有胆了。算是我求各位大虾了

发布日期:
分类:程序 标签:

发表评论

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据