切换到宽版
  • 12486阅读
  • 19回复

[QQ辅助]开始收集QQ去广告显IP补丁原理,发布自己的心得及方法,不要乱顶帖,否则删。 [复制链接]

上一主题 下一主题
离线乱梦春秋
 

只看楼主 倒序阅读 使用道具 楼主  发表于: 2008-07-10
— 本帖被 linzongyi 执行取消加亮操作(2012-03-22) —
开始收集QQ去广告+显示IP外挂补丁原理,请大家发布自己的心得及方法,不要乱顶帖,否则删。


做IPQQ,那么这几个必备工具是不可缺的:OllyDbg,PEExplorer,DASM32,MFCSpy2
QQ是基于接口调用架构的,这为窥探其内部提供了方便之门

0)   经过分析,获知QQ获取IP信息是通过接口调用实现的,其步骤为
IQQCore->IQQData->IQQUserDynData->dwIP方法

1)   获得IQQCore.要获得此全局描述接口的方法有很多,最好的就是通过QQHelperDll.dll的 ?IsLogin@@YAHPAUIQQCore@@@Z方法获得,函数表达为int __cdecl IsLogin (struct IQQCore **).因为这个IsLogIn方法被QQ频繁调用,于是Hook这个函数,便能轻易获得IQQCore了

function IsLogin(pQQCore: Pointer): Integer; cdecl;
begin
  Result := Call original Func;   调用原函数
  pIQQCore := pQQCore;   获得IQQCore
end;

2)   从IQQCore获得IQQData.这个事情好办,QQ的BasicCtrlDll.dll的? GetFriendQQData@@YAHPAUIQQCore@@KPAPAUIQQData@@@Z方法,就是从IQQCore和UIN获得 IQQData,函数表达为int __cdecl GetFriendQQData(struct IQQCore *,unsigned long,struct IQQData * *)

asm
    // int __cdecl GetFriendQQData(struct IQQCore *,unsigned long,struct IQQData * *)
    mov eax, pIQQCore
    mov edx, UIN   // QQ Uin (QQ number)
    lea ecx, Result   // return = pIQQData
    push ecx
    push edx
    push eax
    call GetFriendQQData
    add esp, $C   // fix call stack
end;

3)   从IQQData获得IQQUserDynData.很不幸,QQ没有直接提供该方法,只好DASM QQ的内部,来模拟此过程的调用.
const
  szQQUSER_DYNAMIC_DATA : PChar = 'QQUSER_DYNAMIC_DATA';
  clsid_IQQData : TGUID = '{BA863A1E-C979-498A-975C-C501C4F310A3}';
asm
    // pIQQData = Pointer(IQQData);
    mov ecx, pIQQData
    mov ecx, [ecx]   // ecx = IQQData.vtbl
    mov eax, pIQQData   // this pIQQData
    lea edx, Result   // return = pIQQUDD
    push edx
    lea edx, clsid_IQQData   // clsid_IQQData
    push edx
    push szQQUSER_DYNAMIC_DATA
    push eax
    call [ecx + $54]   // IQQData.vf_54h QQUSER_DYNAMIC_DATA proc entry
end;

4)   从IQQUserDynData获得IP信息.
const
  szdwIP : PChar = 'dwIP';
  szwPort : PChar = 'wPort';
asm
    // get Uin info
    mov eax, pIQQUDD
    mov ecx, [eax]
    lea edx, dwIP
    push edx
    push szdwIP
    push eax   
  call [ecx + $34]   // IQQUDD.vf_34h
    mov eax, pIQQUDD
    mov ecx, [eax]
    lea edx, wPort
    push edx
    push szwPort
    push eax   
    call [ecx + $30]   // IQQUDD.vf_30h
end;

上面的代码,懂ASM的人很容易就能理解的,其实这些代码也是来自QQ的DASM工程.
注意一下接口调用和Cdecl就行了,因为用Delphi写的,所以不好直接支持C++的thiscall,故采用BASM方式来调用~

至于去广告,把目录AD下全部文件和Dat下Ad.gif删除了,广告就不会出来了.
可是这样QQ还是会下载新的广告的,怎么办呢?只好修改QQ内部了,这是属于破解的范畴,做起来也并不复杂.

DASM 分析QQ.EXE即可查询到"广告"和"下载逻辑"的文本常量和OD查找字符串常量"Download_Start",它的上面是 "SECTION_AD",然后把相关的地方NOP了就能使QQ不再下载广告.不同的版本要修改的地方不一样,这里就仅以QQ2007II Beta1为例

