CSK.Blog--个人原创Weblog

« 编写QQ外挂插件的提示[2]假期的打算与向往 »

编写QQ外挂插件的提示[3]


前面说了一大堆的原理,现在终于能具体介绍插件的编写了。

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函数(这步很重要,否则正常的代码逻辑会破坏)

---------------------------

不好,马上要熄灯了,就先提供目前版本的下载,还是相当简陋的东东,假期完善……

明天继续写第二篇:-P

http://www.csksoft.net/data/legacyftp/Products/Crack/OpenPugin_alpha.rar

 

发表评论:

注意:为了有效防止SPAM,任何含有http://字样的消息会被阻止发布同时,本站仅供技术交流,请不要讨论任何政治敏感话题或者低级趣味问题。

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

日历

最新评论及回复

最近发表

Copyright Shikai Chen 2000-2012. Powered By Z-Blog(CSK Modified)