关闭

[置顶] LYSoft之QQ去广告+本地会员外挂原理

6147人阅读 评论(0) 收藏 举报

LYSoft出品之QQ去广告+本地会员原理 (QQ2007,QQ2008和QQ2009适用)

 

QQExt采用自动代码分析,支持QQ2007QQ2008,并支持QQ程序没架构改动下的一切新版

下载 LYSoft QQExt

QQ 2009即将支持

 

LYSoft出品之QQ去广告+本地会员外挂原理 (QQ2007,QQ 2008QQ2009版本)

 

[color=LimeGreen]最近更新内容: QQ2009去广告+本地会员(可去广告显示+表情涂鸦)

[/color]

 

LYSoft QQExt采用自动代码分析,支持QQ2007QQ2008的全部版本的去广告和本地会员功能(不支持IP显示功能)[url=/lysoft/QQExt.rar]下载 v81003[/url]

QQ2009的支持,迟些时候会加进去的.

 

088月针对QQ2007而写

 

QQ2007QQ2008第一版的显示IP主要技术原理:(by LYSoft Liu Yang)

 

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.这个事情好办,QQBasicCtrlDll.dll?GetFriendQQData@@YAHPAUIQQCore@@KPAPAUIQQData@@@Z方法,就是从IQQCoreUIN获得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的人很容易就能理解的,其实这些代码也是来自QQDASM工程.

注意一下接口调用和Cdecl就行了,因为用Delphi写的,所以不好直接支持C++thiscall,故采用BASM方式来调用~

 

QQ2007QQ2008去广告原理:(by LYSoft LiuYang)

 

有人发现把目录AD下全部文件和DatAd.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聊天窗口中任意一个WinControlHandle就能轻松用代码干掉广告窗口的.

 

procedure DisableQQAd(Wnd: LongInt);

label DoNext;

var

  h, t: THandle;

  cn: array [0..254] of Char;

  function RemoveAdLabel(hStatic: THandle): Boolean;

  begin

    Result := False;

    GetClassName(hStatic, @cn, SizeOf(cn));

    if cn = 'Static' then  // class name should be "Static"

       if GetWindowText(hStatic, @cn, SizeOf(cn)) > 0 then

          if Trim(cn) <> '' then  // if Static control contain any Text

             begin

               DestroyWindow(hStatic);  // remove it!

               Result := True;

               Exit;

             end;

  end;

