线程嵌入技术及在外挂中的应用

 

线程嵌入技术及在外挂中的应用


关键字:进程,线程,DLL,线程嵌入技术

呵, 本人经常上CSDN找人帮忙,闲来没事将以前写的一个程序改写成一篇文章还望能抛砖引玉~ 另外文章没有过多检查可能会有一些错误,这些错误还请原谅,其它错误还望你能指正~
 要阅读本文章首先你必须有一定的VC编程经验,并且了解进程,线程,以及DLL的相关知识。
本篇文章其实讲的就是我的一次突发奇想写出来的一个小程序,它并没有多大的用处,但它达到了我预期的目标,并且在这其中遇到了不少问题,想把这些问题的解决经验拿出来与大家分享,还有它不是什么外挂,只是我把它传给一个程序员看的时候,他说这东西像似个外挂,于是,我也这样叫它啦。
 在一次玩QQ游戏中(记得是俄罗斯方块吧),被几个人联合欺负,并且那几个人还非常嚣张(汗。。。),帮手还没找来,那些家伙却一个一个都跑了。。。好胜的我那那么容易放过他们,想一下他们应该不会都出这个房间的。(QQ游戏的树型结构为:游戏类型--》技术等级分类--》房间--》桌)与于打算在房间里把他们给找出来,一找才傻眼了,无论是在大厅里通过桌面显示找,还是在列表框里找都是件吃力的事,有什么好办法解决呢,其实腾讯给我们提供了一个方便的查找功能,但是它有好几个缺点,例如,不能按用户昵称来查找,只能使用QQ号来查找;最重要的是这功能属会员独家使用的!并且我不是。
 为了免费使用这个“功能”,于是我费尽心机想办法,这才发现房间里的那个列表框几乎把本房间玩家的所有信息都记录在这里面了~~,呵,这样的话我们只要使用一些函数将列表框中的数据读出来便可以找到我们所要找的人在哪一桌了。查找了一些操作列表框的API函数后,没发现,我是通过跟踪MFC中CListCtrl类的GetItemText函数发现的,它是通过向列表框发送一个消息:
::SendMessage(m_Hwnd,LVM_GETITEMTEXT,WPARAM,LPARAM);
其中m_Hwnd为列表框句柄,LVM_GETITEMTEXT就故名思意了.
 WPARAM传递一个整数指定行(第一行为0),LPARAM传递一个LVITEM数据结构指针。数据结构的iSubItem指定列(第一列为0), pszText指定为一接收缓冲区,cchTextMax指定缓冲区的大小,其它成员置0即可。函数返回后 LVITEM中的pszText成员就是我们需要的数据。
 于是我做了下面的测试:
 在VC中创建一个基本于对话框的程序,放入一个列表控件,并在列表控件中加入一些数据。然后使用下面的代码来测试:
   char szBuffer[20];
         LVITEM lvi;
         memset(&lvi,0,sizeof(lvi)); 
         lvi.cchTextMax=sizeof(szBuffer);
   lvi.pszText=szBuffer; 
   lvi.iSubItem=3;
   ::SendMessage(m_list.m_hWnd,LVM_GETITEMTEXT,0,(LPARAM)&lvi);
   //m_list是为列表框关联的一个CListCtrl类型变量
   MessageBox(lvi.pszText);
 果然将列表框中的数据取了出来。然后只要将列表框的句柄换成QQ游戏中列表框的句柄就可以达到我们的目的了。
 char szBuffer[20];
         LVITEM lvi; //用于操作列表框的数据结构
   HWND hwnd;
      POINT pt;
        memset(&lvi,0,sizeof(lvi)); 
        lvi.cchTextMax=sizeof(szBuffer);
  lvi.pszText=szBuffer;  //
  lvi.iSubItem=3;  //用于存放列表框中指定行的第四列数据,它是从0开始记数的
        pt.x=750;
     pt.y=250;  //在显示器分辨率800*600情况下
       hwnd=::WindowFromPoint(pt);//获取列表框窗口的句柄
      ::SendMessage(hwnd,LVM_GETITEMTEXT,0,(LPARAM)&lvi);
 //向列表框发送一个要获取第0行,第 lvi.iSubItem列的数据
      MessageBox(lvi.pszText);
