首页 gdb无-g调试实战记录
文章
取消

gdb无-g调试实战记录

问题背景

设备外发日志的内容错乱,甚至产生 core 文件。版本是正常编译的发行版本,没有附带 -g 加入的调试信息。

我懒得加调试信息也不想加 -g 重新编译,二者在本人的开发环境中都十分麻烦。这种无 -g 调试的场景其实还蛮常见的,这次定位比较顺利,故记录之。

定位过程

从现象上看是日志内容错位了,但也不能排除是踩内存,出界内容被直接解析也能出现类似的现象。通过看代码,我高度怀疑日志结构体在传参进入函数 output_syslog(log_s *in_buffer) 的时候就已经有问题了。没有 -g 的话,就只能进行有限的调试,通过跟踪地址、强看汇编来定位。

gdb 给怀疑的函数 output_syslog() 打断点,命中后,寄存器信息如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
(gdb) b output_syslog 
Breakpoint 1 at 0x43ed38
(gdb) c
Continuing.

Breakpoint 1, 0x000000000043ed38 in output_syslog ()
(gdb) i r
x0             0x7f91acf008     547904876552
x1             0x0      0
x2             0x7ffa1eecd0     549657177296
x3             0x7ffa1f0210     549657182736
x4             0x2000   8192
...

设备是 arm 架构,上面的寄存器名称也是一眼 arm。我们现在刚进入函数 output_syslog,还没有做任何其它操作,所以 x0 就是函数第一个参数的值。0x7f91acf008 就是 in_buffer 的值,也就是结构体 log_s 的首地址。

这个结构体还挺大的,大概长下面这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
typedef struct 
{
    u8      member;
    u8      member;    
    u8      member;
    u8      member;
    u8      member;
    u8      member;
    u8      member;
    u8      member;
    u8      member[MAX_LEN];
    u8      member[MAX_LEN];
    u16     member;
    u16     member;
    u16     member;
    u16     member;
    u16     member;
    u16     member;
    u16     member;
    u16     member[COUNT];
    union {
        in_addrv4_s in_addrv4;
        in_addrv6_s in_addrv6;
    } adrv;
    u32     ip_version;
    u32     member;
    u32     member;
    u32     member;
    u32     member;
    u32     member;
    u32     member;
    u32     member;
    u32     focus_on_this_id;
    u32     member;
    u32     member;
    u32     member;
    s8      host[MAX_LEN];
    s8      url[MAX_LEN];
    ...
} log_s;

我直接从首地址开始以字符的形式打印了 2000 个字节,如下:

1
2
3
4
5
6
(gdb) x/2000bc 0x7f91acf008  
0x7f91acf008:   6 '\006'        1 '\001'        0 '\000'        0 '\000'        2 '\002'        1 '\001'   0 '\000' 2 '\002'
0x7f91acf010:   97 'a'  116 't' 116 't' 97 'a'  99 'c'  107 'k' 95 '_'  98 'b'
0x7f91acf018:   108 'l' 111 'o' 99 'c'  107 'k' 0 '\000'        0 '\000'        0 '\000'        0 '\000'
0x7f91acf020:   0 '\000'        0 '\000'        0 '\000'        0 '\000'        0 '\000'        0 '\000'   0 '\000' 0 '\000'
...

观察发现,前 8 个 u8 的值都是对的,没啥问题,说明到这里数据都还是对的,没有发生错误偏移的迹象。难道说是后面的数据发生了偏移,亦或是后面的数据越界踩内存了吗?继续往后看。由于结构体中有很多成员,且数组成员还比较大,按顺序一个个找成员的话,实在麻烦,况且由于对齐,结构体成员内存分布还不一定是连续的,所以我直接从后往前找。我挑选了一个需要重点关注的变量,focus_on_this_id,根据实际情况和代码,我知道这个值取低 16 位后应该为 10035,但实际值却为 16,现在需要重点搞清这个错误的原因。

注意到这一段:

1
2
3
4
5
6
7
8
9
...
0x7f91acf670:   0 '\000'        0 '\000'        0 '\000'        1 '\001'        0 '\000'        0 '\000'        0 '\000'        7 '\a'
0x7f91acf678:   0 '\000'        0 '\000'        0 '\000'        0 '\000'        16 '\020'       0 '\000'        39 '\'' 51 '3'
0x7f91acf680:   82 'R'  0 '\000'        0 '\000'        17 '\021'       48 '0'  0 '\000'        5 '\005'        0 '\000'
---Type <return> to continue, or q <return> to quit---
0x7f91acf688:   48 '0'  0 '\000'        5 '\005'        1 '\001'        119 'w' 119 'w' 119 'w' 46 '.'
0x7f91acf690:   116 't' 101 'e' 115 's' 116 't' 104 'h' 116 't' 116 't' 112 'p'
0x7f91acf698:   46 '.'  99 'c'  111 'o' 109 'm' 0 '\000'        0 '\000'        0 '\000'        0 '\000'
...

第 6 行开始的 www.testhttp.com 一眼就应该是 host 成员的值。观察结构体,focus_on_this_id 附近都是 u32,如果结构体要填充,应该在前面的 adrv 附近就填充完了,也就是说,从 focus_on_this_idhost 应当都是连续的。大胆假设数据偏移没有错误,即 www.testhttp.com 就是 host 的值,那么往前再数 4 * 4 个字节,就是 focus_on_this_id 的值,如下:

1
2
3
4
5
# from low address to high address
16 '\020'    0 '\000'    39 '\''    51 '3'

# convert to hex
\x10    \x00    \x27    \x33

arm 架构是小端序,所以 focus_on_this_id 的数值为 0x33270010,取低 16 位,其十进制正好为 16,与实际值相同,这说明数据偏移大概率真的没有问题,我们找到的值应该就是变量 focus_on_this_id 的值。进一步,注意到 10035 的十六进制为 0x2733,此刻已经很能说明问题了——高度怀疑是字节序问题。试倒转变量的字节序,数值就为 0x10002733,取低 16 位,即是 10035

分析到这里八九不离十了,直接查代码,好家伙果不其然,就是整个结构都没转字节序。

感悟

虽说问题是内部测试发现的,我可以加 -g 重新编译,但如果是在前方环境定位,哪来的调试信息?

实际上,无 -g 直接上 gdb 调试一样能调,只不过很考验调试者在汇编、指针、内存等底层原理上的功底。

临汇编而不惧,寻指针而不乱,观内存而烛微,此谓之高手也。吾辈当以之为目标,不断精进。

本文由作者按照 CC BY 4.0 进行授权
热门标签

简析HTTPS

用户态源码反汇编定位方法记录

热门标签