Ricerca…


Conversione manuale di C Structs in Pack Sintassi

Se hai a che fare con le API C binarie dal codice Perl, tramite le funzioni syscall , ioctl o fcntl , devi sapere come costruire la memoria in un modo compatibile con C.

Ad esempio, se hai mai avuto a che fare con una funzione che si aspettava un timespec , dovresti cercare in /usr/include/time.h e trovare:

struct timespec
{
    __time_t tv_sec;            /* Seconds.  */
    __syscall_slong_t tv_nsec;  /* Nanoseconds.  */
};

Fai una danza con cpp per trovare cosa significhi veramente:

cpp -E /usr/include/time.h -o /dev/stdout | grep __time_t
# typedef long int __time_t;
cpp -E /usr/include/time.h -o /dev/stdout | grep __syscall_slong_t
# typedef long int __syscall_slong_t

Quindi è un (firmato) int

echo 'void main(){ printf("%#lx\n", sizeof(__syscall_slong_t)); }' | 
  gcc -x c -include stdio.h -include time.h - -o /tmp/a.out && /tmp/a.out
# 0x8

E ci vogliono 8 byte. Così firmato a 64 bit int. E io sono su un processore a 64 bit. =)

Il pack Perldoc dice

            q  A signed quad (64-bit) value.

Quindi per imballare un timespec:

sub packtime {
    my ( $config ) = @_; 
    return pack 'qq', @{$config}{qw( tv_sec tv_nsec )};
}

E per decomprimere un timespec:

sub unpacktime {
   my ( $buf ) = @_;
   my $out = {};
   @{$out}{qw( tv_sec tv_nsec )} = unpack 'qq', $buf;
   return $out;
}

Ora puoi semplicemente usare quelle funzioni.

my $timespec = packtime({ tv_sec => 0, tv_nsec => 0 });
syscall(  ..., $timespec ); # some syscall that reads timespec

later ...
syscall( ..., $timespec ); # some syscall that writes timespec
print Dumper( unpacktime( $timespec ));

Costruire un'intestazione IPv4

A volte devi gestire strutture definite in termini di tipi di dati C da Perl. Una di queste applicazioni è la creazione di pacchetti di rete grezzi, nel caso in cui si desideri fare qualcosa di più interessante di quello che l'API di socket normale ha da offrire. Questo è esattamente ciò che pack() (e unpack() ovviamente) è lì per.

La parte obbligatoria di un'intestazione IP è lunga 20 ottetti (AKA "byte"). Come puoi vedere dietro questo link, l'indirizzo IP di origine e di destinazione costituisce gli ultimi due valori a 32 bit nell'intestazione. Tra gli altri campi vi sono alcuni con 16 bit, alcuni con 8 bit e alcuni pezzi più piccoli tra 2 e 13 bit.

Supponendo che abbiamo le seguenti variabili da inserire nella nostra intestazione:

my ($dscp, $ecn, $length,
    $id, $flags, $frag_off,
    $ttl, $proto,
    $src_ip,
    $dst_ip);

Si noti che mancano tre campi dall'intestazione:

  • La versione è sempre 4 (dopo tutto è IPv4)
  • IHL è 5 nel nostro esempio in quanto non abbiamo un campo opzioni ; la lunghezza è specificata in unità di 4 ottetti, quindi 20 ottetti danno una lunghezza di 5.
  • Il checksum può essere lasciato a 0. In realtà dovremmo calcolarlo, ma il codice per farlo non ci riguarda qui.

Potremmo provare e usare le operazioni di bit per costruire ad esempio i primi 32 bit:

my $hdr = 4 << 28 | 5 << 24 | $dscp << 18 | $ecn << 16 | $length;

Questo approccio funziona solo fino alla dimensione di un numero intero, che di solito è di 64 bit ma può arrivare fino a 32. Peggio ancora, dipende dall'endianità della CPU, quindi funzionerà su alcune CPU e fallirà su altre. Proviamo pack() :

my $hdr = pack('H2B8n', '45', sprintf("%06b%02b", $dscp, $ecn), $length);

Il modello specifica innanzitutto H2 , una stringa esadecimale di 2 caratteri, prima il nybble alto . L'argomento corrispondente al pacchetto è "45" - versione 4, lunghezza 5. Il modello successivo è B8 , una stringa di bit a 8 bit, ordine di bit discendente all'interno di ciascun byte . Abbiamo bisogno di utilizzare le stringhe di bit per controllare il layout fino a chunks più piccoli di un nybble (4 bit), quindi lo sprintf() viene utilizzato per costruire una stringa di bit da 6 bit da $dscp e 2 da $ecn . L'ultimo è n , un valore a 16 bit senza segno in Network Byte Order , ovvero sempre big-endian, indipendentemente dal formato intero nativo della CPU, ed è pieno di $length .

Questo è il primo 32 bit dell'intestazione. Il resto può essere costruito in modo simile:

Modello Discussione Osservazioni
n $id
B16 sprintf("%03b%013b", $flags, $frag_off) Come DSCP / ECN
C2 $ttl, $proto Due ottetti consecutivi senza segno
n 0 / $checksum x potrebbe essere usato per inserire un byte null ma n ci permette di specificare un argomento se dovessimo scegliere di calcolare un checksum
N2 $src_ip, $dst_ip usa a4a4 per impacchettare il risultato di due chiamate gethostbyname() come già avviene in Network Byte Order!

Quindi la chiamata completa per comprimere un header IPv4 sarebbe:

my $hdr = pack('H2B8n2B16C2nN2',
    '45', sprintf("%06b%02b", $dscp, $ecn), $length,
    $id, sprintf("%03b%013b", $flags, $frag_off),
    $ttl, $proto, 0,
    $src_ip, $dst_ip
);


Modified text is an extract of the original Stack Overflow Documentation
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow