欢迎来到 嗅灵易学

零基础也能上手的脚本技术课,一对一答疑带你入门

编写QQ显IP外挂插件及原理分析(3)

编写QQ显IP外挂插件及原理分析(3)

上一篇地址:http://www.csksoft.net/blog/post/QQ...ips_2.html.html
前面说了一大堆的原理,现在终于能具体介绍插件的编写了。
2 编写QQ显外挂插件
2.1 插件存在形式
这里说的插件是编写一个外部的dll文件,至于为何要用dll我就不想多说了。QQ自身不支持插件特性,所以做出显ip外挂的可能无非有3
直接修改QQ本身的程序文件->不具备版本无关性,容易引发法律纠纷
使用外部exe进程->必须采用调试进程的办法,效率低且浪费系统资源
编写dll文件让QQ加载->效率高,稳定
好了,现在就不讨论前面2种情况了。
2.2 如何加载dll
dll文件是不可能自己执行的,必须要又调用它的程序主动去加载。这应该是常识。那么如何让qq.exe去“主动”加载我们的插件呢?
同样采用外部插件的coralQQ,你会发现实际上它包含2个文件
coralQQ.exe和coralqq.dll
而平时点击第一个文件就是用来加载珊瑚虫版QQ的,但你千万别认为这个exe文件就是外挂的核心,核心是那个dll。
CoralQQ.exe实际上只做了一件事:加载qq.exe,然后把coralqq.dll注入到qq.exe的进程中去。
关于如何实现该方法我不多说了,可以参考网上关于珊瑚虫外挂原理的文章(当然实际上它没有介绍原理……)
将dll文件注入到程序的方法有2
1.首先通过CreateProcess创建进程,然后模拟windows加载一个dll的全过程,在加载程序主体
2.采用远程线程植入技术,即使用CreateRemoteThread
我们采用后者。
2.3 开始具体编写
2.3.1 插件加载程序
编写一个用于加载我们插件dll的exe,好比coralQQ.exe
其流程伪代码:
int WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPTSTR    lpCmdLine,int nCmdShow){
pinfo=CreateProcess(<加载qq.exe> );
 if (hProcess){
  HANDLE processHandle;
  DWORD Exitcode;
  processHandle=pinfo.hProcess;
  HANDLE hThread;
  char    szLibPath[MAX_PATH];  
  void*   pLibRemote;
  DWORD   hLibModule;
  HMODULE hKernel32 = GetModuleHandle(TEXT(Kernel32));
  strcpy(szLibPath,<插件名称> );
  pLibRemote = VirtualAllocEx( processHandle, NULL, sizeof(szLibPath),MEM_COMMIT, PAGE_READWRITE );
  WriteProcessMemory( processHandle, pLibRemote, (void*)szLibPath,sizeof(szLibPath), NULL );
  hThread = CreateRemoteThread( processHandle, NULL, 0,(LPTHREAD_START_ROUTINE)GetProcAddress( hKernel32,LoadLibraryA ),pLibRemote, 0, NULL );
  WaitForSingleObject( hThread, INFINITE );
  GetExitCodeThread( hThread, &hLibModule );
  CloseHandle( hThread );
  VirtualFreeEx( processHandle, pLibRemote, sizeof(szLibPath), MEM_RELEASE );
 }
 else
 {
  MessageBox(NULL,TEXT(Err,Cannot Load qq.exe),TEXT(Err!),MB_OK);
 }}