可惜,这次得到的结果是空的,(我使用的WIN XP系统,估计在win98上应该是可行的,不过我没试过)为什么呢,当然代码是不会有错的,我在VC中使用一个基于对话框的程序进行测试时是正确的,通过 spy++发现所得的QQ游戏列表框句柄也是对的。
 那么问题出在哪里呢,在这里,我的测试程序是一个进程,而QQ游戏又是一个进程,我的进程要获取QQ游戏进程中的数据,涉及到进程间通信的问题了,并且是在没跟QQ游戏进程协商的情况下进行的。这需要点关于WINDOWS操作系统的知识,在WIN9X中,所有进程共享一个4GB地址空间,每个进程所访问的内存指的是同一块物理地址。而win2000以后的系统中进程不再共享一个地址空间,对于每一个进程来说,它访问的内存地址0x334F与另一个进程中所访问的内存地址0x334F不是同一个地方。这样做的好处很多,我也说不清,反正是使系统更稳定。比如当一个进程出现非法内存地址引用时不会破坏其它进程的数据。
 看来问题出在这,在VC基于对话框的测试程序中,::SendMessage(m_list.hwnd,LVM_GETITEMTEXT,0,(LPARAM)&lvi); 执行时是获取本地进程的数据,而在将列表框句柄改成QQ游戏列表框句柄后就是获取远程进程的数据了。我的解决方案是,即然获取数据的代码需要一个在本地进程中执行的环境,那么我就提供它这样一个环境,提供的方法就是使用远程线程嵌入技术,这样程序的实现思路就是这样的:将获取数据的代码写在一个DLL中,然后让QQ游戏进程加载我们的DLL从而执行我们的代码,与我们的DLL通信我使用的是内存映射文件。
 我们现在要做的就是如何让QQ游戏加载我们的DLL,windows为我们提供了一个远程创建线程的函数
HANDLE CreateRemoteThread(
  HANDLE hProcess,         //远程进程句柄
  LPSECURITY_ATTRIBUTES lpThreadAttributes,  // 安全属性取NULL即可
  DWORD dwStackSize,      // 初始化线程堆大小取0即可
  LPTHREAD_START_ROUTINE lpStartAddress, // 执行线程函数地址
  LPVOID lpParameter,     // 执行线程所需的函数地址
  DWORD dwCreationFlags,  //  创建标
  LPDWORD lpThreadId      // pointer to returned thread identifier
);
用这个函数我们可以在QQGame进程中创建一个线程,该线程执行函数是LoadLibraryA("我的DLL")
(为什么是LoadLibraryA而不是LoadLibrary,因为LoadLibrary是一个宏,它会根据是否定义了Unicode替换成LoadLibraryW还是LoadLibraryA)LoadLibraryA函数的参数可以由lpParameter 传递。就像这样:
Create(hProcessQQgame,NULL,0,LoadLibraryA,"c://me.dll",0,NULL);
可这实际上是行不通的,存在两个问题:第一个是LoadLibraryA在我们程序编译成EXE后这里的地址并不是真正的LoadLibraryA的地址,这个真实地址只有EXE运行后才知道,EXE运行后操作系统会根据真实的地址来填充你EXE文件的一个输入节表,以使你的程序能够调用到LoadLibraryA,当你它传给QQgame时,它是不会知道地址的,因此我们必须自己做操作系统做的那一步,那就是获取LoadLibraryA的真实地址该函数在kernel32.dll中,因此可以通过GetProcAddress来获取。第二问题是"c://me.dll"在编译时译成一个与本程序相关的一个地址引用,因此必须把它转换成一个与QQGame相关的地址,对于这个我们的解决方案是直接用WriteProcessMemory将DLL文件名写入QQGame的进程中。思路就这样,请看下面的代码:
       Char pszLibFile[]="c://me.dll";
       Int cb;
       LPSTR pszLibFileRemote;
       HANDLE hProcess,
 cb=lstrlen(pszLibFile)+1;
    dwProcessId=GetProcessID();//这是我为获取QQGame进程ID写的一个函数
                               //如果你不知道如何获取,可以直接通过任务管理器查看
      hProcess=OpenProcess(PROCESS_ALL_ACCESS,FALSE,dwProcessId);
