【问题标题】:Memory dump formatted like xxd from gdb内存转储格式为 gdb 中的 xxd
【发布时间】:2012-02-10 18:47:58
【问题描述】:

我正在尝试检查一个缓冲区,该缓冲区包含二进制格式的消息,但也包含字符串数据。例如,我正在使用这个 C 代码:

int main (void) {
    char buf[100] = "\x01\x02\x03\x04String Data\xAA\xBB\xCC";

    return 0;
}

我想获得buf 中内容的十六进制转储,格式类似于xxd(我不在乎它是否完全匹配,我真正要找的是十六进制转储与可打印的字符并排)。

在 GDB 中我可以使用类似的东西:

(gdb) x /100bx buf
0x7fffffffdf00: 0x01    0x02    0x03    0x04    0x53    0x74    0x72    0x69
0x7fffffffdf08: 0x6e    0x67    0x20    0x44    0x61    0x74    0x61    0xaa
0x7fffffffdf10: 0xbb    0xcc    0x00    0x00    0x00    0x00    0x00    0x00
0x7fffffffdf18: 0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
0x7fffffffdf20: 0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
0x7fffffffdf28: 0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
0x7fffffffdf30: 0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
0x7fffffffdf38: 0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
0x7fffffffdf40: 0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
0x7fffffffdf48: 0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
0x7fffffffdf50: 0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
0x7fffffffdf58: 0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00

这很好,但很难用这种方式挑选字符串......或者我可以使用

(gdb) x /100bs buf
0x7fffffffdf00:  "\001\002\003\004String Data\252\273\314"
0x7fffffffdf13:  ""
0x7fffffffdf14:  ""
0x7fffffffdf15:  ""
0x7fffffffdf16:  ""
0x7fffffffdf17:  ""
...

这使得阅读二进制部分变得很困难......我正在处理的实际消息中也有很多 ascii nul,所以看起来真的是一团糟。

我能想到的最好办法就是这样做:

(gdb) dump binary memory dump.bin buf buf+100

然后

$ xxd dump.bin
0000000: 0102 0304 5374 7269 6e67 2044 6174 61aa  ....String Data.
0000010: bbcc 0000 0000 0000 0000 0000 0000 0000  ................
0000020: 0000 0000 0000 0000 0000 0000 0000 0000  ................
0000030: 0000 0000 0000 0000 0000 0000 0000 0000  ................
0000040: 0000 0000 0000 0000 0000 0000 0000 0000  ................
0000050: 0000 0000 0000 0000 0000 0000 0000 0000  ................
0000060: 0000 0000                                ....

但每次都这样做很痛苦。我发现有人以前想要这个,所以想知道是否有人找到了在 gdb 中实现它的方法。另外,您会以这种方式丢失原始内存中的地址。

我正在使用内置 python 支持的 GDB 7.4,所以我愿意使用漂亮的打印机或类似设备,但我不知道如何设置。