004E9D49   |.   57         push   edi
004E9D4A   |.   50         push   eax
004E9D4B   |.   57         push   edi
004E9D4C   |.   53         push   ebx
004E9D4D   |.   68 EFB14E00   push   004EB1EF                 ;   Entry address
^ 这里是广告下载过程入口,到入口改为retn直接返回就OK了
004E9D52   |.   E8 DE4EF2FF   call   0040EC35
004E9D57   |.   83C4 14     add   esp, 14
004E9D5A   |.   BF 7CF55A00   mov   edi, 005AF57C               ;   ASCII "C:\config_asam.ini"
004E9D5F   |.   C745 10 60EA0>mov   dword ptr [ebp+10], 0EA60
004E9D66   |.   57         push   edi                     ; /FileName => "C:\config_asam.ini"
004E9D67   |.   FF15 E0035400 call   dword ptr [<&KERNEL32.GetFileAtt>; \GetFileAttributesA
004E9D6D   |.   83F8 FF     cmp   eax, -1
004E9D70   |.   74 64       je     short 004E9DD6
004E9D72   |.   A8 10       test   al, 10
004E9D74   |.   75 60       jnz   short 004E9DD6
004E9D76   |.   57         push   edi
004E9D77   |.   8D4D B8     lea   ecx, dword ptr [ebp-48]
004E9D7A   |.   E8 657BFAFF   call   <jmp.&MFC42.#537_CString::CStrin>
004E9D7F   |.   BF 84DB5500   mov   edi, 0055DB84
004E9D84   |.   C645 FC 0C   mov   byte ptr [ebp-4], 0C
004E9D88   |.   897D B4     mov   dword ptr [ebp-4C], edi
004E9D8B   |.   B8 70F55A00   mov   eax, 005AF570               ;   ASCII "SECTION_AD"
004E9D90   |.   C645 FC 0D   mov   byte ptr [ebp-4], 0D
004E9D94   |.   8BC8       mov   ecx, eax
004E9D96   |.   85C9       test   ecx, ecx
004E9D98   |.   74 1A       je     short 004E9DB4
004E9D9A   |.   B9 60F55A00   mov   ecx, 005AF560               ;   ASCII "Download_Start"
004E9D9F   |.   8BD1       mov   edx, ecx
004E9DA1   |.   85D2       test   edx, edx
004E9DA3   |.   74 0F       je     short 004E9DB4
004E9DA5   |.   FF75 B8     push   dword ptr [ebp-48]           ; /IniFileName
004E9DA8   |.   6A 00       push   0                       ; |Default = 0
004E9DAA   |.   51         push   ecx                     ; |Key => "Download_Start"
004E9DAB   |.   50         push   eax                     ; |Section => "SECTION_AD"
004E9DAC   |.   FF15 3C035400 call   dword ptr [<&KERNEL32.GetPrivate>; \GetPrivateProfileIntA
004E9DB2   |.   EB 02       jmp   short 004E9DB6
004E9DB4   |>   33C0       xor   eax, eax
004E9DB6   |>   85C0       test   eax, eax
004E9DB8   |.   74 09       je     short 004E9DC3
004E9DBA   |.   69C0 E8030000 imul   eax, eax, 3E8
004E9DC0   |.   8945 10     mov   dword ptr [ebp+10], eax
004E9DC3   |>   C645 FC 0B   mov   byte ptr [ebp-4], 0B
004E9DC7   |.   897D B4     mov   dword ptr [ebp-4C], edi
004E9DCA   |.   8D4D B8     lea   ecx, dword ptr [ebp-48]
004E9DCD   |.   C645 FC 0B   mov   byte ptr [ebp-4], 0B
004E9DD1   |.   E8 D679FAFF   call   <jmp.&MFC42.#800_CString::~CStri>
004E9DD6   |>   68 A0F45A00   push   005AF4A0                 ;   ASCII

"D:\QQ\qqbuilder_QQ2007IIbeta1Proj_int\Basic_QQ_VOB\QQ\QQMainApp\QQCSCenterSubApp.cpp"
004E9DDB   |.   B9 886C5B00   mov   ecx, 005B6C88
004E9DE0   |.   E8 997AFAFF   call   <jmp.&MFC42.#860_CString::operat>
004E9DE5   |.   BF 906C5B00   mov   edi, 005B6C90
004E9DEA   |.   68 40165400   push   00541640
004E9DEF   |.   8BCF       mov   ecx, edi
004E9DF1   |.   C705 8C6C5B00>mov   dword ptr [5B6C8C], 470
004E9DFB   |.   E8 7E7AFAFF   call   <jmp.&MFC42.#860_CString::operat>
004E9E00   |.   8B45 10     mov   eax, dword ptr [ebp+10]
004E9E03   |.   33D2       xor   edx, edx
004E9E05   |.   B9 E8030000   mov   ecx, 3E8
004E9E0A   |.   F7F1       div   ecx
004E9E0C   |.   50         push   eax
004E9E0D   |.   68 40F55A00   push   005AF540
004E9E12   |.   68 38F55A00   push   005AF538                 ;   ASCII "AD|asam"
004E9E17   |.   E8 AE78F1FF   call   004016CA
004E9E1C   |.   83C4 0C     add   esp, 0C
004E9E1F   |.   837D EC 00   cmp   dword ptr [ebp-14], 0
004E9E23   |.   74 17       je     short 004E9E3C
004E9E25   |.   6A FF       push   -1
004E9E27   |.   FF75 EC     push   dword ptr [ebp-14]
004E9E2A   |.   56         push   esi
004E9E2B   |.   FF75 10     push   dword ptr [ebp+10]
004E9E2E   |.   6A 0B       push   0B
004E9E30   |.   E8 ED4CF2FF   call   0040EB22
004E9E35   |.   83C4 14     add   esp, 14
004E9E38   |.   85C0       test   eax, eax
004E9E3A   |.   74 3D       je     short 004E9E79
^ 这里是判断广告是否要下载, 直接JMP就可以跳过广告下载了
004E9E3C   |>   68 A0F45A00   push   005AF4A0                 ;   ASCII

"D:\QQ\qqbuilder_QQ2007IIbeta1Proj_int\Basic_QQ_VOB\QQ\QQMainApp\QQCSCenterSubApp.cpp"
004E9E41   |.   B9 886C5B00   mov   ecx, 005B6C88
004E9E46   |.   E8 337AFAFF   call   <jmp.&MFC42.#860_CString::operat>
004E9E4B   |.   68 40165400   push   00541640
004E9E50   |.   8BCF       mov   ecx, edi
004E9E52   |.   C705 8C6C5B00>mov   dword ptr [5B6C8C], 476
004E9E5C   |.   E8 1D7AFAFF   call   <jmp.&MFC42.#860_CString::operat>
004E9E61   |.   68 18F55A00   push   005AF518
004E9E66   |.   68 38F55A00   push   005AF538                 ;   ASCII "AD|asam"
004E9E6B   |.   E8 5A78F1FF   call   004016CA
004E9E70   |.   59         pop   ecx
004E9E71   |.   59         pop   ecx
004E9E72   |.   8BCB       mov   ecx, ebx
004E9E74   |.   E8 76130000   call   004EB1EF
004E9E79   |>   8B45 EC     mov   eax, dword ptr [ebp-14]
004E9E7C   |.   33FF       xor   edi, edi
004E9E7E   |.   3BC7       cmp   eax, edi
004E9E80   |.   74 09       je     short 004E9E8B

可是广告窗口还是照样存在的,而且点击了仍旧会有响应的.这就靠外挂才好处理的.要找到QQ聊天窗口中任意一个WinControl的Handle就能轻松用代码干掉广告窗口的.

procedure DisableQQAd(Wnd: LongInt);
var
  h, t: THandle;
  cn: array [0..254] of Char;
begin
  // disable QQ AD panel
  h := Wnd;
  while GetParent(h) > 0 do h := GetParent(h);
  h := GetWindow(h, GW_CHILD or GW_HWNDFIRST);
  while h > 0 do
  begin
    cn := '';
    GetClassName(h, @cn, SizeOf(cn));
    if cn = '#32770' then   // QQ frame
      begin
      h := GetWindow(h, GW_CHILD or GW_HWNDFIRST);
      while h > 0 do
        begin
          t := GetWindow(h, GW_CHILD or GW_HWNDFIRST);
          if t > 0 then   // has child control
            begin
              GetClassName(h, @cn, SizeOf(cn));
              if cn = 'Static' then   // found!
              begin
                DestroyWindow(t);   // destroy Ad window
              { CreateWindow('Static', 'Hello world!!!', // 这里可以做什么?
// 创建一个Form,用SetParent让你的Form附着在上面的,
// 这样可以用你自己的窗口替换QQ的广告栏,TX一定会非常生气的,
// 为了避免麻烦,最好还是不要做此类事情啦.这里只是讨论方法而已.
// 如果要添加自己的Form,那么你还得用SetWindowLong来Hook WndProc过程,
// 以用来处理WM_CLOSE,确保关闭聊天窗口时能释放你的Form.
                  WS_VISIBLE or WS_CHILD or SS_LEFT,
                  0, 0, 242, 36, h, 0, h, nil); }
                Exit;
              end;
            end;
          h := GetWindow(h, GW_HWNDNEXT);
        end;
      end;
    h := GetWindow(h, GW_HWNDNEXT);
  end;
end;

问题是如何找到QQ聊天窗口中的任意个对象的Handle?
方法可以是EnumWindows列举窗口,从标题栏入手,但是这个方法不保险.最好的做法就是
Hook QQBaseClassInDll.dll中的函数,
QQ2007为?SetUin@CAllInOneStatusBar@@QAEX_JH@Z
QQ2007II Beta为?SetUin@CAllInOneStatusBar@@QAEX_JKH@Z
这个函数用于设置QQ聊天窗口中对方号码的信息用的,调用此函数必定传递一个Handle,这个Handle必定在聊天窗口中的,于是一切好办,剩下要注意的就是Delphi不支持thiscall的,所以Hook这个函数必须用assembler方式.
至于Handle在那里,用MFCSpy2分析就知道,在+0x20那里嘛~
另外此函数同时传递对方的QQ号码,也是目前很多在窗口上现实IP显示的外挂所喜欢Hook的函数之一.

至此一切OK,花了俺2天功夫,大功告成!!

顺便公开另外一个去除广告的方法,此方法不必给QQ程序中打硬补丁,而且兼容性更理想,但是QQ广告下载还是必须Nop掉, 不然广告会照样下载而只是不显示而已.
这就是BasicCtrlDll中的?IsVIP@@YAHPAUIQQCore@@@Z其原型为int __cdecl IsVIP(struct IQQCore *)
和QQHelperDll.dll中的?GetSysBoolData@@YAHPBDAAHH@Z.
OD分析QQAllInOne有:

03605EFF   FF15 38506C03   call   dword ptr [<&BasicCtrlDll.IsVIP>] ; BasicCtr.IsVIP
* ^判断当前登陆的QQ是否为VIP,因为VIP用户是可以关闭QQ广告的
03605F05   8365 FC 00     and   dword ptr [ebp-4], 0
03605F09   8BF0         mov   esi, eax
03605F0B   8D45 FC       lea   eax, dword ptr [ebp-4]
03605F0E   6A 01       push   1
03605F10   50           push   eax
03605F11   68 E8A76D03   push   036DA7E8                   ; ASCII "m_bMemberDisableAD"
03605F16   FF15 206D6C03   call   dword ptr [<&QQHelperDll.GetSysBoolData'>; QQHelper.GetSysBoolData
* ^获取广告显示设置
03605F1C   83C4 10       add   esp, 10
03605F1F   85F6         test   esi, esi
03605F21   5E           pop   esi
03605F22   74 0B       je     short 03605F2F
* ^关键!!! 不是VIP就跳的,所以把这个NOP了
03605F24   837D FC 00     cmp   dword ptr [ebp-4], 0
03605F28   74 05       je     short 03605F2F
* ^关键!!! 没关闭AD就跳,所以再把这个NOP了
03605F2A   6A 01       push   1
03605F2C   58           pop   eax
这样,就实现了去AD了

具体可以使直接NOP代码,或者采用Hook方法:
function IsVIP(pQQCore: Pointer): Integer; cdecl;
begin
  Result := 1;
end;

function GetSysBoolData(AText: PChar; p: Pointer; bIsVIP: Boolean): Integer; cdecl;
// int __cdecl GetSysBoolData(char const *,int &,int)
begin
  if AText = 'm_bMemberDisableAD' then
  begin
    Integer(p^) := 1;
    Result := 1;
    Exit;
  end;
  Result := Call original Func;   调用原函数
end;
[ 此贴被乱梦春秋在2008-07-17 23:49重新编辑 ]
    驿外断桥边,寂寞开无主。已是黄昏独自愁,更著风和雨。无意苦争春,一任群芳妒。零落成泥辗作尘,只有香如故。   
离线leon_h
只看该作者 1楼 发表于: 2009-07-03
恩,好东西
离线quji1234

只看该作者 2楼 发表于: 2009-08-14
我记得有人好像发过类似的软件阿
离线ghoster
只看该作者 3楼 发表于: 2009-08-31
好东西收藏
离线蓶獨噯沵

只看该作者 4楼 发表于: 2009-09-20
哈哈 终于来了
潇洒
离线aa110
只看该作者 5楼 发表于: 2009-09-20
附件不存在 楼主能再发下不  或者发偶邮箱  jundazhu@126.com
感激不尽
离线itlsl
只看该作者 6楼 发表于: 2009-11-22
好东西。。
网站建设开发
离线chenzuchun
只看该作者 7楼 发表于: 2009-12-07
看不懂
离线xydick23
只看该作者 8楼 发表于: 2009-12-26
好帖,顶下
离线冷面飞狐

只看该作者 9楼 发表于: 2010-01-25
怎么我没有下载的权限啊?
快速回复
限100 字节
如果您提交过一次失败了,可以用”恢复数据”来恢复帖子内容
 
上一个 下一个