该部分的代码实际上是通过CreateRemoteThread将LoadLibraryA(<插件dll文件名> );语句注入了qq.exe执行。这样就做到了qq主动加载我们插件的目的。
2.3.2 编写具体插件
这里部分参考了目子版qq的方法。我们拦截CQQApplication.dll中对user32.dll SetForegroundWindow API的调用来判断QQ聊天对话框弹出的时机,以便显示IP信息。 
主要的思路是:
DllMain函数:
创建一个多线程(VC环境推荐采用_beginthread这个crt函数,否则将无法在新创建的线程中调用crt函数)。新创建线程的函数名比如为workproc,他将在我们dll插件被植入qq.exe后开始执行
workproc函数:
主要的功能就是去修改qq.exe内部程序,使得我们能够拦截相关的函数,比如以前提到的获取IQQCore和Uin的函数,以及上面说的SetForegroundWindow 函数。
所谓API函数拦截前一篇文章已经说过了大致的方法,这里我们采用《windows核心编程》中推荐的ReplaceIATEntryInOneMod函数。为了方便,我把函数代码给出:
bool ReplaceIATEntryInOneMod(PCSTR pszCalleeModName,
        PROC pfnCurrent, PROC pfnNew,HMODULE hmodCaller)
{
 ULONG ulSize;
 PIMAGE_IMPORT_DESCRIPTOR pImportDesc = (PIMAGE_IMPORT_DESCRIPTOR)
  ImageDirectoryEntryToData(hmodCaller,TRUE,IMAGE_DIRECTORY_ENTRY_IMPORT,
  &ulSize);
 if (pImportDesc == NULL)
 {
  return false;
 }
 for (;pImportDesc->Name;pImportDesc++)
 {
  PSTR pszModName = (PSTR)
   ((PBYTE) hmodCaller + pImportDesc->Name);
  if (lstrcmpiA(pszModName,pszCalleeModName)==0)
   break;
 }
 if (pImportDesc->Name == 0)
 {
  return false;
 }
 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)
  {
   HANDLE hProcess = GetCurrentProcess();
   DWORD oldAttr;
   VirtualProtectEx(hProcess, ppfn, sizeof(pfnNew), PAGE_READWRITE, &oldAttr);
   if (WriteProcessMemory(hProcess,ppfn,&pfnNew,sizeof(pfnNew),NULL)==TRUE)
   {
    VirtualProtectEx(hProcess, ppfn, sizeof(pfnNew), oldAttr, &oldAttr);
    return true;
   }
   else
   {
    VirtualProtectEx(hProcess, ppfn, sizeof(pfnNew), oldAttr, &oldAttr);
    return false;
   }
  }
 }
 return false;
}
函数的作用是将所用在hmodCaller句柄指向模块中对pszCalleeModName指向文件名dll中提供的入口地址在pfnCurrent的函数的调用,用pfnNew指向的函数去替换。也就是说原先要调用pfnCurrent函数的,现在就会执行pfnNew指向的函数。
要拦截上面说的SetForegroundWindow,就用如下代码:
  OrgFuncProc = GetProcAddress( GetModuleHandleA(user32),SetForegroundWindow);
  if (ReplaceIATEntryInOneMod(user32.dll,OrgFuncProc,(PROC)&OnQQWndShow,global_hCQQAppModule))
  {   
//替换成功
    }
其中global_hCQQAppModule是通过GetModuleHandle获取的CQQApplication.dll文件在qq.exe中的句柄。上面代码调用成功后,以后CQQApplication.dll中代码要调用SetForegroundWindow时,实际上将执行我们编写的OnQQWndShow函数。
现在我们看OnQQWndShow的申明:
extern C  BOOL  APIENTRY OnQQWndShow(HWND hWnd);
为什么要有extern C 和APIENTRY (也就是__stdcall)前缀呢?因为原先的SetForegroundWindow是采用stdcall调用规范的,如果替换函数的调用规范不同,将引发程序崩溃。
同时替换函数的参数也要和原函数一致。
经过这样的替换后,以后每当新弹出一个qq聊天窗口,我们的OnQQWndShow就会被执行,在其中我们就可以负责获取ip,并创建一个edit或者static窗口在qq聊天窗口上显示ip信息
OnQQWndShow函数:
前面已经说了它的作用,具体流程如下:
获取对应好友QQ号码
获取对应的IP信息
在qq聊天窗口,也就是函数hWnd参数指出的窗口中寻找窗口上的广告框,然后把它销毁,创建一个edit或者static窗口,显示ip信息。
最后调用原先的SetForegroundWindow函数(这步很重要,否则正常的代码逻辑会破坏)
-----------------------
好了,最近就写了这3篇,如果觉得还行的话就期待着接下来的后续把~
有什么错误希望多多指正

注意:上传附件及图片大小不得大于30M。

⚠️ 版权声明:
本博客所有内容(含教程、源码、工具)仅供个人技术学习与研究交流使用,严禁商用、倒卖、二次分发及非法用途
未经作者书面授权,任何组织或个人不得转载、复制或用于其他平台,违者将追究相关责任。

0 0 0 举报
复制成功