博客首页 注册 建议与交流 排行榜 加入友情链接
推荐 投诉 搜索: 帮助

Satan.Scorpio 上善若水

Google

sasc.cublog.cn


在不破解NP前提下读写游戏内存(转)

在上一篇文章《反NP监视原理》中说到要去掉NP的注入是很容易的事,但是去掉npggNT.des并不是说我们想对游戏怎么样都可以了,NP还挂钩了很多内核函数,所以很多关键系统函数就算我们在用户层能用也对游戏没有什么效果。

   如果我们想在不破解NP前提下读写游戏内存该怎么办呢,我想办法至少有两个

一、用驱动

    在驱动下读写游戏内存是没问题,但是由于我不懂驱动,所以也没什么可说。

 

二、进入游戏进程

    在用户层,如果我们想在不破解NP的前提下读写游戏内存的话,大概就只能进入游戏进程了。因为很简单,我们的程序无法对游戏使用OpenProcessReadProcessMemoeryWriteProcessMemory这些函数(就算是去掉了NP监视模块npggNT.des),而NP又不可能限制游戏自身使用这些函数,所以只要我们能够进入游戏进程就能够读写游戏的内存。怎么进入游戏呢?下面介绍两种方法:

 

    1,最简单的办法 —全局消息钩子(WH_GETMESSAGE)

      看似很复杂的东西原来很简单就可以实现,大道至易啊。使用消息钩子进入游戏进程无疑是最简单的一种方法,具体编程大概象这样:

一个消息钩子的DLL里面包含一个消息回调函数(什么都不用做),读写内存过程,跟主程序通讯过程或操作界面过程,当然在DLL_PROCESS_ATTACH要判断当前的进程是不是游戏的,是的话就做相应的处理;一个安装全局消息钩子的主程序。大概这样就可以了。使用全局消息钩子的好处是简单易用,但是不足之处是要在游戏完全启动(NP当然也启动啦)后才能进入,如果想在NP启动前做一些什么事的话是不可能的。

     另外也简单介绍一下防全局钩子的办法,Windows是通过调用LoadLibraryExW来向目标进程注入钩子DLL的,所以只要我们在钩子安装前挂钩了这个函数,全局钩子就干扰不了了。

 

     2,更麻烦的办法 远程注入

       知道远程注入方法和原理的人可能会说“有没有搞错,OpenProcessWriteProcessMemory这些必备函数都不能用,怎么注入?,当然啦,NP启动后是不能干这些事情,所以我们要在NP启动前完成。这样一来,时机就很重要了。

       游戏启动的流程大概是这样:游戏Main->GameGuard.des->GameMon.desNP进程)。这里的做法是这样:游戏Main->GameGuard.des(暂停)>注入DLL>GameGuard.des(继续)->GameMon.des。关键点就是让GameGuard.des暂停,有什么办法?我想到一个是全局消息钩子(还是少不了它啊)。要实现大概需要做下面的工作:

1.       一个全局消息钩子DLL,里面只要一个消息回调函数(什么都不用做),

2.       DLL_PROCESS_ATTACH下进行当前进程判断找GameGuard.des,

3.       找到的话就向主程序SendMessage;主程序,负责安装钩子,接收钩子DLL发来的消息,

4.       接收到消息就开始查找游戏进程,

5.       向游戏进程注入内存操作DLL,返回给SendMessageGameGuard.des继续,

6.       卸载钩子(免得它继续钩来钩去);

7.       内存操作DLL,负责对游戏内存进行操作。

        具体编写如下(有省略):

////////////////////////////////////////////////GameHook.cpp//////////////////////////////////////////////////////////////////

BOOL IsGameGuard();

//////////////////////////////////

LRESULT CALLBACK GetMsgProc(int nCode,WPARAM wParam,LPARAM lParam)

{

  return (CallNextHookEx(m_hHook,nCode,wParam,lParam));//什么都不需要做

}

///////////////////////////////////////

BOOL WINAPI DllMain(HINSTANCE hInst,DWORD dwReason,LPVOID lp)

{

  switch(dwReason){

  case DLL_PROCESS_ATTACH:   

    if(IsGameGuard())//判断当前进程是不是GameGuard.des

       SendMessage(m_hwndRecv,WM_HOOK_IN_GAMEGUARD,NULL,NULL);//向主窗体发送消息,SendMessage是等待接受窗体处理完毕才返回的,

    break;                               //所以进程就暂停在这里,我们有足够的时间去做事情

  case DLL_PROCESS_DETACH:

    break;

  }

  return TRUE;

}

///////////////////////////////////

GAMEHOOKAPI BOOL SetGameHook(BOOL fInstall,HWND hwnd)

{

  ...

}

////////////////////////////////////////

BOOL IsGameGuard()

