【文章标题】: BitComet软件中附带广告的去除
【文章作者】: 火翼[CCG]
【作者邮箱】: wyyy@263.net
【作者主页】: 无
【作者QQ号】: 网上能查到
【软件名称】: Bitcomet
【软件大小】: 2.5M
【下载地址】: 自己搜索下载
【加壳方式】: 无
【保护方式】: N多广告
【编写语言】: MFC
【使用工具】: OllyDbg
【操作平台】: XP
【软件介绍】: 使用很广泛的BT下载软件
【作者声明】: 不知道为什么,很讨厌共享软件里面的广告
--------------------------------------------------------------------------------
【详细过程】
BitComet是目前使用最广泛的Bt下载软件,但它最近的几个版本不仅功能没有什么进步,还加入了大量的网页式广告。由于目前最新的0.72版并不稳定,在很多机器上出现了CPU占用100%等其他一些问题,所以本文针对的是BitComet的最近的一个稳定版本--0.70。
BitComet的广告嵌入在软件的很多地方,所以对调试者的耐心是一个挑战。
首先,查找广告的位置和类型,BitComet软件的广告包括主窗体的广告和制作Torrent文件窗口,任务属性页面,视频预览页面的广告。是用Visual C++附带的Spy++工具就能发现这些页面中的广告除了主窗体中间的广告是图片外,其他的都是嵌入软件内部的IE内核的浏览器对象。
然后正式开始调试过程,还是老规矩,使用Peid 0.94检测程序是否加壳,发现程序没有加壳。直接使用IDA 5.0载入,开始反汇编,IDA 自动加载了VC和MFC的函数库进行分析,结束后可以看到大部分MFC封装的类方法已经被自动识别了出来。下面就针对不同页面里的广告进行动态调试,使用的软件是OllyDbg 1.10。
首先是和其他地方都不一样的主页面中间的图片广告,此处图片在软件刚开始时显示的是Paypal的捐款连接,之后才会变为广告图片,那么就从Paypal的图片入手,使用eXeScope v6.5查看可执行文件的资源后,发现Paypal的图片在资源中的名字为PAYPAL.GIF,在IDA中按ALT+T查找PAYPAL,可以找到如下地址
.text:00410F48 push offset aPaypal_gif ; "PAYPAL.GIF"
.text:00410F4D mov [edi], eax
.text:00410F4F call sub_401670
这部分代码属于函数sub_410E70,向上查找调用这个函数的地址,发现只有一处调用
.text:0042AD4E jz short loc_42AD6B
.text:0042AD50 push eax
.text:0042AD51 call sub_410E70 ; 创建捐款对象
试着把上面的调用上面的jz short loc_42AD6B(74 1B)改为jmp loc_42AD6B(EB 1B)后运行,发现此处广告已经不会出现,第一处广告处理结束。
接下来就要处理左侧工具条里面的的搜索页面,使用Spy++软件可以查出主工具条下方的频道/搜索/IE小工具条使用了ReBar和ToolBar两种控件,熟悉MFC的程序员肯定马上就能想到他的实现方式,不熟悉的也没关系,只要去搜索引擎上查找Rebar或者ToolBar就能查到你想要的全部东西。查完资料后,我们知道了CToolBar的按钮要使用CToolBar::SetButtonText对按钮显示的文字进行设置,所以我们在IDA的Name窗口中找到CToolBar::SetButtonText,并在记下函数开始的地方,然后运行OllyDbg,在刚才记下的地址下断点,断下后,观察堆栈中的参数指向的字符串,找到设置我们要寻找的按钮的文字的地方,这里有两点需要注意,一个是本程序使用Unicode编码,所以内存中的字符串格式基本都是Unicode,另一个是由几个地方都把按钮设置成了我们要查找的按钮上的文字,不过通过修改堆栈中的字符串参数指针,并观察程序的变化,我们最后可以找到真正的设置地点是:
.text:0042EC4D push 821Bh//按钮的Command ID
.text:0042EC52 mov ecx, esi
.text:0042EC54 call CToolBar::CommandToIndex(uint)
//由Command ID返回按钮的编号
.text:0042EC59 push eax
.text:0042EC5A mov ecx, esi
.text:0042EC5C call CToolBar::SetButtonText(int,wchar_t const *)
//设置按钮文字
.text:0042EC61 push offset aIe ; "IE"
.text:0042EC66 push 821Ah
.text:0042EC6B mov ecx, esi
.text:0042EC6D call CToolBar::CommandToIndex(uint)
.text:0042EC72 push eax
.text:0042EC73 mov ecx, esi
.text:0042EC75 call CToolBar::SetButtonText(int,wchar_t const *)
.text:0042EC7A cmp dword_6C1B2C, 8
.text:0042EC81 mov eax, dword_6C1B18
.text:0042EC86 jnb short loc_42EC8D
.text:0042EC88 mov eax, offset dword_6C1B18
.text:0042EC8D loc_42EC8D: ; CODE XREF: SetToolBarButtonText+56 j
.text:0042EC8D push eax
.text:0042EC8E push 8229h
.text:0042EC93 mov ecx, esi
.text:0042EC95 call CToolBar::CommandToIndex(uint)
.text:0042EC9A push eax
.text:0042EC9B mov ecx, esi
.text:0042EC9D call CToolBar::SetButtonText(int,wchar_t const *)
这个函数根据选择的语言对3个按钮上显示的文字进行设置,在IDA中自定义此函数的名字为SetToolBarButtonText,然后在OllyDbg中继续执行,可以找到调用这个函数的地方42E6D1,此处代码如下:
.text:0042E68E push 3
.text:0042E690 push 0
.text:0042E692 mov ecx, edi
.text:0042E694 call CToolBar::SetButtons(uint const *,int)
//创建3个空按钮
.text:0042E699 push 4
.text:0042E69B push 10h
.text:0042E69D push 821Bh//Command ID
.text:0042E6A2 push 0
.text:0042E6A4 mov ecx, edi
.text:0042E6A6 call CToolBar::SetButtonInfo(int,uint,uint,int)
//设置第1个按钮的属性
.text:0042E6AB push 11h
.text:0042E6AD push 10h
.text:0042E6AF push 8229h//Command ID
.text:0042E6B4 push 1
.text:0042E6B6 mov ecx, edi
.text:0042E6B8 call CToolBar::SetButtonInfo(int,uint,uint,int)
//设置第2个按钮的属性,全部改为NOP
.text:0042E6BD push 0Fh
.text:0042E6BF push 10h
.text:0042E6C1 push 821Ah//Command ID
.text:0042E6C6 push 2 ;此处改为1
.text:0042E6C8 mov ecx, edi
.text:0042E6CA call CToolBar::SetButtonInfo(int,uint,uint,int)
//设置第3个按钮的属性
.text:0042E6CF mov ecx, esi
.text:0042E6D1 call SetToolBarButtonText
//设置按钮文字
由于我们要去掉第二个按钮搜索,所以我们把创建按钮的参数改为2,并把对应第2个按钮的设置部分改为NOP,由于修改后只有2个按钮所以原来的设置第3个按钮属性的SetButtonInfo函数的指示按钮编号的参数要改为1,修改后测试完全正常,继续进行下一步——主页面内容简介按钮和对应页面的去除。
内容简介按钮所属的控件为TreeView,在IDA中找到CTreeCtrl::InsertItem函数的位置,并在OllyDby中下断,根据堆栈中第2个参数(项目名称)找到调用的地方为sub_431D90 函数的4320DC,继续向上跟踪找到调用sub_431D90的地方为sub_433D50函数内的433E21,此处代码为:
.text:00433E10 mov eax, [esp+28h]
.text:00433E14 cmp eax, edi
.text:00433E16 jz short loc_433E63 ;
//判断是否已经全部完成,此处跳过将完全不在此TrewView中创建任何按钮
.text:00433E18 mov ecx, [esp+20h]
.text:00433E1C push ebp
.text:00433E1D push ebx
.text:00433E1E push eax
.text:00433E1F mov edx, esi
.text:00433E21 call sub_431D90 ; //创建Item
.text:00433E26 mov eax, [esp+28h]
.text:00433E2A cmp eax, edi
.text:00433E2C jz short loc_433E3A
.text:00433E2E mov ecx, [eax]
.text:00433E30 mov edx, [ecx+8]
.text:00433E33 push eax
.text:00433E34 call edx
.text:00433E36 mov [esp+28h], edi
.text:00433E3A loc_433E3A: CODE XREF: sub_433D50+DC j
.text:00433E3A mov eax, [esp+0Ch]
.text:00433E3E mov ecx, [eax]
.text:00433E40 lea edx, [esp+28h]
.text:00433E44 push edx
.text:00433E45 push eax
.text:00433E46 mov eax, [ecx+24h]
.text:00433E49 call eax
.text:00433E4B test eax, eax
.text:00433E4D jge short loc_433E10//继续创建下一个Item
这里不太好改,继续看看下面的代码,从sub_433D50返回sub_46DE20函数的46DF1F处,向下继续执行到
.text:0046E091 push esi ; lParam
.text:0046E092 push 9 ; wParam
.text:0046E094 push TVM_SELECTITEM ; Msg
.text:0046E099 push eax ; hWnd
.text:0046E09A call ds:SendMessageW
此处是设置TreeView的当前选择的Item,我们可以利用这里去掉TreView里面的内容简介,只需要把TVM_SELECTITEM(0x1110B)改为TVM_DELETEITEM(0x1101),并把wParam设为1就可以删除TreeView中的第一项,但这样只是去掉了TreeView中的内容简介,程序执行时还是会默认显示内容简介页。由于点击其它项的时候主页面搜索页会被隐藏,所以在OllyDby中的Cmd工具条中输入bpx ShowWindow,对主程序中所有调用ShowWindow函数的地方设置断点,设置后点击TreeView中的任务列表后断在56A29D处,此处属于MFC标准类函数CWnd::ShowWindow,继续跟踪,返回4C28B6处
.text:004C289C mov [esp+14h+arg_4], eax
.text:004C28A0 jz short loc_4C28B6
.text:004C28A2 push 0 ; dwNewLong
.text:004C28A4 mov ecx, eax
.text:004C28A6 call CWnd::SetDlgCtrlID(int)
.text:004C28AB mov ecx, [esp+14h+arg_4]
.text:004C28AF push 0 ; nCmdShow
.text:004C28B1 call CWnd::ShowWindow(int)//隐藏
.text:004C28B6 loc_4C28B6; CODE XREF: SwitchWindowVisiable+B0 j
.text:004C28B6 push ebp
.text:004C28B7 push ebx
.text:004C28B8 mov ecx, esi
.text:004C28BA call CSplitterWnd::IdFromRowCol(int,int)
.text:004C28BF mov ebx, [esp+14h+var_4]
.text:004C28C3 push eax ; dwNewLong
.text:004C28C4 mov ecx, ebx
.text:004C28C6 call CWnd::SetDlgCtrlID(int)
.text:004C28CB push 5 ; nCmdShow
.text:004C28CD mov ecx, ebx
.text:004C28CF call CWnd::ShowWindow(int) ; 显示
这个函数是根据传入的行和列设定当前显示的页面,在函数开始的地方设置断点,然后在OllyDbg中重新加载程序,可以发现当调用参数为(3,1)的时候为显示搜索页面,代码如下:
.text:0044551E push 3;改为push 1
.text:00445520 push 1;改为push 1
.text:00445522 Call CSplitterWnd::SetActivePane
修改传入的参数为(1,1)即可让程序执行时默认访问任务摘要页面。
然后就是3个子窗口上的广告了,这个3个窗口上广告的调用方式都差不多,我们先来看制作Torrent文件窗口上的广告,使用OllyDbg加载程序运行后,点开制作Torrent文件窗口,然后在在IDA中查出的CDialog::OnInitDialog(void)地址处设断点,点击内容简介,程序中断,函数返回地址为sub_43EC50函数的43EC86,这个函数很长,包括很多控件的初始化过程,继续跟踪,直到这个函数快要结束的地方,
.text:0043F0BE call sub_443B60//创建对象
.text:0043F0C3 push 0
.text:0043F0C5 push 0
.text:0043F0C7 push 300000h
.text:0043F0CC mov ecx, esi
.text:0043F0CE call CWnd::ModifyStyle(ulong,ulong,uint)
.text:0043F0D3 cmp dword_6C2590, 8
.text:0043F0DA mov eax, dword_6C257C
.text:0043F0DF jnb short loc_43F0E6
.text:0043F0E1 mov eax, offset dword_6C257C
.text:0043F0E6 loc_43F0E6:CODE XREF: .text:0043F0DF j
.text:0043F0E6 push 0
.text:0043F0E8 push 0
.text:0043F0EA push 0
.text:0043F0EC push 0
.text:0043F0EE push 0
.text:0043F0F0 push eax
.text:0043F0F1 mov ecx, esi//网址字符串指针
.text:0043F0F3 call sub_5715C6//访问指定网址
.text:0043F0F8 mov eax, [ebp+224h]
.text:0043F0FE push 0
.text:0043F100 push 0
.text:0043F102 push CB_SETCURSEL
.text:0043F107 push eax
.text:0043F108 call ds:SendMessageW
由于执行到43F0F3时堆栈中的参数为http://bcsearch.mdbchina.com/cache/index.html,很明显这个函数就是用来访问网页的,向上浏览代码,看到43F0BE处的对sub_443B60的调用十分可疑,试着把43F0BE到43F0FE之前的全部代码都改成NOP后重新打开制作Torrent文件页,广告果然没有出现,说明猜想是正确的,sub_443B60就是IE内核浏览器对象的创建函数,在这个函数的入口点上设置断点后在点击任务属性和预览视频文件分别能找到类似的代码,都改成NOP就可以了,具体代码如下:
任务属性窗口:
.text:0043CC40 call CreateIECoreObject
// 此处已把函数sub_443B60名称改为CreateIECoreObject
.text:0043CC45 push 0
.text:0043CC47 push 0
.text:0043CC49 push 300000h
.text:0043CC4E mov ecx, ebp
.text:0043CC50 call CWnd::ModifyStyle(ulong,ulong,uint)
.text:0043CC55 cmp dword_6C2590, 8
.text:0043CC5C mov eax, dword_6C257C
.text:0043CC61 jnb short loc_43CC68
.text:0043CC63 mov eax, offset dword_6C257C
.text:0043CC68 loc_43CC68: ; CODE XREF: .text:0043CC61 j
.text:0043CC68 push 0
.text:0043CC6A push 0
.text:0043CC6C push 0
.text:0043CC6E push 0
.text:0043CC70 push 0
.text:0043CC72 push eax
.text:0043CC73 mov ecx, ebp
.text:0043CC75 call VisitURL
视频预览窗口:
.text:0043F879 lea ebx, [esi+0CCh]
.text:0043F87F push ebx
.text:0043F880 mov ecx, esi
.text:0043F882 mov edi, 468h
.text:0043F887 call CreateIECoreObject
.text:0043F88C push 1 ; bEnable
.text:0043F88E mov ecx, ebx
.text:0043F890 call CWnd::EnableWindow(int)
.text:0043F895 push 1 ; nCmdShow
.text:0043F897 mov ecx, ebx
.text:0043F899 call CWnd::ShowWindow(int)
.text:0043F89E push 2 ; uFlags
.text:0043F8A0 push 3Eh ; cy
.text:0043F8A2 push 1D5h ; int
.text:0043F8A7 push 0 ; Y
.text:0043F8A9 push 0 ; X
.text:0043F8AB push offset dword_7D3B40 ; int
.text:0043F8B0 mov ecx, ebx
.text:0043F8B2 call $LN4
.text:0043F8B7 push 0
.text:0043F8B9 push 0
.text:0043F8BB push 0B00000h
.text:0043F8C0 mov ecx, ebx
.text:0043F8C2 call CWnd::ModifyStyle(ulong,ulong,uint)
.text:0043F8C7 push 0
.text:0043F8C9 push 0
.text:0043F8CB push 20300h
.text:0043F8D0 mov ecx, ebx
.text:0043F8D2 call CWnd::ModifyStyleEx(ulong,ulong,uint)
.text:0043F8D7 mov dword ptr [esi+1C8h], 1
.text:0043F8E1 call sub_43FD40
去掉了视频预览页的广告后,为了美观还要用资源编辑软件把文件列表的窗体改大,挡住原来广告的位置,显示部分的修改就到此结束了,下面说一下三个和广告显示无关的优化。
第一个是BitComet软件运行后会自动试图向他的服务器发送UDP数据,对软件中包含的广告进行更新,只要去掉下面的代码就可以禁止这个部分数据的发送和接收。
接收部分:
.text:005632F7 call esi ; WaitForMultipleObjects
.text:005632F9 test eax, eax
.text:005632FB jz short loc_563322 //改为jmp short loc_563322即可
.text:005632FD lea ecx, [ecx+0]
.text:00563300 loc_563300: ; CODE XREF: sub_5632C0+60 j
.text:00563300 cmp eax, 1
.text:00563303 jz short loc_56330C
.text:00563305 cmp eax, WAIT_TIMEOUT
.text:0056330A jnz short loc_563311
.text:0056330C loc_56330C: ; CODE XREF: sub_5632C0+43 j
.text:0056330C call sub_563160//接收UDP数据
.text:00563311 loc_563311: ; CODE XREF: sub_5632C0+4A j
.text:00563311 push 0FFFFFFFFh ; dwMilliseconds
.text:00563313 push 0 ; bWaitAll
.text:00563315 lea ecx, [esp+20h+Handles]
.text:00563319 push ecx ; lpHandles
.text:0056331A push 2 ; nCount
.text:0056331C call esi ; WaitForMultipleObjects
.text:0056331E test eax, eax
.text:00563320 jnz short loc_563300
.text:00563322 push offset stru_7D22B8 ; lpCriticalSection
发送部分:
.text:0056359D call ebx ; EnterCriticalSection
.text:0056359F mov eax, dword_6C8338
.text:005635A4 xor ebp, ebp
.text:005635A6 cmp [eax+14h], ebp
.text:005635A9 jz loc_5637BF//改为jmp loc_5637BF
第二个是BitComet针对5q校园网的下载限制的去除,在BitComet的中IDA分析页面中搜索bt.5qzone.net,就可以找到判断tracker服务器网址中是否包含bt.5qzone.net的部分,跳过这个判断就可以避开对校园网下载的速度限制,具体代码如下:
.text:005610B9 mov eax, offset aBt_5qzone_net ; "bt.5qzone.net"
.text:005610BE mov [esp+0B4h+var_C], 22h
.text:005610C9 lea edx, [eax+2]
.text:005610CC lea esp, [esp+0]
.text:005610D0 loc_5610D0: ; CODE XREF: sub_560610+AC9 j
.text:005610D0 mov cx, [eax]
.text:005610D3 add eax, 2
.text:005610D6 cmp cx, bx
.text:005610D9 jnz short loc_5610D0
.text:005610DB sub eax, edx
.text:005610DD sar eax, 1
.text:005610DF push eax ; 长度
.text:005610E0 push ebx
.text:005610E1 push offset aBt_5qzone_net ; "bt.5qzone.net"
.text:005610E6 lea ecx, [esp+0C0h+var_34]
.text:005610ED call sub_420150 ; 判断函数
.text:005610F2 cmp eax, 0FFFFFFFFh
.text:005610F5 jz loc_56132A;改为jmp loc_56132A
最后是禁止BitComet软件自动连接升级服务器进行自动更新检测:
在IDA中按Alt+T打开Text search,在string框中输入http://update.bitcomet.com/bitcomet/后按OK,就可以找到引用升级网页的地址,检查字符串的交叉引用发现只有函数sub_419E40种的419FBE一个地方引用了此升级网址,代码如下:
.text:00419FBE push offset aHttpUpdate_bit
// "http://update.bitcomet.com/bitcomet/?ve"...
.text:00419FC3 push edx
.text:00419FC4 mov [esp+124h+var_84], ebp
.text:00419FCB mov [esp+124h+var_88], ebx
.text:00419FD2 mov word ptr [esp+124h+var_98], bx
.text:00419FDA call sub_4125F0
向后看一下函数的代码就可以发现这个函数作用是生成访问判断是否更新网页的网址链接字符串,向上来到函数sub_419E40的开始处,可以找到如下代码:
.text:00419E75 mov large fs:0, eax
.text:00419E7B mov eax, [esp+118h+arg_0]
.text:00419E82 xor ebx, ebx
.text:00419E84 cmp dword_7D581C, ebx
.text:00419E8A mov [esp+118h+var_E4], eax
.text:00419E8E jz loc_41A44F
只要把419E8E处的jz loc_41A44F(0F 84 BB 05 00 00)改为jmp loc_41A44F(90 E9 BB 05 00 00)即可阻止生成访问链接,也就禁止了BitComet的自动更新检测。
到此,BitComet软件就差不多真的成为绿色软件了。
--------------------------------------------------------------------------------
【版权声明】: 本文原创于看雪技术论坛, 转载请注明作者并保持文章的完整, 谢谢!
2006年12月18日 23:26:37