web-dev-qa-db-fra.com

Vidage de la mémoire au format xxd de gdb

J'essaie d'inspecter un tampon qui contient un message au format binaire, mais contient également des données de chaîne. À titre d'exemple, j'utilise ce code C:

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

    return 0;
}

Je voudrais obtenir un vidage hexadécimal de ce qui est dans buf, d'un format similaire à xxd (peu m'importe si c'est une correspondance exacte, ce que je recherche vraiment c'est un vidage hexadécimal côte à côte avec des caractères imprimables).

Dans GDB, je peux utiliser quelque chose comme:

(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

ce qui est bien, mais il est difficile de choisir des chaînes de cette façon ... ou je peux utiliser

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

ce qui rend difficile la lecture de la partie binaire ... les messages réels avec lesquels j'ai affaire contiennent aussi beaucoup d'ascii nul, donc ça ressemble vraiment à un gâchis.

Le mieux que je puisse trouver est de faire ceci:

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

puis

$ 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                                ....

mais c'est pénible de le faire à chaque fois. J'ai pensé que quelqu'un là-bas avait déjà voulu cela auparavant, alors je me demandais si quelqu'un avait trouvé un moyen de le faire dans gdb. De plus, vous perdez les adresses de la mémoire d'origine de cette façon.

J'utilise GDB 7.4 avec le support python intégré, donc je suis ouvert à l'idée d'utiliser une jolie imprimante ou similaire, mais je ne sais pas comment configurer cela.

40
FatalError
(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

Semble assez facile ;-)

Vous pourriez probablement écrire un script Python (les versions GDB modernes ont incorporé Python) pour faire de même, et vous débarrasser de la nécessité de "décortiquer") .

68
Employed Russian

Donc, j'ai fini par jouer avec l'interface python et j'ai trouvé ceci:

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()

Je me rends compte que ce n'est peut-être pas la solution la plus belle et élégante, mais cela fait le travail et fonctionne comme un premier projet. Il pourrait être inclus dans ~/.gdbinit comme:

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

Ensuite, pourrait être utilisé avec le programme ci-dessus comme suit:

(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                                      ....

Et quelques autres touches pour faire bonne mesure:

(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              ....

Aucune promesse qu'il n'y a pas de bugs :). Je pourrais le coller dans github ou quelque chose si les gens sont intéressés.

Je ne l'ai testé qu'avec GDB 7.4.

24
FatalError

une version adaptée de la solution de User FatalError

  • fonctionne avec python 3
  • ajouté un en-tête hex-col
  • paramètre de longueur en option
  • renommé en hd

exemples

hd 0xbfffe4f1

hd 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()
2
gunthor

Malheureusement, les versions de @ FatalError et @ gunthor ne fonctionnaient pas pour moi, alors j'en ai écrit une autre moi-même. C'est à ça que ça ressemble:

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

Les nouvelles versions de xxd prennent en charge le -o drapeau qui permet de spécifier un décalage à ajouter à celui affiché (qui commencera toujours à 0000000).

Au cas où xxd -o n'est pas disponible, voici un substitut qui s'aligne correctement et affiche l'adresse de l'emplacement qui est xxd 'd.

La commande xxd:

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

Le script Perl sans doute laid - xxd-o (xxd avec décalage):

#!/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
}

Améliorations bienvenues! :-)

1
a3f

Ma propre contribution, de la solution Employed Russian et de Roger Lipscombe, commente:

  • utilisez xxd,
  • conserver l'adresse (xxd -o)
  • l'argument taille est facultatif
  • petite documentation incluse

Le script (testé avec 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

Exemples:

(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
0
Ek1noX