{

       TCHAR  szFileName[256];

       GetModuleFileName(NULL,szFileName,256);

       if(strstr(szFileName,"GameGuard.des")!=NULL)

{//这样的判断严格来说是有问题的,但实际操作也够用了。当然也可以进行更严格的判断,不过麻烦点

          return TRUE;

       }

  return FALSE;

}

 

 

 

//////////////////////////////////////////////////////Main////////////////////////////////////////////////////////////////////////

void OnGameGuard(WPARAM wParam,LPARAM lParam)//处理消息钩子DLL发来的消息就是上面SendMessage的那个

{ 

  DWORD dwProcessId=FindGameProcess(m_strGameName);//开始查找游戏进程

  if(dwProcessId==0){

    MessageBox(m_hWnd,"没有找到游戏进程","查找游戏进程",MB_OK);

    return;

  } 

 

    if(!InjectDll(dwProcessId)){//查找到就开始注入

    MessageBox(m_hWnd,"向游戏进程注入失败",注入",MB_OK);

    return;

     }

}

/////////////////////////////////////////////////

DWORD FindGameProcess(LPCSTR szGameName)//负责查找游戏进程

{

  HANDLE hSnapshot=CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);

  if(hSnapshot==INVALID_HANDLE_VALUE)

    return 0;

  PROCESSENTRY32 pe={sizeof(pe)};

  DWORD dwProcessID=0;

  for(

BOOL fOK=Process32First(hSnapshot,&pe);

fOK;

fOK=Process32Next(hSnapshot,&pe)

)

 

{

    if(lstrcmpi(szGameName,pe.szExeFile)==0){

      dwProcessID=pe.th32ProcessID;

      break;

    }

  }

  CloseHandle(hSnapshot);

  return dwProcessID;

}

/////////////////////////////////////////////////

BOOL InjectDll(DWORD dwProcessId)//负责注入,参考自Jeffrey Richterwindows核心编程》

{

  CString strText;

  char* szLibFileRemote=NULL;

 

  HANDLE hProcess=OpenProcess(PROCESS_CREATE_THREAD|PROCESS_VM_OPERATION|PROCESS_VM_WRITE,FALSE,dwProcessId);

  if(hProcess==NULL){

  //  SetRecord("Open game process failed!");              

    return FALSE;

  }

  int cch=lstrlen(szDll)+1;

  int cb=cch*sizeof(char);

  szLibFileRemote=(char*)VirtualAllocEx(hProcess,NULL,cb,MEM_COMMIT,PAGE_READWRITE);

  if(szLibFileRemote==NULL){

  //  SetRecord("Alloc memory to game process failed!");

    CloseHandle(hProcess);

    return FALSE;

  }

 

  if(!WriteProcessMemory(hProcess,(LPVOID)szLibFileRemote,(LPVOID)szDll,cb,NULL)){

  //  SetRecord("Write game process memory failed!");

    CloseHandle(hProcess);

    return FALSE;

  }

 

  PTHREAD_START_ROUTINE pfnThreadRtn=(PTHREAD_START_ROUTINE)

     GetProcAddress(GetModuleHandle(TEXT("kernel32")),"LoadLibraryA");

  if(pfnThreadRtn==NULL){

  //  SetRecord("Alloc memory to game process failed!");

    CloseHandle(hProcess);

    return FALSE;

  }

 

  HANDLE hThread=CreateRemoteThread(hProcess,NULL,0,pfnThreadRtn, szLibFileRemote,0,NULL);

   if(!hThread)

   {

    //    SetRecord("Create remote thread failed!");

    CloseHandle(hProcess);

   return FALSE;

   }     

   if(hThread!=NULL)

     CloseHandle(hThread); 

   CloseHandle(hProcess);

    return TRUE;

 

}    

 

 

///////////////////////////操作游戏内存的DLL就不贴了,大家根据不同的需要各显神通吧///////////////////////////////////////////////////     

     

       这种方法比一个全局消息钩子麻烦一点,但是优点是显然易见的:可以在NP启动前做事情,比如HOOK游戏函数或做游戏内存补丁。下面进入NP进程还要用到这种方法。

 

