首页 网络安全 安全学院 查看内容

QQGame浅析系列-多开大厅刷分你也可以

2009-6-9 09:43 776 0

摘要:   本系列仅以学习交流使用,非法使用者,后果自负。  最近闲来无聊就赔朋友一起打打牌,是QQ游戏斗地主游戏,我们通常在一个桌(Table)打牌,这样配合胜率会高一点.有一次他正在和别人打牌,四人已满,...
关键词: nbsp DWORD 100118 push eax ptr 00 100119 QQGame 运行

  本系列仅以学习交流使用,非法使用者,后果自负。  最近闲来无聊就赔朋友一起打打牌,是QQ游戏斗地主游戏,我们通常在一个桌(Table)打牌,这样配合胜率会高一点.有一次他正在和别人打牌,四人已满,我就不能和他一起打了,就随便点了一下发现能进得此桌,原来游戏提供了"旁观"的功能,不过是当时才知道的.于是我就一个个地"旁观"别人的牌,将牌符告诉我的朋友,他果然"运筹帷幄,决胜千里",呵呵....  于是想到了一个"坏点子":自己开两个号打牌,一个用来旁观,另一个用来打牌,岂不是很容易就取胜?当我兴冲冲地准备"作祟"时,发现QQGame并不能同时运行两个实例,所以我上述的想法就暂时不能实现了.要想实现,就要突破这一关,于是抱着试试看的心理分析下"QQGame不能同时运行两个实例"的原理.  程序实现只能运行一个实例的原理通常是使用互斥元,临界区,信号量等机制.  于是就OD载入,下断:  bp CreateMutexA  F9运行,中断,再次F9......若干次后会看到堆栈提示:  0013FE90   100118DF  /CALL 到 CreateMutexA 来自 Utility.100118D9  0013FE94   00000000  |pSecurity = NULL  0013FE98   00000001  |InitialOwner = TRUE  0013FE9C   00F88700  \MutexName = "QQGame_Mutex03/01/2003"  MutexName为"QQGame_Mutex03/01/2003"的就是我们所关心的.  如果知道了这一点,那么要想绕过"QQGame不能同时运行两个实例"就很简单了,我这里采用的是在不破坏原程序完整性  的方法,现介绍如下:  MutexName为"QQGame_Mutex03/01/2003"的串存放在一个叫做MainLogi.dll文件中,在游戏安装目录下的Logic文件夹里.如果每次运行游戏程序MutexName与上次的都不相同,不就可以绕过了吗?也就是每次运行程序前偷偷地改变MainLogi.dll文件中的MutexName.具体实现见代码及分析:  program QQgameSpy;  uses  windows,dialogs,sysutils,classes,graphics,shellapi ,registry;  var  reg:TRegistry;  strPath:string;  strFileName:string;  strNewFile:string;  strGameFile:string;  strMutex:string;  i:integer;  hMutex:DWORD;  hFile:DWORD;  dwBytesOfRW:Cardinal;  buffer:BYTE;  DstFile:TFileStream;  hwnd:DWORD;  {$r QQgameSpy.RES}  function   RenameFile(hdl:THandle;lpOrgName,lpNewName:PAnsichar):boolean;  var  shf:TSHFILEOPSTRUCT;  begin  try  FillChar(shf,SizeOf(shf),0);  shf.Wnd       :=hdl;  shf.wFunc     :=FO_RENAME;  shf.pFrom     :=lpOrgName;  shf.pTo       :=lpNewName;  shf.fFlags    :=FOF_NOCONFIRMATION+FOF_SILENT;  Result:=(0=SHFileOperation(shf));  except  Result:=false;  end;  end;  { 主程序开始 }  begin  reg:= TRegistry.Create;  reg.RootKey:=HKEY_CURRENT_USER;  if reg.OpenKeyReadOnly('\Software\Tencent\QQGame\SYS')= false then  exit;  strPath:=reg.ReadString('GameDirectory');  //showmessage(strPath);  reg.CloseKey;  reg.Free;  strFileName:=strPath+'Logic\MainLogi.dll';  strMutex:='QQGame_Mutex03/01/2000';  for i:=0 to 3 do begin  hMutex:=CreateMutex(nil,false,pchar(strMutex));  if hMutex=0 then  continue;  if GetLastError<>ERROR_ALREADY_EXISTS then  break;  Inc( strMutex[Length(strMutex)] );  end;  //showmessage(strMutex);  buffer:=BYTE(strMutex[Length(strMutex)]);  if hMutex=0 then  exit;  CloseHandle(hMutex);  //DstFile:=TFileStream.Create(strFileName,fmOpenReadWrite);  //DstFile.Seek($581C1,FILE_BEGIN);  //DstFile.WriteBuffer(buffer,sizeof(buffer));  for i:=0 to 2 do begin  strNewFile:=strFileName+inttostr(i);  DeleteFile(strNewFile);  end;  for i:=0 to 2 do begin  strNewFile:=strFileName+inttostr(i);  if FileExists(strNewFile)=false then  break;  end;  RenameFile(0,pchar(strFileName),pchar(strNewFile));  CopyFile(pchar(strNewFile),pchar(strFileName),true);  hFile:=CreateFile(pchar(strFileName),GENERIC_WRITE or GENERIC_READ,FILE_SHARE_WRITE or FILE_SHARE_READ,  nil,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0);  if hFile=INVALID_HANDLE_VALUE then  exit;  SetFilePointer(hFile,$581C1,nil,FILE_BEGIN);  WriteFile(hFile,buffer,sizeof(buffer),dwBytesOfRW,nil);  CloseHandle(hFile);  strGameFile:=strPath+'QQGame.exe';  //showmessage(strGameFile);  ShellExecute(0,'Open',pchar(strGameFile),nil,nil,SW_SHOWNORMAL);  end.  解释: 当程序运行时通过注册表查询QQGame的安装目录,进而获取MainLogi.dll文件的完整文件名。由于QQ游戏一般来说没桌最多不超过四人,所以每次运行前首先查看MutexName为:  'QQGame_Mutex03/01/2000';  'QQGame_Mutex03/01/2001';  'QQGame_Mutex03/01/2002';  'QQGame_Mutex03/01/2000';  的互斥元是否存在,如果遇到有不存在的,说明可以以此互斥元名运行QQ游戏,就改写原MainLogi.dll文件名为其他,复制一份MainLogi.dll文件并更换其中的MutexName,最后ShellExecute QQ游戏,这样当QQ游戏运行检测时就会使用我们更改过的MutexName,由于我们实现已经检测过这个MutexName还没有被使用,当然可以成功运行了。  这里你可能很诧异:MainLogi.dll文件被游戏加载过一次,下次还能被改名吗?我试过了,删除此文件不可以,但是可以对其重命名。上面声明的RenameFile就是实现更改文件名功能的。  运行后,可以开N个大厅,于是乎狂玩了一下午,很快刷到了600多分,胜负率高达60%以上啊!不过也就是当时一会的劲头,再后来就没有玩过了,现在将此方法公布与大家分享一下,希望玩得愉快!  说明:在我欲发此贴时,QQGame有所更新,使用上述方法不行了,如果不给出一个新的解决方法,总感觉不甚完美,于是重新又跟了一次。思路如下:  如果已有一个登录窗体运行了,那么再运行实例会给那个已运行的实例一个焦点,让用户知道“你已经运行啦”,然后退出。  于是  Bp FindWindowA  Bp FindWindowExA  F9运行,中断一个,看堆栈提示:  0013FE7C   100119A3  /CALL 到 FindWindowExA 来自 Utility.1001199D  0013FE80   00000000  |hParent = NULL  0013FE84   00000000  |hAfterWnd = NULL  0013FE88   01069BD0  |Class = "QQGame_MainFrame"  0013FE8C   00000000  \Title = NULL  返回到调用的过程:  《Utility领空》  1001199D    FF15 18920110   call    dword ptr [<&USER32.FindWindowEx>; USER32.FindWindowExA  100119A3    8BF8            mov     edi, eax  100119A5    57              push    edi  100119A6    FF15 78920110   call    dword ptr [<&USER32.IsWindow>]   ; USER32.IsWindow  100119AC    85C0            test    eax, eax  100119AE    74 0E           je      short 100119BE  100119B0    53              push    ebx  100119B1    53              push    ebx  100119B2    FF76 50         push    dword ptr [esi+50]  100119B5    57              push    edi  100119B6    FF15 7C920110   call    dword ptr [<&USER32.PostMessageA>; USER32.PostMessageA  100119BC    EB 10           jmp     short 100119CE  100119BE    83C6 14         add     esi, 14  100119C1    68 1CE70110     push    1001E71C  100119C6    56              push    esi  100119C7    E8 4A4FFFFF     call    10006916  100119CC    59              pop     ecx  100119CD    59              pop     ecx  100119CE    5F              pop     edi  100119CF    5E              pop     esi  100119D0    5B              pop     ebx  100119D1    C3              retn  执行流程是这样的,检测是否已有窗体运行了,如果有找到它,给它发个消息(这个消息估计就是让那个窗体拥有焦点的)然后退出。  我们由此过程再次返回,发现是  100118F5    E8 92000000     call    1001198C  调用的上述过程,前后浏览一下代码:  《Utility领空》  10011846    55              push    ebp  10011847    8BEC            mov     ebp, esp  10011849    51              push    ecx  1001184A    8B45 18         mov     eax, dword ptr [ebp+18]  1001184D    8365 FC 00      and     dword ptr [ebp-4], 0  10011851    53              push    ebx  10011852    56              push    esi  10011853    8BF1            mov     esi, ecx  10011855    57              push    edi  10011856    BB 00010000     mov     ebx, 100  1001185B    8946 54         mov     dword ptr [esi+54], eax  1001185E    8B45 14         mov     eax, dword ptr [ebp+14]  10011861    8DBE 58010000   lea     edi, dword ptr [esi+158]  10011867    8946 50         mov     dword ptr [esi+50], eax  1001186A    85FF            test    edi, edi  1001186C    74 21           je      short 1001188F  1001186E    837D 10 00      cmp     dword ptr [ebp+10], 0  10011872    74 1B           je      short 1001188F  10011874    53              push    ebx  10011875    8D4E 45         lea     ecx, dword ptr [esi+45]  10011878    FF75 10         push    dword ptr [ebp+10]  1001187B    E8 16050000     call    10011D96  10011880    85C0            test    eax, eax  10011882    74 0B           je      short 1001188F  10011884    FF75 10         push    dword ptr [ebp+10]  10011887    57              push    edi  10011888    E8 19580000     call    <jmp.&MSVCRT.strcpy>  1001188D    59              pop     ecx  1001188E    59              pop     ecx  1001188F    8D7E 58         lea     edi, dword ptr [esi+58]  10011892    85FF            test    edi, edi  10011894    74 21           je      short 100118B7  10011896    837D 0C 00      cmp     dword ptr [ebp+C], 0  1001189A    74 1B           je      short 100118B7  1001189C    53              push    ebx  1001189D    8D4E 45         lea     ecx, dword ptr [esi+45]  100118A0    FF75 0C         push    dword ptr [ebp+C]  100118A3    E8 EE040000     call    10011D96  100118A8    85C0            test    eax, eax  100118AA    74 0B           je      short 100118B7  100118AC    FF75 0C         push    dword ptr [ebp+C]  100118AF    57              push    edi  100118B0    E8 F1570000     call    <jmp.&MSVCRT.strcpy>  100118B5    59              pop     ecx  100118B6    59              pop     ecx  100118B7    837D 08 00      cmp     dword ptr [ebp+8], 0  100118BB    74 5F           je      short 1001191C  100118BD    FF75 08         push    dword ptr [ebp+8]  100118C0    E8 E7570000     call    <jmp.&MSVCRT.strlen>  100118C5    F7D8            neg     eax  100118C7    1BC0            sbb     eax, eax  100118C9    59              pop     ecx  100118CA    40              inc     eax  100118CB    8945 10         mov     dword ptr [ebp+10], eax  100118CE    75 4C           jnz     short 1001191C  100118D0    FF75 08         push    dword ptr [ebp+8]  100118D3    6A 01           push    1  100118D5    5F              pop     edi  100118D6    57              push    edi  100118D7    6A 00           push    0  100118D9    FF15 E8900110   call    dword ptr [<&KERNEL32.CreateMutexA>]   ; kernel32.CreateMutexA  100118DF    85C0            test    eax, eax  100118E1    8946 4C         mov     dword ptr [esi+4C], eax  100118E4    74 24           je      short 1001190A  100118E6    FF15 AC900110   call    dword ptr [<&KERNEL32.GetLastError>]   ; ntdll.RtlGetLastWin32Error  100118EC    3D B7000000     cmp     eax, 0B7  100118F1    8BCE            mov     ecx, esi  100118F3    75 07           jnz     short 100118FC  100118F5    E8 92000000     call    1001198C  100118FA    EB 2A           jmp     short 10011926  100118FC    E8 64010000     call    10011A65  10011901    85C0            test    eax, eax  10011903    74 21           je      short 10011926  10011905    897D FC         mov     dword ptr [ebp-4], edi  10011908    EB 1C           jmp     short 10011926  1001190A    83C6 14         add     esi, 14  1001190D    68 F8E60110     push    1001E6F8  10011912    56              push    esi  10011913    E8 FE4FFFFF     call    10006916  10011918    59              pop     ecx  10011919    59              pop     ecx  1001191A    EB 0A           jmp     short 10011926  1001191C    8BCE            mov     ecx, esi  1001191E    E8 42010000     call    10011A65  10011923    8945 FC         mov     dword ptr [ebp-4], eax  10011926    8B45 FC         mov     eax, dword ptr [ebp-4]  10011929    5F              pop     edi  1001192A    5E              pop     esi  1001192B    5B              pop     ebx  1001192C    C9              leave  1001192D    C2 1400         retn    14  在运行到  100118D9    FF15 E8900110   call    dword ptr [<&KERNEL32.CreateMutexA>]   ; kernel32.CreateMutexA  一句时,MutexName没有变,就是和我们之前的分析是一样的,只是多了一个对窗口的检测,呵呵,好对付。  再次返回:  《MainLogi领空》  00F6714E    56              push    esi  00F6714F    8BF1            mov     esi, ecx  00F67151    57              push    edi  00F67152    83BE A8010000 0>cmp     dword ptr [esi+1A8], 0  00F67159    75 25           jnz     short 00F67180  00F6715B    E8 BF0F0100     call    00F7811F  00F67160    85C0            test    eax, eax  00F67162    74 1C           je      short 00F67180  00F67164    8B10            mov     edx, dword ptr [eax]  00F67166    68 02900004     push    4009002  00F6716B    68 8440F800     push    00F84084                               ; ASCII "Utility.dll"  00F67170    56              push    esi  00F67171    8BC8            mov     ecx, eax  00F67173    FF52 08         call    dword ptr [edx+8]  00F67176    85C0            test    eax, eax  00F67178    74 06           je      short 00F67180  00F6717A    8986 A8010000   mov     dword ptr [esi+1A8], eax  00F67180    8B86 A8010000   mov     eax, dword ptr [esi+1A8]  00F67186    8BD6            mov     edx, esi  00F67188    F7DA            neg     edx  00F6718A    8D48 04         lea     ecx, dword ptr [eax+4]  00F6718D    8D7E 04         lea     edi, dword ptr [esi+4]  00F67190    1BD2            sbb     edx, edx  00F67192    6A 00           push    0  00F67194    8B01            mov     eax, dword ptr [ecx]  00F67196    23D7            and     edx, edi  00F67198    52              push    edx  00F67199    FF10            call    dword ptr [eax]  00F6719B    8B8E A8010000   mov     ecx, dword ptr [esi+1A8]  00F671A1    68 5E100000     push    105E  00F671A6    68 861F0000     push    1F86  00F671AB    68 1887F800     push    00F88718                               ; ASCII "QQGame_MainFrame"  00F671B0    8B01            mov     eax, dword ptr [ecx]  00F671B2    68 EC80F800     push    00F880EC                               ; ASCII "QQGame"  00F671B7    68 0087F800     push    00F88700                               ; ASCII "QQGame_Mutex03/01/2003"  00F671BC    FF50 0C         call    dword ptr [eax+C]                      ; 进  00F671BF    8BF8            mov     edi, eax  00F671C1    83C6 08         add     esi, 8  00F671C4    57              push    edi  00F671C5    68 C886F800     push    00F886C8                               ; ASCII "Logout_Optimize CheckMultiInstanceRunning() return [%d]"  00F671CA    56              push    esi  00F671CB    E8 EEF1FEFF     call    00F563BE  00F671D0    83C4 0C         add     esp, 0C  00F671D3    8BC7            mov     eax, edi  00F671D5    5F              pop     edi  00F671D6    5E              pop     esi  00F671D7    C3              retn  再次返回  《QQGame领空》  004010CE  |.  FF50 38       call    dword ptr [eax+38]                ;进  004010D1  |.  85C0          test    eax, eax  004010D3  |.  74 13         je      short 004010E8  注意这个返回值就很重要了,如果暴力一点的话就就将004010D3  je      short 004010E8  Nop掉就OK了。  更新的版本主要是多了一个对窗口的检测,标志值存放在eax里,如果想跟踪的话就关键看对eax的修改即可,我就不跟了。  不过这样有一个缺陷,那就是如果程序中增加了文件校验就会发现程序被修改而拒绝执行,不过“兵来将挡,水来土掩”,知道了这个机理解决办法还是不少的。
声明:文章版权归原作者所有 部分文章转自互联网 如有侵权请联系 [邮箱地址] 删除

路过

雷人

握手

鲜花

鸡蛋

最新评论

返回顶部