pszLibFileRemote=(LPSTR)VirtualAllocEx(hProcess,NULL,cb,MEM_COMMIT, PAGE_READWRITE);      //在QQGame进程中申请存放DLL文件名的内存空间
       WriteProcessMemory(hProcess,pszLibFileRemote,pszLibFile,cb,NULL);//将文件名写//QQGame进程
 PTHREAD_START_ROUTINEpfnThreadRtn=(PTHREAD_START_ROUTINE)GetProcAddress
 (GetModuleHandle("Kernel32"),"LoadLibraryA");


hThread=CreateRemoteThread(hProcess,NULL,0,pfnThreadRtn,pszLibFileRemote,0,NULL);
到这里线程嵌入是大功告成了,接下了写我们的DLL;
因为此时DLL已经进入QQGame进程执行代码了,因此可以像在自己进程那样通过发送消息来获取列表框的那些数据:
获取数据伪代码:
    char szBuffer[20];
 int nCount=0,i=0,nFind=-1;
 HWND hwnd=NULL;
 LVITEM lvi;
 POINT pt;
    memset(&lvi,0,sizeof(lvi)); 
    lvi.cchTextMax=sizeof(szBuffer);
 lvi.pszText=szBuffer;
 lvi.iSubItem=3;   //获取第三列数据,在最近更新的里面是  昵称
 pt.x=(int)(GetSystemMetrics(SM_CXFULLSCREEN)*0.75);
   pt.y=(int)(GetSystemMetrics(SM_CYFULLSCREEN)*0.33);
  hwnd=::WindowFromPoint(pt);  //获取列表框句柄的一种简单方法
 nCount=::SendMessage(hwnd,LVM_GETITEMCOUNT,0,0); //得到列表总共有多少项
 for(i=0;i<nCount;i++)
   {
    ::SendMessage(hwnd,LVM_GETITEMTEXT,i,(LPARAM)&lvi);
             MessageBox(NULL,lvi.pszText,"昵称:",MB_OK);
 }//这样你就可以遍历一个房间的所有玩家,但有时可能会出现问题QQGame会死掉,具体原因我也没弄清楚,不过下面我有解决方案;
到这里DLL也写好了,可我们的程序必须得与DLL通信,在这里我使用的是内存射文件.
先建立一个用于通信的数据结构:
          typedef struct tag_data
{
 HWND hList;
 char QQNumber[14];
 char UserName[40];
}DATA;
然后在主程序建立一个内存映射文件:
 hFileMap=CreateFileMapping(INVALID_HANDLE_VALUE,NULL,PAGE_READWRITE,0,
4*1024,"DATA1");
映射到本进程:
pData=(DATA*)MapViewOfFile(hFileMap,FILE_MAP_READ+FILE_MAP_WRITE,0,0,0);
将数据拷拷贝到内存映射文件中:
memcpy(pData,&data,sizeof(data));
取消映射:
UnmapViewOfFile(pData);
这样我们就可以在DLL中读取数据了:
hFileMap=OpenFileMapping(FILE_MAP_READ,FALSE,"DATA1");
pData=(DATA*)MapViewOfFile(hFileMap,FILE_MAP_READ,0,0,0);
 memcpy(&data,pData,sizeof(data));
UnmapViewOfFile(pData);
在这里其实获取QQ列表框句柄可以通过FindWindow来找,可是当我做成功后刚好QQ游戏更新的,我的程序又不行了,因此我用了一个蠢一点的方法,直接通过列表框所处位置来得到它的句柄。
后记: 如果需要源代码可以与我联系 QQ:402431143
如果这里面有很多不清楚的,可以上网去找相关资料,不过我还是建议你看看
<<windows核心编程>>这本书。
程序在WINDOWS XP Professional +VC6.0下调试通过.
参考资料:《windows核心编程》 机械工业出版社
注:附件有主程的源代码和DLL的源代码.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值