三、进入NP进程

    如果我们对NP有足够的了解,想对它内存补丁一下,来做一些事情,哪又怎样才可以进入NP的进程呢?嗯,我们知道游戏启动流程是这样的游戏Main->GameGuard.des->GameMon.desNP进程),其中GameGuard.desGameMon.des进程是游戏Main通过调用函数CreateProcessA来创建的,上面我们说到有办法在NP进程(GameMon.des)启动前将我们的DLL注入到游戏进程里,因此我们可以在GameMon.des启动前挂钩(HOOKCreateProcessA,游戏创建NP进程时让NP暂停,但是游戏本来创建NP进程时就是让它先暂停的,这步我们可以省了。下面是游戏启动NP(版本900)时传递的参数

 

      ApplicationName:C:\惊天动地Cabal Online\GameGuard\GameMon.des

      CommandLine:\x01\x58\x6d\xae\x99\x55\x57\x5d\x49\xbe\xe4\xe1\x9b\x14\xe6\x88\x57\x68\x6d\x11\xb9\x36\x73\x38\x71\x1e\x88\x46\xa9\x97\xd4\x3a\x20\x90\x62\xae\x15\xcd\x4b\xcd\x72\x82\xbd\x75\x0a\x54\xf0\xcc\x01\xad

      CreationFlags:4

      Directory:

      其中的CommandLine好长啊,它要传递的参数是:一个被保护进程的pid,两个EventHandle,以及当前timeGetTime的毫秒数 (感谢JTR分享)

      CreationFlags:4 查查winbase.h头文件,发现#define CREATE_SUSPENDED  0x00000004,所以NP进程创建时就是暂停的   

      在我们替换的CreateProcessA中,先让游戏创建NP进程(由于游戏创建时NP进程本来就是暂停的,所以不用担心NP的问题),让游戏进程暂停(SendMessage就可以了),然后再向NP进程注入DLL,最后让游戏进程继续。这样我们的DLL就进入NP进程了。实现起来大概是这样子

BOOL

WINAPI

MyCreateProcessA(//替换原来的CreateProcessA

    LPCSTR lpApplicationName,

    LPSTR lpCommandLine,

    LPSECURITY_ATTRIBUTES lpProcessAttributes,

    LPSECURITY_ATTRIBUTES lpThreadAttributes,

    BOOL bInheritHandles,

    DWORD dwCreationFlags,

    LPVOID lpEnvironment,

    LPCSTR lpCurrentDirectory,

    LPSTARTUPINFOA lpStartupInfo,

    LPPROCESS_INFORMATION lpProcessInformation

 

 

 

    )

{

  UnhookCreateProcessA();

  BOOL fRet=CreateProcessA(lpApplicationName,lpCommandLine,lpProcessAttributes,lpThreadAttributes,bInheritHandles,dwCreationFlags,

     lpEnvironment,lpCurrentDirectory,lpStartupInfo,lpProcessInformation); 

  RehookCreateProcessA();

        SendMessage(hwndRecv,//负责注入的窗体句柄

                   WM_HOOK_NP_CREATE,//自定义消息

                   (WPARAM)lpProcessInformation->dwProcessId,//NP进程ID传给负责注入的主窗体

                   NULL);

  return fRet;

}

 

四、注意问题

由于我们是在不破解NP的前提下对游戏内存进行操作,所以一不小心的话,很容易就死游戏。NP保护了游戏进程的代码段,所以在NP启动后就不要再对其代码段进行修改,要补丁或HOOK系统函数这些都要在NP启动前完成。当然读写游戏的数据段是没问题的,因为游戏本身也不断进行这样的操作。

 

 

五、在DLL建立窗体。

 

HINSTANCE   hinst   =   NULL;    

  HWND   hwndDlg   =   NULL;  

   

  BOOL   CALLBACK   DlgProc(HWND   hDlg,   UINT   message,WPARAM   wParam,   LPARAM   lParam);  

   

  BOOL   APIENTRY   DllMain(   HANDLE   hModule,    

                                                DWORD     ul_reason_for_call,    

                                                LPVOID   lpReserved   )  

  {  

          switch   (ul_reason_for_call)  

  {  

  case   DLL_PROCESS_ATTACH:  

                                              hinst   =   (HINSTANCE)hModule;  

                                              hwndDlg   =   CreateDialog(hinst,MAKEINTRESOURCEIDD_DLG_SHOW),  

                                                                                            NULL,(DLGPROC)DlgProc);  

                                              if(hwndDlg   ==   NULL)  

                                              {  

                                                        ShowError("CreateDlg   Error!",   GetLastError());   

                                                        return   1;  

                                              }  

                                              ShowWindow(hwndDlg,   SW_SHOW);  

   

     

  case   DLL_THREAD_ATTACH:  

  case   DLL_THREAD_DETACH:  

  case   DLL_PROCESS_DETACH:  

  break;  

          }  

          return   TRUE;  

  }  

   

  BOOL   CALLBACK   DlgProc(HWND   hDlg,   UINT   message,WPARAM   wParam,   LPARAM   lParam)  

  {  

          switch(message)  

          {    

                case   WM_INITDIALOG:  

                              return   TRUE;  

                case   WM_COMMAND:  

                              ......  

                              return   TRUE;    

                case   WM_...  

                              ...  

                 

                case   WM_CLOSE:  

                              hwndDlg   =   NULL;  

                              return   TRUE;  

          }  

   

          return   FALSE;    

  }  

   

 

 

http://dev.csdn.net/develop/article/25/25358.shtm  

http://topic.csdn.net/t/20040819/13/3289756.html

http://www.vckbase.com/code/listcode.asp?mclsid=13&sclsid=1303  

发表于: 2007-10-25 ,修改于: 2007-10-25 01:18,已浏览1005次,有评论0条 推荐 投诉


网友评论

发表评论