前面说了一大堆的原理,现在终于能具体介绍插件的编写了。
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