【问题讨论】:

    标签: python debugging gdb pretty-print


    【解决方案1】:
    (gdb) define xxd
    >dump binary memory dump.bin $arg0 $arg0+$arg1
    >shell xxd dump.bin
    >end
    (gdb) xxd &j 10 
    0000000: 0000 0000 0000 0000 0000 0000 4d8c a7f7  ............M...
    0000010: ff7f 0000 0000 0000 0000 0000 c8d7 ffff  ................
    0000020: ff7f 0000 0000 0000
    

    看起来很简单;-)

    您可以编写一个 Python 脚本(现代 GDB 版本已嵌入 Python 解释器)来做同样的事情,而无需“脱壳”。

    【讨论】:

    • 啊,好主意。没有意识到将这样的快捷方式添加到 GDB 中是如此容易。就像你提到的那样,我最终用 python 编写了这个代码,但我相信我可以使用这个成语。
    • 请注意,如果$arg0 是指向结构的指针,则$arg1 将被视为对象计数,而不是字节数。如果有问题,请使用 ((void *)$arg0)+$arg1 而不是 $arg0+$arg1
    • 另请注意,较新版本的xxd 支持-o 标志,它会为显示的地址值添加偏移量。
    • 这最大的不便就是不保存地址! (我建议按照 Roger 的建议添加 addres -o 标志)
    【解决方案2】:

    所以,我最终玩弄了 python 界面并想出了这个:

    import gdb
    from curses.ascii import isgraph
    
    def groups_of(iterable, size, first=0):
        first = first if first != 0 else size
        chunk, iterable = iterable[:first], iterable[first:]
        while chunk:
            yield chunk
            chunk, iterable = iterable[:size], iterable[size:]
    
    class HexDump(gdb.Command):
        def __init__(self):
            super (HexDump, self).__init__ ('hex-dump', gdb.COMMAND_DATA)
    
        def invoke(self, arg, from_tty):
            argv = gdb.string_to_argv(arg)
            if len(argv) != 2:
                raise gdb.GdbError('hex-dump takes exactly 2 arguments.')
            addr = gdb.parse_and_eval(argv[0]).cast(
                gdb.lookup_type('void').pointer())
            try:
                bytes = int(gdb.parse_and_eval(argv[1]))
            except ValueError:
                raise gdb.GdbError('Byte count numst be an integer value.')
    
            inferior = gdb.selected_inferior()
    
            align = gdb.parameter('hex-dump-align')
            width = gdb.parameter('hex-dump-width')
            if width == 0:
                width = 16
    
            mem = inferior.read_memory(addr, bytes)
            pr_addr = int(str(addr), 16)
            pr_offset = width
    
            if align:
                pr_offset = width - (pr_addr % width)
                pr_addr -= pr_addr % width
    
            for group in groups_of(mem, width, pr_offset):
                print '0x%x: ' % (pr_addr,) + '   '*(width - pr_offset),
                print ' '.join(['%02X' % (ord(g),) for g in group]) + \
                    '   ' * (width - len(group) if pr_offset == width else 0) + ' ',
                print ' '*(width - pr_offset) +  ''.join(
                    [g if isgraph(g) or g == ' ' else '.' for g in group])
                pr_addr += width
                pr_offset = width
    
    class HexDumpAlign(gdb.Parameter):
        def __init__(self):
            super (HexDumpAlign, self).__init__('hex-dump-align',
                                                gdb.COMMAND_DATA,
                                                gdb.PARAM_BOOLEAN)
    
        set_doc = 'Determines if hex-dump always starts at an "aligned" address (see hex-dump-width'
        show_doc = 'Hex dump alignment is currently'
    
    class HexDumpWidth(gdb.Parameter):
        def __init__(self):
            super (HexDumpWidth, self).__init__('hex-dump-width',
                                                gdb.COMMAND_DATA,
                                                gdb.PARAM_INTEGER)
    
        set_doc = 'Set the number of bytes per line of hex-dump'
    
        show_doc = 'The number of bytes per line in hex-dump is'
    
    HexDump()
    HexDumpAlign()
    HexDumpWidth()
    

    我意识到这可能不是最漂亮、最优雅的解决方案,但它可以完成工作并作为初稿使用。它可以包含在~/.gdbinit 中,例如:

    python
    sys.path.insert(0, '/path/to/module/dir')
    import hexdump
    end
    

    然后可以像这样与上面的程序一起使用:

    (gdb) hex-dump buf 100
    0x7fffffffdf00:  01 02 03 04 53 74 72 69 6E 67 20 44 61 74 61 AA  ....String Data.
    0x7fffffffdf10:  BB CC 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
    0x7fffffffdf20:  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
    0x7fffffffdf30:  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
    0x7fffffffdf40:  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
    0x7fffffffdf50:  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
    0x7fffffffdf60:  00 00 00 00                                      ....
    

    以及其他一些很好的措施:

    (gdb) set hex-dump-align on
    Determines if hex-dump always starts at an "aligned" address (see hex-dump-width
    (gdb) hex-dump &buf[5] 95
    0x7fffffffdf00:                 74 72 69 6E 67 20 44 61 74 61 AA       tring Data.
    0x7fffffffdf10:  BB CC 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
    0x7fffffffdf20:  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
    0x7fffffffdf30:  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
    0x7fffffffdf40:  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
    0x7fffffffdf50:  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
    0x7fffffffdf60:  00 00 00 00                                      ....
    
    (gdb) set hex-dump-width 8
    Set the number of bytes per line of hex-dump
    (gdb) hex-dump &buf[5] 95
    0x7fffffffdf00:                 74 72 69       tri
    0x7fffffffdf08:  6E 67 20 44 61 74 61 AA  ng Data.
    0x7fffffffdf10:  BB CC 00 00 00 00 00 00  ........
    0x7fffffffdf18:  00 00 00 00 00 00 00 00  ........
    0x7fffffffdf20:  00 00 00 00 00 00 00 00  ........
    0x7fffffffdf28:  00 00 00 00 00 00 00 00  ........
    0x7fffffffdf30:  00 00 00 00 00 00 00 00  ........
    0x7fffffffdf38:  00 00 00 00 00 00 00 00  ........
    0x7fffffffdf40:  00 00 00 00 00 00 00 00  ........
    0x7fffffffdf48:  00 00 00 00 00 00 00 00  ........
    0x7fffffffdf50:  00 00 00 00 00 00 00 00  ........
    0x7fffffffdf58:  00 00 00 00 00 00 00 00  ........
    0x7fffffffdf60:  00 00 00 00              ....
    

    没有承诺没有错误:)。如果人们有兴趣,我可能会把它贴在 github 或其他地方。

    我只用 GDB 7.4 测试过。

    【讨论】:

    • +1 因为这个解决方案保留了原始地址,非常好用!
    • 我收到ImportError: no module named hexdump 错误
    • 确保已将脚本保存为 hexdump.py 并且 .gdbinit 中的路径正确
    • 很好,很少有东西像初稿那样“开箱即用”!我把我的放在/usr/local/lib/gdb/hexdump.py
    • 这太棒了。但是,我必须更新以下行: pr_addr = int(str(addr), 16) 到 pr_addr = int(str(addr).split()[0], 16)
    【解决方案3】:

    我自己的贡献,来自 Employed Russian solution 和 Roger Lipscombe cmets:

    • 使用 xxd,
    • 保留地址 (xxd -o)
    • size 参数是可选的
    • 包含小文档

    脚本(使用 gdb 7.8.1 测试):

    define xxd
      if $argc < 2
        set $size = sizeof(*$arg0)
      else
        set $size = $arg1
      end
      dump binary memory dump.bin $arg0 ((void *)$arg0)+$size
      eval "shell xxd -o %d dump.bin; rm dump.bin", ((void *)$arg0)
    end
    document xxd
      Dump memory with xxd command (keep the address as offset)
    
      xxd addr [size]
        addr -- expression resolvable as an address
        size -- size (in byte) of memory to dump
                sizeof(*addr) is used by default
    end
    

    例子:

    (gdb) p &m_data
    $1 = (data_t *) 0x200130dc <m_data>
    
    (gdb) p sizeof(m_data)
    $2 = 32
    
    (gdb) xxd &m_data 32
    200130dc: 0300 0000 e87c 0400 0000 0000 0100 0000  .....|..........
    200130ec: 0c01 0000 b831 0020 0100 0000 0100 0000  .....1. ........
    
    (gdb) xxd &m_data
    200130dc: 0300 0000 e87c 0400 0000 0000 0100 0000  .....|..........
    200130ec: 0c01 0000 b831 0020 0100 0000 0100 0000  .....1. ........
    
    (gdb) help xxd
      Dump memory with xxd command (keep the address as offset)
    
      xxd addr [size]
        addr -- expression resolvable as an address
        size -- size (in byte) of memory to dump
                sizeof(*addr) is used by default
    

    【讨论】:

    • 不错!唉,xxd-o 选项在 Centos7 等旧版本中不可用,所以如果有人获得 xxd 帮助页面,这就是原因。您可以改用 od --traditional -A x -t x1z -Ax dump.bin +0 +$1 来打印原始地址和偏移地址。
    • 实际上,eval "shell od --traditional -t x1z -A x dump.bin +0 +%p", (void*)($arg0) 可以胜任。
    【解决方案4】:

    User FatalError 解决方案的改编版

    • 适用于 python 3
    • 添加了一个 hex-col-header
    • 长度参数可选
    • 重命名为高清

    例子

    高清 0xbfffe4f1

    高清 0xbfffe4f1 500

    import gdb
    from curses.ascii import isgraph
    
    def groups_of(iterable, size, first=0):
        first = first if first != 0 else size
        chunk, iterable = iterable[:first], iterable[first:]
        while chunk:
            yield chunk
            chunk, iterable = iterable[:size], iterable[size:]
    
    class HexDump(gdb.Command):
        def __init__(self):
            super (HexDump, self).__init__ ('hd', gdb.COMMAND_DATA)
    
        def invoke(self, arg, from_tty):
            argv = gdb.string_to_argv(arg)
    
            addr = gdb.parse_and_eval(argv[0]).cast(
                gdb.lookup_type('void').pointer())
            if len(argv) == 2:
                 try:
                     bytes = int(gdb.parse_and_eval(argv[1]))
                 except ValueError:
                     raise gdb.GdbError('Byte count numst be an integer value.')
            else:
                 bytes = 500
    
            inferior = gdb.selected_inferior()
    
            align = gdb.parameter('hex-dump-align')
            width = gdb.parameter('hex-dump-width')
            if width == 0:
                width = 16
    
            mem = inferior.read_memory(addr, bytes)
            pr_addr = int(str(addr), 16)
            pr_offset = width
    
            if align:
                pr_offset = width - (pr_addr % width)
                pr_addr -= pr_addr % width
            start=(pr_addr) & 0xff;
    
    
            print ('            ' , end="")
            print ('  '.join(['%01X' % (i&0x0f,) for i in range(start,start+width)]) , end="")
            print ('  ' , end="")       
            print (' '.join(['%01X' % (i&0x0f,) for i in range(start,start+width)]) )
    
            for group in groups_of(mem, width, pr_offset):
                print ('0x%x: ' % (pr_addr,) + '   '*(width - pr_offset), end="")
                print (' '.join(['%02X' % (ord(g),) for g in group]) + \
                    '   ' * (width - len(group) if pr_offset == width else 0) + ' ', end="")    
                print (' '*(width - pr_offset) +  ' '.join(
                    [chr( int.from_bytes(g, byteorder='big')) if isgraph( int.from_bytes(g, byteorder='big')   ) or g == ' ' else '.' for g in group]))
                pr_addr += width
                pr_offset = width
    
    class HexDumpAlign(gdb.Parameter):
        def __init__(self):
            super (HexDumpAlign, self).__init__('hex-dump-align',
                                                gdb.COMMAND_DATA,
                                                gdb.PARAM_BOOLEAN)
    
        set_doc = 'Determines if hex-dump always starts at an "aligned" address (see hex-dump-width'
        show_doc = 'Hex dump alignment is currently'
    
    class HexDumpWidth(gdb.Parameter):
        def __init__(self):
            super (HexDumpWidth, self).__init__('hex-dump-width',
                                                gdb.COMMAND_DATA,
                                                gdb.PARAM_INTEGER)
    
        set_doc = 'Set the number of bytes per line of hex-dump'
    
        show_doc = 'The number of bytes per line in hex-dump is'
    
    HexDump()
    HexDumpAlign()
    HexDumpWidth()
    

    【讨论】:

    • 这在 Rust 中存在错误,因为 void 指针不是存在的类型
    【解决方案5】:

    不幸的是,@FatalError 和 @gunthor 的版本对我不起作用,所以我自己又写了一个。看起来是这样的:

    (gdb) xxd hello_string 0xc
    00000001_00000f87:                  48 656c 6c6f 0957 6f72         Hello.Wor
    00000001_00000f90: 6c64 0a                                  ld.
    

    较新版本的xxd 支持-o 标志,该标志允许指定偏移量以添加到显示的偏移量(始终从0000000 开始)。

    如果xxd -o 不可用,这里有一个替代品,它可以正确对齐并显示xxd'd 的位置地址。

    xxd 命令:

    define xxd
        dump binary memory /tmp/dump.bin $arg0 $arg0+$arg1
        eval "shell xxd-o %p /tmp/dump.bin", $arg0
    end
    

    可以说是丑陋的 perl 脚本 xxd-oxxd 带有偏移量):

    #!/usr/bin/env perl
    use IPC::Open2; 
    $SIG{'__WARN__'} = sub{ die "$0: $!\n" };
    
    my $offset = shift // "0";
    $offset = oct($offset) if $offset =~ /^0/;
    my $base = $offset >= 2**32 ? 16 : 8;
    
    my $zeroes = $offset % 16;
    my $padding = 1 + int($zeroes / 2) + 2*$zeroes;
    my $bytestr = "\0" x $zeroes;
    { local $/; $bytestr .= <> }
    
    open2(\*XXD_OUT, \*XXD_IN, "xxd") or die "xxd is not available!";
    print XXD_IN $bytestr; close XXD_IN;
    
    if ($zeroes) {
        $_ = <XXD_OUT>;
        s/^(.{50}).{$zeroes}/$1 . (' ' x $zeroes)/ge;
        s/^([[:xdigit:]]+:).{$padding}/$1 . (' ' x $padding)/ge;
        my $newoff = sprintf("%0${base}x",hex($1)+$offset) =~ s/^(.{8})(.{8})$/$1_$2/r;
        s/^([[:xdigit:]]+):/$newoff:/g;
        print
    }
    while (<XXD_OUT>) {
        s/^([[:xdigit:]]+)(?=:)/sprintf("%0${base}x", hex($1)+$offset-$offset%16) =~ s[^(.{8})(.{8})$][$1_$2]r/ge;
        print
    }
    

    欢迎改进! :-)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-01-10
      • 1970-01-01
      相关资源
      最近更新 更多