用Windbg和OllyICE调试Pidgin
由于机器上IM安装较多,就去http://www.pidgin.im/下了一个pidgin,连上了msn,gtalk还有QQ,比较爽,但是近来发现当打开对话窗口时,选中选择字体老是造成程序crash,无奈之下就不只好不改字体。等了3个pidgin新版本,问题依旧。我天真的认为总会被官方fix的。
废话不说,上windbg,开启pidgin,用windbg attach上,一切安好。继续g,依次选中字体,结果问题出现了:
省略若干加载模块。。。。。。
(1ac4.2890): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=002283de ebx=002283f0 ecx=77639d78 edx=002283de esi=00000000 edi=0375d090
eip=68ddf5e8 esp=002282d0 ebp=00228318 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010202
*** ERROR: Symbol file could not be found. Defaulted to export symbols for C:\Program Files\Common Files\GTK\2.0\bin\libcairo-2.dll -
libcairo_2!cairo_scaled_font_text_extents+0x18:
68ddf5e8 8b4604 mov eax,dword ptr [esi+4] ds:0023:00000004=????????
出现了一个Access violation,出现在libcairo_2!cairo_scaled_font_text_extents里,打开register,发现ESI是0,可不访问错误。
打开disassembly,汇编码如下:
2 68ddf5d0 55 push ebp
3 68ddf5d1 89e5 mov ebp,esp
4 68ddf5d3 56 push esi
5 68ddf5d4 53 push ebx
6 68ddf5d5 83ec40 sub esp,40h
7 68ddf5d8 8b7508 mov esi,dword ptr [ebp+8]
8 68ddf5db c745f000000000 mov dword ptr [ebp-10h],0
9 68ddf5e2 8b550c mov edx,dword ptr [ebp+0Ch]
10 68ddf5e5 8b5d10 mov ebx,dword ptr [ebp+10h]
11 68ddf5e8 8b4604 mov eax,dword ptr [esi+4] ds:0023:00000004=????????
12 68ddf5eb 85c0 test eax,eax
13 68ddf5ed 0f858d000000 jne libcairo_2!cairo_scaled_font_text_extents+0xb0 (68ddf680)//goto ZERO_EXTENTS
14 68ddf5f3 85d2 test edx,edx
15 68ddf5f5 0f8485000000 je libcairo_2!cairo_scaled_font_text_extents+0xb0 (68ddf680)// goto ZERO_EXTENTS
16 68ddf5fb 89542414 mov dword ptr [esp+14h],edx
17 68ddf5ff 31c0 xor eax,eax
18 68ddf601 d9ee fldz
19 68ddf603 8944242c mov dword ptr [esp+2Ch],eax
20 68ddf607 31c0 xor eax,eax
21 68ddf609 b9ffffffff mov ecx,0FFFFFFFFh
22 68ddf60e 89442428 mov dword ptr [esp+28h],eax
23 68ddf612 31c0 xor eax,eax
24 68ddf614 89442424 mov dword ptr [esp+24h],eax
25 68ddf618 8d45f4 lea eax,[ebp-0Ch]
26 68ddf61b 89442420 mov dword ptr [esp+20h],eax
27 68ddf61f 8d45f0 lea eax,[ebp-10h]
28 68ddf622 8944241c mov dword ptr [esp+1Ch],eax
29 68ddf626 894c2418 mov dword ptr [esp+18h],ecx
30 68ddf62a dd54240c fst qword ptr [esp+0Ch]
31 68ddf62e dd5c2404 fstp qword ptr [esp+4]
32 68ddf632 893424 mov dword ptr [esp],esi
33 68ddf635 e866f8ffff call libcairo_2!cairo_scaled_font_text_to_glyphs (68ddeea0)
34 68ddf63a 85c0 test eax,eax
35 68ddf63c 7532 jne libcairo_2!cairo_scaled_font_text_extents+0xa0 (68ddf670)
36 68ddf63e 895c240c mov dword ptr [esp+0Ch],ebx
37 68ddf642 8b45f4 mov eax,dword ptr [ebp-0Ch]
38 68ddf645 89442408 mov dword ptr [esp+8],eax
39 68ddf649 8b45f0 mov eax,dword ptr [ebp-10h]
40 68ddf64c 893424 mov dword ptr [esp],esi
41 68ddf64f 89442404 mov dword ptr [esp+4],eax
42 68ddf653 e8f8fcffff call libcairo_2!cairo_scaled_font_glyph_extents (68ddf350)
43 68ddf658 8b45f0 mov eax,dword ptr [ebp-10h]
44 68ddf65b 890424 mov dword ptr [esp],eax
45 68ddf65e e83dcd0600 call libcairo_2!cairo_svg_surface_create_for_stream+0x3fc80 (68e4c3a0)
46 68ddf663 83c440 add esp,40h
47 68ddf666 5b pop ebx
48 68ddf667 5e pop esi
49 68ddf668 5d pop ebp
50 68ddf669 c3 ret//return
51 68ddf66a 8db600000000 lea esi,[esi]
52 68ddf670 89442404 mov dword ptr [esp+4],eax
53 68ddf674 893424 mov dword ptr [esp],esi
54 68ddf677 e864d4ffff call libcairo_2!cairo_pattern_get_radial_circles+0x1660 (68ddcae0)
55 68ddf67c 8d742600 lea esi,[esi]
56 68ddf680 d9ee fldz
57 68ddf682 dd13 fst qword ptr [ebx]//最后六个赋值
58 68ddf684 dd5308 fst qword ptr [ebx+8]
59 68ddf687 dd5310 fst qword ptr [ebx+10h]
60 68ddf68a dd5318 fst qword ptr [ebx+18h]
61 68ddf68d dd5320 fst qword ptr [ebx+20h]
62 68ddf690 dd5b28 fstp qword ptr [ebx+28h]
63 68ddf693 83c440 add esp,40h
64 68ddf696 5b pop ebx
65 68ddf697 5e pop esi
66 68ddf698 5d pop ebp
67 68ddf699 c3 ret
68
看不懂什么意思。我记得cairo好像是开源的图形包啊,去下一个来看看,下了一个最新版,打开了cairo-scaled-font.c,找到了cairo_scaled_font_text_extents函数:
2 const char *utf8,
3 cairo_text_extents_t *extents)
4 {
5 cairo_status_t status;
6 cairo_glyph_t *glyphs = NULL;
7 int num_glyphs;
8
9 if (scaled_font->status)// scaled_font是空,所以访问错误
10 goto ZERO_EXTENTS;
11
12 if (utf8 == NULL)
13 goto ZERO_EXTENTS;
14
15 status = cairo_scaled_font_text_to_glyphs (scaled_font, 0., 0.,
16 utf8, -1,
17 &glyphs, &num_glyphs,
18 NULL, NULL,
19 NULL);
20 if (status)
21 goto ZERO_EXTENTS;
22
23 cairo_scaled_font_glyph_extents (scaled_font, glyphs, num_glyphs, extents);
24 free (glyphs);
25
26 return;
27
28 ZERO_EXTENTS:
29 extents->x_bearing = 0.0;
30 extents->y_bearing = 0.0;
31 extents->width = 0.0;
32 extents->height = 0.0;
33 extents->x_advance = 0.0;
34 extents->y_advance = 0.0;
35 }
36
对着源代码,比较容易读懂上面的汇编码,大家不要弄清楚每行指令的含义,只要能看懂基本的流程就行,我也做了简单的注释。错误是出现在:
看esi是在前面68ddf5d8 8b7508 mov esi,dword ptr [ebp+8]给赋的值,再结合后面test指令和源代码,估计错误行对应的是:
goto ZERO_EXTENTS;
而[esi+4]就是通过偏移来访问变量,而这个scaled_font指针恰好是个NULL,所以才造成了访问错误。
怎么解决呢?首先下了一个最新的Cairo包,替换,问题依旧。可以下个源码包,自己编译一个?那我得把整个gtk+给编译一遍,还不加那些可怕的dependency,还得装cygwin,不由得印堂发黑,估计没有个把小时是搞不定的。
这时候我想起了OllyICE,也是调试的利器,和windbg不同的是它可以改写汇编码,也就是说可以crack软件。打开,同样attach到pidgin,同样crash在同样的错误行,把mov eax,dword ptr [esi+4]改为jmp 68ddf680;对应的源码就是直接改为goto ZERO_EXTENTS或是直接NOP掉。保存libcairo-2.dll,收工,至此pidgin的字体窗口能顺利打开了,而且也能正确设置字体了,半个小时解决问题。这只是个临时的解决办法,最好的办法是报个bug给gtk+。
后记:我在linux上用pidgin是没有问题的,估计是gtk+在win32上有bug。