begin

  // get root Win control

  while GetParent(Wnd) > 0 do Wnd := GetParent(Wnd);

  // remove QQ Ad url label

  h := GetWindow(Wnd, GW_CHILD or GW_HWNDFIRST);

  while h > 0 do

    begin  // search child controls in chat dialog root

      cn := '';

      // for QQ 2008 final or above

      if RemoveAdLabel(h) then goto DoNext;

      // for QQ 2007 II to 2008 beta

      if cn = '#32770' then  // QQ frame

         begin  // searh child controls in frame control "#32770"

           h := GetWindow(h, GW_CHILD or GW_HWNDFIRST);

           while h > 0 do

             begin

               if RemoveAdLabel(h) then goto DoNext;

               h := GetWindow(h, GW_HWNDNEXT);

             end;

         end;

      h := GetWindow(h, GW_HWNDNEXT);

    end;

  DoNext:

  // remove QQ AD panel

  h := GetWindow(Wnd, 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,那么你还得用SetWindowLongHook 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的函数之一.

 

到此时,就显IP+去广告上一切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;

 

说到这里,肯定有人会说,如果实现了本地会员,那就不用这么麻烦了嘛?!

显然,这是个捷径,实现起来也不难,关键是找到突破口

 

QQ2007QQ2008本地会员原理:(by LY Soft Liu Yang)

 

经过DASM分析,QQHelperDll是个入手点

PEExplorer认真查找看看,果然有发现,那就是IsVipUser@qdatCurrentUser@@QAEHXZ

一个无参数函数,Hook,并让其返回EAX=1,嘿嘿,果然成了本地VIP,这个本地VIP可以享受QQ2007II的涂鸦表情哦~

可是到了这里却仍旧发现QQ的设置上,还是说你是"非会员",不能屏蔽广告,咋办?

显然的是QQQSettingCtrl.dll并没有调用qdatCurrentUser::IsVipUser来判断.那它调用了那个函数呢?

继续努力...N小时后发现!原来是IsQQServiceEnable@@YAHI@Z

这是一个unsigned int入口的函数,估计是服务功能号,由此函数判断当前登录QQ用户可用的服务,于是Hook,不管三七二十一,一律返回EAX=1,再测试...一切OK!

 

至此,QQ外挂可告一段落也~~

 

不经意又到了09417,此文发布已经大半年了,QQ2009正式版刚好推出

以前的QQ2009Beta版本,偶就懒的动了,正式版嘛,还要看看的

 

QQ2009本地会员原理:(by LYSoft Liu Yang)

 

PE Explorer分析看了,新的QQ2009采用COM结构,以前的本地VIP之类的功能都不能用了

忍不住分析看看,仍见IsVIP函数,但是已经失效,Hook了毫无效果

 

找了1个多小时,终于发现秘密,原来QQ2009已经换了函数来处理这个

KernelUtil?IsFlagValid@Contact@Util@@YAHKK@Z

就是判断QQ可用的服务,其实质处理的事情就是QQ2008IsQQServiceEnable@@YAHI@Z

 

这次破解连反汇编和动态调试都用不上,一个看DLL ImportExport表的工具足矣,呵呵

 

至于去除广告下载就懒得弄的,删除用户Application Data/Tencent/QQ/Misc/com.tencent.advertisement文件夹,建立一个文件,com.tencent.advertisement,不需要内容的,这样QQ2009就没办法写入广告文件了

 

09418:(by LYSoft Liu Yang)

 

用昨日的方法,会出现一个问题,就是所有好友都成了VIP,因为IsFlagValid对所有QQ联系人都会调用,用来判断其服务标记,它有两个参数,都是DWORD,第一个是QQ号码,第二个是服务类型,具体细节不清楚,不过这无关紧要.

 

说到这里,该知道如何处理了吧?那就是IsFlagValid(QQUIN,Flag)调用时,QQUIN为当前登陆帐号时返回1即可开启本地会员而其它情况就调用正常的处理流程,这样别的好友不受影响了.

 

问题又来了~怎么知道当前QQ登陆号码?QQ2007QQ2008的年代,GetCurrentUin这个函数,QQ2009也有,但问题在于,QQ2008上有IsLogon可以获得IQQCore,QQ2009没有这个函数,而且也不再用IQQCore

 

认真寻觅一番,又有发现:同为Contact管理的GetSelfUin函数!它是无参数的,调用后EAX=当前登陆QQ号码,这下本地会员完美实现了.

 

继续研究一下QQ广告下载逻辑,有这个发现:QQ/Plugin/Com.Tencent.Advertisement/bin下有COM插件Advertisement.dll,明显是广告下载和显示用的,删除之,QQ2009拒绝工作,看来TX还是有保护的

IDA分析Advertisement.dll,有此发现

 

.text:62027DF8 ; protected: virtual unsigned int __thiscall CTXHttpDownload::Run(void)

.text:62027DF8 ?Run@CTXHttpDownload@@MAEIXZ proc near  ; DATA XREF: .rdata:6202E608o

.text:62027DF8                 jmp     ds:__imp_?Run@CTXHttpDownload@@MAEIXZ ; CTXHttpDownload::Run(void)

.text:62027DF8 ?Run@CTXHttpDownload@@MAEIXZ endp

 

Advertisement会调用Common.dll来下载广告,强制在CTXHttpDownloadRun方法上直接Retn,会导致QQ无法更新资源,此方法一刀切,牺牲了正常功能,不可取,那么直接改动AdvertisementImport,JMP见鬼去,修改为Retn,此后QQ2009就不再下载广告了.

 

最后,补充一下,Advertisement在登陆后才加载的,加载后会很快执行下载广告的线程,所以,必须提前加载并处理才能生效.

 

下次有空再继续玩吧,这次就到这里啦^_^

 

--- by LYSoft Liu Yang

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:44242次
    • 积分:664
    • 等级:
    • 排名:千里之外
    • 原创:20篇
    • 转载:0篇
    • 译文:0篇
    • 评论:23条
    文章分类
    最新评论